@cleocode/cleo 2026.5.88 → 2026.5.89

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
@@ -7324,6 +7324,31 @@ var init_registry = __esm({
7324
7324
  }
7325
7325
  ]
7326
7326
  },
7327
+ // T9742 — skill.migrate: legacy XDG store → ~/.cleo/skills/
7328
+ {
7329
+ gateway: "mutate",
7330
+ domain: "tools",
7331
+ operation: "skill.migrate",
7332
+ description: "tools.skill.migrate (mutate) \u2014 migrate ~/.local/share/agents/skills into ~/.cleo/skills (SSoT); supports --dry-run preview + --rollback restore",
7333
+ tier: 2,
7334
+ idempotent: true,
7335
+ sessionRequired: false,
7336
+ requiredParams: [],
7337
+ params: [
7338
+ {
7339
+ name: "dryRun",
7340
+ type: "boolean",
7341
+ required: false,
7342
+ description: "Preview the plan without writing anything"
7343
+ },
7344
+ {
7345
+ name: "rollback",
7346
+ type: "boolean",
7347
+ required: false,
7348
+ description: "Restore the legacy tree from the most recent backup tarball"
7349
+ }
7350
+ ]
7351
+ },
7327
7352
  // T9690 — skill.stats: Sphere B telemetry rollup
