@cleocode/cleo 2026.3.73 → 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 +1181 -829
- package/dist/cli/index.js.map +4 -4
- package/dist/mcp/index.js +2244 -1945
- package/dist/mcp/index.js.map +4 -4
- package/package.json +3 -3
package/dist/mcp/index.js
CHANGED
|
@@ -632,6 +632,13 @@ var init_discovery = __esm({
|
|
|
632
632
|
});
|
|
633
633
|
|
|
634
634
|
// packages/core/src/logger.ts
|
|
635
|
+
var logger_exports = {};
|
|
636
|
+
__export(logger_exports, {
|
|
637
|
+
closeLogger: () => closeLogger,
|
|
638
|
+
getLogDir: () => getLogDir,
|
|
639
|
+
getLogger: () => getLogger,
|
|
640
|
+
initLogger: () => initLogger
|
|
641
|
+
});
|
|
635
642
|
import { existsSync as existsSync2 } from "node:fs";
|
|
636
643
|
import { dirname, join as join3 } from "node:path";
|
|
637
644
|
import pino from "pino";
|
|
@@ -45251,8 +45258,8 @@ var init_checksum = __esm({
|
|
|
45251
45258
|
});
|
|
45252
45259
|
|
|
45253
45260
|
// packages/core/src/migration/logger.ts
|
|
45254
|
-
var
|
|
45255
|
-
__export(
|
|
45261
|
+
var logger_exports2 = {};
|
|
45262
|
+
__export(logger_exports2, {
|
|
45256
45263
|
MigrationLogger: () => MigrationLogger,
|
|
45257
45264
|
createMigrationLogger: () => createMigrationLogger,
|
|
45258
45265
|
getLatestMigrationLog: () => getLatestMigrationLog,
|
|
@@ -48205,1009 +48212,1911 @@ var init_transfer = __esm({
|
|
|
48205
48212
|
}
|
|
48206
48213
|
});
|
|
48207
48214
|
|
|
48208
|
-
// packages/core/src/
|
|
48209
|
-
var
|
|
48210
|
-
__export(
|
|
48211
|
-
|
|
48212
|
-
|
|
48213
|
-
|
|
48214
|
-
|
|
48215
|
-
|
|
48216
|
-
checkPermission: () => checkPermission,
|
|
48217
|
-
checkPermissionDetail: () => checkPermissionDetail,
|
|
48218
|
-
criticalPath: () => criticalPath,
|
|
48219
|
-
discoverRelated: () => discoverRelated,
|
|
48220
|
-
executeTransfer: () => executeTransfer,
|
|
48221
|
-
extractKeywords: () => extractKeywords2,
|
|
48222
|
-
generateProjectHash: () => generateProjectHash,
|
|
48223
|
-
getCurrentProject: () => getCurrentProject,
|
|
48224
|
-
getNexusCacheDir: () => getNexusCacheDir,
|
|
48225
|
-
getNexusHome: () => getNexusHome,
|
|
48226
|
-
getPermission: () => getPermission,
|
|
48227
|
-
getProjectFromQuery: () => getProjectFromQuery,
|
|
48228
|
-
getSharingStatus: () => getSharingStatus,
|
|
48229
|
-
invalidateGraphCache: () => invalidateGraphCache,
|
|
48230
|
-
nexusDeps: () => nexusDeps,
|
|
48231
|
-
nexusGetProject: () => nexusGetProject,
|
|
48232
|
-
nexusInit: () => nexusInit,
|
|
48233
|
-
nexusList: () => nexusList,
|
|
48234
|
-
nexusProjectExists: () => nexusProjectExists,
|
|
48235
|
-
nexusReconcile: () => nexusReconcile,
|
|
48236
|
-
nexusRegister: () => nexusRegister,
|
|
48237
|
-
nexusSetPermission: () => nexusSetPermission,
|
|
48238
|
-
nexusSync: () => nexusSync,
|
|
48239
|
-
nexusSyncAll: () => nexusSyncAll,
|
|
48240
|
-
nexusUnregister: () => nexusUnregister,
|
|
48241
|
-
orphanDetection: () => orphanDetection,
|
|
48242
|
-
parseQuery: () => parseQuery,
|
|
48243
|
-
permissionLevel: () => permissionLevel,
|
|
48244
|
-
previewTransfer: () => previewTransfer,
|
|
48245
|
-
readRegistry: () => readRegistry,
|
|
48246
|
-
readRegistryRequired: () => readRegistryRequired,
|
|
48247
|
-
requirePermission: () => requirePermission,
|
|
48248
|
-
resetNexusDbState: () => resetNexusDbState,
|
|
48249
|
-
resolveCrossDeps: () => resolveCrossDeps,
|
|
48250
|
-
resolveProjectPath: () => resolveProjectPath2,
|
|
48251
|
-
resolveTask: () => resolveTask,
|
|
48252
|
-
searchAcrossProjects: () => searchAcrossProjects,
|
|
48253
|
-
setPermission: () => setPermission,
|
|
48254
|
-
syncGitignore: () => syncGitignore,
|
|
48255
|
-
validateSyntax: () => validateSyntax
|
|
48256
|
-
});
|
|
48257
|
-
var init_nexus = __esm({
|
|
48258
|
-
"packages/core/src/nexus/index.ts"() {
|
|
48259
|
-
"use strict";
|
|
48260
|
-
init_deps();
|
|
48261
|
-
init_discover();
|
|
48262
|
-
init_hash();
|
|
48263
|
-
init_permissions();
|
|
48264
|
-
init_query3();
|
|
48265
|
-
init_registry3();
|
|
48266
|
-
init_sharing();
|
|
48267
|
-
init_transfer();
|
|
48268
|
-
}
|
|
48269
|
-
});
|
|
48270
|
-
|
|
48271
|
-
// packages/core/src/observability/types.ts
|
|
48272
|
-
var PINO_LEVEL_VALUES;
|
|
48273
|
-
var init_types = __esm({
|
|
48274
|
-
"packages/core/src/observability/types.ts"() {
|
|
48275
|
-
"use strict";
|
|
48276
|
-
PINO_LEVEL_VALUES = {
|
|
48277
|
-
TRACE: 10,
|
|
48278
|
-
DEBUG: 20,
|
|
48279
|
-
INFO: 30,
|
|
48280
|
-
WARN: 40,
|
|
48281
|
-
ERROR: 50,
|
|
48282
|
-
FATAL: 60
|
|
48283
|
-
};
|
|
48284
|
-
}
|
|
48215
|
+
// packages/core/src/task-work/index.ts
|
|
48216
|
+
var task_work_exports = {};
|
|
48217
|
+
__export(task_work_exports, {
|
|
48218
|
+
currentTask: () => currentTask,
|
|
48219
|
+
getTaskHistory: () => getTaskHistory,
|
|
48220
|
+
getWorkHistory: () => getWorkHistory,
|
|
48221
|
+
startTask: () => startTask,
|
|
48222
|
+
stopTask: () => stopTask
|
|
48285
48223
|
});
|
|
48286
|
-
|
|
48287
|
-
|
|
48288
|
-
|
|
48289
|
-
return
|
|
48224
|
+
async function currentTask(cwd, accessor) {
|
|
48225
|
+
const acc = accessor ?? await getAccessor(cwd);
|
|
48226
|
+
const focus = await acc.getMetaValue("focus_state");
|
|
48227
|
+
return {
|
|
48228
|
+
currentTask: focus?.currentTask ?? null,
|
|
48229
|
+
currentPhase: focus?.currentPhase ?? null,
|
|
48230
|
+
sessionNote: focus?.sessionNote ?? null,
|
|
48231
|
+
nextAction: focus?.nextAction ?? null
|
|
48232
|
+
};
|
|
48290
48233
|
}
|
|
48291
|
-
function
|
|
48292
|
-
if (
|
|
48293
|
-
|
|
48294
|
-
return false;
|
|
48295
|
-
if (filter.since !== void 0 && entry.time < filter.since) return false;
|
|
48296
|
-
if (filter.until !== void 0 && entry.time > filter.until) return false;
|
|
48297
|
-
if (filter.subsystem !== void 0 && entry.subsystem !== filter.subsystem) return false;
|
|
48298
|
-
if (filter.code !== void 0 && entry.code !== filter.code) return false;
|
|
48299
|
-
if (filter.exitCode !== void 0 && entry.exitCode !== filter.exitCode) return false;
|
|
48300
|
-
if (filter.pid !== void 0 && entry.pid !== filter.pid) return false;
|
|
48301
|
-
if (filter.msgContains !== void 0) {
|
|
48302
|
-
if (!entry.msg.toLowerCase().includes(filter.msgContains.toLowerCase())) return false;
|
|
48234
|
+
async function startTask(taskId, cwd, accessor) {
|
|
48235
|
+
if (!taskId) {
|
|
48236
|
+
throw new CleoError(2 /* INVALID_INPUT */, "Task ID is required");
|
|
48303
48237
|
}
|
|
48304
|
-
|
|
48305
|
-
|
|
48306
|
-
|
|
48307
|
-
|
|
48308
|
-
}
|
|
48309
|
-
|
|
48310
|
-
const start = offset ?? 0;
|
|
48311
|
-
if (limit !== void 0) {
|
|
48312
|
-
return entries.slice(start, start + limit);
|
|
48238
|
+
const acc = accessor ?? await getAccessor(cwd);
|
|
48239
|
+
const task = await acc.loadSingleTask(taskId);
|
|
48240
|
+
if (!task) {
|
|
48241
|
+
throw new CleoError(4 /* NOT_FOUND */, `Task not found: ${taskId}`, {
|
|
48242
|
+
fix: `Use 'cleo find "${taskId}"' to search`
|
|
48243
|
+
});
|
|
48313
48244
|
}
|
|
48314
|
-
|
|
48315
|
-
|
|
48316
|
-
|
|
48317
|
-
|
|
48318
|
-
|
|
48319
|
-
|
|
48245
|
+
const { tasks: allTasks } = await acc.queryTasks({});
|
|
48246
|
+
const unresolvedDeps = getUnresolvedDeps(taskId, allTasks);
|
|
48247
|
+
if (unresolvedDeps.length > 0) {
|
|
48248
|
+
throw new CleoError(
|
|
48249
|
+
5 /* DEPENDENCY_ERROR */,
|
|
48250
|
+
`Task ${taskId} is blocked by unresolved dependencies: ${unresolvedDeps.join(", ")}`,
|
|
48251
|
+
{
|
|
48252
|
+
fix: `Complete blockers first: ${unresolvedDeps.map((d) => `cleo complete ${d}`).join(", ")}`
|
|
48253
|
+
}
|
|
48254
|
+
);
|
|
48320
48255
|
}
|
|
48321
|
-
}
|
|
48322
|
-
|
|
48323
|
-
|
|
48324
|
-
|
|
48325
|
-
|
|
48256
|
+
const focus = await acc.getMetaValue("focus_state") ?? {};
|
|
48257
|
+
const previousTask = focus.currentTask ?? null;
|
|
48258
|
+
focus.currentTask = taskId;
|
|
48259
|
+
focus.currentPhase = task.phase ?? null;
|
|
48260
|
+
const noteEntry = {
|
|
48261
|
+
note: `Started work on ${taskId}: ${task.title}`,
|
|
48262
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
48263
|
+
};
|
|
48264
|
+
if (!focus.sessionNotes) {
|
|
48265
|
+
focus.sessionNotes = [];
|
|
48266
|
+
}
|
|
48267
|
+
focus.sessionNotes.push(noteEntry);
|
|
48268
|
+
await acc.setMetaValue("focus_state", focus);
|
|
48269
|
+
await logOperation(
|
|
48270
|
+
"task_start",
|
|
48271
|
+
taskId,
|
|
48272
|
+
{
|
|
48273
|
+
previousTask,
|
|
48274
|
+
title: task.title
|
|
48275
|
+
},
|
|
48276
|
+
accessor
|
|
48277
|
+
);
|
|
48278
|
+
const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
|
|
48279
|
+
hooks2.dispatch("PreToolUse", cwd ?? process.cwd(), {
|
|
48280
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
48281
|
+
taskId,
|
|
48282
|
+
taskTitle: task.title
|
|
48283
|
+
}).catch(() => {
|
|
48284
|
+
});
|
|
48285
|
+
return {
|
|
48286
|
+
taskId,
|
|
48287
|
+
taskTitle: task.title,
|
|
48288
|
+
previousTask
|
|
48289
|
+
};
|
|
48326
48290
|
}
|
|
48327
|
-
function
|
|
48328
|
-
const
|
|
48329
|
-
|
|
48330
|
-
|
|
48331
|
-
|
|
48332
|
-
|
|
48333
|
-
} catch {
|
|
48334
|
-
return null;
|
|
48291
|
+
async function stopTask(cwd, accessor) {
|
|
48292
|
+
const acc = accessor ?? await getAccessor(cwd);
|
|
48293
|
+
const focus = await acc.getMetaValue("focus_state");
|
|
48294
|
+
const previousTask = focus?.currentTask ?? null;
|
|
48295
|
+
if (!focus) {
|
|
48296
|
+
return { previousTask: null };
|
|
48335
48297
|
}
|
|
48336
|
-
|
|
48337
|
-
const
|
|
48338
|
-
|
|
48339
|
-
|
|
48340
|
-
|
|
48341
|
-
|
|
48342
|
-
|
|
48343
|
-
|
|
48344
|
-
|
|
48345
|
-
|
|
48346
|
-
|
|
48347
|
-
|
|
48348
|
-
|
|
48349
|
-
|
|
48350
|
-
extra[key] = raw[key];
|
|
48351
|
-
}
|
|
48298
|
+
const taskId = focus.currentTask;
|
|
48299
|
+
const task = taskId ? await acc.loadSingleTask(taskId) : void 0;
|
|
48300
|
+
focus.currentTask = null;
|
|
48301
|
+
focus.nextAction = null;
|
|
48302
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
48303
|
+
if (taskId && task) {
|
|
48304
|
+
const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
|
|
48305
|
+
hooks2.dispatch("PostToolUse", cwd ?? process.cwd(), {
|
|
48306
|
+
timestamp: now2,
|
|
48307
|
+
taskId,
|
|
48308
|
+
taskTitle: task.title,
|
|
48309
|
+
status: "done"
|
|
48310
|
+
}).catch(() => {
|
|
48311
|
+
});
|
|
48352
48312
|
}
|
|
48353
|
-
|
|
48354
|
-
|
|
48355
|
-
|
|
48356
|
-
|
|
48357
|
-
|
|
48358
|
-
|
|
48359
|
-
|
|
48360
|
-
|
|
48361
|
-
|
|
48362
|
-
|
|
48363
|
-
if (typeof raw.exitCode === "number") entry.exitCode = raw.exitCode;
|
|
48364
|
-
return entry;
|
|
48313
|
+
await acc.setMetaValue("focus_state", focus);
|
|
48314
|
+
await logOperation(
|
|
48315
|
+
"task_stop",
|
|
48316
|
+
previousTask ?? "none",
|
|
48317
|
+
{
|
|
48318
|
+
previousTask
|
|
48319
|
+
},
|
|
48320
|
+
accessor
|
|
48321
|
+
);
|
|
48322
|
+
return { previousTask };
|
|
48365
48323
|
}
|
|
48366
|
-
function
|
|
48367
|
-
const
|
|
48368
|
-
|
|
48369
|
-
|
|
48370
|
-
|
|
48324
|
+
async function getWorkHistory(cwd, accessor) {
|
|
48325
|
+
const acc = accessor ?? await getAccessor(cwd);
|
|
48326
|
+
const focus = await acc.getMetaValue("focus_state");
|
|
48327
|
+
const notes = focus?.sessionNotes ?? [];
|
|
48328
|
+
const history = [];
|
|
48329
|
+
for (const note of notes) {
|
|
48330
|
+
const match = note.note.match(/^(?:Focus set to|Started work on) (T\d+)/);
|
|
48331
|
+
if (match) {
|
|
48332
|
+
history.push({
|
|
48333
|
+
taskId: match[1],
|
|
48334
|
+
timestamp: note.timestamp
|
|
48335
|
+
});
|
|
48336
|
+
}
|
|
48371
48337
|
}
|
|
48372
|
-
return
|
|
48338
|
+
return history.reverse();
|
|
48373
48339
|
}
|
|
48374
|
-
var
|
|
48375
|
-
var
|
|
48376
|
-
"packages/core/src/
|
|
48340
|
+
var getTaskHistory;
|
|
48341
|
+
var init_task_work = __esm({
|
|
48342
|
+
"packages/core/src/task-work/index.ts"() {
|
|
48377
48343
|
"use strict";
|
|
48378
|
-
|
|
48379
|
-
|
|
48380
|
-
|
|
48381
|
-
|
|
48382
|
-
|
|
48383
|
-
|
|
48384
|
-
|
|
48385
|
-
"msg",
|
|
48386
|
-
"subsystem",
|
|
48387
|
-
"code",
|
|
48388
|
-
"exitCode"
|
|
48389
|
-
]);
|
|
48344
|
+
init_src();
|
|
48345
|
+
init_errors3();
|
|
48346
|
+
init_data_accessor();
|
|
48347
|
+
init_add();
|
|
48348
|
+
init_dependency_check();
|
|
48349
|
+
init_handlers();
|
|
48350
|
+
getTaskHistory = getWorkHistory;
|
|
48390
48351
|
}
|
|
48391
48352
|
});
|
|
48392
48353
|
|
|
48393
|
-
// packages/core/src/
|
|
48394
|
-
|
|
48395
|
-
|
|
48396
|
-
|
|
48397
|
-
|
|
48398
|
-
|
|
48399
|
-
|
|
48400
|
-
const cleoDir = getCleoDirAbsolute(cwd);
|
|
48401
|
-
const logsDir = join62(cleoDir, "logs");
|
|
48402
|
-
return existsSync56(logsDir) ? logsDir : null;
|
|
48354
|
+
// packages/core/src/tasks/complete.ts
|
|
48355
|
+
var complete_exports = {};
|
|
48356
|
+
__export(complete_exports, {
|
|
48357
|
+
completeTask: () => completeTask
|
|
48358
|
+
});
|
|
48359
|
+
function isVerificationGate(value) {
|
|
48360
|
+
return VERIFICATION_GATES.has(value);
|
|
48403
48361
|
}
|
|
48404
|
-
function
|
|
48405
|
-
|
|
48362
|
+
async function loadCompletionEnforcement(cwd) {
|
|
48363
|
+
const isTest = !!process.env.VITEST;
|
|
48364
|
+
const config2 = await loadConfig(cwd);
|
|
48365
|
+
const acceptance = config2.enforcement?.acceptance;
|
|
48366
|
+
const verificationCfg = config2.verification;
|
|
48367
|
+
const acceptanceMode = acceptance?.mode ?? (isTest ? "off" : "block");
|
|
48368
|
+
const acceptanceRequiredForPriorities = acceptance?.requiredForPriorities ?? (isTest ? [] : ["critical", "high", "medium", "low"]);
|
|
48369
|
+
const rawVerificationEnabled = await getRawConfigValue("verification.enabled", cwd);
|
|
48370
|
+
const verificationEnabled = rawVerificationEnabled !== void 0 ? rawVerificationEnabled : !isTest;
|
|
48371
|
+
const verificationRequiredGates = (verificationCfg?.requiredGates ?? []).filter(isVerificationGate).length > 0 ? (verificationCfg?.requiredGates ?? []).filter(isVerificationGate) : DEFAULT_VERIFICATION_REQUIRED_GATES;
|
|
48372
|
+
const verificationMaxRounds = verificationCfg?.maxRounds ?? 5;
|
|
48373
|
+
const lifecycleMode = config2.lifecycle?.mode ?? (isTest ? "off" : "strict");
|
|
48374
|
+
return {
|
|
48375
|
+
acceptanceMode,
|
|
48376
|
+
acceptanceRequiredForPriorities,
|
|
48377
|
+
verificationEnabled,
|
|
48378
|
+
verificationRequiredGates,
|
|
48379
|
+
verificationMaxRounds,
|
|
48380
|
+
lifecycleMode
|
|
48381
|
+
};
|
|
48406
48382
|
}
|
|
48407
|
-
function
|
|
48408
|
-
|
|
48409
|
-
|
|
48410
|
-
|
|
48411
|
-
|
|
48412
|
-
|
|
48413
|
-
return [];
|
|
48414
|
-
}
|
|
48415
|
-
const files = [];
|
|
48416
|
-
for (const name2 of fileNames) {
|
|
48417
|
-
const cleoMatch = name2.match(CLEO_LOG_PATTERN);
|
|
48418
|
-
const isMigration = MIGRATION_LOG_PATTERN.test(name2);
|
|
48419
|
-
if (!cleoMatch && (!isMigration || !includeMigration)) continue;
|
|
48420
|
-
const filePath = join62(dir, name2);
|
|
48421
|
-
let stat2;
|
|
48422
|
-
try {
|
|
48423
|
-
stat2 = statSync16(filePath);
|
|
48424
|
-
} catch {
|
|
48425
|
-
continue;
|
|
48426
|
-
}
|
|
48427
|
-
files.push({
|
|
48428
|
-
path: filePath,
|
|
48429
|
-
name: name2,
|
|
48430
|
-
size: stat2.size,
|
|
48431
|
-
mtime: stat2.mtime.toISOString(),
|
|
48432
|
-
date: cleoMatch ? cleoMatch[1] : null,
|
|
48433
|
-
isActive: false
|
|
48434
|
-
// set below
|
|
48383
|
+
async function completeTask(options, cwd, accessor) {
|
|
48384
|
+
const acc = accessor ?? await getAccessor(cwd);
|
|
48385
|
+
const task = await acc.loadSingleTask(options.taskId);
|
|
48386
|
+
if (!task) {
|
|
48387
|
+
throw new CleoError(4 /* NOT_FOUND */, `Task not found: ${options.taskId}`, {
|
|
48388
|
+
fix: `Use 'cleo find "${options.taskId}"' to search`
|
|
48435
48389
|
});
|
|
48436
48390
|
}
|
|
48437
|
-
|
|
48438
|
-
|
|
48439
|
-
|
|
48440
|
-
|
|
48441
|
-
const includeMigration = options?.includeMigration ?? false;
|
|
48442
|
-
const sinceDate = options?.since;
|
|
48443
|
-
let files = [];
|
|
48444
|
-
if (scope === "project" || scope === "both") {
|
|
48445
|
-
const dir = getProjectLogDir(cwd);
|
|
48446
|
-
if (dir) files.push(...scanLogDir(dir, includeMigration));
|
|
48391
|
+
await requireActiveSession("tasks.complete", cwd);
|
|
48392
|
+
const enforcement = await loadCompletionEnforcement(cwd);
|
|
48393
|
+
if (task.status === "done") {
|
|
48394
|
+
throw new CleoError(17 /* TASK_COMPLETED */, `Task ${options.taskId} is already completed`);
|
|
48447
48395
|
}
|
|
48448
|
-
if (
|
|
48449
|
-
const
|
|
48450
|
-
|
|
48396
|
+
if (task.depends?.length) {
|
|
48397
|
+
const deps = await acc.loadTasks(task.depends);
|
|
48398
|
+
const incompleteDeps = deps.filter((d) => d.status !== "done" && d.status !== "cancelled").map((d) => d.id);
|
|
48399
|
+
if (incompleteDeps.length > 0) {
|
|
48400
|
+
throw new CleoError(
|
|
48401
|
+
5 /* DEPENDENCY_ERROR */,
|
|
48402
|
+
`Task ${options.taskId} has incomplete dependencies: ${incompleteDeps.join(", ")}`,
|
|
48403
|
+
{
|
|
48404
|
+
fix: `Complete dependencies first: ${incompleteDeps.map((d) => `cleo complete ${d}`).join(", ")}`
|
|
48405
|
+
}
|
|
48406
|
+
);
|
|
48407
|
+
}
|
|
48451
48408
|
}
|
|
48452
|
-
|
|
48453
|
-
|
|
48409
|
+
const acceptanceEnforcement = await createAcceptanceEnforcement(cwd);
|
|
48410
|
+
const completionValidation = acceptanceEnforcement.validateCompletion(task);
|
|
48411
|
+
if (!completionValidation.valid) {
|
|
48412
|
+
throw new CleoError(
|
|
48413
|
+
completionValidation.exitCode ?? 6 /* VALIDATION_ERROR */,
|
|
48414
|
+
completionValidation.error,
|
|
48415
|
+
{ fix: completionValidation.fix }
|
|
48416
|
+
);
|
|
48454
48417
|
}
|
|
48455
|
-
|
|
48456
|
-
|
|
48457
|
-
|
|
48418
|
+
if (enforcement.verificationEnabled && task.type !== "epic") {
|
|
48419
|
+
if (!task.verification) {
|
|
48420
|
+
throw new CleoError(
|
|
48421
|
+
40 /* VERIFICATION_INIT_FAILED */,
|
|
48422
|
+
`Task ${options.taskId} is missing verification metadata`,
|
|
48423
|
+
{
|
|
48424
|
+
fix: `Initialize verification for ${options.taskId} before completion`
|
|
48425
|
+
}
|
|
48426
|
+
);
|
|
48427
|
+
}
|
|
48428
|
+
if (task.verification.round > enforcement.verificationMaxRounds) {
|
|
48429
|
+
throw new CleoError(
|
|
48430
|
+
44 /* MAX_ROUNDS_EXCEEDED */,
|
|
48431
|
+
`Task ${options.taskId} exceeded verification max rounds (${enforcement.verificationMaxRounds})`,
|
|
48432
|
+
{
|
|
48433
|
+
fix: `Review failure log and resolve blockers before retrying completion`
|
|
48434
|
+
}
|
|
48435
|
+
);
|
|
48436
|
+
}
|
|
48437
|
+
const missingRequiredGates = enforcement.verificationRequiredGates.filter(
|
|
48438
|
+
(gate) => task.verification?.gates?.[gate] !== true
|
|
48439
|
+
);
|
|
48440
|
+
if (missingRequiredGates.length > 0 || task.verification.passed !== true) {
|
|
48441
|
+
const exitCode = enforcement.lifecycleMode === "strict" ? 80 /* LIFECYCLE_GATE_FAILED */ : 45 /* GATE_DEPENDENCY */;
|
|
48442
|
+
throw new CleoError(
|
|
48443
|
+
exitCode,
|
|
48444
|
+
`Task ${options.taskId} failed verification gates: ${missingRequiredGates.join(", ") || "verification.passed=false"}`,
|
|
48445
|
+
{
|
|
48446
|
+
fix: `Set required verification gates before completion: ${enforcement.verificationRequiredGates.join(", ")}`
|
|
48447
|
+
}
|
|
48448
|
+
);
|
|
48449
|
+
}
|
|
48458
48450
|
}
|
|
48459
|
-
|
|
48460
|
-
|
|
48461
|
-
|
|
48462
|
-
|
|
48463
|
-
|
|
48464
|
-
|
|
48465
|
-
|
|
48466
|
-
|
|
48451
|
+
const children = await acc.getChildren(options.taskId);
|
|
48452
|
+
const incompleteChildren = children.filter(
|
|
48453
|
+
(c) => c.status !== "done" && c.status !== "cancelled"
|
|
48454
|
+
);
|
|
48455
|
+
if (incompleteChildren.length > 0 && task.type === "epic") {
|
|
48456
|
+
if (!task.noAutoComplete) {
|
|
48457
|
+
throw new CleoError(
|
|
48458
|
+
16 /* HAS_CHILDREN */,
|
|
48459
|
+
`Epic ${options.taskId} has ${incompleteChildren.length} incomplete children: ${incompleteChildren.map((c) => c.id).join(", ")}`,
|
|
48460
|
+
{
|
|
48461
|
+
fix: `Complete children first or use 'cleo update ${options.taskId} --no-auto-complete'`
|
|
48462
|
+
}
|
|
48463
|
+
);
|
|
48464
|
+
}
|
|
48467
48465
|
}
|
|
48468
|
-
|
|
48469
|
-
|
|
48470
|
-
|
|
48471
|
-
|
|
48472
|
-
|
|
48473
|
-
|
|
48474
|
-
|
|
48475
|
-
|
|
48476
|
-
|
|
48477
|
-
for await (const line2 of rl) {
|
|
48478
|
-
if (line2.trim()) yield line2;
|
|
48466
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
48467
|
+
const before = { ...task };
|
|
48468
|
+
task.status = "done";
|
|
48469
|
+
task.completedAt = now2;
|
|
48470
|
+
task.updatedAt = now2;
|
|
48471
|
+
if (options.notes) {
|
|
48472
|
+
const timestampedNote = `${(/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace(/\.\d+Z$/, " UTC")}: ${options.notes}`;
|
|
48473
|
+
if (!task.notes) task.notes = [];
|
|
48474
|
+
task.notes.push(timestampedNote);
|
|
48479
48475
|
}
|
|
48480
|
-
|
|
48481
|
-
|
|
48482
|
-
|
|
48483
|
-
"packages/core/src/observability/log-reader.ts"() {
|
|
48484
|
-
"use strict";
|
|
48485
|
-
init_logger();
|
|
48486
|
-
init_paths();
|
|
48487
|
-
CLEO_LOG_PATTERN = /^cleo\.(\d{4}-\d{2}-\d{2})\.(\d+)\.log$/;
|
|
48488
|
-
MIGRATION_LOG_PATTERN = /^migration-.*\.jsonl$/;
|
|
48476
|
+
if (options.changeset) {
|
|
48477
|
+
if (!task.notes) task.notes = [];
|
|
48478
|
+
task.notes.push(`Changeset: ${options.changeset}`);
|
|
48489
48479
|
}
|
|
48490
|
-
|
|
48491
|
-
|
|
48492
|
-
|
|
48493
|
-
|
|
48494
|
-
|
|
48495
|
-
|
|
48496
|
-
|
|
48497
|
-
|
|
48498
|
-
|
|
48499
|
-
|
|
48500
|
-
|
|
48501
|
-
|
|
48502
|
-
|
|
48503
|
-
|
|
48504
|
-
|
|
48505
|
-
|
|
48506
|
-
parseLogLines: () => parseLogLines,
|
|
48507
|
-
queryLogs: () => queryLogs,
|
|
48508
|
-
readLogFileLines: () => readLogFileLines,
|
|
48509
|
-
streamLogFileLines: () => streamLogFileLines,
|
|
48510
|
-
streamLogs: () => streamLogs
|
|
48511
|
-
});
|
|
48512
|
-
function queryLogs(filter, options, cwd) {
|
|
48513
|
-
const files = discoverLogFiles(options, cwd);
|
|
48514
|
-
const filesPaths = files.map((f) => f.path);
|
|
48515
|
-
let totalScanned = 0;
|
|
48516
|
-
const allEntries = [];
|
|
48517
|
-
for (const file2 of files) {
|
|
48518
|
-
const lines = readLogFileLines(file2.path);
|
|
48519
|
-
totalScanned += lines.length;
|
|
48520
|
-
const parsed = parseLogLines(lines);
|
|
48521
|
-
allEntries.push(...parsed);
|
|
48522
|
-
}
|
|
48523
|
-
const matched = filter ? filterEntries(allEntries, filter) : allEntries;
|
|
48524
|
-
const totalMatched = matched.length;
|
|
48525
|
-
const entries = paginate2(matched, filter?.limit, filter?.offset);
|
|
48526
|
-
return {
|
|
48527
|
-
entries,
|
|
48528
|
-
totalScanned,
|
|
48529
|
-
totalMatched,
|
|
48530
|
-
files: filesPaths
|
|
48531
|
-
};
|
|
48532
|
-
}
|
|
48533
|
-
async function* streamLogs(filter, options, cwd) {
|
|
48534
|
-
const files = discoverLogFiles(options, cwd);
|
|
48535
|
-
let yielded = 0;
|
|
48536
|
-
const limit = filter?.limit;
|
|
48537
|
-
for (const file2 of files) {
|
|
48538
|
-
if (limit !== void 0 && yielded >= limit) break;
|
|
48539
|
-
for await (const line2 of streamLogFileLines(file2.path)) {
|
|
48540
|
-
const entry = parseLogLine(line2);
|
|
48541
|
-
if (!entry) continue;
|
|
48542
|
-
if (filter && !matchesFilter(entry, filter)) continue;
|
|
48543
|
-
yield entry;
|
|
48544
|
-
yielded++;
|
|
48545
|
-
if (limit !== void 0 && yielded >= limit) return;
|
|
48480
|
+
const autoCompleted = [];
|
|
48481
|
+
const autoCompletedTasks = [];
|
|
48482
|
+
if (task.parentId) {
|
|
48483
|
+
const parent = await acc.loadSingleTask(task.parentId);
|
|
48484
|
+
if (parent && parent.type === "epic" && !parent.noAutoComplete) {
|
|
48485
|
+
const siblings = await acc.getChildren(parent.id);
|
|
48486
|
+
const allDone = siblings.every(
|
|
48487
|
+
(c) => c.id === task.id || c.status === "done" || c.status === "cancelled"
|
|
48488
|
+
);
|
|
48489
|
+
if (allDone) {
|
|
48490
|
+
parent.status = "done";
|
|
48491
|
+
parent.completedAt = now2;
|
|
48492
|
+
parent.updatedAt = now2;
|
|
48493
|
+
autoCompleted.push(parent.id);
|
|
48494
|
+
autoCompletedTasks.push(parent);
|
|
48495
|
+
}
|
|
48546
48496
|
}
|
|
48547
48497
|
}
|
|
48548
|
-
|
|
48549
|
-
|
|
48550
|
-
|
|
48551
|
-
|
|
48552
|
-
|
|
48553
|
-
|
|
48554
|
-
|
|
48555
|
-
|
|
48556
|
-
|
|
48557
|
-
|
|
48558
|
-
|
|
48559
|
-
|
|
48560
|
-
|
|
48561
|
-
|
|
48562
|
-
|
|
48563
|
-
|
|
48498
|
+
await acc.transaction(async (tx) => {
|
|
48499
|
+
await tx.upsertSingleTask(task);
|
|
48500
|
+
for (const parentTask of autoCompletedTasks) {
|
|
48501
|
+
await tx.upsertSingleTask(parentTask);
|
|
48502
|
+
}
|
|
48503
|
+
await tx.appendLog({
|
|
48504
|
+
id: `log-${Math.floor(Date.now() / 1e3)}-${(await import("node:crypto")).randomBytes(3).toString("hex")}`,
|
|
48505
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
48506
|
+
action: "task_completed",
|
|
48507
|
+
taskId: options.taskId,
|
|
48508
|
+
actor: "system",
|
|
48509
|
+
details: { title: task.title, previousStatus: before.status },
|
|
48510
|
+
before: null,
|
|
48511
|
+
after: { title: task.title, previousStatus: before.status }
|
|
48512
|
+
});
|
|
48513
|
+
});
|
|
48514
|
+
const dependents = await acc.getDependents(options.taskId);
|
|
48515
|
+
const unblockedTasks = [];
|
|
48516
|
+
for (const dep of dependents) {
|
|
48517
|
+
if (dep.status === "done" || dep.status === "cancelled") continue;
|
|
48518
|
+
if (dep.depends?.length) {
|
|
48519
|
+
const depDeps = await acc.loadTasks(dep.depends);
|
|
48520
|
+
const stillUnresolved = depDeps.filter(
|
|
48521
|
+
(d) => d.id !== options.taskId && d.status !== "done" && d.status !== "cancelled"
|
|
48522
|
+
);
|
|
48523
|
+
if (stillUnresolved.length === 0) {
|
|
48524
|
+
unblockedTasks.push({ id: dep.id, title: dep.title });
|
|
48564
48525
|
}
|
|
48565
|
-
|
|
48566
|
-
|
|
48526
|
+
} else {
|
|
48527
|
+
unblockedTasks.push({ id: dep.id, title: dep.title });
|
|
48567
48528
|
}
|
|
48568
48529
|
}
|
|
48530
|
+
Promise.resolve().then(() => (init_auto_extract(), auto_extract_exports)).then(
|
|
48531
|
+
({ extractTaskCompletionMemory: extractTaskCompletionMemory2 }) => extractTaskCompletionMemory2(cwd ?? process.cwd(), task)
|
|
48532
|
+
).catch(() => {
|
|
48533
|
+
});
|
|
48569
48534
|
return {
|
|
48570
|
-
|
|
48571
|
-
|
|
48572
|
-
|
|
48573
|
-
dateRange: earliest && latest ? { earliest, latest } : null,
|
|
48574
|
-
files
|
|
48535
|
+
task,
|
|
48536
|
+
...autoCompleted.length > 0 && { autoCompleted },
|
|
48537
|
+
...unblockedTasks.length > 0 && { unblockedTasks }
|
|
48575
48538
|
};
|
|
48576
48539
|
}
|
|
48577
|
-
var
|
|
48578
|
-
|
|
48540
|
+
var DEFAULT_VERIFICATION_REQUIRED_GATES, VERIFICATION_GATES;
|
|
48541
|
+
var init_complete = __esm({
|
|
48542
|
+
"packages/core/src/tasks/complete.ts"() {
|
|
48579
48543
|
"use strict";
|
|
48580
|
-
|
|
48581
|
-
|
|
48582
|
-
|
|
48583
|
-
|
|
48584
|
-
|
|
48585
|
-
|
|
48586
|
-
|
|
48544
|
+
init_src();
|
|
48545
|
+
init_config();
|
|
48546
|
+
init_errors3();
|
|
48547
|
+
init_session_enforcement();
|
|
48548
|
+
init_data_accessor();
|
|
48549
|
+
init_enforcement();
|
|
48550
|
+
DEFAULT_VERIFICATION_REQUIRED_GATES = [
|
|
48551
|
+
"implemented",
|
|
48552
|
+
"testsPassed",
|
|
48553
|
+
"qaPassed",
|
|
48554
|
+
"securityPassed",
|
|
48555
|
+
"documented"
|
|
48556
|
+
];
|
|
48557
|
+
VERIFICATION_GATES = /* @__PURE__ */ new Set([
|
|
48558
|
+
"implemented",
|
|
48559
|
+
"testsPassed",
|
|
48560
|
+
"qaPassed",
|
|
48561
|
+
"cleanupDone",
|
|
48562
|
+
"securityPassed",
|
|
48563
|
+
"documented"
|
|
48564
|
+
]);
|
|
48587
48565
|
}
|
|
48588
48566
|
});
|
|
48589
48567
|
|
|
48590
|
-
// packages/core/src/
|
|
48591
|
-
|
|
48592
|
-
|
|
48593
|
-
|
|
48594
|
-
|
|
48568
|
+
// packages/core/src/tasks/update.ts
|
|
48569
|
+
var update_exports = {};
|
|
48570
|
+
__export(update_exports, {
|
|
48571
|
+
updateTask: () => updateTask
|
|
48572
|
+
});
|
|
48573
|
+
function hasNonStatusDoneFields(options) {
|
|
48574
|
+
return NON_STATUS_DONE_FIELDS.some((field) => options[field] !== void 0);
|
|
48595
48575
|
}
|
|
48596
|
-
function
|
|
48597
|
-
const
|
|
48598
|
-
const
|
|
48599
|
-
|
|
48600
|
-
|
|
48601
|
-
|
|
48602
|
-
title: task.title,
|
|
48603
|
-
status: task.status,
|
|
48604
|
-
depends: (task.depends ?? []).filter((d) => taskMap.has(d)),
|
|
48605
|
-
dependents: []
|
|
48576
|
+
async function updateTask(options, cwd, accessor) {
|
|
48577
|
+
const acc = accessor ?? await getAccessor(cwd);
|
|
48578
|
+
const task = await acc.loadSingleTask(options.taskId);
|
|
48579
|
+
if (!task) {
|
|
48580
|
+
throw new CleoError(4 /* NOT_FOUND */, `Task not found: ${options.taskId}`, {
|
|
48581
|
+
fix: `Use 'cleo find "${options.taskId}"' to search`
|
|
48606
48582
|
});
|
|
48607
48583
|
}
|
|
48608
|
-
|
|
48609
|
-
|
|
48610
|
-
|
|
48611
|
-
|
|
48612
|
-
|
|
48584
|
+
await requireActiveSession("tasks.update", cwd);
|
|
48585
|
+
const changes = [];
|
|
48586
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
48587
|
+
const isStatusOnlyDoneTransition = options.status === "done" && task.status !== "done" && !hasNonStatusDoneFields(options);
|
|
48588
|
+
if (isStatusOnlyDoneTransition) {
|
|
48589
|
+
const result = await completeTask({ taskId: options.taskId }, cwd, accessor);
|
|
48590
|
+
return { task: result.task, changes: ["status"] };
|
|
48591
|
+
}
|
|
48592
|
+
if (options.status === "done" && task.status !== "done") {
|
|
48593
|
+
throw new CleoError(
|
|
48594
|
+
6 /* VALIDATION_ERROR */,
|
|
48595
|
+
"status=done must use complete flow; do not combine with other update fields",
|
|
48596
|
+
{
|
|
48597
|
+
fix: `Run 'cleo complete ${options.taskId}' first, then apply additional updates with 'cleo update ${options.taskId} ...'`
|
|
48613
48598
|
}
|
|
48614
|
-
|
|
48599
|
+
);
|
|
48615
48600
|
}
|
|
48616
|
-
|
|
48617
|
-
}
|
|
48618
|
-
|
|
48619
|
-
|
|
48620
|
-
|
|
48621
|
-
|
|
48622
|
-
|
|
48623
|
-
|
|
48624
|
-
throw new CleoError(4 /* NOT_FOUND */, `Epic not found: ${epicId}`);
|
|
48625
|
-
}
|
|
48626
|
-
const childIds = new Set(allTasks.filter((t) => t.parentId === epicId).map((t) => t.id));
|
|
48627
|
-
tasks2 = tasks2.filter((t) => childIds.has(t.id) || t.id === epicId);
|
|
48601
|
+
const enforcement = await createAcceptanceEnforcement(cwd);
|
|
48602
|
+
const updateValidation = enforcement.validateUpdate(task, { acceptance: options.acceptance });
|
|
48603
|
+
if (!updateValidation.valid) {
|
|
48604
|
+
throw new CleoError(
|
|
48605
|
+
updateValidation.exitCode ?? 6 /* VALIDATION_ERROR */,
|
|
48606
|
+
updateValidation.error,
|
|
48607
|
+
{ fix: updateValidation.fix }
|
|
48608
|
+
);
|
|
48628
48609
|
}
|
|
48629
|
-
|
|
48630
|
-
|
|
48631
|
-
|
|
48632
|
-
|
|
48633
|
-
|
|
48634
|
-
|
|
48635
|
-
|
|
48636
|
-
|
|
48637
|
-
|
|
48638
|
-
|
|
48639
|
-
|
|
48640
|
-
|
|
48641
|
-
);
|
|
48642
|
-
if (allDepsComplete) {
|
|
48643
|
-
wave.push(id);
|
|
48644
|
-
}
|
|
48645
|
-
}
|
|
48646
|
-
if (wave.length === 0) {
|
|
48647
|
-
wave.push(...remaining);
|
|
48610
|
+
if (options.title !== void 0) {
|
|
48611
|
+
validateTitle(options.title);
|
|
48612
|
+
task.title = options.title;
|
|
48613
|
+
changes.push("title");
|
|
48614
|
+
}
|
|
48615
|
+
if (options.status !== void 0) {
|
|
48616
|
+
validateStatus(options.status);
|
|
48617
|
+
const oldStatus = task.status;
|
|
48618
|
+
task.status = options.status;
|
|
48619
|
+
changes.push("status");
|
|
48620
|
+
if (options.status === "done" && oldStatus !== "done") {
|
|
48621
|
+
task.completedAt = now2;
|
|
48648
48622
|
}
|
|
48649
|
-
|
|
48650
|
-
|
|
48651
|
-
tasks: wave.map((id) => {
|
|
48652
|
-
const node = graph.get(id);
|
|
48653
|
-
return {
|
|
48654
|
-
id,
|
|
48655
|
-
title: node.title,
|
|
48656
|
-
status: node.status,
|
|
48657
|
-
depends: node.depends
|
|
48658
|
-
};
|
|
48659
|
-
})
|
|
48660
|
-
});
|
|
48661
|
-
for (const id of wave) {
|
|
48662
|
-
completed.add(id);
|
|
48663
|
-
remaining.delete(id);
|
|
48623
|
+
if (options.status === "cancelled" && oldStatus !== "cancelled") {
|
|
48624
|
+
task.cancelledAt = now2;
|
|
48664
48625
|
}
|
|
48665
|
-
waveNum++;
|
|
48666
48626
|
}
|
|
48667
|
-
|
|
48668
|
-
|
|
48669
|
-
|
|
48670
|
-
|
|
48671
|
-
const task = allTasks.find((t) => t.id === taskId);
|
|
48672
|
-
if (!task) {
|
|
48673
|
-
throw new CleoError(4 /* NOT_FOUND */, `Task not found: ${taskId}`);
|
|
48627
|
+
if (options.priority !== void 0) {
|
|
48628
|
+
const normalizedPriority = normalizePriority(options.priority);
|
|
48629
|
+
task.priority = normalizedPriority;
|
|
48630
|
+
changes.push("priority");
|
|
48674
48631
|
}
|
|
48675
|
-
|
|
48676
|
-
|
|
48677
|
-
|
|
48678
|
-
|
|
48679
|
-
visited.add(id);
|
|
48680
|
-
const node = graph.get(id);
|
|
48681
|
-
if (!node || node.dependents.length === 0) {
|
|
48682
|
-
return [id];
|
|
48683
|
-
}
|
|
48684
|
-
let longest = [];
|
|
48685
|
-
for (const depId of node.dependents) {
|
|
48686
|
-
const path3 = findLongestPath(depId, new Set(visited));
|
|
48687
|
-
if (path3.length > longest.length) {
|
|
48688
|
-
longest = path3;
|
|
48689
|
-
}
|
|
48690
|
-
}
|
|
48691
|
-
return [id, ...longest];
|
|
48632
|
+
if (options.type !== void 0) {
|
|
48633
|
+
validateTaskType(options.type);
|
|
48634
|
+
task.type = options.type;
|
|
48635
|
+
changes.push("type");
|
|
48692
48636
|
}
|
|
48693
|
-
|
|
48694
|
-
|
|
48695
|
-
|
|
48696
|
-
|
|
48697
|
-
return t ? { id: t.id, title: t.title, status: t.status } : { id, title: "Unknown", status: "unknown" };
|
|
48698
|
-
}),
|
|
48699
|
-
length: path2.length
|
|
48700
|
-
};
|
|
48701
|
-
}
|
|
48702
|
-
var init_deps2 = __esm({
|
|
48703
|
-
"packages/core/src/phases/deps.ts"() {
|
|
48704
|
-
"use strict";
|
|
48705
|
-
init_src();
|
|
48706
|
-
init_errors3();
|
|
48707
|
-
init_data_accessor();
|
|
48637
|
+
if (options.size !== void 0) {
|
|
48638
|
+
validateSize(options.size);
|
|
48639
|
+
task.size = options.size;
|
|
48640
|
+
changes.push("size");
|
|
48708
48641
|
}
|
|
48709
|
-
|
|
48710
|
-
|
|
48711
|
-
|
|
48712
|
-
function buildDependencyGraph(tasks2) {
|
|
48713
|
-
const graph = /* @__PURE__ */ new Map();
|
|
48714
|
-
for (const task of tasks2) {
|
|
48715
|
-
if (!graph.has(task.id)) {
|
|
48716
|
-
graph.set(task.id, /* @__PURE__ */ new Set());
|
|
48717
|
-
}
|
|
48718
|
-
if (task.depends) {
|
|
48719
|
-
for (const dep of task.depends) {
|
|
48720
|
-
graph.get(task.id).add(dep);
|
|
48721
|
-
}
|
|
48722
|
-
}
|
|
48642
|
+
if (options.phase !== void 0) {
|
|
48643
|
+
task.phase = options.phase;
|
|
48644
|
+
changes.push("phase");
|
|
48723
48645
|
}
|
|
48724
|
-
|
|
48725
|
-
|
|
48726
|
-
|
|
48727
|
-
const depGraph = graph ?? buildDependencyGraph(tasks2);
|
|
48728
|
-
const circularDeps = [];
|
|
48729
|
-
const visited = /* @__PURE__ */ new Set();
|
|
48730
|
-
const recursionStack = /* @__PURE__ */ new Set();
|
|
48731
|
-
function dfs(taskId, path2) {
|
|
48732
|
-
visited.add(taskId);
|
|
48733
|
-
recursionStack.add(taskId);
|
|
48734
|
-
const deps = depGraph.get(taskId) || /* @__PURE__ */ new Set();
|
|
48735
|
-
for (const dep of deps) {
|
|
48736
|
-
if (!visited.has(dep)) {
|
|
48737
|
-
dfs(dep, [...path2, taskId]);
|
|
48738
|
-
} else if (recursionStack.has(dep)) {
|
|
48739
|
-
circularDeps.push([...path2, taskId, dep]);
|
|
48740
|
-
}
|
|
48741
|
-
}
|
|
48742
|
-
recursionStack.delete(taskId);
|
|
48646
|
+
if (options.description !== void 0) {
|
|
48647
|
+
task.description = options.description;
|
|
48648
|
+
changes.push("description");
|
|
48743
48649
|
}
|
|
48744
|
-
|
|
48745
|
-
if (
|
|
48746
|
-
|
|
48747
|
-
|
|
48650
|
+
if (options.labels !== void 0) {
|
|
48651
|
+
if (options.labels.length) validateLabels(options.labels);
|
|
48652
|
+
task.labels = options.labels;
|
|
48653
|
+
changes.push("labels");
|
|
48748
48654
|
}
|
|
48749
|
-
|
|
48750
|
-
|
|
48751
|
-
|
|
48752
|
-
|
|
48753
|
-
|
|
48754
|
-
|
|
48755
|
-
if (task.depends) {
|
|
48756
|
-
for (const dep of task.depends) {
|
|
48757
|
-
if (!childIds.has(dep) && !allTasks.find((t) => t.id === dep && t.status === "done")) {
|
|
48758
|
-
missingDeps.push({ taskId: task.id, missingDep: dep });
|
|
48759
|
-
}
|
|
48760
|
-
}
|
|
48761
|
-
}
|
|
48655
|
+
if (options.addLabels?.length) {
|
|
48656
|
+
validateLabels(options.addLabels);
|
|
48657
|
+
const existing = new Set(task.labels ?? []);
|
|
48658
|
+
for (const l of options.addLabels) existing.add(l.trim());
|
|
48659
|
+
task.labels = [...existing];
|
|
48660
|
+
changes.push("labels");
|
|
48762
48661
|
}
|
|
48763
|
-
|
|
48764
|
-
|
|
48765
|
-
|
|
48766
|
-
|
|
48767
|
-
const circularDependencies = detectCircularDependencies(children, graph);
|
|
48768
|
-
const missingDependencies = findMissingDependencies(children, allTasks);
|
|
48769
|
-
const dependencyGraph = {};
|
|
48770
|
-
for (const [key, value] of graph.entries()) {
|
|
48771
|
-
dependencyGraph[key] = Array.from(value);
|
|
48662
|
+
if (options.removeLabels?.length) {
|
|
48663
|
+
const toRemove = new Set(options.removeLabels.map((l) => l.trim()));
|
|
48664
|
+
task.labels = (task.labels ?? []).filter((l) => !toRemove.has(l));
|
|
48665
|
+
changes.push("labels");
|
|
48772
48666
|
}
|
|
48773
|
-
|
|
48774
|
-
|
|
48775
|
-
|
|
48776
|
-
missingDependencies
|
|
48777
|
-
};
|
|
48778
|
-
}
|
|
48779
|
-
var init_analyze = __esm({
|
|
48780
|
-
"packages/core/src/orchestration/analyze.ts"() {
|
|
48781
|
-
"use strict";
|
|
48667
|
+
if (options.depends !== void 0) {
|
|
48668
|
+
task.depends = options.depends;
|
|
48669
|
+
changes.push("depends");
|
|
48782
48670
|
}
|
|
48783
|
-
|
|
48784
|
-
|
|
48785
|
-
|
|
48786
|
-
|
|
48787
|
-
|
|
48788
|
-
const manifestPath = getManifestPath(projectRoot);
|
|
48789
|
-
if (!existsSync57(manifestPath)) {
|
|
48790
|
-
return 0;
|
|
48671
|
+
if (options.addDepends?.length) {
|
|
48672
|
+
const existing = new Set(task.depends ?? []);
|
|
48673
|
+
for (const d of options.addDepends) existing.add(d.trim());
|
|
48674
|
+
task.depends = [...existing];
|
|
48675
|
+
changes.push("depends");
|
|
48791
48676
|
}
|
|
48792
|
-
|
|
48793
|
-
const
|
|
48794
|
-
|
|
48795
|
-
|
|
48796
|
-
return 0;
|
|
48677
|
+
if (options.removeDepends?.length) {
|
|
48678
|
+
const toRemove = new Set(options.removeDepends.map((d) => d.trim()));
|
|
48679
|
+
task.depends = (task.depends ?? []).filter((d) => !toRemove.has(d));
|
|
48680
|
+
changes.push("depends");
|
|
48797
48681
|
}
|
|
48798
|
-
|
|
48799
|
-
|
|
48800
|
-
|
|
48801
|
-
|
|
48802
|
-
|
|
48803
|
-
epicId: epicId || null,
|
|
48804
|
-
taskCount,
|
|
48805
|
-
manifestEntries: manifestEntries2,
|
|
48806
|
-
estimatedTokens,
|
|
48807
|
-
recommendation: estimatedTokens > 5e3 ? "Consider using manifest summaries instead of full task details" : "Context usage is within recommended limits",
|
|
48808
|
-
limits: {
|
|
48809
|
-
orchestratorBudget: 1e4,
|
|
48810
|
-
maxFilesPerAgent: 3,
|
|
48811
|
-
currentUsage: estimatedTokens
|
|
48812
|
-
}
|
|
48813
|
-
};
|
|
48814
|
-
}
|
|
48815
|
-
var init_context2 = __esm({
|
|
48816
|
-
"packages/core/src/orchestration/context.ts"() {
|
|
48817
|
-
"use strict";
|
|
48818
|
-
init_paths();
|
|
48682
|
+
if (options.notes !== void 0) {
|
|
48683
|
+
const timestampedNote = `${(/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace(/\.\d+Z$/, " UTC")}: ${options.notes}`;
|
|
48684
|
+
if (!task.notes) task.notes = [];
|
|
48685
|
+
task.notes.push(timestampedNote);
|
|
48686
|
+
changes.push("notes");
|
|
48819
48687
|
}
|
|
48820
|
-
|
|
48821
|
-
|
|
48822
|
-
|
|
48823
|
-
|
|
48824
|
-
|
|
48825
|
-
|
|
48826
|
-
|
|
48827
|
-
|
|
48688
|
+
if (options.acceptance !== void 0) {
|
|
48689
|
+
task.acceptance = options.acceptance;
|
|
48690
|
+
changes.push("acceptance");
|
|
48691
|
+
}
|
|
48692
|
+
if (options.files !== void 0) {
|
|
48693
|
+
task.files = options.files;
|
|
48694
|
+
changes.push("files");
|
|
48695
|
+
}
|
|
48696
|
+
if (options.blockedBy !== void 0) {
|
|
48697
|
+
task.blockedBy = options.blockedBy;
|
|
48698
|
+
changes.push("blockedBy");
|
|
48699
|
+
}
|
|
48700
|
+
if (options.noAutoComplete !== void 0) {
|
|
48701
|
+
task.noAutoComplete = options.noAutoComplete;
|
|
48702
|
+
changes.push("noAutoComplete");
|
|
48703
|
+
}
|
|
48704
|
+
if (options.pipelineStage !== void 0) {
|
|
48705
|
+
validatePipelineTransition(task.pipelineStage, options.pipelineStage);
|
|
48706
|
+
if (task.type === "epic" && task.pipelineStage) {
|
|
48707
|
+
await validateEpicStageAdvancement(
|
|
48708
|
+
{
|
|
48709
|
+
epicId: task.id,
|
|
48710
|
+
currentStage: task.pipelineStage,
|
|
48711
|
+
newStage: options.pipelineStage
|
|
48712
|
+
},
|
|
48713
|
+
acc,
|
|
48714
|
+
cwd
|
|
48715
|
+
);
|
|
48828
48716
|
}
|
|
48829
|
-
if (task.
|
|
48830
|
-
|
|
48831
|
-
|
|
48717
|
+
if (task.type !== "epic") {
|
|
48718
|
+
const epicAncestor = task.parentId ? await findEpicAncestor(task.parentId, acc) : null;
|
|
48719
|
+
const directParent = task.parentId ? await acc.loadSingleTask(task.parentId) : null;
|
|
48720
|
+
const epicToCheck = directParent?.type === "epic" ? directParent : epicAncestor;
|
|
48721
|
+
if (epicToCheck) {
|
|
48722
|
+
await validateChildStageCeiling(
|
|
48723
|
+
{ childStage: options.pipelineStage, epicId: epicToCheck.id },
|
|
48724
|
+
acc,
|
|
48725
|
+
cwd
|
|
48726
|
+
);
|
|
48832
48727
|
}
|
|
48833
48728
|
}
|
|
48729
|
+
task.pipelineStage = options.pipelineStage;
|
|
48730
|
+
changes.push("pipelineStage");
|
|
48834
48731
|
}
|
|
48835
|
-
|
|
48836
|
-
|
|
48837
|
-
|
|
48838
|
-
|
|
48839
|
-
|
|
48840
|
-
|
|
48841
|
-
|
|
48842
|
-
|
|
48843
|
-
|
|
48732
|
+
if (options.parentId !== void 0) {
|
|
48733
|
+
const newParentId = options.parentId || null;
|
|
48734
|
+
const currentParentId = task.parentId ?? null;
|
|
48735
|
+
if (newParentId !== currentParentId) {
|
|
48736
|
+
const originalType = task.type;
|
|
48737
|
+
if (!newParentId) {
|
|
48738
|
+
task.parentId = null;
|
|
48739
|
+
if (task.type === "subtask") task.type = "task";
|
|
48740
|
+
changes.push("parentId");
|
|
48741
|
+
if (task.type !== originalType) changes.push("type");
|
|
48742
|
+
} else {
|
|
48743
|
+
const newParent = await acc.loadSingleTask(newParentId);
|
|
48744
|
+
if (!newParent) {
|
|
48745
|
+
throw new CleoError(10 /* PARENT_NOT_FOUND */, `Parent task ${newParentId} not found`);
|
|
48746
|
+
}
|
|
48747
|
+
if (newParent.type === "subtask") {
|
|
48748
|
+
throw new CleoError(
|
|
48749
|
+
13 /* INVALID_PARENT_TYPE */,
|
|
48750
|
+
`Cannot parent under subtask '${newParentId}'`
|
|
48751
|
+
);
|
|
48752
|
+
}
|
|
48753
|
+
const subtree = await acc.getSubtree(options.taskId);
|
|
48754
|
+
if (subtree.some((t) => t.id === newParentId)) {
|
|
48755
|
+
throw new CleoError(
|
|
48756
|
+
14 /* CIRCULAR_REFERENCE */,
|
|
48757
|
+
`Moving '${options.taskId}' under '${newParentId}' would create a circular reference`
|
|
48758
|
+
);
|
|
48759
|
+
}
|
|
48760
|
+
const ancestors = await acc.getAncestorChain(newParentId);
|
|
48761
|
+
const parentDepth = ancestors.length;
|
|
48762
|
+
const config2 = await loadConfig(cwd);
|
|
48763
|
+
const policy = resolveHierarchyPolicy(config2);
|
|
48764
|
+
if (parentDepth + 1 >= policy.maxDepth) {
|
|
48765
|
+
throw new CleoError(
|
|
48766
|
+
11 /* DEPTH_EXCEEDED */,
|
|
48767
|
+
`Maximum nesting depth ${policy.maxDepth} would be exceeded`
|
|
48768
|
+
);
|
|
48769
|
+
}
|
|
48770
|
+
task.parentId = newParentId;
|
|
48771
|
+
const newDepth = parentDepth + 1;
|
|
48772
|
+
if (newDepth === 1) task.type = "task";
|
|
48773
|
+
else if (newDepth >= 2) task.type = "subtask";
|
|
48774
|
+
changes.push("parentId");
|
|
48775
|
+
if (task.type !== originalType) changes.push("type");
|
|
48776
|
+
}
|
|
48844
48777
|
}
|
|
48845
48778
|
}
|
|
48846
|
-
|
|
48847
|
-
|
|
48848
|
-
const maxWaves = 50;
|
|
48849
|
-
while (remaining.length > 0 && waveNumber <= maxWaves) {
|
|
48850
|
-
const waveTasks = remaining.filter((t) => {
|
|
48851
|
-
const deps = graph.get(t.id) || /* @__PURE__ */ new Set();
|
|
48852
|
-
return Array.from(deps).every((d) => completed.has(d));
|
|
48853
|
-
});
|
|
48854
|
-
if (waveTasks.length === 0) break;
|
|
48855
|
-
waves.push({
|
|
48856
|
-
waveNumber,
|
|
48857
|
-
tasks: waveTasks.map((t) => t.id),
|
|
48858
|
-
status: waveTasks.every((t) => completed.has(t.id)) ? "completed" : waveTasks.some((t) => t.status === "active") ? "in_progress" : "pending"
|
|
48859
|
-
});
|
|
48860
|
-
for (const t of waveTasks) {
|
|
48861
|
-
completed.add(t.id);
|
|
48862
|
-
}
|
|
48863
|
-
remaining = remaining.filter((t) => !waveTasks.some((wt) => wt.id === t.id));
|
|
48864
|
-
waveNumber++;
|
|
48779
|
+
if (changes.length === 0) {
|
|
48780
|
+
throw new CleoError(102 /* NO_CHANGE */, "No changes specified");
|
|
48865
48781
|
}
|
|
48866
|
-
|
|
48867
|
-
|
|
48868
|
-
|
|
48869
|
-
|
|
48870
|
-
|
|
48782
|
+
task.updatedAt = now2;
|
|
48783
|
+
await acc.transaction(async (tx) => {
|
|
48784
|
+
await tx.upsertSingleTask(task);
|
|
48785
|
+
await tx.appendLog({
|
|
48786
|
+
id: `log-${Math.floor(Date.now() / 1e3)}-${(await import("node:crypto")).randomBytes(3).toString("hex")}`,
|
|
48787
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
48788
|
+
action: "task_updated",
|
|
48789
|
+
taskId: options.taskId,
|
|
48790
|
+
actor: "system",
|
|
48791
|
+
details: { changes, title: task.title },
|
|
48792
|
+
before: null,
|
|
48793
|
+
after: { changes, title: task.title }
|
|
48871
48794
|
});
|
|
48872
|
-
}
|
|
48873
|
-
return
|
|
48874
|
-
}
|
|
48875
|
-
async function getEnrichedWaves(epicId, cwd, accessor) {
|
|
48876
|
-
const acc = accessor ?? await getAccessor(cwd);
|
|
48877
|
-
const children = await acc.getChildren(epicId);
|
|
48878
|
-
const waves = computeWaves(children);
|
|
48879
|
-
const taskMap = new Map(children.map((t) => [t.id, t]));
|
|
48880
|
-
const enrichedWaves = waves.map((w) => ({
|
|
48881
|
-
...w,
|
|
48882
|
-
tasks: w.tasks.map((id) => ({
|
|
48883
|
-
id,
|
|
48884
|
-
title: taskMap.get(id)?.title || id,
|
|
48885
|
-
status: taskMap.get(id)?.status || "unknown"
|
|
48886
|
-
}))
|
|
48887
|
-
}));
|
|
48888
|
-
return {
|
|
48889
|
-
epicId,
|
|
48890
|
-
waves: enrichedWaves,
|
|
48891
|
-
totalWaves: waves.length,
|
|
48892
|
-
totalTasks: children.length
|
|
48893
|
-
};
|
|
48795
|
+
});
|
|
48796
|
+
return { task, changes };
|
|
48894
48797
|
}
|
|
48895
|
-
var
|
|
48896
|
-
|
|
48798
|
+
var NON_STATUS_DONE_FIELDS;
|
|
48799
|
+
var init_update2 = __esm({
|
|
48800
|
+
"packages/core/src/tasks/update.ts"() {
|
|
48897
48801
|
"use strict";
|
|
48802
|
+
init_src();
|
|
48803
|
+
init_config();
|
|
48804
|
+
init_errors3();
|
|
48805
|
+
init_session_enforcement();
|
|
48898
48806
|
init_data_accessor();
|
|
48807
|
+
init_add();
|
|
48808
|
+
init_complete();
|
|
48809
|
+
init_enforcement();
|
|
48810
|
+
init_epic_enforcement();
|
|
48811
|
+
init_hierarchy_policy();
|
|
48812
|
+
init_pipeline_stage();
|
|
48813
|
+
NON_STATUS_DONE_FIELDS = [
|
|
48814
|
+
"title",
|
|
48815
|
+
"priority",
|
|
48816
|
+
"type",
|
|
48817
|
+
"size",
|
|
48818
|
+
"phase",
|
|
48819
|
+
"description",
|
|
48820
|
+
"labels",
|
|
48821
|
+
"addLabels",
|
|
48822
|
+
"removeLabels",
|
|
48823
|
+
"depends",
|
|
48824
|
+
"addDepends",
|
|
48825
|
+
"removeDepends",
|
|
48826
|
+
"notes",
|
|
48827
|
+
"acceptance",
|
|
48828
|
+
"files",
|
|
48829
|
+
"blockedBy",
|
|
48830
|
+
"parentId",
|
|
48831
|
+
"noAutoComplete",
|
|
48832
|
+
"pipelineStage"
|
|
48833
|
+
];
|
|
48899
48834
|
}
|
|
48900
48835
|
});
|
|
48901
48836
|
|
|
48902
|
-
// packages/core/src/
|
|
48903
|
-
function
|
|
48904
|
-
|
|
48905
|
-
|
|
48906
|
-
|
|
48907
|
-
|
|
48908
|
-
|
|
48909
|
-
cancelled: tasks2.filter((t) => t.status === "cancelled").length
|
|
48910
|
-
};
|
|
48911
|
-
}
|
|
48912
|
-
function computeEpicStatus(epicId, epicTitle, children) {
|
|
48913
|
-
const waves = computeWaves(children);
|
|
48914
|
-
const byStatus = countByStatus(children);
|
|
48915
|
-
return {
|
|
48916
|
-
epicId,
|
|
48917
|
-
epicTitle,
|
|
48918
|
-
totalTasks: children.length,
|
|
48919
|
-
byStatus,
|
|
48920
|
-
waves: waves.length,
|
|
48921
|
-
currentWave: waves.find((w) => w.status !== "completed")?.waveNumber || null
|
|
48922
|
-
};
|
|
48923
|
-
}
|
|
48924
|
-
function computeOverallStatus(tasks2) {
|
|
48925
|
-
const epics = tasks2.filter(
|
|
48926
|
-
(t) => !t.parentId && (t.type === "epic" || tasks2.some((c) => c.parentId === t.id))
|
|
48927
|
-
);
|
|
48928
|
-
return {
|
|
48929
|
-
totalEpics: epics.length,
|
|
48930
|
-
totalTasks: tasks2.length,
|
|
48931
|
-
byStatus: {
|
|
48932
|
-
pending: tasks2.filter((t) => t.status === "pending").length,
|
|
48933
|
-
active: tasks2.filter((t) => t.status === "active").length,
|
|
48934
|
-
blocked: tasks2.filter((t) => t.status === "blocked").length,
|
|
48935
|
-
done: tasks2.filter((t) => t.status === "done").length
|
|
48936
|
-
}
|
|
48937
|
-
};
|
|
48938
|
-
}
|
|
48939
|
-
function computeProgress(tasks2) {
|
|
48940
|
-
const total = tasks2.length;
|
|
48941
|
-
const done = tasks2.filter((t) => t.status === "done").length;
|
|
48942
|
-
const pending = tasks2.filter((t) => t.status === "pending").length;
|
|
48943
|
-
const blocked = tasks2.filter((t) => t.status === "blocked").length;
|
|
48944
|
-
const active = tasks2.filter((t) => t.status === "active").length;
|
|
48945
|
-
return {
|
|
48946
|
-
total,
|
|
48947
|
-
done,
|
|
48948
|
-
pending,
|
|
48949
|
-
blocked,
|
|
48950
|
-
active,
|
|
48951
|
-
percentComplete: total > 0 ? Math.round(done / total * 100) : 0
|
|
48952
|
-
};
|
|
48953
|
-
}
|
|
48954
|
-
function computeStartupSummary(epicId, epicTitle, children, readyCount) {
|
|
48955
|
-
const waves = computeWaves(children);
|
|
48956
|
-
const byStatus = countByStatus(children);
|
|
48957
|
-
return {
|
|
48958
|
-
epicId,
|
|
48959
|
-
epicTitle,
|
|
48960
|
-
initialized: true,
|
|
48961
|
-
summary: {
|
|
48962
|
-
totalTasks: children.length,
|
|
48963
|
-
totalWaves: waves.length,
|
|
48964
|
-
readyTasks: readyCount,
|
|
48965
|
-
byStatus
|
|
48966
|
-
},
|
|
48967
|
-
firstWave: waves[0] || null
|
|
48968
|
-
};
|
|
48969
|
-
}
|
|
48970
|
-
var init_status = __esm({
|
|
48971
|
-
"packages/core/src/orchestration/status.ts"() {
|
|
48972
|
-
"use strict";
|
|
48973
|
-
init_waves();
|
|
48974
|
-
}
|
|
48975
|
-
});
|
|
48976
|
-
|
|
48977
|
-
// packages/core/src/orchestration/index.ts
|
|
48978
|
-
var orchestration_exports = {};
|
|
48979
|
-
__export(orchestration_exports, {
|
|
48980
|
-
analyzeDependencies: () => analyzeDependencies,
|
|
48981
|
-
analyzeEpic: () => analyzeEpic,
|
|
48982
|
-
autoDispatch: () => autoDispatch,
|
|
48983
|
-
buildDependencyGraph: () => buildDependencyGraph,
|
|
48984
|
-
computeEpicStatus: () => computeEpicStatus,
|
|
48985
|
-
computeOverallStatus: () => computeOverallStatus,
|
|
48986
|
-
computeProgress: () => computeProgress,
|
|
48987
|
-
computeStartupSummary: () => computeStartupSummary,
|
|
48988
|
-
countByStatus: () => countByStatus,
|
|
48989
|
-
countManifestEntries: () => countManifestEntries,
|
|
48990
|
-
detectCircularDependencies: () => detectCircularDependencies,
|
|
48991
|
-
estimateContext: () => estimateContext,
|
|
48992
|
-
findMissingDependencies: () => findMissingDependencies,
|
|
48993
|
-
getNextTask: () => getNextTask,
|
|
48994
|
-
getOrchestratorContext: () => getOrchestratorContext,
|
|
48995
|
-
getReadyTasks: () => getReadyTasks2,
|
|
48996
|
-
prepareSpawn: () => prepareSpawn,
|
|
48997
|
-
resolveTokens: () => resolveTokens,
|
|
48998
|
-
startOrchestration: () => startOrchestration,
|
|
48999
|
-
validateSpawnOutput: () => validateSpawnOutput
|
|
49000
|
-
});
|
|
49001
|
-
async function startOrchestration(epicId, _cwd, accessor) {
|
|
49002
|
-
const epic = await accessor.loadSingleTask(epicId);
|
|
49003
|
-
if (!epic) {
|
|
49004
|
-
throw new CleoError(4 /* NOT_FOUND */, `Epic not found: ${epicId}`);
|
|
48837
|
+
// packages/core/src/nexus/workspace.ts
|
|
48838
|
+
function checkRateLimit(agentId) {
|
|
48839
|
+
const now2 = Date.now();
|
|
48840
|
+
const entry = rateLimitCounters.get(agentId);
|
|
48841
|
+
if (!entry || now2 - entry.windowStart > RATE_LIMIT_WINDOW_MS) {
|
|
48842
|
+
rateLimitCounters.set(agentId, { count: 1, windowStart: now2 });
|
|
48843
|
+
return;
|
|
49005
48844
|
}
|
|
49006
|
-
|
|
48845
|
+
entry.count++;
|
|
48846
|
+
if (entry.count > RATE_LIMIT_MAX_OPS) {
|
|
49007
48847
|
throw new CleoError(
|
|
49008
|
-
|
|
49009
|
-
`
|
|
48848
|
+
1 /* GENERAL_ERROR */,
|
|
48849
|
+
`Agent '${agentId}' exceeded rate limit: ${RATE_LIMIT_MAX_OPS} routing ops per ${RATE_LIMIT_WINDOW_MS / 1e3}s`
|
|
49010
48850
|
);
|
|
49011
48851
|
}
|
|
49012
|
-
const session = {
|
|
49013
|
-
epicId,
|
|
49014
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
49015
|
-
status: "active",
|
|
49016
|
-
currentWave: 1,
|
|
49017
|
-
completedTasks: [],
|
|
49018
|
-
spawnedAgents: []
|
|
49019
|
-
};
|
|
49020
|
-
return session;
|
|
49021
48852
|
}
|
|
49022
|
-
async function
|
|
49023
|
-
|
|
49024
|
-
|
|
49025
|
-
|
|
48853
|
+
async function loadProjectACL(projectPath) {
|
|
48854
|
+
try {
|
|
48855
|
+
const { loadConfig: loadConfig4 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
48856
|
+
const config2 = await loadConfig4(projectPath);
|
|
48857
|
+
const agents = config2?.authorizedAgents;
|
|
48858
|
+
if (Array.isArray(agents) && agents.length > 0) {
|
|
48859
|
+
return { authorizedAgents: agents };
|
|
48860
|
+
}
|
|
48861
|
+
} catch {
|
|
49026
48862
|
}
|
|
49027
|
-
|
|
49028
|
-
const waves = await getExecutionWaves(epicId, cwd, accessor);
|
|
49029
|
-
const completedTasks = childTasks.filter((t) => t.status === "done").map((t) => t.id);
|
|
49030
|
-
const blockedTasks = childTasks.filter((t) => t.status === "blocked").map((t) => t.id);
|
|
49031
|
-
const completedSet = new Set(completedTasks);
|
|
49032
|
-
const readyTasks = childTasks.filter((t) => {
|
|
49033
|
-
if (t.status === "done" || t.status === "cancelled") return false;
|
|
49034
|
-
const deps = t.depends ?? [];
|
|
49035
|
-
return deps.every((d) => completedSet.has(d));
|
|
49036
|
-
}).map((t) => t.id);
|
|
49037
|
-
return {
|
|
49038
|
-
epicId,
|
|
49039
|
-
totalTasks: childTasks.length,
|
|
49040
|
-
waves: waves.map((w) => ({
|
|
49041
|
-
wave: w.wave,
|
|
49042
|
-
tasks: w.tasks.map((t) => ({ id: t.id, title: t.title, status: t.status }))
|
|
49043
|
-
})),
|
|
49044
|
-
readyTasks,
|
|
49045
|
-
blockedTasks,
|
|
49046
|
-
completedTasks
|
|
49047
|
-
};
|
|
49048
|
-
}
|
|
49049
|
-
async function getReadyTasks2(epicId, _cwd, accessor) {
|
|
49050
|
-
const childTasks = await accessor.getChildren(epicId);
|
|
49051
|
-
const { tasks: allTasks } = await accessor.queryTasks({});
|
|
49052
|
-
const completedIds = new Set(allTasks.filter((t) => t.status === "done").map((t) => t.id));
|
|
49053
|
-
return childTasks.filter((t) => t.status !== "done" && t.status !== "cancelled").map((task) => {
|
|
49054
|
-
const deps = task.depends ?? [];
|
|
49055
|
-
const unmetDeps = deps.filter((d) => !completedIds.has(d));
|
|
49056
|
-
const protocol = autoDispatch(task);
|
|
49057
|
-
return {
|
|
49058
|
-
taskId: task.id,
|
|
49059
|
-
title: task.title,
|
|
49060
|
-
ready: unmetDeps.length === 0,
|
|
49061
|
-
blockers: unmetDeps,
|
|
49062
|
-
protocol
|
|
49063
|
-
};
|
|
49064
|
-
});
|
|
48863
|
+
return DEFAULT_ACL;
|
|
49065
48864
|
}
|
|
49066
|
-
|
|
49067
|
-
|
|
49068
|
-
|
|
49069
|
-
if (ready.length === 0) return null;
|
|
49070
|
-
return ready[0];
|
|
48865
|
+
function isAuthorized(acl, agentId) {
|
|
48866
|
+
if (acl.authorizedAgents.includes("*")) return true;
|
|
48867
|
+
return acl.authorizedAgents.includes(agentId);
|
|
49071
48868
|
}
|
|
49072
|
-
|
|
49073
|
-
const
|
|
49074
|
-
|
|
49075
|
-
|
|
48869
|
+
function parseDirective(message) {
|
|
48870
|
+
const content = message.content;
|
|
48871
|
+
const verbMatch = content.match(/^\/(\w+)/);
|
|
48872
|
+
if (!verbMatch) return null;
|
|
48873
|
+
const verb = verbMatch[1];
|
|
48874
|
+
const taskRefs = [];
|
|
48875
|
+
const pattern = new RegExp(TASK_REF_PATTERN.source, "g");
|
|
48876
|
+
for (const m of content.matchAll(pattern)) {
|
|
48877
|
+
taskRefs.push(`T${m[1]}`);
|
|
49076
48878
|
}
|
|
49077
|
-
const
|
|
49078
|
-
|
|
49079
|
-
|
|
49080
|
-
|
|
49081
|
-
|
|
49082
|
-
|
|
49083
|
-
prompt,
|
|
49084
|
-
tokenResolution: {
|
|
49085
|
-
fullyResolved: unresolvedTokens.length === 0,
|
|
49086
|
-
unresolvedTokens
|
|
48879
|
+
const metaRefs = message.metadata?.taskRefs;
|
|
48880
|
+
if (Array.isArray(metaRefs)) {
|
|
48881
|
+
for (const ref of metaRefs) {
|
|
48882
|
+
if (typeof ref === "string" && !taskRefs.includes(ref)) {
|
|
48883
|
+
taskRefs.push(ref);
|
|
48884
|
+
}
|
|
49087
48885
|
}
|
|
48886
|
+
}
|
|
48887
|
+
if (taskRefs.length === 0) return null;
|
|
48888
|
+
return {
|
|
48889
|
+
verb,
|
|
48890
|
+
taskRefs,
|
|
48891
|
+
agentId: message.from,
|
|
48892
|
+
messageId: message.id,
|
|
48893
|
+
timestamp: message.timestamp
|
|
49088
48894
|
};
|
|
49089
48895
|
}
|
|
49090
|
-
async function
|
|
49091
|
-
|
|
49092
|
-
|
|
49093
|
-
|
|
49094
|
-
|
|
49095
|
-
|
|
49096
|
-
errors.push("No manifest entry appended");
|
|
48896
|
+
async function routeDirective(directive) {
|
|
48897
|
+
checkRateLimit(directive.agentId);
|
|
48898
|
+
const results = [];
|
|
48899
|
+
const operation = VERB_TO_OPERATION[directive.verb];
|
|
48900
|
+
if (!operation) {
|
|
48901
|
+
return results;
|
|
49097
48902
|
}
|
|
49098
|
-
|
|
49099
|
-
|
|
49100
|
-
|
|
49101
|
-
|
|
49102
|
-
if (!epic) {
|
|
49103
|
-
throw new CleoError(4 /* NOT_FOUND */, `Epic not found: ${epicId}`);
|
|
48903
|
+
const projects = await nexusList();
|
|
48904
|
+
for (const taskRef of directive.taskRefs) {
|
|
48905
|
+
const result = await routeSingleTask(taskRef, directive, operation, projects);
|
|
48906
|
+
results.push(result);
|
|
49104
48907
|
}
|
|
49105
|
-
|
|
49106
|
-
const completed = children.filter((t) => t.status === "done").length;
|
|
49107
|
-
const inProgress = children.filter((t) => t.status === "active").length;
|
|
49108
|
-
const blocked = children.filter((t) => t.status === "blocked").length;
|
|
49109
|
-
const pending = children.filter((t) => t.status === "pending").length;
|
|
49110
|
-
return {
|
|
49111
|
-
epicId,
|
|
49112
|
-
epicTitle: epic.title,
|
|
49113
|
-
totalTasks: children.length,
|
|
49114
|
-
completed,
|
|
49115
|
-
inProgress,
|
|
49116
|
-
blocked,
|
|
49117
|
-
pending,
|
|
49118
|
-
completionPercent: children.length > 0 ? Math.floor(completed * 100 / children.length) : 0
|
|
49119
|
-
};
|
|
48908
|
+
return results;
|
|
49120
48909
|
}
|
|
49121
|
-
function
|
|
49122
|
-
|
|
49123
|
-
|
|
49124
|
-
|
|
49125
|
-
|
|
48910
|
+
async function routeSingleTask(taskId, directive, operation, projects) {
|
|
48911
|
+
let targetProject = null;
|
|
48912
|
+
let targetAccessor = null;
|
|
48913
|
+
for (const project of projects) {
|
|
48914
|
+
try {
|
|
48915
|
+
const acc = await getAccessor(project.path);
|
|
48916
|
+
const { tasks: tasks2 } = await acc.queryTasks({});
|
|
48917
|
+
const task = tasks2.find((t) => t.id === taskId);
|
|
48918
|
+
if (task) {
|
|
48919
|
+
targetProject = project;
|
|
48920
|
+
targetAccessor = acc;
|
|
48921
|
+
break;
|
|
49126
48922
|
}
|
|
48923
|
+
} catch {
|
|
49127
48924
|
}
|
|
49128
48925
|
}
|
|
49129
|
-
if (
|
|
49130
|
-
|
|
49131
|
-
|
|
49132
|
-
|
|
49133
|
-
|
|
49134
|
-
|
|
48926
|
+
if (!targetProject || !targetAccessor) {
|
|
48927
|
+
return {
|
|
48928
|
+
success: false,
|
|
48929
|
+
project: "unknown",
|
|
48930
|
+
projectPath: "",
|
|
48931
|
+
taskId,
|
|
48932
|
+
operation,
|
|
48933
|
+
error: `Task ${taskId} not found in any registered project`
|
|
48934
|
+
};
|
|
48935
|
+
}
|
|
48936
|
+
const acl = await loadProjectACL(targetProject.path);
|
|
48937
|
+
if (!isAuthorized(acl, directive.agentId)) {
|
|
48938
|
+
return {
|
|
48939
|
+
success: false,
|
|
48940
|
+
project: targetProject.name,
|
|
48941
|
+
projectPath: targetProject.path,
|
|
48942
|
+
taskId,
|
|
48943
|
+
operation,
|
|
48944
|
+
error: `Agent '${directive.agentId}' not authorized to mutate project '${targetProject.name}'`
|
|
48945
|
+
};
|
|
48946
|
+
}
|
|
48947
|
+
try {
|
|
48948
|
+
await executeOperation(operation, taskId, targetProject.path, targetAccessor, directive);
|
|
48949
|
+
await logRouteAudit(directive, targetProject.name, taskId, operation, true);
|
|
48950
|
+
return {
|
|
48951
|
+
success: true,
|
|
48952
|
+
project: targetProject.name,
|
|
48953
|
+
projectPath: targetProject.path,
|
|
48954
|
+
taskId,
|
|
48955
|
+
operation
|
|
48956
|
+
};
|
|
48957
|
+
} catch (err) {
|
|
48958
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
48959
|
+
await logRouteAudit(directive, targetProject.name, taskId, operation, false, errorMsg);
|
|
48960
|
+
return {
|
|
48961
|
+
success: false,
|
|
48962
|
+
project: targetProject.name,
|
|
48963
|
+
projectPath: targetProject.path,
|
|
48964
|
+
taskId,
|
|
48965
|
+
operation,
|
|
48966
|
+
error: errorMsg
|
|
48967
|
+
};
|
|
49135
48968
|
}
|
|
49136
|
-
return "implementation";
|
|
49137
48969
|
}
|
|
49138
|
-
function
|
|
49139
|
-
|
|
49140
|
-
|
|
49141
|
-
|
|
49142
|
-
|
|
49143
|
-
|
|
49144
|
-
|
|
49145
|
-
|
|
49146
|
-
|
|
49147
|
-
|
|
49148
|
-
|
|
49149
|
-
|
|
49150
|
-
|
|
49151
|
-
|
|
49152
|
-
|
|
49153
|
-
|
|
49154
|
-
|
|
49155
|
-
|
|
49156
|
-
|
|
49157
|
-
|
|
49158
|
-
|
|
49159
|
-
|
|
49160
|
-
|
|
48970
|
+
async function executeOperation(operation, taskId, projectPath, accessor, directive) {
|
|
48971
|
+
switch (operation) {
|
|
48972
|
+
case "tasks.start": {
|
|
48973
|
+
const { startTask: startTask3 } = await Promise.resolve().then(() => (init_task_work(), task_work_exports));
|
|
48974
|
+
await startTask3(taskId, projectPath, accessor);
|
|
48975
|
+
break;
|
|
48976
|
+
}
|
|
48977
|
+
case "tasks.complete": {
|
|
48978
|
+
const { completeTask: completeTask2 } = await Promise.resolve().then(() => (init_complete(), complete_exports));
|
|
48979
|
+
await completeTask2(
|
|
48980
|
+
{ taskId, notes: `Completed via Conduit directive from ${directive.agentId}` },
|
|
48981
|
+
projectPath,
|
|
48982
|
+
accessor
|
|
48983
|
+
);
|
|
48984
|
+
break;
|
|
48985
|
+
}
|
|
48986
|
+
case "tasks.stop": {
|
|
48987
|
+
const { stopTask: stopTask3 } = await Promise.resolve().then(() => (init_task_work(), task_work_exports));
|
|
48988
|
+
await stopTask3(projectPath, accessor);
|
|
48989
|
+
break;
|
|
48990
|
+
}
|
|
48991
|
+
case "tasks.update": {
|
|
48992
|
+
const { updateTask: updateTask3 } = await Promise.resolve().then(() => (init_update2(), update_exports));
|
|
48993
|
+
await updateTask3(
|
|
48994
|
+
{ taskId, notes: `Marked blocked via Conduit directive from ${directive.agentId}` },
|
|
48995
|
+
projectPath,
|
|
48996
|
+
accessor
|
|
48997
|
+
);
|
|
48998
|
+
break;
|
|
48999
|
+
}
|
|
49000
|
+
}
|
|
49161
49001
|
}
|
|
49162
|
-
function
|
|
49163
|
-
|
|
49164
|
-
|
|
49165
|
-
|
|
49166
|
-
|
|
49002
|
+
async function logRouteAudit(directive, projectName, taskId, operation, success2, error40) {
|
|
49003
|
+
try {
|
|
49004
|
+
const { getLogger: getLogger2 } = await Promise.resolve().then(() => (init_logger(), logger_exports));
|
|
49005
|
+
const log11 = getLogger2("nexus.route");
|
|
49006
|
+
const level = success2 ? "info" : "warn";
|
|
49007
|
+
log11[level](
|
|
49008
|
+
{
|
|
49009
|
+
directive: directive.verb,
|
|
49010
|
+
agentId: directive.agentId,
|
|
49011
|
+
messageId: directive.messageId,
|
|
49012
|
+
project: projectName,
|
|
49013
|
+
taskId,
|
|
49014
|
+
operation,
|
|
49015
|
+
success: success2,
|
|
49016
|
+
error: error40
|
|
49017
|
+
},
|
|
49018
|
+
`Conduit directive routed: ${directive.verb} ${taskId} \u2192 ${projectName} (${success2 ? "OK" : "FAILED"})`
|
|
49019
|
+
);
|
|
49020
|
+
} catch {
|
|
49167
49021
|
}
|
|
49168
|
-
|
|
49169
|
-
|
|
49170
|
-
|
|
49022
|
+
}
|
|
49023
|
+
async function workspaceStatus() {
|
|
49024
|
+
const projects = await nexusList();
|
|
49025
|
+
const summaries = [];
|
|
49026
|
+
const totals = { pending: 0, active: 0, done: 0, total: 0 };
|
|
49027
|
+
for (const project of projects) {
|
|
49028
|
+
try {
|
|
49029
|
+
const acc = await getAccessor(project.path);
|
|
49030
|
+
const { tasks: tasks2 } = await acc.queryTasks({});
|
|
49031
|
+
const counts2 = {
|
|
49032
|
+
pending: tasks2.filter((t) => t.status === "pending").length,
|
|
49033
|
+
active: tasks2.filter((t) => t.status === "active").length,
|
|
49034
|
+
done: tasks2.filter((t) => t.status === "done").length,
|
|
49035
|
+
total: tasks2.length
|
|
49036
|
+
};
|
|
49037
|
+
summaries.push({
|
|
49038
|
+
name: project.name,
|
|
49039
|
+
path: project.path,
|
|
49040
|
+
counts: counts2,
|
|
49041
|
+
health: project.healthStatus,
|
|
49042
|
+
lastSync: project.lastSync
|
|
49043
|
+
});
|
|
49044
|
+
totals.pending += counts2.pending;
|
|
49045
|
+
totals.active += counts2.active;
|
|
49046
|
+
totals.done += counts2.done;
|
|
49047
|
+
totals.total += counts2.total;
|
|
49048
|
+
} catch {
|
|
49049
|
+
summaries.push({
|
|
49050
|
+
name: project.name,
|
|
49051
|
+
path: project.path,
|
|
49052
|
+
counts: { pending: 0, active: 0, done: 0, total: 0 },
|
|
49053
|
+
health: "unreachable",
|
|
49054
|
+
lastSync: project.lastSync
|
|
49055
|
+
});
|
|
49056
|
+
}
|
|
49171
49057
|
}
|
|
49172
|
-
return
|
|
49058
|
+
return {
|
|
49059
|
+
projectCount: projects.length,
|
|
49060
|
+
projects: summaries,
|
|
49061
|
+
totals,
|
|
49062
|
+
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
49063
|
+
};
|
|
49173
49064
|
}
|
|
49174
|
-
function
|
|
49175
|
-
|
|
49176
|
-
const
|
|
49177
|
-
|
|
49178
|
-
|
|
49179
|
-
|
|
49065
|
+
async function workspaceAgents() {
|
|
49066
|
+
const projects = await nexusList();
|
|
49067
|
+
const agents = [];
|
|
49068
|
+
for (const project of projects) {
|
|
49069
|
+
try {
|
|
49070
|
+
const { listAgentInstances: listAgentInstances2 } = await Promise.resolve().then(() => (init_registry2(), registry_exports2));
|
|
49071
|
+
const instances = await listAgentInstances2(void 0, project.path);
|
|
49072
|
+
for (const inst of instances) {
|
|
49073
|
+
agents.push({
|
|
49074
|
+
agentId: inst.id,
|
|
49075
|
+
agentType: inst.agentType,
|
|
49076
|
+
status: inst.status,
|
|
49077
|
+
project: project.name,
|
|
49078
|
+
taskId: inst.taskId ?? null,
|
|
49079
|
+
lastHeartbeat: inst.lastHeartbeat
|
|
49080
|
+
});
|
|
49081
|
+
}
|
|
49082
|
+
} catch {
|
|
49180
49083
|
}
|
|
49181
|
-
|
|
49182
|
-
|
|
49183
|
-
});
|
|
49184
|
-
return { resolved, unresolved };
|
|
49084
|
+
}
|
|
49085
|
+
return agents;
|
|
49185
49086
|
}
|
|
49186
|
-
var
|
|
49187
|
-
var
|
|
49188
|
-
"packages/core/src/
|
|
49087
|
+
var RATE_LIMIT_WINDOW_MS, RATE_LIMIT_MAX_OPS, rateLimitCounters, DEFAULT_ACL, TASK_REF_PATTERN, VERB_TO_OPERATION;
|
|
49088
|
+
var init_workspace = __esm({
|
|
49089
|
+
"packages/core/src/nexus/workspace.ts"() {
|
|
49189
49090
|
"use strict";
|
|
49190
49091
|
init_src();
|
|
49191
49092
|
init_errors3();
|
|
49192
|
-
|
|
49193
|
-
|
|
49194
|
-
|
|
49195
|
-
|
|
49196
|
-
|
|
49197
|
-
|
|
49198
|
-
|
|
49199
|
-
|
|
49200
|
-
|
|
49201
|
-
|
|
49202
|
-
|
|
49203
|
-
|
|
49204
|
-
|
|
49205
|
-
|
|
49206
|
-
|
|
49207
|
-
|
|
49208
|
-
|
|
49209
|
-
|
|
49210
|
-
|
|
49093
|
+
init_data_accessor();
|
|
49094
|
+
init_registry3();
|
|
49095
|
+
RATE_LIMIT_WINDOW_MS = 6e4;
|
|
49096
|
+
RATE_LIMIT_MAX_OPS = 100;
|
|
49097
|
+
rateLimitCounters = /* @__PURE__ */ new Map();
|
|
49098
|
+
DEFAULT_ACL = { authorizedAgents: ["*"] };
|
|
49099
|
+
TASK_REF_PATTERN = /\bT(\d+)\b/g;
|
|
49100
|
+
VERB_TO_OPERATION = {
|
|
49101
|
+
claim: "tasks.start",
|
|
49102
|
+
done: "tasks.complete",
|
|
49103
|
+
complete: "tasks.complete",
|
|
49104
|
+
blocked: "tasks.update",
|
|
49105
|
+
// Update status to blocked
|
|
49106
|
+
start: "tasks.start",
|
|
49107
|
+
stop: "tasks.stop"
|
|
49108
|
+
};
|
|
49109
|
+
}
|
|
49110
|
+
});
|
|
49111
|
+
|
|
49112
|
+
// packages/core/src/nexus/index.ts
|
|
49113
|
+
var nexus_exports = {};
|
|
49114
|
+
__export(nexus_exports, {
|
|
49115
|
+
blockingAnalysis: () => blockingAnalysis,
|
|
49116
|
+
buildGlobalGraph: () => buildGlobalGraph,
|
|
49117
|
+
canExecute: () => canExecute,
|
|
49118
|
+
canRead: () => canRead,
|
|
49119
|
+
canWrite: () => canWrite,
|
|
49120
|
+
checkPermission: () => checkPermission,
|
|
49121
|
+
checkPermissionDetail: () => checkPermissionDetail,
|
|
49122
|
+
criticalPath: () => criticalPath,
|
|
49123
|
+
discoverRelated: () => discoverRelated,
|
|
49124
|
+
executeTransfer: () => executeTransfer,
|
|
49125
|
+
extractKeywords: () => extractKeywords2,
|
|
49126
|
+
generateProjectHash: () => generateProjectHash,
|
|
49127
|
+
getCurrentProject: () => getCurrentProject,
|
|
49128
|
+
getNexusCacheDir: () => getNexusCacheDir,
|
|
49129
|
+
getNexusHome: () => getNexusHome,
|
|
49130
|
+
getPermission: () => getPermission,
|
|
49131
|
+
getProjectFromQuery: () => getProjectFromQuery,
|
|
49132
|
+
getSharingStatus: () => getSharingStatus,
|
|
49133
|
+
invalidateGraphCache: () => invalidateGraphCache,
|
|
49134
|
+
nexusDeps: () => nexusDeps,
|
|
49135
|
+
nexusGetProject: () => nexusGetProject,
|
|
49136
|
+
nexusInit: () => nexusInit,
|
|
49137
|
+
nexusList: () => nexusList,
|
|
49138
|
+
nexusProjectExists: () => nexusProjectExists,
|
|
49139
|
+
nexusReconcile: () => nexusReconcile,
|
|
49140
|
+
nexusRegister: () => nexusRegister,
|
|
49141
|
+
nexusSetPermission: () => nexusSetPermission,
|
|
49142
|
+
nexusSync: () => nexusSync,
|
|
49143
|
+
nexusSyncAll: () => nexusSyncAll,
|
|
49144
|
+
nexusUnregister: () => nexusUnregister,
|
|
49145
|
+
orphanDetection: () => orphanDetection,
|
|
49146
|
+
parseDirective: () => parseDirective,
|
|
49147
|
+
parseQuery: () => parseQuery,
|
|
49148
|
+
permissionLevel: () => permissionLevel,
|
|
49149
|
+
previewTransfer: () => previewTransfer,
|
|
49150
|
+
readRegistry: () => readRegistry,
|
|
49151
|
+
readRegistryRequired: () => readRegistryRequired,
|
|
49152
|
+
requirePermission: () => requirePermission,
|
|
49153
|
+
resetNexusDbState: () => resetNexusDbState,
|
|
49154
|
+
resolveCrossDeps: () => resolveCrossDeps,
|
|
49155
|
+
resolveProjectPath: () => resolveProjectPath2,
|
|
49156
|
+
resolveTask: () => resolveTask,
|
|
49157
|
+
routeDirective: () => routeDirective,
|
|
49158
|
+
searchAcrossProjects: () => searchAcrossProjects,
|
|
49159
|
+
setPermission: () => setPermission,
|
|
49160
|
+
syncGitignore: () => syncGitignore,
|
|
49161
|
+
validateSyntax: () => validateSyntax,
|
|
49162
|
+
workspaceAgents: () => workspaceAgents,
|
|
49163
|
+
workspaceStatus: () => workspaceStatus
|
|
49164
|
+
});
|
|
49165
|
+
var init_nexus = __esm({
|
|
49166
|
+
"packages/core/src/nexus/index.ts"() {
|
|
49167
|
+
"use strict";
|
|
49168
|
+
init_deps();
|
|
49169
|
+
init_discover();
|
|
49170
|
+
init_hash();
|
|
49171
|
+
init_permissions();
|
|
49172
|
+
init_query3();
|
|
49173
|
+
init_registry3();
|
|
49174
|
+
init_sharing();
|
|
49175
|
+
init_transfer();
|
|
49176
|
+
init_workspace();
|
|
49177
|
+
}
|
|
49178
|
+
});
|
|
49179
|
+
|
|
49180
|
+
// packages/core/src/observability/types.ts
|
|
49181
|
+
var PINO_LEVEL_VALUES;
|
|
49182
|
+
var init_types = __esm({
|
|
49183
|
+
"packages/core/src/observability/types.ts"() {
|
|
49184
|
+
"use strict";
|
|
49185
|
+
PINO_LEVEL_VALUES = {
|
|
49186
|
+
TRACE: 10,
|
|
49187
|
+
DEBUG: 20,
|
|
49188
|
+
INFO: 30,
|
|
49189
|
+
WARN: 40,
|
|
49190
|
+
ERROR: 50,
|
|
49191
|
+
FATAL: 60
|
|
49192
|
+
};
|
|
49193
|
+
}
|
|
49194
|
+
});
|
|
49195
|
+
|
|
49196
|
+
// packages/core/src/observability/log-filter.ts
|
|
49197
|
+
function compareLevels(a, b) {
|
|
49198
|
+
return PINO_LEVEL_VALUES[a] - PINO_LEVEL_VALUES[b];
|
|
49199
|
+
}
|
|
49200
|
+
function matchesFilter(entry, filter) {
|
|
49201
|
+
if (filter.level !== void 0 && entry.level !== filter.level) return false;
|
|
49202
|
+
if (filter.minLevel !== void 0 && compareLevels(entry.level, filter.minLevel) < 0)
|
|
49203
|
+
return false;
|
|
49204
|
+
if (filter.since !== void 0 && entry.time < filter.since) return false;
|
|
49205
|
+
if (filter.until !== void 0 && entry.time > filter.until) return false;
|
|
49206
|
+
if (filter.subsystem !== void 0 && entry.subsystem !== filter.subsystem) return false;
|
|
49207
|
+
if (filter.code !== void 0 && entry.code !== filter.code) return false;
|
|
49208
|
+
if (filter.exitCode !== void 0 && entry.exitCode !== filter.exitCode) return false;
|
|
49209
|
+
if (filter.pid !== void 0 && entry.pid !== filter.pid) return false;
|
|
49210
|
+
if (filter.msgContains !== void 0) {
|
|
49211
|
+
if (!entry.msg.toLowerCase().includes(filter.msgContains.toLowerCase())) return false;
|
|
49212
|
+
}
|
|
49213
|
+
return true;
|
|
49214
|
+
}
|
|
49215
|
+
function filterEntries(entries, filter) {
|
|
49216
|
+
return entries.filter((entry) => matchesFilter(entry, filter));
|
|
49217
|
+
}
|
|
49218
|
+
function paginate2(entries, limit, offset) {
|
|
49219
|
+
const start = offset ?? 0;
|
|
49220
|
+
if (limit !== void 0) {
|
|
49221
|
+
return entries.slice(start, start + limit);
|
|
49222
|
+
}
|
|
49223
|
+
return start > 0 ? entries.slice(start) : entries;
|
|
49224
|
+
}
|
|
49225
|
+
var init_log_filter = __esm({
|
|
49226
|
+
"packages/core/src/observability/log-filter.ts"() {
|
|
49227
|
+
"use strict";
|
|
49228
|
+
init_types();
|
|
49229
|
+
}
|
|
49230
|
+
});
|
|
49231
|
+
|
|
49232
|
+
// packages/core/src/observability/log-parser.ts
|
|
49233
|
+
function isValidLevel(level) {
|
|
49234
|
+
return VALID_LEVELS.has(level);
|
|
49235
|
+
}
|
|
49236
|
+
function parseLogLine(line2) {
|
|
49237
|
+
const trimmed = line2.trim();
|
|
49238
|
+
if (!trimmed) return null;
|
|
49239
|
+
let raw;
|
|
49240
|
+
try {
|
|
49241
|
+
raw = JSON.parse(trimmed);
|
|
49242
|
+
} catch {
|
|
49243
|
+
return null;
|
|
49244
|
+
}
|
|
49245
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
|
|
49246
|
+
const level = raw.level;
|
|
49247
|
+
if (typeof level !== "string" || !isValidLevel(level)) return null;
|
|
49248
|
+
const time4 = raw.time;
|
|
49249
|
+
if (typeof time4 !== "string") return null;
|
|
49250
|
+
const pid = raw.pid;
|
|
49251
|
+
if (typeof pid !== "number") return null;
|
|
49252
|
+
const hostname4 = raw.hostname;
|
|
49253
|
+
if (typeof hostname4 !== "string") return null;
|
|
49254
|
+
const msg = raw.msg;
|
|
49255
|
+
if (typeof msg !== "string") return null;
|
|
49256
|
+
const extra = {};
|
|
49257
|
+
for (const key of Object.keys(raw)) {
|
|
49258
|
+
if (!KNOWN_FIELDS.has(key)) {
|
|
49259
|
+
extra[key] = raw[key];
|
|
49260
|
+
}
|
|
49261
|
+
}
|
|
49262
|
+
const entry = {
|
|
49263
|
+
level,
|
|
49264
|
+
time: time4,
|
|
49265
|
+
pid,
|
|
49266
|
+
hostname: hostname4,
|
|
49267
|
+
msg,
|
|
49268
|
+
extra
|
|
49269
|
+
};
|
|
49270
|
+
if (typeof raw.subsystem === "string") entry.subsystem = raw.subsystem;
|
|
49271
|
+
if (typeof raw.code === "string") entry.code = raw.code;
|
|
49272
|
+
if (typeof raw.exitCode === "number") entry.exitCode = raw.exitCode;
|
|
49273
|
+
return entry;
|
|
49274
|
+
}
|
|
49275
|
+
function parseLogLines(lines) {
|
|
49276
|
+
const entries = [];
|
|
49277
|
+
for (const line2 of lines) {
|
|
49278
|
+
const entry = parseLogLine(line2);
|
|
49279
|
+
if (entry) entries.push(entry);
|
|
49280
|
+
}
|
|
49281
|
+
return entries;
|
|
49282
|
+
}
|
|
49283
|
+
var VALID_LEVELS, KNOWN_FIELDS;
|
|
49284
|
+
var init_log_parser = __esm({
|
|
49285
|
+
"packages/core/src/observability/log-parser.ts"() {
|
|
49286
|
+
"use strict";
|
|
49287
|
+
init_types();
|
|
49288
|
+
VALID_LEVELS = new Set(Object.keys(PINO_LEVEL_VALUES));
|
|
49289
|
+
KNOWN_FIELDS = /* @__PURE__ */ new Set([
|
|
49290
|
+
"level",
|
|
49291
|
+
"time",
|
|
49292
|
+
"pid",
|
|
49293
|
+
"hostname",
|
|
49294
|
+
"msg",
|
|
49295
|
+
"subsystem",
|
|
49296
|
+
"code",
|
|
49297
|
+
"exitCode"
|
|
49298
|
+
]);
|
|
49299
|
+
}
|
|
49300
|
+
});
|
|
49301
|
+
|
|
49302
|
+
// packages/core/src/observability/log-reader.ts
|
|
49303
|
+
import { createReadStream, existsSync as existsSync56, readdirSync as readdirSync23, readFileSync as readFileSync38, statSync as statSync16 } from "node:fs";
|
|
49304
|
+
import { join as join62 } from "node:path";
|
|
49305
|
+
import { createInterface } from "node:readline";
|
|
49306
|
+
function getProjectLogDir(cwd) {
|
|
49307
|
+
const runtimeDir = getLogDir();
|
|
49308
|
+
if (runtimeDir) return runtimeDir;
|
|
49309
|
+
const cleoDir = getCleoDirAbsolute(cwd);
|
|
49310
|
+
const logsDir = join62(cleoDir, "logs");
|
|
49311
|
+
return existsSync56(logsDir) ? logsDir : null;
|
|
49312
|
+
}
|
|
49313
|
+
function getGlobalLogDir() {
|
|
49314
|
+
return join62(getCleoHome(), "logs");
|
|
49315
|
+
}
|
|
49316
|
+
function scanLogDir(dir, includeMigration) {
|
|
49317
|
+
if (!existsSync56(dir)) return [];
|
|
49318
|
+
let fileNames;
|
|
49319
|
+
try {
|
|
49320
|
+
fileNames = readdirSync23(dir);
|
|
49321
|
+
} catch {
|
|
49322
|
+
return [];
|
|
49323
|
+
}
|
|
49324
|
+
const files = [];
|
|
49325
|
+
for (const name2 of fileNames) {
|
|
49326
|
+
const cleoMatch = name2.match(CLEO_LOG_PATTERN);
|
|
49327
|
+
const isMigration = MIGRATION_LOG_PATTERN.test(name2);
|
|
49328
|
+
if (!cleoMatch && (!isMigration || !includeMigration)) continue;
|
|
49329
|
+
const filePath = join62(dir, name2);
|
|
49330
|
+
let stat2;
|
|
49331
|
+
try {
|
|
49332
|
+
stat2 = statSync16(filePath);
|
|
49333
|
+
} catch {
|
|
49334
|
+
continue;
|
|
49335
|
+
}
|
|
49336
|
+
files.push({
|
|
49337
|
+
path: filePath,
|
|
49338
|
+
name: name2,
|
|
49339
|
+
size: stat2.size,
|
|
49340
|
+
mtime: stat2.mtime.toISOString(),
|
|
49341
|
+
date: cleoMatch ? cleoMatch[1] : null,
|
|
49342
|
+
isActive: false
|
|
49343
|
+
// set below
|
|
49344
|
+
});
|
|
49345
|
+
}
|
|
49346
|
+
return files;
|
|
49347
|
+
}
|
|
49348
|
+
function discoverLogFiles(options, cwd) {
|
|
49349
|
+
const scope = options?.scope ?? "project";
|
|
49350
|
+
const includeMigration = options?.includeMigration ?? false;
|
|
49351
|
+
const sinceDate = options?.since;
|
|
49352
|
+
let files = [];
|
|
49353
|
+
if (scope === "project" || scope === "both") {
|
|
49354
|
+
const dir = getProjectLogDir(cwd);
|
|
49355
|
+
if (dir) files.push(...scanLogDir(dir, includeMigration));
|
|
49356
|
+
}
|
|
49357
|
+
if (scope === "global" || scope === "both") {
|
|
49358
|
+
const dir = getGlobalLogDir();
|
|
49359
|
+
files.push(...scanLogDir(dir, includeMigration));
|
|
49360
|
+
}
|
|
49361
|
+
if (sinceDate) {
|
|
49362
|
+
files = files.filter((f) => f.mtime >= sinceDate);
|
|
49363
|
+
}
|
|
49364
|
+
files.sort((a, b) => b.mtime.localeCompare(a.mtime));
|
|
49365
|
+
if (files.length > 0) {
|
|
49366
|
+
files[0].isActive = true;
|
|
49367
|
+
}
|
|
49368
|
+
return files;
|
|
49369
|
+
}
|
|
49370
|
+
function readLogFileLines(filePath) {
|
|
49371
|
+
let content;
|
|
49372
|
+
try {
|
|
49373
|
+
content = readFileSync38(filePath, "utf-8");
|
|
49374
|
+
} catch {
|
|
49375
|
+
return [];
|
|
49376
|
+
}
|
|
49377
|
+
if (!content.trim()) return [];
|
|
49378
|
+
return content.split("\n").filter((line2) => line2.trim() !== "");
|
|
49379
|
+
}
|
|
49380
|
+
async function* streamLogFileLines(filePath) {
|
|
49381
|
+
if (!existsSync56(filePath)) return;
|
|
49382
|
+
const rl = createInterface({
|
|
49383
|
+
input: createReadStream(filePath, { encoding: "utf-8" }),
|
|
49384
|
+
crlfDelay: Infinity
|
|
49385
|
+
});
|
|
49386
|
+
for await (const line2 of rl) {
|
|
49387
|
+
if (line2.trim()) yield line2;
|
|
49388
|
+
}
|
|
49389
|
+
}
|
|
49390
|
+
var CLEO_LOG_PATTERN, MIGRATION_LOG_PATTERN;
|
|
49391
|
+
var init_log_reader = __esm({
|
|
49392
|
+
"packages/core/src/observability/log-reader.ts"() {
|
|
49393
|
+
"use strict";
|
|
49394
|
+
init_logger();
|
|
49395
|
+
init_paths();
|
|
49396
|
+
CLEO_LOG_PATTERN = /^cleo\.(\d{4}-\d{2}-\d{2})\.(\d+)\.log$/;
|
|
49397
|
+
MIGRATION_LOG_PATTERN = /^migration-.*\.jsonl$/;
|
|
49398
|
+
}
|
|
49399
|
+
});
|
|
49400
|
+
|
|
49401
|
+
// packages/core/src/observability/index.ts
|
|
49402
|
+
var observability_exports = {};
|
|
49403
|
+
__export(observability_exports, {
|
|
49404
|
+
PINO_LEVEL_VALUES: () => PINO_LEVEL_VALUES,
|
|
49405
|
+
compareLevels: () => compareLevels,
|
|
49406
|
+
discoverLogFiles: () => discoverLogFiles,
|
|
49407
|
+
filterEntries: () => filterEntries,
|
|
49408
|
+
getGlobalLogDir: () => getGlobalLogDir,
|
|
49409
|
+
getLogSummary: () => getLogSummary,
|
|
49410
|
+
getProjectLogDir: () => getProjectLogDir,
|
|
49411
|
+
isValidLevel: () => isValidLevel,
|
|
49412
|
+
matchesFilter: () => matchesFilter,
|
|
49413
|
+
paginate: () => paginate2,
|
|
49414
|
+
parseLogLine: () => parseLogLine,
|
|
49415
|
+
parseLogLines: () => parseLogLines,
|
|
49416
|
+
queryLogs: () => queryLogs,
|
|
49417
|
+
readLogFileLines: () => readLogFileLines,
|
|
49418
|
+
streamLogFileLines: () => streamLogFileLines,
|
|
49419
|
+
streamLogs: () => streamLogs
|
|
49420
|
+
});
|
|
49421
|
+
function queryLogs(filter, options, cwd) {
|
|
49422
|
+
const files = discoverLogFiles(options, cwd);
|
|
49423
|
+
const filesPaths = files.map((f) => f.path);
|
|
49424
|
+
let totalScanned = 0;
|
|
49425
|
+
const allEntries = [];
|
|
49426
|
+
for (const file2 of files) {
|
|
49427
|
+
const lines = readLogFileLines(file2.path);
|
|
49428
|
+
totalScanned += lines.length;
|
|
49429
|
+
const parsed = parseLogLines(lines);
|
|
49430
|
+
allEntries.push(...parsed);
|
|
49431
|
+
}
|
|
49432
|
+
const matched = filter ? filterEntries(allEntries, filter) : allEntries;
|
|
49433
|
+
const totalMatched = matched.length;
|
|
49434
|
+
const entries = paginate2(matched, filter?.limit, filter?.offset);
|
|
49435
|
+
return {
|
|
49436
|
+
entries,
|
|
49437
|
+
totalScanned,
|
|
49438
|
+
totalMatched,
|
|
49439
|
+
files: filesPaths
|
|
49440
|
+
};
|
|
49441
|
+
}
|
|
49442
|
+
async function* streamLogs(filter, options, cwd) {
|
|
49443
|
+
const files = discoverLogFiles(options, cwd);
|
|
49444
|
+
let yielded = 0;
|
|
49445
|
+
const limit = filter?.limit;
|
|
49446
|
+
for (const file2 of files) {
|
|
49447
|
+
if (limit !== void 0 && yielded >= limit) break;
|
|
49448
|
+
for await (const line2 of streamLogFileLines(file2.path)) {
|
|
49449
|
+
const entry = parseLogLine(line2);
|
|
49450
|
+
if (!entry) continue;
|
|
49451
|
+
if (filter && !matchesFilter(entry, filter)) continue;
|
|
49452
|
+
yield entry;
|
|
49453
|
+
yielded++;
|
|
49454
|
+
if (limit !== void 0 && yielded >= limit) return;
|
|
49455
|
+
}
|
|
49456
|
+
}
|
|
49457
|
+
}
|
|
49458
|
+
function getLogSummary(options, cwd) {
|
|
49459
|
+
const files = discoverLogFiles(options, cwd);
|
|
49460
|
+
const byLevel = {};
|
|
49461
|
+
const bySubsystem = {};
|
|
49462
|
+
let totalEntries = 0;
|
|
49463
|
+
let earliest = null;
|
|
49464
|
+
let latest = null;
|
|
49465
|
+
for (const file2 of files) {
|
|
49466
|
+
const lines = readLogFileLines(file2.path);
|
|
49467
|
+
const entries = parseLogLines(lines);
|
|
49468
|
+
totalEntries += entries.length;
|
|
49469
|
+
for (const entry of entries) {
|
|
49470
|
+
byLevel[entry.level] = (byLevel[entry.level] ?? 0) + 1;
|
|
49471
|
+
if (entry.subsystem) {
|
|
49472
|
+
bySubsystem[entry.subsystem] = (bySubsystem[entry.subsystem] ?? 0) + 1;
|
|
49473
|
+
}
|
|
49474
|
+
if (earliest === null || entry.time < earliest) earliest = entry.time;
|
|
49475
|
+
if (latest === null || entry.time > latest) latest = entry.time;
|
|
49476
|
+
}
|
|
49477
|
+
}
|
|
49478
|
+
return {
|
|
49479
|
+
totalEntries,
|
|
49480
|
+
byLevel,
|
|
49481
|
+
bySubsystem,
|
|
49482
|
+
dateRange: earliest && latest ? { earliest, latest } : null,
|
|
49483
|
+
files
|
|
49484
|
+
};
|
|
49485
|
+
}
|
|
49486
|
+
var init_observability = __esm({
|
|
49487
|
+
"packages/core/src/observability/index.ts"() {
|
|
49488
|
+
"use strict";
|
|
49489
|
+
init_log_filter();
|
|
49490
|
+
init_log_parser();
|
|
49491
|
+
init_log_reader();
|
|
49492
|
+
init_types();
|
|
49493
|
+
init_log_filter();
|
|
49494
|
+
init_log_parser();
|
|
49495
|
+
init_log_reader();
|
|
49496
|
+
}
|
|
49497
|
+
});
|
|
49498
|
+
|
|
49499
|
+
// packages/core/src/phases/deps.ts
|
|
49500
|
+
async function loadAllTasks3(cwd, accessor) {
|
|
49501
|
+
const acc = accessor ?? await getAccessor(cwd);
|
|
49502
|
+
const { tasks: tasks2 } = await acc.queryTasks({});
|
|
49503
|
+
return tasks2;
|
|
49504
|
+
}
|
|
49505
|
+
function buildGraph(tasks2) {
|
|
49506
|
+
const graph = /* @__PURE__ */ new Map();
|
|
49507
|
+
const taskMap = new Map(tasks2.map((t) => [t.id, t]));
|
|
49508
|
+
for (const task of tasks2) {
|
|
49509
|
+
graph.set(task.id, {
|
|
49510
|
+
id: task.id,
|
|
49511
|
+
title: task.title,
|
|
49512
|
+
status: task.status,
|
|
49513
|
+
depends: (task.depends ?? []).filter((d) => taskMap.has(d)),
|
|
49514
|
+
dependents: []
|
|
49515
|
+
});
|
|
49516
|
+
}
|
|
49517
|
+
for (const [id, node] of graph) {
|
|
49518
|
+
for (const depId of node.depends) {
|
|
49519
|
+
const depNode = graph.get(depId);
|
|
49520
|
+
if (depNode) {
|
|
49521
|
+
depNode.dependents.push(id);
|
|
49522
|
+
}
|
|
49523
|
+
}
|
|
49524
|
+
}
|
|
49525
|
+
return graph;
|
|
49526
|
+
}
|
|
49527
|
+
async function getExecutionWaves(epicId, cwd, accessor) {
|
|
49528
|
+
const allTasks = await loadAllTasks3(cwd, accessor);
|
|
49529
|
+
let tasks2 = allTasks.filter((t) => t.status !== "done" && t.status !== "cancelled");
|
|
49530
|
+
if (epicId) {
|
|
49531
|
+
const epicTask = allTasks.find((t) => t.id === epicId);
|
|
49532
|
+
if (!epicTask) {
|
|
49533
|
+
throw new CleoError(4 /* NOT_FOUND */, `Epic not found: ${epicId}`);
|
|
49534
|
+
}
|
|
49535
|
+
const childIds = new Set(allTasks.filter((t) => t.parentId === epicId).map((t) => t.id));
|
|
49536
|
+
tasks2 = tasks2.filter((t) => childIds.has(t.id) || t.id === epicId);
|
|
49537
|
+
}
|
|
49538
|
+
const graph = buildGraph(tasks2);
|
|
49539
|
+
const waves = [];
|
|
49540
|
+
const completed = /* @__PURE__ */ new Set();
|
|
49541
|
+
const remaining = new Set(tasks2.map((t) => t.id));
|
|
49542
|
+
let waveNum = 1;
|
|
49543
|
+
while (remaining.size > 0) {
|
|
49544
|
+
const wave = [];
|
|
49545
|
+
for (const id of remaining) {
|
|
49546
|
+
const node = graph.get(id);
|
|
49547
|
+
if (!node) continue;
|
|
49548
|
+
const allDepsComplete = node.depends.every(
|
|
49549
|
+
(dep) => completed.has(dep) || !remaining.has(dep)
|
|
49550
|
+
);
|
|
49551
|
+
if (allDepsComplete) {
|
|
49552
|
+
wave.push(id);
|
|
49553
|
+
}
|
|
49554
|
+
}
|
|
49555
|
+
if (wave.length === 0) {
|
|
49556
|
+
wave.push(...remaining);
|
|
49557
|
+
}
|
|
49558
|
+
waves.push({
|
|
49559
|
+
wave: waveNum,
|
|
49560
|
+
tasks: wave.map((id) => {
|
|
49561
|
+
const node = graph.get(id);
|
|
49562
|
+
return {
|
|
49563
|
+
id,
|
|
49564
|
+
title: node.title,
|
|
49565
|
+
status: node.status,
|
|
49566
|
+
depends: node.depends
|
|
49567
|
+
};
|
|
49568
|
+
})
|
|
49569
|
+
});
|
|
49570
|
+
for (const id of wave) {
|
|
49571
|
+
completed.add(id);
|
|
49572
|
+
remaining.delete(id);
|
|
49573
|
+
}
|
|
49574
|
+
waveNum++;
|
|
49575
|
+
}
|
|
49576
|
+
return waves;
|
|
49577
|
+
}
|
|
49578
|
+
async function getCriticalPath2(taskId, cwd, accessor) {
|
|
49579
|
+
const allTasks = await loadAllTasks3(cwd, accessor);
|
|
49580
|
+
const task = allTasks.find((t) => t.id === taskId);
|
|
49581
|
+
if (!task) {
|
|
49582
|
+
throw new CleoError(4 /* NOT_FOUND */, `Task not found: ${taskId}`);
|
|
49583
|
+
}
|
|
49584
|
+
const graph = buildGraph(allTasks);
|
|
49585
|
+
const taskMap = new Map(allTasks.map((t) => [t.id, t]));
|
|
49586
|
+
function findLongestPath(id, visited) {
|
|
49587
|
+
if (visited.has(id)) return [];
|
|
49588
|
+
visited.add(id);
|
|
49589
|
+
const node = graph.get(id);
|
|
49590
|
+
if (!node || node.dependents.length === 0) {
|
|
49591
|
+
return [id];
|
|
49592
|
+
}
|
|
49593
|
+
let longest = [];
|
|
49594
|
+
for (const depId of node.dependents) {
|
|
49595
|
+
const path3 = findLongestPath(depId, new Set(visited));
|
|
49596
|
+
if (path3.length > longest.length) {
|
|
49597
|
+
longest = path3;
|
|
49598
|
+
}
|
|
49599
|
+
}
|
|
49600
|
+
return [id, ...longest];
|
|
49601
|
+
}
|
|
49602
|
+
const path2 = findLongestPath(taskId, /* @__PURE__ */ new Set());
|
|
49603
|
+
return {
|
|
49604
|
+
path: path2.map((id) => {
|
|
49605
|
+
const t = taskMap.get(id);
|
|
49606
|
+
return t ? { id: t.id, title: t.title, status: t.status } : { id, title: "Unknown", status: "unknown" };
|
|
49607
|
+
}),
|
|
49608
|
+
length: path2.length
|
|
49609
|
+
};
|
|
49610
|
+
}
|
|
49611
|
+
var init_deps2 = __esm({
|
|
49612
|
+
"packages/core/src/phases/deps.ts"() {
|
|
49613
|
+
"use strict";
|
|
49614
|
+
init_src();
|
|
49615
|
+
init_errors3();
|
|
49616
|
+
init_data_accessor();
|
|
49617
|
+
}
|
|
49618
|
+
});
|
|
49619
|
+
|
|
49620
|
+
// packages/core/src/orchestration/analyze.ts
|
|
49621
|
+
function buildDependencyGraph(tasks2) {
|
|
49622
|
+
const graph = /* @__PURE__ */ new Map();
|
|
49623
|
+
for (const task of tasks2) {
|
|
49624
|
+
if (!graph.has(task.id)) {
|
|
49625
|
+
graph.set(task.id, /* @__PURE__ */ new Set());
|
|
49626
|
+
}
|
|
49627
|
+
if (task.depends) {
|
|
49628
|
+
for (const dep of task.depends) {
|
|
49629
|
+
graph.get(task.id).add(dep);
|
|
49630
|
+
}
|
|
49631
|
+
}
|
|
49632
|
+
}
|
|
49633
|
+
return graph;
|
|
49634
|
+
}
|
|
49635
|
+
function detectCircularDependencies(tasks2, graph) {
|
|
49636
|
+
const depGraph = graph ?? buildDependencyGraph(tasks2);
|
|
49637
|
+
const circularDeps = [];
|
|
49638
|
+
const visited = /* @__PURE__ */ new Set();
|
|
49639
|
+
const recursionStack = /* @__PURE__ */ new Set();
|
|
49640
|
+
function dfs(taskId, path2) {
|
|
49641
|
+
visited.add(taskId);
|
|
49642
|
+
recursionStack.add(taskId);
|
|
49643
|
+
const deps = depGraph.get(taskId) || /* @__PURE__ */ new Set();
|
|
49644
|
+
for (const dep of deps) {
|
|
49645
|
+
if (!visited.has(dep)) {
|
|
49646
|
+
dfs(dep, [...path2, taskId]);
|
|
49647
|
+
} else if (recursionStack.has(dep)) {
|
|
49648
|
+
circularDeps.push([...path2, taskId, dep]);
|
|
49649
|
+
}
|
|
49650
|
+
}
|
|
49651
|
+
recursionStack.delete(taskId);
|
|
49652
|
+
}
|
|
49653
|
+
for (const task of tasks2) {
|
|
49654
|
+
if (!visited.has(task.id)) {
|
|
49655
|
+
dfs(task.id, []);
|
|
49656
|
+
}
|
|
49657
|
+
}
|
|
49658
|
+
return circularDeps;
|
|
49659
|
+
}
|
|
49660
|
+
function findMissingDependencies(children, allTasks) {
|
|
49661
|
+
const childIds = new Set(children.map((t) => t.id));
|
|
49662
|
+
const missingDeps = [];
|
|
49663
|
+
for (const task of children) {
|
|
49664
|
+
if (task.depends) {
|
|
49665
|
+
for (const dep of task.depends) {
|
|
49666
|
+
if (!childIds.has(dep) && !allTasks.find((t) => t.id === dep && t.status === "done")) {
|
|
49667
|
+
missingDeps.push({ taskId: task.id, missingDep: dep });
|
|
49668
|
+
}
|
|
49669
|
+
}
|
|
49670
|
+
}
|
|
49671
|
+
}
|
|
49672
|
+
return missingDeps;
|
|
49673
|
+
}
|
|
49674
|
+
function analyzeDependencies(children, allTasks) {
|
|
49675
|
+
const graph = buildDependencyGraph(children);
|
|
49676
|
+
const circularDependencies = detectCircularDependencies(children, graph);
|
|
49677
|
+
const missingDependencies = findMissingDependencies(children, allTasks);
|
|
49678
|
+
const dependencyGraph = {};
|
|
49679
|
+
for (const [key, value] of graph.entries()) {
|
|
49680
|
+
dependencyGraph[key] = Array.from(value);
|
|
49681
|
+
}
|
|
49682
|
+
return {
|
|
49683
|
+
dependencyGraph,
|
|
49684
|
+
circularDependencies,
|
|
49685
|
+
missingDependencies
|
|
49686
|
+
};
|
|
49687
|
+
}
|
|
49688
|
+
var init_analyze = __esm({
|
|
49689
|
+
"packages/core/src/orchestration/analyze.ts"() {
|
|
49690
|
+
"use strict";
|
|
49691
|
+
}
|
|
49692
|
+
});
|
|
49693
|
+
|
|
49694
|
+
// packages/core/src/orchestration/context.ts
|
|
49695
|
+
import { existsSync as existsSync57, readFileSync as readFileSync39 } from "node:fs";
|
|
49696
|
+
function countManifestEntries(projectRoot) {
|
|
49697
|
+
const manifestPath = getManifestPath(projectRoot);
|
|
49698
|
+
if (!existsSync57(manifestPath)) {
|
|
49699
|
+
return 0;
|
|
49700
|
+
}
|
|
49701
|
+
try {
|
|
49702
|
+
const content = readFileSync39(manifestPath, "utf-8");
|
|
49703
|
+
return content.split("\n").filter((l) => l.trim()).length;
|
|
49704
|
+
} catch {
|
|
49705
|
+
return 0;
|
|
49706
|
+
}
|
|
49707
|
+
}
|
|
49708
|
+
function estimateContext(taskCount, projectRoot, epicId) {
|
|
49709
|
+
const estimatedTokens = taskCount * 100;
|
|
49710
|
+
const manifestEntries2 = countManifestEntries(projectRoot);
|
|
49711
|
+
return {
|
|
49712
|
+
epicId: epicId || null,
|
|
49713
|
+
taskCount,
|
|
49714
|
+
manifestEntries: manifestEntries2,
|
|
49715
|
+
estimatedTokens,
|
|
49716
|
+
recommendation: estimatedTokens > 5e3 ? "Consider using manifest summaries instead of full task details" : "Context usage is within recommended limits",
|
|
49717
|
+
limits: {
|
|
49718
|
+
orchestratorBudget: 1e4,
|
|
49719
|
+
maxFilesPerAgent: 3,
|
|
49720
|
+
currentUsage: estimatedTokens
|
|
49721
|
+
}
|
|
49722
|
+
};
|
|
49723
|
+
}
|
|
49724
|
+
var init_context2 = __esm({
|
|
49725
|
+
"packages/core/src/orchestration/context.ts"() {
|
|
49726
|
+
"use strict";
|
|
49727
|
+
init_paths();
|
|
49728
|
+
}
|
|
49729
|
+
});
|
|
49730
|
+
|
|
49731
|
+
// packages/core/src/orchestration/waves.ts
|
|
49732
|
+
function buildDependencyGraph2(tasks2) {
|
|
49733
|
+
const graph = /* @__PURE__ */ new Map();
|
|
49734
|
+
for (const task of tasks2) {
|
|
49735
|
+
if (!graph.has(task.id)) {
|
|
49736
|
+
graph.set(task.id, /* @__PURE__ */ new Set());
|
|
49737
|
+
}
|
|
49738
|
+
if (task.depends) {
|
|
49739
|
+
for (const dep of task.depends) {
|
|
49740
|
+
graph.get(task.id).add(dep);
|
|
49741
|
+
}
|
|
49742
|
+
}
|
|
49743
|
+
}
|
|
49744
|
+
return graph;
|
|
49745
|
+
}
|
|
49746
|
+
function computeWaves(tasks2) {
|
|
49747
|
+
const graph = buildDependencyGraph2(tasks2);
|
|
49748
|
+
const waves = [];
|
|
49749
|
+
const completed = /* @__PURE__ */ new Set();
|
|
49750
|
+
for (const task of tasks2) {
|
|
49751
|
+
if (task.status === "done" || task.status === "cancelled") {
|
|
49752
|
+
completed.add(task.id);
|
|
49753
|
+
}
|
|
49754
|
+
}
|
|
49755
|
+
let remaining = tasks2.filter((t) => t.status !== "done" && t.status !== "cancelled");
|
|
49756
|
+
let waveNumber = 1;
|
|
49757
|
+
const maxWaves = 50;
|
|
49758
|
+
while (remaining.length > 0 && waveNumber <= maxWaves) {
|
|
49759
|
+
const waveTasks = remaining.filter((t) => {
|
|
49760
|
+
const deps = graph.get(t.id) || /* @__PURE__ */ new Set();
|
|
49761
|
+
return Array.from(deps).every((d) => completed.has(d));
|
|
49762
|
+
});
|
|
49763
|
+
if (waveTasks.length === 0) break;
|
|
49764
|
+
waves.push({
|
|
49765
|
+
waveNumber,
|
|
49766
|
+
tasks: waveTasks.map((t) => t.id),
|
|
49767
|
+
status: waveTasks.every((t) => completed.has(t.id)) ? "completed" : waveTasks.some((t) => t.status === "active") ? "in_progress" : "pending"
|
|
49768
|
+
});
|
|
49769
|
+
for (const t of waveTasks) {
|
|
49770
|
+
completed.add(t.id);
|
|
49771
|
+
}
|
|
49772
|
+
remaining = remaining.filter((t) => !waveTasks.some((wt) => wt.id === t.id));
|
|
49773
|
+
waveNumber++;
|
|
49774
|
+
}
|
|
49775
|
+
if (remaining.length > 0) {
|
|
49776
|
+
waves.push({
|
|
49777
|
+
waveNumber,
|
|
49778
|
+
tasks: remaining.map((t) => t.id),
|
|
49779
|
+
status: "pending"
|
|
49780
|
+
});
|
|
49781
|
+
}
|
|
49782
|
+
return waves;
|
|
49783
|
+
}
|
|
49784
|
+
async function getEnrichedWaves(epicId, cwd, accessor) {
|
|
49785
|
+
const acc = accessor ?? await getAccessor(cwd);
|
|
49786
|
+
const children = await acc.getChildren(epicId);
|
|
49787
|
+
const waves = computeWaves(children);
|
|
49788
|
+
const taskMap = new Map(children.map((t) => [t.id, t]));
|
|
49789
|
+
const enrichedWaves = waves.map((w) => ({
|
|
49790
|
+
...w,
|
|
49791
|
+
tasks: w.tasks.map((id) => ({
|
|
49792
|
+
id,
|
|
49793
|
+
title: taskMap.get(id)?.title || id,
|
|
49794
|
+
status: taskMap.get(id)?.status || "unknown"
|
|
49795
|
+
}))
|
|
49796
|
+
}));
|
|
49797
|
+
return {
|
|
49798
|
+
epicId,
|
|
49799
|
+
waves: enrichedWaves,
|
|
49800
|
+
totalWaves: waves.length,
|
|
49801
|
+
totalTasks: children.length
|
|
49802
|
+
};
|
|
49803
|
+
}
|
|
49804
|
+
var init_waves = __esm({
|
|
49805
|
+
"packages/core/src/orchestration/waves.ts"() {
|
|
49806
|
+
"use strict";
|
|
49807
|
+
init_data_accessor();
|
|
49808
|
+
}
|
|
49809
|
+
});
|
|
49810
|
+
|
|
49811
|
+
// packages/core/src/orchestration/status.ts
|
|
49812
|
+
function countByStatus(tasks2) {
|
|
49813
|
+
return {
|
|
49814
|
+
pending: tasks2.filter((t) => t.status === "pending").length,
|
|
49815
|
+
active: tasks2.filter((t) => t.status === "active").length,
|
|
49816
|
+
blocked: tasks2.filter((t) => t.status === "blocked").length,
|
|
49817
|
+
done: tasks2.filter((t) => t.status === "done").length,
|
|
49818
|
+
cancelled: tasks2.filter((t) => t.status === "cancelled").length
|
|
49819
|
+
};
|
|
49820
|
+
}
|
|
49821
|
+
function computeEpicStatus(epicId, epicTitle, children) {
|
|
49822
|
+
const waves = computeWaves(children);
|
|
49823
|
+
const byStatus = countByStatus(children);
|
|
49824
|
+
return {
|
|
49825
|
+
epicId,
|
|
49826
|
+
epicTitle,
|
|
49827
|
+
totalTasks: children.length,
|
|
49828
|
+
byStatus,
|
|
49829
|
+
waves: waves.length,
|
|
49830
|
+
currentWave: waves.find((w) => w.status !== "completed")?.waveNumber || null
|
|
49831
|
+
};
|
|
49832
|
+
}
|
|
49833
|
+
function computeOverallStatus(tasks2) {
|
|
49834
|
+
const epics = tasks2.filter(
|
|
49835
|
+
(t) => !t.parentId && (t.type === "epic" || tasks2.some((c) => c.parentId === t.id))
|
|
49836
|
+
);
|
|
49837
|
+
return {
|
|
49838
|
+
totalEpics: epics.length,
|
|
49839
|
+
totalTasks: tasks2.length,
|
|
49840
|
+
byStatus: {
|
|
49841
|
+
pending: tasks2.filter((t) => t.status === "pending").length,
|
|
49842
|
+
active: tasks2.filter((t) => t.status === "active").length,
|
|
49843
|
+
blocked: tasks2.filter((t) => t.status === "blocked").length,
|
|
49844
|
+
done: tasks2.filter((t) => t.status === "done").length
|
|
49845
|
+
}
|
|
49846
|
+
};
|
|
49847
|
+
}
|
|
49848
|
+
function computeProgress(tasks2) {
|
|
49849
|
+
const total = tasks2.length;
|
|
49850
|
+
const done = tasks2.filter((t) => t.status === "done").length;
|
|
49851
|
+
const pending = tasks2.filter((t) => t.status === "pending").length;
|
|
49852
|
+
const blocked = tasks2.filter((t) => t.status === "blocked").length;
|
|
49853
|
+
const active = tasks2.filter((t) => t.status === "active").length;
|
|
49854
|
+
return {
|
|
49855
|
+
total,
|
|
49856
|
+
done,
|
|
49857
|
+
pending,
|
|
49858
|
+
blocked,
|
|
49859
|
+
active,
|
|
49860
|
+
percentComplete: total > 0 ? Math.round(done / total * 100) : 0
|
|
49861
|
+
};
|
|
49862
|
+
}
|
|
49863
|
+
function computeStartupSummary(epicId, epicTitle, children, readyCount) {
|
|
49864
|
+
const waves = computeWaves(children);
|
|
49865
|
+
const byStatus = countByStatus(children);
|
|
49866
|
+
return {
|
|
49867
|
+
epicId,
|
|
49868
|
+
epicTitle,
|
|
49869
|
+
initialized: true,
|
|
49870
|
+
summary: {
|
|
49871
|
+
totalTasks: children.length,
|
|
49872
|
+
totalWaves: waves.length,
|
|
49873
|
+
readyTasks: readyCount,
|
|
49874
|
+
byStatus
|
|
49875
|
+
},
|
|
49876
|
+
firstWave: waves[0] || null
|
|
49877
|
+
};
|
|
49878
|
+
}
|
|
49879
|
+
var init_status = __esm({
|
|
49880
|
+
"packages/core/src/orchestration/status.ts"() {
|
|
49881
|
+
"use strict";
|
|
49882
|
+
init_waves();
|
|
49883
|
+
}
|
|
49884
|
+
});
|
|
49885
|
+
|
|
49886
|
+
// packages/core/src/orchestration/index.ts
|
|
49887
|
+
var orchestration_exports = {};
|
|
49888
|
+
__export(orchestration_exports, {
|
|
49889
|
+
analyzeDependencies: () => analyzeDependencies,
|
|
49890
|
+
analyzeEpic: () => analyzeEpic,
|
|
49891
|
+
autoDispatch: () => autoDispatch,
|
|
49892
|
+
buildDependencyGraph: () => buildDependencyGraph,
|
|
49893
|
+
computeEpicStatus: () => computeEpicStatus,
|
|
49894
|
+
computeOverallStatus: () => computeOverallStatus,
|
|
49895
|
+
computeProgress: () => computeProgress,
|
|
49896
|
+
computeStartupSummary: () => computeStartupSummary,
|
|
49897
|
+
countByStatus: () => countByStatus,
|
|
49898
|
+
countManifestEntries: () => countManifestEntries,
|
|
49899
|
+
detectCircularDependencies: () => detectCircularDependencies,
|
|
49900
|
+
estimateContext: () => estimateContext,
|
|
49901
|
+
findMissingDependencies: () => findMissingDependencies,
|
|
49902
|
+
getNextTask: () => getNextTask,
|
|
49903
|
+
getOrchestratorContext: () => getOrchestratorContext,
|
|
49904
|
+
getReadyTasks: () => getReadyTasks2,
|
|
49905
|
+
prepareSpawn: () => prepareSpawn,
|
|
49906
|
+
resolveTokens: () => resolveTokens,
|
|
49907
|
+
startOrchestration: () => startOrchestration,
|
|
49908
|
+
validateSpawnOutput: () => validateSpawnOutput
|
|
49909
|
+
});
|
|
49910
|
+
async function startOrchestration(epicId, _cwd, accessor) {
|
|
49911
|
+
const epic = await accessor.loadSingleTask(epicId);
|
|
49912
|
+
if (!epic) {
|
|
49913
|
+
throw new CleoError(4 /* NOT_FOUND */, `Epic not found: ${epicId}`);
|
|
49914
|
+
}
|
|
49915
|
+
if (epic.type !== "epic") {
|
|
49916
|
+
throw new CleoError(
|
|
49917
|
+
2 /* INVALID_INPUT */,
|
|
49918
|
+
`Task ${epicId} is not an epic (type: ${epic.type})`
|
|
49919
|
+
);
|
|
49920
|
+
}
|
|
49921
|
+
const session = {
|
|
49922
|
+
epicId,
|
|
49923
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
49924
|
+
status: "active",
|
|
49925
|
+
currentWave: 1,
|
|
49926
|
+
completedTasks: [],
|
|
49927
|
+
spawnedAgents: []
|
|
49928
|
+
};
|
|
49929
|
+
return session;
|
|
49930
|
+
}
|
|
49931
|
+
async function analyzeEpic(epicId, cwd, accessor) {
|
|
49932
|
+
const epic = await accessor.loadSingleTask(epicId);
|
|
49933
|
+
if (!epic) {
|
|
49934
|
+
throw new CleoError(4 /* NOT_FOUND */, `Epic not found: ${epicId}`);
|
|
49935
|
+
}
|
|
49936
|
+
const childTasks = await accessor.getChildren(epicId);
|
|
49937
|
+
const waves = await getExecutionWaves(epicId, cwd, accessor);
|
|
49938
|
+
const completedTasks = childTasks.filter((t) => t.status === "done").map((t) => t.id);
|
|
49939
|
+
const blockedTasks = childTasks.filter((t) => t.status === "blocked").map((t) => t.id);
|
|
49940
|
+
const completedSet = new Set(completedTasks);
|
|
49941
|
+
const readyTasks = childTasks.filter((t) => {
|
|
49942
|
+
if (t.status === "done" || t.status === "cancelled") return false;
|
|
49943
|
+
const deps = t.depends ?? [];
|
|
49944
|
+
return deps.every((d) => completedSet.has(d));
|
|
49945
|
+
}).map((t) => t.id);
|
|
49946
|
+
return {
|
|
49947
|
+
epicId,
|
|
49948
|
+
totalTasks: childTasks.length,
|
|
49949
|
+
waves: waves.map((w) => ({
|
|
49950
|
+
wave: w.wave,
|
|
49951
|
+
tasks: w.tasks.map((t) => ({ id: t.id, title: t.title, status: t.status }))
|
|
49952
|
+
})),
|
|
49953
|
+
readyTasks,
|
|
49954
|
+
blockedTasks,
|
|
49955
|
+
completedTasks
|
|
49956
|
+
};
|
|
49957
|
+
}
|
|
49958
|
+
async function getReadyTasks2(epicId, _cwd, accessor) {
|
|
49959
|
+
const childTasks = await accessor.getChildren(epicId);
|
|
49960
|
+
const { tasks: allTasks } = await accessor.queryTasks({});
|
|
49961
|
+
const completedIds = new Set(allTasks.filter((t) => t.status === "done").map((t) => t.id));
|
|
49962
|
+
return childTasks.filter((t) => t.status !== "done" && t.status !== "cancelled").map((task) => {
|
|
49963
|
+
const deps = task.depends ?? [];
|
|
49964
|
+
const unmetDeps = deps.filter((d) => !completedIds.has(d));
|
|
49965
|
+
const protocol = autoDispatch(task);
|
|
49966
|
+
return {
|
|
49967
|
+
taskId: task.id,
|
|
49968
|
+
title: task.title,
|
|
49969
|
+
ready: unmetDeps.length === 0,
|
|
49970
|
+
blockers: unmetDeps,
|
|
49971
|
+
protocol
|
|
49972
|
+
};
|
|
49973
|
+
});
|
|
49974
|
+
}
|
|
49975
|
+
async function getNextTask(epicId, cwd, accessor) {
|
|
49976
|
+
const readyTasks = await getReadyTasks2(epicId, cwd, accessor);
|
|
49977
|
+
const ready = readyTasks.filter((t) => t.ready);
|
|
49978
|
+
if (ready.length === 0) return null;
|
|
49979
|
+
return ready[0];
|
|
49980
|
+
}
|
|
49981
|
+
async function prepareSpawn(taskId, _cwd, accessor) {
|
|
49982
|
+
const task = await accessor.loadSingleTask(taskId);
|
|
49983
|
+
if (!task) {
|
|
49984
|
+
throw new CleoError(4 /* NOT_FOUND */, `Task not found: ${taskId}`);
|
|
49985
|
+
}
|
|
49986
|
+
const protocol = autoDispatch(task);
|
|
49987
|
+
const prompt = buildSpawnPrompt(task, protocol);
|
|
49988
|
+
const unresolvedTokens = findUnresolvedTokens(prompt);
|
|
49989
|
+
return {
|
|
49990
|
+
taskId,
|
|
49991
|
+
protocol,
|
|
49992
|
+
prompt,
|
|
49993
|
+
tokenResolution: {
|
|
49994
|
+
fullyResolved: unresolvedTokens.length === 0,
|
|
49995
|
+
unresolvedTokens
|
|
49996
|
+
}
|
|
49997
|
+
};
|
|
49998
|
+
}
|
|
49999
|
+
async function validateSpawnOutput(_taskId, output) {
|
|
50000
|
+
const errors = [];
|
|
50001
|
+
if (!output.file) {
|
|
50002
|
+
errors.push("No output file specified");
|
|
50003
|
+
}
|
|
50004
|
+
if (!output.manifestEntry) {
|
|
50005
|
+
errors.push("No manifest entry appended");
|
|
50006
|
+
}
|
|
50007
|
+
return { valid: errors.length === 0, errors };
|
|
50008
|
+
}
|
|
50009
|
+
async function getOrchestratorContext(epicId, _cwd, accessor) {
|
|
50010
|
+
const epic = await accessor.loadSingleTask(epicId);
|
|
50011
|
+
if (!epic) {
|
|
50012
|
+
throw new CleoError(4 /* NOT_FOUND */, `Epic not found: ${epicId}`);
|
|
50013
|
+
}
|
|
50014
|
+
const children = await accessor.getChildren(epicId);
|
|
50015
|
+
const completed = children.filter((t) => t.status === "done").length;
|
|
50016
|
+
const inProgress = children.filter((t) => t.status === "active").length;
|
|
50017
|
+
const blocked = children.filter((t) => t.status === "blocked").length;
|
|
50018
|
+
const pending = children.filter((t) => t.status === "pending").length;
|
|
50019
|
+
return {
|
|
50020
|
+
epicId,
|
|
50021
|
+
epicTitle: epic.title,
|
|
50022
|
+
totalTasks: children.length,
|
|
50023
|
+
completed,
|
|
50024
|
+
inProgress,
|
|
50025
|
+
blocked,
|
|
50026
|
+
pending,
|
|
50027
|
+
completionPercent: children.length > 0 ? Math.floor(completed * 100 / children.length) : 0
|
|
50028
|
+
};
|
|
50029
|
+
}
|
|
50030
|
+
function autoDispatch(task) {
|
|
50031
|
+
if (task.labels?.length) {
|
|
50032
|
+
for (const [protocol, config2] of Object.entries(DISPATCH_MAP)) {
|
|
50033
|
+
if (task.labels.some((l) => config2.labels.includes(l))) {
|
|
50034
|
+
return protocol;
|
|
50035
|
+
}
|
|
50036
|
+
}
|
|
50037
|
+
}
|
|
50038
|
+
if (task.type === "epic") return "decomposition";
|
|
50039
|
+
const text3 = `${task.title} ${task.description ?? ""}`.toLowerCase();
|
|
50040
|
+
for (const [protocol, config2] of Object.entries(DISPATCH_MAP)) {
|
|
50041
|
+
if (config2.keywords.some((kw) => text3.includes(kw))) {
|
|
50042
|
+
return protocol;
|
|
50043
|
+
}
|
|
50044
|
+
}
|
|
50045
|
+
return "implementation";
|
|
50046
|
+
}
|
|
50047
|
+
function buildSpawnPrompt(task, protocol) {
|
|
50048
|
+
const epicId = task.parentId ?? "none";
|
|
50049
|
+
const date6 = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
50050
|
+
return [
|
|
50051
|
+
`## Task: ${task.id}`,
|
|
50052
|
+
`**Title**: ${task.title}`,
|
|
50053
|
+
task.description ? `**Description**: ${task.description}` : "",
|
|
50054
|
+
`**Protocol**: ${protocol}`,
|
|
50055
|
+
`**Epic**: ${epicId}`,
|
|
50056
|
+
`**Date**: ${date6}`,
|
|
50057
|
+
"",
|
|
50058
|
+
`### Instructions`,
|
|
50059
|
+
`1. Start task: \`cleo start ${task.id}\``,
|
|
50060
|
+
`2. Execute the ${protocol} protocol`,
|
|
50061
|
+
`3. Write output file`,
|
|
50062
|
+
`4. Append manifest entry to MANIFEST.jsonl`,
|
|
50063
|
+
`5. Complete: \`cleo complete ${task.id}\``,
|
|
50064
|
+
"",
|
|
50065
|
+
task.acceptance?.length ? `### Acceptance Criteria
|
|
50066
|
+
${task.acceptance.map((a) => `- ${a}`).join("\n")}` : "",
|
|
50067
|
+
task.depends?.length ? `### Dependencies
|
|
50068
|
+
${task.depends.map((d) => `- ${d}`).join("\n")}` : ""
|
|
50069
|
+
].filter(Boolean).join("\n");
|
|
50070
|
+
}
|
|
50071
|
+
function findUnresolvedTokens(prompt) {
|
|
50072
|
+
const tokens = [];
|
|
50073
|
+
const tokenRegex = /\{\{([A-Z_]+)\}\}/g;
|
|
50074
|
+
for (const match of prompt.matchAll(tokenRegex)) {
|
|
50075
|
+
tokens.push(match[1]);
|
|
50076
|
+
}
|
|
50077
|
+
const refRegex = /@([a-zA-Z0-9_./-]+\.md)/g;
|
|
50078
|
+
for (const match of prompt.matchAll(refRegex)) {
|
|
50079
|
+
tokens.push(`@${match[1]}`);
|
|
50080
|
+
}
|
|
50081
|
+
return tokens;
|
|
50082
|
+
}
|
|
50083
|
+
function resolveTokens(prompt, context) {
|
|
50084
|
+
let resolved = prompt;
|
|
50085
|
+
const unresolved = [];
|
|
50086
|
+
resolved = resolved.replace(/\{\{([A-Z_]+)\}\}/g, (fullMatch, token) => {
|
|
50087
|
+
if (context[token] !== void 0) {
|
|
50088
|
+
return context[token];
|
|
50089
|
+
}
|
|
50090
|
+
unresolved.push(token);
|
|
50091
|
+
return fullMatch;
|
|
50092
|
+
});
|
|
50093
|
+
return { resolved, unresolved };
|
|
50094
|
+
}
|
|
50095
|
+
var DISPATCH_MAP;
|
|
50096
|
+
var init_orchestration = __esm({
|
|
50097
|
+
"packages/core/src/orchestration/index.ts"() {
|
|
50098
|
+
"use strict";
|
|
50099
|
+
init_src();
|
|
50100
|
+
init_errors3();
|
|
50101
|
+
init_deps2();
|
|
50102
|
+
init_analyze();
|
|
50103
|
+
init_context2();
|
|
50104
|
+
init_status();
|
|
50105
|
+
DISPATCH_MAP = {
|
|
50106
|
+
research: {
|
|
50107
|
+
keywords: ["research", "investigate", "explore", "analyze", "study"],
|
|
50108
|
+
labels: ["research", "investigation", "analysis"]
|
|
50109
|
+
},
|
|
50110
|
+
consensus: {
|
|
50111
|
+
keywords: ["vote", "validate", "decide", "consensus"],
|
|
50112
|
+
labels: ["consensus", "voting", "decision"]
|
|
50113
|
+
},
|
|
50114
|
+
specification: {
|
|
50115
|
+
keywords: ["spec", "rfc", "design", "specification"],
|
|
50116
|
+
labels: ["specification", "design", "rfc"]
|
|
50117
|
+
},
|
|
50118
|
+
decomposition: {
|
|
50119
|
+
keywords: ["epic", "plan", "decompose", "breakdown"],
|
|
49211
50120
|
labels: ["decomposition", "planning", "epic"]
|
|
49212
50121
|
},
|
|
49213
50122
|
implementation: {
|
|
@@ -49313,969 +50222,490 @@ async function getOtelSessions(opts) {
|
|
|
49313
50222
|
});
|
|
49314
50223
|
}
|
|
49315
50224
|
if (opts.task) {
|
|
49316
|
-
sessions2 = sessions2.filter((e) => e.task_id === opts.task);
|
|
49317
|
-
}
|
|
49318
|
-
return { sessions: sessions2, count: sessions2.length };
|
|
49319
|
-
}
|
|
49320
|
-
async function getOtelSpawns(opts) {
|
|
49321
|
-
const entries = readJsonlFile3(getTokenFilePath2());
|
|
49322
|
-
let spawns = entries.filter((e) => e.event_type !== "session_start");
|
|
49323
|
-
if (opts.task) {
|
|
49324
|
-
spawns = spawns.filter((e) => e.task_id === opts.task);
|
|
49325
|
-
}
|
|
49326
|
-
return { spawns, count: spawns.length };
|
|
49327
|
-
}
|
|
49328
|
-
async function getRealTokenUsage(opts) {
|
|
49329
|
-
const otelEnabled = process.env.CLAUDE_CODE_ENABLE_TELEMETRY === "1";
|
|
49330
|
-
const entries = readJsonlFile3(getTokenFilePath2());
|
|
49331
|
-
if (entries.length === 0) {
|
|
49332
|
-
return {
|
|
49333
|
-
message: otelEnabled ? "OTel enabled but no token data recorded yet" : "Real token usage requires OpenTelemetry configuration",
|
|
49334
|
-
otelEnabled,
|
|
49335
|
-
totalEvents: 0
|
|
49336
|
-
};
|
|
49337
|
-
}
|
|
49338
|
-
let filtered = entries;
|
|
49339
|
-
if (opts.session) {
|
|
49340
|
-
filtered = filtered.filter((e) => {
|
|
49341
|
-
const ctx = e.context ?? {};
|
|
49342
|
-
return ctx.session_id === opts.session || e.session_id === opts.session;
|
|
49343
|
-
});
|
|
49344
|
-
}
|
|
49345
|
-
if (opts.since) {
|
|
49346
|
-
const sinceDate = new Date(opts.since).getTime();
|
|
49347
|
-
filtered = filtered.filter((e) => {
|
|
49348
|
-
const ts = e.timestamp ?? e.recorded_at;
|
|
49349
|
-
return ts ? new Date(ts).getTime() >= sinceDate : true;
|
|
49350
|
-
});
|
|
49351
|
-
}
|
|
49352
|
-
const totalTokens = filtered.reduce((sum, e) => sum + (e.estimated_tokens ?? 0), 0);
|
|
49353
|
-
const inputTokens = filtered.reduce((sum, e) => sum + (e.input_tokens ?? 0), 0);
|
|
49354
|
-
const outputTokens = filtered.reduce((sum, e) => sum + (e.output_tokens ?? 0), 0);
|
|
49355
|
-
return {
|
|
49356
|
-
otelEnabled,
|
|
49357
|
-
totalEvents: filtered.length,
|
|
49358
|
-
totalTokens,
|
|
49359
|
-
inputTokens,
|
|
49360
|
-
outputTokens,
|
|
49361
|
-
filters: {
|
|
49362
|
-
session: opts.session ?? null,
|
|
49363
|
-
since: opts.since ?? null
|
|
49364
|
-
}
|
|
49365
|
-
};
|
|
49366
|
-
}
|
|
49367
|
-
async function clearOtelData() {
|
|
49368
|
-
const tokenFile = getTokenFilePath2();
|
|
49369
|
-
if (existsSync58(tokenFile)) {
|
|
49370
|
-
const backup = `${tokenFile}.backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
49371
|
-
copyFileSync6(tokenFile, backup);
|
|
49372
|
-
writeFileSync5(tokenFile, "");
|
|
49373
|
-
return { message: "Token tracking cleared", backup };
|
|
49374
|
-
}
|
|
49375
|
-
return { message: "No token file to clear" };
|
|
49376
|
-
}
|
|
49377
|
-
var init_otel = __esm({
|
|
49378
|
-
"packages/core/src/otel/index.ts"() {
|
|
49379
|
-
"use strict";
|
|
49380
|
-
}
|
|
49381
|
-
});
|
|
49382
|
-
|
|
49383
|
-
// packages/core/src/phases/index.ts
|
|
49384
|
-
var phases_exports = {};
|
|
49385
|
-
__export(phases_exports, {
|
|
49386
|
-
advancePhase: () => advancePhase,
|
|
49387
|
-
completePhase: () => completePhase,
|
|
49388
|
-
deletePhase: () => deletePhase,
|
|
49389
|
-
listPhases: () => listPhases,
|
|
49390
|
-
renamePhase: () => renamePhase,
|
|
49391
|
-
setPhase: () => setPhase,
|
|
49392
|
-
showPhase: () => showPhase,
|
|
49393
|
-
startPhase: () => startPhase
|
|
49394
|
-
});
|
|
49395
|
-
async function listPhases(_cwd, accessor) {
|
|
49396
|
-
const meta = await accessor.getMetaValue("project_meta");
|
|
49397
|
-
const phases = meta?.phases ?? {};
|
|
49398
|
-
const currentPhase = meta?.currentPhase ?? null;
|
|
49399
|
-
const entries = Object.entries(phases).map(([slug, phase]) => ({
|
|
49400
|
-
slug,
|
|
49401
|
-
name: phase.name,
|
|
49402
|
-
order: phase.order,
|
|
49403
|
-
status: phase.status,
|
|
49404
|
-
startedAt: phase.startedAt ?? null,
|
|
49405
|
-
completedAt: phase.completedAt ?? null,
|
|
49406
|
-
isCurrent: slug === currentPhase
|
|
49407
|
-
})).sort((a, b) => a.order - b.order);
|
|
49408
|
-
return {
|
|
49409
|
-
currentPhase,
|
|
49410
|
-
phases: entries,
|
|
49411
|
-
summary: {
|
|
49412
|
-
total: entries.length,
|
|
49413
|
-
pending: entries.filter((p) => p.status === "pending").length,
|
|
49414
|
-
active: entries.filter((p) => p.status === "active").length,
|
|
49415
|
-
completed: entries.filter((p) => p.status === "completed").length
|
|
49416
|
-
}
|
|
49417
|
-
};
|
|
49418
|
-
}
|
|
49419
|
-
async function showPhase(slug, _cwd, accessor) {
|
|
49420
|
-
const meta = await accessor.getMetaValue("project_meta");
|
|
49421
|
-
const targetSlug = slug ?? meta?.currentPhase ?? null;
|
|
49422
|
-
if (!targetSlug) {
|
|
49423
|
-
throw new CleoError(4 /* NOT_FOUND */, "No current phase set");
|
|
49424
|
-
}
|
|
49425
|
-
const phase = meta?.phases?.[targetSlug];
|
|
49426
|
-
if (!phase) {
|
|
49427
|
-
throw new CleoError(4 /* NOT_FOUND */, `Phase '${targetSlug}' not found`);
|
|
49428
|
-
}
|
|
49429
|
-
const { tasks: phaseTasks } = await accessor.queryTasks({ phase: targetSlug });
|
|
49430
|
-
const completedTasks = phaseTasks.filter((t) => t.status === "done");
|
|
49431
|
-
return {
|
|
49432
|
-
slug: targetSlug,
|
|
49433
|
-
name: phase.name,
|
|
49434
|
-
status: phase.status,
|
|
49435
|
-
order: phase.order,
|
|
49436
|
-
startedAt: phase.startedAt ?? null,
|
|
49437
|
-
completedAt: phase.completedAt ?? null,
|
|
49438
|
-
taskCount: phaseTasks.length,
|
|
49439
|
-
completedTaskCount: completedTasks.length
|
|
49440
|
-
};
|
|
49441
|
-
}
|
|
49442
|
-
async function setPhase(options, _cwd, accessor) {
|
|
49443
|
-
const meta = await accessor.getMetaValue("project_meta");
|
|
49444
|
-
const phases = meta?.phases ?? {};
|
|
49445
|
-
if (!phases[options.slug]) {
|
|
49446
|
-
throw new CleoError(4 /* NOT_FOUND */, `Phase '${options.slug}' does not exist`);
|
|
49447
|
-
}
|
|
49448
|
-
const oldPhase = meta?.currentPhase ?? null;
|
|
49449
|
-
let isRollback = false;
|
|
49450
|
-
let isSkip = false;
|
|
49451
|
-
let skippedPhases = 0;
|
|
49452
|
-
if (oldPhase && phases[oldPhase]) {
|
|
49453
|
-
const oldOrder = phases[oldPhase].order;
|
|
49454
|
-
const newOrder = phases[options.slug].order;
|
|
49455
|
-
if (newOrder < oldOrder) {
|
|
49456
|
-
isRollback = true;
|
|
49457
|
-
if (!options.rollback) {
|
|
49458
|
-
throw new CleoError(
|
|
49459
|
-
6 /* VALIDATION_ERROR */,
|
|
49460
|
-
`Rolling back from '${oldPhase}' (order ${oldOrder}) to '${options.slug}' (order ${newOrder}) requires --rollback flag`
|
|
49461
|
-
);
|
|
49462
|
-
}
|
|
49463
|
-
if (!options.force) {
|
|
49464
|
-
throw new CleoError(
|
|
49465
|
-
6 /* VALIDATION_ERROR */,
|
|
49466
|
-
"Rollback requires --force flag in non-interactive mode"
|
|
49467
|
-
);
|
|
49468
|
-
}
|
|
49469
|
-
} else if (newOrder > oldOrder + 1) {
|
|
49470
|
-
isSkip = true;
|
|
49471
|
-
skippedPhases = newOrder - oldOrder - 1;
|
|
49472
|
-
}
|
|
49473
|
-
}
|
|
49474
|
-
if (options.dryRun) {
|
|
49475
|
-
return {
|
|
49476
|
-
previousPhase: oldPhase,
|
|
49477
|
-
currentPhase: options.slug,
|
|
49478
|
-
isRollback,
|
|
49479
|
-
isSkip,
|
|
49480
|
-
...isSkip && { skippedPhases },
|
|
49481
|
-
dryRun: true
|
|
49482
|
-
};
|
|
49483
|
-
}
|
|
49484
|
-
const updatedMeta = meta ?? { name: "", phases: {} };
|
|
49485
|
-
updatedMeta.currentPhase = options.slug;
|
|
49486
|
-
if (isRollback && oldPhase) {
|
|
49487
|
-
const taskCount = await accessor.countTasks({ status: void 0 });
|
|
49488
|
-
addPhaseHistoryEntryToMeta(
|
|
49489
|
-
updatedMeta,
|
|
49490
|
-
options.slug,
|
|
49491
|
-
"rollback",
|
|
49492
|
-
oldPhase,
|
|
49493
|
-
`Rollback from ${oldPhase}`,
|
|
49494
|
-
taskCount
|
|
49495
|
-
);
|
|
49496
|
-
}
|
|
49497
|
-
await accessor.setMetaValue("project_meta", updatedMeta);
|
|
49498
|
-
await logOperation(
|
|
49499
|
-
"phase_set",
|
|
49500
|
-
options.slug,
|
|
49501
|
-
{
|
|
49502
|
-
previousPhase: oldPhase,
|
|
49503
|
-
isRollback
|
|
49504
|
-
},
|
|
49505
|
-
accessor
|
|
49506
|
-
);
|
|
49507
|
-
return {
|
|
49508
|
-
previousPhase: oldPhase,
|
|
49509
|
-
currentPhase: options.slug,
|
|
49510
|
-
isRollback,
|
|
49511
|
-
isSkip,
|
|
49512
|
-
...isSkip && {
|
|
49513
|
-
skippedPhases,
|
|
49514
|
-
warning: `Skipped ${skippedPhases} intermediate phase(s)`
|
|
49515
|
-
}
|
|
49516
|
-
};
|
|
49517
|
-
}
|
|
49518
|
-
async function startPhase(slug, _cwd, accessor) {
|
|
49519
|
-
const meta = await accessor.getMetaValue("project_meta");
|
|
49520
|
-
const phase = meta?.phases?.[slug];
|
|
49521
|
-
if (!phase) {
|
|
49522
|
-
throw new CleoError(4 /* NOT_FOUND */, `Phase '${slug}' does not exist`);
|
|
49523
|
-
}
|
|
49524
|
-
if (phase.status !== "pending") {
|
|
49525
|
-
throw new CleoError(
|
|
49526
|
-
2 /* INVALID_INPUT */,
|
|
49527
|
-
`Can only start pending phases. Phase '${slug}' has status '${phase.status}'`
|
|
49528
|
-
);
|
|
49529
|
-
}
|
|
49530
|
-
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
49531
|
-
phase.status = "active";
|
|
49532
|
-
phase.startedAt = now2;
|
|
49533
|
-
const updatedMeta = meta;
|
|
49534
|
-
const { tasks: phaseTasks } = await accessor.queryTasks({ phase: slug });
|
|
49535
|
-
addPhaseHistoryEntryToMeta(
|
|
49536
|
-
updatedMeta,
|
|
49537
|
-
slug,
|
|
49538
|
-
"started",
|
|
49539
|
-
null,
|
|
49540
|
-
"Phase started",
|
|
49541
|
-
phaseTasks.length
|
|
49542
|
-
);
|
|
49543
|
-
await accessor.setMetaValue("project_meta", updatedMeta);
|
|
49544
|
-
await logOperation("phase_started", slug, {}, accessor);
|
|
49545
|
-
return { phase: slug, startedAt: now2 };
|
|
49546
|
-
}
|
|
49547
|
-
async function completePhase(slug, _cwd, accessor) {
|
|
49548
|
-
const meta = await accessor.getMetaValue("project_meta");
|
|
49549
|
-
const phase = meta?.phases?.[slug];
|
|
49550
|
-
if (!phase) {
|
|
49551
|
-
throw new CleoError(4 /* NOT_FOUND */, `Phase '${slug}' does not exist`);
|
|
49552
|
-
}
|
|
49553
|
-
if (phase.status !== "active") {
|
|
49554
|
-
throw new CleoError(
|
|
49555
|
-
2 /* INVALID_INPUT */,
|
|
49556
|
-
`Can only complete active phases. Phase '${slug}' has status '${phase.status}'`
|
|
49557
|
-
);
|
|
49558
|
-
}
|
|
49559
|
-
const { tasks: phaseTasks } = await accessor.queryTasks({ phase: slug });
|
|
49560
|
-
const incompleteTasks = phaseTasks.filter((t) => t.status !== "done");
|
|
49561
|
-
if (incompleteTasks.length > 0) {
|
|
49562
|
-
throw new CleoError(
|
|
49563
|
-
6 /* VALIDATION_ERROR */,
|
|
49564
|
-
`Cannot complete phase '${slug}' - ${incompleteTasks.length} incomplete task(s) pending`
|
|
49565
|
-
);
|
|
49566
|
-
}
|
|
49567
|
-
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
49568
|
-
phase.status = "completed";
|
|
49569
|
-
phase.completedAt = now2;
|
|
49570
|
-
const updatedMeta = meta;
|
|
49571
|
-
addPhaseHistoryEntryToMeta(
|
|
49572
|
-
updatedMeta,
|
|
49573
|
-
slug,
|
|
49574
|
-
"completed",
|
|
49575
|
-
null,
|
|
49576
|
-
"Phase completed",
|
|
49577
|
-
phaseTasks.length
|
|
49578
|
-
);
|
|
49579
|
-
await accessor.setMetaValue("project_meta", updatedMeta);
|
|
49580
|
-
await logOperation("phase_completed", slug, {}, accessor);
|
|
49581
|
-
return { phase: slug, completedAt: now2 };
|
|
49582
|
-
}
|
|
49583
|
-
async function advancePhase(force = false, _cwd, accessor) {
|
|
49584
|
-
const meta = await accessor.getMetaValue("project_meta");
|
|
49585
|
-
const currentSlug = meta?.currentPhase ?? null;
|
|
49586
|
-
if (!currentSlug) {
|
|
49587
|
-
throw new CleoError(4 /* NOT_FOUND */, "No current phase set");
|
|
49588
|
-
}
|
|
49589
|
-
const phases = meta?.phases ?? {};
|
|
49590
|
-
const currentPhase = phases[currentSlug];
|
|
49591
|
-
if (!currentPhase) {
|
|
49592
|
-
throw new CleoError(4 /* NOT_FOUND */, `Current phase '${currentSlug}' not found`);
|
|
49593
|
-
}
|
|
49594
|
-
const sortedEntries = Object.entries(phases).sort(([, a], [, b]) => a.order - b.order);
|
|
49595
|
-
const currentIndex = sortedEntries.findIndex(([slug]) => slug === currentSlug);
|
|
49596
|
-
if (currentIndex === -1 || currentIndex >= sortedEntries.length - 1) {
|
|
49597
|
-
throw new CleoError(100 /* NO_DATA */, `No more phases after '${currentSlug}'`);
|
|
49598
|
-
}
|
|
49599
|
-
const [nextSlug] = sortedEntries[currentIndex + 1];
|
|
49600
|
-
const { tasks: phaseTasks } = await accessor.queryTasks({ phase: currentSlug });
|
|
49601
|
-
const incompleteTasks = phaseTasks.filter((t) => t.status !== "done");
|
|
49602
|
-
if (incompleteTasks.length > 0) {
|
|
49603
|
-
const criticalTasks = incompleteTasks.filter((t) => t.priority === "critical");
|
|
49604
|
-
if (criticalTasks.length > 0) {
|
|
49605
|
-
throw new CleoError(
|
|
49606
|
-
6 /* VALIDATION_ERROR */,
|
|
49607
|
-
`Cannot advance - ${criticalTasks.length} critical task(s) remain in phase '${currentSlug}'`
|
|
49608
|
-
);
|
|
49609
|
-
}
|
|
49610
|
-
const totalTasks = phaseTasks.length;
|
|
49611
|
-
const completionPercent = totalTasks > 0 ? Math.floor((totalTasks - incompleteTasks.length) * 100 / totalTasks) : 0;
|
|
49612
|
-
const threshold = 90;
|
|
49613
|
-
if (completionPercent < threshold && !force) {
|
|
49614
|
-
throw new CleoError(
|
|
49615
|
-
6 /* VALIDATION_ERROR */,
|
|
49616
|
-
`Cannot advance - ${incompleteTasks.length} incomplete task(s) in phase '${currentSlug}' (${completionPercent}% complete, threshold: ${threshold}%)`,
|
|
49617
|
-
{ fix: "Use --force to override" }
|
|
49618
|
-
);
|
|
49619
|
-
}
|
|
49620
|
-
}
|
|
49621
|
-
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
49622
|
-
currentPhase.status = "completed";
|
|
49623
|
-
currentPhase.completedAt = now2;
|
|
49624
|
-
const nextPhase = phases[nextSlug];
|
|
49625
|
-
nextPhase.status = "active";
|
|
49626
|
-
nextPhase.startedAt = now2;
|
|
49627
|
-
const updatedMeta = meta;
|
|
49628
|
-
updatedMeta.currentPhase = nextSlug;
|
|
49629
|
-
const currentPhaseTaskCount = phaseTasks.length;
|
|
49630
|
-
const { tasks: nextPhaseTasks } = await accessor.queryTasks({ phase: nextSlug });
|
|
49631
|
-
const nextPhaseTaskCount = nextPhaseTasks.length;
|
|
49632
|
-
addPhaseHistoryEntryToMeta(
|
|
49633
|
-
updatedMeta,
|
|
49634
|
-
currentSlug,
|
|
49635
|
-
"completed",
|
|
49636
|
-
null,
|
|
49637
|
-
"Phase completed via advance",
|
|
49638
|
-
currentPhaseTaskCount
|
|
49639
|
-
);
|
|
49640
|
-
addPhaseHistoryEntryToMeta(
|
|
49641
|
-
updatedMeta,
|
|
49642
|
-
nextSlug,
|
|
49643
|
-
"started",
|
|
49644
|
-
currentSlug,
|
|
49645
|
-
`Phase started via advance from ${currentSlug}`,
|
|
49646
|
-
nextPhaseTaskCount
|
|
49647
|
-
);
|
|
49648
|
-
await accessor.setMetaValue("project_meta", updatedMeta);
|
|
49649
|
-
return {
|
|
49650
|
-
previousPhase: currentSlug,
|
|
49651
|
-
currentPhase: nextSlug,
|
|
49652
|
-
forced: force
|
|
49653
|
-
};
|
|
49654
|
-
}
|
|
49655
|
-
async function renamePhase(oldName, newName, _cwd, accessor) {
|
|
49656
|
-
const meta = await accessor.getMetaValue("project_meta");
|
|
49657
|
-
const phases = meta?.phases ?? {};
|
|
49658
|
-
if (!phases[oldName]) {
|
|
49659
|
-
throw new CleoError(4 /* NOT_FOUND */, `Phase '${oldName}' does not exist`);
|
|
49660
|
-
}
|
|
49661
|
-
if (phases[newName]) {
|
|
49662
|
-
throw new CleoError(101 /* ALREADY_EXISTS */, `Phase '${newName}' already exists`);
|
|
49663
|
-
}
|
|
49664
|
-
if (!/^[a-z][a-z0-9-]*$/.test(newName)) {
|
|
49665
|
-
throw new CleoError(2 /* INVALID_INPUT */, `Invalid phase name '${newName}'`);
|
|
49666
|
-
}
|
|
49667
|
-
phases[newName] = phases[oldName];
|
|
49668
|
-
delete phases[oldName];
|
|
49669
|
-
const { tasks: oldPhaseTasks } = await accessor.queryTasks({ phase: oldName });
|
|
49670
|
-
let tasksUpdated = 0;
|
|
49671
|
-
for (const task of oldPhaseTasks) {
|
|
49672
|
-
task.phase = newName;
|
|
49673
|
-
await accessor.upsertSingleTask(task);
|
|
49674
|
-
tasksUpdated++;
|
|
49675
|
-
}
|
|
49676
|
-
const updatedMeta = meta;
|
|
49677
|
-
let currentPhaseUpdated = false;
|
|
49678
|
-
if (updatedMeta.currentPhase === oldName) {
|
|
49679
|
-
updatedMeta.currentPhase = newName;
|
|
49680
|
-
currentPhaseUpdated = true;
|
|
49681
|
-
}
|
|
49682
|
-
await accessor.setMetaValue("project_meta", updatedMeta);
|
|
49683
|
-
const focus = await accessor.getMetaValue("focus_state");
|
|
49684
|
-
if (focus?.currentPhase === oldName) {
|
|
49685
|
-
focus.currentPhase = newName;
|
|
49686
|
-
await accessor.setMetaValue("focus_state", focus);
|
|
49687
|
-
}
|
|
49688
|
-
return { oldName, newName, tasksUpdated, currentPhaseUpdated };
|
|
49689
|
-
}
|
|
49690
|
-
async function deletePhase(slug, options = {}, _cwd, accessor) {
|
|
49691
|
-
const meta = await accessor.getMetaValue("project_meta");
|
|
49692
|
-
const phases = meta?.phases ?? {};
|
|
49693
|
-
if (!phases[slug]) {
|
|
49694
|
-
throw new CleoError(4 /* NOT_FOUND */, `Phase '${slug}' does not exist`);
|
|
49695
|
-
}
|
|
49696
|
-
if (meta?.currentPhase === slug) {
|
|
49697
|
-
throw new CleoError(
|
|
49698
|
-
6 /* VALIDATION_ERROR */,
|
|
49699
|
-
`Cannot delete current project phase '${slug}'. Use 'phase set' to change phase first`
|
|
49700
|
-
);
|
|
49701
|
-
}
|
|
49702
|
-
const { tasks: phaseTasks } = await accessor.queryTasks({ phase: slug });
|
|
49703
|
-
if (phaseTasks.length > 0 && !options.reassignTo) {
|
|
49704
|
-
throw new CleoError(
|
|
49705
|
-
6 /* VALIDATION_ERROR */,
|
|
49706
|
-
`Cannot delete '${slug}': ${phaseTasks.length} tasks would be orphaned. Use --reassign-to <phase>`
|
|
49707
|
-
);
|
|
49708
|
-
}
|
|
49709
|
-
if (!options.force) {
|
|
49710
|
-
throw new CleoError(2 /* INVALID_INPUT */, "Phase deletion requires --force flag for safety");
|
|
49711
|
-
}
|
|
49712
|
-
if (options.reassignTo) {
|
|
49713
|
-
if (!phases[options.reassignTo]) {
|
|
49714
|
-
throw new CleoError(
|
|
49715
|
-
4 /* NOT_FOUND */,
|
|
49716
|
-
`Reassignment target phase '${options.reassignTo}' does not exist`
|
|
49717
|
-
);
|
|
49718
|
-
}
|
|
49719
|
-
}
|
|
49720
|
-
let tasksReassigned = 0;
|
|
49721
|
-
if (options.reassignTo) {
|
|
49722
|
-
for (const task of phaseTasks) {
|
|
49723
|
-
task.phase = options.reassignTo;
|
|
49724
|
-
await accessor.upsertSingleTask(task);
|
|
49725
|
-
tasksReassigned++;
|
|
49726
|
-
}
|
|
49727
|
-
}
|
|
49728
|
-
delete phases[slug];
|
|
49729
|
-
await accessor.setMetaValue("project_meta", meta);
|
|
49730
|
-
return {
|
|
49731
|
-
deletedPhase: slug,
|
|
49732
|
-
tasksReassigned,
|
|
49733
|
-
reassignedTo: options.reassignTo ?? null
|
|
49734
|
-
};
|
|
49735
|
-
}
|
|
49736
|
-
function addPhaseHistoryEntryToMeta(meta, phase, transitionType, fromPhase, reason, taskCount) {
|
|
49737
|
-
if (!meta.phaseHistory) {
|
|
49738
|
-
meta.phaseHistory = [];
|
|
49739
|
-
}
|
|
49740
|
-
meta.phaseHistory.push({
|
|
49741
|
-
phase,
|
|
49742
|
-
transitionType,
|
|
49743
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
49744
|
-
taskCount,
|
|
49745
|
-
fromPhase,
|
|
49746
|
-
reason
|
|
49747
|
-
});
|
|
49748
|
-
}
|
|
49749
|
-
var init_phases = __esm({
|
|
49750
|
-
"packages/core/src/phases/index.ts"() {
|
|
49751
|
-
"use strict";
|
|
49752
|
-
init_src();
|
|
49753
|
-
init_errors3();
|
|
49754
|
-
init_add();
|
|
50225
|
+
sessions2 = sessions2.filter((e) => e.task_id === opts.task);
|
|
49755
50226
|
}
|
|
49756
|
-
}
|
|
49757
|
-
|
|
49758
|
-
|
|
49759
|
-
|
|
49760
|
-
|
|
49761
|
-
|
|
49762
|
-
|
|
49763
|
-
} catch (err) {
|
|
49764
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
49765
|
-
return {
|
|
49766
|
-
success: false,
|
|
49767
|
-
error: { code: "E_PHASE_LIST_FAILED", message }
|
|
49768
|
-
};
|
|
50227
|
+
return { sessions: sessions2, count: sessions2.length };
|
|
50228
|
+
}
|
|
50229
|
+
async function getOtelSpawns(opts) {
|
|
50230
|
+
const entries = readJsonlFile3(getTokenFilePath2());
|
|
50231
|
+
let spawns = entries.filter((e) => e.event_type !== "session_start");
|
|
50232
|
+
if (opts.task) {
|
|
50233
|
+
spawns = spawns.filter((e) => e.task_id === opts.task);
|
|
49769
50234
|
}
|
|
50235
|
+
return { spawns, count: spawns.length };
|
|
49770
50236
|
}
|
|
49771
|
-
async function
|
|
49772
|
-
|
|
49773
|
-
|
|
49774
|
-
|
|
49775
|
-
} catch (err) {
|
|
49776
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
50237
|
+
async function getRealTokenUsage(opts) {
|
|
50238
|
+
const otelEnabled = process.env.CLAUDE_CODE_ENABLE_TELEMETRY === "1";
|
|
50239
|
+
const entries = readJsonlFile3(getTokenFilePath2());
|
|
50240
|
+
if (entries.length === 0) {
|
|
49777
50241
|
return {
|
|
49778
|
-
|
|
49779
|
-
|
|
50242
|
+
message: otelEnabled ? "OTel enabled but no token data recorded yet" : "Real token usage requires OpenTelemetry configuration",
|
|
50243
|
+
otelEnabled,
|
|
50244
|
+
totalEvents: 0
|
|
49780
50245
|
};
|
|
49781
50246
|
}
|
|
50247
|
+
let filtered = entries;
|
|
50248
|
+
if (opts.session) {
|
|
50249
|
+
filtered = filtered.filter((e) => {
|
|
50250
|
+
const ctx = e.context ?? {};
|
|
50251
|
+
return ctx.session_id === opts.session || e.session_id === opts.session;
|
|
50252
|
+
});
|
|
50253
|
+
}
|
|
50254
|
+
if (opts.since) {
|
|
50255
|
+
const sinceDate = new Date(opts.since).getTime();
|
|
50256
|
+
filtered = filtered.filter((e) => {
|
|
50257
|
+
const ts = e.timestamp ?? e.recorded_at;
|
|
50258
|
+
return ts ? new Date(ts).getTime() >= sinceDate : true;
|
|
50259
|
+
});
|
|
50260
|
+
}
|
|
50261
|
+
const totalTokens = filtered.reduce((sum, e) => sum + (e.estimated_tokens ?? 0), 0);
|
|
50262
|
+
const inputTokens = filtered.reduce((sum, e) => sum + (e.input_tokens ?? 0), 0);
|
|
50263
|
+
const outputTokens = filtered.reduce((sum, e) => sum + (e.output_tokens ?? 0), 0);
|
|
50264
|
+
return {
|
|
50265
|
+
otelEnabled,
|
|
50266
|
+
totalEvents: filtered.length,
|
|
50267
|
+
totalTokens,
|
|
50268
|
+
inputTokens,
|
|
50269
|
+
outputTokens,
|
|
50270
|
+
filters: {
|
|
50271
|
+
session: opts.session ?? null,
|
|
50272
|
+
since: opts.since ?? null
|
|
50273
|
+
}
|
|
50274
|
+
};
|
|
50275
|
+
}
|
|
50276
|
+
async function clearOtelData() {
|
|
50277
|
+
const tokenFile = getTokenFilePath2();
|
|
50278
|
+
if (existsSync58(tokenFile)) {
|
|
50279
|
+
const backup = `${tokenFile}.backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
50280
|
+
copyFileSync6(tokenFile, backup);
|
|
50281
|
+
writeFileSync5(tokenFile, "");
|
|
50282
|
+
return { message: "Token tracking cleared", backup };
|
|
50283
|
+
}
|
|
50284
|
+
return { message: "No token file to clear" };
|
|
49782
50285
|
}
|
|
49783
|
-
var
|
|
49784
|
-
"packages/core/src/
|
|
50286
|
+
var init_otel = __esm({
|
|
50287
|
+
"packages/core/src/otel/index.ts"() {
|
|
49785
50288
|
"use strict";
|
|
49786
|
-
init_phases();
|
|
49787
50289
|
}
|
|
49788
50290
|
});
|
|
49789
50291
|
|
|
49790
|
-
// packages/core/src/
|
|
49791
|
-
var
|
|
49792
|
-
__export(
|
|
49793
|
-
|
|
49794
|
-
|
|
49795
|
-
|
|
49796
|
-
|
|
49797
|
-
|
|
49798
|
-
|
|
49799
|
-
|
|
49800
|
-
|
|
50292
|
+
// packages/core/src/phases/index.ts
|
|
50293
|
+
var phases_exports = {};
|
|
50294
|
+
__export(phases_exports, {
|
|
50295
|
+
advancePhase: () => advancePhase,
|
|
50296
|
+
completePhase: () => completePhase,
|
|
50297
|
+
deletePhase: () => deletePhase,
|
|
50298
|
+
listPhases: () => listPhases,
|
|
50299
|
+
renamePhase: () => renamePhase,
|
|
50300
|
+
setPhase: () => setPhase,
|
|
50301
|
+
showPhase: () => showPhase,
|
|
50302
|
+
startPhase: () => startPhase
|
|
49801
50303
|
});
|
|
49802
|
-
|
|
49803
|
-
|
|
49804
|
-
|
|
49805
|
-
|
|
49806
|
-
|
|
49807
|
-
|
|
49808
|
-
|
|
49809
|
-
|
|
49810
|
-
|
|
49811
|
-
|
|
49812
|
-
|
|
49813
|
-
|
|
49814
|
-
|
|
49815
|
-
const verificationEnabled = rawVerificationEnabled !== void 0 ? rawVerificationEnabled : !isTest;
|
|
49816
|
-
const verificationRequiredGates = (verificationCfg?.requiredGates ?? []).filter(isVerificationGate).length > 0 ? (verificationCfg?.requiredGates ?? []).filter(isVerificationGate) : DEFAULT_VERIFICATION_REQUIRED_GATES;
|
|
49817
|
-
const verificationMaxRounds = verificationCfg?.maxRounds ?? 5;
|
|
49818
|
-
const lifecycleMode = config2.lifecycle?.mode ?? (isTest ? "off" : "strict");
|
|
50304
|
+
async function listPhases(_cwd, accessor) {
|
|
50305
|
+
const meta = await accessor.getMetaValue("project_meta");
|
|
50306
|
+
const phases = meta?.phases ?? {};
|
|
50307
|
+
const currentPhase = meta?.currentPhase ?? null;
|
|
50308
|
+
const entries = Object.entries(phases).map(([slug, phase]) => ({
|
|
50309
|
+
slug,
|
|
50310
|
+
name: phase.name,
|
|
50311
|
+
order: phase.order,
|
|
50312
|
+
status: phase.status,
|
|
50313
|
+
startedAt: phase.startedAt ?? null,
|
|
50314
|
+
completedAt: phase.completedAt ?? null,
|
|
50315
|
+
isCurrent: slug === currentPhase
|
|
50316
|
+
})).sort((a, b) => a.order - b.order);
|
|
49819
50317
|
return {
|
|
49820
|
-
|
|
49821
|
-
|
|
49822
|
-
|
|
49823
|
-
|
|
49824
|
-
|
|
49825
|
-
|
|
50318
|
+
currentPhase,
|
|
50319
|
+
phases: entries,
|
|
50320
|
+
summary: {
|
|
50321
|
+
total: entries.length,
|
|
50322
|
+
pending: entries.filter((p) => p.status === "pending").length,
|
|
50323
|
+
active: entries.filter((p) => p.status === "active").length,
|
|
50324
|
+
completed: entries.filter((p) => p.status === "completed").length
|
|
50325
|
+
}
|
|
49826
50326
|
};
|
|
49827
50327
|
}
|
|
49828
|
-
async function
|
|
49829
|
-
const
|
|
49830
|
-
const
|
|
49831
|
-
if (!
|
|
49832
|
-
throw new CleoError(4 /* NOT_FOUND */,
|
|
49833
|
-
fix: `Use 'cleo find "${options.taskId}"' to search`
|
|
49834
|
-
});
|
|
50328
|
+
async function showPhase(slug, _cwd, accessor) {
|
|
50329
|
+
const meta = await accessor.getMetaValue("project_meta");
|
|
50330
|
+
const targetSlug = slug ?? meta?.currentPhase ?? null;
|
|
50331
|
+
if (!targetSlug) {
|
|
50332
|
+
throw new CleoError(4 /* NOT_FOUND */, "No current phase set");
|
|
49835
50333
|
}
|
|
49836
|
-
|
|
49837
|
-
|
|
49838
|
-
|
|
49839
|
-
throw new CleoError(17 /* TASK_COMPLETED */, `Task ${options.taskId} is already completed`);
|
|
50334
|
+
const phase = meta?.phases?.[targetSlug];
|
|
50335
|
+
if (!phase) {
|
|
50336
|
+
throw new CleoError(4 /* NOT_FOUND */, `Phase '${targetSlug}' not found`);
|
|
49840
50337
|
}
|
|
49841
|
-
|
|
49842
|
-
|
|
49843
|
-
|
|
49844
|
-
|
|
49845
|
-
|
|
49846
|
-
|
|
49847
|
-
|
|
49848
|
-
|
|
49849
|
-
|
|
49850
|
-
|
|
49851
|
-
|
|
50338
|
+
const { tasks: phaseTasks } = await accessor.queryTasks({ phase: targetSlug });
|
|
50339
|
+
const completedTasks = phaseTasks.filter((t) => t.status === "done");
|
|
50340
|
+
return {
|
|
50341
|
+
slug: targetSlug,
|
|
50342
|
+
name: phase.name,
|
|
50343
|
+
status: phase.status,
|
|
50344
|
+
order: phase.order,
|
|
50345
|
+
startedAt: phase.startedAt ?? null,
|
|
50346
|
+
completedAt: phase.completedAt ?? null,
|
|
50347
|
+
taskCount: phaseTasks.length,
|
|
50348
|
+
completedTaskCount: completedTasks.length
|
|
50349
|
+
};
|
|
50350
|
+
}
|
|
50351
|
+
async function setPhase(options, _cwd, accessor) {
|
|
50352
|
+
const meta = await accessor.getMetaValue("project_meta");
|
|
50353
|
+
const phases = meta?.phases ?? {};
|
|
50354
|
+
if (!phases[options.slug]) {
|
|
50355
|
+
throw new CleoError(4 /* NOT_FOUND */, `Phase '${options.slug}' does not exist`);
|
|
50356
|
+
}
|
|
50357
|
+
const oldPhase = meta?.currentPhase ?? null;
|
|
50358
|
+
let isRollback = false;
|
|
50359
|
+
let isSkip = false;
|
|
50360
|
+
let skippedPhases = 0;
|
|
50361
|
+
if (oldPhase && phases[oldPhase]) {
|
|
50362
|
+
const oldOrder = phases[oldPhase].order;
|
|
50363
|
+
const newOrder = phases[options.slug].order;
|
|
50364
|
+
if (newOrder < oldOrder) {
|
|
50365
|
+
isRollback = true;
|
|
50366
|
+
if (!options.rollback) {
|
|
50367
|
+
throw new CleoError(
|
|
50368
|
+
6 /* VALIDATION_ERROR */,
|
|
50369
|
+
`Rolling back from '${oldPhase}' (order ${oldOrder}) to '${options.slug}' (order ${newOrder}) requires --rollback flag`
|
|
50370
|
+
);
|
|
50371
|
+
}
|
|
50372
|
+
if (!options.force) {
|
|
50373
|
+
throw new CleoError(
|
|
50374
|
+
6 /* VALIDATION_ERROR */,
|
|
50375
|
+
"Rollback requires --force flag in non-interactive mode"
|
|
50376
|
+
);
|
|
50377
|
+
}
|
|
50378
|
+
} else if (newOrder > oldOrder + 1) {
|
|
50379
|
+
isSkip = true;
|
|
50380
|
+
skippedPhases = newOrder - oldOrder - 1;
|
|
49852
50381
|
}
|
|
49853
50382
|
}
|
|
49854
|
-
|
|
49855
|
-
|
|
49856
|
-
|
|
49857
|
-
|
|
49858
|
-
|
|
49859
|
-
|
|
49860
|
-
{
|
|
49861
|
-
|
|
50383
|
+
if (options.dryRun) {
|
|
50384
|
+
return {
|
|
50385
|
+
previousPhase: oldPhase,
|
|
50386
|
+
currentPhase: options.slug,
|
|
50387
|
+
isRollback,
|
|
50388
|
+
isSkip,
|
|
50389
|
+
...isSkip && { skippedPhases },
|
|
50390
|
+
dryRun: true
|
|
50391
|
+
};
|
|
49862
50392
|
}
|
|
49863
|
-
|
|
49864
|
-
|
|
49865
|
-
|
|
49866
|
-
|
|
49867
|
-
|
|
49868
|
-
|
|
49869
|
-
|
|
49870
|
-
|
|
49871
|
-
|
|
49872
|
-
|
|
49873
|
-
|
|
49874
|
-
throw new CleoError(
|
|
49875
|
-
44 /* MAX_ROUNDS_EXCEEDED */,
|
|
49876
|
-
`Task ${options.taskId} exceeded verification max rounds (${enforcement.verificationMaxRounds})`,
|
|
49877
|
-
{
|
|
49878
|
-
fix: `Review failure log and resolve blockers before retrying completion`
|
|
49879
|
-
}
|
|
49880
|
-
);
|
|
49881
|
-
}
|
|
49882
|
-
const missingRequiredGates = enforcement.verificationRequiredGates.filter(
|
|
49883
|
-
(gate) => task.verification?.gates?.[gate] !== true
|
|
50393
|
+
const updatedMeta = meta ?? { name: "", phases: {} };
|
|
50394
|
+
updatedMeta.currentPhase = options.slug;
|
|
50395
|
+
if (isRollback && oldPhase) {
|
|
50396
|
+
const taskCount = await accessor.countTasks({ status: void 0 });
|
|
50397
|
+
addPhaseHistoryEntryToMeta(
|
|
50398
|
+
updatedMeta,
|
|
50399
|
+
options.slug,
|
|
50400
|
+
"rollback",
|
|
50401
|
+
oldPhase,
|
|
50402
|
+
`Rollback from ${oldPhase}`,
|
|
50403
|
+
taskCount
|
|
49884
50404
|
);
|
|
49885
|
-
if (missingRequiredGates.length > 0 || task.verification.passed !== true) {
|
|
49886
|
-
const exitCode = enforcement.lifecycleMode === "strict" ? 80 /* LIFECYCLE_GATE_FAILED */ : 45 /* GATE_DEPENDENCY */;
|
|
49887
|
-
throw new CleoError(
|
|
49888
|
-
exitCode,
|
|
49889
|
-
`Task ${options.taskId} failed verification gates: ${missingRequiredGates.join(", ") || "verification.passed=false"}`,
|
|
49890
|
-
{
|
|
49891
|
-
fix: `Set required verification gates before completion: ${enforcement.verificationRequiredGates.join(", ")}`
|
|
49892
|
-
}
|
|
49893
|
-
);
|
|
49894
|
-
}
|
|
49895
50405
|
}
|
|
49896
|
-
|
|
49897
|
-
|
|
49898
|
-
|
|
50406
|
+
await accessor.setMetaValue("project_meta", updatedMeta);
|
|
50407
|
+
await logOperation(
|
|
50408
|
+
"phase_set",
|
|
50409
|
+
options.slug,
|
|
50410
|
+
{
|
|
50411
|
+
previousPhase: oldPhase,
|
|
50412
|
+
isRollback
|
|
50413
|
+
},
|
|
50414
|
+
accessor
|
|
49899
50415
|
);
|
|
49900
|
-
if (incompleteChildren.length > 0 && task.type === "epic") {
|
|
49901
|
-
if (!task.noAutoComplete) {
|
|
49902
|
-
throw new CleoError(
|
|
49903
|
-
16 /* HAS_CHILDREN */,
|
|
49904
|
-
`Epic ${options.taskId} has ${incompleteChildren.length} incomplete children: ${incompleteChildren.map((c) => c.id).join(", ")}`,
|
|
49905
|
-
{
|
|
49906
|
-
fix: `Complete children first or use 'cleo update ${options.taskId} --no-auto-complete'`
|
|
49907
|
-
}
|
|
49908
|
-
);
|
|
49909
|
-
}
|
|
49910
|
-
}
|
|
49911
|
-
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
49912
|
-
const before = { ...task };
|
|
49913
|
-
task.status = "done";
|
|
49914
|
-
task.completedAt = now2;
|
|
49915
|
-
task.updatedAt = now2;
|
|
49916
|
-
if (options.notes) {
|
|
49917
|
-
const timestampedNote = `${(/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace(/\.\d+Z$/, " UTC")}: ${options.notes}`;
|
|
49918
|
-
if (!task.notes) task.notes = [];
|
|
49919
|
-
task.notes.push(timestampedNote);
|
|
49920
|
-
}
|
|
49921
|
-
if (options.changeset) {
|
|
49922
|
-
if (!task.notes) task.notes = [];
|
|
49923
|
-
task.notes.push(`Changeset: ${options.changeset}`);
|
|
49924
|
-
}
|
|
49925
|
-
const autoCompleted = [];
|
|
49926
|
-
const autoCompletedTasks = [];
|
|
49927
|
-
if (task.parentId) {
|
|
49928
|
-
const parent = await acc.loadSingleTask(task.parentId);
|
|
49929
|
-
if (parent && parent.type === "epic" && !parent.noAutoComplete) {
|
|
49930
|
-
const siblings = await acc.getChildren(parent.id);
|
|
49931
|
-
const allDone = siblings.every(
|
|
49932
|
-
(c) => c.id === task.id || c.status === "done" || c.status === "cancelled"
|
|
49933
|
-
);
|
|
49934
|
-
if (allDone) {
|
|
49935
|
-
parent.status = "done";
|
|
49936
|
-
parent.completedAt = now2;
|
|
49937
|
-
parent.updatedAt = now2;
|
|
49938
|
-
autoCompleted.push(parent.id);
|
|
49939
|
-
autoCompletedTasks.push(parent);
|
|
49940
|
-
}
|
|
49941
|
-
}
|
|
49942
|
-
}
|
|
49943
|
-
await acc.transaction(async (tx) => {
|
|
49944
|
-
await tx.upsertSingleTask(task);
|
|
49945
|
-
for (const parentTask of autoCompletedTasks) {
|
|
49946
|
-
await tx.upsertSingleTask(parentTask);
|
|
49947
|
-
}
|
|
49948
|
-
await tx.appendLog({
|
|
49949
|
-
id: `log-${Math.floor(Date.now() / 1e3)}-${(await import("node:crypto")).randomBytes(3).toString("hex")}`,
|
|
49950
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
49951
|
-
action: "task_completed",
|
|
49952
|
-
taskId: options.taskId,
|
|
49953
|
-
actor: "system",
|
|
49954
|
-
details: { title: task.title, previousStatus: before.status },
|
|
49955
|
-
before: null,
|
|
49956
|
-
after: { title: task.title, previousStatus: before.status }
|
|
49957
|
-
});
|
|
49958
|
-
});
|
|
49959
|
-
const dependents = await acc.getDependents(options.taskId);
|
|
49960
|
-
const unblockedTasks = [];
|
|
49961
|
-
for (const dep of dependents) {
|
|
49962
|
-
if (dep.status === "done" || dep.status === "cancelled") continue;
|
|
49963
|
-
if (dep.depends?.length) {
|
|
49964
|
-
const depDeps = await acc.loadTasks(dep.depends);
|
|
49965
|
-
const stillUnresolved = depDeps.filter(
|
|
49966
|
-
(d) => d.id !== options.taskId && d.status !== "done" && d.status !== "cancelled"
|
|
49967
|
-
);
|
|
49968
|
-
if (stillUnresolved.length === 0) {
|
|
49969
|
-
unblockedTasks.push({ id: dep.id, title: dep.title });
|
|
49970
|
-
}
|
|
49971
|
-
} else {
|
|
49972
|
-
unblockedTasks.push({ id: dep.id, title: dep.title });
|
|
49973
|
-
}
|
|
49974
|
-
}
|
|
49975
|
-
Promise.resolve().then(() => (init_auto_extract(), auto_extract_exports)).then(
|
|
49976
|
-
({ extractTaskCompletionMemory: extractTaskCompletionMemory2 }) => extractTaskCompletionMemory2(cwd ?? process.cwd(), task)
|
|
49977
|
-
).catch(() => {
|
|
49978
|
-
});
|
|
49979
50416
|
return {
|
|
49980
|
-
|
|
49981
|
-
|
|
49982
|
-
|
|
50417
|
+
previousPhase: oldPhase,
|
|
50418
|
+
currentPhase: options.slug,
|
|
50419
|
+
isRollback,
|
|
50420
|
+
isSkip,
|
|
50421
|
+
...isSkip && {
|
|
50422
|
+
skippedPhases,
|
|
50423
|
+
warning: `Skipped ${skippedPhases} intermediate phase(s)`
|
|
50424
|
+
}
|
|
49983
50425
|
};
|
|
49984
50426
|
}
|
|
49985
|
-
|
|
49986
|
-
|
|
49987
|
-
|
|
49988
|
-
|
|
49989
|
-
|
|
49990
|
-
init_config();
|
|
49991
|
-
init_errors3();
|
|
49992
|
-
init_session_enforcement();
|
|
49993
|
-
init_data_accessor();
|
|
49994
|
-
init_enforcement();
|
|
49995
|
-
DEFAULT_VERIFICATION_REQUIRED_GATES = [
|
|
49996
|
-
"implemented",
|
|
49997
|
-
"testsPassed",
|
|
49998
|
-
"qaPassed",
|
|
49999
|
-
"securityPassed",
|
|
50000
|
-
"documented"
|
|
50001
|
-
];
|
|
50002
|
-
VERIFICATION_GATES = /* @__PURE__ */ new Set([
|
|
50003
|
-
"implemented",
|
|
50004
|
-
"testsPassed",
|
|
50005
|
-
"qaPassed",
|
|
50006
|
-
"cleanupDone",
|
|
50007
|
-
"securityPassed",
|
|
50008
|
-
"documented"
|
|
50009
|
-
]);
|
|
50427
|
+
async function startPhase(slug, _cwd, accessor) {
|
|
50428
|
+
const meta = await accessor.getMetaValue("project_meta");
|
|
50429
|
+
const phase = meta?.phases?.[slug];
|
|
50430
|
+
if (!phase) {
|
|
50431
|
+
throw new CleoError(4 /* NOT_FOUND */, `Phase '${slug}' does not exist`);
|
|
50010
50432
|
}
|
|
50011
|
-
|
|
50012
|
-
|
|
50013
|
-
|
|
50014
|
-
|
|
50015
|
-
|
|
50016
|
-
updateTask: () => updateTask
|
|
50017
|
-
});
|
|
50018
|
-
function hasNonStatusDoneFields(options) {
|
|
50019
|
-
return NON_STATUS_DONE_FIELDS.some((field) => options[field] !== void 0);
|
|
50020
|
-
}
|
|
50021
|
-
async function updateTask(options, cwd, accessor) {
|
|
50022
|
-
const acc = accessor ?? await getAccessor(cwd);
|
|
50023
|
-
const task = await acc.loadSingleTask(options.taskId);
|
|
50024
|
-
if (!task) {
|
|
50025
|
-
throw new CleoError(4 /* NOT_FOUND */, `Task not found: ${options.taskId}`, {
|
|
50026
|
-
fix: `Use 'cleo find "${options.taskId}"' to search`
|
|
50027
|
-
});
|
|
50433
|
+
if (phase.status !== "pending") {
|
|
50434
|
+
throw new CleoError(
|
|
50435
|
+
2 /* INVALID_INPUT */,
|
|
50436
|
+
`Can only start pending phases. Phase '${slug}' has status '${phase.status}'`
|
|
50437
|
+
);
|
|
50028
50438
|
}
|
|
50029
|
-
await requireActiveSession("tasks.update", cwd);
|
|
50030
|
-
const changes = [];
|
|
50031
50439
|
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
50032
|
-
|
|
50033
|
-
|
|
50034
|
-
|
|
50035
|
-
|
|
50440
|
+
phase.status = "active";
|
|
50441
|
+
phase.startedAt = now2;
|
|
50442
|
+
const updatedMeta = meta;
|
|
50443
|
+
const { tasks: phaseTasks } = await accessor.queryTasks({ phase: slug });
|
|
50444
|
+
addPhaseHistoryEntryToMeta(
|
|
50445
|
+
updatedMeta,
|
|
50446
|
+
slug,
|
|
50447
|
+
"started",
|
|
50448
|
+
null,
|
|
50449
|
+
"Phase started",
|
|
50450
|
+
phaseTasks.length
|
|
50451
|
+
);
|
|
50452
|
+
await accessor.setMetaValue("project_meta", updatedMeta);
|
|
50453
|
+
await logOperation("phase_started", slug, {}, accessor);
|
|
50454
|
+
return { phase: slug, startedAt: now2 };
|
|
50455
|
+
}
|
|
50456
|
+
async function completePhase(slug, _cwd, accessor) {
|
|
50457
|
+
const meta = await accessor.getMetaValue("project_meta");
|
|
50458
|
+
const phase = meta?.phases?.[slug];
|
|
50459
|
+
if (!phase) {
|
|
50460
|
+
throw new CleoError(4 /* NOT_FOUND */, `Phase '${slug}' does not exist`);
|
|
50036
50461
|
}
|
|
50037
|
-
if (
|
|
50462
|
+
if (phase.status !== "active") {
|
|
50038
50463
|
throw new CleoError(
|
|
50039
|
-
|
|
50040
|
-
|
|
50041
|
-
{
|
|
50042
|
-
fix: `Run 'cleo complete ${options.taskId}' first, then apply additional updates with 'cleo update ${options.taskId} ...'`
|
|
50043
|
-
}
|
|
50464
|
+
2 /* INVALID_INPUT */,
|
|
50465
|
+
`Can only complete active phases. Phase '${slug}' has status '${phase.status}'`
|
|
50044
50466
|
);
|
|
50045
50467
|
}
|
|
50046
|
-
const
|
|
50047
|
-
const
|
|
50048
|
-
if (
|
|
50468
|
+
const { tasks: phaseTasks } = await accessor.queryTasks({ phase: slug });
|
|
50469
|
+
const incompleteTasks = phaseTasks.filter((t) => t.status !== "done");
|
|
50470
|
+
if (incompleteTasks.length > 0) {
|
|
50049
50471
|
throw new CleoError(
|
|
50050
|
-
|
|
50051
|
-
|
|
50052
|
-
{ fix: updateValidation.fix }
|
|
50472
|
+
6 /* VALIDATION_ERROR */,
|
|
50473
|
+
`Cannot complete phase '${slug}' - ${incompleteTasks.length} incomplete task(s) pending`
|
|
50053
50474
|
);
|
|
50054
50475
|
}
|
|
50055
|
-
|
|
50056
|
-
|
|
50057
|
-
|
|
50058
|
-
|
|
50059
|
-
|
|
50060
|
-
|
|
50061
|
-
|
|
50062
|
-
|
|
50063
|
-
|
|
50064
|
-
|
|
50065
|
-
|
|
50066
|
-
|
|
50067
|
-
|
|
50068
|
-
|
|
50069
|
-
|
|
50070
|
-
|
|
50071
|
-
|
|
50072
|
-
|
|
50073
|
-
|
|
50074
|
-
|
|
50075
|
-
|
|
50076
|
-
}
|
|
50077
|
-
if (options.type !== void 0) {
|
|
50078
|
-
validateTaskType(options.type);
|
|
50079
|
-
task.type = options.type;
|
|
50080
|
-
changes.push("type");
|
|
50081
|
-
}
|
|
50082
|
-
if (options.size !== void 0) {
|
|
50083
|
-
validateSize(options.size);
|
|
50084
|
-
task.size = options.size;
|
|
50085
|
-
changes.push("size");
|
|
50476
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
50477
|
+
phase.status = "completed";
|
|
50478
|
+
phase.completedAt = now2;
|
|
50479
|
+
const updatedMeta = meta;
|
|
50480
|
+
addPhaseHistoryEntryToMeta(
|
|
50481
|
+
updatedMeta,
|
|
50482
|
+
slug,
|
|
50483
|
+
"completed",
|
|
50484
|
+
null,
|
|
50485
|
+
"Phase completed",
|
|
50486
|
+
phaseTasks.length
|
|
50487
|
+
);
|
|
50488
|
+
await accessor.setMetaValue("project_meta", updatedMeta);
|
|
50489
|
+
await logOperation("phase_completed", slug, {}, accessor);
|
|
50490
|
+
return { phase: slug, completedAt: now2 };
|
|
50491
|
+
}
|
|
50492
|
+
async function advancePhase(force = false, _cwd, accessor) {
|
|
50493
|
+
const meta = await accessor.getMetaValue("project_meta");
|
|
50494
|
+
const currentSlug = meta?.currentPhase ?? null;
|
|
50495
|
+
if (!currentSlug) {
|
|
50496
|
+
throw new CleoError(4 /* NOT_FOUND */, "No current phase set");
|
|
50086
50497
|
}
|
|
50087
|
-
|
|
50088
|
-
|
|
50089
|
-
|
|
50498
|
+
const phases = meta?.phases ?? {};
|
|
50499
|
+
const currentPhase = phases[currentSlug];
|
|
50500
|
+
if (!currentPhase) {
|
|
50501
|
+
throw new CleoError(4 /* NOT_FOUND */, `Current phase '${currentSlug}' not found`);
|
|
50090
50502
|
}
|
|
50091
|
-
|
|
50092
|
-
|
|
50093
|
-
|
|
50503
|
+
const sortedEntries = Object.entries(phases).sort(([, a], [, b]) => a.order - b.order);
|
|
50504
|
+
const currentIndex = sortedEntries.findIndex(([slug]) => slug === currentSlug);
|
|
50505
|
+
if (currentIndex === -1 || currentIndex >= sortedEntries.length - 1) {
|
|
50506
|
+
throw new CleoError(100 /* NO_DATA */, `No more phases after '${currentSlug}'`);
|
|
50094
50507
|
}
|
|
50095
|
-
|
|
50096
|
-
|
|
50097
|
-
|
|
50098
|
-
|
|
50508
|
+
const [nextSlug] = sortedEntries[currentIndex + 1];
|
|
50509
|
+
const { tasks: phaseTasks } = await accessor.queryTasks({ phase: currentSlug });
|
|
50510
|
+
const incompleteTasks = phaseTasks.filter((t) => t.status !== "done");
|
|
50511
|
+
if (incompleteTasks.length > 0) {
|
|
50512
|
+
const criticalTasks = incompleteTasks.filter((t) => t.priority === "critical");
|
|
50513
|
+
if (criticalTasks.length > 0) {
|
|
50514
|
+
throw new CleoError(
|
|
50515
|
+
6 /* VALIDATION_ERROR */,
|
|
50516
|
+
`Cannot advance - ${criticalTasks.length} critical task(s) remain in phase '${currentSlug}'`
|
|
50517
|
+
);
|
|
50518
|
+
}
|
|
50519
|
+
const totalTasks = phaseTasks.length;
|
|
50520
|
+
const completionPercent = totalTasks > 0 ? Math.floor((totalTasks - incompleteTasks.length) * 100 / totalTasks) : 0;
|
|
50521
|
+
const threshold = 90;
|
|
50522
|
+
if (completionPercent < threshold && !force) {
|
|
50523
|
+
throw new CleoError(
|
|
50524
|
+
6 /* VALIDATION_ERROR */,
|
|
50525
|
+
`Cannot advance - ${incompleteTasks.length} incomplete task(s) in phase '${currentSlug}' (${completionPercent}% complete, threshold: ${threshold}%)`,
|
|
50526
|
+
{ fix: "Use --force to override" }
|
|
50527
|
+
);
|
|
50528
|
+
}
|
|
50099
50529
|
}
|
|
50100
|
-
|
|
50101
|
-
|
|
50102
|
-
|
|
50103
|
-
|
|
50104
|
-
|
|
50105
|
-
|
|
50530
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
50531
|
+
currentPhase.status = "completed";
|
|
50532
|
+
currentPhase.completedAt = now2;
|
|
50533
|
+
const nextPhase = phases[nextSlug];
|
|
50534
|
+
nextPhase.status = "active";
|
|
50535
|
+
nextPhase.startedAt = now2;
|
|
50536
|
+
const updatedMeta = meta;
|
|
50537
|
+
updatedMeta.currentPhase = nextSlug;
|
|
50538
|
+
const currentPhaseTaskCount = phaseTasks.length;
|
|
50539
|
+
const { tasks: nextPhaseTasks } = await accessor.queryTasks({ phase: nextSlug });
|
|
50540
|
+
const nextPhaseTaskCount = nextPhaseTasks.length;
|
|
50541
|
+
addPhaseHistoryEntryToMeta(
|
|
50542
|
+
updatedMeta,
|
|
50543
|
+
currentSlug,
|
|
50544
|
+
"completed",
|
|
50545
|
+
null,
|
|
50546
|
+
"Phase completed via advance",
|
|
50547
|
+
currentPhaseTaskCount
|
|
50548
|
+
);
|
|
50549
|
+
addPhaseHistoryEntryToMeta(
|
|
50550
|
+
updatedMeta,
|
|
50551
|
+
nextSlug,
|
|
50552
|
+
"started",
|
|
50553
|
+
currentSlug,
|
|
50554
|
+
`Phase started via advance from ${currentSlug}`,
|
|
50555
|
+
nextPhaseTaskCount
|
|
50556
|
+
);
|
|
50557
|
+
await accessor.setMetaValue("project_meta", updatedMeta);
|
|
50558
|
+
return {
|
|
50559
|
+
previousPhase: currentSlug,
|
|
50560
|
+
currentPhase: nextSlug,
|
|
50561
|
+
forced: force
|
|
50562
|
+
};
|
|
50563
|
+
}
|
|
50564
|
+
async function renamePhase(oldName, newName, _cwd, accessor) {
|
|
50565
|
+
const meta = await accessor.getMetaValue("project_meta");
|
|
50566
|
+
const phases = meta?.phases ?? {};
|
|
50567
|
+
if (!phases[oldName]) {
|
|
50568
|
+
throw new CleoError(4 /* NOT_FOUND */, `Phase '${oldName}' does not exist`);
|
|
50106
50569
|
}
|
|
50107
|
-
if (
|
|
50108
|
-
|
|
50109
|
-
task.labels = (task.labels ?? []).filter((l) => !toRemove.has(l));
|
|
50110
|
-
changes.push("labels");
|
|
50570
|
+
if (phases[newName]) {
|
|
50571
|
+
throw new CleoError(101 /* ALREADY_EXISTS */, `Phase '${newName}' already exists`);
|
|
50111
50572
|
}
|
|
50112
|
-
if (
|
|
50113
|
-
|
|
50114
|
-
changes.push("depends");
|
|
50573
|
+
if (!/^[a-z][a-z0-9-]*$/.test(newName)) {
|
|
50574
|
+
throw new CleoError(2 /* INVALID_INPUT */, `Invalid phase name '${newName}'`);
|
|
50115
50575
|
}
|
|
50116
|
-
|
|
50117
|
-
|
|
50118
|
-
|
|
50119
|
-
|
|
50120
|
-
|
|
50576
|
+
phases[newName] = phases[oldName];
|
|
50577
|
+
delete phases[oldName];
|
|
50578
|
+
const { tasks: oldPhaseTasks } = await accessor.queryTasks({ phase: oldName });
|
|
50579
|
+
let tasksUpdated = 0;
|
|
50580
|
+
for (const task of oldPhaseTasks) {
|
|
50581
|
+
task.phase = newName;
|
|
50582
|
+
await accessor.upsertSingleTask(task);
|
|
50583
|
+
tasksUpdated++;
|
|
50121
50584
|
}
|
|
50122
|
-
|
|
50123
|
-
|
|
50124
|
-
|
|
50125
|
-
|
|
50585
|
+
const updatedMeta = meta;
|
|
50586
|
+
let currentPhaseUpdated = false;
|
|
50587
|
+
if (updatedMeta.currentPhase === oldName) {
|
|
50588
|
+
updatedMeta.currentPhase = newName;
|
|
50589
|
+
currentPhaseUpdated = true;
|
|
50126
50590
|
}
|
|
50127
|
-
|
|
50128
|
-
|
|
50129
|
-
|
|
50130
|
-
|
|
50131
|
-
|
|
50591
|
+
await accessor.setMetaValue("project_meta", updatedMeta);
|
|
50592
|
+
const focus = await accessor.getMetaValue("focus_state");
|
|
50593
|
+
if (focus?.currentPhase === oldName) {
|
|
50594
|
+
focus.currentPhase = newName;
|
|
50595
|
+
await accessor.setMetaValue("focus_state", focus);
|
|
50132
50596
|
}
|
|
50133
|
-
|
|
50134
|
-
|
|
50135
|
-
|
|
50597
|
+
return { oldName, newName, tasksUpdated, currentPhaseUpdated };
|
|
50598
|
+
}
|
|
50599
|
+
async function deletePhase(slug, options = {}, _cwd, accessor) {
|
|
50600
|
+
const meta = await accessor.getMetaValue("project_meta");
|
|
50601
|
+
const phases = meta?.phases ?? {};
|
|
50602
|
+
if (!phases[slug]) {
|
|
50603
|
+
throw new CleoError(4 /* NOT_FOUND */, `Phase '${slug}' does not exist`);
|
|
50136
50604
|
}
|
|
50137
|
-
if (
|
|
50138
|
-
|
|
50139
|
-
|
|
50605
|
+
if (meta?.currentPhase === slug) {
|
|
50606
|
+
throw new CleoError(
|
|
50607
|
+
6 /* VALIDATION_ERROR */,
|
|
50608
|
+
`Cannot delete current project phase '${slug}'. Use 'phase set' to change phase first`
|
|
50609
|
+
);
|
|
50140
50610
|
}
|
|
50141
|
-
|
|
50142
|
-
|
|
50143
|
-
|
|
50611
|
+
const { tasks: phaseTasks } = await accessor.queryTasks({ phase: slug });
|
|
50612
|
+
if (phaseTasks.length > 0 && !options.reassignTo) {
|
|
50613
|
+
throw new CleoError(
|
|
50614
|
+
6 /* VALIDATION_ERROR */,
|
|
50615
|
+
`Cannot delete '${slug}': ${phaseTasks.length} tasks would be orphaned. Use --reassign-to <phase>`
|
|
50616
|
+
);
|
|
50144
50617
|
}
|
|
50145
|
-
if (options.
|
|
50146
|
-
|
|
50147
|
-
changes.push("noAutoComplete");
|
|
50618
|
+
if (!options.force) {
|
|
50619
|
+
throw new CleoError(2 /* INVALID_INPUT */, "Phase deletion requires --force flag for safety");
|
|
50148
50620
|
}
|
|
50149
|
-
if (options.
|
|
50150
|
-
|
|
50151
|
-
|
|
50152
|
-
|
|
50153
|
-
{
|
|
50154
|
-
epicId: task.id,
|
|
50155
|
-
currentStage: task.pipelineStage,
|
|
50156
|
-
newStage: options.pipelineStage
|
|
50157
|
-
},
|
|
50158
|
-
acc,
|
|
50159
|
-
cwd
|
|
50621
|
+
if (options.reassignTo) {
|
|
50622
|
+
if (!phases[options.reassignTo]) {
|
|
50623
|
+
throw new CleoError(
|
|
50624
|
+
4 /* NOT_FOUND */,
|
|
50625
|
+
`Reassignment target phase '${options.reassignTo}' does not exist`
|
|
50160
50626
|
);
|
|
50161
50627
|
}
|
|
50162
|
-
if (task.type !== "epic") {
|
|
50163
|
-
const epicAncestor = task.parentId ? await findEpicAncestor(task.parentId, acc) : null;
|
|
50164
|
-
const directParent = task.parentId ? await acc.loadSingleTask(task.parentId) : null;
|
|
50165
|
-
const epicToCheck = directParent?.type === "epic" ? directParent : epicAncestor;
|
|
50166
|
-
if (epicToCheck) {
|
|
50167
|
-
await validateChildStageCeiling(
|
|
50168
|
-
{ childStage: options.pipelineStage, epicId: epicToCheck.id },
|
|
50169
|
-
acc,
|
|
50170
|
-
cwd
|
|
50171
|
-
);
|
|
50172
|
-
}
|
|
50173
|
-
}
|
|
50174
|
-
task.pipelineStage = options.pipelineStage;
|
|
50175
|
-
changes.push("pipelineStage");
|
|
50176
50628
|
}
|
|
50177
|
-
|
|
50178
|
-
|
|
50179
|
-
const
|
|
50180
|
-
|
|
50181
|
-
|
|
50182
|
-
|
|
50183
|
-
task.parentId = null;
|
|
50184
|
-
if (task.type === "subtask") task.type = "task";
|
|
50185
|
-
changes.push("parentId");
|
|
50186
|
-
if (task.type !== originalType) changes.push("type");
|
|
50187
|
-
} else {
|
|
50188
|
-
const newParent = await acc.loadSingleTask(newParentId);
|
|
50189
|
-
if (!newParent) {
|
|
50190
|
-
throw new CleoError(10 /* PARENT_NOT_FOUND */, `Parent task ${newParentId} not found`);
|
|
50191
|
-
}
|
|
50192
|
-
if (newParent.type === "subtask") {
|
|
50193
|
-
throw new CleoError(
|
|
50194
|
-
13 /* INVALID_PARENT_TYPE */,
|
|
50195
|
-
`Cannot parent under subtask '${newParentId}'`
|
|
50196
|
-
);
|
|
50197
|
-
}
|
|
50198
|
-
const subtree = await acc.getSubtree(options.taskId);
|
|
50199
|
-
if (subtree.some((t) => t.id === newParentId)) {
|
|
50200
|
-
throw new CleoError(
|
|
50201
|
-
14 /* CIRCULAR_REFERENCE */,
|
|
50202
|
-
`Moving '${options.taskId}' under '${newParentId}' would create a circular reference`
|
|
50203
|
-
);
|
|
50204
|
-
}
|
|
50205
|
-
const ancestors = await acc.getAncestorChain(newParentId);
|
|
50206
|
-
const parentDepth = ancestors.length;
|
|
50207
|
-
const config2 = await loadConfig(cwd);
|
|
50208
|
-
const policy = resolveHierarchyPolicy(config2);
|
|
50209
|
-
if (parentDepth + 1 >= policy.maxDepth) {
|
|
50210
|
-
throw new CleoError(
|
|
50211
|
-
11 /* DEPTH_EXCEEDED */,
|
|
50212
|
-
`Maximum nesting depth ${policy.maxDepth} would be exceeded`
|
|
50213
|
-
);
|
|
50214
|
-
}
|
|
50215
|
-
task.parentId = newParentId;
|
|
50216
|
-
const newDepth = parentDepth + 1;
|
|
50217
|
-
if (newDepth === 1) task.type = "task";
|
|
50218
|
-
else if (newDepth >= 2) task.type = "subtask";
|
|
50219
|
-
changes.push("parentId");
|
|
50220
|
-
if (task.type !== originalType) changes.push("type");
|
|
50221
|
-
}
|
|
50629
|
+
let tasksReassigned = 0;
|
|
50630
|
+
if (options.reassignTo) {
|
|
50631
|
+
for (const task of phaseTasks) {
|
|
50632
|
+
task.phase = options.reassignTo;
|
|
50633
|
+
await accessor.upsertSingleTask(task);
|
|
50634
|
+
tasksReassigned++;
|
|
50222
50635
|
}
|
|
50223
50636
|
}
|
|
50224
|
-
|
|
50225
|
-
|
|
50637
|
+
delete phases[slug];
|
|
50638
|
+
await accessor.setMetaValue("project_meta", meta);
|
|
50639
|
+
return {
|
|
50640
|
+
deletedPhase: slug,
|
|
50641
|
+
tasksReassigned,
|
|
50642
|
+
reassignedTo: options.reassignTo ?? null
|
|
50643
|
+
};
|
|
50644
|
+
}
|
|
50645
|
+
function addPhaseHistoryEntryToMeta(meta, phase, transitionType, fromPhase, reason, taskCount) {
|
|
50646
|
+
if (!meta.phaseHistory) {
|
|
50647
|
+
meta.phaseHistory = [];
|
|
50226
50648
|
}
|
|
50227
|
-
|
|
50228
|
-
|
|
50229
|
-
|
|
50230
|
-
|
|
50231
|
-
|
|
50232
|
-
|
|
50233
|
-
|
|
50234
|
-
taskId: options.taskId,
|
|
50235
|
-
actor: "system",
|
|
50236
|
-
details: { changes, title: task.title },
|
|
50237
|
-
before: null,
|
|
50238
|
-
after: { changes, title: task.title }
|
|
50239
|
-
});
|
|
50649
|
+
meta.phaseHistory.push({
|
|
50650
|
+
phase,
|
|
50651
|
+
transitionType,
|
|
50652
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
50653
|
+
taskCount,
|
|
50654
|
+
fromPhase,
|
|
50655
|
+
reason
|
|
50240
50656
|
});
|
|
50241
|
-
return { task, changes };
|
|
50242
50657
|
}
|
|
50243
|
-
var
|
|
50244
|
-
|
|
50245
|
-
"packages/core/src/tasks/update.ts"() {
|
|
50658
|
+
var init_phases = __esm({
|
|
50659
|
+
"packages/core/src/phases/index.ts"() {
|
|
50246
50660
|
"use strict";
|
|
50247
50661
|
init_src();
|
|
50248
|
-
init_config();
|
|
50249
50662
|
init_errors3();
|
|
50250
|
-
init_session_enforcement();
|
|
50251
|
-
init_data_accessor();
|
|
50252
50663
|
init_add();
|
|
50253
|
-
|
|
50254
|
-
|
|
50255
|
-
|
|
50256
|
-
|
|
50257
|
-
|
|
50258
|
-
|
|
50259
|
-
|
|
50260
|
-
|
|
50261
|
-
|
|
50262
|
-
|
|
50263
|
-
|
|
50264
|
-
|
|
50265
|
-
"
|
|
50266
|
-
|
|
50267
|
-
|
|
50268
|
-
|
|
50269
|
-
|
|
50270
|
-
|
|
50271
|
-
|
|
50272
|
-
|
|
50273
|
-
|
|
50274
|
-
|
|
50275
|
-
|
|
50276
|
-
|
|
50277
|
-
"
|
|
50278
|
-
|
|
50664
|
+
}
|
|
50665
|
+
});
|
|
50666
|
+
|
|
50667
|
+
// packages/core/src/pipeline/phase.ts
|
|
50668
|
+
async function listPhases2(projectRoot, accessor) {
|
|
50669
|
+
try {
|
|
50670
|
+
const result = await listPhases(projectRoot, accessor);
|
|
50671
|
+
return { success: true, data: result };
|
|
50672
|
+
} catch (err) {
|
|
50673
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
50674
|
+
return {
|
|
50675
|
+
success: false,
|
|
50676
|
+
error: { code: "E_PHASE_LIST_FAILED", message }
|
|
50677
|
+
};
|
|
50678
|
+
}
|
|
50679
|
+
}
|
|
50680
|
+
async function showPhase2(projectRoot, phaseId, accessor) {
|
|
50681
|
+
try {
|
|
50682
|
+
const result = await showPhase(phaseId, projectRoot, accessor);
|
|
50683
|
+
return { success: true, data: result };
|
|
50684
|
+
} catch (err) {
|
|
50685
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
50686
|
+
return {
|
|
50687
|
+
success: false,
|
|
50688
|
+
error: { code: "E_PHASE_SHOW_FAILED", message }
|
|
50689
|
+
};
|
|
50690
|
+
}
|
|
50691
|
+
}
|
|
50692
|
+
var init_phase = __esm({
|
|
50693
|
+
"packages/core/src/pipeline/phase.ts"() {
|
|
50694
|
+
"use strict";
|
|
50695
|
+
init_phases();
|
|
50696
|
+
}
|
|
50697
|
+
});
|
|
50698
|
+
|
|
50699
|
+
// packages/core/src/pipeline/index.ts
|
|
50700
|
+
var pipeline_exports2 = {};
|
|
50701
|
+
__export(pipeline_exports2, {
|
|
50702
|
+
listPhases: () => listPhases2,
|
|
50703
|
+
showPhase: () => showPhase2
|
|
50704
|
+
});
|
|
50705
|
+
var init_pipeline2 = __esm({
|
|
50706
|
+
"packages/core/src/pipeline/index.ts"() {
|
|
50707
|
+
"use strict";
|
|
50708
|
+
init_phase();
|
|
50279
50709
|
}
|
|
50280
50710
|
});
|
|
50281
50711
|
|
|
@@ -62663,145 +63093,6 @@ var init_system2 = __esm({
|
|
|
62663
63093
|
}
|
|
62664
63094
|
});
|
|
62665
63095
|
|
|
62666
|
-
// packages/core/src/task-work/index.ts
|
|
62667
|
-
var task_work_exports = {};
|
|
62668
|
-
__export(task_work_exports, {
|
|
62669
|
-
currentTask: () => currentTask,
|
|
62670
|
-
getTaskHistory: () => getTaskHistory,
|
|
62671
|
-
getWorkHistory: () => getWorkHistory,
|
|
62672
|
-
startTask: () => startTask,
|
|
62673
|
-
stopTask: () => stopTask
|
|
62674
|
-
});
|
|
62675
|
-
async function currentTask(cwd, accessor) {
|
|
62676
|
-
const acc = accessor ?? await getAccessor(cwd);
|
|
62677
|
-
const focus = await acc.getMetaValue("focus_state");
|
|
62678
|
-
return {
|
|
62679
|
-
currentTask: focus?.currentTask ?? null,
|
|
62680
|
-
currentPhase: focus?.currentPhase ?? null,
|
|
62681
|
-
sessionNote: focus?.sessionNote ?? null,
|
|
62682
|
-
nextAction: focus?.nextAction ?? null
|
|
62683
|
-
};
|
|
62684
|
-
}
|
|
62685
|
-
async function startTask(taskId, cwd, accessor) {
|
|
62686
|
-
if (!taskId) {
|
|
62687
|
-
throw new CleoError(2 /* INVALID_INPUT */, "Task ID is required");
|
|
62688
|
-
}
|
|
62689
|
-
const acc = accessor ?? await getAccessor(cwd);
|
|
62690
|
-
const task = await acc.loadSingleTask(taskId);
|
|
62691
|
-
if (!task) {
|
|
62692
|
-
throw new CleoError(4 /* NOT_FOUND */, `Task not found: ${taskId}`, {
|
|
62693
|
-
fix: `Use 'cleo find "${taskId}"' to search`
|
|
62694
|
-
});
|
|
62695
|
-
}
|
|
62696
|
-
const { tasks: allTasks } = await acc.queryTasks({});
|
|
62697
|
-
const unresolvedDeps = getUnresolvedDeps(taskId, allTasks);
|
|
62698
|
-
if (unresolvedDeps.length > 0) {
|
|
62699
|
-
throw new CleoError(
|
|
62700
|
-
5 /* DEPENDENCY_ERROR */,
|
|
62701
|
-
`Task ${taskId} is blocked by unresolved dependencies: ${unresolvedDeps.join(", ")}`,
|
|
62702
|
-
{
|
|
62703
|
-
fix: `Complete blockers first: ${unresolvedDeps.map((d) => `cleo complete ${d}`).join(", ")}`
|
|
62704
|
-
}
|
|
62705
|
-
);
|
|
62706
|
-
}
|
|
62707
|
-
const focus = await acc.getMetaValue("focus_state") ?? {};
|
|
62708
|
-
const previousTask = focus.currentTask ?? null;
|
|
62709
|
-
focus.currentTask = taskId;
|
|
62710
|
-
focus.currentPhase = task.phase ?? null;
|
|
62711
|
-
const noteEntry = {
|
|
62712
|
-
note: `Started work on ${taskId}: ${task.title}`,
|
|
62713
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
62714
|
-
};
|
|
62715
|
-
if (!focus.sessionNotes) {
|
|
62716
|
-
focus.sessionNotes = [];
|
|
62717
|
-
}
|
|
62718
|
-
focus.sessionNotes.push(noteEntry);
|
|
62719
|
-
await acc.setMetaValue("focus_state", focus);
|
|
62720
|
-
await logOperation(
|
|
62721
|
-
"task_start",
|
|
62722
|
-
taskId,
|
|
62723
|
-
{
|
|
62724
|
-
previousTask,
|
|
62725
|
-
title: task.title
|
|
62726
|
-
},
|
|
62727
|
-
accessor
|
|
62728
|
-
);
|
|
62729
|
-
const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
|
|
62730
|
-
hooks2.dispatch("PreToolUse", cwd ?? process.cwd(), {
|
|
62731
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
62732
|
-
taskId,
|
|
62733
|
-
taskTitle: task.title
|
|
62734
|
-
}).catch(() => {
|
|
62735
|
-
});
|
|
62736
|
-
return {
|
|
62737
|
-
taskId,
|
|
62738
|
-
taskTitle: task.title,
|
|
62739
|
-
previousTask
|
|
62740
|
-
};
|
|
62741
|
-
}
|
|
62742
|
-
async function stopTask(cwd, accessor) {
|
|
62743
|
-
const acc = accessor ?? await getAccessor(cwd);
|
|
62744
|
-
const focus = await acc.getMetaValue("focus_state");
|
|
62745
|
-
const previousTask = focus?.currentTask ?? null;
|
|
62746
|
-
if (!focus) {
|
|
62747
|
-
return { previousTask: null };
|
|
62748
|
-
}
|
|
62749
|
-
const taskId = focus.currentTask;
|
|
62750
|
-
const task = taskId ? await acc.loadSingleTask(taskId) : void 0;
|
|
62751
|
-
focus.currentTask = null;
|
|
62752
|
-
focus.nextAction = null;
|
|
62753
|
-
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
62754
|
-
if (taskId && task) {
|
|
62755
|
-
const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
|
|
62756
|
-
hooks2.dispatch("PostToolUse", cwd ?? process.cwd(), {
|
|
62757
|
-
timestamp: now2,
|
|
62758
|
-
taskId,
|
|
62759
|
-
taskTitle: task.title,
|
|
62760
|
-
status: "done"
|
|
62761
|
-
}).catch(() => {
|
|
62762
|
-
});
|
|
62763
|
-
}
|
|
62764
|
-
await acc.setMetaValue("focus_state", focus);
|
|
62765
|
-
await logOperation(
|
|
62766
|
-
"task_stop",
|
|
62767
|
-
previousTask ?? "none",
|
|
62768
|
-
{
|
|
62769
|
-
previousTask
|
|
62770
|
-
},
|
|
62771
|
-
accessor
|
|
62772
|
-
);
|
|
62773
|
-
return { previousTask };
|
|
62774
|
-
}
|
|
62775
|
-
async function getWorkHistory(cwd, accessor) {
|
|
62776
|
-
const acc = accessor ?? await getAccessor(cwd);
|
|
62777
|
-
const focus = await acc.getMetaValue("focus_state");
|
|
62778
|
-
const notes = focus?.sessionNotes ?? [];
|
|
62779
|
-
const history = [];
|
|
62780
|
-
for (const note of notes) {
|
|
62781
|
-
const match = note.note.match(/^(?:Focus set to|Started work on) (T\d+)/);
|
|
62782
|
-
if (match) {
|
|
62783
|
-
history.push({
|
|
62784
|
-
taskId: match[1],
|
|
62785
|
-
timestamp: note.timestamp
|
|
62786
|
-
});
|
|
62787
|
-
}
|
|
62788
|
-
}
|
|
62789
|
-
return history.reverse();
|
|
62790
|
-
}
|
|
62791
|
-
var getTaskHistory;
|
|
62792
|
-
var init_task_work = __esm({
|
|
62793
|
-
"packages/core/src/task-work/index.ts"() {
|
|
62794
|
-
"use strict";
|
|
62795
|
-
init_src();
|
|
62796
|
-
init_errors3();
|
|
62797
|
-
init_data_accessor();
|
|
62798
|
-
init_add();
|
|
62799
|
-
init_dependency_check();
|
|
62800
|
-
init_handlers();
|
|
62801
|
-
getTaskHistory = getWorkHistory;
|
|
62802
|
-
}
|
|
62803
|
-
});
|
|
62804
|
-
|
|
62805
63096
|
// packages/core/src/tasks/archive.ts
|
|
62806
63097
|
async function archiveTasks(options = {}, cwd, accessor) {
|
|
62807
63098
|
const acc = accessor ?? await getAccessor(cwd);
|
|
@@ -68304,6 +68595,7 @@ var init_cleo = __esm({
|
|
|
68304
68595
|
init_permissions();
|
|
68305
68596
|
init_registry3();
|
|
68306
68597
|
init_sharing();
|
|
68598
|
+
init_workspace();
|
|
68307
68599
|
init_orchestration();
|
|
68308
68600
|
init_reconciliation();
|
|
68309
68601
|
init_link_store();
|
|
@@ -68542,7 +68834,14 @@ var init_cleo = __esm({
|
|
|
68542
68834
|
discover: (p) => discoverRelated(p.query, p.method, p.limit),
|
|
68543
68835
|
search: (p) => searchAcrossProjects(p.pattern, p.project, p.limit),
|
|
68544
68836
|
setPermission: (p) => setPermission(p.name, p.level),
|
|
68545
|
-
sharingStatus: () => getSharingStatus()
|
|
68837
|
+
sharingStatus: () => getSharingStatus(),
|
|
68838
|
+
route: (message) => {
|
|
68839
|
+
const directive = parseDirective(message);
|
|
68840
|
+
if (!directive) return Promise.resolve([]);
|
|
68841
|
+
return routeDirective(directive);
|
|
68842
|
+
},
|
|
68843
|
+
workspaceStatus: () => workspaceStatus(),
|
|
68844
|
+
workspaceAgents: () => workspaceAgents()
|
|
68546
68845
|
};
|
|
68547
68846
|
}
|
|
68548
68847
|
// === Agents ===
|
|
@@ -75669,7 +75968,7 @@ async function runUpgrade(options = {}) {
|
|
|
75669
75968
|
return { success: false, upToDate: false, dryRun: isDryRun, actions, applied: 0, errors };
|
|
75670
75969
|
}
|
|
75671
75970
|
await forceCheckpointBeforeOperation("storage-migration", options.cwd);
|
|
75672
|
-
const { MigrationLogger: MigrationLogger2 } = await Promise.resolve().then(() => (init_logger3(),
|
|
75971
|
+
const { MigrationLogger: MigrationLogger2 } = await Promise.resolve().then(() => (init_logger3(), logger_exports2));
|
|
75673
75972
|
const {
|
|
75674
75973
|
createMigrationState: createMigrationState2,
|
|
75675
75974
|
updateMigrationPhase: updateMigrationPhase2,
|