@meistrari/tela-cli 1.11.0 → 1.12.0
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/commands/skills.js +9 -3
- package/dist/index.js +207 -57
- package/package.json +1 -1
package/dist/commands/skills.js
CHANGED
|
@@ -177,15 +177,21 @@ async function installSkills(args, options, io) {
|
|
|
177
177
|
ignoreScripts: flags.ignoreScripts,
|
|
178
178
|
...(flags.symlink ? {} : { targets: [] }),
|
|
179
179
|
});
|
|
180
|
+
// A failed install script rolls back only its own skill, so report the rest and still fail.
|
|
181
|
+
const installedSlugs = new Set(installed.map(entry => entry.slug));
|
|
182
|
+
const failed = flags.rest.filter(slug => !installedSlugs.has(slug));
|
|
180
183
|
if (options.json) {
|
|
181
|
-
(0, output_1.writeJson)(io, { installed });
|
|
182
|
-
return 0;
|
|
184
|
+
(0, output_1.writeJson)(io, failed.length > 0 ? { installed, failed } : { installed });
|
|
185
|
+
return failed.length > 0 ? 1 : 0;
|
|
183
186
|
}
|
|
184
187
|
for (const entry of installed)
|
|
185
188
|
(0, output_1.writeLine)(io, `Installed ${entry.slug}@${entry.sha.slice(0, 7)} to ${entry.dir}`);
|
|
186
|
-
return 0;
|
|
189
|
+
return failed.length > 0 ? 1 : 0;
|
|
187
190
|
}
|
|
188
191
|
function writeSkillManagerDiagnostic(io, diagnostic) {
|
|
192
|
+
// A failed uninstall script throws, and the command prints that error.
|
|
193
|
+
if (diagnostic.type === 'lifecycle-failed' && diagnostic.phase === 'uninstall')
|
|
194
|
+
return;
|
|
189
195
|
(0, output_1.writeError)(io, diagnostic.message);
|
|
190
196
|
}
|
|
191
197
|
async function upgradeSkills(args, options, io) {
|
package/dist/index.js
CHANGED
|
@@ -77236,9 +77236,7 @@ var require_skill_filesystem = __commonJS({
|
|
|
77236
77236
|
var node_os_1 = require("node:os");
|
|
77237
77237
|
var node_path_1 = __importDefault(require("node:path"));
|
|
77238
77238
|
var node_process_1 = __importDefault(require("node:process"));
|
|
77239
|
-
var node_util_1 = require("node:util");
|
|
77240
77239
|
var archive_1 = require_archive();
|
|
77241
|
-
var run = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
77242
77240
|
function resolveDefaultSkillManagerPaths(home = (0, node_os_1.homedir)()) {
|
|
77243
77241
|
return {
|
|
77244
77242
|
installRoot: node_path_1.default.join(home, ".agents", "skills"),
|
|
@@ -77425,14 +77423,72 @@ There is now nothing at ${target}. The previous installation is intact at ${back
|
|
|
77425
77423
|
return { removed: false, reason: `Skipped ${linkPath}: ${describeError(error48)}` };
|
|
77426
77424
|
}
|
|
77427
77425
|
}
|
|
77428
|
-
|
|
77426
|
+
var SCRIPT_KILL_GRACE_MS = 3e3;
|
|
77427
|
+
var SCRIPT_GROUP_POLL_MS = 50;
|
|
77428
|
+
var MAX_SCRIPT_OUTPUT_LENGTH = 64 * 1024;
|
|
77429
|
+
async function runSkillScript(script, cwd, onOutput, signal) {
|
|
77430
|
+
if (signal?.aborted)
|
|
77431
|
+
return { ok: false, cancelled: true, output: "" };
|
|
77432
|
+
return await new Promise((resolve) => {
|
|
77433
|
+
const child = (0, node_child_process_1.spawn)("sh", ["-c", script], { cwd, detached: node_process_1.default.platform !== "win32" });
|
|
77434
|
+
let output = "";
|
|
77435
|
+
let settled = false;
|
|
77436
|
+
let cancelled = false;
|
|
77437
|
+
const append2 = (chunk) => {
|
|
77438
|
+
if (settled)
|
|
77439
|
+
return;
|
|
77440
|
+
const text2 = chunk.toString();
|
|
77441
|
+
output = `${output}${text2}`.slice(-MAX_SCRIPT_OUTPUT_LENGTH);
|
|
77442
|
+
onOutput?.(text2);
|
|
77443
|
+
};
|
|
77444
|
+
const finish = (ok) => {
|
|
77445
|
+
if (settled)
|
|
77446
|
+
return;
|
|
77447
|
+
settled = true;
|
|
77448
|
+
signal?.removeEventListener("abort", cancel2);
|
|
77449
|
+
resolve({ ok: ok && !cancelled, cancelled, output: output.trimEnd() });
|
|
77450
|
+
};
|
|
77451
|
+
function cancel2() {
|
|
77452
|
+
cancelled = true;
|
|
77453
|
+
killProcessTree(child, "SIGTERM");
|
|
77454
|
+
setTimeout(() => killProcessTree(child, "SIGKILL"), SCRIPT_KILL_GRACE_MS).unref();
|
|
77455
|
+
}
|
|
77456
|
+
signal?.addEventListener("abort", cancel2, { once: true });
|
|
77457
|
+
child.stdout.on("data", append2);
|
|
77458
|
+
child.stderr.on("data", append2);
|
|
77459
|
+
child.on("error", (error48) => {
|
|
77460
|
+
append2(`${describeError(error48)}
|
|
77461
|
+
`);
|
|
77462
|
+
finish(false);
|
|
77463
|
+
});
|
|
77464
|
+
child.on("exit", () => {
|
|
77465
|
+
if (cancelled)
|
|
77466
|
+
void waitForProcessGroupExit(child).then(() => finish(false));
|
|
77467
|
+
});
|
|
77468
|
+
child.on("close", (code) => finish(code === 0));
|
|
77469
|
+
});
|
|
77470
|
+
}
|
|
77471
|
+
async function waitForProcessGroupExit(child) {
|
|
77472
|
+
if (child.pid === void 0 || node_process_1.default.platform === "win32")
|
|
77473
|
+
return;
|
|
77474
|
+
while (true) {
|
|
77475
|
+
try {
|
|
77476
|
+
node_process_1.default.kill(-child.pid, 0);
|
|
77477
|
+
} catch {
|
|
77478
|
+
return;
|
|
77479
|
+
}
|
|
77480
|
+
await new Promise((resolve) => setTimeout(resolve, SCRIPT_GROUP_POLL_MS));
|
|
77481
|
+
}
|
|
77482
|
+
}
|
|
77483
|
+
function killProcessTree(child, signal) {
|
|
77484
|
+
if (child.pid === void 0)
|
|
77485
|
+
return;
|
|
77429
77486
|
try {
|
|
77430
|
-
|
|
77431
|
-
|
|
77432
|
-
|
|
77433
|
-
|
|
77434
|
-
|
|
77435
|
-
return { ok: false, output };
|
|
77487
|
+
if (node_process_1.default.platform === "win32")
|
|
77488
|
+
(0, node_child_process_1.spawn)("taskkill", ["/pid", String(child.pid), "/T", "/F"], { stdio: "ignore" });
|
|
77489
|
+
else
|
|
77490
|
+
node_process_1.default.kill(-child.pid, signal);
|
|
77491
|
+
} catch {
|
|
77436
77492
|
}
|
|
77437
77493
|
}
|
|
77438
77494
|
function describeError(error48) {
|
|
@@ -77489,8 +77545,8 @@ var require_skills = __commonJS({
|
|
|
77489
77545
|
...path2 ? { queries: { path: path2 } } : {}
|
|
77490
77546
|
}));
|
|
77491
77547
|
}
|
|
77492
|
-
async function downloadSkillArchive(archiveUrl) {
|
|
77493
|
-
const response = await fetch(archiveUrl);
|
|
77548
|
+
async function downloadSkillArchive(archiveUrl, signal) {
|
|
77549
|
+
const response = await fetch(archiveUrl, { signal });
|
|
77494
77550
|
if (!response.ok) {
|
|
77495
77551
|
(0, http_1.assertHttpOk)(response, await response.text().catch(() => ""), "Failed to download skill archive");
|
|
77496
77552
|
}
|
|
@@ -77507,7 +77563,7 @@ var require_skill_manager = __commonJS({
|
|
|
77507
77563
|
return mod && mod.__esModule ? mod : { "default": mod };
|
|
77508
77564
|
};
|
|
77509
77565
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
77510
|
-
exports2.SkillInstallDirectoryConflictError = exports2.SkillLifecycleError = exports2.resolveDefaultSkillManagerPaths = void 0;
|
|
77566
|
+
exports2.SkillInstallDirectoryConflictError = exports2.SkillInstallCancelledError = exports2.SkillLifecycleError = exports2.resolveDefaultSkillManagerPaths = void 0;
|
|
77511
77567
|
exports2.createSkillManager = createSkillManager2;
|
|
77512
77568
|
var promises_1 = require("node:fs/promises");
|
|
77513
77569
|
var node_path_1 = __importDefault(require("node:path"));
|
|
@@ -77529,10 +77585,22 @@ var require_skill_manager = __commonJS({
|
|
|
77529
77585
|
this.phase = phase;
|
|
77530
77586
|
this.name = "SkillLifecycleError";
|
|
77531
77587
|
this.slug = entry.slug;
|
|
77532
|
-
this.retainedState =
|
|
77588
|
+
this.retainedState = "unchanged";
|
|
77533
77589
|
}
|
|
77534
77590
|
};
|
|
77535
77591
|
exports2.SkillLifecycleError = SkillLifecycleError;
|
|
77592
|
+
var SkillInstallCancelledError = class extends Error {
|
|
77593
|
+
slug;
|
|
77594
|
+
installed;
|
|
77595
|
+
code = "skill-install-cancelled";
|
|
77596
|
+
constructor(slug, installed) {
|
|
77597
|
+
super(`Installation was cancelled before ${slug} finished.`);
|
|
77598
|
+
this.slug = slug;
|
|
77599
|
+
this.installed = installed;
|
|
77600
|
+
this.name = "SkillInstallCancelledError";
|
|
77601
|
+
}
|
|
77602
|
+
};
|
|
77603
|
+
exports2.SkillInstallCancelledError = SkillInstallCancelledError;
|
|
77536
77604
|
var SkillInstallDirectoryConflictError = class extends Error {
|
|
77537
77605
|
code = "skill-install-directory-conflict";
|
|
77538
77606
|
path;
|
|
@@ -77567,17 +77635,31 @@ Run \`tela skills install ${slug} --force\` to replace it, or remove it by hand.
|
|
|
77567
77635
|
const diagnostics = [];
|
|
77568
77636
|
const installed = [];
|
|
77569
77637
|
const emit = createDiagnosticEmitter(options2, diagnostics);
|
|
77638
|
+
const signal = installOptions.signal;
|
|
77570
77639
|
for (const slug of slugs) {
|
|
77571
|
-
|
|
77572
|
-
|
|
77573
|
-
|
|
77574
|
-
|
|
77575
|
-
|
|
77576
|
-
|
|
77577
|
-
|
|
77578
|
-
|
|
77579
|
-
|
|
77580
|
-
|
|
77640
|
+
if (signal?.aborted)
|
|
77641
|
+
throw new SkillInstallCancelledError(slug, installed);
|
|
77642
|
+
let entry;
|
|
77643
|
+
try {
|
|
77644
|
+
entry = await installOne({
|
|
77645
|
+
client,
|
|
77646
|
+
diagnostics: emit,
|
|
77647
|
+
force: installOptions.force ?? false,
|
|
77648
|
+
ignoreScripts: installOptions.ignoreScripts ?? false,
|
|
77649
|
+
inventoryFile,
|
|
77650
|
+
paths,
|
|
77651
|
+
progress: options2.onInstallProgress,
|
|
77652
|
+
signal,
|
|
77653
|
+
slug,
|
|
77654
|
+
targets
|
|
77655
|
+
});
|
|
77656
|
+
} catch (error48) {
|
|
77657
|
+
if (signal?.aborted)
|
|
77658
|
+
throw new SkillInstallCancelledError(slug, installed);
|
|
77659
|
+
if (error48 instanceof SkillLifecycleError && error48.phase === "install")
|
|
77660
|
+
continue;
|
|
77661
|
+
throw error48;
|
|
77662
|
+
}
|
|
77581
77663
|
if (!entry)
|
|
77582
77664
|
throw new Error(`Skill install for ${slug} did not produce an inventory entry.`);
|
|
77583
77665
|
installed.push(entry);
|
|
@@ -77611,6 +77693,7 @@ Run \`tela skills install ${slug} --force\` to replace it, or remove it by hand.
|
|
|
77611
77693
|
ignoreScripts: upgradeOptions.ignoreScripts ?? false,
|
|
77612
77694
|
inventoryFile,
|
|
77613
77695
|
paths,
|
|
77696
|
+
progress: options2.onInstallProgress,
|
|
77614
77697
|
slug: replacementSlug,
|
|
77615
77698
|
targets
|
|
77616
77699
|
});
|
|
@@ -77641,6 +77724,7 @@ Run \`tela skills install ${slug} --force\` to replace it, or remove it by hand.
|
|
|
77641
77724
|
inventoryFile,
|
|
77642
77725
|
operation: "upgrade",
|
|
77643
77726
|
paths,
|
|
77727
|
+
progress: options2.onInstallProgress,
|
|
77644
77728
|
slug: current.slug,
|
|
77645
77729
|
targets
|
|
77646
77730
|
});
|
|
@@ -77739,30 +77823,68 @@ Run \`tela skills install ${slug} --force\` to replace it, or remove it by hand.
|
|
|
77739
77823
|
};
|
|
77740
77824
|
}
|
|
77741
77825
|
async function installOne(options2) {
|
|
77826
|
+
options2.progress?.({
|
|
77827
|
+
type: "phase",
|
|
77828
|
+
slug: options2.slug,
|
|
77829
|
+
phase: "fetching-metadata",
|
|
77830
|
+
message: "Fetching skill metadata"
|
|
77831
|
+
});
|
|
77742
77832
|
const { ingredient, snapshot } = await (0, skills_1.fetchSkillFile)(options2.client, options2.slug);
|
|
77743
|
-
|
|
77833
|
+
options2.progress?.({
|
|
77834
|
+
type: "phase",
|
|
77835
|
+
slug: options2.slug,
|
|
77836
|
+
phase: "downloading",
|
|
77837
|
+
message: "Downloading skill files"
|
|
77838
|
+
});
|
|
77839
|
+
const archive = await (0, skills_1.downloadSkillArchive)(snapshot.archiveUrl, options2.signal);
|
|
77840
|
+
options2.signal?.throwIfAborted();
|
|
77841
|
+
options2.progress?.({
|
|
77842
|
+
type: "phase",
|
|
77843
|
+
slug: options2.slug,
|
|
77844
|
+
phase: "extracting",
|
|
77845
|
+
message: "Verifying and extracting files"
|
|
77846
|
+
});
|
|
77744
77847
|
const files = (0, archive_1.verifySnapshotFiles)((0, archive_1.parseTarGz)(archive), snapshot.manifest);
|
|
77745
77848
|
const target = (0, skill_filesystem_1.resolveInstallDir)(options2.paths, options2.slug);
|
|
77746
|
-
const
|
|
77849
|
+
const reportConcurrentChange = () => options2.diagnostics({
|
|
77850
|
+
type: "concurrent-change",
|
|
77851
|
+
slug: options2.slug,
|
|
77852
|
+
operation: options2.operation ?? "install",
|
|
77853
|
+
message: `Skipped ${options2.slug}: it was changed while this ${options2.operation ?? "install"} ran`
|
|
77854
|
+
});
|
|
77855
|
+
const before = (await (0, tela_local_config_1.listInstalledSkills)(options2.inventoryFile)).find((entry2) => entry2.slug === options2.slug) ?? null;
|
|
77856
|
+
if (options2.expectedCurrent && !isSameRegistration(before, options2.expectedCurrent)) {
|
|
77857
|
+
reportConcurrentChange();
|
|
77858
|
+
return null;
|
|
77859
|
+
}
|
|
77860
|
+
if ((0, skill_filesystem_1.isUnownedInstallDir)(target, before?.dir ?? null) && !options2.force)
|
|
77861
|
+
throw new SkillInstallDirectoryConflictError(options2.slug, target);
|
|
77862
|
+
const staged = await (0, skill_filesystem_1.stageSkillFiles)(target, files);
|
|
77747
77863
|
let concurrentChange = false;
|
|
77748
77864
|
let entry = null;
|
|
77749
77865
|
try {
|
|
77866
|
+
const lifecycleEntry = {
|
|
77867
|
+
slug: options2.slug,
|
|
77868
|
+
dir: target,
|
|
77869
|
+
sha: snapshot.sha,
|
|
77870
|
+
bundle: ingredient.bundle,
|
|
77871
|
+
symlinks: [],
|
|
77872
|
+
scripts: ingredient.scripts,
|
|
77873
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
77874
|
+
};
|
|
77875
|
+
await runLifecycle(lifecycleEntry, "install", options2.ignoreScripts, options2.diagnostics, options2.progress, options2.signal);
|
|
77876
|
+
options2.signal?.throwIfAborted();
|
|
77877
|
+
options2.progress?.({
|
|
77878
|
+
type: "phase",
|
|
77879
|
+
slug: options2.slug,
|
|
77880
|
+
phase: "finalizing",
|
|
77881
|
+
message: "Linking and finalizing installation"
|
|
77882
|
+
});
|
|
77750
77883
|
entry = await (0, tela_local_config_1.updateInstalledSkill)(options2.slug, async (registered) => {
|
|
77751
|
-
if (
|
|
77884
|
+
if (!isSameRegistration(registered, before)) {
|
|
77752
77885
|
concurrentChange = true;
|
|
77753
|
-
options2.diagnostics({
|
|
77754
|
-
type: "concurrent-change",
|
|
77755
|
-
slug: options2.slug,
|
|
77756
|
-
operation: options2.operation ?? "install",
|
|
77757
|
-
message: `Skipped ${options2.slug}: it was changed while this upgrade ran`
|
|
77758
|
-
});
|
|
77759
77886
|
return registered;
|
|
77760
77887
|
}
|
|
77761
|
-
if ((0, skill_filesystem_1.isUnownedInstallDir)(target, registered?.dir ?? null) && !options2.force) {
|
|
77762
|
-
throw new SkillInstallDirectoryConflictError(options2.slug, target);
|
|
77763
|
-
}
|
|
77764
|
-
const staged = await (0, skill_filesystem_1.stageSkillFiles)(target, files);
|
|
77765
|
-
stagedInstalls.push(staged);
|
|
77766
77888
|
const desiredLinkPaths = new Set(options2.targets.map((linkTarget) => node_path_1.default.join(linkTarget.root, options2.slug)));
|
|
77767
77889
|
for (const linkPath of registered?.symlinks ?? []) {
|
|
77768
77890
|
if (desiredLinkPaths.has(linkPath))
|
|
@@ -77791,25 +77913,27 @@ Run \`tela skills install ${slug} --force\` to replace it, or remove it by hand.
|
|
|
77791
77913
|
}
|
|
77792
77914
|
}
|
|
77793
77915
|
return {
|
|
77794
|
-
|
|
77795
|
-
|
|
77796
|
-
sha: snapshot.sha,
|
|
77797
|
-
bundle: ingredient.bundle,
|
|
77798
|
-
symlinks,
|
|
77799
|
-
scripts: ingredient.scripts,
|
|
77800
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
77916
|
+
...lifecycleEntry,
|
|
77917
|
+
symlinks
|
|
77801
77918
|
};
|
|
77802
77919
|
}, options2.inventoryFile);
|
|
77803
77920
|
} catch (error48) {
|
|
77804
|
-
|
|
77921
|
+
options2.progress?.({
|
|
77922
|
+
type: "phase",
|
|
77923
|
+
slug: options2.slug,
|
|
77924
|
+
phase: "rolling-back",
|
|
77925
|
+
message: "Rolling back failed installation"
|
|
77926
|
+
});
|
|
77927
|
+
await rollbackStagedInstall(staged, error48);
|
|
77805
77928
|
}
|
|
77806
|
-
if (concurrentChange)
|
|
77929
|
+
if (concurrentChange) {
|
|
77930
|
+
await staged.rollback();
|
|
77931
|
+
reportConcurrentChange();
|
|
77807
77932
|
return null;
|
|
77808
|
-
|
|
77809
|
-
|
|
77933
|
+
}
|
|
77934
|
+
await staged.commit();
|
|
77810
77935
|
if (!entry)
|
|
77811
77936
|
throw new Error(`Skill install for ${options2.slug} did not produce an inventory entry.`);
|
|
77812
|
-
await runLifecycle(entry, "install", options2.ignoreScripts, options2.diagnostics);
|
|
77813
77937
|
return entry;
|
|
77814
77938
|
}
|
|
77815
77939
|
function selectUpgradeTargets(installed, slugs) {
|
|
@@ -77828,6 +77952,11 @@ Run \`tela skills install ${slug} --force\` to replace it, or remove it by hand.
|
|
|
77828
77952
|
throw new Error(`${missing.join(", ")} not installed by tela. Nothing to remove.`);
|
|
77829
77953
|
return slugs.map((slug) => bySlug.get(slug));
|
|
77830
77954
|
}
|
|
77955
|
+
function isSameRegistration(left, right) {
|
|
77956
|
+
if (left === null || right === null)
|
|
77957
|
+
return left === right;
|
|
77958
|
+
return isSameInstall(left, right);
|
|
77959
|
+
}
|
|
77831
77960
|
function isSameInstall(left, right) {
|
|
77832
77961
|
return left.installedAt === right.installedAt && left.sha === right.sha && left.dir === right.dir;
|
|
77833
77962
|
}
|
|
@@ -77848,7 +77977,7 @@ Cleanup failed: ${describeError(cleanupError)}`);
|
|
|
77848
77977
|
}
|
|
77849
77978
|
throw error48;
|
|
77850
77979
|
}
|
|
77851
|
-
async function runLifecycle(entry, phase, ignoreScripts, emit) {
|
|
77980
|
+
async function runLifecycle(entry, phase, ignoreScripts, emit, progress2, signal) {
|
|
77852
77981
|
const script = entry.scripts?.[phase];
|
|
77853
77982
|
if (!script)
|
|
77854
77983
|
return;
|
|
@@ -77869,7 +77998,17 @@ Cleanup failed: ${describeError(cleanupError)}`);
|
|
|
77869
77998
|
script,
|
|
77870
77999
|
message: `Running ${phase} script for ${entry.slug}: ${script}`
|
|
77871
78000
|
});
|
|
77872
|
-
|
|
78001
|
+
if (phase === "install") {
|
|
78002
|
+
progress2?.({
|
|
78003
|
+
type: "phase",
|
|
78004
|
+
slug: entry.slug,
|
|
78005
|
+
phase: "running-install-script",
|
|
78006
|
+
message: "Running install script"
|
|
78007
|
+
});
|
|
78008
|
+
}
|
|
78009
|
+
const outcome = await (0, skill_filesystem_1.runSkillScript)(script, entry.dir, phase === "install" ? (output) => progress2?.({ type: "script-output", slug: entry.slug, output }) : void 0, signal);
|
|
78010
|
+
if (outcome.cancelled)
|
|
78011
|
+
signal?.throwIfAborted();
|
|
77873
78012
|
if (outcome.output) {
|
|
77874
78013
|
emit({
|
|
77875
78014
|
type: "lifecycle-output",
|
|
@@ -77880,7 +78019,14 @@ Cleanup failed: ${describeError(cleanupError)}`);
|
|
|
77880
78019
|
});
|
|
77881
78020
|
}
|
|
77882
78021
|
if (!outcome.ok) {
|
|
77883
|
-
|
|
78022
|
+
const error48 = new SkillLifecycleError(entry, phase);
|
|
78023
|
+
emit({
|
|
78024
|
+
type: "lifecycle-failed",
|
|
78025
|
+
slug: entry.slug,
|
|
78026
|
+
phase,
|
|
78027
|
+
message: error48.message
|
|
78028
|
+
});
|
|
78029
|
+
throw error48;
|
|
77884
78030
|
}
|
|
77885
78031
|
}
|
|
77886
78032
|
function lifecycleFailureMessage(entry, phase) {
|
|
@@ -77889,8 +78035,8 @@ Cleanup failed: ${describeError(cleanupError)}`);
|
|
|
77889
78035
|
return `${opener}
|
|
77890
78036
|
${entry.slug} was left in place. Re-run with --ignore-scripts to remove it anyway.`;
|
|
77891
78037
|
return `${opener}
|
|
77892
|
-
${entry.slug} was installed
|
|
77893
|
-
|
|
78038
|
+
${entry.slug} was not installed because its files and inventory entry were rolled back.
|
|
78039
|
+
Retry without the script with \`tela skills install ${entry.slug} --ignore-scripts\`.`;
|
|
77894
78040
|
}
|
|
77895
78041
|
function normalizePaths(paths) {
|
|
77896
78042
|
const installRoot = normalizeAbsolutePath(paths.installRoot, "installRoot");
|
|
@@ -389332,15 +389478,19 @@ async function installSkills(args, options2, io) {
|
|
|
389332
389478
|
ignoreScripts: flags.ignoreScripts,
|
|
389333
389479
|
...flags.symlink ? {} : { targets: [] }
|
|
389334
389480
|
});
|
|
389481
|
+
const installedSlugs = new Set(installed.map((entry) => entry.slug));
|
|
389482
|
+
const failed = flags.rest.filter((slug) => !installedSlugs.has(slug));
|
|
389335
389483
|
if (options2.json) {
|
|
389336
|
-
writeJson(io, { installed });
|
|
389337
|
-
return 0;
|
|
389484
|
+
writeJson(io, failed.length > 0 ? { installed, failed } : { installed });
|
|
389485
|
+
return failed.length > 0 ? 1 : 0;
|
|
389338
389486
|
}
|
|
389339
389487
|
for (const entry of installed)
|
|
389340
389488
|
writeLine(io, `Installed ${entry.slug}@${entry.sha.slice(0, 7)} to ${entry.dir}`);
|
|
389341
|
-
return 0;
|
|
389489
|
+
return failed.length > 0 ? 1 : 0;
|
|
389342
389490
|
}
|
|
389343
389491
|
function writeSkillManagerDiagnostic(io, diagnostic) {
|
|
389492
|
+
if (diagnostic.type === "lifecycle-failed" && diagnostic.phase === "uninstall")
|
|
389493
|
+
return;
|
|
389344
389494
|
writeError(io, diagnostic.message);
|
|
389345
389495
|
}
|
|
389346
389496
|
async function upgradeSkills(args, options2, io) {
|