Skip to content

cli: let flags be written anywhere on the line - #11

Merged
ale-rinaldi merged 1 commit into
mainfrom
fix/position-independent-flags
Sep 5, 2026
Merged

cli: let flags be written anywhere on the line#11
ale-rinaldi merged 1 commit into
mainfrom
fix/position-independent-flags

Conversation

@ale-rinaldi

Copy link
Copy Markdown
Member

Replaces #10, which GitHub closed automatically when its base branch was deleted on merge. Same work, retargeted to main and integrated with everything that landed since.

Answers "can't flags be position-independent?" — yes, everywhere, not just before the subcommand.

What was wrong

dwshell dispatched on os.Args[1], so a flag could only follow its subcommand. The failure mode was not an error:

$ dwshell --config /x list
dwshell: authentication required: run `dwshell login`   # took "list" for a machine name
$ dwshell --json list
flag provided but not defined: -json

The first is the bad one: it did not complain, it quietly did something else.

What it does now

Arguments are partitioned into flags and positionals before anything is dispatched, so the command is the first positional wherever it sits — at the top level and inside both account <verb> and agent <verb>. Everything after a bare -- is positional, so an agent or path starting with a dash can still be passed.

The hazard, and the guard

Telling a flag's value from a positional requires knowing which flags consume a following token, and that registry is the risk: a value flag missing from it makes dwshell take the value for a command name and quietly do the wrong thing.

TestValueFlagsMatchesTheFlagsActuallyRegistered walks the package's own source with go/ast and holds the registry to what is actually registered, in both directions. It has already earned its place three times:

  • it rejected two entries added from memory that no command registered;
  • it surfaced a pre-existing gap — --user was missing, harmless only because login is dispatched by name;
  • during this integration it required --description and --group, the agent flags, which would otherwise have been silently wrong.

Integration with agent management

Merging the two feature branches needed real work, not just conflict resolution:

  • --account added to every agent subcommand, and threaded through agentSession
  • duplicated extractPositional and confirm helpers, defined independently on both branches, reduced to one
  • cmdAgentManage still dispatched on args[0], so dwshell agent --account … group … answered unknown agent subcommand "--account". Caught by live testing after the build was already green — the same fix cmdAccount had.

Verification

Live, against a copy of a real configuration, with all four features together:

form result
--account … agent create … --json creates, prints the code
agent --account … group … <bad group> error listing existing groups
agent rm … --yes --account … deletes
--account … GHE -c "uname -s" Linux
--json list, list --json identical
shell list for a machine named like a command still resolves as a machine
--, help, version, unknown flag unchanged

gofmt, go vet, go test -race ./... green.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MvidAFW9a2r4hTgHPW9ywG

dwshell dispatched on os.Args[1], so a flag could only follow its
subcommand. `dwshell --config x list` did not complain — it took
"list" for a machine name and tried to connect to it, which is worse
than an error, and `dwshell --json list` failed with a puzzling "flag
provided but not defined".

Arguments are now partitioned into flags and positionals before
anything is dispatched, so the command is the first positional wherever
it sits. `dwshell list --account a@b` and `dwshell --account a@b list`
are the same command, and the same holds inside `account <verb>` and
for the shell shortcut.

Telling a flag's value from a positional needs to know which flags
consume a following token, and that registry is the hazard: a value flag
missing from it makes dwshell take the value for a command name and
quietly do the wrong thing. So a test walks the package's own source
with go/ast and holds the registry to the flags actually registered, in
both directions. It earned its place immediately, catching two stale
entries I had added from memory, and it found a real gap: --user was
absent, harmless only because login is dispatched by name.

Everything after a bare -- is positional, so an agent or path beginning
with a dash can still be passed.

Verified live: the reported case, both orders of every flag, a flag
between two positionals, the shell shortcut with the flag on either
side, the explicit `shell <name>` form for a machine named like a
command, help, version, -- and an unknown flag still failing loudly.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01MvidAFW9a2r4hTgHPW9ywG
@ale-rinaldi
ale-rinaldi merged commit b8c54e8 into main Sep 5, 2026
1 check passed
@ale-rinaldi
ale-rinaldi deleted the fix/position-independent-flags branch September 5, 2026 21:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant