* feat(exit-codes): add Unix-standard exit codes to all CliError types
Introduce EXIT_CODES constant table (sysexits.h conventions) and wire
exitCode into every CliError subclass so the process exit code reflects
the semantic type of failure:
0 success (default)
1 generic / unexpected error
2 argument / usage error (ArgumentError)
66 empty result / not found (EmptyResultError, SelectorError)
69 service unavailable (BrowserConnectError, AdapterLoadError)
77 permission / auth required (AuthRequiredError)
78 configuration error (ConfigError)
124 timeout (TimeoutError)
130 Ctrl-C / SIGINT (unchanged, tui.ts)
resolveExitCode() in commanderAdapter.ts reads err.exitCode for typed
CliErrors, and falls back to pattern-matching message text for untyped
adapter errors (auth pattern → 77, not-found pattern → 66, else → 1).
Shell scripts can now distinguish error categories:
opencli spotify status || echo "exit $?" # 69 if browser not running
opencli github issues --repo x 2>/dev/null; [ $? -eq 77 ] && opencli github auth
* fix(exit-codes): address review findings
- TIMEOUT: change from 124 → 75 (EX_TEMPFAIL); 124 is bash timeout(1)'s
own exit code, creating ambiguity when shell runs `timeout 30 opencli`
- SelectorError: change from EMPTY_RESULT(66) → GENERIC_ERROR(1); a
missing DOM selector is an adapter bug, not a user "no data" condition
- normalizeArgValue: throw ArgumentError instead of bare CliError so
invalid bool args correctly exit with USAGE_ERROR(2) not GENERIC_ERROR(1)
- resolveExitCode: explicitly map 'http' classification to GENERIC_ERROR
to keep exit-code path in sync with the render path
- tui.ts: replace hardcoded process.exit(130) with EXIT_CODES.INTERRUPTED
* feat(exit-codes): replace all hardcoded exit numbers with EXIT_CODES constants
Extend the exit code system to cover every process exit point in the codebase.
No magic numbers remain — all exit codes are now referenced by name.
Semantic upgrades beyond pure renaming:
- plugin update missing args → USAGE_ERROR (2) instead of 1
- plugin update conflicting → USAGE_ERROR (2) instead of 1
- opencli install <unknown> → USAGE_ERROR (2) instead of 1
- unknown command fallback → USAGE_ERROR (2) instead of 1
- record with no candidates → EMPTY_RESULT (66) instead of 1
- external CLI install fail → SERVICE_UNAVAIL (69) instead of 1
- daemon EADDRINUSE → SERVICE_UNAVAIL (69) instead of 1
Files touched: cli.ts, external.ts, daemon.ts, main.ts,
clis/antigravity/serve.ts
Non-browser commands (`browser: false`) ran without any timeout
protection, even when `timeoutSeconds` was explicitly set. This wraps
the non-browser execution path with `runWithTimeout()` when the
adapter defines a positive `timeoutSeconds`.
Also adds an optional `hint` parameter to `TimeoutError` so the
non-browser path shows a relevant suggestion instead of the
browser-specific `OPENCLI_BROWSER_COMMAND_TIMEOUT` env var hint.