fix(eve): simplify add failure results (#3495)

Signed-off-by: owenkephart <owen.kephart@vercel.com>
This commit is contained in:
OwenKephart
2026-09-18 13:20:15 -07:00
committed by GitHub
parent 06e17ae15f
commit 38ad163b3b
5 changed files with 43 additions and 17 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"eve": patch
---
Keep registry installation error traces visible in the dev TUI after an `/add` failure, alongside the per-item recovery guidance.
@@ -17,7 +17,8 @@ describe("formatRegistrySessionResult", () => {
failures: [
{
title: "Slack",
message: "Vercel CLI is not authenticated.\nRun /deploy, then try again.",
message:
"Vercel CLI is not authenticated. Try again with `eve add channel/slack --skip-install`.",
},
],
}),
@@ -30,7 +31,7 @@ describe("formatRegistrySessionResult", () => {
" Configured MCP connection.\n\n" +
" Slack\n" +
" Vercel CLI is not authenticated.\n" +
" Run /deploy, then try again.",
" Try again with `eve add channel/slack --skip-install`.",
);
});
@@ -30,6 +30,10 @@ function joinedTitles(titles: readonly string[]): string {
return `${titles.slice(0, -1).join(", ")}, and ${titles.at(-1)}`;
}
function formatFailureMessage(message: string): string {
return message.replace(" Try again with `", "\nTry again with `");
}
function resultHeadline(
outcomes: readonly { kind: "installed" | "failed" | "cancelled" }[],
installedTitles: readonly string[],
@@ -67,7 +71,11 @@ export function formatRegistrySessionResult(result: RegistrySessionResult): stri
continue;
}
if (outcome.kind === "failed") {
lines.push(...outcome.message.split("\n").map((line) => ` ${line}`));
lines.push(
...formatFailureMessage(outcome.message)
.split("\n")
.map((line) => ` ${line}`),
);
continue;
}
if (outcome.facts.length === 0 && outcome.output.length === 0) {
@@ -118,6 +118,31 @@ describe("runRegistryFlow", () => {
expect(flow.browseRegistryCatalog).not.toHaveBeenCalled();
expect(flow.installRegistryItem).toHaveBeenCalledOnce();
});
it("reports an installation failure without another decision prompt", async () => {
const flow = deps();
flow.installRegistryItem = vi.fn(async () => {
throw new Error("Dependency installation failed.");
});
const fake = createFakePrompter();
await expect(
runRegistryFlow({
appRoot: "/agent",
initialAddress: "connection/linear",
prompter: fake.prompter,
deps: flow,
}),
).resolves.toMatchObject({
kind: "done",
result: {
failures: [{ title: "connection/linear", message: "Dependency installation failed." }],
},
});
expect(fake.prompter.log.error).not.toHaveBeenCalled();
expect(fake.selectMessages).toEqual([]);
});
it("cancels before installing anything", async () => {
const flow = deps();
const fake = createFakePrompter({
+1 -14
View File
@@ -1,6 +1,6 @@
import type { RegistryCatalogItem } from "#cli/commands/registry.js";
import { HumanActionRequiredError } from "#setup/human-action.js";
import type { Prompter, SingleSelectOptions } from "#setup/prompter.js";
import type { Prompter } from "#setup/prompter.js";
import { WizardCancelledError } from "#setup/step.js";
import { withSpinner } from "#setup/with-spinner.js";
@@ -120,20 +120,7 @@ export async function runRegistryFlow(input: {
if (error instanceof HumanActionRequiredError) throw error;
const message = error instanceof Error ? error.message : String(error);
const failureMessage = message.trim() || "Installation failed.";
const summary = failureMessage.split("\n").find((line) => line.trim() !== "");
activeSession.addFailure(label(item), failureMessage);
const request: SingleSelectOptions<"skip" | "cancel"> = {
message: `Couldn't add ${label(item)}`,
options: [
{ value: "skip", label: `Skip ${label(item)}` },
{ value: "cancel", label: "Cancel setup" },
],
};
if (summary !== undefined) request.description = summary;
const action = await input.prompter.select(request);
if (action === "cancel") {
return { kind: "done", result: { ...activeSession.result(), cancelled: true } };
}
}
}
return {