feat(FN-4764): complete Step 2-4 — surface structured gh errors in API UI CLI

Fusion-Task-Id: FN-4764
Fusion-Task-Lineage: 14809ad5-8463-4e16-a934-1414225f17ae
This commit is contained in:
Fusion (runfusion.ai)
2026-05-17 14:27:53 -07:00
committed by gsxdsm
parent 71dfc916f4
commit 26c3d330d3
8 changed files with 242 additions and 25 deletions

View File

@@ -9,6 +9,7 @@ import { watchFile, unwatchFile, statSync, existsSync, readFileSync } from "node
import { basename, join } from "node:path";
import * as dashboard from "@fusion/dashboard";
import {
classifyGhError,
getGhErrorMessage,
getCurrentRepo,
isGhAuthenticated,
@@ -29,6 +30,17 @@ try {
// Some tests partially mock @fusion/dashboard and omit the hook export.
}
function formatGhErrorForCli(err: unknown): string {
const structured = classifyGhError(err);
const lines = [`GitHub error: ${structured.message}`];
if (structured.hint) lines.push(` Hint: ${structured.hint}`);
if (structured.action?.kind === "shell") lines.push(` Action: run \`${structured.action.command}\``);
if (structured.action?.kind === "open") lines.push(` Action: open ${structured.action.url}`);
if (structured.action?.kind === "retry") lines.push(" Action: retry the command");
if (structured.retryable) lines.push(" (retryable — re-run `fn pr create <task-id>` to try again)");
return `${lines.join("\n")}\n`;
}
function getGitHubIssueUrl(sourceMetadata: unknown): string | undefined {
if (!sourceMetadata || typeof sourceMetadata !== "object") return undefined;
const issueUrl = (sourceMetadata as { issueUrl?: unknown }).issueUrl;
@@ -1536,7 +1548,7 @@ export async function runTaskPrCreate(id: string, options: PrCreateOptions = {},
console.error(`Error: No commits between ${options.base || "default base"} and ${branchName}. Push changes before creating PR.`);
process.exit(1);
} else {
console.error(`Error: ${msg || "Failed to create PR"}`);
process.stderr.write(formatGhErrorForCli(err));
process.exit(1);
}
}