@meistrari/tela-cli 1.10.0 → 1.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -196,22 +196,24 @@ async function upgradeSkills(args, options, io) {
196
196
  getClient: async () => (await (0, session_1.loadPantry)(flags)).client,
197
197
  onDiagnostic: diagnostic => writeSkillManagerDiagnostic(io, diagnostic),
198
198
  });
199
- const { upgraded, unchanged } = await manager.upgradeSkills(flags.rest, {
199
+ const { upgraded, unchanged, replaced } = await manager.upgradeSkills(flags.rest, {
200
200
  force: flags.force,
201
201
  ignoreScripts: flags.ignoreScripts,
202
202
  ...(flags.symlink ? {} : { targets: [] }),
203
203
  });
204
- if (upgraded.length === 0 && unchanged.length === 0) {
204
+ if (upgraded.length === 0 && unchanged.length === 0 && replaced.length === 0) {
205
205
  if (options.json)
206
- (0, output_1.writeJson)(io, { upgraded: [], unchanged: [] });
206
+ (0, output_1.writeJson)(io, { upgraded: [], unchanged: [], replaced: [] });
207
207
  else
208
208
  (0, output_1.writeLine)(io, 'No skills installed.');
209
209
  return 0;
210
210
  }
211
211
  if (options.json) {
212
- (0, output_1.writeJson)(io, { upgraded, unchanged });
212
+ (0, output_1.writeJson)(io, { upgraded, unchanged, replaced });
213
213
  return 0;
214
214
  }
215
+ for (const { from, to } of replaced)
216
+ (0, output_1.writeLine)(io, `Replaced deprecated ${from} with ${to.slug} at ${to.sha.slice(0, 7)}`);
215
217
  for (const entry of upgraded)
216
218
  (0, output_1.writeLine)(io, `Upgraded ${entry.slug} to ${entry.sha.slice(0, 7)}`);
217
219
  for (const slug of unchanged)
package/dist/index.js CHANGED
@@ -77216,6 +77216,7 @@ var require_skills = __commonJS({
77216
77216
  name: ingredient.name,
77217
77217
  description: ingredient.description,
77218
77218
  status: ingredient.status,
77219
+ replacement: ingredient.replacement,
77219
77220
  // A listing carries the skill-only fields under `skillDetails`,
77220
77221
  // which is null for anything Pantry has not synced as a skill.
77221
77222
  bundle: ingredient.skillDetails?.bundle ?? null,
@@ -77338,13 +77339,42 @@ Run \`tela skills install ${slug} --force\` to replace it, or remove it by hand.
77338
77339
  const diagnostics = [];
77339
77340
  const upgraded = [];
77340
77341
  const unchanged = [];
77342
+ const replaced = [];
77341
77343
  const emit = createDiagnosticEmitter(options2, diagnostics);
77342
77344
  if (selected.length === 0)
77343
- return { upgraded, unchanged, diagnostics };
77345
+ return { upgraded, unchanged, replaced, diagnostics };
77344
77346
  const targets = normalizeTargets(upgradeOptions.targets ?? paths.targets, paths.installRoot);
77345
77347
  const client = await resolveClient();
77346
77348
  for (const current of selected) {
77347
- const { snapshot } = await (0, skills_1.fetchSkillFile)(client, current.slug);
77349
+ const { ingredient, snapshot } = await (0, skills_1.fetchSkillFile)(client, current.slug);
77350
+ const replacementSlug = await resolveReplacementSlug(client, current.slug, ingredient);
77351
+ if (replacementSlug) {
77352
+ const installedReplacement = (await (0, tela_local_config_1.listInstalledSkills)(inventoryFile)).find((entry2) => entry2.slug === replacementSlug);
77353
+ const replacement = await installOne({
77354
+ client,
77355
+ diagnostics: emit,
77356
+ ...installedReplacement ? { expectedCurrent: installedReplacement, operation: "upgrade" } : {},
77357
+ force: upgradeOptions.force ?? false,
77358
+ ignoreScripts: upgradeOptions.ignoreScripts ?? false,
77359
+ inventoryFile,
77360
+ paths,
77361
+ slug: replacementSlug,
77362
+ targets
77363
+ });
77364
+ if (!replacement)
77365
+ continue;
77366
+ if (await uninstallOne({
77367
+ diagnostics: emit,
77368
+ entry: current,
77369
+ ignoreScripts: upgradeOptions.ignoreScripts ?? false,
77370
+ inventoryFile,
77371
+ operation: "upgrade",
77372
+ paths
77373
+ })) {
77374
+ replaced.push({ from: current.slug, to: replacement });
77375
+ }
77376
+ continue;
77377
+ }
77348
77378
  if (snapshot.sha === current.sha) {
77349
77379
  unchanged.push(current.slug);
77350
77380
  continue;
@@ -77364,7 +77394,7 @@ Run \`tela skills install ${slug} --force\` to replace it, or remove it by hand.
77364
77394
  if (entry)
77365
77395
  upgraded.push(entry);
77366
77396
  }
77367
- return { upgraded, unchanged, diagnostics };
77397
+ return { upgraded, unchanged, replaced, diagnostics };
77368
77398
  },
77369
77399
  uninstallSkills: async (slugs, uninstallOptions = {}) => {
77370
77400
  for (const slug of slugs)
@@ -77375,44 +77405,77 @@ Run \`tela skills install ${slug} --force\` to replace it, or remove it by hand.
77375
77405
  const removed = [];
77376
77406
  const emit = createDiagnosticEmitter(options2, diagnostics);
77377
77407
  for (const entry of selected) {
77378
- assertManagedInstallDir(entry.slug, entry, paths);
77379
- await runLifecycle(entry, "uninstall", uninstallOptions.ignoreScripts ?? false, emit);
77380
- let gone = false;
77381
- await (0, tela_local_config_1.updateInstalledSkill)(entry.slug, async (registered) => {
77382
- if (registered === null)
77383
- return null;
77384
- if (!isSameInstall(registered, entry)) {
77385
- emit({
77386
- type: "concurrent-change",
77387
- slug: entry.slug,
77388
- operation: "uninstall",
77389
- message: `Skipped ${entry.slug}: it was reinstalled while this uninstall ran`
77390
- });
77391
- return registered;
77392
- }
77393
- assertManagedInstallDir(entry.slug, registered, paths);
77394
- for (const linkPath of registered.symlinks) {
77395
- const outcome = await (0, skill_filesystem_1.removeManagedSymlink)(linkPath, registered.dir);
77396
- if (outcome.reason) {
77397
- emit({
77398
- type: "link-removal-skipped",
77399
- slug: entry.slug,
77400
- path: linkPath,
77401
- message: outcome.reason
77402
- });
77403
- }
77404
- }
77405
- await (0, promises_1.rm)(registered.dir, { recursive: true, force: true });
77406
- gone = true;
77407
- return null;
77408
- }, inventoryFile);
77409
- if (gone)
77408
+ if (await uninstallOne({
77409
+ diagnostics: emit,
77410
+ entry,
77411
+ ignoreScripts: uninstallOptions.ignoreScripts ?? false,
77412
+ inventoryFile,
77413
+ operation: "uninstall",
77414
+ paths
77415
+ })) {
77410
77416
  removed.push(entry.slug);
77417
+ }
77411
77418
  }
77412
77419
  return { removed, diagnostics };
77413
77420
  }
77414
77421
  };
77415
77422
  }
77423
+ var MAX_REPLACEMENT_HOPS = 5;
77424
+ async function resolveReplacementSlug(client, slug, ingredient) {
77425
+ let replacement = deprecatedReplacement(ingredient);
77426
+ if (!replacement)
77427
+ return null;
77428
+ const visited = /* @__PURE__ */ new Set([slug]);
77429
+ for (let hop = 0; hop < MAX_REPLACEMENT_HOPS; hop++) {
77430
+ (0, skill_filesystem_1.assertSafeSlug)(replacement, "upgrade");
77431
+ if (visited.has(replacement))
77432
+ throw new Error(`Pantry replacements for ${slug} form a cycle through ${replacement}.`);
77433
+ visited.add(replacement);
77434
+ const next = deprecatedReplacement((await (0, skills_1.fetchSkillFile)(client, replacement)).ingredient);
77435
+ if (!next)
77436
+ return replacement;
77437
+ replacement = next;
77438
+ }
77439
+ throw new Error(`Pantry replacements for ${slug} chain through more than ${MAX_REPLACEMENT_HOPS} deprecated skills.`);
77440
+ }
77441
+ function deprecatedReplacement(ingredient) {
77442
+ return ingredient.status === "deprecated" && ingredient.replacement ? ingredient.replacement : null;
77443
+ }
77444
+ async function uninstallOne(options2) {
77445
+ const { entry, paths } = options2;
77446
+ assertManagedInstallDir(entry.slug, entry, paths);
77447
+ await runLifecycle(entry, "uninstall", options2.ignoreScripts, options2.diagnostics);
77448
+ let gone = false;
77449
+ await (0, tela_local_config_1.updateInstalledSkill)(entry.slug, async (registered) => {
77450
+ if (registered === null)
77451
+ return null;
77452
+ if (!isSameInstall(registered, entry)) {
77453
+ options2.diagnostics({
77454
+ type: "concurrent-change",
77455
+ slug: entry.slug,
77456
+ operation: options2.operation,
77457
+ message: `Skipped ${entry.slug}: it was reinstalled while this ${options2.operation} ran`
77458
+ });
77459
+ return registered;
77460
+ }
77461
+ assertManagedInstallDir(entry.slug, registered, paths);
77462
+ for (const linkPath of registered.symlinks) {
77463
+ const outcome = await (0, skill_filesystem_1.removeManagedSymlink)(linkPath, registered.dir);
77464
+ if (outcome.reason) {
77465
+ options2.diagnostics({
77466
+ type: "link-removal-skipped",
77467
+ slug: entry.slug,
77468
+ path: linkPath,
77469
+ message: outcome.reason
77470
+ });
77471
+ }
77472
+ }
77473
+ await (0, promises_1.rm)(registered.dir, { recursive: true, force: true });
77474
+ gone = true;
77475
+ return null;
77476
+ }, options2.inventoryFile);
77477
+ return gone;
77478
+ }
77416
77479
  function createDiagnosticEmitter(options2, diagnostics) {
77417
77480
  return (diagnostic) => {
77418
77481
  diagnostics.push(diagnostic);
@@ -389037,22 +389100,24 @@ async function upgradeSkills(args, options2, io) {
389037
389100
  getClient: async () => (await loadPantry(flags)).client,
389038
389101
  onDiagnostic: (diagnostic) => writeSkillManagerDiagnostic(io, diagnostic)
389039
389102
  });
389040
- const { upgraded, unchanged } = await manager.upgradeSkills(flags.rest, {
389103
+ const { upgraded, unchanged, replaced } = await manager.upgradeSkills(flags.rest, {
389041
389104
  force: flags.force,
389042
389105
  ignoreScripts: flags.ignoreScripts,
389043
389106
  ...flags.symlink ? {} : { targets: [] }
389044
389107
  });
389045
- if (upgraded.length === 0 && unchanged.length === 0) {
389108
+ if (upgraded.length === 0 && unchanged.length === 0 && replaced.length === 0) {
389046
389109
  if (options2.json)
389047
- writeJson(io, { upgraded: [], unchanged: [] });
389110
+ writeJson(io, { upgraded: [], unchanged: [], replaced: [] });
389048
389111
  else
389049
389112
  writeLine(io, "No skills installed.");
389050
389113
  return 0;
389051
389114
  }
389052
389115
  if (options2.json) {
389053
- writeJson(io, { upgraded, unchanged });
389116
+ writeJson(io, { upgraded, unchanged, replaced });
389054
389117
  return 0;
389055
389118
  }
389119
+ for (const { from, to } of replaced)
389120
+ writeLine(io, `Replaced deprecated ${from} with ${to.slug} at ${to.sha.slice(0, 7)}`);
389056
389121
  for (const entry of upgraded)
389057
389122
  writeLine(io, `Upgraded ${entry.slug} to ${entry.sha.slice(0, 7)}`);
389058
389123
  for (const slug of unchanged)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/tela-cli",
3
- "version": "1.10.0",
3
+ "version": "1.10.1",
4
4
  "description": "Tela command line tools for local state and agent workflows.",
5
5
  "engines": {
6
6
  "node": ">=22"