Any error a command throws reaches the user as a raw Node stack trace. The commands leave thrown errors to their caller (src/cli-commands.ts:3-4). run in bin/quak.ts:49-62 does not catch them, and program.parse() (bin/quak.ts:172) does not await async actions, so the rejection is unhandled. Reproduced on next2:
$ quak --cache-dir /dev/null/x collections
node:internal/fs/promises:1605
return await PromisePrototypeThen(
Error: ENOTDIR: not a directory, mkdir '/dev/null/x/originals'
at async mkdir (node:internal/fs/promises:1605:10)
... 12 more lines ...
Node.js v26.7.0
The same happens for everyday failures: no network, an expired session in collections, files, get or get-thumb, or a disk-full error during get.
Definition of done
bin/quak.ts uses program.parseAsync(). The wrapper catches any error from a command, prints quak: MESSAGE on one line to stderr, and exits 1 once output has drained.
The wrapper lives in src/ so it can be tested. A test shows that a command which throws produces that one line and exit code 1, and no stack trace.
make check green; TODO.md updated in the same commit.
Model: opus-5-5
## Problem
Any error a command throws reaches the user as a raw Node stack trace. The commands leave thrown errors to their caller (`src/cli-commands.ts:3-4`). `run` in `bin/quak.ts:49-62` does not catch them, and `program.parse()` (`bin/quak.ts:172`) does not await async actions, so the rejection is unhandled. Reproduced on `next2`:
```
$ quak --cache-dir /dev/null/x collections
node:internal/fs/promises:1605
return await PromisePrototypeThen(
Error: ENOTDIR: not a directory, mkdir '/dev/null/x/originals'
at async mkdir (node:internal/fs/promises:1605:10)
... 12 more lines ...
Node.js v26.7.0
```
The same happens for everyday failures: no network, an expired session in `collections`, `files`, `get` or `get-thumb`, or a disk-full error during `get`.
## Definition of done
1. `bin/quak.ts` uses `program.parseAsync()`. The wrapper catches any error from a command, prints `quak: MESSAGE` on one line to stderr, and exits 1 once output has drained.
2. The wrapper lives in `src/` so it can be tested. A test shows that a command which throws produces that one line and exit code 1, and no stack trace.
3. `make check` green; `TODO.md` updated in the same commit.
Model: opus-5-5
clawbot
self-assigned this 2026-09-23 05:34:58 +02:00
Implemented in #121: a thrown command error now prints one quak: MESSAGE line on stderr and exits 1; the wrapper lives in src/cli-run.ts with a test, and bin/quak.ts awaits program.parseAsync().
Model: opus-5-5
Implemented in https://git.eeqj.de/sneak/quak/pulls/121: a thrown command error now prints one `quak: MESSAGE` line on stderr and exits 1; the wrapper lives in `src/cli-run.ts` with a test, and `bin/quak.ts` awaits `program.parseAsync()`.
Model: opus-5-5
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Problem
Any error a command throws reaches the user as a raw Node stack trace. The commands leave thrown errors to their caller (
src/cli-commands.ts:3-4).runinbin/quak.ts:49-62does not catch them, andprogram.parse()(bin/quak.ts:172) does not await async actions, so the rejection is unhandled. Reproduced onnext2:The same happens for everyday failures: no network, an expired session in
collections,files,getorget-thumb, or a disk-full error duringget.Definition of done
bin/quak.tsusesprogram.parseAsync(). The wrapper catches any error from a command, printsquak: MESSAGEon one line to stderr, and exits 1 once output has drained.src/so it can be tested. A test shows that a command which throws produces that one line and exit code 1, and no stack trace.make checkgreen;TODO.mdupdated in the same commit.Model: opus-5-5
Implemented in #121: a thrown command error now prints one
quak: MESSAGEline on stderr and exits 1; the wrapper lives insrc/cli-run.tswith a test, andbin/quak.tsawaitsprogram.parseAsync().Model: opus-5-5