@cleocode/cleo 2026.6.5 → 2026.6.7

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
@@ -362,6 +362,21 @@ var init_dist = __esm({
362
362
  }
363
363
  });
364
364
 
365
+ // packages/cleo/src/cli/describe-context.ts
366
+ function setDescribeMode(requested) {
367
+ describeRequested = requested;
368
+ }
369
+ function isDescribeMode() {
370
+ return describeRequested;
371
+ }
372
+ var describeRequested;
373
+ var init_describe_context = __esm({
374
+ "packages/cleo/src/cli/describe-context.ts"() {
375
+ "use strict";
376
+ describeRequested = false;
377
+ }
378
+ });
379
+
365
380
  // packages/cleo/src/cli/field-context.ts
366
381
  import {
367
382
  resolveFieldExtraction
@@ -1219,7 +1234,6 @@ var init_tree = __esm({
1219
1234
  // packages/animations/src/render/index.ts
1220
1235
  var init_render = __esm({
1221
1236
  "packages/animations/src/render/index.ts"() {
1222
- "use strict";
1223
1237
  init_legend();
1224
1238
  init_tree();
1225
1239
  }
@@ -1337,6 +1351,7 @@ var init_spinner_handle = __esm({
1337
1351
  var init_src = __esm({
1338
1352
  "packages/animations/src/index.ts"() {
1339
1353
  init_animate_context();
1354
+ init_render();
1340
1355
  init_spinner_handle();
1341
1356
  }
1342
1357
  });
@@ -12979,6 +12994,87 @@ var init_nexus_scope_map = __esm({
12979
12994
  }
12980
12995
  });
12981
12996
 
12997
+ // packages/contracts/src/operations/output-contracts-data.ts
12998
+ var TASK_MUTATION_DATA_SCHEMA, tasksAddOutputContract, tasksAddBatchOutputContract, tasksUpdateOutputContract, tasksCompleteOutputContract;
12999
+ var init_output_contracts_data = __esm({
13000
+ "packages/contracts/src/operations/output-contracts-data.ts"() {
13001
+ "use strict";
13002
+ TASK_MUTATION_DATA_SCHEMA = {
13003
+ type: "object",
13004
+ required: ["count", "created", "updated", "deleted"],
13005
+ additionalProperties: true,
13006
+ properties: {
13007
+ count: { type: "number", description: "Number of records the mutation affected." },
13008
+ created: {
13009
+ type: "array",
13010
+ description: 'Task IDs created by the mutation (bare strings, e.g. "T11692"). Empty for update/delete-only mutations.',
13011
+ items: { type: "string" }
13012
+ },
13013
+ updated: {
13014
+ type: "array",
13015
+ description: "Task IDs updated by the mutation (bare strings). Empty for create/delete-only mutations.",
13016
+ items: { type: "string" }
13017
+ },
13018
+ deleted: {
13019
+ type: "array",
13020
+ description: "Task IDs deleted by the mutation (bare strings). Empty for create/update-only mutations.",
13021
+ items: { type: "string" }
13022
+ },
13023
+ ids: {
13024
+ type: "array",
13025
+ description: "Deprecated alias for the non-empty bucket. Prefer created/updated/deleted.",
13026
+ items: { type: "string" }
13027
+ },
13028
+ dryRun: { type: "boolean", description: "True when this was a preview-only mutation." },
13029
+ status: { type: "string", description: "Post-mutation task status (add/update/complete)." }
13030
+ }
13031
+ };
13032
+ tasksAddOutputContract = {
13033
+ operation: "tasks.add",
13034
+ shapeNote: 'The created task ID (bare string) is at /data/created/0 \u2014 NOT /data/created/0/id. Example: /data/created/0 \u2192 "T11692".',
13035
+ dataSchema: { ...TASK_MUTATION_DATA_SCHEMA },
13036
+ fieldPointers: ["/data/created/0", "/data/count"]
13037
+ };
13038
+ tasksAddBatchOutputContract = {
13039
+ operation: "tasks.add-batch",
13040
+ shapeNote: "Atomic batch insert. Each created task ID (bare string) is in /data/created (array). Dry-run projections are at root: /data/wouldCreate and /data/insertedCount (=0). NOT under /data/dryRunSummary.",
13041
+ dataSchema: {
13042
+ type: "object",
13043
+ required: ["count", "created", "updated", "deleted"],
13044
+ additionalProperties: true,
13045
+ properties: {
13046
+ ...TASK_MUTATION_DATA_SCHEMA.properties,
13047
+ wouldCreate: {
13048
+ type: "number",
13049
+ description: "Dry-run: predicted write count. Present only when dryRun=true."
13050
+ },
13051
+ insertedCount: {
13052
+ type: "number",
13053
+ description: "Dry-run: always 0 (no DB write). Present only when dryRun=true."
13054
+ },
13055
+ wouldAffect: {
13056
+ type: "number",
13057
+ description: "Dry-run: generic affected count. Present only when dryRun=true."
13058
+ }
13059
+ }
13060
+ },
13061
+ fieldPointers: ["/data/created/0", "/data/count", "/data/wouldCreate", "/data/insertedCount"]
13062
+ };
13063
+ tasksUpdateOutputContract = {
13064
+ operation: "tasks.update",
13065
+ shapeNote: "The updated task ID (bare string) is at /data/updated/0 \u2014 NOT /data/updated/0/id. Use /data/status for the post-mutation status.",
13066
+ dataSchema: { ...TASK_MUTATION_DATA_SCHEMA },
13067
+ fieldPointers: ["/data/updated/0", "/data/status", "/data/count"]
13068
+ };
13069
+ tasksCompleteOutputContract = {
13070
+ operation: "tasks.complete",
13071
+ shapeNote: "Completion is a status mutation \u2014 the task ID (bare string) is at /data/updated/0 (status=done). Use /data/status for the post-mutation status.",
13072
+ dataSchema: { ...TASK_MUTATION_DATA_SCHEMA },
13073
+ fieldPointers: ["/data/updated/0", "/data/status", "/data/count"]
13074
+ };
13075
+ }
13076
+ });
13077
+
12982
13078
  // packages/contracts/src/peer.ts
12983
13079
  var init_peer = __esm({
12984
13080
  "packages/contracts/src/peer.ts"() {
@@ -14239,6 +14335,7 @@ var init_src2 = __esm({
14239
14335
  init_docs();
14240
14336
  init_operations();
14241
14337
  init_nexus_scope_map();
14338
+ init_output_contracts_data();
14242
14339
  init_params();
14243
14340
  init_tasks();
14244
14341
  init_peer();
@@ -32693,6 +32790,7 @@ var init_tasks3 = __esm({
32693
32790
  await taskCancel(projectRoot, params.taskId, {
32694
32791
  reason: params.reason,
32695
32792
  children: params.children,
32793
+ reparentTo: params.reparentTo,
32696
32794
  force: params.force,
32697
32795
  cascadeThreshold: params.cascadeThreshold,
32698
32796
  allowCascade: params.allowCascade
@@ -35298,6 +35396,7 @@ __export(cli_exports, {
35298
35396
  dispatchRaw: () => dispatchRaw,
35299
35397
  getCliDispatcher: () => getCliDispatcher,
35300
35398
  handleRawError: () => handleRawError,
35399
+ maybeEmitDescribe: () => maybeEmitDescribe,
35301
35400
  resetCliDispatcher: () => resetCliDispatcher
35302
35401
  });
35303
35402
  import { randomUUID as randomUUID5 } from "node:crypto";
@@ -35306,7 +35405,12 @@ import { createRequire } from "node:module";
35306
35405
  import { dirname as dirname3, join as join8 } from "node:path";
35307
35406
  import { fileURLToPath as fileURLToPath2 } from "node:url";
35308
35407
  import { catalog, registerSkillLibraryFromPath } from "@cleocode/caamp";
35309
- import { autoRecordDispatchTokenUsage, getProjectRoot as getProjectRoot22, hooks } from "@cleocode/core/internal";
35408
+ import {
35409
+ autoRecordDispatchTokenUsage,
35410
+ describeOperation,
35411
+ getProjectRoot as getProjectRoot22,
35412
+ hooks
35413
+ } from "@cleocode/core/internal";
35310
35414
  function ensureCaampLibrary() {
35311
35415
  if (catalog.isCatalogAvailable()) return;
35312
35416
  try {
@@ -35387,7 +35491,31 @@ function createCliDispatcher() {
35387
35491
  function resetCliDispatcher() {
35388
35492
  _dispatcher = null;
35389
35493
  }
35494
+ function maybeEmitDescribe(gateway, domain, operation, outputOpts) {
35495
+ if (!isDescribeMode()) return false;
35496
+ const key = `${domain}.${operation}`;
35497
+ const descriptor = describeOperation(key);
35498
+ const command = outputOpts?.command ?? operation;
35499
+ if (descriptor === null) {
35500
+ cliOutput(
35501
+ { operation: key, gateway, inputContract: null, outputContract: null },
35502
+ {
35503
+ command,
35504
+ operation: `describe.${key}`,
35505
+ message: `No registered operation found for "${key}".`
35506
+ }
35507
+ );
35508
+ return true;
35509
+ }
35510
+ cliOutput(descriptor, {
35511
+ command,
35512
+ operation: `describe.${key}`,
35513
+ message: `Schema for ${key} (input + output). Use the outputContract.fieldPointers for --field.`
35514
+ });
35515
+ return true;
35516
+ }
35390
35517
  async function dispatchFromCli(gateway, domain, operation, params, outputOpts) {
35518
+ if (maybeEmitDescribe(gateway, domain, operation, outputOpts)) return;
35391
35519
  const dispatcher = getCliDispatcher();
35392
35520
  const projectRoot = getProjectRoot22();
35393
35521
  const dispatchStart = Date.now();
@@ -35550,6 +35678,7 @@ var init_cli = __esm({
35550
35678
  "packages/cleo/src/dispatch/adapters/cli.ts"() {
35551
35679
  "use strict";
35552
35680
  init_animation_bridge();
35681
+ init_describe_context();
35553
35682
  init_idempotency_context();
35554
35683
  init_renderers();
35555
35684
  init_dispatcher();
@@ -35845,6 +35974,7 @@ var init_add_batch = __esm({
35845
35974
  }
35846
35975
  },
35847
35976
  async run({ args }) {
35977
+ if (maybeEmitDescribe("mutate", "tasks", "add-batch", { command: "add-batch" })) return;
35848
35978
  const defaultParent = args.parent;
35849
35979
  const dryRunFlag = args["dry-run"];
35850
35980
  const paramsArg = args.params;
@@ -36156,6 +36286,7 @@ For 2+ tasks at once: cleo add-batch --file tasks.json (single transaction, atom
36156
36286
  }
36157
36287
  },
36158
36288
  async run({ args, cmd }) {
36289
+ if (maybeEmitDescribe("mutate", "tasks", "add", { command: "add" })) return;
36159
36290
  const paramsArg = args.params;
36160
36291
  const paramsFileArg = args["params-file"];
36161
36292
  if (paramsArg !== void 0 || paramsFileArg !== void 0) {
@@ -42018,9 +42149,13 @@ var init_cancel = __esm({
42018
42149
  },
42019
42150
  children: {
42020
42151
  type: "string",
42021
- description: "Child handling mode: block (default), cascade, or orphan",
42152
+ description: "Child handling mode: block (default), cascade (cancel subtree), or reparent (move children under --to)",
42022
42153
  default: "block"
42023
42154
  },
42155
+ to: {
42156
+ type: "string",
42157
+ description: "Target parent ID for --children reparent (T11811): children move under this epic"
42158
+ },
42024
42159
  force: {
42025
42160
  type: "boolean",
42026
42161
  description: "Required waiver for cascade cancellation above the subtree threshold",
@@ -42040,6 +42175,7 @@ var init_cancel = __esm({
42040
42175
  taskId: args.taskId,
42041
42176
  reason: args.reason,
42042
42177
  children: args.children,
42178
+ reparentTo: typeof args.to === "string" && args.to.length > 0 ? args.to : void 0,
42043
42179
  force: args.force === true,
42044
42180
  cascadeThreshold: typeof args.cascadeThreshold === "string" && args.cascadeThreshold.length > 0 ? Number.parseInt(args.cascadeThreshold, 10) : void 0
42045
42181
  },
@@ -43668,6 +43804,7 @@ var init_complete = __esm({
43668
43804
  }
43669
43805
  },
43670
43806
  async run({ args }) {
43807
+ if (maybeEmitDescribe("mutate", "tasks", "complete", { command: "complete" })) return;
43671
43808
  const response = await dispatchRaw("mutate", "tasks", "complete", {
43672
43809
  taskId: args.taskId,
43673
43810
  notes: args.notes,
@@ -47938,7 +48075,7 @@ import {
47938
48075
  resolveWorktreeFilePath,
47939
48076
  resolveWorktreeRouting as resolveWorktreeRouting4
47940
48077
  } from "@cleocode/core/internal";
47941
- import { describeOperation } from "@cleocode/lafs";
48078
+ import { describeOperation as describeOperation2 } from "@cleocode/lafs";
47942
48079
  async function dispatchDocsRaw(gateway, operation, params) {
47943
48080
  const response = await dispatchRaw(gateway, "docs", operation, params);
47944
48081
  handleRawError(response, { command: "docs", operation: `docs.${operation}` });
@@ -49176,7 +49313,7 @@ var init_docs3 = __esm({
49176
49313
  if (docsUpdateOperation === void 0) {
49177
49314
  throw new Error("docs.update operation is missing from the registry");
49178
49315
  }
49179
- docsUpdateSchema = describeOperation(docsUpdateOperation, {
49316
+ docsUpdateSchema = describeOperation2(docsUpdateOperation, {
49180
49317
  includeGates: false,
49181
49318
  includeExamples: true
49182
49319
  });
@@ -50088,6 +50225,158 @@ var init_doctor_db_substrate = __esm({
50088
50225
  }
50089
50226
  });
50090
50227
 
50228
+ // packages/cleo/src/cli/commands/doctor-exodus-residue.ts
50229
+ var doctor_exodus_residue_exports = {};
50230
+ __export(doctor_exodus_residue_exports, {
50231
+ doctorExodusResidueCommand: () => doctorExodusResidueCommand
50232
+ });
50233
+ import {
50234
+ archiveStrandedResidue,
50235
+ buildExodusPlan,
50236
+ detectStrandedResidue
50237
+ } from "@cleocode/core/store/exodus/index.js";
50238
+ var doctorExodusResidueCommand;
50239
+ var init_doctor_exodus_residue = __esm({
50240
+ "packages/cleo/src/cli/commands/doctor-exodus-residue.ts"() {
50241
+ "use strict";
50242
+ init_define_cli_command();
50243
+ init_renderers();
50244
+ doctorExodusResidueCommand = defineCommand({
50245
+ meta: {
50246
+ name: "exodus-residue",
50247
+ description: "Detect legacy exodus source DBs still present after a cutover (stranded residue that re-arms the exodus-on-open corruption trigger). Use --fix to archive them into _archive/ (reversible move, never deletes)."
50248
+ },
50249
+ args: {
50250
+ fix: {
50251
+ type: "boolean",
50252
+ description: "Archive stranded legacy source DBs into _archive/ (move, never delete)",
50253
+ default: false
50254
+ }
50255
+ },
50256
+ run({ args }) {
50257
+ const fix = args.fix === true;
50258
+ const cwd = process.cwd();
50259
+ const plan = buildExodusPlan(cwd);
50260
+ const stranded = detectStrandedResidue(plan.sources, cwd);
50261
+ if (stranded.length === 0) {
50262
+ cliOutput(
50263
+ {
50264
+ kind: "generic",
50265
+ ok: true,
50266
+ strandedCount: 0,
50267
+ stranded: [],
50268
+ fixed: false
50269
+ },
50270
+ {
50271
+ command: "doctor exodus-residue",
50272
+ message: "No stranded legacy exodus source DBs (clean cutover or pre-migration install)."
50273
+ }
50274
+ );
50275
+ return;
50276
+ }
50277
+ for (const entry of stranded) {
50278
+ humanInfo(` STRANDED [${entry.scope}] ${entry.name} \u2192 ${entry.path}`);
50279
+ }
50280
+ if (!fix) {
50281
+ cliOutput(
50282
+ {
50283
+ kind: "generic",
50284
+ ok: false,
50285
+ strandedCount: stranded.length,
50286
+ stranded: stranded.map((s) => ({ name: s.name, path: s.path, scope: s.scope })),
50287
+ fixed: false
50288
+ },
50289
+ {
50290
+ command: "doctor exodus-residue",
50291
+ message: `${stranded.length} stranded legacy source DB(s) found. Run \`cleo doctor exodus-residue --fix\` to archive them.`
50292
+ }
50293
+ );
50294
+ process.exitCode = 1;
50295
+ return;
50296
+ }
50297
+ const archived = archiveStrandedResidue(stranded, plan.sources, cwd);
50298
+ const movedCount = archived.filter((a) => a.action === "archived").length;
50299
+ humanInfo(` Archived ${movedCount} stranded source DB(s) \u2192 _archive/.`);
50300
+ cliOutput(
50301
+ {
50302
+ kind: "generic",
50303
+ ok: true,
50304
+ strandedCount: stranded.length,
50305
+ stranded: stranded.map((s) => ({ name: s.name, path: s.path, scope: s.scope })),
50306
+ fixed: true,
50307
+ archived: archived.map((a) => ({
50308
+ name: a.name,
50309
+ action: a.action,
50310
+ archivedTo: a.archivedTo
50311
+ }))
50312
+ },
50313
+ {
50314
+ command: "doctor exodus-residue",
50315
+ message: `Archived ${movedCount} stranded legacy source DB(s) into _archive/.`
50316
+ }
50317
+ );
50318
+ }
50319
+ });
50320
+ }
50321
+ });
50322
+
50323
+ // packages/cleo/src/cli/commands/doctor-exodus.ts
50324
+ var doctor_exodus_exports = {};
50325
+ __export(doctor_exodus_exports, {
50326
+ doctorExodusCommand: () => doctorExodusCommand
50327
+ });
50328
+ import { buildExodusHealth } from "@cleocode/core/store/exodus/index.js";
50329
+ function fmtBytes2(n) {
50330
+ if (n < 1024) return `${n} B`;
50331
+ if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
50332
+ if (n < 1024 * 1024 * 1024) return `${(n / 1024 / 1024).toFixed(1)} MB`;
50333
+ return `${(n / 1024 / 1024 / 1024).toFixed(2)} GB`;
50334
+ }
50335
+ var doctorExodusCommand;
50336
+ var init_doctor_exodus = __esm({
50337
+ "packages/cleo/src/cli/commands/doctor-exodus.ts"() {
50338
+ "use strict";
50339
+ init_define_cli_command();
50340
+ init_renderers();
50341
+ doctorExodusCommand = defineCommand({
50342
+ meta: {
50343
+ name: "exodus-health",
50344
+ description: "Read-only exodus health report: per-scope migration state, legacy DB sizes, completion markers, disk headroom, and the kill-switch \u2014 with recommended next steps."
50345
+ },
50346
+ run() {
50347
+ const health = buildExodusHealth(process.cwd());
50348
+ for (const sc of [health.project, health.global]) {
50349
+ humanInfo(
50350
+ ` [${sc.scope}] state=${sc.state} consolidated=${sc.consolidatedExists ? "yes" : "no"} marker=${sc.markerPresent ? "yes" : "no"}`
50351
+ );
50352
+ for (const s of sc.legacySources) {
50353
+ humanInfo(
50354
+ ` ${s.present ? "\u2713" : "\xB7"} ${s.name.padEnd(18)} ${s.present ? fmtBytes2(s.bytes) : "-"}${s.large ? " \u26A0 LARGE" : ""}`
50355
+ );
50356
+ }
50357
+ }
50358
+ humanInfo(
50359
+ ` disk: ${fmtBytes2(health.availableBytes)} free / ${fmtBytes2(health.requiredBytes)} required \u2014 ${health.diskHeadroomOk ? "OK" : "LOW"}`
50360
+ );
50361
+ humanInfo(
50362
+ ` kill-switch (CLEO_DISABLE_EXODUS_ON_OPEN): ${health.killSwitchSet ? "SET" : "off"}`
50363
+ );
50364
+ if (health.recommendations.length > 0) {
50365
+ humanInfo(" recommendations:");
50366
+ for (const r of health.recommendations) humanInfo(` \u2022 ${r}`);
50367
+ }
50368
+ cliOutput(
50369
+ { kind: "generic", ...health },
50370
+ {
50371
+ command: "doctor exodus",
50372
+ message: `exodus health: project=${health.project.state}, global=${health.global.state}${health.dataParityOk ? "" : `, ${health.dataDeficits} DEFICIT(S)`}.`
50373
+ }
50374
+ );
50375
+ }
50376
+ });
50377
+ }
50378
+ });
50379
+
50091
50380
  // packages/cleo/src/cli/commands/doctor-legacy-backups.ts
50092
50381
  var doctor_legacy_backups_exports = {};
50093
50382
  __export(doctor_legacy_backups_exports, {
@@ -50692,97 +50981,6 @@ ${this.prefix}: \u2717 ${message}
50692
50981
  }
50693
50982
  });
50694
50983
 
50695
- // packages/cleo/src/cli/commands/doctor-exodus-residue.ts
50696
- import {
50697
- archiveStrandedResidue,
50698
- buildExodusPlan,
50699
- detectStrandedResidue
50700
- } from "@cleocode/core/store/exodus/index.js";
50701
- var doctorExodusResidueCommand;
50702
- var init_doctor_exodus_residue = __esm({
50703
- "packages/cleo/src/cli/commands/doctor-exodus-residue.ts"() {
50704
- "use strict";
50705
- init_define_cli_command();
50706
- init_renderers();
50707
- doctorExodusResidueCommand = defineCommand({
50708
- meta: {
50709
- name: "exodus-residue",
50710
- description: "Detect legacy exodus source DBs still present after a cutover (stranded residue that re-arms the exodus-on-open corruption trigger). Use --fix to archive them into _archive/ (reversible move, never deletes)."
50711
- },
50712
- args: {
50713
- fix: {
50714
- type: "boolean",
50715
- description: "Archive stranded legacy source DBs into _archive/ (move, never delete)",
50716
- default: false
50717
- }
50718
- },
50719
- run({ args }) {
50720
- const fix = args.fix === true;
50721
- const cwd = process.cwd();
50722
- const plan = buildExodusPlan(cwd);
50723
- const stranded = detectStrandedResidue(plan.sources, cwd);
50724
- if (stranded.length === 0) {
50725
- cliOutput(
50726
- {
50727
- kind: "generic",
50728
- ok: true,
50729
- strandedCount: 0,
50730
- stranded: [],
50731
- fixed: false
50732
- },
50733
- {
50734
- command: "doctor exodus-residue",
50735
- message: "No stranded legacy exodus source DBs (clean cutover or pre-migration install)."
50736
- }
50737
- );
50738
- return;
50739
- }
50740
- for (const entry of stranded) {
50741
- humanInfo(` STRANDED [${entry.scope}] ${entry.name} \u2192 ${entry.path}`);
50742
- }
50743
- if (!fix) {
50744
- cliOutput(
50745
- {
50746
- kind: "generic",
50747
- ok: false,
50748
- strandedCount: stranded.length,
50749
- stranded: stranded.map((s) => ({ name: s.name, path: s.path, scope: s.scope })),
50750
- fixed: false
50751
- },
50752
- {
50753
- command: "doctor exodus-residue",
50754
- message: `${stranded.length} stranded legacy source DB(s) found. Run \`cleo doctor exodus-residue --fix\` to archive them.`
50755
- }
50756
- );
50757
- process.exitCode = 1;
50758
- return;
50759
- }
50760
- const archived = archiveStrandedResidue(stranded, plan.sources, cwd);
50761
- const movedCount = archived.filter((a) => a.action === "archived").length;
50762
- humanInfo(` Archived ${movedCount} stranded source DB(s) \u2192 _archive/.`);
50763
- cliOutput(
50764
- {
50765
- kind: "generic",
50766
- ok: true,
50767
- strandedCount: stranded.length,
50768
- stranded: stranded.map((s) => ({ name: s.name, path: s.path, scope: s.scope })),
50769
- fixed: true,
50770
- archived: archived.map((a) => ({
50771
- name: a.name,
50772
- action: a.action,
50773
- archivedTo: a.archivedTo
50774
- }))
50775
- },
50776
- {
50777
- command: "doctor exodus-residue",
50778
- message: `Archived ${movedCount} stranded legacy source DB(s) into _archive/.`
50779
- }
50780
- );
50781
- }
50782
- });
50783
- }
50784
- });
50785
-
50786
50984
  // packages/cleo/src/cli/commands/migrate-agents-v2.ts
50787
50985
  var migrate_agents_v2_exports = {};
50788
50986
  __export(migrate_agents_v2_exports, {
@@ -51147,6 +51345,7 @@ var init_doctor = __esm({
51147
51345
  init_progress();
51148
51346
  init_renderers();
51149
51347
  init_doctor_db_substrate();
51348
+ init_doctor_exodus();
51150
51349
  init_doctor_exodus_residue();
51151
51350
  init_doctor_legacy_backups();
51152
51351
  init_doctor_projects();
@@ -51168,6 +51367,8 @@ var init_doctor = __esm({
51168
51367
  "legacy-backups": doctorLegacyBackupsCommand,
51169
51368
  // T11777 / Saga T11242 / Epic T11249 — exodus stranded-residue check (+ --fix)
51170
51369
  "exodus-residue": doctorExodusResidueCommand,
51370
+ // T11837 / Saga T11242 / Epic T11833 — read-only exodus health report
51371
+ "exodus-health": doctorExodusCommand,
51171
51372
  // T10458 / Saga T10431 / Epic T10436 — Release-readiness preflight
51172
51373
  "release-readiness": doctorReleaseReadinessCommand
51173
51374
  },
@@ -52295,14 +52496,16 @@ import { resolveDualScopeDbPath } from "@cleocode/core/store/dual-scope-db.js";
52295
52496
  import {
52296
52497
  archiveMigratedSources,
52297
52498
  buildExodusPlan as buildExodusPlan2,
52499
+ hasExodusCompleteMarker,
52298
52500
  runExodusMigrate,
52299
52501
  runExodusStatus,
52300
52502
  runExodusVerify,
52503
+ sealExodus,
52301
52504
  sourcesPresent,
52302
52505
  verifyMigration
52303
52506
  } from "@cleocode/core/store/exodus/index.js";
52304
52507
  import { isDataContinuityOk } from "@cleocode/core/store/exodus/on-open.js";
52305
- function fmtBytes2(n) {
52508
+ function fmtBytes3(n) {
52306
52509
  if (n < 1024) return `${n} B`;
52307
52510
  if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
52308
52511
  return `${(n / 1024 / 1024).toFixed(1)} MB`;
@@ -52327,7 +52530,7 @@ function showStatus() {
52327
52530
  }
52328
52531
  cliOutput(result, { command: "exodus status" });
52329
52532
  }
52330
- var migrateSubCommand, verifySubCommand, statusSubCommand, exodusCommand;
52533
+ var migrateSubCommand, verifySubCommand, sealSubCommand, statusSubCommand, exodusCommand;
52331
52534
  var init_exodus = __esm({
52332
52535
  "packages/cleo/src/cli/commands/exodus.ts"() {
52333
52536
  "use strict";
@@ -52350,23 +52553,52 @@ var init_exodus = __esm({
52350
52553
  type: "boolean",
52351
52554
  description: "Skip schema-version guard (AC9 \u2014 use with care)",
52352
52555
  default: false
52556
+ },
52557
+ scope: {
52558
+ type: "string",
52559
+ description: "Which scope to migrate: 'global', 'project', or 'both' (default = full fleet)",
52560
+ default: "both"
52353
52561
  }
52354
52562
  },
52355
52563
  async run({ args }) {
52356
52564
  const dryRun = args["dry-run"] === true;
52357
52565
  const forceCrossVersion = args["force-cross-version"] === true;
52358
- const plan = buildExodusPlan2(process.cwd());
52566
+ const scope = args.scope ?? "both";
52567
+ if (scope !== "both" && scope !== "project" && scope !== "global") {
52568
+ cliError(
52569
+ `Invalid --scope '${scope}'. Use 'global', 'project', or 'both'.`,
52570
+ 6 /* VALIDATION_ERROR */,
52571
+ { name: "E_INVALID_SCOPE" },
52572
+ { operation: "exodus.migrate" }
52573
+ );
52574
+ process.exitCode = 6 /* VALIDATION_ERROR */;
52575
+ return;
52576
+ }
52577
+ const fullPlan = buildExodusPlan2(process.cwd());
52578
+ const plan = scope === "both" ? fullPlan : { ...fullPlan, sources: fullPlan.sources.filter((s) => s.targetScope === scope) };
52579
+ if (scope !== "both" && hasExodusCompleteMarker(scope) && !dryRun) {
52580
+ cliOutput(
52581
+ { kind: "generic", ok: true, alreadySealed: true, scope },
52582
+ {
52583
+ command: "exodus migrate",
52584
+ message: `Scope '${scope}' already migrated + sealed (completion marker present) \u2014 nothing to do.`
52585
+ }
52586
+ );
52587
+ return;
52588
+ }
52359
52589
  humanInfo(`Exodus migration plan:`);
52360
52590
  humanInfo(
52361
- ` Source DBs (${plan.sources.length} total, ${fmtBytes2(plan.totalSourceBytes)} combined):`
52591
+ ` Source DBs (${plan.sources.length} total, ${fmtBytes3(plan.totalSourceBytes)} combined):`
52362
52592
  );
52363
52593
  for (const s of plan.sources) {
52364
52594
  humanInfo(` [${s.targetScope.padEnd(7)}] ${s.name.padEnd(20)} ${s.path}`);
52365
52595
  }
52366
52596
  humanInfo(` Target project cleo.db: ${plan.projectDbPath}`);
52367
52597
  humanInfo(` Target global cleo.db: ${plan.globalDbPath}`);
52368
- humanInfo(` Available disk: ${fmtBytes2(plan.availableBytes)}`);
52369
- humanInfo(` Required (3\xD7): ${fmtBytes2(3 * plan.totalSourceBytes)}`);
52598
+ humanInfo(` Available disk: ${fmtBytes3(plan.availableBytes)}`);
52599
+ humanInfo(
52600
+ ` Required (1.2\xD7 largest ${fmtBytes3(plan.largestSourceBytes)} + consolidated): ${fmtBytes3(plan.requiredBytes)}`
52601
+ );
52370
52602
  humanInfo(` Disk pre-flight: ${plan.diskPreflight ? "PASS" : "FAIL"}`);
52371
52603
  if (plan.resumeFromStaging) {
52372
52604
  humanInfo(` Resuming from existing staging: ${plan.stagingDir}`);
@@ -52383,6 +52615,8 @@ var init_exodus = __esm({
52383
52615
  targetScope: s.targetScope
52384
52616
  })),
52385
52617
  totalSourceBytes: plan.totalSourceBytes,
52618
+ largestSourceBytes: plan.largestSourceBytes,
52619
+ requiredBytes: plan.requiredBytes,
52386
52620
  availableBytes: plan.availableBytes,
52387
52621
  diskPreflight: plan.diskPreflight,
52388
52622
  stagingDir: plan.stagingDir,
@@ -52406,8 +52640,9 @@ var init_exodus = __esm({
52406
52640
  return;
52407
52641
  }
52408
52642
  if (!plan.diskPreflight) {
52643
+ const shortfall = Math.max(0, plan.requiredBytes - plan.availableBytes);
52409
52644
  cliError(
52410
- `Insufficient disk space for exodus. Need \u22653\xD7 source size (${fmtBytes2(plan.totalSourceBytes)}), have ${fmtBytes2(plan.availableBytes)}.`,
52645
+ `Insufficient disk space for exodus. Need \u2265${fmtBytes3(plan.requiredBytes)} (\u22481.2\xD7 largest source ${fmtBytes3(plan.largestSourceBytes)} + consolidated estimate ${fmtBytes3(plan.totalSourceBytes)}), have ${fmtBytes3(plan.availableBytes)} \u2014 ${fmtBytes3(shortfall)} short. Free up space (e.g. \`cleo backup prune\`) or move .cleo/ to a larger volume.`,
52411
52646
  6 /* VALIDATION_ERROR */,
52412
52647
  { name: "E_DISK_PREFLIGHT_FAIL" },
52413
52648
  { operation: "exodus.migrate" }
@@ -52544,6 +52779,67 @@ Passing tables (${passing.length}):`);
52544
52779
  }
52545
52780
  }
52546
52781
  });
52782
+ sealSubCommand = defineCommand({
52783
+ meta: {
52784
+ name: "seal",
52785
+ description: "Certify an already-migrated install: count-parity gate (no digest) \u2192 archive legacy source DBs (reversible) + write the completion marker so exodus-on-open stops re-firing."
52786
+ },
52787
+ args: {
52788
+ scope: {
52789
+ type: "string",
52790
+ description: "Which scope to seal: 'global', 'project', or 'both' (default)",
52791
+ default: "both"
52792
+ }
52793
+ },
52794
+ run({ args }) {
52795
+ const scope = args.scope ?? "both";
52796
+ if (scope !== "both" && scope !== "project" && scope !== "global") {
52797
+ cliError(
52798
+ `Invalid --scope '${scope}'. Use 'global', 'project', or 'both'.`,
52799
+ 6 /* VALIDATION_ERROR */,
52800
+ { name: "E_INVALID_SCOPE" },
52801
+ { operation: "exodus.seal" }
52802
+ );
52803
+ process.exitCode = 6 /* VALIDATION_ERROR */;
52804
+ return;
52805
+ }
52806
+ const plan = buildExodusPlan2(process.cwd());
52807
+ const result = sealExodus(plan, scope, process.cwd());
52808
+ if (!result.ok) {
52809
+ cliError(
52810
+ result.refusedReason ?? "Seal refused \u2014 parity deficit.",
52811
+ 1 /* GENERAL_ERROR */,
52812
+ { name: "E_EXODUS_SEAL_DEFICIT" },
52813
+ { operation: "exodus.seal" }
52814
+ );
52815
+ process.exitCode = 1 /* GENERAL_ERROR */;
52816
+ return;
52817
+ }
52818
+ for (const sc of result.scopes) {
52819
+ const moved = sc.archived.filter((a) => a.action === "archived").length;
52820
+ humanInfo(
52821
+ ` [${sc.scope}] ${sc.alreadySealed ? "re-sealed" : "sealed"} \u2014 archived ${moved} legacy DB(s), marker: ${sc.markerPath}`
52822
+ );
52823
+ }
52824
+ cliOutput(
52825
+ {
52826
+ kind: "generic",
52827
+ ok: true,
52828
+ scopesSealed: result.scopes.map((s) => s.scope),
52829
+ tablesVerified: result.parity.checked,
52830
+ archived: result.scopes.flatMap(
52831
+ (s) => s.archived.filter((a) => a.action === "archived").map((a) => ({ scope: s.scope, name: a.name, archivedTo: a.archivedTo }))
52832
+ )
52833
+ },
52834
+ {
52835
+ command: "exodus seal",
52836
+ message: `Sealed ${result.scopes.map((s) => s.scope).join(
52837
+ " + "
52838
+ )} \u2014 ${result.parity.checked} tables count-verified, legacy DBs archived (reversible).`
52839
+ }
52840
+ );
52841
+ }
52842
+ });
52547
52843
  statusSubCommand = defineCommand({
52548
52844
  meta: {
52549
52845
  name: "status",
@@ -52561,6 +52857,7 @@ Passing tables (${passing.length}):`);
52561
52857
  subCommands: {
52562
52858
  migrate: migrateSubCommand,
52563
52859
  verify: verifySubCommand,
52860
+ seal: sealSubCommand,
52564
52861
  status: statusSubCommand
52565
52862
  },
52566
52863
  async run({ cmd, rawArgs }) {
@@ -52939,6 +53236,7 @@ var init_find = __esm({
52939
53236
  }
52940
53237
  },
52941
53238
  async run({ args }) {
53239
+ if (maybeEmitDescribe("query", "tasks", "find", { command: "find" })) return;
52942
53240
  const limit = args.limit !== void 0 ? Number.parseInt(args.limit, 10) : void 0;
52943
53241
  const offset = args.offset !== void 0 ? Number.parseInt(args.offset, 10) : void 0;
52944
53242
  const params = {};
@@ -55651,6 +55949,7 @@ var init_list3 = __esm({
55651
55949
  meta: { name: "list", description: "List tasks with optional filters" },
55652
55950
  args: listArgs,
55653
55951
  async run({ args }) {
55952
+ if (maybeEmitDescribe("query", "tasks", "list", { command: "list" })) return;
55654
55953
  const limit = args["limit"] !== void 0 ? parseInt(args["limit"], 10) : void 0;
55655
55954
  const offset = args["offset"] !== void 0 ? parseInt(args["offset"], 10) : void 0;
55656
55955
  const params = {};
@@ -55800,13 +56099,7 @@ __export(llm_stream_exports, {
55800
56099
  streamCommand: () => streamCommand
55801
56100
  });
55802
56101
  async function buildSession(provider, model) {
55803
- const [
55804
- { resolveCredentials: resolveCredentials2 },
55805
- { ConcreteSession },
55806
- { AnthropicTransport },
55807
- { ChatCompletionsTransport },
55808
- { GeminiTransport }
55809
- ] = await Promise.all([
56102
+ const [{ resolveCredentials: resolveCredentials2 }, { ConcreteSession }, { ModelRunner }, { deriveApiWire }] = await Promise.all([
55810
56103
  import(
55811
56104
  /* webpackIgnore: true */
55812
56105
  "@cleocode/core/llm/credentials.js"
@@ -55817,15 +56110,11 @@ async function buildSession(provider, model) {
55817
56110
  ),
55818
56111
  import(
55819
56112
  /* webpackIgnore: true */
55820
- "@cleocode/core/llm/transports/anthropic.js"
55821
- ),
55822
- import(
55823
- /* webpackIgnore: true */
55824
- "@cleocode/core/llm/transports/chat-completions.js"
56113
+ "@cleocode/core/llm/model-runner.js"
55825
56114
  ),
55826
56115
  import(
55827
56116
  /* webpackIgnore: true */
55828
- "@cleocode/core/llm/transports/gemini.js"
56117
+ "@cleocode/core/llm/api-mode.js"
55829
56118
  )
55830
56119
  ]);
55831
56120
  const cred = resolveCredentials2(provider);
@@ -55834,14 +56123,7 @@ async function buildSession(provider, model) {
55834
56123
  `No credential found for provider '${provider}'. Set the appropriate environment variable or run 'cleo llm add ${provider}'.`
55835
56124
  );
55836
56125
  }
55837
- let transport;
55838
- if (provider === "anthropic") {
55839
- transport = cred.authType === "oauth" ? new AnthropicTransport({ authToken: cred.apiKey }) : new AnthropicTransport({ apiKey: cred.apiKey });
55840
- } else if (provider === "gemini") {
55841
- transport = new GeminiTransport({ apiKey: cred.apiKey });
55842
- } else {
55843
- transport = new ChatCompletionsTransport({ provider, apiKey: cred.apiKey });
55844
- }
56126
+ const wire = deriveApiWire(provider, cred.authType);
55845
56127
  const resolvedCredential = {
55846
56128
  provider,
55847
56129
  label: "default",
@@ -55850,9 +56132,14 @@ async function buildSession(provider, model) {
55850
56132
  expiresAt: null,
55851
56133
  refreshToken: null,
55852
56134
  extraHeaders: {},
55853
- baseUrl: null,
56135
+ baseUrl: wire.baseUrl,
55854
56136
  awsProfile: null
55855
56137
  };
56138
+ const transport = ModelRunner.buildTransportFromCredential(
56139
+ provider,
56140
+ resolvedCredential,
56141
+ wire.apiMode
56142
+ );
55856
56143
  return new ConcreteSession({ transport, model, credential: resolvedCredential });
55857
56144
  }
55858
56145
  async function getComputeCost() {
@@ -66768,7 +67055,7 @@ __export(schema_exports, {
66768
67055
  schemaCommand: () => schemaCommand2
66769
67056
  });
66770
67057
  import { getInputContract } from "@cleocode/core";
66771
- import { describeOperation as describeOperation2 } from "@cleocode/lafs";
67058
+ import { describeOperation as describeOperation3 } from "@cleocode/lafs";
66772
67059
  function resolveOperationDef(operationArg) {
66773
67060
  const dotIdx = operationArg.indexOf(".");
66774
67061
  if (dotIdx === -1) {
@@ -66904,7 +67191,7 @@ var init_schema = __esm({
66904
67191
  );
66905
67192
  return;
66906
67193
  }
66907
- const schema2 = describeOperation2(def, {
67194
+ const schema2 = describeOperation3(def, {
66908
67195
  includeGates,
66909
67196
  includeExamples
66910
67197
  });
@@ -73314,6 +73601,7 @@ var init_update = __esm({
73314
73601
  }
73315
73602
  },
73316
73603
  async run({ args, cmd }) {
73604
+ if (maybeEmitDescribe("mutate", "tasks", "update", { command: "update" })) return;
73317
73605
  const paramsArg = args.params;
73318
73606
  const paramsFileArg = args["params-file"];
73319
73607
  if (paramsArg !== void 0 || paramsFileArg !== void 0) {
@@ -74817,6 +75105,7 @@ var init_worktree3 = __esm({
74817
75105
 
74818
75106
  // packages/cleo/src/cli/index.ts
74819
75107
  init_dist();
75108
+ init_describe_context();
74820
75109
  init_field_context();
74821
75110
  init_format_context();
74822
75111
  import { readFileSync as readFileSync21 } from "node:fs";
@@ -75144,6 +75433,18 @@ var COMMAND_MANIFEST = [
75144
75433
  description: "Walk every DB in the inventory + report integrity, row counts, orphan dirs. ",
75145
75434
  load: async () => (await Promise.resolve().then(() => (init_doctor_db_substrate(), doctor_db_substrate_exports))).doctorDbSubstrateCommand
75146
75435
  },
75436
+ {
75437
+ exportName: "doctorExodusResidueCommand",
75438
+ name: "exodus-residue",
75439
+ description: "Detect legacy exodus source DBs still present after a cutover (stranded residue that ",
75440
+ load: async () => (await Promise.resolve().then(() => (init_doctor_exodus_residue(), doctor_exodus_residue_exports))).doctorExodusResidueCommand
75441
+ },
75442
+ {
75443
+ exportName: "doctorExodusCommand",
75444
+ name: "exodus-health",
75445
+ description: "Read-only exodus health report: per-scope migration state, legacy DB sizes, completion ",
75446
+ load: async () => (await Promise.resolve().then(() => (init_doctor_exodus(), doctor_exodus_exports))).doctorExodusCommand
75447
+ },
75147
75448
  {
75148
75449
  exportName: "doctorLegacyBackupsCommand",
75149
75450
  name: "legacy-backups",
@@ -76063,6 +76364,7 @@ async function startCli() {
76063
76364
  let verboseFlag = false;
76064
76365
  let outputModeRaw;
76065
76366
  let summaryFlag = false;
76367
+ let describeFlag = false;
76066
76368
  for (let i = 0; i < argv.length; i++) {
76067
76369
  const arg = argv[i];
76068
76370
  if (arg === "--json") rawOpts["json"] = true;
@@ -76074,6 +76376,7 @@ async function startCli() {
76074
76376
  else if (arg === "--verbose" || arg === "--full") verboseFlag = true;
76075
76377
  else if (arg === "--output" && i + 1 < argv.length) outputModeRaw = argv[++i];
76076
76378
  else if (arg === "--summary") summaryFlag = true;
76379
+ else if (arg === "--describe") describeFlag = true;
76077
76380
  }
76078
76381
  const interactiveTty = isInteractiveInvocation(argv) && process.stdout.isTTY === true;
76079
76382
  const formatResolution = resolveFormat(rawOpts, void 0, interactiveTty);
@@ -76103,6 +76406,7 @@ Valid modes: ${validModes.join(", ")}
76103
76406
  setOutputMode(resolvedMode);
76104
76407
  }
76105
76408
  setSummaryMode(summaryFlag);
76409
+ setDescribeMode(describeFlag);
76106
76410
  const isHelpOrVersion = argv.length === 0 || argv[0] === "--help" || argv[0] === "-h" || argv[0] === "--version" || argv[0] === "-V" || argv[0] === "help";
76107
76411
  if (argv[0] === "--version" || argv[0] === "-V") {
76108
76412
  const { cliOutput: cliOutput2 } = await Promise.resolve().then(() => (init_renderers(), renderers_exports));
@@ -76151,7 +76455,8 @@ Valid modes: ${validModes.join(", ")}
76151
76455
  }
76152
76456
  }
76153
76457
  }
76154
- await runMainWithLafsEnvelope(main, argv, customShowUsage);
76458
+ const dispatchArgv = describeFlag && !isHelpOrVersion ? [...argv, "__cleo_describe__"] : argv;
76459
+ await runMainWithLafsEnvelope(main, dispatchArgv, customShowUsage);
76155
76460
  }
76156
76461
  function asCittyCliError(value) {
76157
76462
  if (!(value instanceof Error)) return null;