fix(cli): improve skill install failure output
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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.",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user