From 0487882c1ef6b96728bdac0d8d6b906f510e82ce Mon Sep 17 00:00:00 2001 From: Giulio Fagioli Date: Mon, 1 Jun 2026 11:56:36 +0200 Subject: [PATCH 1/4] refactor(cli): centralize output, error formatting, and error handling Extract shared formatError (errors.ts), terminal output/colors/icons (ui.ts, NO_COLOR/TTY-aware via styleText), and a runAction wrapper that gives every command uniform error UX. Removes duplicated catch blocks and ANSI magic strings; fixes list-style commands lacking error handling. --- src/commands/copy-config.ts | 29 +++++------ src/commands/create.ts | 15 +++--- src/commands/login.ts | 56 +++++++++++----------- src/commands/remove.ts | 15 +++--- src/commands/rename.ts | 15 +++--- src/commands/run.ts | 12 ++--- src/commands/skills.ts | 34 ++++++------- src/commands/use.ts | 12 ++--- src/lib/compliance.ts | 4 +- src/lib/errors.ts | 4 ++ src/lib/profile-config-copy.ts | 5 +- src/lib/profile-store.ts | 5 +- src/lib/run-action.ts | 30 ++++++++++++ src/lib/ui.ts | 66 +++++++++++++++++++++++++ tests/commands/copy-config.test.ts | 12 +++-- tests/commands/create.test.ts | 2 +- tests/lib/compliance.test.ts | 3 +- tests/lib/errors.test.ts | 14 ++++++ tests/lib/run-action.test.ts | 62 ++++++++++++++++++++++++ tests/lib/ui.test.ts | 77 ++++++++++++++++++++++++++++++ 20 files changed, 358 insertions(+), 114 deletions(-) create mode 100644 src/lib/errors.ts create mode 100644 src/lib/run-action.ts create mode 100644 src/lib/ui.ts create mode 100644 tests/lib/errors.test.ts create mode 100644 tests/lib/run-action.test.ts create mode 100644 tests/lib/ui.test.ts diff --git a/src/commands/copy-config.ts b/src/commands/copy-config.ts index 4ed544e..d049cff 100644 --- a/src/commands/copy-config.ts +++ b/src/commands/copy-config.ts @@ -7,6 +7,8 @@ import { type ProfileConfigCopySelector, planProfileConfigCopy, } from '../lib/profile-config-copy.js' +import { runAction } from '../lib/run-action.js' +import { printSuccess, warnLine } from '../lib/ui.js' function confirm(question: string): Promise { const rl = createInterface({ input: process.stdin, output: process.stdout }) @@ -56,12 +58,12 @@ export function registerCopyConfig(program: Command): void { .option('--dry-run', 'Preview what would be copied without writing files') .option('-f, --force', 'Skip overwrite confirmation and apply immediately') .action( - async ( - source: string, - target: string, - opts: { dryRun?: boolean; force?: boolean; only?: string[] }, - ) => { - try { + runAction( + async ( + source: string, + target: string, + opts: { dryRun?: boolean; force?: boolean; only?: string[] }, + ) => { const plan = planProfileConfigCopy( source, target, @@ -79,7 +81,9 @@ export function registerCopyConfig(program: Command): void { if (plan.overwriteCount > 0) { console.log( - `! ${plan.overwriteCount} existing path(s) in "${target}" will be overwritten.`, + warnLine( + `${plan.overwriteCount} existing path(s) in "${target}" will be overwritten.`, + ), ) if (!opts.force) { const ok = await confirm('Continue? (y/N) ') @@ -91,13 +95,10 @@ export function registerCopyConfig(program: Command): void { } const result = applyProfileConfigCopy(plan) - console.log( - `\x1b[32m✓\x1b[0m Copied ${result.copiedCount} item(s) from "${source}" to "${target}" (${result.createdCount} created, ${result.overwrittenCount} overwritten)`, + printSuccess( + `Copied ${result.copiedCount} item(s) from "${source}" to "${target}" (${result.createdCount} created, ${result.overwrittenCount} overwritten)`, ) - } catch (e) { - console.error(`\x1b[31m✗\x1b[0m ${e instanceof Error ? e.message : String(e)}`) - process.exit(1) - } - }, + }, + ), ) } diff --git a/src/commands/create.ts b/src/commands/create.ts index 929c908..138eb40 100644 --- a/src/commands/create.ts +++ b/src/commands/create.ts @@ -4,6 +4,8 @@ import { getCompactComplianceNoticeLines } from '../lib/compliance.js' import { addProfile } from '../lib/config.js' import { applyProfileConfigCopy, planProfileConfigCopy } from '../lib/profile-config-copy.js' import { createProfileDir, removeProfileDir } from '../lib/profiles.js' +import { runAction } from '../lib/run-action.js' +import { printSuccess } from '../lib/ui.js' /** Registers the CLI workflow for creating a managed profile. */ export function registerCreate(program: Command): void { @@ -13,8 +15,8 @@ export function registerCreate(program: Command): void { .option('-l, --label