Decision: Restore the top-level abx init shortcut, make bare abx create an explicit error, and add unknown-command hints
Status: implemented
Implemented: 2026-09-29
Supersedes (partial): 2026-09-29-init-create-semantics.md — decision item 1 (delete abx init) is reversed; the create routing row "abx create → base template, direct create" is replaced by an explicit usage error.
Problem
The earlier init/create convergence (ADR 2026-09-29-init-create-semantics.md) removed the top-level abx init alias and let bare abx create silently launch the base template. Follow-up review (tasks 206/210/211/218) surfaced three issues:
- The alias removal hurt real usage patterns.
abx initmaps naturally to "scaffold my project", the most common first action after install. The alias costs nothing (it is the same click command object) and its earlier confusion is better solved by help text than by removal. - Bare
abx createstill silently chose a path. The convergence made DESCRIPTION +--templateexplicit but kept the implicitbasedefault for the no-argument case — inconsistent with the fail-loud convention. - Unknown top-level commands gave no guidance. Typos produced a bare
No such command 'x'., and users who expectedabx <their-command>to work (templatecustom_commands, SandboxServer registry commands) were not pointed atabx run.
Decision
- Restore the top-level
abx initshortcut. src/agent_sandbox/cli/main.py: register"init": "agent_sandbox.cli.commands.template:init"inlazy_subcommands.- It is the same command object as
abx template init— option surface, TTY picker, and scaffolding logic are shared; nothing is duplicated. -
The three entry points stay distinct:
abx config init(credentials),abx create(cloud sandbox),abx init/abx template init(scaffold). -
Bare
abx createis now an explicit usage error (exit code 2) listing the three valid routes:--template base,--template <NAME>,"<DESCRIPTION>". No config read, AI generation, or network call happens. -
Unknown top-level commands get targeted hints (root group only):
difflib.get_close_matches(cutoff 0.6) over root commands yields aDid you mean '…'?suggestion; with no plausible candidate the error points to templatecustom_commands(declared intemplate.yaml) invoked viaabx run COMMAND. ClickUsageErrorsemantics are preserved: exit code 2, message on stderr; nested groups keep the stock message. -
Registration semantics clarified (docs, not code change):
- The
LazyGroup(lazy_subcommands=...)map inmain.pyis the project-maintainer registration point for built-in top-level shortcuts. It is not a user-facing extension mechanism. - User-defined commands live in
template.yamlcustom_commandsor the SandboxServer@registry.commandregistry and run viaabx run. -
The
config.toml [shortcuts]section from the original CLI design draft (.agents/design/2026-09-23-cli-final-design.md§2.2) was never implemented; it must not be described as a current capability. -
Evidence / docs sync:
error-init-removedevidence case replaced byerror-create-noargs,error-unknown-suggestion,error-unknown-custom-hint, andhelp-init-alias; golden snapshots and.agents/evidence/cli/*regenerated; cli-reference, cli-design, cli-tutorial (incl. new bilingual "top-level shortcuts vs custom commands" advanced-usage section), e2e-template-workflow and CHANGELOG updated (en+zh).
Alternatives considered
- Keep the alias removed and rely on
abx template init— rejected: the alias is free (same command object) and matches user intuition. - Keep bare
abx create→basefor quick start — rejected: silent defaults contradict the CLI-wide fail-loud convention and hide the routing model;abx create --template baseis one flag away. - Implement
config.toml [shortcuts]— rejected (still): two competing extension mechanisms (custom_commands+ Server registry already cover the need); a third, config-file-based mechanism would split where commands are discovered.
Dependencies
- Click
LazyGroupregistration andresolve_commandoverride insrc/agent_sandbox/cli/main.py. handle_errorsexit-code mapping (UsageError → exit 1/2 preserved).
Test Strategy
tests/test_cli/test_main.py— init present again, delegating to the same command object; unknown-command suggestion and custom-command hint cases.tests/test_cli/test_create_nl.py/test_sandbox_commands.py— bare create rejected before any side effect.tests/test_cli/test_template_commands.py— TTY arrow-key picker vs non-TTY/CI/JSON fail-fast behaviour unchanged.- Evidence: registry cases above;
capture_cli_evidence.py+ golden snapshots regenerated (98 cases).
Files changed
scripts/evidence_cases.py;tests/test_cli_evidence/golden/*;.agents/evidence/cli/*- Docs (en/zh):
reference/cli-reference.md,design/cli-design.md,guide/cli-tutorial.md,guide/e2e-template-workflow.md;CHANGELOG.md - This ADR + supersede annotation on
2026-09-29-init-create-semantics.md
Acceptance criteria
- ✅
abx init --helprenders thetemplate inithelp;abx init -t pythonscaffolds identically toabx template init -t python - ✅ Bare
abx createexits 2 with the three-route hint; no side effects - ✅
abx crateexits 2 withDid you mean 'create'?;abx frobnicateexits 2 pointing tocustom_commands+abx run; both hints on stderr only - ✅ TTY arrow-key case picker; non-TTY/CI/JSON never block
- ✅ Docs state
lazy_subcommandsis the maintainer registration point and markconfig.toml [shortcuts]as never-implemented - ✅ Evidence tests, ruff pass; stale goldens removed