@cleocode/cleo 2026.4.91 → 2026.4.92

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
@@ -12424,26 +12424,67 @@ function reconcileJournal(nativeDb, migrationsFolder, existenceTable, logSubsyst
12424
12424
  const journaledHashes = new Set(journalEntries.map((e) => e.hash));
12425
12425
  for (const migration of localMigrations) {
12426
12426
  if (journaledHashes.has(migration.hash)) continue;
12427
- const alterColumnRegex = /ALTER\s+TABLE\s+[`"]?(\w+)[`"]?\s+ADD\s+COLUMN\s+[`"]?(\w+)[`"]?/gi;
12427
+ const alterColumnRegex = /ALTER\s+TABLE\s+[`"]?(\w+)[`"]?\s+ADD\s+COLUMN\s+[`"]?(\w+)[`"]?\s*(.*?)(?:;|$)/gi;
12428
12428
  const alterMatches = [];
12429
12429
  const sqlStatements = Array.isArray(migration.sql) ? migration.sql : [migration.sql ?? ""];
12430
12430
  const fullSql = sqlStatements.join("\n");
12431
12431
  for (const m2 of fullSql.matchAll(alterColumnRegex)) {
12432
- alterMatches.push({ table: m2[1], column: m2[2] });
12432
+ alterMatches.push({
12433
+ table: m2[1],
12434
+ column: m2[2],
12435
+ ddl: (m2[3] || "").trim()
12436
+ });
12433
12437
  }
12434
12438
  if (alterMatches.length === 0) continue;
12435
- const allColumnsExist = alterMatches.every(({ table, column }) => {
12436
- if (!tableExists(nativeDb, table)) return false;
12437
- const cols = nativeDb.prepare(`PRAGMA table_info(${table})`).all();
12438
- return cols.some((c) => c.name === column);
12439
- });
12440
- if (allColumnsExist) {
12439
+ const existingColumns = [];
12440
+ const missingColumns = [];
12441
+ for (const target of alterMatches) {
12442
+ if (!tableExists(nativeDb, target.table)) {
12443
+ missingColumns.push(target);
12444
+ continue;
12445
+ }
12446
+ const cols = nativeDb.prepare(`PRAGMA table_info(${target.table})`).all();
12447
+ if (cols.some((c) => c.name === target.column)) {
12448
+ existingColumns.push(target);
12449
+ } else {
12450
+ missingColumns.push(target);
12451
+ }
12452
+ }
12453
+ if (missingColumns.length === 0) {
12441
12454
  const log15 = getLogger(logSubsystem);
12442
12455
  log15.warn(
12443
12456
  { migration: migration.name, columns: alterMatches },
12444
12457
  `Detected partially-applied migration ${migration.name} \u2014 columns exist but journal entry missing. Auto-reconciling.`
12445
12458
  );
12446
12459
  insertJournalEntry(nativeDb, migration.hash, migration.folderMillis, migration.name ?? "");
12460
+ continue;
12461
+ }
12462
+ if (existingColumns.length > 0 && missingColumns.length > 0) {
12463
+ const log15 = getLogger(logSubsystem);
12464
+ log15.warn(
12465
+ {
12466
+ migration: migration.name,
12467
+ existingColumns: existingColumns.map((c) => `${c.table}.${c.column}`),
12468
+ missingColumns: missingColumns.map((c) => `${c.table}.${c.column}`)
12469
+ },
12470
+ `T920: Detected partial migration ${migration.name} \u2014 some ALTER columns exist, some missing. Adding missing columns and marking applied.`
12471
+ );
12472
+ for (const { table, column, ddl } of missingColumns) {
12473
+ if (!tableExists(nativeDb, table)) continue;
12474
+ try {
12475
+ nativeDb.exec(`ALTER TABLE ${table} ADD COLUMN ${column}${ddl ? ` ${ddl}` : ""}`);
12476
+ log15.warn(
12477
+ { migration: migration.name, table, column },
12478
+ `T920: Added missing column ${table}.${column} to complete partial migration.`
12479
+ );
12480
+ } catch {
12481
+ log15.warn(
12482
+ { migration: migration.name, table, column },
12483
+ `T920: Could not add missing column ${table}.${column} \u2014 will let Drizzle migrate() handle it.`
12484
+ );
12485
+ }
12486
+ }
12487
+ insertJournalEntry(nativeDb, migration.hash, migration.folderMillis, migration.name ?? "");
12447
12488
  }
12448
12489
  }
12449
12490
  }
@@ -74223,10 +74264,10 @@ async function execCommand(command, dryRun) {
74223
74264
  return { success: true, output: `[DRY RUN] Would execute: ${command}`, dryRun: true };
74224
74265
  }
74225
74266
  try {
74226
- const { stdout: stdout2, stderr: stderr2 } = await execFileAsync4("sh", ["-c", command], {
74267
+ const { stdout, stderr: stderr2 } = await execFileAsync4("sh", ["-c", command], {
74227
74268
  timeout: 3e5
74228
74269
  });
74229
- return { success: true, output: (stdout2 + stderr2).trim(), dryRun: false };
74270
+ return { success: true, output: (stdout + stderr2).trim(), dryRun: false };
74230
74271
  } catch (err) {
74231
74272
  const message = err instanceof Error ? err.message : String(err);
74232
74273
  return { success: false, output: message, dryRun: false };
@@ -86020,20 +86061,37 @@ async function pathExists(p2) {
86020
86061
  return false;
86021
86062
  }
86022
86063
  }
86023
- async function probeJsonFile(path12) {
86064
+ async function probeJsonFile(path12, expectedVersion) {
86024
86065
  if (!await pathExists(path12)) {
86025
- return { exists: false, parseable: false, error: "File not found" };
86066
+ const base = { exists: false, parseable: false, error: "File not found" };
86067
+ if (expectedVersion !== void 0) {
86068
+ base.expectedVersion = expectedVersion;
86069
+ base.schemaDrift = false;
86070
+ }
86071
+ return base;
86026
86072
  }
86027
86073
  try {
86028
86074
  const raw = await readFile17(path12, "utf-8");
86029
- JSON.parse(raw);
86030
- return { exists: true, parseable: true };
86075
+ const parsed = JSON.parse(raw);
86076
+ const rawVersion = parsed["schemaVersion"] ?? parsed["version"];
86077
+ const schemaVersion = typeof rawVersion === "string" ? rawVersion : void 0;
86078
+ const result = { exists: true, parseable: true, schemaVersion };
86079
+ if (expectedVersion !== void 0) {
86080
+ result.expectedVersion = expectedVersion;
86081
+ result.schemaDrift = schemaVersion !== void 0 && schemaVersion !== expectedVersion;
86082
+ }
86083
+ return result;
86031
86084
  } catch (err) {
86032
- return {
86085
+ const result = {
86033
86086
  exists: true,
86034
86087
  parseable: false,
86035
86088
  error: err instanceof Error ? err.message : String(err)
86036
86089
  };
86090
+ if (expectedVersion !== void 0) {
86091
+ result.expectedVersion = expectedVersion;
86092
+ result.schemaDrift = false;
86093
+ }
86094
+ return result;
86037
86095
  }
86038
86096
  }
86039
86097
  async function runWithConcurrency(items, limit, worker) {
@@ -86061,11 +86119,13 @@ function deriveOverallStatus(reachable, cleoDirExists, dbs, files) {
86061
86119
  if (!cleoDirExists) return "unknown";
86062
86120
  const dbProbes = [dbs.tasks, dbs.brain, dbs.conduit];
86063
86121
  const degradedDb = dbProbes.some(
86064
- (d) => d.exists && (!d.integrityOk || !d.walSidecarClean || !d.sqliteOpenable)
86122
+ (d) => d.exists && (!d.integrityOk || !d.walSidecarClean || !d.sqliteOpenable || d.schemaDrift === true)
86065
86123
  );
86066
86124
  const configBroken = files.config.exists && !files.config.parseable;
86067
86125
  const infoBroken = files.projectInfo.exists && !files.projectInfo.parseable;
86068
- if (degradedDb || configBroken || infoBroken) return "degraded";
86126
+ const configDrifted = files.config.schemaDrift === true;
86127
+ const infoDrifted = files.projectInfo.schemaDrift === true;
86128
+ if (degradedDb || configBroken || infoBroken || configDrifted || infoDrifted) return "degraded";
86069
86129
  if (!dbs.tasks.exists) return "unknown";
86070
86130
  return "healthy";
86071
86131
  }
@@ -86086,13 +86146,25 @@ function collectProjectIssues(report) {
86086
86146
  issues.push(`${label}: integrity_check failed`);
86087
86147
  } else if (probe.exists && !probe.walSidecarClean) {
86088
86148
  issues.push(`${label}: WAL sidecar conflict`);
86149
+ } else if (probe.schemaDrift === true) {
86150
+ issues.push(
86151
+ `${label}: schema drift (migrationCount=${probe.migrationCount ?? "?"} < expected=${probe.expectedVersion ?? "?"})`
86152
+ );
86089
86153
  }
86090
86154
  }
86091
86155
  if (report.files.config.exists && !report.files.config.parseable) {
86092
86156
  issues.push(`config.json: ${report.files.config.error ?? "parse error"}`);
86157
+ } else if (report.files.config.schemaDrift === true) {
86158
+ issues.push(
86159
+ `config.json: schema drift (version=${report.files.config.schemaVersion ?? "?"} != expected=${report.files.config.expectedVersion ?? "?"})`
86160
+ );
86093
86161
  }
86094
86162
  if (report.files.projectInfo.exists && !report.files.projectInfo.parseable) {
86095
86163
  issues.push(`project-info.json: ${report.files.projectInfo.error ?? "parse error"}`);
86164
+ } else if (report.files.projectInfo.schemaDrift === true) {
86165
+ issues.push(
86166
+ `project-info.json: schema drift (version=${report.files.projectInfo.schemaVersion ?? "?"} != expected=${report.files.projectInfo.expectedVersion ?? "?"})`
86167
+ );
86096
86168
  }
86097
86169
  return issues;
86098
86170
  }
@@ -86112,6 +86184,10 @@ function collectGlobalIssues(dbs) {
86112
86184
  issues.push(`${label}: integrity_check failed`);
86113
86185
  } else if (!probe.walSidecarClean) {
86114
86186
  issues.push(`${label}: WAL sidecar conflict`);
86187
+ } else if (probe.schemaDrift === true) {
86188
+ issues.push(
86189
+ `${label}: schema drift (migrationCount=${probe.migrationCount ?? "?"} < expected=${probe.expectedVersion ?? "?"})`
86190
+ );
86115
86191
  }
86116
86192
  }
86117
86193
  return issues;
@@ -86120,11 +86196,11 @@ function deriveGlobalStatus(dbs) {
86120
86196
  const probes = [dbs.nexus, dbs.signaldock];
86121
86197
  if (probes.every((p2) => !p2.exists)) return "unknown";
86122
86198
  const degraded = probes.some(
86123
- (p2) => p2.exists && (!p2.integrityOk || !p2.walSidecarClean || !p2.sqliteOpenable)
86199
+ (p2) => p2.exists && (!p2.integrityOk || !p2.walSidecarClean || !p2.sqliteOpenable || p2.schemaDrift === true)
86124
86200
  );
86125
86201
  return degraded ? "degraded" : "healthy";
86126
86202
  }
86127
- async function probeDb(dbPath) {
86203
+ async function probeDb(dbPath, expectedVersion) {
86128
86204
  const result = {
86129
86205
  path: dbPath,
86130
86206
  exists: false,
@@ -86134,10 +86210,14 @@ async function probeDb(dbPath) {
86134
86210
  walSidecarClean: true,
86135
86211
  sizeBytes: -1
86136
86212
  };
86213
+ if (expectedVersion !== void 0) {
86214
+ result.expectedVersion = expectedVersion;
86215
+ }
86137
86216
  const exists3 = await pathExists(dbPath);
86138
86217
  result.exists = exists3;
86139
86218
  if (!exists3) {
86140
86219
  result.error = "File not found";
86220
+ if (expectedVersion !== void 0) result.schemaDrift = false;
86141
86221
  return result;
86142
86222
  }
86143
86223
  try {
@@ -86145,6 +86225,7 @@ async function probeDb(dbPath) {
86145
86225
  result.readable = true;
86146
86226
  } catch (err) {
86147
86227
  result.error = err instanceof Error ? err.message : String(err);
86228
+ if (expectedVersion !== void 0) result.schemaDrift = false;
86148
86229
  return result;
86149
86230
  }
86150
86231
  try {
@@ -86159,6 +86240,7 @@ async function probeDb(dbPath) {
86159
86240
  if (!DatabaseSyncCtor) {
86160
86241
  result.error = "node:sqlite runtime not available";
86161
86242
  result.walSidecarClean = !walExists;
86243
+ if (expectedVersion !== void 0) result.schemaDrift = false;
86162
86244
  return result;
86163
86245
  }
86164
86246
  let db = null;
@@ -86182,11 +86264,30 @@ async function probeDb(dbPath) {
86182
86264
  }
86183
86265
  } catch {
86184
86266
  }
86267
+ try {
86268
+ const tables = db.prepare(
86269
+ "SELECT name FROM sqlite_master WHERE type='table' AND name IN ('__drizzle_migrations','_conduit_migrations')"
86270
+ ).all();
86271
+ const migrationTable = tables.find(
86272
+ (t) => t.name === "__drizzle_migrations" || t.name === "_conduit_migrations"
86273
+ );
86274
+ if (migrationTable) {
86275
+ const countRow = db.prepare(`SELECT COUNT(*) AS c FROM "${migrationTable.name}"`).get();
86276
+ if (typeof countRow?.c === "number") {
86277
+ result.migrationCount = countRow.c;
86278
+ }
86279
+ }
86280
+ } catch {
86281
+ }
86282
+ if (expectedVersion !== void 0) {
86283
+ result.schemaDrift = result.migrationCount !== void 0 && result.migrationCount < expectedVersion;
86284
+ }
86185
86285
  result.walSidecarClean = true;
86186
86286
  } catch (err) {
86187
86287
  result.sqliteOpenable = false;
86188
86288
  result.error = err instanceof Error ? err.message : String(err);
86189
86289
  result.walSidecarClean = !walExists;
86290
+ if (expectedVersion !== void 0) result.schemaDrift = false;
86190
86291
  } finally {
86191
86292
  try {
86192
86293
  db?.close();
@@ -86238,11 +86339,11 @@ async function checkProjectHealth(projectPath, projectHash) {
86238
86339
  const cleoDir = getCleoDirAbsolute(projectPath);
86239
86340
  const cleoDirExists = await pathExists(cleoDir);
86240
86341
  const [tasks2, brain, conduit, configProbe, infoProbe] = await Promise.all([
86241
- probeDb(join103(cleoDir, TASKS_DB)),
86242
- probeDb(join103(cleoDir, BRAIN_DB)),
86243
- probeDb(join103(cleoDir, CONDUIT_DB)),
86244
- probeJsonFile(join103(cleoDir, CONFIG_JSON)),
86245
- probeJsonFile(join103(cleoDir, PROJECT_INFO_JSON))
86342
+ probeDb(join103(cleoDir, TASKS_DB), DB_EXPECTED_VERSIONS[TASKS_DB]),
86343
+ probeDb(join103(cleoDir, BRAIN_DB), DB_EXPECTED_VERSIONS[BRAIN_DB]),
86344
+ probeDb(join103(cleoDir, CONDUIT_DB), DB_EXPECTED_VERSIONS[CONDUIT_DB]),
86345
+ probeJsonFile(join103(cleoDir, CONFIG_JSON), JSON_EXPECTED_VERSIONS[CONFIG_JSON]),
86346
+ probeJsonFile(join103(cleoDir, PROJECT_INFO_JSON), JSON_EXPECTED_VERSIONS[PROJECT_INFO_JSON])
86246
86347
  ]);
86247
86348
  const base = {
86248
86349
  projectHash,
@@ -86261,8 +86362,8 @@ async function checkProjectHealth(projectPath, projectHash) {
86261
86362
  async function checkGlobalHealth() {
86262
86363
  const cleoHome = getCleoHome();
86263
86364
  const [nexus, signaldock] = await Promise.all([
86264
- probeDb(join103(cleoHome, NEXUS_DB)),
86265
- probeDb(join103(cleoHome, SIGNALDOCK_DB))
86365
+ probeDb(join103(cleoHome, NEXUS_DB), DB_EXPECTED_VERSIONS[NEXUS_DB]),
86366
+ probeDb(join103(cleoHome, SIGNALDOCK_DB), DB_EXPECTED_VERSIONS[SIGNALDOCK_DB])
86266
86367
  ]);
86267
86368
  const dbs = { nexus, signaldock };
86268
86369
  return {
@@ -86358,7 +86459,7 @@ async function writeHealthBack(reports, log15) {
86358
86459
  log15.warn({ err }, "project-health: could not open nexus.db for write-back");
86359
86460
  }
86360
86461
  }
86361
- var _require14, _databaseSyncCtor, TASKS_DB, BRAIN_DB, CONDUIT_DB, NEXUS_DB, SIGNALDOCK_DB, CONFIG_JSON, PROJECT_INFO_JSON;
86462
+ var _require14, _databaseSyncCtor, TASKS_DB, BRAIN_DB, CONDUIT_DB, NEXUS_DB, SIGNALDOCK_DB, CONFIG_JSON, PROJECT_INFO_JSON, DB_EXPECTED_VERSIONS, JSON_EXPECTED_VERSIONS;
86362
86463
  var init_project_health = __esm({
86363
86464
  "packages/core/src/system/project-health.ts"() {
86364
86465
  "use strict";
@@ -86372,6 +86473,17 @@ var init_project_health = __esm({
86372
86473
  SIGNALDOCK_DB = "signaldock.db";
86373
86474
  CONFIG_JSON = "config.json";
86374
86475
  PROJECT_INFO_JSON = "project-info.json";
86476
+ DB_EXPECTED_VERSIONS = {
86477
+ [TASKS_DB]: 11,
86478
+ [BRAIN_DB]: 14,
86479
+ [NEXUS_DB]: 3,
86480
+ [SIGNALDOCK_DB]: 1,
86481
+ [CONDUIT_DB]: 1
86482
+ };
86483
+ JSON_EXPECTED_VERSIONS = {
86484
+ [CONFIG_JSON]: "2.10.0",
86485
+ [PROJECT_INFO_JSON]: "1.0.0"
86486
+ };
86375
86487
  }
86376
86488
  });
86377
86489
 
@@ -86459,8 +86571,8 @@ async function resolveBinaryPath(name2) {
86459
86571
  const execFileAsync7 = promisify6(execFile6);
86460
86572
  const resolver = process.platform === "win32" ? "where" : "which";
86461
86573
  try {
86462
- const { stdout: stdout2 } = await execFileAsync7(resolver, [name2]);
86463
- const first = stdout2.trim().split(/\r?\n/)[0] ?? "";
86574
+ const { stdout } = await execFileAsync7(resolver, [name2]);
86575
+ const first = stdout.trim().split(/\r?\n/)[0] ?? "";
86464
86576
  return first || null;
86465
86577
  } catch {
86466
86578
  return null;
@@ -87218,20 +87330,20 @@ async function revalidateEvidence(evidence, projectRoot) {
87218
87330
  }
87219
87331
  function runCommand(cmd, args, cwd) {
87220
87332
  return new Promise((resolve20) => {
87221
- let stdout2 = "";
87333
+ let stdout = "";
87222
87334
  let stderr2 = "";
87223
87335
  const child = spawn2(cmd, args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
87224
87336
  child.stdout?.on("data", (d) => {
87225
- stdout2 += d.toString("utf-8");
87337
+ stdout += d.toString("utf-8");
87226
87338
  });
87227
87339
  child.stderr?.on("data", (d) => {
87228
87340
  stderr2 += d.toString("utf-8");
87229
87341
  });
87230
87342
  child.on("error", () => {
87231
- resolve20({ exitCode: null, stdout: stdout2, stderr: stderr2 });
87343
+ resolve20({ exitCode: null, stdout, stderr: stderr2 });
87232
87344
  });
87233
87345
  child.on("close", (code) => {
87234
- resolve20({ exitCode: code, stdout: stdout2, stderr: stderr2 });
87346
+ resolve20({ exitCode: code, stdout, stderr: stderr2 });
87235
87347
  });
87236
87348
  });
87237
87349
  }
@@ -95622,7 +95734,7 @@ async function runTestGate(gate, index2, projectRoot, timeoutMs) {
95622
95734
  const cwd = resolveCwd(projectRoot, gate.cwd);
95623
95735
  const [bin, ...defaultArgs] = gate.command.split(" ");
95624
95736
  const args = gate.args ?? defaultArgs;
95625
- let stdout2 = "";
95737
+ let stdout = "";
95626
95738
  let stderr2 = "";
95627
95739
  let exitCode = 0;
95628
95740
  let timedOut = false;
@@ -95632,11 +95744,11 @@ async function runTestGate(gate, index2, projectRoot, timeoutMs) {
95632
95744
  env: { ...process.env, ...gate.env },
95633
95745
  timeout: timeoutMs
95634
95746
  });
95635
- stdout2 = out.stdout;
95747
+ stdout = out.stdout;
95636
95748
  stderr2 = out.stderr;
95637
95749
  } catch (err) {
95638
95750
  const e = err;
95639
- stdout2 = e.stdout ?? "";
95751
+ stdout = e.stdout ?? "";
95640
95752
  stderr2 = e.stderr ?? "";
95641
95753
  exitCode = typeof e.status === "number" ? e.status : 1;
95642
95754
  if (e.killed || e.code === "ETIMEDOUT") {
@@ -95644,7 +95756,7 @@ async function runTestGate(gate, index2, projectRoot, timeoutMs) {
95644
95756
  }
95645
95757
  }
95646
95758
  const durationMs = Date.now() - startMs;
95647
- const combined = truncate(`${stdout2}
95759
+ const combined = truncate(`${stdout}
95648
95760
  ${stderr2}`.trim(), MAX_EVIDENCE_BYTES);
95649
95761
  if (timedOut) {
95650
95762
  return makeResult(
@@ -95661,7 +95773,7 @@ ${stderr2}`.trim(), MAX_EVIDENCE_BYTES);
95661
95773
  }
95662
95774
  if (gate.expect === "pass") {
95663
95775
  const failPattern = /\bFAIL\b|failing|Error:/i;
95664
- if (failPattern.test(stdout2)) {
95776
+ if (failPattern.test(stdout)) {
95665
95777
  return makeResult(
95666
95778
  index2,
95667
95779
  gate,
@@ -95803,7 +95915,7 @@ async function runCommandGate(gate, index2, projectRoot, timeoutMs) {
95803
95915
  const startMs = Date.now();
95804
95916
  const cwd = resolveCwd(projectRoot, gate.cwd);
95805
95917
  const expectedExitCode = gate.exitCode ?? 0;
95806
- let stdout2 = "";
95918
+ let stdout = "";
95807
95919
  let stderr2 = "";
95808
95920
  let actualExitCode = 0;
95809
95921
  let timedOut = false;
@@ -95813,11 +95925,11 @@ async function runCommandGate(gate, index2, projectRoot, timeoutMs) {
95813
95925
  env: { ...process.env, ...gate.env },
95814
95926
  timeout: timeoutMs
95815
95927
  });
95816
- stdout2 = out.stdout;
95928
+ stdout = out.stdout;
95817
95929
  stderr2 = out.stderr;
95818
95930
  } catch (err) {
95819
95931
  const e = err;
95820
- stdout2 = e.stdout ?? "";
95932
+ stdout = e.stdout ?? "";
95821
95933
  stderr2 = e.stderr ?? "";
95822
95934
  actualExitCode = typeof e.status === "number" ? e.status : 1;
95823
95935
  if (e.killed || e.code === "ETIMEDOUT") {
@@ -95825,7 +95937,7 @@ async function runCommandGate(gate, index2, projectRoot, timeoutMs) {
95825
95937
  }
95826
95938
  }
95827
95939
  const durationMs = Date.now() - startMs;
95828
- const combined = truncate(`${stdout2}
95940
+ const combined = truncate(`${stdout}
95829
95941
  ${stderr2}`.trim(), MAX_EVIDENCE_BYTES);
95830
95942
  if (timedOut) {
95831
95943
  return makeResult(
@@ -95849,7 +95961,7 @@ ${stderr2}`.trim(), MAX_EVIDENCE_BYTES);
95849
95961
  }
95850
95962
  if (gate.stdoutMatches) {
95851
95963
  const re2 = new RegExp(gate.stdoutMatches);
95852
- if (!re2.test(stdout2)) {
95964
+ if (!re2.test(stdout)) {
95853
95965
  return makeResult(
95854
95966
  index2,
95855
95967
  gate,
@@ -95880,17 +95992,17 @@ async function runLintGate(gate, index2, projectRoot, timeoutMs) {
95880
95992
  const cwd = resolveCwd(projectRoot, gate.cwd);
95881
95993
  const toolDef = LINT_TOOL_DEFAULTS[gate.tool];
95882
95994
  const args = gate.args ?? toolDef.defaultArgs;
95883
- let stdout2 = "";
95995
+ let stdout = "";
95884
95996
  let stderr2 = "";
95885
95997
  let exitCode = 0;
95886
95998
  let timedOut = false;
95887
95999
  try {
95888
96000
  const out = await execFileAsync6(toolDef.cmd, args, { cwd, timeout: timeoutMs });
95889
- stdout2 = out.stdout;
96001
+ stdout = out.stdout;
95890
96002
  stderr2 = out.stderr;
95891
96003
  } catch (err) {
95892
96004
  const e = err;
95893
- stdout2 = e.stdout ?? "";
96005
+ stdout = e.stdout ?? "";
95894
96006
  stderr2 = e.stderr ?? "";
95895
96007
  exitCode = typeof e.status === "number" ? e.status : 1;
95896
96008
  if (e.killed || e.code === "ETIMEDOUT") {
@@ -95898,7 +96010,7 @@ async function runLintGate(gate, index2, projectRoot, timeoutMs) {
95898
96010
  }
95899
96011
  }
95900
96012
  const durationMs = Date.now() - startMs;
95901
- const combined = truncate(`${stdout2}
96013
+ const combined = truncate(`${stdout}
95902
96014
  ${stderr2}`.trim(), MAX_EVIDENCE_BYTES);
95903
96015
  if (timedOut) {
95904
96016
  return makeResult(
@@ -132636,6 +132748,9 @@ Or set CLEO_OWNER_OVERRIDE=1 with CLEO_OWNER_OVERRIDE_REASON=<reason> for emerge
132636
132748
  result.evidenceStored = evidenceStored;
132637
132749
  result.override = override.override;
132638
132750
  }
132751
+ if ((action === "set_gate" || action === "set_all") && verification.passed === true && missing.length === 0) {
132752
+ result.hint = `All gates green. Run: cleo complete ${taskId}`;
132753
+ }
132639
132754
  return { success: true, data: result };
132640
132755
  } catch (err) {
132641
132756
  const message = err instanceof Error ? err.message : String(err);
@@ -144847,11 +144962,11 @@ var workCommand = defineCommand({
144847
144962
  }
144848
144963
  };
144849
144964
  const runCleo = async (cleoArgs, timeoutMs = 15e3) => {
144850
- const { stdout: stdout2 } = await execFileAsync7("cleo", cleoArgs, {
144965
+ const { stdout } = await execFileAsync7("cleo", cleoArgs, {
144851
144966
  encoding: "utf-8",
144852
144967
  timeout: timeoutMs
144853
144968
  });
144854
- return stdout2;
144969
+ return stdout;
144855
144970
  };
144856
144971
  const taskInterval = Number.parseInt(args["poll-interval"], 10);
144857
144972
  let inFlight = false;
@@ -150059,8 +150174,8 @@ function checkDiskSpace(directoryPath, dependencies = {
150059
150174
  pathSep: sep2,
150060
150175
  cpExecFile: promisify8(execFile8)
150061
150176
  }) {
150062
- function mapOutput(stdout2, filter, mapping, coefficient) {
150063
- const parsed = stdout2.split("\n").map((line2) => line2.trim()).filter((line2) => line2.length !== 0).slice(1).map((line2) => line2.split(/\s+(?=[\d/])/));
150177
+ function mapOutput(stdout, filter, mapping, coefficient) {
150178
+ const parsed = stdout.split("\n").map((line2) => line2.trim()).filter((line2) => line2.length !== 0).slice(1).map((line2) => line2.split(/\s+(?=[\d/])/));
150064
150179
  const filtered = parsed.filter(filter);
150065
150180
  if (filtered.length === 0) {
150066
150181
  throw new NoMatchError();
@@ -150078,8 +150193,8 @@ function checkDiskSpace(directoryPath, dependencies = {
150078
150193
  return Promise.reject(new Error("cmd must contain at least one item"));
150079
150194
  }
150080
150195
  try {
150081
- const { stdout: stdout2 } = await dependencies.cpExecFile(file2, args, { windowsHide: true });
150082
- return mapOutput(stdout2, filter, mapping, coefficient);
150196
+ const { stdout } = await dependencies.cpExecFile(file2, args, { windowsHide: true });
150197
+ return mapOutput(stdout, filter, mapping, coefficient);
150083
150198
  } catch (error48) {
150084
150199
  return Promise.reject(error48);
150085
150200
  }
@@ -151771,7 +151886,7 @@ var docsCommand = defineCommand({
151771
151886
  init_cli();
151772
151887
 
151773
151888
  // packages/cleo/src/cli/progress.ts
151774
- import { stderr, stdout } from "node:process";
151889
+ import { stderr } from "node:process";
151775
151890
  var ProgressTracker = class {
151776
151891
  enabled;
151777
151892
  prefix;
@@ -151813,16 +151928,21 @@ ${this.prefix}: Starting...
151813
151928
  }
151814
151929
  /**
151815
151930
  * Mark as complete with optional summary.
151931
+ *
151932
+ * @remarks
151933
+ * Writes to stderr (not stdout) so that progress messages never pollute
151934
+ * the machine-readable JSON envelope on stdout. Stdout MUST contain exactly
151935
+ * one JSON object per CLI invocation (ADR-039 / T927).
151816
151936
  */
151817
151937
  complete(summary) {
151818
151938
  if (!this.enabled) return;
151819
151939
  if (summary) {
151820
- stdout.write(`
151940
+ stderr.write(`
151821
151941
  ${this.prefix}: \u2713 ${summary}
151822
151942
 
151823
151943
  `);
151824
151944
  } else {
151825
- stdout.write(`
151945
+ stderr.write(`
151826
151946
  ${this.prefix}: \u2713 Complete
151827
151947
 
151828
151948
  `);
@@ -158495,11 +158615,11 @@ var diffCommand = defineCommand({
158495
158615
  const execFileAsync7 = promisify10(execFileNode);
158496
158616
  const resolveSha = async (ref) => {
158497
158617
  try {
158498
- const { stdout: stdout2 } = await execFileAsync7("git", ["rev-parse", "--short", ref], {
158618
+ const { stdout } = await execFileAsync7("git", ["rev-parse", "--short", ref], {
158499
158619
  timeout: 5e3,
158500
158620
  cwd: repoPath
158501
158621
  });
158502
- return stdout2.trim();
158622
+ return stdout.trim();
158503
158623
  } catch {
158504
158624
  return ref;
158505
158625
  }
@@ -161830,14 +161950,14 @@ async function getCurrentVersion() {
161830
161950
  }
161831
161951
  async function getNpmInstalledVersion() {
161832
161952
  try {
161833
- const { stdout: stdout2 } = await execAsync("npm", [
161953
+ const { stdout } = await execAsync("npm", [
161834
161954
  "ls",
161835
161955
  "-g",
161836
161956
  "@cleocode/cleo",
161837
161957
  "--depth=0",
161838
161958
  "--json"
161839
161959
  ]);
161840
- const data = JSON.parse(stdout2);
161960
+ const data = JSON.parse(stdout);
161841
161961
  return data.dependencies?.["@cleocode/cleo"]?.version ?? null;
161842
161962
  } catch {
161843
161963
  return null;
@@ -161845,19 +161965,19 @@ async function getNpmInstalledVersion() {
161845
161965
  }
161846
161966
  async function getDistTagVersion(tag) {
161847
161967
  try {
161848
- const { stdout: stdout2 } = await execAsync("npm", ["view", `@cleocode/cleo@${tag}`, "version"]);
161849
- const v2 = stdout2.trim();
161968
+ const { stdout } = await execAsync("npm", ["view", `@cleocode/cleo@${tag}`, "version"]);
161969
+ const v2 = stdout.trim();
161850
161970
  return v2 || null;
161851
161971
  } catch {
161852
161972
  if (tag === "latest") {
161853
161973
  try {
161854
- const { stdout: stdout2 } = await execAsync("curl", [
161974
+ const { stdout } = await execAsync("curl", [
161855
161975
  "-sL",
161856
161976
  "--max-time",
161857
161977
  "10",
161858
161978
  `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`
161859
161979
  ]);
161860
- const data = JSON.parse(stdout2);
161980
+ const data = JSON.parse(stdout);
161861
161981
  return data.tag_name?.replace(/^v/, "") ?? null;
161862
161982
  } catch {
161863
161983
  return null;