# Shell completion implementation plan Status: Stages 1–7 complete. Stage 8 is next. 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 Status: complete. 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. 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. ## Stage 2: Capture completion hints Status: complete. 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. 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`. ## Stage 3: Install completion as an ordinary declaration Status: complete. 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. 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`. ## Stage 4: Resolve the entered command context Status: complete. 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. Completion: added private request context resolution using completed words and the committed routing snapshot. Canonical child candidates are checked by resolving the entered route with the candidate inserted; discovery-only and unknown contexts do not invent command continuations. Tests cover canonical and aliased routes, chains, scoped and discovery flag aliases, shadowed children, runnable operand metadata, and paths ended by flags or operands. The root package suite passes with `GOWORK=off` and `GOTOOLCHAIN=local`. ## Stage 5: Interpret flags and locate value positions Status: complete. 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. Completion: shared neutral token classification and flag lookup with normal parsing, and extracted operand count allocation for execution and completion. The completion walk records consumed flags and operands, locates pending or attached values, preserves entered prefixes, and filters canonical flag names by supplied identity. Tests cover aliases, short clusters, repeated flags, boolean assignments, option-looking values, terminators, interspersed operands, and operand cardinality. Root, adapter, and neutral metadata package tests pass with `GOWORK=off` and `GOTOOLCHAIN=local`. ## Stage 6: Assemble candidates and deliver query replies Status: complete. 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. Completion: connected the installed query before normal routing and reporting selection, assembled neutral candidates and filesystem strategies, and routed protocol output through managed reporting. Invalid transport requests return an error directive and a separate diagnostic; write failures remain presentation evidence. The adapter preserves short attached prefixes through a private cue consumed by the generated scripts, while retaining literal choice values and normalizing descriptions. Query tests cover empty and malformed requests, aliases and discovery, later registration, callback isolation, reporting flags as data, and writer failure. Root and adapter package suites and the full interactive Bash, Zsh, and Fish transport matrix pass. ## Stage 7: Annotate maintained commands and examples Status: complete. 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. Completion: annotated built-in version, configuration, and PromptKit formats, supported configuration scopes, and filename selections. The core and composed examples install completion and demonstrate choices, paths, and entered aliases. Integration tests verify scope availability and completion without input, state, acquisition, comparison, engine, or client callbacks. GoDoc, CLI, consumer, example, and roadmap documentation now describe the delivered scope; alias and dynamic discovery remain deferred. Root and both integration package tests pass in the local workspace, and changed Markdown links and whitespace checks pass. ## 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.