@cleocode/cleo 2026.9.21 → 2026.9.22

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.
Files changed (57) hide show
  1. package/dist/cli/index.js +205 -63
  2. package/package.json +13 -13
  3. package/studio-dist/client/_app/immutable/chunks/{BAm68yLM.js → 0tcsbE6D.js} +1 -1
  4. package/studio-dist/client/_app/immutable/chunks/{Dm-Z-HM3.js → BKjRqkYg.js} +1 -1
  5. package/studio-dist/client/_app/immutable/chunks/{VBlPNtFM.js → BzdFEK5o.js} +1 -1
  6. package/studio-dist/client/_app/immutable/chunks/D2e9iItv.js +1 -0
  7. package/studio-dist/client/_app/immutable/entry/{app.CAn3fZgZ.js → app.CT-LXuds.js} +2 -2
  8. package/studio-dist/client/_app/immutable/entry/start.SG-NqdSp.js +1 -0
  9. package/studio-dist/client/_app/immutable/nodes/{0.DPKG0nUt.js → 0.CJNVG-lR.js} +1 -1
  10. package/studio-dist/client/_app/immutable/nodes/{1.C5hwmzhS.js → 1.C7ULqnrP.js} +1 -1
  11. package/studio-dist/client/_app/immutable/nodes/{15.CkAQwL0m.js → 15.BNOT6L5f.js} +1 -1
  12. package/studio-dist/client/_app/immutable/nodes/{16.Bd_23uIv.js → 16.D5aa4hwV.js} +1 -1
  13. package/studio-dist/client/_app/immutable/nodes/{17.CtZyGphA.js → 17.WSqtVr_-.js} +1 -1
  14. package/studio-dist/client/_app/immutable/nodes/{18.CIOnp98T.js → 18.Lyg3PWST.js} +1 -1
  15. package/studio-dist/client/_app/immutable/nodes/{20.Ba5DRAxg.js → 20.zTYVKImv.js} +1 -1
  16. package/studio-dist/client/_app/immutable/nodes/{24.8bVX9Vmh.js → 24.B2cU3PXS.js} +1 -1
  17. package/studio-dist/client/_app/immutable/nodes/{25.bGZTIjlV.js → 25.CKddYoS3.js} +2 -2
  18. package/studio-dist/client/_app/immutable/nodes/{26.fZumF6nO.js → 26.eXqGXEyv.js} +1 -1
  19. package/studio-dist/client/_app/immutable/nodes/{3.pmT0DBeN.js → 3.C5vwxSyN.js} +1 -1
  20. package/studio-dist/client/_app/version.json +1 -1
  21. package/studio-dist/server/chunks/{0-BM8t6VtI.js → 0-g-9QBgTv.js} +3 -3
  22. package/studio-dist/server/chunks/1-BkPP00YN.js +9 -0
  23. package/studio-dist/server/chunks/{15-CFHLKUmW.js → 15-5CTacdmx.js} +3 -3
  24. package/studio-dist/server/chunks/{16-tfoYc0dp.js → 16-DIJruZT8.js} +3 -3
  25. package/studio-dist/server/chunks/{17-C_IrYgVa.js → 17-yv-WCUot.js} +3 -3
  26. package/studio-dist/server/chunks/{18-BN7k6H6T.js → 18-qo45V8TJ.js} +3 -3
  27. package/studio-dist/server/chunks/{20-zMbfOrDH.js → 20-BImtok4E.js} +3 -3
  28. package/studio-dist/server/chunks/{24-CeL43Zy5.js → 24-JR2XU8Zk.js} +3 -3
  29. package/studio-dist/server/chunks/{25-x4Xarpc5.js → 25-FQ-ABq7g.js} +4 -4
  30. package/studio-dist/server/chunks/{26-BtHN2PS0.js → 26-C0EfiL70.js} +3 -3
  31. package/studio-dist/server/chunks/{3-CtS2Tb9q.js → 3-YulKrPK4.js} +3 -3
  32. package/studio-dist/server/chunks/{_layout.svelte-CvTfh8wi.js → _layout.svelte-DFY_t32i.js} +4 -4
  33. package/studio-dist/server/chunks/{_page.svelte-BmmYIPYK.js → _page.svelte-B1-qqkuN.js} +3 -3
  34. package/studio-dist/server/chunks/{_page.svelte-DRNt1JOo.js → _page.svelte-B2iUE3vx.js} +3 -3
  35. package/studio-dist/server/chunks/{_page.svelte-DdSAHPFQ.js → _page.svelte-BKGofZjs.js} +3 -3
  36. package/studio-dist/server/chunks/{_page.svelte-C_tWaWK8.js → _page.svelte-BY7VnsnD.js} +3 -3
  37. package/studio-dist/server/chunks/{_page.svelte-C9sPd8Bb.js → _page.svelte-DLPbtByz.js} +5 -5
  38. package/studio-dist/server/chunks/{_page.svelte-B5rNNTgb.js → _page.svelte-DbctFR26.js} +3 -3
  39. package/studio-dist/server/chunks/{_page.svelte-CCvfJcoQ.js → _page.svelte-DfLMLIQh.js} +4 -4
  40. package/studio-dist/server/chunks/{_page.svelte-BXtuVcSb.js → _page.svelte-DoMZFgyT.js} +3 -3
  41. package/studio-dist/server/chunks/{_page.svelte-D6Dzqvn1.js → _page.svelte-Drj7fNS_.js} +3 -3
  42. package/studio-dist/server/chunks/{_server.ts-DUl6nxe3.js → _server.ts-BMYX3KLE.js} +3 -3
  43. package/studio-dist/server/chunks/{_server.ts-Js54BBh9.js → _server.ts-BX1KvJuM.js} +3 -3
  44. package/studio-dist/server/chunks/{_server.ts-BfTazvMM.js → _server.ts-Ba7uukQ7.js} +3 -3
  45. package/studio-dist/server/chunks/{_server.ts-DTjKmh4V.js → _server.ts-BlYsd_GD.js} +2 -2
  46. package/studio-dist/server/chunks/{_server.ts-eLwAnREE.js → _server.ts-D8P3mren.js} +3 -3
  47. package/studio-dist/server/chunks/{client-LLdiye3o.js → client-BqWu1TLm.js} +2 -2
  48. package/studio-dist/server/chunks/{dist-BvU7NTCZ.js → dist-DPV5iK3m.js} +6 -4
  49. package/studio-dist/server/chunks/{error.svelte-Vdi9HF4O.js → error.svelte-CvROslRV.js} +3 -3
  50. package/studio-dist/server/chunks/{http-j9xEUElI.js → http-BVzADggg.js} +2 -2
  51. package/studio-dist/server/chunks/{internal-D5m7h-oY.js → internal-BlXMjztN.js} +2 -2
  52. package/studio-dist/server/chunks/{stores-rkKr99O6.js → stores-C8kmowVZ.js} +2 -2
  53. package/studio-dist/server/index.js +1 -1
  54. package/studio-dist/server/manifest.js +17 -17
  55. package/studio-dist/client/_app/immutable/chunks/B0moCQFp.js +0 -1
  56. package/studio-dist/client/_app/immutable/entry/start.DxHadWyO.js +0 -1
  57. package/studio-dist/server/chunks/1-CdIGyFBZ.js +0 -9
