You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The CLI mixes three command shapes. daemon status|stop is noun then verb. ls, revoke, link, invoke are bare verbs. tools [selector] [name] and events [selector] are nouns whose positional argument doubles as a selector, so listing and acting share one word and the first positional is ambiguous (packages/appduct/src/cli/command-options.ts:128). The cost lands in three places:
Agents cannot guess a command from its neighbours. Knowing appduct tools says nothing about appduct ls or appduct invoke, so the shipped skill has to spell out every form.
New listings have nowhere natural to go. Let apps declare the events they post, and let agents list them #95 needs a flag, appduct events --list, because a bare appduct events ls would parse ls as a session selector. appduct events --since is likewise a mode switch on a streaming command.
Selector placement differs per command, so --help output is the only way to learn each one.
Expected outcome
Every command is appduct <noun> <verb> [selector] [args], with daemon as the model:
appduct sessions ls | revoke [selector] | link [...] replacing ls, revoke and link.
daemon, init, keygen, doctor and mcp keep their shape; the last four are one-verb nouns already.
This is a clean break, with no aliases: we are pre-1.0. A removed form fails with a usage error that names its replacement, so an agent working from an old prompt can correct itself in one step. --json data does not change, so scripts that parse output only need the command renamed. The shipped skill, the READMEs, website/, docs/ and the CLI's own hints all switch to the new forms.
Shape: routes/sessions/, routes/tools/ and routes/events/ become routers like routes/daemon/, one route per verb. tools describe and tools call take [selector] <name> through splitSelectorAndRequiredTarget, and splitOptionalSelectorAndTarget is deleted. cac matches only the first word and reads flag types from that command, so each noun command in create-cli.ts declares every option its verbs use. The keys in output.ts's renderSuccessData become the new paths. The root router's unknown-command error carries a map from each removed form to its replacement.
Constraints and non-goals
No RPC or daemon change: this is cli/ routing and docs only.
Exit codes, --json data, global flags and selector semantics stay as they are. Under --verbose, meta.command reports the new name ("tools ls").
Non-goal: renaming the MCP tools (appduct_list_tools and friends already follow a verb-noun pattern of their own).
Non-goal: changing what any command does. That rules out sessions describe here; file it separately if it's wanted.
appduct sessions ls, sessions revoke [selector], sessions link [flags], tools ls [selector] [flags], tools describe [selector] <name>, tools call [selector] <name> --input '{}', events tail [selector] and events since [selector] <cursor> each produce the same stdout, exit code and --json data as the command they replace. SIGINT during tools call cancels the call as it does for invoke today, and events since prints the trailing cursor line.
appduct ls, revoke, link and invoke exit 64 with a message naming the new form, e.g. Unknown command "ls"; use "appduct sessions ls".
appduct sessions, appduct tools and appduct events with no verb, or with an unknown verb, exit 64 naming that noun's verbs, like appduct daemon does. With --help they print the verbs and exit 0.
A boolean flag placed before a positional does not swallow it: appduct tools ls --full <selector> and appduct sessions link --qr --open ios-sim parse as intended.
The shipped skill, both READMEs, website/, docs/ (including ARCHITECTURE.md §10) and the CLI's own hints (output.ts, commands/init.ts) use only the new forms, and the CLI integration tests run against them.
A Breaking changelog entry maps each old form to its new one.
Alternatives considered
Keep bare verbs and add flags per listing (events --list): works, but leaves three command shapes and the positional ambiguity.
Deprecated aliases for a release: rejected. We are pre-1.0, and aliases would need a fallback router, legacy routes and a notice that stays silent under --json, all for one release.
Verb-noun (appduct list tools): reads well but does not match daemon status, the one noun-verb command already shipped.
Why
The CLI mixes three command shapes.
daemon status|stopis noun then verb.ls,revoke,link,invokeare bare verbs.tools [selector] [name]andevents [selector]are nouns whose positional argument doubles as a selector, so listing and acting share one word and the first positional is ambiguous (packages/appduct/src/cli/command-options.ts:128). The cost lands in three places:appduct toolssays nothing aboutappduct lsorappduct invoke, so the shipped skill has to spell out every form.appduct events --list, because a bareappduct events lswould parselsas a session selector.appduct events --sinceis likewise a mode switch on a streaming command.--helpoutput is the only way to learn each one.Expected outcome
Every command is
appduct <noun> <verb> [selector] [args], withdaemonas the model:appduct sessions ls | revoke [selector] | link [...]replacingls,revokeandlink.appduct tools ls [selector] [--full|--groups|--group|--filter|--limit|--offset] | describe [selector] <name> | call [selector] <name> --input ... [--timeout]replacingtools,tools <name>andinvoke.appduct events tail [selector] | since [selector] <cursor>replacingeventsandevents --since.events lsis reserved for the catalog from Let apps declare the events they post, and let agents list them #95.daemon,init,keygen,doctorandmcpkeep their shape; the last four are one-verb nouns already.This is a clean break, with no aliases: we are pre-1.0. A removed form fails with a usage error that names its replacement, so an agent working from an old prompt can correct itself in one step.
--jsondata does not change, so scripts that parse output only need the command renamed. The shipped skill, the READMEs,website/,docs/and the CLI's own hints all switch to the new forms.Shape:
routes/sessions/,routes/tools/androutes/events/become routers likeroutes/daemon/, one route per verb.tools describeandtools calltake[selector] <name>throughsplitSelectorAndRequiredTarget, andsplitOptionalSelectorAndTargetis deleted. cac matches only the first word and reads flag types from that command, so each noun command increate-cli.tsdeclares every option its verbs use. The keys inoutput.ts'srenderSuccessDatabecome the new paths. The root router's unknown-command error carries a map from each removed form to its replacement.Constraints and non-goals
cli/routing and docs only.--jsondata, global flags and selector semantics stay as they are. Under--verbose,meta.commandreports the new name ("tools ls").appduct_list_toolsand friends already follow a verb-noun pattern of their own).sessions describehere; file it separately if it's wanted.--nameonevents tail/since, which Filter app events by name glob and bound payloads on every surface #94 adds to the new verbs.appduct events ls.How we know it is done
appduct sessions ls,sessions revoke [selector],sessions link [flags],tools ls [selector] [flags],tools describe [selector] <name>,tools call [selector] <name> --input '{}',events tail [selector]andevents since [selector] <cursor>each produce the same stdout, exit code and--jsondata as the command they replace. SIGINT duringtools callcancels the call as it does forinvoketoday, andevents sinceprints the trailing cursor line.appduct ls,revoke,linkandinvokeexit 64 with a message naming the new form, e.g.Unknown command "ls"; use "appduct sessions ls".appduct sessions,appduct toolsandappduct eventswith no verb, or with an unknown verb, exit 64 naming that noun's verbs, likeappduct daemondoes. With--helpthey print the verbs and exit 0.appduct events lsexits 64 namingtailandsince.appduct tools ls --full <selector>andappduct sessions link --qr --open ios-simparse as intended.website/,docs/(includingARCHITECTURE.md§10) and the CLI's own hints (output.ts,commands/init.ts) use only the new forms, and the CLI integration tests run against them.Alternatives considered
events --list): works, but leaves three command shapes and the positional ambiguity.--json, all for one release.appduct list tools): reads well but does not matchdaemon status, the one noun-verb command already shipped.