2026-11-03 03:32:18 +00:00
|
|
|
|
# Shell completion implementation plan
|
|
|
|
|
|
|
2026-11-03 03:50:12 +00:00
|
|
|
|
Status: Stages 1–3 complete. Stage 4 is next.
|
2026-11-03 03:32:18 +00:00
|
|
|
|
|
|
|
|
|
|
Implement the [shell completion roadmap](completion.md) from baseline
|
|
|
|
|
|
`2301872`. Follow the stages in order in one sprint. Intermediate stages may
|
|
|
|
|
|
leave unfinished wiring or a noncompiling tree; validate the assembled result
|
|
|
|
|
|
before calling the sprint complete. Use the existing ownership boundaries in
|
|
|
|
|
|
[ADR 0013](../adr/0013-application-alias-routing.md),
|
|
|
|
|
|
[ADR 0014](../adr/0014-invocation-reporting.md), and the
|
|
|
|
|
|
[architecture policy](../policy/architecture.md). Apply the
|
|
|
|
|
|
[testing policy](../policy/testing.md) to integration assertions. Keep protocol
|
|
|
|
|
|
names, candidate ordering, and backend mechanics private.
|
|
|
|
|
|
|
|
|
|
|
|
## Stage 1: Prove the shell transport
|
|
|
|
|
|
|
2026-11-03 03:45:47 +00:00
|
|
|
|
Status: complete.
|
|
|
|
|
|
|
2026-11-03 03:32:18 +00:00
|
|
|
|
1. Use the Cobra version pinned in the root module. In a disposable prototype,
|
|
|
|
|
|
generate scripts with `GenBashCompletionV2(w, true)`,
|
|
|
|
|
|
`GenZshCompletion(w)`, and `GenFishCompletion(w, true)`. Give the generator
|
|
|
|
|
|
a minimal root whose name is the installed executable basename. Do not
|
|
|
|
|
|
install Cobra's completion command or execute its completion parser.
|
|
|
|
|
|
2. Supply a fixture executable that handles the generated scripts' hidden
|
|
|
|
|
|
request (`__complete` with these generator settings). Verify the actual
|
|
|
|
|
|
arguments and replies using each script in its shell. The request carries
|
|
|
|
|
|
the words through the cursor, including the partial word as its last
|
|
|
|
|
|
argument; preserve an explicitly empty last argument. Check what each
|
|
|
|
|
|
script does with the prefix of an attached value before deciding how to
|
|
|
|
|
|
encode its replacement. Cover `--config=./par`, separated path values,
|
|
|
|
|
|
spaces, an empty no-file reply, directory-only completion, and a partial
|
|
|
|
|
|
short cluster. Verify the edited line, not only the printed candidate list.
|
|
|
|
|
|
3. Use Cobra's line protocol: candidate lines with optional tab-separated
|
|
|
|
|
|
descriptions, followed by the colon-prefixed directive integer and newline.
|
|
|
|
|
|
Use the adapter's Cobra directive constants, not hardcoded numbers in core.
|
|
|
|
|
|
Map command/flag/static choices and no candidates to NoFileComp, file hints
|
|
|
|
|
|
to native file completion, and directory hints to FilterDirs. If a generated
|
|
|
|
|
|
script cannot express the selected behavior as emitted, make the smallest
|
|
|
|
|
|
adapter-owned script adjustment that preserves it and add a shell regression.
|
|
|
|
|
|
In particular, verify attached-value prefix reconstruction and Fish's
|
|
|
|
|
|
filesystem handling. Do not narrow the supported contexts to fit a script.
|
|
|
|
|
|
4. Record the observed argument and replacement conventions in the sprint
|
|
|
|
|
|
evidence. Carry forward only the adapter and fixtures needed for the final
|
|
|
|
|
|
implementation; do not commit disposable prototype machinery.
|
|
|
|
|
|
|
2026-11-03 03:45:47 +00:00
|
|
|
|
Completion: generated scripts using Cobra v1.10.1 and exercised them through
|
|
|
|
|
|
interactive Bash 5.3.9, Zsh 5.9, and Fish 4.2.1 with a fixture that records the
|
|
|
|
|
|
query and the executed edited line. In the tested attached-value cases the
|
|
|
|
|
|
query includes the entered flag and value; a static candidate contains the value
|
|
|
|
|
|
alone, and the shell script preserves the flag prefix. Explicit empty words,
|
|
|
|
|
|
quoted prior arguments, short clusters, file and directory hints, and no-file
|
|
|
|
|
|
replies were exercised. Zsh's generated word splitting lost the attached flag
|
|
|
|
|
|
context when a path contained an escaped space. Fish split quoted prior words,
|
|
|
|
|
|
passed escapes in current words, and treated directory-only replies as ordinary
|
|
|
|
|
|
file completion. The adapter adjusts those scripts and preserves the first `=`
|
|
|
|
|
|
as the flag/value separator. The opt-in `TestCompletionShells` regression checks
|
|
|
|
|
|
actual edited lines for these cases. Run it with Bash, Zsh, Fish, Python pexpect,
|
|
|
|
|
|
and Bash completion installed using `COMMANDKIT_COMPLETION_SHELLS=1
|
|
|
|
|
|
GOWORK=off GOTOOLCHAIN=local go test -count=1 -run '^TestCompletionShells$'
|
|
|
|
|
|
./internal/cobra`. Zsh and Fish were extracted into a disposable directory for
|
|
|
|
|
|
this run; the focused adapter suite and the full interactive matrix pass.
|
|
|
|
|
|
|
2026-11-03 03:32:18 +00:00
|
|
|
|
## Stage 2: Capture completion hints
|
|
|
|
|
|
|
2026-11-03 03:47:23 +00:00
|
|
|
|
Status: complete.
|
|
|
|
|
|
|
2026-11-03 03:32:18 +00:00
|
|
|
|
1. Add the following public API in the root package. Add
|
|
|
|
|
|
`Completion CompletionHint` to both `FlagMeta` and `ArgumentMeta`;
|
|
|
|
|
|
existing binding constructors and their signatures stay in place.
|
|
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
|
type CompletionChoice struct {
|
|
|
|
|
|
Value string
|
|
|
|
|
|
Description string
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type CompletionHint struct { /* private representation */ }
|
|
|
|
|
|
|
|
|
|
|
|
func CompleteChoices(choices ...CompletionChoice) CompletionHint
|
|
|
|
|
|
func CompleteFiles() CompletionHint
|
|
|
|
|
|
func CompleteDirectories() CompletionHint
|
|
|
|
|
|
func NoCompletion() CompletionHint
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
2. Represent the four strategies as a private discriminator and choice list.
|
|
|
|
|
|
The zero hint and `NoCompletion()` are equivalent. Copy choice slices
|
|
|
|
|
|
when constructing the hint. Capture the hint through ordinary bindings,
|
|
|
|
|
|
repeatable bindings, and `CaptureFlags`; bridge it through
|
|
|
|
|
|
`bridgeFlags` and `bridgeArguments` to neutral metadata. Use neutral
|
|
|
|
|
|
strategy and choice types in `internal/commandmeta`; backend types do not
|
|
|
|
|
|
cross the adapter boundary.
|
|
|
|
|
|
3. Keep hints independent of binding identity, requiredness, defaults,
|
|
|
|
|
|
repetition, and converters. Static choices are literal prefix matches and
|
|
|
|
|
|
do not constrain accepted values. Empty choices mean no suggestions, not
|
|
|
|
|
|
file fallback. Use ordinary boolean literals `true` and `false` for
|
|
|
|
|
|
the built-in boolean assignment case; an application boolean binding
|
|
|
|
|
|
without a hint has no value suggestions.
|
|
|
|
|
|
4. Verify caller mutation of the original choice slice cannot change captured
|
|
|
|
|
|
suggestions, repeated/captured bindings keep hints, and values absent from
|
|
|
|
|
|
a suggestion list still reach the existing converter.
|
|
|
|
|
|
|
2026-11-03 03:47:23 +00:00
|
|
|
|
Completion: added the public choice and hint constructors, carried hints through
|
|
|
|
|
|
flag and operand bindings into neutral metadata, and supplied explicit boolean
|
|
|
|
|
|
choices for built-in help and reporting flags. Focused tests cover copied
|
|
|
|
|
|
choices, repeated and captured bindings, the empty-choice strategy, independent
|
|
|
|
|
|
bridge copies, and execution of values outside the suggested choices. Root,
|
|
|
|
|
|
internal adapter, and neutral metadata package tests pass with `GOWORK=off` and
|
|
|
|
|
|
`GOTOOLCHAIN=local`.
|
|
|
|
|
|
|
2026-11-03 03:32:18 +00:00
|
|
|
|
## Stage 3: Install completion as an ordinary declaration
|
|
|
|
|
|
|
2026-11-03 03:50:12 +00:00
|
|
|
|
Status: complete.
|
|
|
|
|
|
|
2026-11-03 03:32:18 +00:00
|
|
|
|
1. Add `func Completion(executable string) Command`. The parameter is the
|
|
|
|
|
|
executable basename the operator types, independent of the human-readable
|
|
|
|
|
|
`Identity.Name`. Use a root-only `completion` group with three runnable
|
|
|
|
|
|
children, `bash`, `zsh`, and `fish`. Use the existing declaration and
|
|
|
|
|
|
registration API (`New(..., Completion("app"))` or
|
|
|
|
|
|
`app.Register(Completion("app"))`). Keep script-specific name handling
|
|
|
|
|
|
inside completion; do not change validation of ordinary application identity.
|
|
|
|
|
|
2. Carry a private completion-installation marker with the declaration and
|
|
|
|
|
|
derive query availability from the committed routing snapshot. Use the
|
|
|
|
|
|
existing registration candidate to validate collisions in either order,
|
|
|
|
|
|
including aliases and same-batch declarations. Reserve the query's first
|
|
|
|
|
|
token only while installed: a canonical command or alias beginning with
|
|
|
|
|
|
that token would be shadowed by raw query dispatch. Reuse existing
|
|
|
|
|
|
command/alias collision rules for `completion`. Do not represent the
|
|
|
|
|
|
query as a canonical node, alias target, or public help entry.
|
|
|
|
|
|
3. Generate scripts in the internal adapter using the transport established
|
|
|
|
|
|
in Stage 1. Surface generator errors through ordinary presentation.
|
|
|
|
|
|
Use `Reporter.RenderOutput` for script generation and delivery, so
|
|
|
|
|
|
`app completion bash --quiet` follows normal reporting. Document the
|
|
|
|
|
|
basename expected by the generated scripts; handle unsupported generator
|
|
|
|
|
|
names at the completion boundary.
|
|
|
|
|
|
4. Verify opt-in behavior, root-only placement, help visibility, batch
|
|
|
|
|
|
rollback, late installation, and absence of new reserved names for an app
|
|
|
|
|
|
that does not install completion.
|
|
|
|
|
|
|
2026-11-03 03:50:12 +00:00
|
|
|
|
Completion: added the root-only completion declaration and three script
|
|
|
|
|
|
commands, with generator output delivered through the invocation reporter.
|
|
|
|
|
|
Registration validates hidden-query collisions in either order, and the routing
|
|
|
|
|
|
snapshot reflects committed installation. Tests cover help, quiet output,
|
|
|
|
|
|
delivery failure, invalid basenames, opt-in behavior, collisions, rollback,
|
|
|
|
|
|
nesting, and late installation. Fish variable names are adjusted to support
|
|
|
|
|
|
dotted executable basenames; interactive checks of those names pass in Bash,
|
|
|
|
|
|
Zsh, and Fish. Root and adapter package tests pass with `GOWORK=off` and
|
|
|
|
|
|
`GOTOOLCHAIN=local`.
|
|
|
|
|
|
|
2026-11-03 03:32:18 +00:00
|
|
|
|
## Stage 4: Resolve the entered command context
|
|
|
|
|
|
|
|
|
|
|
|
1. Add a private core completion request that separates completed words from
|
|
|
|
|
|
the current word. Call `resolveEnteredRoute` on completed words only;
|
|
|
|
|
|
use the snapshot's canonical metadata, resolved flag-alias context, and
|
|
|
|
|
|
discovery context. Keep entered words unchanged. For canonical routes
|
|
|
|
|
|
with a runnable operand owner, use the existing canonical prefix length
|
|
|
|
|
|
to distinguish its trailing operands/options from command words.
|
|
|
|
|
|
2. Suggest canonical child names matching the partial word while the input
|
|
|
|
|
|
is still in command-path position. At a node with both children and
|
|
|
|
|
|
operands, matching children take precedence; if there are no matching
|
|
|
|
|
|
children, use the operand context when available. Check insertion against
|
|
|
|
|
|
normal routing: resolve completed entered words plus the candidate and
|
|
|
|
|
|
retain it only if it reaches the intended canonical child. Do not treat a
|
|
|
|
|
|
partial alias as already entered. After a flag or operand has ended the
|
|
|
|
|
|
command path, do not suggest children.
|
|
|
|
|
|
3. An alias-only discovery prefix provides no command continuations, but can
|
|
|
|
|
|
supply applicable canonical flags using discovery metadata and alias
|
|
|
|
|
|
mappings. Unknown routes with no valid context yield no candidates.
|
|
|
|
|
|
At root, use the installed visible commands, including built-ins and
|
|
|
|
|
|
integrations. Exclude the protocol endpoint everywhere.
|
|
|
|
|
|
4. Add context tests for root/group/runnable paths, a partial canonical
|
|
|
|
|
|
child, a complete alias and alias chain, an alias-only prefix, a canonical
|
|
|
|
|
|
child shadowed in the entered route, and flags beginning before the
|
|
|
|
|
|
complete command path. Use existing routing rules as the oracle.
|
|
|
|
|
|
|
|
|
|
|
|
## Stage 5: Interpret flags and locate value positions
|
|
|
|
|
|
|
|
|
|
|
|
1. Walk the completed tail using the same canonical and alias spellings and
|
|
|
|
|
|
flag token rules as normal parsing. Factor shared token classification
|
|
|
|
|
|
from `internal/cobra/flags.go` into neutral helpers where needed; keep
|
|
|
|
|
|
backend parsing and failure wording at their current owner. The completion
|
|
|
|
|
|
walk records supplied canonical flags, consumed operands, whether options
|
|
|
|
|
|
ended, and any pending value flag. It does not run Cobra dispatch,
|
|
|
|
|
|
converters, required checks, input factories, or providers.
|
|
|
|
|
|
2. A pending value takes precedence even if the current word starts with
|
|
|
|
|
|
a dash. For long equals forms, explicit boolean assignments, and short
|
|
|
|
|
|
value flags in clusters, separate the value prefix from the entered
|
|
|
|
|
|
spelling and separator. Preserve that spelling, including an alias.
|
|
|
|
|
|
For a partially typed short cluster still accepting flag members, use its
|
|
|
|
|
|
canonical short spellings for continuations; do not rewrite earlier members.
|
|
|
|
|
|
A standalone `--` ends flags unless consumed as a value. After it,
|
|
|
|
|
|
dash-looking words are operands. An unknown completed option that makes
|
|
|
|
|
|
further interpretation ambiguous yields no suggestions.
|
|
|
|
|
|
3. For flag-name suggestions, match canonical spellings and suppress already
|
|
|
|
|
|
supplied singletons by canonical identity, including occurrences through
|
|
|
|
|
|
aliases. Keep repeatable flags and the four reporting flags eligible.
|
|
|
|
|
|
When a repeated singleton is typed anyway, still complete its value.
|
|
|
|
|
|
4. For an operand, use the same cardinality allocation as `allocateOperands`
|
|
|
|
|
|
in `binding.go` without required validation: include the current word
|
|
|
|
|
|
as a placeholder, determine which binding receives that final position,
|
|
|
|
|
|
and use its hint. Extract the cardinality calculation if necessary to avoid
|
|
|
|
|
|
a second allocation policy. Excess operands receive no suggestions.
|
|
|
|
|
|
When both operand and flag positions are available, an empty word uses
|
|
|
|
|
|
the operand hint and an option-looking partial word requests flags.
|
|
|
|
|
|
5. Test pending/attached/empty values, short clusters, scoped aliases and
|
|
|
|
|
|
unavailable targets, repeats, booleans, option-looking values, interspersed
|
|
|
|
|
|
operands, terminators, and optional/repeated operands before a required
|
|
|
|
|
|
suffix. Keep existing normal execution tests green at the sprint boundary.
|
|
|
|
|
|
|
|
|
|
|
|
## Stage 6: Assemble candidates and deliver query replies
|
|
|
|
|
|
|
|
|
|
|
|
1. Produce a neutral result of candidate replacements, optional descriptions,
|
|
|
|
|
|
or a filesystem strategy with value context. Keep prefix preservation
|
|
|
|
|
|
distinct from matching; the adapter translates the neutral replacement
|
|
|
|
|
|
according to the shell conventions verified in Stage 1. Do not interpret
|
|
|
|
|
|
choice values as shell source. Normalize description line breaks and omit
|
|
|
|
|
|
candidates the line protocol cannot represent rather than changing what
|
|
|
|
|
|
an inserted value means. Do not add execution-time validation for these
|
|
|
|
|
|
presentation limitations.
|
|
|
|
|
|
2. In `Execute`, after normal receiver/context checks and before ordinary
|
|
|
|
|
|
routing, recognize the exact raw query token only on an installed snapshot.
|
|
|
|
|
|
Treat all following tokens as data. Split off the final word; a missing
|
|
|
|
|
|
current-word argument is a malformed transport request, distinct from an
|
|
|
|
|
|
empty string. Use the current snapshot on each query, including after
|
|
|
|
|
|
later registration. Do not enter normal parsing, reporting selection,
|
|
|
|
|
|
input preparation, or handler execution for this path.
|
|
|
|
|
|
3. Write the adapter response only to normalized `Streams.Out`, with
|
|
|
|
|
|
managed output delivery in ordinary mode. A valid query with no inferable
|
|
|
|
|
|
context returns success and a NoFileComp reply. For a malformed request
|
|
|
|
|
|
or query construction failure, return an operational outcome and, if the
|
|
|
|
|
|
output is still usable, emit an error directive with no candidates.
|
|
|
|
|
|
Explanations go to diagnostics, never into the reply. Preserve write
|
|
|
|
|
|
failure evidence through normal presentation bookkeeping; do not retry
|
|
|
|
|
|
a broken output stream. No generated script should consume ordinary
|
|
|
|
|
|
usage guidance or debug output as a candidate.
|
|
|
|
|
|
4. Test query dispatch via `App.Execute`, including explicit empty last word,
|
|
|
|
|
|
no-context reply, malformed request, writer failure, hidden discovery,
|
|
|
|
|
|
later registration, and target words containing `--quiet` and `--debug`.
|
|
|
|
|
|
Use counter fixtures to prove target handlers, converters, factories,
|
|
|
|
|
|
and providers are untouched. Verify no special dispatch without installation.
|
|
|
|
|
|
|
|
|
|
|
|
## Stage 7: Annotate maintained commands and examples
|
|
|
|
|
|
|
|
|
|
|
|
1. Annotate static format/scope flags where their choices already come from
|
|
|
|
|
|
fixed command metadata or literals. Use the same values as the existing
|
|
|
|
|
|
version, config, and PromptKit command implementations; omit scope hints
|
|
|
|
|
|
that would advertise values not supported by that installed declaration.
|
|
|
|
|
|
Mark existing config/overlay filename flags as files where the binding
|
|
|
|
|
|
takes a filename. Leave prompt IDs, dynamic selectors, and composite
|
|
|
|
|
|
assignments unannotated. Do not infer domain semantics or load providers.
|
|
|
|
|
|
2. Add explicit installation to the maintained core and composed examples.
|
|
|
|
|
|
Demonstrate a static choice, a path hint, and an already-entered alias
|
|
|
|
|
|
with no new runtime dependency. Include an integration-level check
|
|
|
|
|
|
that installed hints reach completion without acquisition.
|
|
|
|
|
|
3. Update GoDoc, the CLI reference, and consumer/example instructions with
|
|
|
|
|
|
actual syntax, basename selection, manual script loading for each shell,
|
|
|
|
|
|
and examples of hints. Keep the wire protocol private. Once delivered,
|
|
|
|
|
|
update the feature status and keep alias discovery and dynamic discovery
|
|
|
|
|
|
in deferred work.
|
|
|
|
|
|
|
|
|
|
|
|
## Stage 8: Validate the assembled sprint
|
|
|
|
|
|
|
|
|
|
|
|
1. Exercise the generated scripts in Bash, Zsh, and Fish using a built fixture
|
|
|
|
|
|
executable, a disposable directory with filenames and directories containing
|
|
|
|
|
|
spaces, and isolated shell configuration. Verify actual completion output
|
|
|
|
|
|
and replacement behavior. Cover canonical commands, an entered alias,
|
|
|
|
|
|
static choices and descriptions, no fallback, file/directory hints in
|
|
|
|
|
|
separated and attached values, and the empty operand/leading-dash rule.
|
|
|
|
|
|
Use a PTY or a shell's native completion test interface where appropriate.
|
|
|
|
|
|
Syntax checking alone is insufficient. Record shell versions and the
|
|
|
|
|
|
commands or harness used so the evidence is reproducible.
|
|
|
|
|
|
2. Run the root and both optional module suites under the local workspace
|
|
|
|
|
|
procedure in [development.md](../development.md), plus the root suite
|
|
|
|
|
|
with `GOWORK=off`. Keep shell-dependent checks outside the default Go
|
|
|
|
|
|
suite unless the existing environment provides those shells; an absent
|
|
|
|
|
|
shell is missing acceptance evidence, not a passing check.
|
|
|
|
|
|
3. Verify no backend types in public declarations, no reverse optional-module
|
|
|
|
|
|
dependency, and no changes to ordinary routing, flag acceptance, or report
|
|
|
|
|
|
behavior outside the selected completion scope. Review documentation links,
|
|
|
|
|
|
formatting, and the final diff. Record the actual validation results.
|