Skip to content

Commit dfc1c60

Browse files
committed
feat: add issue filing guidance to error messages
Added issue filing guidance to both CLI and MCP error messages to help users report unexpected errors or bad UX. Changes: - Added formatCliError() helper in CLI shared.ts that formats error messages with GitHub issue URL for non-user errors - Updated program.ts to include global error handlers for uncaught exceptions and unhandled promise rejections - Enhanced wrapError() in MCP server with issue filing guidance for unexpected errors (controlled by isUserError parameter) - All unexpected errors now suggest filing an issue at: https://github.com/ExaDev/SysProM/issues/new User errors (e.g. invalid input, missing files) don't include the issue filing guidance, only genuine unexpected errors do.
1 parent d679587 commit dfc1c60

3 files changed

Lines changed: 53 additions & 6 deletions

File tree

src/cli/program.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { dirname, resolve } from "node:path";
33
import { fileURLToPath } from "node:url";
44
import { Command } from "commander";
55
import { buildCommander } from "./define-command.js";
6+
import { formatCliError } from "./shared.js";
67
import type { CommandDef } from "./define-command.js";
78

89
let cachedVersion: string | undefined;
@@ -94,3 +95,15 @@ program
9495
.action(async () => {
9596
await import("../mcp/server.js");
9697
});
98+
99+
// Global error handlers for uncaught exceptions
100+
process.on("uncaughtException", (error) => {
101+
console.error(formatCliError(error, false));
102+
process.exit(1);
103+
});
104+
105+
process.on("unhandledRejection", (reason) => {
106+
const error = reason instanceof Error ? reason : new Error(String(reason));
107+
console.error(formatCliError(error, false));
108+
process.exit(1);
109+
});

src/cli/shared.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,3 +251,31 @@ export function persistDoc(
251251
}
252252
}
253253
}
254+
255+
// ---------------------------------------------------------------------------
256+
// Error formatting and reporting
257+
// ---------------------------------------------------------------------------
258+
259+
const ISSUE_URL = "https://github.com/ExaDev/SysProM/issues/new";
260+
261+
/**
262+
* Format an error message for CLI output with issue filing guidance.
263+
* If the error appears to be unexpected (not a user error), suggests filing an issue.
264+
* @param error - The error to format
265+
* @param isUserError - Whether this is a user error (e.g. invalid input); if false, suggests filing an issue
266+
* @returns Formatted error message
267+
* @example
268+
* ```ts
269+
* try { ... } catch (err: unknown) {
270+
* console.error(formatCliError(err, false)); // Not a user error - suggest issue
271+
* process.exit(1);
272+
* }
273+
* ```
274+
*/
275+
export function formatCliError(error: unknown, isUserError = false): string {
276+
const message = error instanceof Error ? error.message : String(error);
277+
if (isUserError) {
278+
return message;
279+
}
280+
return `${message}\n\nIf this was unexpected or bad UX, please file an issue:\n${ISSUE_URL}`;
281+
}

src/mcp/server.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,28 @@ import {
2727

2828
/**
2929
* Wrap an error with a descriptive prefix and attach the original as cause.
30+
* Adds issue filing guidance for unexpected errors.
3031
* @param prefix - The error prefix (e.g., "Failed to add node")
3132
* @param error - The caught error
33+
* @param isUserError - Whether this is a user error; if false, suggests filing an issue
3234
* @example
3335
* ```ts
3436
* try {
3537
* someOperation();
3638
* } catch (error) {
37-
* wrapError("Failed to do X", error);
39+
* wrapError("Failed to do X", error, false); // Not a user error - suggest issue
3840
* }
3941
* ```
4042
*/
41-
function wrapError(prefix: string, error: unknown): never {
42-
throw new Error(
43-
`${prefix}: ${error instanceof Error ? error.message : String(error)}`,
44-
{ cause: error },
45-
);
43+
function wrapError(prefix: string, error: unknown, isUserError = false): never {
44+
const message = error instanceof Error ? error.message : String(error);
45+
const fullMessage = `${prefix}: ${message}`;
46+
const issueUrl = "https://github.com/ExaDev/SysProM/issues/new";
47+
const errorMsg = isUserError
48+
? fullMessage
49+
: `${fullMessage}\n\nIf this was unexpected or bad UX, please file an issue: ${issueUrl}`;
50+
51+
throw new Error(errorMsg, { cause: error });
4652
}
4753

4854
// Create MCP server instance

0 commit comments

Comments
 (0)