Skip to content

Complete command arguments from the manifest; widen the platform filter - #243

Merged
jasperf merged 5 commits into
mainfrom
feature/cli-arg-completion-platform-filter
Sep 21, 2026
Merged

jasperf merged 5 commits into
mainfrom
feature/cli-arg-completion-platform-filter

Conversation

@jasperf

@jasperf jasperf commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Closes tier 1 of docs/cli-ux-plan.md Phase G: G3 (argument completion) and G6 (--platform semantics). Released as 5.26.0.

G3 — argument completion from the manifest

Shell completion stopped at the command name. wp-ops db-pull <TAB> offered nothing, even though the script header declares site and env with {production|staging} — the CLI had that parsed and indexed and never put it on screen.

It now completes the argument slots too, on all three invocation forms (bare basename, category + name, full key), resolving the command the same way execution does. An ambiguous basename completes nothing: the entries behind it may declare different arguments, and it wouldn't run either.

What a slot offers depends on what the manifest knows about it:

Slot Offers
@arg with {a|b} choices those values, described
site / site-name site names from the detected Trellis project's group_vars/*/wordpress_sites.yml
a path-shaped name (input, *-file, *-dir, …) hands back to the shell's own file completion
anything else one line of ActiveHelp: name, required/optional, description
a token starting with - the command's @flag names, plus --help and --where

Three decisions worth flagging for review:

  • A bracketed manifest value is never offered as a completion. {example.com}, {~/wp-cli.phar}, {/opt/plesk/php/8.2/bin/php} are placeholders showing the shape of an answer, not defaults. Inserting one would be worse than offering nothing, so they appear as "e.g." inside the hint.
  • Site names are read from the project silently. resolveTrellisDir() confirms a detected directory interactively, which a completion function must never do, so completionSites() takes $TRELLIS_DIR or a silently detected project. internal/detect/sites.go reads it with a line scanner rather than pulling a YAML dependency into the binary for four lines of well-known structure — a stopgap until M5's shared site registry exists.
  • Flags are skipped, not parsed, when counting slots. wp-ops re-parses no script's flag grammar (DisableFlagParsing everywhere), so it cannot know whether the token after --host is that flag's value or the next positional. Counting only non-flag tokens is right for the common cases and, in the --flag value positional case, offers the previous slot — wrong in a way that costs a keystroke, not a mistake, since nothing is inserted without the user picking it.

Two assertions in dispatch_test.go pinned the old two-token grammar ("no completions once a basename is already chosen"). They now pin the unresolvable-name case, with a comment recording that the reversal is deliberate.

G6 — --platform trellis includes the commands that need no WordPress

FilterByPlatform matched @platform exactly, so --platform trellis returned the 29 Trellis commands and hid the 32 tagged any — the image converters, git helpers and release scripts that run perfectly well on a Trellis box. The flag reads as "what can I run here?", and that answer was wrong by 32 commands. It now returns 61; --platform wordpress returns 51.

The widening stops there. Rolling wordpress into a trellis filter would return the whole catalog, at which point the flag says nothing. --platform any stays exact, since "what needs no WordPress at all" is a real question and that is the only way left to ask it — which is why the separate --platform-only the plan floated wasn't needed.

catalog.RunsOnPlatform holds the relation in one place, shared with list.go's per-category filterEntriesByPlatform so the category view and the summary counts can't disagree. search keeps its [platform] badge under --platform now: a filtered result set mixes [trellis] rows with [any] ones, so the badge still distinguishes them. The listing scope under trellis ops widens with it, via defaultPlatform().

Verification

go vet and go test ./... green. Beyond the unit tests, completion was driven through a real interactive zsh over a pty against the installed _wp-ops script:

  • wp-ops db-pull <TAB> → the project's site names, with the argument's description
  • wp-ops db-pull example.com <TAB> → production staging
  • wp-ops jpg-to-webp <TAB> → the hint above an ordinary file menu

The installed completion script already supports ActiveHelp, so no reinstall is needed to see the hints.

--platform trellis matched entry.Platform exactly, so it returned the 29
Trellis commands and hid the 32 tagged @platform any — the image
converters, git helpers and release scripts that run perfectly well on a
Trellis box. The flag reads as "what can I run here?", and that answer
was wrong by 32 commands. Same for --platform wordpress, and for the
listing scope under `trellis ops`, which takes the same path.

The widening stops at "any". Rolling wordpress into a trellis filter
would return the whole catalog, at which point the flag says nothing.
--platform any stays exact, since "what needs no WordPress at all" is a
real question and this is the only way left to ask it.

catalog.RunsOnPlatform now holds that relation in one place, shared by
FilterByPlatform and list.go's per-category filterEntriesByPlatform so
the category view and the summary counts can't disagree. search keeps
its [platform] badge under --platform now too: a filtered result set
mixes trellis rows with any ones, so the badge still distinguishes them.
Shell completion stopped at the command name. `wp-ops db-pull <TAB>`
offered nothing, even though the script header two directories away
declares `site` and `env` with {production|staging} — the CLI had the
answer parsed and indexed and never put it on screen.

It now completes the argument slots too, on all three invocation forms
(bare basename, category + name, full key), resolving the command the
same way execution does. What each slot offers depends on what the
manifest knows:

- declared {a|b} choices become the completion values;
- `site` / `site-name` complete from the group_vars of the Trellis
  project in front of you, since that answer lives in the project rather
  than in the script — silently detected, never prompting, because a
  completion function must not ask questions;
- a slot that holds a path hands back to the shell's own file
  completion;
- anything else gets one line of ActiveHelp naming the argument, whether
  it is required, and what it means.

A bracketed manifest value is deliberately never offered as a value.
{example.com}, {~/wp-cli.phar} and {/opt/plesk/php/8.2/bin/php} are
placeholders showing the shape of an answer; inserting one as though it
were a default would be worse than offering nothing, so they appear as
"e.g." inside the hint instead.

Typing a dash offers the command's own @Flag lines plus --help and
--where, which executeEntry handles for every command.

Flags are skipped rather than parsed when counting which slot is being
filled: wp-ops re-parses no script's flag grammar, so it cannot know
whether the token after --host is that flag's value or the next
positional. The approximation is right for the common cases and costs a
keystroke, not a mistake, in the rest.

Two assertions in dispatch_test.go said completion must stop once a
basename is present. That was the old grammar's contract; they now pin
the unresolvable-name case instead, with a note recording the reversal.
Tier 1 of Phase G is closed. Replaces both items' "not started" notes
with what was actually built and why: the slot-by-slot completion table,
the three decisions worth carrying forward (placeholders are never
offered as values, site names come from the project silently until M5's
registry exists, flags are skipped rather than parsed), and the reason
G6's floated --platform-only turned out not to be needed.

Also corrects a figure in the G6 write-up: the honest answer for a
Trellis user is 61 of 80, not the 41 the item guessed at.
Both are user-facing surfaces the README already describes one level
short of: it covered `wp-ops init` installing completion without saying
what now completes, and described --platform without saying that trellis
and wordpress admit the commands that need no WordPress.
@jasperf
jasperf merged commit 522fbd8 into main Sep 21, 2026
1 check passed
@jasperf
jasperf deleted the feature/cli-arg-completion-platform-filter branch September 21, 2026 02:58
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