@cleocode/cleo 2026.5.0 → 2026.5.1

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.
@@ -13,8 +13,13 @@
13
13
  * when a task touches symbols with CRITICAL impact risk. The acknowledgment is
14
14
  * audited to `.cleo/audit/nexus-risk-ack.jsonl`.
15
15
  *
16
+ * As of T1632, `cleo complete <epicId>` is REJECTED with E_EPIC_HAS_PENDING_CHILDREN
17
+ * when the epic has pending or active children. Pass `--override-reason "<reason>"`
18
+ * to bypass (audited to `.cleo/audit/premature-close.jsonl`).
19
+ *
16
20
  * @task T4461
17
21
  * @task T832
22
+ * @task T1632
18
23
  * @adr ADR-051
19
24
  * @epic T4454
20
25
  */
@@ -45,5 +50,9 @@ export declare const completeCommand: import("citty").CommandDef<{
45
50
  readonly type: "string";
46
51
  readonly description: "Reason for acknowledging CRITICAL impact risk (bypasses nexusImpact gate)";
47
52
  };
53
+ readonly 'override-reason': {
54
+ readonly type: "string";
55
+ readonly description: "Reason for bypassing E_EPIC_HAS_PENDING_CHILDREN guard (audited to .cleo/audit/premature-close.jsonl)";
56
+ };
48
57
  }>;
49
58
  //# sourceMappingURL=complete.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"complete.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/complete.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAMH;;;;GAIG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;EAwD1B,CAAC"}
