Jeroen's shape for the tooling CLI: move the Python into a package with a
proper domain split, one door that answers everything with help, and errors
that hand back instructions rather than a status.
The domain split turns out to be discoverable rather than invented. tooling/ is
85 top-level entries — 37 loose .py, ~36 extensionless executables, 11 dirs of
which only 6 hold anything — across four coexisting naming conventions. But the
domains are already encoded as filename prefixes: blender x14, atlas x8,
generate x7, check x7, then visual/validate/test x3 and
godot/garment/pql/install x2. Those prefixes are the subcommand groups, which
is what makes the consolidation mechanical enough to be safe.
Two constraints recorded against "a new prompt not an error code", because
taken literally each would break something:
- Exit codes stay. Four of these run in the pre-push hook, which fails a push
ONLY by non-zero exit; a tool that explains itself and exits 0 silently
disables its own gate. That exact failure was observed in clide today, where
unsupported-format, no-such-file and unknown-subsystem all returned 0.
So: code AND message, never either/or.
- It must not become literally interactive. Agents and git hooks have no TTY,
and the tea scar is already written down — its prompts "crash in Claude Code
(no TTY)", which is why every tea call passes all flags explicitly. Any
prompt must be TTY-gated and suppressible.
pql was cited as the precedent and measured rather than assumed. The principle
holds there for unknown subcommands (full usage dump) and not for invalid
values: `ticket status <id> nonsense` says invalid without naming the six legal
values it knows, `ticket new` says "accepts 2 arg(s)" without naming which two.
The gap is the closed sets, and it is the more common failure. Logged upstream
as pql T-112 rather than worked around here — the bar for our CLI is the
stronger one: whenever the accepted set is known, print it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>