Skip to content

feat(cli): discovery — norbix schema, --dry-run everywhere, typed hub/api values and --body - #27

Merged
Domantas merged 13 commits into
mainfrom
feat/agent-native-cli-2
Oct 4, 2026
Merged

Domantas merged 13 commits into
mainfrom
feat/agent-native-cli-2

Conversation

@Domantas

@Domantas Domantas commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

PR 2 of 3 for "Agent-native CLI" (docs/tasks/agent-native-cli.md). Stacked on #26 (feat/agent-native-cli) — the base of this PR is that branch, so the diff shows only this PR's work; merge #26 first, then retarget or merge this one. PR 3 (feat/agent-native-cli-3) stacks on top of this.

What changed

  • norbix schema [command] --json (src/lib/schema.ts, src/commands/schema.ts): every command with args, flags (type, env var, default, global), examples, and the two booleans an agent branches on — destructive (asks for confirmation; exit 3 without a terminal unless --yes) and supportsDryRun. Plus the exit-code table, the contract path and how to discover the dynamic hub/api methods. norbix schema users delete --json for one command, norbix schema db --json for a topic; a compact table without --json. The builder has no oclif import so the MCP server can emit the same document.
  • Request fields from the SDK (scripts/gen-request-fields.mjs → src/generated/request-fields.json, 578 methods): norbix hub <module> --help lists every method with its request fields and types; --json returns them as data (words, destructive, http, path, fields). npm run gen:fields after an SDK bump; the build copies the JSON into dist/.
  • Typed hub/api values: --field value is typed from the matched method's request type — a string field keeps 0042, a boolean flag no longer swallows the word after it (--showArchived tpl_1 → {showArchived: true, id: "tpl_1"}). Explicit types win: --field:str, --field:num, --field:bool, --field:json. --body '<json>' (or -) passes the whole request; mixing it with --field flags is a usage error (exit 2), never a silent merge. disable / block count as destructive verbs, like the named commands.
  • --dry-run on every mutating command (35 more files): SDK calls stop in the transport after auth, region and project are resolved; config set/unset, env use, logout, raw and files publish/unpublish print the file or the HTTP request they would write / send. Read-only commands get no flag.
  • Help examples: real-looking ids everywhere; every mutating command shows the --dry-run pattern, every destructive one the --yes pattern (checked by a test).

Choices (named open in the spec)

  • Typed-value escape: --field:str value (type suffix on the flag name; also --field:str=value). Chosen over --field=@str:value because the value stays untouched and shell-quoting is unchanged.
  • looksLikeId ("v2", "PROD", "SMS" are values, not words) is kept and documented in the scope help — an uppercase token is almost always an environment name or an id, and lowercase words reach every method.
  • files upload is three calls; --dry-run shows the first (the signed-URL request).

Tests

tests/schema.test.ts (lists every file in dist/commands, booleans present, destructive implies dry-run, every mutating command has a --dry-run example and no placeholder values, snapshot), tests/dispatch.test.ts (typing rules), and new agent-mode cases (dry run on users invite, files publish, config set; typed hub fields; --body and its mixing error). 192 tests green; the §6 harness output is unchanged from #26.

🤖 Generated with Claude Code

Domantas and others added 8 commits October 2, 2026 12:36
…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>
Plan step [2] of docs/tasks/agent-native-cli.md.

- scripts/gen-request-fields.mjs reads the installed @norbix.ai/ts (dist/
  index.js for method → route, dist/types/*.d.ts for the request DTO) and
  writes src/generated/request-fields.json: 578 methods with their fields,
  kinds and required flags. `npm run gen:fields` after an SDK bump; the
  build copies it into dist/.
- `norbix hub <module> --help` lists every method with its fields;
  `--json` returns them as data (words, destructive, http, path, fields).
- Values are typed from the map: a string field keeps "0042", a boolean
  flag no longer swallows the word after it (it goes back to being the id).
  Explicit types win: --field:str / :num / :bool / :json.
- --body '<json>' (or -) passes the whole request; mixing it with --field
  flags is a usage error (exit 2), never a silent merge.
- disable / block count as destructive verbs, like the named commands.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…data

Plan step [2] of docs/tasks/agent-native-cli.md.

Every command with its args, flags (type, env var, default, global),
examples, and two booleans an agent branches on: `destructive` (asks for
confirmation; exit 3 without a terminal unless --yes) and `supportsDryRun`.
Plus the exit-code table, the contract path and how to discover the
dynamic hub/api methods. Without --json: a compact table. The builder in
src/lib/schema.ts has no oclif import so the MCP server can emit the same
document.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Plan step [2] of docs/tasks/agent-native-cli.md.

- Every command that changes something takes --dry-run: SDK calls are
  stopped in the transport (auth, region and project resolved first);
  config set/unset, env use, logout, raw and files publish/unpublish print
  the file or the HTTP request they would write / send. Read-only commands
  get no flag.
- Every example uses real-looking ids; every mutating command shows the
  --dry-run pattern and every destructive one the --yes pattern.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Plan step [2] of docs/tasks/agent-native-cli.md.

- schema: lists every file in dist/commands, booleans present, destructive
  implies dry-run, every mutating command has a --dry-run example and no
  placeholder values, one command / one topic / unknown, snapshot of ids,
  args, flags and markers.
- dispatch: string keeps zeros, number rejects text, boolean gives the next
  token back, explicit types, JSON fields, destructive verbs.
- agent mode: dry run on users invite, files publish (raw hub request),
  config set (file untouched), hub typed fields, --body and its mixing
  error.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Domantas and others added 4 commits October 3, 2026 16:59
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>
…/agent-native-cli-2

# Conflicts:
#	src/commands/raw.ts
… sms, project)

Merging main brought in 115 commands written before the contract. They now
follow it the same way the older commands do:

- destructive ones (delete, disable, revoke-key, cors remove) take
  mutatingFlags and call confirmOrFail — exit 3 without --yes when there is
  no terminal, and --dry-run. The four integration disables and cors remove
  had no confirmation at all.
- every other mutating command takes dryRunFlags and has a --dry-run example.
- examples carry real-looking IDs instead of `66b2f0a1...`.
- ProjectCommand.confirmOrStop is gone (confirmOrFail replaces it), and
  projectClient also returns `reader`: in a dry run the read before a write
  (current origins, current AI settings) goes out live and only the write is
  stopped, so the printed request is the change itself.

Tests: --yes added where the commands now confirm, the destructive ones
added to agent-mode's DESTRUCTIVE list, schema snapshot regenerated (115
entries added, none of the existing ones changed).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Domantas
Domantas changed the base branch from feat/agent-native-cli to main October 3, 2026 14:21
@Domantas
Domantas merged commit 7824310 into main Oct 4, 2026
11 checks passed
@Domantas
Domantas deleted the feat/agent-native-cli-2 branch October 4, 2026 06:12
@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 1.7.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant