From 0feded23c43ab304bb7e7a30d8f110270d092591 Mon Sep 17 00:00:00 2001 From: josephine Date: Sun, 7 Jun 2026 10:53:59 +0200 Subject: [PATCH] feat: add suggestion for error messages --- src/lib/utils/errors.ts | 49 ++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/src/lib/utils/errors.ts b/src/lib/utils/errors.ts index 8e4a8ec..d14c6d9 100644 --- a/src/lib/utils/errors.ts +++ b/src/lib/utils/errors.ts @@ -1,9 +1,12 @@ -import { red } from "ansis"; +import { cyan, red } from "ansis"; export class CLIError extends Error { - constructor(message: string) { + public readonly suggestion?: string; + + constructor(message: string, suggestion?: string) { super(message); this.name = this.constructor.name; + this.suggestion = suggestion; Object.setPrototypeOf(this, new.target.prototype); } } @@ -11,51 +14,70 @@ export class CLIError extends Error { export class ConfigNotFoundError extends CLIError { constructor(configPath: string) { super( - `Configuration file not found at path: ${configPath}. Please run 'nf new' or provide a valid --config path.`, + `Configuration file not found at path: ${configPath}.`, + "Please run 'nf new' or provide a valid --config path.", ); } } export class BuildError extends CLIError { constructor(details: string) { - super(`Build failed: ${details}`); + super( + `Build failed: ${details}`, + "Check the logs above for syntax errors or configuration issues.", + ); } } export class InvalidCommandArgumentError extends CLIError { constructor(argName: string, expected: string) { - super(`Invalid argument '${argName}'. Expected: ${expected}.`); + super( + `Invalid argument '${argName}'. Expected: ${expected}.`, + "Verify the command syntax using the --help flag.", + ); } } export class RegistryAuthenticationError extends CLIError { constructor() { - super("You must be logged in to perform this action. Run 'nf login'."); + super("You must be logged in to perform this action.", "Run 'nf login' to authenticate."); } } export class ProjectInitializationError extends CLIError { constructor(details: string) { - super(`Failed to create new project: ${details}`); + super( + `Failed to create new project: ${details}`, + "Verify your permissions and that the target directory is empty.", + ); } } export class ManifestError extends CLIError { constructor(detail: string) { - super(`Manifest Error: ${detail}`); + super( + `Manifest Error: ${detail}`, + "Check your nanoforge.manifest.json file for syntax or formatting errors.", + ); } } export class FileSystemError extends CLIError { constructor(action: string, targetPath: string) { - super(`File System Error [${action}]: ${targetPath}`); + super( + `File System Error [${action}]: ${targetPath}`, + "Verify your file permissions and ensure the path exists.", + ); } } export class ApiRequestError extends CLIError { constructor(status: number, cause?: unknown) { const causeStr = cause && typeof cause === "object" ? JSON.stringify(cause, null, 2) : cause; - super(`API Request failed (Status ${status})${causeStr ? `\nDetails: ${causeStr}` : ""}`); + super( + `API Request failed (Status ${status})${causeStr ? `\nDetails: ${causeStr}` : ""}`, + "Check your network connection, API key, or the registry status.", + ); } } @@ -77,10 +99,17 @@ export const getErrorMessage = (error: unknown): string | undefined => { export const handleActionError = (context: string, error: unknown): never => { console.error(); console.error(red(context)); + if (error instanceof CLIError) { console.error(error.message); + + if (error.suggestion) { + console.info(cyan(`\n💡 Suggestion: ${error.suggestion}`)); + } + process.exit(1); } + const msg = getErrorMessage(error); if (msg) console.error(msg); process.exit(1);