From 004bb0ea337977926520854affbba375d24500b3 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Sat, 14 Feb 2026 01:51:36 +0700 Subject: [PATCH] refactor(cli): unify daemon start error message handling What changed: - Added a shared utility `getErrorMessage(error: unknown)` in `packages/cli/src/utils/errors.ts`. - Refactored `packages/cli/src/commands/daemon/start.ts` to use `getErrorMessage` and a local `exitWithError` helper instead of repeating inline error-message extraction + exit logic in each catch block. - Added `packages/cli/tests/19-errors-utils.test.ts` to lock utility behavior for Error and non-Error throw values. Reasoning: - `runStart` had repeated branching (`err instanceof Error ? err.message : String(err)`) across multiple catch sites. - Centralizing this keeps behavior stable while reducing duplication and cognitive overhead, aligned with the refactor skill guidance to simplify structure without changing user-visible behavior. Verification: - Ran `npx tsx packages/cli/tests/18-local-daemon-utils.test.ts` (pass). - Ran `npx tsx packages/cli/tests/19-errors-utils.test.ts` (pass). - Ran `npm run -w @getpaseo/cli typecheck` (pass). Notes for next agent: - Existing `scripts/codex-refactor-loop.sh` was already modified before this change and is intentionally not included. - `packages/cli/tests/03-daemon.test.ts` currently failed in this environment because Test 3 expected daemon status failure, but command exited successfully (likely environment state dependent). This refactor does not touch that path; worth deflakifying or isolating in a follow-up. --- packages/cli/src/commands/daemon/start.ts | 24 +++++++++++----------- packages/cli/src/utils/errors.ts | 10 +++++++++ packages/cli/tests/19-errors-utils.test.ts | 22 ++++++++++++++++++++ 3 files changed, 44 insertions(+), 12 deletions(-) create mode 100644 packages/cli/src/utils/errors.ts create mode 100644 packages/cli/tests/19-errors-utils.test.ts diff --git a/packages/cli/src/commands/daemon/start.ts b/packages/cli/src/commands/daemon/start.ts index 3b6bc230d..caa84ef69 100644 --- a/packages/cli/src/commands/daemon/start.ts +++ b/packages/cli/src/commands/daemon/start.ts @@ -12,6 +12,7 @@ import { startLocalDaemonDetached, type DaemonStartOptions as StartOptions, } from './local-daemon.js' +import { getErrorMessage } from '../../utils/errors.js' export type { DaemonStartOptions as StartOptions } from './local-daemon.js' @@ -73,9 +74,7 @@ export async function runStart(options: StartOptions): Promise { console.log(chalk.green(`Daemon starting in background (PID ${startup.pid ?? 'unknown'}).`)) console.log(chalk.dim(`Logs: ${startup.logPath}`)) } catch (err) { - const message = err instanceof Error ? err.message : String(err) - console.error(chalk.red(message)) - process.exit(1) + exitWithError(getErrorMessage(err)) } return } @@ -94,18 +93,15 @@ export async function runStart(options: StartOptions): Promise { logger = createRootLogger(persistedConfig) config = loadConfig(paseoHome, { cli: toCliOverrides(options) }) } catch (err) { - const message = err instanceof Error ? err.message : String(err) - console.error(chalk.red(message)) - process.exit(1) + exitWithError(getErrorMessage(err)) } let daemon: Awaited> try { daemon = await createPaseoDaemon(config, logger) } catch (err) { - const message = err instanceof Error ? err.message : String(err) - console.error(chalk.red(`Failed to initialize daemon: ${message}`)) - process.exit(1) + const message = getErrorMessage(err) + exitWithError(`Failed to initialize daemon: ${message}`) } let shuttingDown = false @@ -140,8 +136,12 @@ export async function runStart(options: StartOptions): Promise { try { await daemon.start() } catch (err) { - const message = err instanceof Error ? err.message : String(err) - console.error(chalk.red(`Failed to start daemon: ${message}`)) - process.exit(1) + const message = getErrorMessage(err) + exitWithError(`Failed to start daemon: ${message}`) } } + +function exitWithError(message: string): never { + console.error(chalk.red(message)) + process.exit(1) +} diff --git a/packages/cli/src/utils/errors.ts b/packages/cli/src/utils/errors.ts new file mode 100644 index 000000000..7c01a64ca --- /dev/null +++ b/packages/cli/src/utils/errors.ts @@ -0,0 +1,10 @@ +/** + * Convert unknown thrown values into a user-safe error string. + */ +export function getErrorMessage(error: unknown): string { + if (error instanceof Error) { + return error.message + } + + return String(error) +} diff --git a/packages/cli/tests/19-errors-utils.test.ts b/packages/cli/tests/19-errors-utils.test.ts new file mode 100644 index 000000000..af0289d42 --- /dev/null +++ b/packages/cli/tests/19-errors-utils.test.ts @@ -0,0 +1,22 @@ +#!/usr/bin/env npx tsx + +import assert from 'node:assert' +import { getErrorMessage } from '../src/utils/errors.js' + +console.log('=== Error Utils ===\n') + +{ + console.log('Test 1: returns Error.message for Error instances') + assert.strictEqual(getErrorMessage(new Error('boom')), 'boom') + console.log('✓ returns Error.message\n') +} + +{ + console.log('Test 2: stringifies non-Error values') + assert.strictEqual(getErrorMessage('plain string'), 'plain string') + assert.strictEqual(getErrorMessage(42), '42') + assert.strictEqual(getErrorMessage(null), 'null') + console.log('✓ stringifies non-Error values\n') +} + +console.log('=== All error utility tests passed ===')