# Application-owned aliases Status: command- and flag-alias behavior and consumer API direction accepted; implementation has not started. OD-1 through OD-6 are resolved. This roadmap records the target and work needed before implementation. [ADR 0013](../adr/0013-application-alias-routing.md) records the accepted architectural ownership and shared installation decisions. ## Purpose and ownership Make alternate command paths and flag spellings simple for consuming applications to declare. CommandKit core owns alias registration, resolution, and integration with routing and help. Applications choose their aliases. Integrated libraries continue to own their canonical command behavior. Aliases occupy a separate application routing layer above installed canonical commands: ```text CLI argument tokens → command-path alias resolution → canonical command or discovery-help context selection → applicable flag-alias resolution using that context's flag metadata → input parsing → ordinary invocation lifecycle or discovery help ``` Alias declarations do not install new handlers or modify the canonical command tree. An alias spelled inside `promptkit` therefore does not add an application-owned operation to the integration's namespace. The ownership and placement rules in [ADR 0011](../adr/0011-captured-declarations-and-installation.md) and the [architecture policy](../policy/architecture.md) continue to govern canonical command installation. Flag aliases likewise leave canonical flag definitions and typed bindings unchanged. This is one conceptual routing layer; its internal implementation cooperates with the existing parser to preserve token interpretation. ## Current baseline Core captures reusable `Command` declarations and installs them through `New` and `App.Register`. Installation validates a batch before registry mutation. Optional standard families own their installed root namespaces. Core routing and the internal backend coordinate parsing, help, and execution through one invocation lifecycle. The current public model has no application alias layer. PromptKit already exposes the collection catalog and smoke-test command used in the examples below. `promptkit test` takes a `KIND` operand, including `all`; those values are not registered child commands. These examples illustrate alternate routes to existing capabilities; they do not introduce new PromptKit operations. The [CLI reference](../cli.md) owns current grammar. Options follow the complete command path, can be interspersed with operands, and use `--` to terminate option parsing. Aliasing preserves the target's grammar and typed binding contracts. ## Selected declaration and registration conventions Commands and aliases use the same installation boundary through `New` and `App.Register`. Core validates the complete batch together while retaining aliases as separate routing records internally. No separate alias-registration API is needed. ### Installation types and composition Introduce a common `Declaration` interface with an unexported marker method. CommandKit supplies its implementations through public factories. `Command` represents canonical commands; a separate opaque `RoutingRule` represents aliases. Both satisfy `Declaration`: ```text Declaration ├── Command → canonical command tree └── RoutingRule → alias routing layer ``` The selected installation signatures are: ```go func New(identity Identity, declarations ...Declaration) (*App, error) func (a *App) Register(declarations ...Declaration) error ``` `NewCommand`, `Group`, and integration command factories return `Command`. `Alias`, `FlagAlias`, and `FlagAliasFor` return `RoutingRule`. The common installation boundary validates relationships across the layers atomically; installation directs each declaration to its corresponding registry. Routing rules reference canonical commands without becoming command nodes or owning handlers. Preserve eager capture and retained construction errors for both declaration kinds, without introducing deferred installation callbacks. `Group`, `RootGroup`, and command children continue to accept only `Command`. Install routing rules alongside the completed canonical tree through `New` or `Register`. Alias paths remain relative to the application root; moving a command beneath a group does not silently rebase an alias. Use ordinary `[]Declaration` slices for reusable collections containing commands and routing rules. Command-only helpers may return `Command` or `[]Command`. Go does not allow expanding `[]Command` into `...Declaration`; callers assembling installation collections should use `[]Declaration` or convert their elements. No additional bundle abstraction is selected. ### Consumer declarations Use space-separated strings for command paths, interpreted as sequences of command names without shell quoting or expansion. Application-wide flag aliases use `FlagAlias`; command-scoped aliases use `FlagAliasFor` with an explicit canonical command path. The selected consumer-facing shape is: ```go app, err := commandkit.New( identity, ckprompt.Command(suite), commandkit.Alias("list collections", "promptkit list collections"), commandkit.FlagAlias("--hlep", "--help"), commandkit.FlagAliasFor("promptkit show prompt", "--fmt", "--format"), ) ``` These are planned APIs. Later registration uses the same model and validation. Accept identical duplicate alias mappings as harmless, including when reusable helpers contribute them. Identity includes the alias kind, source, target, and scope where applicable. Different-target command mappings for the same source remain errors; flag scope precedence is specified below. Alias support lives entirely in core and works with built-in commands, application commands, optional integrations, or any combination. Validation uses the commands and flags installed in that application. No integration dependency is required for alias support. Core continues to supply help and version automatically, including for applications whose other commands are entirely application-defined. ### Shared path handling Centralize path parsing, component validation, comparison, and formatting in shared internal helpers with a common structured path representation. Alias validation, routing, help lookup, and invocation metadata use those primitives. Keep the routing and canonical command models distinct while sharing path operations. Declaration factories tokenize command-path strings on whitespace, accepting leading, trailing, and repeated whitespace, then validate each component using the canonical command-name rules. Reject empty paths and invalid components. Normalize equivalent paths before duplicate detection. Quotes and backslashes receive no shell interpretation; paths cannot contain flag assignments or operand templates. Capture parsed paths once when constructing declarations. CLI arguments are already tokenized. Preserve those boundaries: an argument containing `list collections` is one argument, not two command components. Do not trim or split incoming operands or flag values. Invocation routing identifies the command portion of the supplied tokens and carries structured paths through alias resolution and canonical selection. Format paths for presentation when needed, avoiding repeated string joining and parsing through alias chains. Convert inputs at their appropriate boundaries and reuse the result where practical. Parsing once per query is an implementation objective, not a permanent allocation or parsing guarantee. Use an internal path package if sharing across core and the backend adapter warrants it; otherwise private core helpers and a path type suffice. No additional public path package is selected. ## Selected command-alias behavior ### Paths and resolution Declare mappings between command paths relative to the application root. The executable name is not part of either path: | Alias path | Target path | | --- | --- | | `list collections` | `promptkit list collections` | | `promptkit list bundles` | `promptkit list collections` | | `list` | `promptkit list` | Aliases can target individual commands or groups. A group alias preserves remaining subcommand tokens. For example, `list collections` can reach `promptkit list collections` through the `list` group alias. Match exact, complete argument tokens at the beginning of the command path; `list` does not match `listing`. Resolve overlapping alias paths using the longest matching path, measured by the number of command-path tokens, not character count. This rule applies at each command-alias lookup and is independent of registration order. Sorting by descending token count and taking the first matching prefix is one possible implementation; the algorithm remains an implementation choice. For example, with `list → promptkit list` and `list collections → application collections`, the more specific mapping wins for `list collections`, while `list prompts` uses the `list` mapping. Once a matching alias is selected, resolve that route and apply canonical validation. A validation failure reports an error; it does not retry a shorter alias that could invoke a different command. Preserve trailing arguments, including flags, operands, explicit empty values, and `--`, for canonical parsing. Values and operands are not searched for alias text. The initial command-alias feature maps command paths. Preset flags, operand transformation, and arbitrary argument rewriting are outside this delivery. ### Chains and group composition Support alias chains and resolve them to their canonical target. At each lookup, select the longest matching command-path prefix, replace it with its target, and preserve the remaining tokens. Repeat command-path resolution until the canonical route is reached, then parse its inputs normally. Flags, flag values, and operands remain outside command-alias matching under the command/operand boundary rules below. A group shortcut provides access to routes beneath its target, including aliases: ```text pk → promptkit promptkit list bundles → promptkit list collections ``` Here, `pk list bundles` resolves through `promptkit list bundles` to `promptkit list collections`. An explicitly declared `pk list bundles` alias would take precedence over `pk` at the initial lookup. Ordinary chains follow the same rules, for example `collections → list collections → promptkit list collections`. Validate composition during installation and reject cycles and nonterminating prefix expansion with diagnostics identifying the participating mappings. Checking only for repeated complete paths is insufficient: `a → b extra` and `b → a` continually expand the path without repeating it. Analyze prefix composition so these configuration errors are caught before invocation. An arbitrary runtime hop limit is not a substitute for installation validation and must not reject an otherwise valid long chain. A dependency-graph cycle or repeated use of a declaration does not by itself establish nontermination. With canonical group `group`, the mappings `a → group` and `group again → a` allow `a again again` to terminate: each round consumes an `again` token. Validate actual prefix-resolution behavior, including terminating repetitions, suffix-dependent cycles, and growing expansions. Establish focused evidence for this validation before building the rest of routing around the algorithm. ### Installation and collisions Use the shared declaration and installation conventions above for aliases, application commands, and integration commands. An alias target may already be installed or be supplied in the same installation batch. Declaration order within that batch does not affect resolution. Missing targets, collisions, and cycles produce installation errors with useful path context. Failed installation preserves the existing application registry and alias mappings under the existing batch-atomicity model. A declared target must resolve entirely to a canonical command or group, without leftover operand tokens. For example, reject `Alias("fast", "run production")` when canonical `run` takes `production` as an operand. Command aliases do not inject fixed operands. Likewise, `promptkit test full → promptkit test all` is invalid: `all` is an operand of canonical `promptkit test KIND`, and the source would intercept that command's operand space. A command-path shortcut such as `smoke → promptkit test` is valid; `smoke all` preserves the operator-supplied `all` operand. Synonyms for operand values require a separate capability and are outside this delivery. An alias cannot conflict with or overload a canonical command path. Reject such a declaration during installation as invalid application configuration. Apply the same protection when later registration adds a canonical command conflicting with an existing alias. Reject competing mappings from the same source path to different targets; registration order must not select a winner. Identical duplicate mappings are accepted under the shared registration conventions. Aliases do not create canonical parent groups, merge integration trees, or replace handlers. ### Prefix interactions and canonical reachability Alias paths may share prefixes with canonical paths when they introduce a distinct route. Preserve existing canonical invocations, including the declared operand space of runnable commands. | Canonical declaration | Proposed alias | Result | | --- | --- | --- | | `promptkit list collections` | `promptkit list bundles` → `promptkit list collections` | Allow the distinct route beneath the shared prefix. | | `list projects` | `list collections` → `promptkit list collections` | Allow the distinct route beneath the shared prefix. | | `list` group | `list` → `promptkit list` | Reject replacement of the canonical group path. | | `run` accepts an operand | `run full` → another command | Reject interception of `full` in the canonical command's operand space. | Determine command and operand boundaries from declared command structure and argument metadata, without executing handlers or application validation. Longest-path matching chooses among valid aliases; it does not authorize consumption of canonical operands. Sharing a runnable ancestor alone does not establish a conflict: check whether the proposed route intercepts that ancestor's operands or an existing canonical child route. Explicit longer aliases beneath an alternate spelling still use longest-match precedence. With canonical `run TARGET`, `r → run`, and `r full → inspect`, `r full` invokes canonical `inspect`; canonical `run full` retains its operand meaning. If only `r → run` exists, `r full` reaches `run` with operand `full`, which is not searched for aliases after the route resolves. Canonical operand protection does not reserve the operand space of every alternate spelling. Check these conflicts against the complete candidate registry when adding either aliases or canonical commands, including later registration. Report the conflicting paths and preserve batch atomicity. Apply the chain and group composition rules above when validating resolved routes. ### Execution and help An invocation through an alias uses the canonical target's flags, operands, defaults, typed bindings, input preparation, providers, diagnostics, and outcomes. Execute the ordinary lifecycle once. Alias registration, resolution, and help do not invoke application input/state factories or acquisition callbacks. Expose the original entered command path and final resolved canonical command path as read-only invocation metadata. Both paths exclude the executable name, flags, and operands. For a direct canonical invocation the paths are equal; for `app pk list bundles`, they can be `pk list bundles` and `promptkit list collections`. Retain the endpoints through a chain; intermediate rewrites are not part of the selected public metadata. Consumers can use the canonical path to identify the operation consistently and the entered path for logging and diagnostics. CommandKit dispatch and bindings continue to use the canonical target. Expose the paths through these accessors, returning normalized, space-separated strings: ```go func (inv Invocation) EnteredPath() string func (inv Invocation) CanonicalPath() string ``` Strings provide read-only values and match the declaration path representation. Support both help forms: ```text scriptorium help list collections scriptorium list collections --help ``` Alias help describes the canonical target's inputs using the alias invocation path and identifies the canonical command. Root help lists explicitly declared command aliases separately from canonical commands, showing their resolved canonical targets. Ordinary help need not show intermediate chain mappings or enumerate every route reachable through group shortcuts. Group help includes relevant aliases separately from canonical children. For example, `help promptkit list` shows `bundles → promptkit list collections`. Help reached through a group alias uses that spelling in usage examples and presents the routes available through it, including applicable aliases beneath the target. Account for more-specific mappings so displayed destinations match routing. Provide discovery help for alias-only prefixes. If `list collections` and `list prompts` are declared without a canonical `list` group, both `help list` and `list --help` display the available routes. This help view is derived from alias declarations and creates no canonical group or namespace ownership. Bare `list` reports an incomplete command and points to that help. Routing can select this discovery-help context without selecting a canonical command. It uses core's help metadata and applicable application-wide flag aliases, so `list --hlep` can request the same help. It neither inherits flags from descendant commands nor creates a canonical command node. Usage errors use the entered path and identify the canonical target when one has been resolved, while preserving the canonical command's diagnostic content: ```text Usage: scriptorium list collections [flags] Alias for: scriptorium promptkit list collections ``` If resolution fails before identifying a target, report the entered route without assigning a canonical identity. ## Selected flag-alias behavior ### Spellings and scopes Applications can declare alternate spellings for canonical flags, including `--hlep` mapping to `--help`. Declaring a flag alias should be as simple as declaring a command alias, using the shared registration conventions above. Validate each source and target as an individual flag spelling; declarations do not accept clusters, attached values, or other argument expressions. Support application-wide mappings and mappings scoped to a canonical command. The selected canonical command determines the applicable flag aliases, so a command-scoped mapping works through every command alias that reaches that target. A command-scoped mapping takes precedence over an application-wide mapping for the same alias spelling. Reject different-target mappings within the same scope; identical duplicates are harmless. Registration order does not choose a winner, and neither scope may overload an applicable canonical flag. An application-wide mapping is available wherever its target flag exists and contributes nothing elsewhere. For example, `--colour → --color` need not require every command to define `--color`. On a command without that target, `--colour` receives the ordinary unknown-flag error. Reusable application-wide declarations are valid even when no currently installed command has their target flag. A command-scoped mapping requires its named canonical command and target flag to exist in the complete candidate installation batch. An unavailable explicit destination is invalid configuration. Application-wide mappings also apply to built-in flags at the application root and on groups. In particular, `--hlep → --help` works wherever canonical `--help` works, including help for alias-only prefixes. Later registration repeats validation against the candidate registry. Newly installed commands gain applicable application-wide aliases automatically; canonical spelling collisions reject the batch under the collision rules below. ### Token interpretation Resolve command aliases and select the canonical command or discovery-help context before applying its flag aliases. Use that context's flag metadata to distinguish flag names from values. Preserve the existing grammar, including flags interspersed with operands. | Input | Effect | | --- | --- | | `list collections --hlep` | Resolve the command path and route `--hlep` to canonical help. | | `foo --colour=blue` | With `--colour` mapped to `--color`, preserve `blue` as the value. | | `foo --message=--hlep` | Preserve the value `--hlep`. | | `foo --message --hlep` | When `--message` consumes a value, preserve `--hlep` as that value. | | `foo -- --hlep` | Preserve the operand `--hlep` after the option terminator. | Only tokens interpreted as flag names participate in flag aliasing. Preserve values, explicit empty strings, and operands. The routing layer cooperates with the existing parser rather than maintaining a separate interpretation of flag arity, boolean syntax, or option termination. Match the complete flag name. Length precedence does not apply: `--out` and `--output` are different names, and only `--out` participates in alias lookup in `--out=value`. Use the scope precedence selected above. Resolution must be deterministic and must not depend on registration order. ### Canonical flag collisions An alias cannot conflict with or overload an applicable canonical flag spelling. Reject conflicts during installation as invalid application configuration. Check a command-scoped alias against that command's canonical flags, and an application-wide alias across the commands where it applies. Apply the same checks when later registration adds canonical declarations. Failed batches preserve existing commands and alias mappings. ### Canonical binding behavior An alias feeds the same typed binding as its canonical flag. The target retains its value conversion, defaults, required-value checks, presence tracking, repetition rules, and behavior. Mixed spellings retain invocation order: ```text foo --color red --colour blue ``` With `--colour` mapped to singleton `--color`, the normal last-occurrence rule selects `blue`. Earlier invalid occurrences remain errors under the existing conversion contract. For a repeatable target, occurrences through either spelling accumulate in order. Alias resolution adds no second conversion or input-preparation lifecycle. Flag aliases do not change canonical flag ownership or add new provider capabilities. ### Chains and short spellings Support flag-alias chains, such as `--colour → --hue → --color`. When resolving a target, an existing canonical flag spelling terminates the chain. Otherwise, look up the next alias using the selected context's effective mappings, with command-scoped mappings taking precedence over application-wide mappings. Resolve to the final canonical flag and use its binding. Validate applicable alias-source collisions separately; a canonical endpoint does not excuse a conflicting source declaration. For example, given canonical `--color` and global mappings `--color → --missing` and `--colour → --color`, the first mapping contributes nothing while `--missing` is unavailable. The second still reaches canonical `--color`. If later registration makes `--missing` available in that context, the first mapping becomes a canonical spelling collision and the batch fails. Apply target-availability rules to the final destination. An application-wide chain contributes nothing where its final canonical flag is unavailable. A command-scoped declaration must resolve to an available canonical flag. Reject cycles during installation, identify the participating spellings in diagnostics, and revalidate composition after later registration. Support individual short alias spellings, such as `-c → --color`, alongside long aliases. Canonical short spellings and aliases use the same cluster and attached-value grammar and may mix freely within a cluster. Resolve each alias to its final canonical flag before determining whether it consumes a value. Process short spellings left to right. Boolean flags use presence as true; explicit boolean values require `=`. The first value-taking flag consumes the remaining suffix, removing a separating `=` when present, or consumes the next argument if there is no suffix. Consumed values are never searched for aliases. Unknown spellings within a cluster produce a usage error. Preserve occurrence order for repeatable bindings and singleton last-occurrence behavior. With boolean aliases `-v → --verbose` and `-q → --quiet`, and value-taking `-c → --color`: | Input | Interpretation | | --- | --- | | `-vq` | `--verbose --quiet` | | `-c blue`, `-c=blue`, or `-cblue` | `--color=blue` | | `-vqcblue` or `-vqc blue` | `--verbose --quiet --color=blue` | | `-cvq` | `--color=vq` | | `-v=false` | `--verbose=false` | | `-qv=false` | `--quiet --verbose=false` | `-vfalse` is a cluster beginning with `-v`, not an explicit false value. A spelling such as `-color` follows short-option parsing; long aliases use the `--` prefix. Options still follow the complete command path, and `--` ends option parsing. Correct explicit empty-value handling for both canonical short spellings and aliases: `-c=` supplies an empty string, consistently with `--color=` and `-c ""`. The current backend instead passes the literal string `"="` for `-c=`; this delivery corrects that behavior. Likewise, `-v=` supplies an empty value to boolean conversion and fails conversion. This correction preserves the distinction between an omitted value and an explicitly supplied empty value. ### Flag help and diagnostics Present aliases alongside their canonical flags in help. Show only mappings effective in the current scope, resolve chains to their final targets, and document each canonical input once. For example: ```text --color VALUE Output color. Aliases: --colour, -c ``` When identifying an invalid occurrence in diagnostics, retain the spelling the operator supplied and identify the canonical flag where helpful. Conversion, required-value checks, and repetition behavior continue to come from the canonical binding. ## Future shell completion The direction for future shell completion is canonical suggestions with alias-aware interpretation. Suggest canonical command and flag names rather than enumerating aliases. Resolve aliases the operator has already entered so completion can continue using the target's command structure and input metadata. For example, `app ` suggests canonical commands. With `pk → promptkit`, `app pk ` can suggest canonical children available through that route. After a complete command alias, use the target's flags and operands as the basis for completion. Recognize manually entered flag aliases when identifying the canonical binding whose value is being completed. Suggestions beneath a group alias must account for more-specific mappings so their interpretation agrees with execution routing. Keep validated canonical metadata and alias relationships available internally for metadata-only lookup independently of execution and help rendering. Future completion can reuse those records and shared path operations without parsing rendered help or invoking application input factories and acquisition callbacks just to discover command and flag names. This does not select a lifecycle or provider API for future dynamic operand or value suggestions. Completion requests contain partial tokens and cursor context. A future completion lookup must accommodate incomplete input without relaxing exact matching for execution or requiring partial input to pass execution validation. Preserve the distinction between structured paths and supplied argument tokens; shell-specific tokenization and quoting belong to that future work. Shell completion itself is outside this implementation. Do not add completion APIs, shell scripts, partial-input lookup machinery, or completion-specific tests in this delivery. Internal lookup separation supports the current routing and help responsibilities; it does not require a public metadata API or a general completion framework. Shell selection, candidate presentation, and dynamic value completion remain future design work. ## Work needed from the baseline 1. Prepare consumer examples for the selected declaration types and shared path handling, covering a command shortcut, a synonym inside an integration namespace, and a group shortcut, an application-wide help flag alias, and a command-scoped flag alias reached through a command shortcut. 2. Establish termination-validation behavior with focused evidence for valid repetitions, suffix-dependent cycles, and growing prefix expansions before building the rest of routing around the algorithm. 3. Implement the selected declaration types and shared path primitives. Define and verify installation handling for nil declarations and zero `RoutingRule` values alongside the existing invalid-zero and explicit-absence command contracts. Validate flag declarations as individual spellings. Extend installation to validate aliases and commands against the complete candidate batch, preserving retained errors and atomicity. Keep aliases independent of optional-library dependencies and backend-specific exports. 4. Integrate command and flag alias resolution with canonical routing, help, and invocation identity. Use canonical flag metadata and the existing parser to preserve values and operands, and reuse typed execution machinery. Apply the selected short-option grammar and correct explicit empty values for canonical short spellings and aliases together. Keep metadata lookup separate from execution and help rendering as described under future shell completion, without implementing completion in this delivery. 5. Add focused behavioral evidence for both example mappings, group aliases, longest matches, chains, group composition, cycles and nonterminating prefix expansion, collisions, missing targets, same-batch resolution, later registration, argument preservation, help without acquisition, and a single execution lifecycle. Cover application-wide and command-scoped flag mappings, attached and separate values, option termination, mixed canonical/alias repetitions, flag chains and scope overrides, short aliases, mixed clusters, attached values, explicit empty-value correction, flag help and diagnostics, and unchanged typed binding semantics. Include discovery-help contexts without canonical commands, longer aliases beneath operand-taking command shortcuts, rejection of operand-bearing targets, and canonical flag endpoints alongside inactive global mappings. Verify rejection of `promptkit test full → promptkit test all` and preservation of the supplied `all` operand through the valid `smoke → promptkit test` shortcut. 6. Add maintained consumer examples and update GoDoc, the CLI reference, consumer guidance, and internal documentation when implementation lands. Validate the affected modules under the [developer guide](../development.md). 7. Document migration from `...Command` to `...Declaration`, including slice conversion, and the explicit empty-value correction. Include the compatibility impact and required consumer action in release notes under the [release policy](../policy/release.md#versioning-and-compatibility). This delivery is complete when consumers can declare alternate command paths and flag spellings with minimal wiring and invoke canonical behavior and help through them while canonical ownership and binding semantics stay intact. Installation rejects conflicts with canonical commands and applicable flags in either registration order.