@cleocode/cleo 2026.4.72 → 2026.4.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 CHANGED
@@ -91183,7 +91183,6 @@ __export(src_exports, {
91183
91183
  });
91184
91184
  var init_src3 = __esm({
91185
91185
  "packages/core/src/index.ts"() {
91186
- "use strict";
91187
91186
  init_src();
91188
91187
  init_adapters();
91189
91188
  init_admin();
@@ -92550,12 +92549,8 @@ function createAttachmentStore() {
92550
92549
  await db.update(attachments).set({ refCount: sql14`ref_count + 1` }).where(eq14(attachments.id, attachmentId)).run();
92551
92550
  nativeDb.prepare("COMMIT").run();
92552
92551
  if (wasNew) {
92553
- try {
92554
- await mkdir18(join112(filePath, ".."), { recursive: true });
92555
- await writeFile13(filePath, buf);
92556
- } catch (err2) {
92557
- throw err2;
92558
- }
92552
+ await mkdir18(join112(filePath, ".."), { recursive: true });
92553
+ await writeFile13(filePath, buf);
92559
92554
  }
92560
92555
  const finalRow = await db.select().from(attachments).where(eq14(attachments.id, attachmentId)).get();
92561
92556
  return rowToMetadata(finalRow);
@@ -107358,7 +107353,7 @@ ${stderr2}`.trim(), MAX_EVIDENCE_BYTES);
107358
107353
  `${gate.tool} exited with ${exitCode}`
107359
107354
  );
107360
107355
  }
107361
- if (toolDef.errorPattern && toolDef.errorPattern.test(combined)) {
107356
+ if (toolDef.errorPattern?.test(combined)) {
107362
107357
  return makeResult(
107363
107358
  index2,
107364
107359
  gate,
@@ -141639,8 +141634,7 @@ function toHistoryEntry(e) {
141639
141634
  }
141640
141635
  async function taskCompleteStrict(projectRoot, taskId, notes, force) {
141641
141636
  try {
141642
- const { loadConfig: loadConfig4 } = await Promise.resolve().then(() => (init_internal(), internal_exports));
141643
- const config2 = await loadConfig4(projectRoot);
141637
+ const config2 = await loadConfig(projectRoot);
141644
141638
  const lifecycleMode = config2.lifecycle?.mode ?? "strict";
141645
141639
  if (lifecycleMode === "strict") {
141646
141640
  const ivtrState = await getIvtrState(taskId, { cwd: projectRoot });
@@ -141727,6 +141721,81 @@ async function taskCompleteStrict(projectRoot, taskId, notes, force) {
141727
141721
  };
141728
141722
  }
141729
141723
  }
141724
+ if (lifecycleMode === "strict" || lifecycleMode === "advisory") {
141725
+ const accessor = await getAccessor(projectRoot);
141726
+ const task = await accessor.loadSingleTask(taskId);
141727
+ if (task?.parentId) {
141728
+ const parent = await accessor.loadSingleTask(task.parentId);
141729
+ if (parent?.type === "epic") {
141730
+ const earlyStages = /* @__PURE__ */ new Set([
141731
+ "research",
141732
+ "consensus",
141733
+ "architecture_decision",
141734
+ "specification",
141735
+ "decomposition"
141736
+ ]);
141737
+ const epicStage = parent.pipelineStage ?? null;
141738
+ if (epicStage && earlyStages.has(epicStage)) {
141739
+ const msg = `Task ${taskId} cannot complete: parent epic ${task.parentId} is still in '${epicStage}' stage. Advance the epic past decomposition before completing children.`;
141740
+ if (lifecycleMode === "strict" && !force) {
141741
+ return engineError("E_LIFECYCLE_GATE_FAILED", msg, {
141742
+ details: {
141743
+ taskId,
141744
+ parentEpicId: task.parentId,
141745
+ epicStage,
141746
+ requiredStages: ["implementation", "validation", "testing", "release"]
141747
+ },
141748
+ fix: `Advance the parent epic via 'cleo lifecycle complete ${task.parentId} ${epicStage}' and then the next stages, or use '--force' to bypass.`
141749
+ });
141750
+ }
141751
+ getLogger("engine:lifecycle").warn(
141752
+ {
141753
+ taskId,
141754
+ parentEpicId: task.parentId,
141755
+ epicStage,
141756
+ mode: lifecycleMode,
141757
+ forced: force === true
141758
+ },
141759
+ lifecycleMode === "advisory" ? `[ADVISORY] parent-epic lifecycle gate: ${msg}` : `[OWNER WARNING] cleo complete --force bypassed parent-epic lifecycle gate: ${msg}`
141760
+ );
141761
+ if (lifecycleMode === "strict" && force === true) {
141762
+ try {
141763
+ const now2 = (/* @__PURE__ */ new Date()).toISOString();
141764
+ if (!task.verification) {
141765
+ task.verification = {
141766
+ round: 0,
141767
+ gates: {},
141768
+ passed: false,
141769
+ lastAgent: null,
141770
+ lastUpdated: null,
141771
+ failureLog: []
141772
+ };
141773
+ }
141774
+ if (!task.verification.failureLog) {
141775
+ task.verification.failureLog = [];
141776
+ }
141777
+ task.verification.failureLog.push({
141778
+ round: task.verification.round,
141779
+ agent: "owner-forced",
141780
+ reason: `OWNER FORCED cleo complete --force \u2014 parent-epic lifecycle gate bypass. Epic ${task.parentId} at stage='${epicStage}'.`,
141781
+ timestamp: now2
141782
+ });
141783
+ task.updatedAt = now2;
141784
+ await accessor.upsertSingleTask(task);
141785
+ } catch {
141786
+ }
141787
+ const innerResult = await taskComplete(projectRoot, taskId, notes);
141788
+ if (!innerResult.success)
141789
+ return innerResult;
141790
+ return {
141791
+ success: true,
141792
+ data: { ...innerResult.data, lifecycleGateBypassed: true }
141793
+ };
141794
+ }
141795
+ }
141796
+ }
141797
+ }
141798
+ }
141730
141799
  return taskComplete(projectRoot, taskId, notes);
141731
141800
  } catch (err2) {
141732
141801
  return cleoErrorToEngineError(err2, "E_INTERNAL", "Failed to complete task (strict mode)");