fix(cli): improve skill install failure output

This commit is contained in:
Peter Steinberger
2026-01-01 22:55:15 +01:00
parent b858fdd755
commit 1a3323a261
2 changed files with 68 additions and 8 deletions

View File

@@ -29,6 +29,42 @@ export type SkillInstallResult = {
code: number | null;
};
function summarizeInstallOutput(text: string): string | undefined {
const raw = text.trim();
if (!raw) return undefined;
const lines = raw
.split("\n")
.map((line) => line.trim())
.filter(Boolean);
if (lines.length === 0) return undefined;
const preferred =
lines.find((line) => /^error\b/i.test(line)) ??
lines.find((line) => /\b(err!|error:|failed)\b/i.test(line)) ??
lines.at(-1);
if (!preferred) return undefined;
const normalized = preferred.replace(/\s+/g, " ").trim();
const maxLen = 200;
return normalized.length > maxLen
? `${normalized.slice(0, maxLen - 1)}`
: normalized;
}
function formatInstallFailureMessage(result: {
code: number | null;
stdout: string;
stderr: string;
}): string {
const code =
typeof result.code === "number" ? `exit ${result.code}` : "unknown exit";
const summary =
summarizeInstallOutput(result.stderr) ??
summarizeInstallOutput(result.stdout);
if (!summary) return `Install failed (${code})`;
return `Install failed (${code}): ${summary}`;
}
function resolveInstallId(spec: SkillInstallSpec, index: number): string {
return (spec.id ?? `${spec.kind}-${index}`).trim();
}
@@ -91,7 +127,9 @@ function buildInstallCommand(
}
}
async function resolveBrewBinDir(timeoutMs: number): Promise<string | undefined> {
async function resolveBrewBinDir(
timeoutMs: number,
): Promise<string | undefined> {
if (!hasBinary("brew")) return undefined;
const prefixResult = await runCommandWithTimeout(["brew", "--prefix"], {
timeoutMs: Math.min(timeoutMs, 30_000),
@@ -204,9 +242,12 @@ export async function installSkill(
if (spec.kind === "go" && !hasBinary("go")) {
if (hasBinary("brew")) {
const brewResult = await runCommandWithTimeout(["brew", "install", "go"], {
timeoutMs,
});
const brewResult = await runCommandWithTimeout(
["brew", "install", "go"],
{
timeoutMs,
},
);
if (brewResult.code !== 0) {
return {
ok: false,
@@ -252,7 +293,7 @@ export async function installSkill(
const success = result.code === 0;
return {
ok: success,
message: success ? "Installed" : "Install failed",
message: success ? "Installed" : formatInstallFailureMessage(result),
stdout: result.stdout.trim(),
stderr: result.stderr.trim(),
code: result.code,

View File

@@ -13,6 +13,15 @@ import type { ClawdisConfig } from "../config/config.js";
import type { RuntimeEnv } from "../runtime.js";
import { guardCancel, resolveNodeManagerOptions } from "./onboard-helpers.js";
function summarizeInstallFailure(message: string): string | undefined {
const cleaned = message
.replace(/^Install failed(?:\s*\([^)]*\))?\s*:?\s*/i, "")
.trim();
if (!cleaned) return undefined;
const maxLen = 140;
return cleaned.length > maxLen ? `${cleaned.slice(0, maxLen - 1)}` : cleaned;
}
function upsertSkillEntry(
cfg: ClawdisConfig,
skillKey: string,
@@ -108,9 +117,19 @@ export async function setupSkills(
installId,
config: next,
});
spin.stop(result.ok ? `Installed ${name}` : `Install failed: ${name}`);
if (!result.ok && result.stderr) {
runtime.log(result.stderr.trim());
if (result.ok) {
spin.stop(`Installed ${name}`);
} else {
const code = result.code == null ? "" : ` (exit ${result.code})`;
const detail = summarizeInstallFailure(result.message);
spin.stop(
`Install failed: ${name}${code}${detail ? `${detail}` : ""}`,
);
if (result.stderr) runtime.log(result.stderr.trim());
else if (result.stdout) runtime.log(result.stdout.trim());
runtime.log(
"Tip: run `clawdis doctor` to review skills + requirements.",
);
}
}
}