package/dist/cli/index.js CHANGED
@@ -13753,7 +13753,7 @@ var init_operations_registry = __esm({
13753
13753
  type: "string",
13754
13754
  required: false,
13755
13755
  enum: DOCS_LIFECYCLE_STATUSES,
13756
- description: `Override the new lifecycle status. Defaults to "draft" on every update. Valid: ${DOCS_LIFECYCLE_STATUSES.join("|")}.`
13756
+ description: `Override the new lifecycle status. Defaults to "draft" on every update. Alone (no file or content) it changes only the lifecycle status. Valid: ${DOCS_LIFECYCLE_STATUSES.join("|")}.`
13757
13757
  },
13758
13758
  {
13759
13759
  name: "dryRun",
@@ -16545,6 +16545,14 @@ function validateEvidenceForGate(gate, atoms) {
16545
16545
  hint: formatGateRequirementHint(gate)
16546
16546
  };
16547
16547
  }
16548
+ function parsePrReference(payload) {
16549
+ const match = /^(\d+)(?:@(\d+))?$/.exec(payload);
16550
+ if (!match) return null;
16551
+ const first = Number(match[1]);
16552
+ const second = match[2] === void 0 ? null : Number(match[2]);
16553
+ if (first <= 0 || second !== null && (second <= 0 || second === first)) return null;
16554
+ return second === null ? { prNumber: first } : { prNumber: second, componentPrNumber: first };
16555
+ }
16548
16556
  function parseEvidenceString(raw) {
16549
16557
  if (!raw || typeof raw !== "string") {
16550
16558
  throw new EvidenceParseError(
@@ -16603,26 +16611,16 @@ function parseEvidenceString(raw) {
16603
16611
  atoms.push({ kind: "decision", decisionId: payload });
16604
16612
  break;
16605
16613
  }
16606
- case "pr": {
16607
- const prNumber = Number(payload);
16608
- if (!Number.isInteger(prNumber) || prNumber <= 0) {
16609
- throw new EvidenceParseError(
16610
- `pr atom requires a positive integer PR number, got "${payload}" in "${chunk}"`,
16611
- "Use format: pr:<number> e.g. pr:357"
16612
- );
16613
- }
16614
- atoms.push({ kind: "pr", prNumber });
16615
- break;
16616
- }
16614
+ case "pr":
16617
16615
  case "ci": {
16618
- const prNumber = Number(payload);
16619
- if (!Number.isInteger(prNumber) || prNumber <= 0) {
16616
+ const ref = parsePrReference(payload);
16617
+ if (!ref) {
16620
16618
  throw new EvidenceParseError(
16621
- `ci atom requires a positive integer PR number, got "${payload}" in "${chunk}"`,
16622
- "Use format: ci:<number> e.g. ci:357"
16619
+ `${kind} atom requires a positive integer PR number, got "${payload}" in "${chunk}"`,
16620
+ `Use format: ${kind}:<number> e.g. ${kind}:357, or ${kind}:<component>@<integration> e.g. ${kind}:356@357`
16623
16621
  );
16624
16622
  }
16625
- atoms.push({ kind: "ci", prNumber });
16623
+ atoms.push(kind === "pr" ? { kind: "pr", ...ref } : { kind: "ci", ...ref });
16626
16624
  break;
16627
16625
  }
16628
16626
  case "loc-drop": {
@@ -16811,11 +16809,13 @@ var init_evidence_atom_schema = __esm({
16811
16809
  });
16812
16810
  prAtomSchema = z10.object({
16813
16811
  kind: z10.literal("pr"),
16814
- prNumber: z10.number().int().positive("pr atom requires a positive integer PR number")
16812
+ prNumber: z10.number().int().positive("pr atom requires a positive integer PR number"),
16813
+ componentPrNumber: z10.number().int().positive().optional()
16815
16814
  });
16816
16815
  ciAtomSchema = z10.object({
16817
16816
  kind: z10.literal("ci"),
16818
- prNumber: z10.number().int().positive("ci atom requires a positive integer PR number")
16817
+ prNumber: z10.number().int().positive("ci atom requires a positive integer PR number"),
16818
+ componentPrNumber: z10.number().int().positive().optional()
16819
16819
  });
16820
16820
  locDropAtomSchema = z10.object({
16821
16821
  kind: z10.literal("loc-drop"),
@@ -28765,14 +28765,23 @@ Recovery command: cleo docs update {slug} --file <your-file>`;
28765
28765
  }
28766
28766
  const hasFile = typeof filePath === "string" && filePath.length > 0;
28767
28767
  const hasContent = typeof inlineContent === "string";
28768
- if (hasFile === hasContent) {
28768
+ const statusOnly = !hasFile && !hasContent;
28769
+ if (hasFile && hasContent) {
28769
28770
  return lafsError(
28770
28771
  "E_INVALID_INPUT",
28771
- "Provide exactly one of --file <path> or --content <text>",
28772
+ "--file and --content are mutually exclusive",
28772
28773
  "update",
28773
28774
  'Use `cleo docs update <slug> --file ./new.md` OR `cleo docs update <slug> --content "..."`.'
28774
28775
  );
28775
28776
  }
28777
+ if (statusOnly && rawStatus === void 0) {
28778
+ return lafsError(
28779
+ "E_INVALID_INPUT",
28780
+ "Provide --file <path>, --content <text>, or --status <status> alone",
28781
+ "update",
28782
+ 'Use `--file ./new.md`, `--content "..."`, or `cleo docs update <slug> --status accepted` to change only the lifecycle status.'
28783
+ );
28784
+ }
28776
28785
  if (rawStatus !== void 0 && !isLifecycleStatus(rawStatus)) {
28777
28786
  return lafsError(
28778
28787
  "E_INVALID_INPUT",
@@ -28793,28 +28802,32 @@ Recovery command: cleo docs update {slug} --file <your-file>`;
28793
28802
  );
28794
28803
  }
28795
28804
  let backend = "llmtxt";
28796
- try {
28797
- const blobMirror = createAttachmentBlobStore(getProjectRoot5());
28798
- const updatedBytes = hasFile ? new Uint8Array(await readFile(resolve2(filePath))) : new Uint8Array(Buffer.from(inlineContent, "utf-8"));
28799
- const contentType = hasFile ? mimeFromPath(resolve2(filePath)) : "text/plain";
28800
- const store = createAttachmentStore();
28801
- const rows = await store.listAllInProject(projectRoot);
28802
- const ownerIds = Array.from(
28803
- new Set(
28804
- rows.filter((row) => row.metadata.id === outcome.result.attachmentId).map((row) => row.ownerId)
28805
- )
28806
- );
28807
- const mirrorOwnerIds = ownerIds.length > 0 ? ownerIds : [`slug:${outcome.result.slug}`];
28808
- for (const mirrorOwnerId of mirrorOwnerIds) {
28809
- const mirrorResult = await blobMirror.put(mirrorOwnerId, {
28810
- name: outcome.result.slug,
28811
- data: updatedBytes,
28812
- contentType
28813
- });
28814
- backend = mirrorResult.backend;
28815
- }
28816
- } catch {
28805
+ if (statusOnly) {
28817
28806
  backend = await currentAttachmentBackend();
28807
+ } else {
28808
+ try {
28809
+ const blobMirror = createAttachmentBlobStore(getProjectRoot5());
28810
+ const updatedBytes = hasFile ? new Uint8Array(await readFile(resolve2(filePath))) : new Uint8Array(Buffer.from(inlineContent, "utf-8"));
28811
+ const contentType = hasFile ? mimeFromPath(resolve2(filePath)) : "text/plain";
28812
+ const store = createAttachmentStore();
28813
+ const rows = await store.listAllInProject(projectRoot);
28814
+ const ownerIds = Array.from(
28815
+ new Set(
28816
+ rows.filter((row) => row.metadata.id === outcome.result.attachmentId).map((row) => row.ownerId)
28817
+ )
28818
+ );
28819
+ const mirrorOwnerIds = ownerIds.length > 0 ? ownerIds : [`slug:${outcome.result.slug}`];
28820
+ for (const mirrorOwnerId of mirrorOwnerIds) {
28821
+ const mirrorResult = await blobMirror.put(mirrorOwnerId, {
28822
+ name: outcome.result.slug,
28823
+ data: updatedBytes,
28824
+ contentType
28825
+ });
28826
+ backend = mirrorResult.backend;
28827
+ }
28828
+ } catch {
28829
+ backend = await currentAttachmentBackend();
28830
+ }
28818
28831
  }
28819
28832
  try {
28820
28833
  writeAuditEntry(getProjectRoot5(), {
@@ -38229,7 +38242,10 @@ async function orchestrateContextOp(params) {
38229
38242
  return orchestrateContext(params.epicId, getProjectRoot11());
38230
38243
  }
38231
38244
  async function orchestrateWavesOp(params) {
38232
- return orchestrateWaves(params.epicId, getProjectRoot11(), { via: params.via });
38245
+ return orchestrateWaves(params.epicId, getProjectRoot11(), {
38246
+ via: params.via,
38247
+ ...params.hideCompleted === true ? { hideCompleted: true } : {}
38248
+ });
38233
38249
  }
38234
38250
  async function orchestratePlanOp(params) {
38235
38251
  return orchestratePlan({
@@ -39048,7 +39064,8 @@ var init_orchestrate2 = __esm({
39048
39064
  const wavesVia = wavesViaRaw === "parent" || wavesViaRaw === "saga" || wavesViaRaw === "both" ? wavesViaRaw : void 0;
39049
39065
  const p = {
39050
39066
  epicId: params.epicId,
39051
- ...wavesVia !== void 0 && { via: wavesVia }
39067
+ ...wavesVia !== void 0 && { via: wavesVia },
39068
+ ...params.hideCompleted === true && { hideCompleted: true }
39052
39069
  };
39053
39070
  return wrapResult(await coreOps2.waves(p), "query", "orchestrate", operation, startTime);
39054
39071
  }
@@ -45129,6 +45146,9 @@ var init_mutate_minimal_envelope = __esm({
45129
45146
  "duplicate",
45130
45147
  "acceptanceCriteriaIds",
45131
45148
  "autoCompleted",
45149
+ // T12660: `cleo complete` reports a cleared focus pointer + next ready task.
45150
+ "focusCleared",
45151
+ "nextSuggested",
45132
45152
  "cascadeDeleted",
45133
45153
  // T10599 add-batch dry-run semantics exposed by the projected mutate
45134
45154
  // envelope. Keep these pointer-addressable for --field consumers.
@@ -45433,7 +45453,7 @@ function ensureCaampLibrary() {
45433
45453
  const req = createRequire(import.meta.url);
45434
45454
  const skillsPkgJson = req.resolve("@cleocode/skills/package.json");
45435
45455
  const candidate = dirname2(skillsPkgJson);
45436
- if (existsSync5(join8(candidate, "skills.json"))) {
45456
+ if (existsSync5(join8(candidate, "skills", "manifest.json"))) {
45437
45457
  skillsRoot = candidate;
45438
45458
  }
45439
45459
  } catch {
@@ -45442,7 +45462,7 @@ function ensureCaampLibrary() {
45442
45462
  const thisFile = fileURLToPath(import.meta.url);
45443
45463
  const packageRoot = join8(dirname2(thisFile), "..", "..", "..", "..", "..");
45444
45464
  const candidate = join8(packageRoot, "packages", "skills");
45445
- if (existsSync5(join8(candidate, "skills.json"))) {
45465
+ if (existsSync5(join8(candidate, "skills", "manifest.json"))) {
45446
45466
  skillsRoot = candidate;
45447
45467
  }
45448
45468
  }
@@ -54316,6 +54336,30 @@ var init_check3 = __esm({
54316
54336
  task: "T12648",
54317
54337
  script: "scripts/lint-emitted-skills.mjs",
54318
54338
  description: "Every skill code emits is installable (ratchet)"
54339
+ },
54340
+ {
54341
+ // T12649: gate 14's command/flag rules applied to every canonical
54342
+ // skill. Core-tier skills are zero-tolerance; others ratchet.
54343
+ id: "gate-31",
54344
+ task: "T12649",
54345
+ script: "scripts/lint-skill-commands.mjs",
54346
+ description: "Every cleo command a skill teaches exists and is runnable"
54347
+ },
54348
+ {
54349
+ // T12124 (gh#1256): skills declare the code they document in
54350
+ // metadata.covers. Here: every core/LOOM skill declares live covers;
54351
+ // CI adds the PR diff check (--base).
54352
+ id: "gate-32",
54353
+ task: "T12124",
54354
+ script: "scripts/lint-skill-coverage.mjs",
54355
+ description: "Skills declare live metadata.covers (PR diff checked in CI)"
54356
+ },
54357
+ {
54358
+ // T9148 / T12124: ct-cleo stays a thin pointer — ratcheted.
54359
+ id: "gate-33",
54360
+ task: "T12124",
54361
+ script: "scripts/check-ct-cleo-thin.mjs",
54362
+ description: "ct-cleo SKILL.md does not grow (thin-pointer ratchet)"
54319
54363
  }
54320
54364
  ];
54321
54365
  const scriptArgs = strict ? ["--strict"] : ["--check"];
@@ -55001,6 +55045,8 @@ var init_complete = __esm({
55001
55045
  if (Array.isArray(unblockedTasks) && unblockedTasks.length > 0) {
55002
55046
  output2["unblockedTasks"] = unblockedTasks;
55003
55047
  }
55048
+ if (typeof data?.focusCleared === "boolean") output2["focusCleared"] = data.focusCleared;
55049
+ if (data?.nextSuggested !== void 0) output2["nextSuggested"] = data.nextSuggested;
55004
55050
  const worktreeAutoComplete = data?.worktreeAutoComplete;
55005
55051
  if (worktreeAutoComplete && typeof worktreeAutoComplete === "object") {
55006
55052
  output2["worktreeAutoComplete"] = worktreeAutoComplete;
@@ -55451,7 +55497,7 @@ var init_conduit3 = __esm({
55451
55497
  args: {
55452
55498
  topic: {
55453
55499
  type: "string",
55454
- description: 'Topic name to publish to (e.g. "epic-T1149.wave-T1253")',
55500
+ description: 'Topic name to publish to (e.g. "epic-T1149.wave-2")',
55455
55501
  required: true
55456
55502
  },
55457
55503
  kind: {
@@ -55536,7 +55582,7 @@ var init_conduit3 = __esm({
55536
55582
  args: {
55537
55583
  topic: {
55538
55584
  type: "string",
55539
- description: 'Topic name to poll (e.g. "epic-T1149.wave-T1253")',
55585
+ description: 'Topic name to poll (e.g. "epic-T1149.wave-2")',
55540
55586
  required: true
55541
55587
  },
55542
55588
  limit: {
@@ -61248,11 +61294,15 @@ var init_docs3 = __esm({
61248
61294
  });
61249
61295
  process.exit(6 /* VALIDATION_ERROR */);
61250
61296
  }
61251
- if (filePath === void 0 && inlineContent === void 0) {
61252
- cliError("provide --file <path> OR --content <text>", 6 /* VALIDATION_ERROR */, {
61253
- name: "E_VALIDATION",
61254
- fix: 'Example: `cleo docs update my-doc --file ./new.md` OR `cleo docs update my-doc --content "..."`.'
61255
- });
61297
+ if (filePath === void 0 && inlineContent === void 0 && typeof args.status !== "string") {
61298
+ cliError(
61299
+ "provide --file <path>, --content <text>, or --status <status> alone",
61300
+ 6 /* VALIDATION_ERROR */,
61301
+ {
61302
+ name: "E_VALIDATION",
61303
+ fix: 'Example: `cleo docs update my-doc --file ./new.md`, `--content "..."`, or `cleo docs update my-doc --status accepted` to change only the lifecycle status.'
61304
+ }
61305
+ );
61256
61306
  process.exit(6 /* VALIDATION_ERROR */);
61257
61307
  }
61258
61308
  let resolvedFile;
@@ -74434,11 +74484,15 @@ The code-intelligence graph is project-scoped (ADR-090 \xB7 T11648): one store p
74434
74484
  },
74435
74485
  full: {
74436
74486
  type: "boolean",
74437
- description: "Parse every file instead of reusing unchanged files (default: incremental, falling back to full with a stated reason)"
74487
+ description: "Parse every file instead of reusing unchanged files (default: incremental, falling back to full with a stated reason). Also re-binds a moved project's graph (same project id, old recorded root) to the live root"
74438
74488
  },
74439
74489
  incremental: {
74440
74490
  type: "boolean",
74441
74491
  description: "Deprecated no-op: incremental analysis is now the default (use --full to rebuild)"
74492
+ },
74493
+ "drop-missing-repositories": {
74494
+ type: "boolean",
74495
+ description: "Drop included repositories that are missing on disk instead of refusing (the default keeps the previous graph). Dropped paths are reported with a one-command restore"
74442
74496
  }
74443
74497
  },
74444
74498
  async run({ args }) {
@@ -74459,6 +74513,7 @@ The code-intelligence graph is project-scoped (ADR-090 \xB7 T11648): one store p
74459
74513
  repoPath,
74460
74514
  projectIdOverride,
74461
74515
  full,
74516
+ dropMissingRepositories: args["drop-missing-repositories"] === true,
74462
74517
  includedRepositories: args["include-repositories"]?.split(",").map((entry) => entry.trim()).filter(Boolean),
74463
74518
  onProgress: ctx.format === "json" ? void 0 : (current, total, filePath) => {
74464
74519
  if (current % 50 === 0 || current === total) {
@@ -74469,6 +74524,14 @@ The code-intelligence graph is project-scoped (ADR-090 \xB7 T11648): one store p
74469
74524
  });
74470
74525
  humanInfo(`[nexus] nexus-bridge.md refreshed at ${repoPath}/.cleo/nexus-bridge.md`);
74471
74526
  humanInfo("[nexus] Project registered/updated in multi-project registry.");
74527
+ if (result.droppedRepositories)
74528
+ humanWarn(
74529
+ `[nexus] DROPPED missing included repositories: ${result.droppedRepositories.paths.join(", ")}. Restore them once they exist again: ${result.droppedRepositories.restoreCommand}`
74530
+ );
74531
+ if (result.rebind)
74532
+ humanInfo(
74533
+ `[nexus] Re-bound graph for ${result.rebind.projectId}: ${result.rebind.oldRoot} -> ${result.rebind.newRoot}`
74534
+ );
74472
74535
  humanInfo(
74473
74536
  `[nexus] Mode: ${result.summary.mode} \u2014 ${result.summary.reason} (parsed ${result.summary.parsedFiles}, reused ${result.summary.reusedFiles})`
74474
74537
  );
@@ -74484,7 +74547,10 @@ The code-intelligence graph is project-scoped (ADR-090 \xB7 T11648): one store p
74484
74547
  relationCount: result.relationCount,
74485
74548
  fileCount: result.fileCount,
74486
74549
  durationMs: result.durationMs,
74487
- assessment: result.assessment
74550
+ assessment: result.assessment,
74551
+ // T12659: receipt of a moved project's graph re-bound to the live root.
74552
+ ...result.rebind ? { rebind: result.rebind } : {},
74553
+ ...result.droppedRepositories ? { droppedRepositories: result.droppedRepositories } : {}
74488
74554
  },
74489
74555
  {
74490
74556
  command: "nexus-analyze",
@@ -76292,7 +76358,7 @@ var init_orchestrate3 = __esm({
76292
76358
  },
76293
76359
  wave: {
76294
76360
  type: "string",
76295
- description: "Filter to a single wave id (0-indexed)"
76361
+ description: "Filter to one wave, numbered from 1 as `cleo orchestrate waves` prints it (the n of topic epic-<id>.wave-<n>)"
76296
76362
  },
76297
76363
  json: {
76298
76364
  type: "boolean",
@@ -76507,7 +76573,7 @@ var init_orchestrate3 = __esm({
76507
76573
  wavesCommand2 = defineCommand({
76508
76574
  meta: {
76509
76575
  name: "waves",
76510
- description: "Compute dependency waves for an epic. When the epic is a Saga (label='saga', ADR-073), per-member wave plans are merged by index (wave N across all members \u2192 one unified wave N)."
76576
+ description: "Compute dependency waves for an epic. Wave numbers are stable: a finished wave keeps its number and is listed as completed (--hide-completed omits it). When the epic is a Saga (label='saga', ADR-073), per-member wave plans are merged by index (wave N across all members \u2192 one unified wave N)."
76511
76577
  },
76512
76578
  args: {
76513
76579
  epicId: {
@@ -76518,6 +76584,10 @@ var init_orchestrate3 = __esm({
76518
76584
  via: {
76519
76585
  type: "string",
76520
76586
  description: "Traversal mode (gh-390/ADR-073): 'parent' walks parentId only, 'saga' walks task_relations.type='groups' only, 'both' (default) auto-detects saga-labeled epics."
76587
+ },
76588
+ "hide-completed": {
76589
+ type: "boolean",
76590
+ description: "Omit finished waves. Wave numbers are stable, so the remaining waves keep their numbers (the n of topic epic-<id>.wave-<n>)"
76521
76591
  }
76522
76592
  },
76523
76593
  async run({ args }) {
@@ -76527,7 +76597,8 @@ var init_orchestrate3 = __esm({
76527
76597
  "waves",
76528
76598
  {
76529
76599
  epicId: args.epicId,
76530
- ...args.via !== void 0 && { via: args.via }
76600
+ ...args.via !== void 0 && { via: args.via },
76601
+ ...args["hide-completed"] === true && { hideCompleted: true }
76531
76602
  },
76532
76603
  { command: "orchestrate" }
76533
76604
  );
@@ -84359,6 +84430,8 @@ __export(skills_exports, {
84359
84430
  import {
84360
84431
  AgentsSkillsRealDirError,
84361
84432
  skills as coreSkills,
84433
+ restoreBundledSkillQuarantine,
84434
+ runBundledSkillPrune,
84362
84435
  runDoctorAdopt,
84363
84436
  runDoctorBridge
84364
84437
  } from "@cleocode/core";
@@ -84388,7 +84461,7 @@ function buildSkillsDoctorAdoptAdapters() {
84388
84461
  }
84389
84462
  };
84390
84463
  }
84391
- var listCommand24, searchCommand4, findCommand8, validateCommand8, infoCommand, installCommand3, uninstallCommand2, enableCommand2, disableCommand2, refreshCommand, dispatchCommand, catalogCommand, precedenceCommand, depsCommand4, doctorBridgeCommand, doctorAdoptOrphansCommand, statsCommand6, importHermesCommand, migrateCommand2, pruneTelemetryCommand, spawnProvidersCommand, doctorDiagnoseCommand, doctorCommand4, proposePatchCommand, skillsCommand2;
84464
+ var listCommand24, searchCommand4, findCommand8, validateCommand8, infoCommand, installCommand3, uninstallCommand2, enableCommand2, disableCommand2, refreshCommand, dispatchCommand, catalogCommand, precedenceCommand, depsCommand4, doctorBridgeCommand, doctorAdoptOrphansCommand, doctorPruneCommand, doctorRestoreCommand, statsCommand6, importHermesCommand, migrateCommand2, pruneTelemetryCommand, spawnProvidersCommand, doctorDiagnoseCommand, doctorCommand4, proposePatchCommand, skillsCommand2;
84392
84465
  var init_skills2 = __esm({
84393
84466
  "packages/cleo/src/cli/commands/skills.ts"() {
84394
84467
  "use strict";
@@ -84481,11 +84554,14 @@ var init_skills2 = __esm({
84481
84554
  }
84482
84555
  });
84483
84556
  validateCommand8 = defineCommand({
84484
- meta: { name: "validate", description: "Validate skill against protocol" },
84557
+ meta: {
84558
+ name: "validate",
84559
+ description: "Validate a SKILL.md; exits non-zero with findings. Every skill is checked against the Agent Skills standard (valid YAML; name and description present, well-formed, no tags). A CLEO bundled skill (packages/skills/skills, or an installed copy of one) must also meet the CLEO frontmatter contract gate 29 enforces: metadata.version X.Y.Z, metadata.tier, metadata.install, no top-level tier/core/category, name equal to its directory."
84560
+ },
84485
84561
  args: {
84486
84562
  "skill-name": {
84487
84563
  type: "positional",
84488
- description: "Skill name to validate",
84564
+ description: "Skill name, skill directory, or SKILL.md path",
84489
84565
  required: true
84490
84566
  }
84491
84567
  },
@@ -84827,6 +84903,70 @@ var init_skills2 = __esm({
84827
84903
  }
84828
84904
  }
84829
84905
  });
84906
+ doctorPruneCommand = defineCommand({
84907
+ meta: {
84908
+ name: "prune",
84909
+ description: "Quarantine bundled skills CLEO no longer installs (internal/retired), only where its install ledger proves ownership"
84910
+ },
84911
+ args: {
84912
+ "dry-run": {
84913
+ type: "boolean",
84914
+ description: "List what would be quarantined or kept; move nothing"
84915
+ },
84916
+ json: { type: "boolean", description: "Output as JSON (default)" },
84917
+ human: { type: "boolean", description: "Output in human-readable format" }
84918
+ },
84919
+ async run({ args }) {
84920
+ try {
84921
+ const receipt = await runBundledSkillPrune({ dryRun: args["dry-run"] === true });
84922
+ if (!receipt) {
84923
+ cliError("bundled @cleocode/skills not found", "E_NOT_FOUND", void 0, {
84924
+ operation: "skills.doctor.prune"
84925
+ });
84926
+ process.exit(4);
84927
+ }
84928
+ cliOutput(receipt, { command: "skills doctor prune", operation: "skills.doctor.prune" });
84929
+ } catch (error) {
84930
+ cliError(
84931
+ error instanceof Error ? error.message : String(error),
84932
+ "E_INTERNAL_ERROR",
84933
+ void 0,
84934
+ {
84935
+ operation: "skills.doctor.prune"
84936
+ }
84937
+ );
84938
+ process.exit(1);
84939
+ }
84940
+ }
84941
+ });
84942
+ doctorRestoreCommand = defineCommand({
84943
+ meta: {
84944
+ name: "restore",
84945
+ description: "List skill quarantines, or restore one by id (reverses cleo skills doctor prune)"
84946
+ },
84947
+ args: {
84948
+ id: { type: "positional", required: false, description: "Quarantine id (omit to list)" },
84949
+ json: { type: "boolean", description: "Output as JSON (default)" },
84950
+ human: { type: "boolean", description: "Output in human-readable format" }
84951
+ },
84952
+ async run({ args }) {
84953
+ try {
84954
+ const id = typeof args.id === "string" && args.id !== "" ? args.id : void 0;
84955
+ const result = await restoreBundledSkillQuarantine(id);
84956
+ cliOutput(result, { command: "skills doctor restore", operation: "skills.doctor.restore" });
84957
+ } catch (error) {
84958
+ cliError(
84959
+ error instanceof Error ? error.message : String(error),
84960
+ "E_INTERNAL_ERROR",
84961
+ void 0,
84962
+ {
84963
+ operation: "skills.doctor.restore"
84964
+ }
84965
+ );
84966
+ process.exit(1);
84967
+ }
84968
+ }
84969
+ });
84830
84970
  statsCommand6 = defineCommand({
84831
84971
  meta: {
84832
84972
  name: "stats",
@@ -85012,12 +85152,14 @@ var init_skills2 = __esm({
85012
85152
  doctorCommand4 = defineCommand({
85013
85153
  meta: {
85014
85154
  name: "doctor",
85015
- description: "Skill-store health checks (diagnose, bridge, adopt-orphans)"
85155
+ description: "Skill-store health checks (diagnose, bridge, adopt-orphans, prune, restore)"
85016
85156
  },
85017
85157
  subCommands: {
85018
85158
  diagnose: doctorDiagnoseCommand,
85019
85159
  bridge: doctorBridgeCommand,
85020
- "adopt-orphans": doctorAdoptOrphansCommand
85160
+ "adopt-orphans": doctorAdoptOrphansCommand,
85161
+ prune: doctorPruneCommand,
85162
+ restore: doctorRestoreCommand
85021
85163
  },
85022
85164
  async run({ cmd, rawArgs }) {
85023
85165
  if (isSubCommandDispatch(rawArgs, cmd.subCommands)) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cleocode/cleo",
3
- "version": "2026.9.21",
3
+ "version": "2026.9.22",
4
4
  "description": "CLEO CLI — the assembled product consuming @cleocode/core",
5
5
  "type": "module",
6
6
  "main": "./dist/cli/index.js",
@@ -31,17 +31,17 @@
31
31
  "tree-sitter-rust": "0.23.1",
32
32
  "tree-sitter-typescript": "^0.23.2",
33
33
  "yaml": "^2.8.3",
34
- "@cleocode/animations": "2026.9.21",
35
- "@cleocode/cant": "2026.9.21",
36
- "@cleocode/core": "2026.9.21",
37
- "@cleocode/contracts": "2026.9.21",
38
- "@cleocode/caamp": "2026.9.21",
39
- "@cleocode/nexus": "2026.9.21",
40
- "@cleocode/lafs": "2026.9.21",
41
- "@cleocode/playbooks": "2026.9.21",
42
- "@cleocode/worktree": "2026.9.21",
43
- "@cleocode/runtime": "2026.9.21",
44
- "@cleocode/paths": "2026.9.21"
34
+ "@cleocode/animations": "2026.9.22",
35
+ "@cleocode/caamp": "2026.9.22",
36
+ "@cleocode/cant": "2026.9.22",
37
+ "@cleocode/contracts": "2026.9.22",
38
+ "@cleocode/core": "2026.9.22",
39
+ "@cleocode/lafs": "2026.9.22",
40
+ "@cleocode/nexus": "2026.9.22",
41
+ "@cleocode/paths": "2026.9.22",
42
+ "@cleocode/playbooks": "2026.9.22",
43
+ "@cleocode/runtime": "2026.9.22",
44
+ "@cleocode/worktree": "2026.9.22"
45
45
  },
46
46
  "engines": {
47
47
  "node": ">=24.16.0"
@@ -63,7 +63,7 @@
63
63
  "devDependencies": {
64
64
  "@types/node-cron": "^3.0.11",
65
65
  "vitest": "^4.1.4",
66
- "@cleocode/utils": "2026.9.21"
66
+ "@cleocode/utils": "2026.9.22"
67
67
  },
68
68
  "repository": {
69
69
  "type": "git",