Repository navigation
feat(cli): agent contract — exit codes, error envelope, confirmation without a terminal, dry run - #26
Merged
Merged
Conversation
…without a terminal, dry run Plan step [1] of docs/tasks/agent-native-cli.md. - src/lib/exit-codes.ts: the documented exit-code table (0–9), identical with and without --json. No oclif import, so the MCP server can share it. - src/lib/cli-error.ts: CliError + toEnvelope, one shape for every failure (SDK errors, parser errors, JSON input, prompts, bugs): code, message, status, exit, fieldErrors, url, traceId, hint, docs. No oclif import. - BaseCommand.catch prints the envelope (stdout with --json, stderr as text) and exits with the documented code; oclif's own JSON error path (22k-line parse-context dump) never runs. Unknown commands go through a command_not_found hook and get the same envelope. - confirmOrFail: destructive commands (delete, stop, regenerate, block, disable, --many updates) ask in a terminal; without a terminal and without --yes they exit 3 and send nothing; "no" at the prompt exits 9. The old `isTTY` check let `norbix users delete x </dev/null` (or `| tee`) delete silently. - --dry-run on destructive commands: the SDK builds the request with the real auth/region/project and a transport middleware stops it before fetch; the SDK call and the HTTP request are printed, exit 0. - login / configure: every prompt is guarded; a non-interactive shell gets exit 2 with the flag-based alternatives instead of a stack trace. - hub/api: --yes / --dry-run are oclif flags now; context is resolved before the confirmation or the dry run. - No ANSI codes with --json, NO_COLOR or a pipe (init hook). - README: "Errors and exit codes". Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ive sweep Plan step [1] of docs/tasks/agent-native-cli.md. The built CLI runs as a child process (pipes everywhere, like an agent sees it) against a fake gateway on a free port. Covers: every destructive command exits 3 without --yes and sends nothing, runs with --yes; --dry-run prints the call and sends nothing, and still fails on a missing region or key; the JSON envelope and exit code for 404, 400 with fieldErrors, 401, 500, network, usage, unknown command, bad JSON input, unknown profile and login without a terminal — the same exit code with and without --json, stdout one JSON document, stderr empty; no ANSI codes. Plus unit tests for toEnvelope. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The handover (target behaviour, verified findings, specification, PR order, reproduction harness, acceptance tests) plus the work log kept while implementing it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This was referenced Oct 2, 2026
Merged
# Conflicts: # tests/push.test.ts
Each of the three tests spawns the CLI once per destructive command, four at a time. Windows runners take ~3 s per spawn, so they ran past vitest's 20 s default, and the timed-out pool kept sending requests into the next test (the --dry-run test then saw DELETE/PUT hits it never made). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
🎉 This PR is included in version 1.6.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR 1 of 3 for "Agent-native CLI" (
docs/tasks/agent-native-cli.md). PR 2 (feat/agent-native-cli-2, discovery + dry-run everywhere) and PR 3 (feat/agent-native-cli-3, startup + agent docs +norbix ai init) are stacked on top of this branch so each stays reviewable; merge this one first.Breaking: destructive commands (
delete,stop,regenerate,block,disable,db update --many,db delete --id) now exit 3 and send nothing when there is no terminal and no--yes— before,norbix users delete x </dev/null(or| tee) deleted silently.users block,scheduler disable,payments disable,webhooks disable,push integration disableask for confirmation now. Error text moved to stderr asError: … / Hint: …; exit codes are the documented 0–9 table instead of oclif's 1/2.What changed
src/lib/exit-codes.ts): the documented table 0–9, the same with and without--json. No oclif import — the MCP server can share it.src/lib/cli-error.ts,BaseCommand.catch): every failure — SDK errors, parser errors, bad JSON input, prompts, bugs, unknown commands — becomes{"error": {code, message, status, exit, fieldErrors, url, traceId, hint, docs}}on stdout with--json, orError: … / Hint: …on stderr. oclif's own JSON error path (the 22 000-line parse-context dump) never runs. Unknown commands go through acommand_not_foundhook. No oclif import in the lib either.BaseCommand.confirmOrFail): destructive commands ask in a terminal as before; in a non-interactive shell (stdout or stdin not a TTY,CIset, or--json) they exit 3 with a one-line message naming--yes, and the request is never sent. "No" at the prompt exits 9 instead of printingCancelled.and exiting 0.--dry-runon every destructive command and onhub/api: the SDK builds the request with the real auth, region and project; a transport middleware stops it just beforefetch; the SDK call and the HTTP request (method, URL, headers with the token redacted, body) are printed; exit 0. A dry run with a missing region or key fails like the real call would.login/configure: every prompt guarded; non-interactive → exit 2 with the three flag/env alternatives, no stack trace, no unsettled-await warning.--json,NO_COLORor a pipe (init hook; there is still no colour library).Tests
tests/agent-mode.test.tsruns the built CLI as a child process (pipes everywhere, like an agent sees it) against a fake gateway on a free port: every destructive command exits 3 without--yesand sends nothing, and runs with--yes;--dry-runprints the call and sends nothing; the envelope and exit code for 404 / 400 withfieldErrors/ 401 / 500 / network / usage / unknown command / bad JSON input / unknown profile /loginwithout a terminal — same exit code with and without--json, stdout one JSON document, stderr empty.tests/cli-error.test.tsunit-tests the mapping. 165 tests green.Reproduction (the §6 harness, after)
🤖 Generated with Claude Code