7328
7353
  {
7329
7354
  gateway: "query",
@@ -26037,16 +26062,16 @@ async function orchestrateRejectOp(params) {
26037
26062
  async function orchestrateClassify(request, context, projectRoot) {
26038
26063
  try {
26039
26064
  const { getCleoCantWorkflowsDir } = await import("@cleocode/core/internal");
26040
- const { readFileSync: readFileSync19, readdirSync: readdirSync4, existsSync: existsSync18 } = await import("node:fs");
26065
+ const { readFileSync: readFileSync18, readdirSync: readdirSync4, existsSync: existsSync17 } = await import("node:fs");
26041
26066
  const { join: join33 } = await import("node:path");
26042
26067
  const workflowsDir = getCleoCantWorkflowsDir();
26043
26068
  const combined = `${request} ${context ?? ""}`.toLowerCase();
26044
26069
  const matches = [];
26045
- if (existsSync18(workflowsDir)) {
26070
+ if (existsSync17(workflowsDir)) {
26046
26071
  const files = readdirSync4(workflowsDir).filter((f) => f.endsWith(".cant"));
26047
26072
  for (const file of files) {
26048
26073
  try {
26049
- const src = readFileSync19(join33(workflowsDir, file), "utf-8");
26074
+ const src = readFileSync18(join33(workflowsDir, file), "utf-8");
26050
26075
  const teamMatch = /^team\s+(\S+):/m.exec(src);
26051
26076
  if (!teamMatch) continue;
26052
26077
  const teamName = teamMatch[1];
@@ -26062,11 +26087,11 @@ async function orchestrateClassify(request, context, projectRoot) {
26062
26087
  }
26063
26088
  }
26064
26089
  const localCantDir = join33(projectRoot, CLEO_DIR_NAME, WORKFLOWS_SUBDIR);
26065
- if (existsSync18(localCantDir)) {
26090
+ if (existsSync17(localCantDir)) {
26066
26091
  const files = readdirSync4(localCantDir).filter((f) => f.endsWith(".cant"));
26067
26092
  for (const file of files) {
26068
26093
  try {
26069
- const src = readFileSync19(join33(localCantDir, file), "utf-8");
26094
+ const src = readFileSync18(join33(localCantDir, file), "utf-8");
26070
26095
  const teamMatch = /^team\s+(\S+):/m.exec(src);
26071
26096
  if (!teamMatch) continue;
26072
26097
  const teamName = teamMatch[1];
@@ -29913,6 +29938,7 @@ import {
29913
29938
  toolsSkillImportHermes,
29914
29939
  toolsSkillInstall,
29915
29940
  toolsSkillList,
29941
+ toolsSkillMigrate,
29916
29942
  toolsSkillPrecedenceResolve,
29917
29943
  toolsSkillPrecedenceShow,
29918
29944
  toolsSkillPruneTelemetry,
@@ -30016,6 +30042,7 @@ var init_tools = __esm({
30016
30042
  "skill.uninstall",
30017
30043
  "skill.refresh",
30018
30044
  "skill.import.hermes",
30045
+ "skill.migrate",
30019
30046
  "skill.prune.telemetry",
30020
30047
  // provider
30021
30048
  "provider.inject",
@@ -30301,6 +30328,14 @@ var init_tools = __esm({
30301
30328
  const result = await toolsSkillImportHermes({ hermesHome, dryRun });
30302
30329
  return wrapResult(result, "mutate", "tools", "skill.import.hermes", startTime);
30303
30330
  }
30331
+ // T9742 — skill.migrate: legacy XDG store → ~/.cleo/skills/ (SSoT)
30332
+ case "migrate": {
30333
+ const result = await toolsSkillMigrate({
30334
+ dryRun: params?.dryRun === true,
30335
+ rollback: params?.rollback === true
30336
+ });
30337
+ return wrapResult(result, "mutate", "tools", "skill.migrate", startTime);
30338
+ }
30304
30339
  // T9693 — skill.prune.telemetry: retention sweep on skill_usage
30305
30340
  case "prune.telemetry": {
30306
30341
  const olderThanDays = typeof params?.olderThanDays === "number" ? params.olderThanDays : void 0;
@@ -33228,12 +33263,12 @@ var init_agent = __esm({
33228
33263
  transportConfig: {},
33229
33264
  isActive: true
33230
33265
  });
33231
- const { existsSync: existsSync18, mkdirSync: mkdirSync7, writeFileSync: writeFileSync7 } = await import("node:fs");
33266
+ const { existsSync: existsSync17, mkdirSync: mkdirSync7, writeFileSync: writeFileSync7 } = await import("node:fs");
33232
33267
  const { join: join33 } = await import("node:path");
33233
33268
  const cantDir = join33(CLEO_DIR_NAME, AGENTS_SUBDIR);
33234
33269
  const cantPath = join33(cantDir, `${agentId}.cant`);
33235
33270
  let cantScaffolded = false;
33236
- if (!existsSync18(cantPath)) {
33271
+ if (!existsSync17(cantPath)) {
33237
33272
  mkdirSync7(cantDir, { recursive: true });
33238
33273
  const role = classification ?? "specialist";
33239
33274
  const cantContent = `---
@@ -33293,7 +33328,7 @@ agent ${agentId}:
33293
33328
  data: {
33294
33329
  agentId: credential.agentId,
33295
33330
  displayName: credential.displayName,
33296
- cantFile: cantScaffolded ? cantPath : existsSync18(cantPath) ? cantPath : null,
33331
+ cantFile: cantScaffolded ? cantPath : existsSync17(cantPath) ? cantPath : null,
33297
33332
  cantScaffolded
33298
33333
  }
33299
33334
  },
@@ -33414,7 +33449,7 @@ agent ${agentId}:
33414
33449
  const { AgentRegistryAccessor } = await import("@cleocode/core/agents");
33415
33450
  const { getDb: getDb3 } = await import("@cleocode/core/internal");
33416
33451
  const { createRuntime } = await import("@cleocode/runtime");
33417
- const { existsSync: existsSync18, readFileSync: readFileSync19 } = await import("node:fs");
33452
+ const { existsSync: existsSync17, readFileSync: readFileSync18 } = await import("node:fs");
33418
33453
  const { join: join33 } = await import("node:path");
33419
33454
  await getDb3();
33420
33455
  const registry = new AgentRegistryAccessor(getProjectRoot23());
@@ -33436,8 +33471,8 @@ agent ${agentId}:
33436
33471
  let profile = null;
33437
33472
  let cantValidation = null;
33438
33473
  const cantPath = args.cant ?? join33(CLEO_DIR_NAME, AGENTS_SUBDIR, `${args.agentId}.cant`);
33439
- if (existsSync18(cantPath)) {
33440
- profile = readFileSync19(cantPath, "utf-8");
33474
+ if (existsSync17(cantPath)) {
33475
+ profile = readFileSync18(cantPath, "utf-8");
33441
33476
  try {
33442
33477
  const cantModule = await import("@cleocode/cant");
33443
33478
  const validate = "validate" in cantModule ? cantModule.validate : null;
@@ -33968,7 +34003,7 @@ Task ${args.taskId} reassigned to you by ${active.agentId}. Run: cleo show ${arg
33968
34003
  const { AgentRegistryAccessor } = await import("@cleocode/core/agents");
33969
34004
  const { getDb: getDb3 } = await import("@cleocode/core/internal");
33970
34005
  const { createRuntime } = await import("@cleocode/runtime");
33971
- const { existsSync: existsSync18 } = await import("node:fs");
34006
+ const { existsSync: existsSync17 } = await import("node:fs");
33972
34007
  const { join: join33 } = await import("node:path");
33973
34008
  const { execFile: execFile2 } = await import("node:child_process");
33974
34009
  const { promisify: promisify2 } = await import("node:util");
@@ -33990,7 +34025,7 @@ Task ${args.taskId} reassigned to you by ${active.agentId}. Run: cleo show ${arg
33990
34025
  await registry.update(args.agentId, { isActive: true });
33991
34026
  await registry.markUsed(args.agentId);
33992
34027
  const cantPath = join33(CLEO_DIR_NAME, AGENTS_SUBDIR, `${args.agentId}.cant`);
33993
- const hasProfile = existsSync18(cantPath);
34028
+ const hasProfile = existsSync17(cantPath);
33994
34029
  const runtime = await createRuntime(registry, {
33995
34030
  agentId: args.agentId,
33996
34031
  pollIntervalMs: 5e3,
@@ -34848,11 +34883,11 @@ Task ${args.taskId} reassigned to you by ${active.agentId}. Run: cleo show ${arg
34848
34883
  async run({ args }) {
34849
34884
  let tempDir = null;
34850
34885
  try {
34851
- const { existsSync: existsSync18, mkdirSync: mkdirSync7, statSync, readdirSync: readdirSync4, copyFileSync } = await import("node:fs");
34886
+ const { existsSync: existsSync17, mkdirSync: mkdirSync7, statSync, readdirSync: readdirSync4, copyFileSync } = await import("node:fs");
34852
34887
  const { join: join33, basename, resolve: resolve9, extname } = await import("node:path");
34853
34888
  const { tmpdir: tmpdir2 } = await import("node:os");
34854
34889
  const resolvedPath = resolve9(args.path);
34855
- if (!existsSync18(resolvedPath)) {
34890
+ if (!existsSync17(resolvedPath)) {
34856
34891
  cliOutput(
34857
34892
  {
34858
34893
  success: false,
@@ -34872,11 +34907,11 @@ Task ${args.taskId} reassigned to you by ${active.agentId}. Run: cleo show ${arg
34872
34907
  if (stat3.isFile() && ext === ".cant") {
34873
34908
  cantPath = resolvedPath;
34874
34909
  } else if (stat3.isFile() && ext === ".cantz") {
34875
- const { execFileSync: execFileSync6 } = await import("node:child_process");
34910
+ const { execFileSync: execFileSync5 } = await import("node:child_process");
34876
34911
  tempDir = join33(tmpdir2(), `cleo-agent-install-${Date.now()}`);
34877
34912
  mkdirSync7(tempDir, { recursive: true });
34878
34913
  try {
34879
- execFileSync6("unzip", ["-o", "-q", resolvedPath, "-d", tempDir], {
34914
+ execFileSync5("unzip", ["-o", "-q", resolvedPath, "-d", tempDir], {
34880
34915
  encoding: "utf-8",
34881
34916
  timeout: 3e4
34882
34917
  });
@@ -34913,7 +34948,7 @@ Task ${args.taskId} reassigned to you by ${active.agentId}. Run: cleo show ${arg
34913
34948
  }
34914
34949
  const agentName = topLevel[0];
34915
34950
  const personaPath = join33(tempDir, agentName, "persona.cant");
34916
- if (!existsSync18(personaPath)) {
34951
+ if (!existsSync17(personaPath)) {
34917
34952
  cliOutput(
34918
34953
  {
34919
34954
  success: false,
@@ -34932,7 +34967,7 @@ Task ${args.taskId} reassigned to you by ${active.agentId}. Run: cleo show ${arg
34932
34967
  } else if (stat3.isDirectory()) {
34933
34968
  const agentName = basename(resolvedPath);
34934
34969
  const personaPath = join33(resolvedPath, "persona.cant");
34935
- if (!existsSync18(personaPath)) {
34970
+ if (!existsSync17(personaPath)) {
34936
34971
  cliOutput(
34937
34972
  {
34938
34973
  success: false,
@@ -35067,11 +35102,11 @@ Task ${args.taskId} reassigned to you by ${active.agentId}. Run: cleo show ${arg
35067
35102
  },
35068
35103
  async run({ args }) {
35069
35104
  try {
35070
- const { existsSync: existsSync18, statSync } = await import("node:fs");
35105
+ const { existsSync: existsSync17, statSync } = await import("node:fs");
35071
35106
  const { resolve: resolve9, basename, dirname: dirname13 } = await import("node:path");
35072
- const { execFileSync: execFileSync6 } = await import("node:child_process");
35107
+ const { execFileSync: execFileSync5 } = await import("node:child_process");
35073
35108
  const resolvedDir = resolve9(args.dir);
35074
- if (!existsSync18(resolvedDir) || !statSync(resolvedDir).isDirectory()) {
35109
+ if (!existsSync17(resolvedDir) || !statSync(resolvedDir).isDirectory()) {
35075
35110
  cliOutput(
35076
35111
  {
35077
35112
  success: false,
@@ -35087,7 +35122,7 @@ Task ${args.taskId} reassigned to you by ${active.agentId}. Run: cleo show ${arg
35087
35122
  }
35088
35123
  const { join: join33 } = await import("node:path");
35089
35124
  const personaPath = join33(resolvedDir, "persona.cant");
35090
- if (!existsSync18(personaPath)) {
35125
+ if (!existsSync17(personaPath)) {
35091
35126
  cliOutput(
35092
35127
  {
35093
35128
  success: false,
@@ -35106,7 +35141,7 @@ Task ${args.taskId} reassigned to you by ${active.agentId}. Run: cleo show ${arg
35106
35141
  const archivePath = resolve9(archiveName);
35107
35142
  const parentDir = dirname13(resolvedDir);
35108
35143
  try {
35109
- execFileSync6("zip", ["-r", archivePath, agentName], {
35144
+ execFileSync5("zip", ["-r", archivePath, agentName], {
35110
35145
  cwd: parentDir,
35111
35146
  encoding: "utf-8",
35112
35147
  timeout: 3e4
@@ -35203,7 +35238,7 @@ Task ${args.taskId} reassigned to you by ${active.agentId}. Run: cleo show ${arg
35203
35238
  },
35204
35239
  async run({ args }) {
35205
35240
  try {
35206
- const { existsSync: existsSync18, mkdirSync: mkdirSync7, writeFileSync: writeFileSync7 } = await import("node:fs");
35241
+ const { existsSync: existsSync17, mkdirSync: mkdirSync7, writeFileSync: writeFileSync7 } = await import("node:fs");
35207
35242
  const { join: join33 } = await import("node:path");
35208
35243
  const { homedir: homedir8 } = await import("node:os");
35209
35244
  const name = args.name;
@@ -35270,7 +35305,7 @@ Task ${args.taskId} reassigned to you by ${active.agentId}. Run: cleo show ${arg
35270
35305
  targetRoot = join33(getProjectRoot23(), CLEO_DIR_NAME, CANT_AGENTS_SUBDIR);
35271
35306
  }
35272
35307
  const agentDir = join33(targetRoot, name);
35273
- if (existsSync18(agentDir)) {
35308
+ if (existsSync17(agentDir)) {
35274
35309
  cliOutput(
35275
35310
  {
35276
35311
  success: false,
@@ -35415,15 +35450,15 @@ Task ${args.taskId} reassigned to you by ${active.agentId}. Run: cleo show ${arg
35415
35450
  },
35416
35451
  async run({ args }) {
35417
35452
  try {
35418
- const { existsSync: existsSync18, readFileSync: readFileSync19, mkdirSync: mkdirSync7 } = await import("node:fs");
35453
+ const { existsSync: existsSync17, readFileSync: readFileSync18, mkdirSync: mkdirSync7 } = await import("node:fs");
35419
35454
  const { resolve: resolve9, join: join33 } = await import("node:path");
35420
35455
  const specPath = resolve9(args.spec);
35421
- if (!existsSync18(specPath)) {
35456
+ if (!existsSync17(specPath)) {
35422
35457
  cliError(`spec file not found: ${specPath}`, 4, { name: "E_NOT_FOUND" });
35423
35458
  process.exitCode = 4;
35424
35459
  return;
35425
35460
  }
35426
- const specContent = readFileSync19(specPath, "utf-8");
35461
+ const specContent = readFileSync18(specPath, "utf-8");
35427
35462
  const projectRoot = getProjectRoot23();
35428
35463
  const outputDir = args["output-dir"] ? resolve9(args["output-dir"]) : join33(projectRoot, ".cleo", "cant", "agents");
35429
35464
  mkdirSync7(outputDir, { recursive: true });
@@ -37951,11 +37986,11 @@ var init_caamp = __esm({
37951
37986
  }
37952
37987
  if (args["dry-run"]) {
37953
37988
  const { parseCaampBlocks } = await import("@cleocode/caamp");
37954
- const { existsSync: existsSync18 } = await import("node:fs");
37989
+ const { existsSync: existsSync17 } = await import("node:fs");
37955
37990
  const { readFile: readFile7 } = await import("node:fs/promises");
37956
37991
  const dryResults = [];
37957
37992
  for (const filePath of filePaths) {
37958
- if (!existsSync18(filePath)) {
37993
+ if (!existsSync17(filePath)) {
37959
37994
  dryResults.push({ filePath, exists: false, blockCount: 0, wouldRemove: 0 });
37960
37995
  continue;
37961
37996
  }
@@ -38502,10 +38537,10 @@ var init_check2 = __esm({
38502
38537
  }
38503
38538
  },
38504
38539
  async run({ args }) {
38505
- const { readFileSync: readFileSync19 } = await import("node:fs");
38540
+ const { readFileSync: readFileSync18 } = await import("node:fs");
38506
38541
  let chain;
38507
38542
  try {
38508
- chain = JSON.parse(readFileSync19(args.file, "utf8"));
38543
+ chain = JSON.parse(readFileSync18(args.file, "utf8"));
38509
38544
  } catch (err) {
38510
38545
  const message = err instanceof Error ? err.message : String(err);
38511
38546
  cliError(`Failed to read or parse chain file: ${message}`, 2, {
@@ -44663,8 +44698,8 @@ var init_event = __esm({
44663
44698
  }
44664
44699
  for (const file of filesToTail) {
44665
44700
  const agentId = file.replace(".jsonl", "");
44666
- const { readFileSync: readFileSync19 } = await import("node:fs");
44667
- const content = readFileSync19(join21(eventsDir, file), "utf-8");
44701
+ const { readFileSync: readFileSync18 } = await import("node:fs");
44702
+ const content = readFileSync18(join21(eventsDir, file), "utf-8");
44668
44703
  const eventLines = content.trim().split("\n").filter(Boolean);
44669
44704
  const tail = eventLines.slice(-lines);
44670
44705
  if (jsonMode) {
@@ -44759,8 +44794,8 @@ var init_event = __esm({
44759
44794
  const printAgentLog = async (file) => {
44760
44795
  const agentId = file.replace(".jsonl", "");
44761
44796
  try {
44762
- const { readFileSync: readFileSync19 } = await import("node:fs");
44763
- const content = readFileSync19(join21(eventsDir, file), "utf-8");
44797
+ const { readFileSync: readFileSync18 } = await import("node:fs");
44798
+ const content = readFileSync18(join21(eventsDir, file), "utf-8");
44764
44799
  const eventLines = content.trim().split("\n").filter(Boolean);
44765
44800
  const tail = eventLines.slice(-lines);
44766
44801
  if (jsonMode) {
@@ -59144,6 +59179,12 @@ import {
59144
59179
  import { safeRunProposeTick } from "@cleocode/core/sentient/propose-tick.js";
59145
59180
  import { patchSentientState, readSentientState as readSentientState2 } from "@cleocode/core/sentient/state.js";
59146
59181
  import { safeRunTick } from "@cleocode/core/sentient/tick.js";
59182
+ import {
59183
+ getSkillPatch,
59184
+ getSkillReview,
59185
+ listSkillReviews,
59186
+ markSkillPatchRejected
59187
+ } from "@cleocode/core/store/skills-store.js";
59147
59188
  function resolveProjectRoot6(arg) {
59148
59189
  return arg && arg.length > 0 ? arg : processCwd3();
59149
59190
  }
@@ -59807,29 +59848,7 @@ ${b64List.map((k, i) => ` ${i + 1}. ${k}`).join("\n")}`
59807
59848
  const limit = args.limit ? Number.parseInt(args.limit, 10) : 50;
59808
59849
  const pendingOnly = args.pending === true;
59809
59850
  try {
59810
- const { openSkillsDb } = await import("@cleocode/core/store/skills-db.js");
59811
- const { skills: skills2, skillReviews, skillPatches } = await import("@cleocode/core/store/skills-schema.js");
59812
- const db = await openSkillsDb();
59813
- const reviews = db.select().from(skillReviews).orderBy(skillReviews.reviewedAt).limit(Number.isInteger(limit) && limit > 0 ? limit : 50).all();
59814
- const entries = [];
59815
- for (const review of reviews) {
59816
- const { eq: eq2, desc } = await import("drizzle-orm");
59817
- const skillRow = db.select().from(skills2).where(eq2(skills2.name, review.skillName)).limit(1).all()[0];
59818
- const latestPatch = db.select().from(skillPatches).where(eq2(skillPatches.skillName, review.skillName)).orderBy(desc(skillPatches.proposedAt)).limit(1).all()[0];
59819
- if (pendingOnly && latestPatch?.status !== "proposed") {
59820
- continue;
59821
- }
59822
- entries.push({
59823
- reviewId: review.id,
59824
- skillName: review.skillName,
59825
- reviewedAt: review.reviewedAt,
59826
- outcome: review.outcome,
59827
- summary: review.summary ?? null,
59828
- patchId: latestPatch?.id ?? null,
59829
- patchStatus: latestPatch?.status ?? null,
59830
- isCanonical: skillRow?.sourceType === "canonical"
59831
- });
59832
- }
59851
+ const entries = await listSkillReviews({ pendingOnly, limit });
59833
59852
  emitSuccess2(
59834
59853
  { entries, count: entries.length, pendingOnly },
59835
59854
  jsonMode,
@@ -59872,12 +59891,8 @@ ${b64List.map((k, i) => ` ${i + 1}. ${k}`).join("\n")}`
59872
59891
  return;
59873
59892
  }
59874
59893
  try {
59875
- const { openSkillsDb } = await import("@cleocode/core/store/skills-db.js");
59876
- const { skills: skills2, skillReviews, skillPatches } = await import("@cleocode/core/store/skills-schema.js");
59877
- const { eq: eq2 } = await import("drizzle-orm");
59878
- const db = await openSkillsDb();
59879
- const review = db.select().from(skillReviews).where(eq2(skillReviews.id, reviewId)).get();
59880
- if (!review) {
59894
+ const detail = await getSkillReview(reviewId);
59895
+ if (!detail.review) {
59881
59896
  emitFailure3(
59882
59897
  "E_NOT_FOUND",
59883
59898
  `No skill_reviews row for id=${reviewId}`,
@@ -59886,17 +59901,15 @@ ${b64List.map((k, i) => ` ${i + 1}. ${k}`).join("\n")}`
59886
59901
  );
59887
59902
  return;
59888
59903
  }
59889
- const skillRow = db.select().from(skills2).where(eq2(skills2.name, review.skillName)).limit(1).all()[0];
59890
- const patches = db.select().from(skillPatches).where(eq2(skillPatches.reviewId, reviewId)).all();
59891
59904
  emitSuccess2(
59892
59905
  {
59893
- review,
59894
- patches,
59895
- isCanonical: skillRow?.sourceType === "canonical",
59896
- skillRow: skillRow ?? null
59906
+ review: detail.review,
59907
+ patches: detail.patches,
59908
+ isCanonical: detail.isCanonical,
59909
+ skillRow: detail.skillRow
59897
59910
  },
59898
59911
  jsonMode,
59899
- `Review ${reviewId} for ${review.skillName}: ${review.outcome}`,
59912
+ `Review ${reviewId} for ${detail.review.skillName}: ${detail.review.outcome}`,
59900
59913
  "sentient.review-status.show"
59901
59914
  );
59902
59915
  } catch (err) {
@@ -59935,11 +59948,8 @@ ${b64List.map((k, i) => ` ${i + 1}. ${k}`).join("\n")}`
59935
59948
  return;
59936
59949
  }
59937
59950
  try {
59938
- const { openSkillsDb } = await import("@cleocode/core/store/skills-db.js");
59939
- const { skills: skills2, skillPatches } = await import("@cleocode/core/store/skills-schema.js");
59940
- const { eq: eq2 } = await import("drizzle-orm");
59941
- const db = await openSkillsDb();
59942
- const patch = db.select().from(skillPatches).where(eq2(skillPatches.id, patchId)).get();
59951
+ const detail = await getSkillPatch(patchId);
59952
+ const patch = detail.patch;
59943
59953
  if (!patch) {
59944
59954
  emitFailure3(
59945
59955
  "E_NOT_FOUND",
@@ -59958,7 +59968,7 @@ ${b64List.map((k, i) => ` ${i + 1}. ${k}`).join("\n")}`
59958
59968
  );
59959
59969
  return;
59960
59970
  }
59961
- const skillRow = db.select().from(skills2).where(eq2(skills2.name, patch.skillName)).limit(1).all()[0];
59971
+ const skillRow = detail.skillRow;
59962
59972
  if (!skillRow) {
59963
59973
  emitFailure3(
59964
59974
  "E_NOT_FOUND",
@@ -60036,12 +60046,8 @@ ${b64List.map((k, i) => ` ${i + 1}. ${k}`).join("\n")}`
60036
60046
  return;
60037
60047
  }
60038
60048
  try {
60039
- const { openSkillsDb } = await import("@cleocode/core/store/skills-db.js");
60040
- const { skillPatches } = await import("@cleocode/core/store/skills-schema.js");
60041
- const { eq: eq2 } = await import("drizzle-orm");
60042
- const db = await openSkillsDb();
60043
- const patch = db.select().from(skillPatches).where(eq2(skillPatches.id, patchId)).get();
60044
- if (!patch) {
60049
+ const detail = await getSkillPatch(patchId);
60050
+ if (!detail.patch) {
60045
60051
  emitFailure3(
60046
60052
  "E_NOT_FOUND",
60047
60053
  `No skill_patches row for id=${patchId}`,
@@ -60050,19 +60056,19 @@ ${b64List.map((k, i) => ` ${i + 1}. ${k}`).join("\n")}`
60050
60056
  );
60051
60057
  return;
60052
60058
  }
60053
- if (patch.status !== "proposed") {
60059
+ if (detail.patch.status !== "proposed") {
60054
60060
  emitFailure3(
60055
60061
  "E_VALIDATION",
60056
- `patch ${patchId} is not in 'proposed' state (status='${patch.status}')`,
60062
+ `patch ${patchId} is not in 'proposed' state (status='${detail.patch.status}')`,
60057
60063
  jsonMode,
60058
60064
  "sentient.review-status.reject"
60059
60065
  );
60060
60066
  return;
60061
60067
  }
60062
- db.update(skillPatches).set({ status: "rejected" }).where(eq2(skillPatches.id, patchId)).run();
60068
+ await markSkillPatchRejected(patchId);
60063
60069
  const reason = args.reason ?? "rejected by operator";
60064
60070
  emitSuccess2(
60065
- { patchId, skillName: patch.skillName, status: "rejected", reason },
60071
+ { patchId, skillName: detail.patch.skillName, status: "rejected", reason },
60066
60072
  jsonMode,
60067
60073
  `Patch ${patchId} rejected (${reason})`,
60068
60074
  "sentient.review-status.reject"
@@ -61490,12 +61496,12 @@ var skills_exports = {};
61490
61496
  __export(skills_exports, {
61491
61497
  skillsCommand: () => skillsCommand2
61492
61498
  });
61493
- import { execFileSync as execFileSync4 } from "node:child_process";
61494
- import { existsSync as existsSync16, readFileSync as readFileSync16 } from "node:fs";
61495
- import { resolve as resolvePath2 } from "node:path";
61496
- import { cwd as processCwd4 } from "node:process";
61497
- import { AgentsSkillsRealDirError, runDoctorAdopt, runDoctorBridge } from "@cleocode/caamp";
61498
- import { withProvenance } from "@cleocode/core/sentient";
61499
+ import {
61500
+ AgentsSkillsRealDirError,
61501
+ skills as coreSkills,
61502
+ runDoctorAdopt,
61503
+ runDoctorBridge
61504
+ } from "@cleocode/core";
61499
61505
  function buildSkillsDoctorAdoptAdapters() {
61500
61506
  return {
61501
61507
  loadRegisteredNames: async () => {
@@ -61522,7 +61528,7 @@ function buildSkillsDoctorAdoptAdapters() {
61522
61528
  }
61523
61529
  };
61524
61530
  }
61525
- var listCommand23, searchCommand4, findCommand7, validateCommand8, infoCommand, installCommand3, uninstallCommand2, enableCommand2, disableCommand2, refreshCommand, dispatchCommand, catalogCommand, precedenceCommand, depsCommand4, doctorBridgeCommand, doctorAdoptOrphansCommand, statsCommand6, importHermesCommand, pruneTelemetryCommand, spawnProvidersCommand, doctorDiagnoseCommand, doctorCommand4, proposePatchCommand, skillsCommand2;
61531
+ var listCommand23, searchCommand4, findCommand7, validateCommand8, infoCommand, installCommand3, uninstallCommand2, enableCommand2, disableCommand2, refreshCommand, dispatchCommand, catalogCommand, precedenceCommand, depsCommand4, doctorBridgeCommand, doctorAdoptOrphansCommand, statsCommand6, importHermesCommand, migrateCommand2, pruneTelemetryCommand, spawnProvidersCommand, doctorDiagnoseCommand, doctorCommand4, proposePatchCommand, skillsCommand2;
61526
61532
  var init_skills2 = __esm({
61527
61533
  "packages/cleo/src/cli/commands/skills.ts"() {
61528
61534
  "use strict";
@@ -62034,6 +62040,42 @@ var init_skills2 = __esm({
62034
62040
  );
62035
62041
  }
62036
62042
  });
62043
+ migrateCommand2 = defineCommand({
62044
+ meta: {
62045
+ name: "migrate",
62046
+ description: "Migrate legacy skills (~/.local/share/agents/skills) into ~/.cleo/skills (SSoT)"
62047
+ },
62048
+ args: {
62049
+ "dry-run": {
62050
+ type: "boolean",
62051
+ description: "Preview the plan without writing anything"
62052
+ },
62053
+ rollback: {
62054
+ type: "boolean",
62055
+ description: "Restore from the most recent backup tarball"
62056
+ },
62057
+ json: {
62058
+ type: "boolean",
62059
+ description: "Emit LAFS JSON envelope (default)"
62060
+ },
62061
+ human: {
62062
+ type: "boolean",
62063
+ description: "Emit a coloured human-readable summary instead of JSON"
62064
+ }
62065
+ },
62066
+ async run({ args }) {
62067
+ await dispatchFromCli(
62068
+ "mutate",
62069
+ "tools",
62070
+ "skill.migrate",
62071
+ {
62072
+ dryRun: args["dry-run"] === true,
62073
+ rollback: args.rollback === true
62074
+ },
62075
+ { command: "skills", operation: "tools.skill.migrate" }
62076
+ );
62077
+ }
62078
+ });
62037
62079
  pruneTelemetryCommand = defineCommand({
62038
62080
  meta: {
62039
62081
  name: "prune-telemetry",
@@ -62168,119 +62210,60 @@ var init_skills2 = __esm({
62168
62210
  async run({ args }) {
62169
62211
  const skillName = String(args["skill-name"]);
62170
62212
  const diffPath = String(args.diff);
62171
- const title = typeof args.title === "string" && args.title.length > 0 ? args.title : `skill(${skillName}): proposed patch`;
62172
- const body = typeof args.body === "string" && args.body.length > 0 ? args.body : [
62173
- `Auto-improve patch for canonical skill **${skillName}**.`,
62174
- "",
62175
- `Diff source: \`${diffPath}\``,
62176
- "",
62177
- "This PR was opened via `cleo skill propose-patch` (T9714).",
62178
- "Sphere A canonical skills are owner-CI-only \u2014 the local",
62179
- "sentient daemon CANNOT mutate them in place; this PR is the",
62180
- "audited path for incorporating a council-approved patch."
62181
- ].join("\n");
62182
- const base = typeof args.base === "string" && args.base.length > 0 ? args.base : "main";
62213
+ const title = typeof args.title === "string" ? args.title : void 0;
62214
+ const body = typeof args.body === "string" ? args.body : void 0;
62215
+ const base = typeof args.base === "string" ? args.base : void 0;
62183
62216
  const dryRun = args["dry-run"] === true;
62184
- const jsonMode = args.json === true;
62185
- const resolvedDiff = resolvePath2(processCwd4(), diffPath);
62186
- if (!existsSync16(resolvedDiff)) {
62187
- cliError(
62188
- `Diff file not found at '${resolvedDiff}'`,
62189
- "E_NOT_FOUND",
62190
- { name: "E_NOT_FOUND" },
62191
- { operation: "tools.skill.propose-patch" }
62192
- );
62193
- process.exit(1);
62194
- return;
62195
- }
62196
- const diffBytes = readFileSync16(resolvedDiff, "utf8");
62197
- if (diffBytes.length === 0) {
62217
+ void (args.json === true);
62218
+ const result = await coreSkills.proposeCanonicalPatch({
62219
+ skillName,
62220
+ diffPath,
62221
+ title,
62222
+ body,
62223
+ base,
62224
+ dryRun
62225
+ });
62226
+ if (result.kind === "error") {
62198
62227
  cliError(
62199
- `Diff file '${resolvedDiff}' is empty`,
62200
- "E_PATCH_EMPTY",
62201
- { name: "E_PATCH_EMPTY" },
62228
+ result.message,
62229
+ result.code,
62230
+ { name: result.code },
62202
62231
  { operation: "tools.skill.propose-patch" }
62203
62232
  );
62204
62233
  process.exit(1);
62205
62234
  return;
62206
62235
  }
62207
- if (!dryRun) {
62208
- try {
62209
- execFileSync4("gh", ["--version"], { stdio: "pipe" });
62210
- } catch {
62211
- cliError(
62212
- "gh CLI not found or not authenticated \u2014 install gh and run `gh auth login`",
62213
- "E_GH_UNAVAILABLE",
62214
- { name: "E_GH_UNAVAILABLE" },
62215
- { operation: "tools.skill.propose-patch" }
62216
- );
62217
- process.exit(1);
62218
- return;
62219
- }
62220
- }
62221
- const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
62222
- const branchName = `propose-patch/skill-${skillName}-${timestamp2}`;
62223
- const steps = [
62224
- `git checkout -b ${branchName}`,
62225
- `git apply ${resolvedDiff}`,
62226
- `git add -A`,
62227
- `git commit -m "skill(${skillName}): propose patch"`,
62228
- `git push -u origin ${branchName}`,
62229
- `gh pr create --base ${base} --head ${branchName} --title "${title}" --body <stdin>`
62230
- ];
62231
- if (dryRun) {
62236
+ if (result.kind === "dry-run") {
62232
62237
  cliOutput(
62233
62238
  {
62234
- skillName,
62235
- diffPath: resolvedDiff,
62236
- branchName,
62237
- base,
62238
- steps,
62239
+ skillName: result.skillName,
62240
+ diffPath: result.diffPath,
62241
+ branchName: result.branchName,
62242
+ base: result.base,
62243
+ steps: result.steps,
62239
62244
  dryRun: true
62240
62245
  },
62241
62246
  {
62242
62247
  command: "skills",
62243
- message: `[dry-run] would open PR for ${skillName} on branch ${branchName}`,
62248
+ message: `[dry-run] would open PR for ${result.skillName} on branch ${result.branchName}`,
62244
62249
  operation: "tools.skill.propose-patch"
62245
62250
  }
62246
62251
  );
62247
62252
  return;
62248
62253
  }
62249
- try {
62250
- const result = await withProvenance("pr-generator", async () => {
62251
- execFileSync4("git", ["checkout", "-b", branchName], { stdio: "pipe" });
62252
- execFileSync4("git", ["apply", resolvedDiff], { stdio: "pipe" });
62253
- execFileSync4("git", ["add", "-A"], { stdio: "pipe" });
62254
- execFileSync4("git", ["commit", "-m", `skill(${skillName}): propose patch`], {
62255
- stdio: "pipe"
62256
- });
62257
- execFileSync4("git", ["push", "-u", "origin", branchName], { stdio: "pipe" });
62258
- const prUrl = execFileSync4(
62259
- "gh",
62260
- ["pr", "create", "--base", base, "--head", branchName, "--title", title, "--body", body],
62261
- { stdio: "pipe" }
62262
- ).toString("utf8").trim();
62263
- return { prUrl, branchName };
62264
- });
62265
- cliOutput(
62266
- { skillName, prUrl: result.prUrl, branchName: result.branchName, base },
62267
- {
62268
- command: "skills",
62269
- message: `Opened PR ${result.prUrl}`,
62270
- operation: "tools.skill.propose-patch"
62271
- }
62272
- );
62273
- } catch (err) {
62274
- const message = err instanceof Error ? err.message : String(err);
62275
- cliError(
62276
- `propose-patch failed: ${message}`,
62277
- "E_PROPOSE_PATCH_FAILED",
62278
- { name: "E_PROPOSE_PATCH_FAILED" },
62279
- { operation: "tools.skill.propose-patch" }
62280
- );
62281
- process.exit(1);
62282
- }
62283
- void jsonMode;
62254
+ cliOutput(
62255
+ {
62256
+ skillName: result.skillName,
62257
+ prUrl: result.prUrl,
62258
+ branchName: result.branchName,
62259
+ base: result.base
62260
+ },
62261
+ {
62262
+ command: "skills",
62263
+ message: `Opened PR ${result.prUrl}`,
62264
+ operation: "tools.skill.propose-patch"
62265
+ }
62266
+ );
62284
62267
  }
62285
62268
  });
62286
62269
  skillsCommand2 = defineCommand({
@@ -62305,6 +62288,7 @@ var init_skills2 = __esm({
62305
62288
  "propose-patch": proposePatchCommand,
62306
62289
  stats: statsCommand6,
62307
62290
  "import-hermes": importHermesCommand,
62291
+ migrate: migrateCommand2,
62308
62292
  "prune-telemetry": pruneTelemetryCommand
62309
62293
  },
62310
62294
  async run({ cmd, rawArgs }) {
@@ -63085,10 +63069,10 @@ var init_sync = __esm({
63085
63069
  }
63086
63070
  },
63087
63071
  async run({ args }) {
63088
- const { readFileSync: readFileSync19 } = await import("node:fs");
63072
+ const { readFileSync: readFileSync18 } = await import("node:fs");
63089
63073
  let externalTasks;
63090
63074
  try {
63091
- externalTasks = JSON.parse(readFileSync19(args.file, "utf8"));
63075
+ externalTasks = JSON.parse(readFileSync18(args.file, "utf8"));
63092
63076
  } catch (err) {
63093
63077
  const message = err instanceof Error ? err.message : String(err);
63094
63078
  cliError(`Failed to read or parse external tasks file: ${message}`, 2, {
@@ -63474,12 +63458,12 @@ var token_exports = {};
63474
63458
  __export(token_exports, {
63475
63459
  tokenCommand: () => tokenCommand
63476
63460
  });
63477
- import { readFileSync as readFileSync17 } from "node:fs";
63461
+ import { readFileSync as readFileSync16 } from "node:fs";
63478
63462
  import { getProjectRoot as getProjectRoot40, measureTokenExchange, recordTokenExchange as recordTokenExchange2 } from "@cleocode/core/internal";
63479
63463
  function readPayload(args, textKey, fileKey) {
63480
63464
  const text = args[textKey];
63481
63465
  const file = args[fileKey];
63482
- if (file) return readFileSync17(file, "utf-8");
63466
+ if (file) return readFileSync16(file, "utf-8");
63483
63467
  return text;
63484
63468
  }
63485
63469
  var filterArgs, summaryCommand3, listCommand25, showCommand16, deleteCommand3, clearCommand2, estimateCommand, tokenCommand;
@@ -63681,7 +63665,7 @@ import {
63681
63665
  pruneTranscripts,
63682
63666
  scanTranscripts
63683
63667
  } from "@cleocode/core/gc/transcript.js";
63684
- var scanCommand, extractCommand, migrateCommand2, pruneCommand2, transcriptCommand;
63668
+ var scanCommand, extractCommand, migrateCommand3, pruneCommand2, transcriptCommand;
63685
63669
  var init_transcript = __esm({
63686
63670
  "packages/cleo/src/cli/commands/transcript.ts"() {
63687
63671
  "use strict";
@@ -63880,7 +63864,7 @@ var init_transcript = __esm({
63880
63864
  }
63881
63865
  }
63882
63866
  });
63883
- migrateCommand2 = defineCommand({
63867
+ migrateCommand3 = defineCommand({
63884
63868
  meta: {
63885
63869
  name: "migrate",
63886
63870
  description: "Backfill extraction from existing ~/.claude/projects/ session JSONLs (T733)"
@@ -64076,7 +64060,7 @@ var init_transcript = __esm({
64076
64060
  subCommands: {
64077
64061
  scan: scanCommand,
64078
64062
  extract: extractCommand,
64079
- migrate: migrateCommand2,
64063
+ migrate: migrateCommand3,
64080
64064
  prune: pruneCommand2
64081
64065
  },
64082
64066
  async run({ cmd, rawArgs }) {
@@ -64697,7 +64681,7 @@ var web_exports = {};
64697
64681
  __export(web_exports, {
64698
64682
  webCommand: () => webCommand
64699
64683
  });
64700
- import { execFileSync as execFileSync5, spawn as spawn3 } from "node:child_process";
64684
+ import { execFileSync as execFileSync4, spawn as spawn3 } from "node:child_process";
64701
64685
  import { mkdir as mkdir4, open, readFile as readFile6, rm, stat as stat2, writeFile as writeFile3 } from "node:fs/promises";
64702
64686
  import { join as join30 } from "node:path";
64703
64687
  import { CleoError as CleoError12, formatError as formatError9, getCleoHome as getCleoHome5 } from "@cleocode/core";
@@ -64761,7 +64745,7 @@ async function startWebServer(port, host) {
64761
64745
  await stat2(webIndexPath);
64762
64746
  } catch {
64763
64747
  try {
64764
- execFileSync5("pnpm", ["--filter", "@cleocode/studio", "run", "build"], {
64748
+ execFileSync4("pnpm", ["--filter", "@cleocode/studio", "run", "build"], {
64765
64749
  cwd: projectRoot,
64766
64750
  stdio: "ignore"
64767
64751
  });
@@ -65262,7 +65246,7 @@ var init_worktree3 = __esm({
65262
65246
  init_dist();
65263
65247
  init_field_context();
65264
65248
  init_format_context();
65265
- import { readFileSync as readFileSync18 } from "node:fs";
65249
+ import { readFileSync as readFileSync17 } from "node:fs";
65266
65250
  import { dirname as dirname12, join as join32 } from "node:path";
65267
65251
  import { fileURLToPath as fileURLToPath8 } from "node:url";
65268
65252
 
@@ -66388,14 +66372,14 @@ function didYouMean(input2, candidates, maxDistance = 2) {
66388
66372
  }
66389
66373
 
66390
66374
  // packages/cleo/src/cli/lib/first-run-detection.ts
66391
- import { existsSync as existsSync17 } from "node:fs";
66375
+ import { existsSync as existsSync16 } from "node:fs";
66392
66376
  import { join as join31 } from "node:path";
66393
66377
  async function detectFirstRun() {
66394
66378
  const envKey = process.env["ANTHROPIC_API_KEY"];
66395
66379
  if (typeof envKey === "string" && envKey.length > 0) return false;
66396
66380
  const { getCleoPlatformPaths } = await import("@cleocode/paths");
66397
66381
  const configPath = join31(getCleoPlatformPaths().config, "config.json");
66398
- if (existsSync17(configPath)) return false;
66382
+ if (existsSync16(configPath)) return false;
66399
66383
  try {
66400
66384
  const { getCredentialPool } = await import("@cleocode/core/llm/credential-pool.js");
66401
66385
  const pool = getCredentialPool();
@@ -66484,7 +66468,7 @@ Or via NodeSource: https://github.com/nodesource/distributions
66484
66468
  }
66485
66469
  function getPackageVersion() {
66486
66470
  const pkgPath = join32(dirname12(fileURLToPath8(import.meta.url)), "../../package.json");
66487
- const pkg = JSON.parse(readFileSync18(pkgPath, "utf-8"));
66471
+ const pkg = JSON.parse(readFileSync17(pkgPath, "utf-8"));
66488
66472
  return pkg.version;
66489
66473
  }
66490
66474
  var CLI_VERSION = getPackageVersion();