1
+ {"version":3,"file":"complete.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/complete.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAMH;;;;GAIG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;EA8D1B,CAAC"}
package/dist/cli/index.js CHANGED
@@ -416,6 +416,7 @@ var init_exit_codes = __esm({
416
416
  ExitCode2[ExitCode2["VERIFICATION_LOCKED"] = 46] = "VERIFICATION_LOCKED";
417
417
  ExitCode2[ExitCode2["ROUND_MISMATCH"] = 47] = "ROUND_MISMATCH";
418
418
  ExitCode2[ExitCode2["AC_LOCKED"] = 48] = "AC_LOCKED";
419
+ ExitCode2[ExitCode2["EPIC_HAS_PENDING_CHILDREN"] = 49] = "EPIC_HAS_PENDING_CHILDREN";
419
420
  ExitCode2[ExitCode2["CONTEXT_WARNING"] = 50] = "CONTEXT_WARNING";
420
421
  ExitCode2[ExitCode2["CONTEXT_CAUTION"] = 51] = "CONTEXT_CAUTION";
421
422
  ExitCode2[ExitCode2["CONTEXT_CRITICAL"] = 52] = "CONTEXT_CRITICAL";
@@ -465,6 +466,7 @@ var init_exit_codes = __esm({
465
466
  ExitCode2[ExitCode2["ALREADY_EXISTS"] = 101] = "ALREADY_EXISTS";
466
467
  ExitCode2[ExitCode2["NO_CHANGE"] = 102] = "NO_CHANGE";
467
468
  ExitCode2[ExitCode2["TESTS_SKIPPED"] = 103] = "TESTS_SKIPPED";
469
+ ExitCode2[ExitCode2["DUPLICATE_TASK_LIKELY"] = 105] = "DUPLICATE_TASK_LIKELY";
468
470
  ExitCode2[ExitCode2["LAFS_VIOLATION"] = 104] = "LAFS_VIOLATION";
469
471
  return ExitCode2;
470
472
  })(ExitCode || {});
@@ -24218,7 +24220,9 @@ var init_tasks3 = __esm({
24218
24220
  // T944: orthogonal axes — role is the canonical wire field (ADR-057 D2)
24219
24221
  role: params.role,
24220
24222
  scope: params.scope,
24221
- severity: params.severity
24223
+ severity: params.severity,
24224
+ // T1633: BRAIN duplicate-bypass flag
24225
+ forceDuplicate: params.forceDuplicate
24222
24226
  }),
24223
24227
  "add"
24224
24228
  );
@@ -24260,7 +24264,11 @@ var init_tasks3 = __esm({
24260
24264
  "complete"
24261
24265
  );
24262
24266
  }
24263
- const result = await completeTaskStrict(projectRoot, params.taskId, params.notes);
24267
+ const result = await completeTaskStrict(projectRoot, params.taskId, {
24268
+ notes: params.notes,
24269
+ overrideReason: params.overrideReason,
24270
+ acknowledgeRisk: params.acknowledgeRisk
24271
+ });
24264
24272
  setImmediate(async () => {
24265
24273
  try {
24266
24274
  const { trackMemoryUsage } = await import("@cleocode/core/internal");
@@ -26882,6 +26890,19 @@ var addCommand = defineCommand({
26882
26890
  severity: {
26883
26891
  type: "string",
26884
26892
  description: "Bug severity (P0|P1|P2|P3) \u2014 only valid with --role bug (T944)"
26893
+ },
26894
+ /**
26895
+ * Bypass the E_DUPLICATE_TASK_LIKELY rejection guard.
26896
+ *
26897
+ * When passed, `cleo add` proceeds even if BRAIN similarity scoring
26898
+ * determines the task is very likely a duplicate (score >= 0.92).
26899
+ * The bypass is audited to `.cleo/audit/duplicate-bypass.jsonl`.
26900
+ *
26901
+ * @task T1633
26902
+ */
26903
+ "force-duplicate": {
26904
+ type: "boolean",
26905
+ description: "Bypass BRAIN duplicate-task rejection (audited to .cleo/audit/duplicate-bypass.jsonl) (T1633)"
26885
26906
  }
26886
26907
  },
26887
26908
  async run({ args, cmd }) {
@@ -26916,6 +26937,7 @@ var addCommand = defineCommand({
26916
26937
  params["role"] = params["role"] ?? args.kind;
26917
26938
  if (args.scope !== void 0) params["scope"] = args.scope;
26918
26939
  if (args.severity !== void 0) params["severity"] = args.severity;
26940
+ if (args["force-duplicate"] !== void 0) params["forceDuplicate"] = args["force-duplicate"];
26919
26941
  const inferred = await inferTaskAddParams(getProjectRoot18(), {
26920
26942
  title: args.title,
26921
26943
  description: args.description ?? args.desc,
@@ -33013,6 +33035,10 @@ var completeCommand = defineCommand({
33013
33035
  "acknowledge-risk": {
33014
33036
  type: "string",
33015
33037
  description: "Reason for acknowledging CRITICAL impact risk (bypasses nexusImpact gate)"
33038
+ },
33039
+ "override-reason": {
33040
+ type: "string",
33041
+ description: "Reason for bypassing E_EPIC_HAS_PENDING_CHILDREN guard (audited to .cleo/audit/premature-close.jsonl)"
33016
33042
  }
33017
33043
  },
33018
33044
  async run({ args }) {
@@ -33021,7 +33047,8 @@ var completeCommand = defineCommand({
33021
33047
  notes: args.notes,
33022
33048
  changeset: args.changeset,
33023
33049
  verificationNote: args["verification-note"],
33024
- acknowledgeRisk: args["acknowledge-risk"]
33050
+ acknowledgeRisk: args["acknowledge-risk"],
33051
+ overrideReason: args["override-reason"]
33025
33052
  });
33026
33053
  if (!response.success) {
33027
33054
  handleRawError(response, { command: "complete", operation: "tasks.complete" });
@@ -34000,30 +34027,88 @@ var currentCommand = defineCommand({
34000
34027
  import { homedir as homedir2 } from "node:os";
34001
34028
  import { join as join9 } from "node:path";
34002
34029
  import { getGCDaemonStatus, spawnGCDaemon, stopGCDaemon } from "@cleocode/core/gc/daemon.js";
34003
- async function showDaemonStatus(cleoDir, json2) {
34030
+ import { getSentientDaemonStatus } from "@cleocode/core/sentient";
34031
+ async function showDaemonStatus(cleoDir, projectRoot, json2) {
34004
34032
  try {
34005
- const status = await getGCDaemonStatus(cleoDir);
34006
- const result = { success: true, data: status };
34033
+ const gcStatus = await getGCDaemonStatus(cleoDir);
34034
+ let hygieneLastRunAt = null;
34035
+ let hygieneSummary = null;
34036
+ let hygieneStats = {
34037
+ projectsChecked: 0,
34038
+ projectsHealthy: 0,
34039
+ tempGcCandidates: 0,
34040
+ duplicateEpicGroups: 0,
34041
+ worktreesPruned: 0
34042
+ };
34043
+ try {
34044
+ const sentientStatus = await getSentientDaemonStatus(projectRoot);
34045
+ hygieneLastRunAt = sentientStatus.hygieneLastRunAt;
34046
+ hygieneSummary = sentientStatus.hygieneSummary;
34047
+ hygieneStats = sentientStatus.hygieneStats;
34048
+ } catch {
34049
+ }
34050
+ const result = {
34051
+ success: true,
34052
+ data: {
34053
+ gc: gcStatus,
34054
+ hygiene: {
34055
+ lastRunAt: hygieneLastRunAt,
34056
+ summary: hygieneSummary,
34057
+ stats: hygieneStats
34058
+ }
34059
+ }
34060
+ };
34007
34061
  if (json2) {
34008
34062
  process.stdout.write(JSON.stringify(result) + "\n");
34009
34063
  } else {
34010
- const runningStr = status.running ? `running (PID ${status.pid})` : "stopped";
34011
- process.stdout.write(`Daemon: ${runningStr}
34064
+ const runningStr = gcStatus.running ? `running (PID ${gcStatus.pid})` : "stopped";
34065
+ process.stdout.write(`GC Daemon: ${runningStr}
34012
34066
  `);
34013
- process.stdout.write(`Started at: ${status.startedAt ?? "never"}
34067
+ process.stdout.write(`Started at: ${gcStatus.startedAt ?? "never"}
34014
34068
  `);
34015
- process.stdout.write(`Last GC run: ${status.lastRunAt ?? "never"}
34069
+ process.stdout.write(`Last GC run: ${gcStatus.lastRunAt ?? "never"}
34016
34070
  `);
34017
- const diskStr = status.lastDiskUsedPct !== null ? `${status.lastDiskUsedPct.toFixed(1)}%` : "unknown";
34018
- process.stdout.write(`Disk used: ${diskStr}
34071
+ const diskStr = gcStatus.lastDiskUsedPct !== null ? `${gcStatus.lastDiskUsedPct.toFixed(1)}%` : "unknown";
34072
+ process.stdout.write(`Disk used: ${diskStr}
34019
34073
  `);
34020
- if (status.escalationNeeded) {
34074
+ if (gcStatus.escalationNeeded) {
34021
34075
  process.stdout.write(
34022
34076
  `
34023
34077
  WARNING: Disk threshold breached. Run 'cleo gc run' to reclaim space.
34024
34078
  `
34025
34079
  );
34026
34080
  }
34081
+ process.stdout.write(`
34082
+ Hygiene Loop (cross-project):
34083
+ `);
34084
+ process.stdout.write(` Last run: ${hygieneLastRunAt ?? "never"}
34085
+ `);
34086
+ process.stdout.write(` Summary: ${hygieneSummary ?? "not yet run"}
34087
+ `);
34088
+ if (hygieneStats.projectsChecked > 0) {
34089
+ process.stdout.write(
34090
+ ` Projects: ${hygieneStats.projectsHealthy}/${hygieneStats.projectsChecked} healthy
34091
+ `
34092
+ );
34093
+ if (hygieneStats.tempGcCandidates > 0) {
34094
+ process.stdout.write(
34095
+ ` Temp GC: ${hygieneStats.tempGcCandidates} candidate(s) pending approval
34096
+ `
34097
+ );
34098
+ }
34099
+ if (hygieneStats.duplicateEpicGroups > 0) {
34100
+ process.stdout.write(
34101
+ ` Duplicate epics: ${hygieneStats.duplicateEpicGroups} group(s) detected
34102
+ `
34103
+ );
34104
+ }
34105
+ if (hygieneStats.worktreesPruned > 0) {
34106
+ process.stdout.write(
34107
+ ` Worktrees: ${hygieneStats.worktreesPruned} stale worktree(s) pruned
34108
+ `
34109
+ );
34110
+ }
34111
+ }
34027
34112
  }
34028
34113
  } catch (err) {
34029
34114
  const message = err instanceof Error ? err.message : String(err);
@@ -34144,7 +34229,7 @@ var stopCommand3 = defineCommand({
34144
34229
  var statusCommand4 = defineCommand({
34145
34230
  meta: {
34146
34231
  name: "status",
34147
- description: "Show daemon running state, PID, last GC run, and disk usage"
34232
+ description: "Show daemon running state, PID, last GC run, disk usage, and cross-project hygiene summary"
34148
34233
  },
34149
34234
  args: {
34150
34235
  "cleo-dir": {
@@ -34158,7 +34243,7 @@ var statusCommand4 = defineCommand({
34158
34243
  },
34159
34244
  async run({ args }) {
34160
34245
  const cleoDir = args["cleo-dir"] ?? join9(homedir2(), ".cleo");
34161
- await showDaemonStatus(cleoDir, args.json ?? false);
34246
+ await showDaemonStatus(cleoDir, process.cwd(), args.json ?? false);
34162
34247
  }
34163
34248
  });
34164
34249
  var daemonCommand = defineCommand({
@@ -34184,7 +34269,7 @@ var daemonCommand = defineCommand({
34184
34269
  async run({ args, cmd, rawArgs }) {
34185
34270
  if (isSubCommandDispatch(rawArgs, cmd.subCommands)) return;
34186
34271
  const cleoDir = args["cleo-dir"] ?? join9(homedir2(), ".cleo");
34187
- await showDaemonStatus(cleoDir, args.json ?? false);
34272
+ await showDaemonStatus(cleoDir, process.cwd(), args.json ?? false);
34188
34273
  }
34189
34274
  });
34190
34275
 
@@ -34199,6 +34284,10 @@ var dashCommand = defineCommand({
34199
34284
  "blocked-limit": {
34200
34285
  type: "string",
34201
34286
  description: "Max blocked tasks to show"
34287
+ },
34288
+ "no-hygiene": {
34289
+ type: "boolean",
34290
+ description: "Suppress the hygiene section note"
34202
34291
  }
34203
34292
  },
34204
34293
  async run({ args }) {
@@ -34211,6 +34300,11 @@ var dashCommand = defineCommand({
34211
34300
  },
34212
34301
  { command: "dash" }
34213
34302
  );
34303
+ if (!args["no-hygiene"]) {
34304
+ process.stdout.write(
34305
+ "\n--- Sentient Hygiene (T1636) ---\nBackground scan runs every 4h (dream cycle). Run `cleo memory digest --hygiene` to see the latest hygiene observations (orphan tasks, top-level tasks without an epic, content defects, premature-close leaks).\n"
34306
+ );
34307
+ }
34214
34308
  }
34215
34309
  });
34216
34310
 
@@ -40266,21 +40360,49 @@ var backfillCommand3 = defineCommand({
40266
40360
  await showUsage(cmd);
40267
40361
  }
40268
40362
  });
40269
- var digestCommand = makeMemorySubcommand({
40270
- name: "digest",
40271
- description: "Summarized top-N brain observations for session briefing (T1006). Sorted by citation_count + quality_score.",
40363
+ var digestCommand = defineCommand({
40364
+ meta: {
40365
+ name: "digest",
40366
+ description: "Summarized top-N brain observations for session briefing (T1006). Pass --hygiene to show latest hygiene scan results from the sentient background loop (T1636)."
40367
+ },
40272
40368
  args: {
40273
40369
  limit: {
40274
40370
  type: "string",
40275
40371
  description: "Maximum number of observations to include (default: 10)."
40372
+ },
40373
+ hygiene: {
40374
+ type: "boolean",
40375
+ description: "Show only hygiene observations (hygiene:orphan, hygiene:top-level-orphan, hygiene:content-defect, hygiene:premature-close-leak) from the sentient background loop (T1636)."
40376
+ },
40377
+ json: {
40378
+ type: "boolean",
40379
+ description: "Output as JSON"
40276
40380
  }
40277
40381
  },
40278
- gateway: "query",
40279
- operation: "digest",
40280
- output: { command: "memory-digest", operation: "memory.digest" },
40281
- paramBuilder: (args) => ({
40282
- ...args["limit"] !== void 0 && { limit: parseInt(args["limit"], 10) }
40283
- })
40382
+ async run({ args }) {
40383
+ if (args.hygiene) {
40384
+ await dispatchFromCli(
40385
+ "query",
40386
+ "memory",
40387
+ "find",
40388
+ {
40389
+ query: "hygiene:",
40390
+ limit: args.limit !== void 0 ? parseInt(args.limit, 10) : 20
40391
+ },
40392
+ { command: "memory-hygiene-digest", operation: "memory.find" }
40393
+ );
40394
+ } else {
40395
+ await dispatchFromCli(
40396
+ "query",
40397
+ "memory",
40398
+ "digest",
40399
+ {
40400
+ ...args["limit"] !== void 0 && { limit: parseInt(args["limit"], 10) }
40401
+ },
40402
+ { command: "memory-digest", operation: "memory.digest" }
40403
+ );
40404
+ }
40405
+ }
40284
40406
  });
40285
40407
  var recentCommand = makeMemorySubcommand({
40286
40408
  name: "recent",
@@ -48696,7 +48818,7 @@ async function runPostUpdateDiagnostics(opts) {
48696
48818
  import { join as join16 } from "node:path";
48697
48819
  import { cwd as processCwd } from "node:process";
48698
48820
  import {
48699
- getSentientDaemonStatus,
48821
+ getSentientDaemonStatus as getSentientDaemonStatus2,
48700
48822
  resumeSentientDaemon,
48701
48823
  SENTIENT_STATE_FILE,
48702
48824
  spawnSentientDaemon,
@@ -48754,7 +48876,7 @@ var startSub = defineCommand({
48754
48876
  const jsonMode = args.json === true;
48755
48877
  const dryRun = args["dry-run"] === true;
48756
48878
  try {
48757
- const existing = await getSentientDaemonStatus(projectRoot);
48879
+ const existing = await getSentientDaemonStatus2(projectRoot);
48758
48880
  if (existing.running && existing.pid) {
48759
48881
  emitSuccess(
48760
48882
  { running: true, pid: existing.pid, message: "daemon already running" },
@@ -48822,7 +48944,7 @@ var statusSub = defineCommand({
48822
48944
  const projectRoot = resolveProjectRoot4(args.project);
48823
48945
  const jsonMode = args.json === true;
48824
48946
  try {
48825
- const status = await getSentientDaemonStatus(projectRoot);
48947
+ const status = await getSentientDaemonStatus2(projectRoot);
48826
48948
  if (jsonMode) {
48827
48949
  process.stdout.write(`${JSON.stringify({ success: true, data: status })}
48828
48950
  `);
@@ -49334,7 +49456,7 @@ var sentientCommand = defineCommand({
49334
49456
  const projectRoot = resolveProjectRoot4(args.project);
49335
49457
  const jsonMode = args.json === true;
49336
49458
  try {
49337
- const status = await getSentientDaemonStatus(projectRoot);
49459
+ const status = await getSentientDaemonStatus2(projectRoot);
49338
49460
  if (jsonMode) {
49339
49461
  process.stdout.write(`${JSON.stringify({ success: true, data: status })}
49340
49462
  `);