Dynamic Shell Completion
Goal
Add reusable Azure-backed positional completion for Cobra commands. First production integration: azdo repo list / ls / l.
Expected behavior:
azdo repo ls org:<TAB> returns project candidates such as org:ProjectA.
azdo repo ls my-repo<TAB> returns project candidates from the configured default organization.
- Completion returns names only, case-insensitive prefix matches, sorted deterministically.
- Completion uses Cobra's existing Bash, Zsh, Fish, and PowerShell generators; no custom shell scripts.
- Completion failures, missing auth, rate limits, and timeouts return no candidates without blocking or printing diagnostics into the completion protocol.
repo list still executes with [ORG:]PROJECT and lists repositories for the selected project. Initial completion does not list repositories across all projects or change command arguments.
Decisions
- Register handlers per command in
NewCmd; no root registry.
- Add generic scope-aware completion helpers so future commands can supply resource-specific candidate fetchers without reimplementing partial
ORG: parsing.
- No cache in v1: Cobra starts a fresh completion process per request; in-memory cache has little value and disk cache adds invalidation/staleness/security concerns.
- Add persistent config keys:
completion_timeout: Go duration, default 800ms.
completion_limit: positive integer, default 100.
- Invalid or missing settings fall back to defaults and never make completion fail.
- Apply timeout to the completion fetch path and use
cobra.ShellCompDirectiveNoFileComp for every result, including suppressed failures.
- Do not start progress indicators or write logs/output to stdout during completion.
Architecture
Shared completion utility
Add internal/cmd/util/completion.go with:
- A request type containing completion context, already-consumed positional args, resolved organization, typed prefix, and whether the organization was explicitly supplied.
- A handler type returning raw candidate names and an error. Handlers own resource/API lookup; shared utility owns scope parsing, filtering, formatting, limits, timeout, and failure suppression.
RegisterProjectScopeCompletion(cmd, ctx, handler) assigning cmd.ValidArgsFunction for the first positional argument.
- A reusable project handler backed by
ctx.ClientFactory().Core(...) and azdo.GetProjects(...), which already handles continuation tokens.
- Partial scope parsing separate from
ParseProjectScope: recognize at most one ORG: prefix, allow an empty suffix for org:<TAB>, reject malformed colons/slashes, never call the full parser on incomplete input.
- Default organization resolution only for unprefixed input. Bare
org remains a project prefix under default organization; never interpreted as an organization for project-scoped completion.
- Candidate formatting: explicit org returns
org:Name; implicit org returns Name.
- Case-insensitive prefix filtering, deterministic case-insensitive sorting, deduplication, then candidate cap from
completion_limit.
- Empty prefix returns up to configured limit of project names.
len(args) >= 1 returns no candidates (repo list accepts one positional scope).
- Bounded completion context and a hard callback return bound where necessary; fetcher/API errors convert to empty results. No
ShellCompDirectiveError or file fallback.
- Debug-only structured logging if useful; never log tokens, project data beyond candidate metadata, or write to stdout.
Future commands reuse the same registration helper with project, pipeline, team, repository, or other resource handlers. Nested resource completion can use request args and add a specialized parser without changing existing command registrations.
Configuration
Update internal/config/config_options.go with completion_timeout and completion_limit so azdo config set/list flows expose them. Add shared parsing/default logic in internal/config or the completion utility, with positive-duration/positive-integer validation and fallback to defaults for invalid persisted values. Keep configuration interface changes minimal; no new auth or Azure client interface is needed.
Document settings in command/config help or generated docs where the repository's config conventions require it. No environment-variable settings in v1.
Command wiring
Update internal/cmd/repo/list/list.go to register project-scope completion after command flags are created. Existing aliases automatically share the callback. Keep execution parsing and API behavior unchanged.
Existing Cobra completion initialization in ExecuteC already supplies completion and __complete; no root wiring or shell generator implementation is required.
Tests (hermetic)
Add internal/cmd/util/completion_test.go covering:
- Partial input: empty,
my, org:, org:my, bare org, whitespace, malformed multiple colons, empty organization, slash/target input.
- Explicit and default organization resolution.
- Case-insensitive filtering, sorting, formatting, deduplication, and configured cap.
- Empty results, handler error, missing default organization, invalid config values, and timeout cancellation.
- Project handler with mocked Core client and multi-page
GetProjects continuation responses.
- No progress/log/stdout contamination.
- A minimal Cobra root invoking
__complete to assert candidate lines plus final :4 (NoFileComp) protocol marker.
Extend internal/cmd/repo/list/list_test.go with:
- Registration assertion that
ValidArgsFunction is non-nil.
- Default-org completion returning bare project names.
- Explicit
org: completion returning org:Project candidates.
- Prefix filtering and candidate cap through mocked Core client.
- Failure path returning no candidates without invoking Git repository listing.
Add configuration tests for the new option keys/defaults and invalid-value fallback.
All tests remain hermetic. Reuse generated Core/client/config mocks; do not call Azure REST APIs.
Verification
Run focused tests first:
go test ./internal/config/... ./internal/cmd/util/... -run Completion -v
go test ./internal/cmd/repo/list/... -v
Run full validation:
go test ./...
make lint
make docs
Review generated documentation diff. Smoke-test all shell generators and protocol paths:
go run cmd/azdo/azdo.go completion bash
go run cmd/azdo/azdo.go completion zsh
go run cmd/azdo/azdo.go completion fish
go run cmd/azdo/azdo.go completion powershell
Use hermetic __complete tests for candidate output; manual unauthenticated invocation may correctly produce only :4.
After source changes, run graphify update . and verify completion relationships remain indexed. Inspect git diff for only completion/config/tests/docs changes and preserve unrelated worktree changes.
Likely Files
internal/cmd/util/completion.go — shared completion API, partial scope parsing, fetch/filter/timeout behavior.
internal/config/config_options.go — persistent completion settings.
internal/cmd/repo/list/list.go — initial production registration.
internal/azdo/loader.go — paginated project retrieval reuse.
internal/cmd/util/scope.go — canonical scope semantics to mirror without invoking on partial input.
internal/cmd/util/completion_test.go — reusable utility and Cobra protocol tests.
internal/cmd/repo/list/list_test.go — command integration tests.
internal/cmd/root/root.go and vendored Cobra completion sources — protocol/generator verification only; modify only if tests prove wiring requires it.
Dynamic Shell Completion
Goal
Add reusable Azure-backed positional completion for Cobra commands. First production integration:
azdo repo list/ls/l.Expected behavior:
azdo repo ls org:<TAB>returns project candidates such asorg:ProjectA.azdo repo ls my-repo<TAB>returns project candidates from the configured default organization.repo liststill executes with[ORG:]PROJECTand lists repositories for the selected project. Initial completion does not list repositories across all projects or change command arguments.Decisions
NewCmd; no root registry.ORG:parsing.completion_timeout: Go duration, default800ms.completion_limit: positive integer, default100.cobra.ShellCompDirectiveNoFileCompfor every result, including suppressed failures.Architecture
Shared completion utility
Add
internal/cmd/util/completion.gowith:RegisterProjectScopeCompletion(cmd, ctx, handler)assigningcmd.ValidArgsFunctionfor the first positional argument.ctx.ClientFactory().Core(...)andazdo.GetProjects(...), which already handles continuation tokens.ParseProjectScope: recognize at most oneORG:prefix, allow an empty suffix fororg:<TAB>, reject malformed colons/slashes, never call the full parser on incomplete input.orgremains a project prefix under default organization; never interpreted as an organization for project-scoped completion.org:Name; implicit org returnsName.completion_limit.len(args) >= 1returns no candidates (repo listaccepts one positional scope).ShellCompDirectiveErroror file fallback.Future commands reuse the same registration helper with project, pipeline, team, repository, or other resource handlers. Nested resource completion can use request args and add a specialized parser without changing existing command registrations.
Configuration
Update
internal/config/config_options.gowithcompletion_timeoutandcompletion_limitsoazdo config set/listflows expose them. Add shared parsing/default logic ininternal/configor the completion utility, with positive-duration/positive-integer validation and fallback to defaults for invalid persisted values. Keep configuration interface changes minimal; no new auth or Azure client interface is needed.Document settings in command/config help or generated docs where the repository's config conventions require it. No environment-variable settings in v1.
Command wiring
Update
internal/cmd/repo/list/list.goto register project-scope completion after command flags are created. Existing aliases automatically share the callback. Keep execution parsing and API behavior unchanged.Existing Cobra completion initialization in
ExecuteCalready suppliescompletionand__complete; no root wiring or shell generator implementation is required.Tests (hermetic)
Add
internal/cmd/util/completion_test.gocovering:my,org:,org:my, bareorg, whitespace, malformed multiple colons, empty organization, slash/target input.GetProjectscontinuation responses.__completeto assert candidate lines plus final:4(NoFileComp) protocol marker.Extend
internal/cmd/repo/list/list_test.gowith:ValidArgsFunctionis non-nil.org:completion returningorg:Projectcandidates.Add configuration tests for the new option keys/defaults and invalid-value fallback.
All tests remain hermetic. Reuse generated Core/client/config mocks; do not call Azure REST APIs.
Verification
Run focused tests first:
Run full validation:
Review generated documentation diff. Smoke-test all shell generators and protocol paths:
Use hermetic
__completetests for candidate output; manual unauthenticated invocation may correctly produce only:4.After source changes, run
graphify update .and verify completion relationships remain indexed. Inspectgit difffor only completion/config/tests/docs changes and preserve unrelated worktree changes.Likely Files
internal/cmd/util/completion.go— shared completion API, partial scope parsing, fetch/filter/timeout behavior.internal/config/config_options.go— persistent completion settings.internal/cmd/repo/list/list.go— initial production registration.internal/azdo/loader.go— paginated project retrieval reuse.internal/cmd/util/scope.go— canonical scope semantics to mirror without invoking on partial input.internal/cmd/util/completion_test.go— reusable utility and Cobra protocol tests.internal/cmd/repo/list/list_test.go— command integration tests.internal/cmd/root/root.goand vendored Cobra completion sources — protocol/generator verification only; modify only if tests prove wiring requires it.