Files
commandkit/docs/roadmap/aliases.md

438 lines
21 KiB
Markdown
Raw Normal View History

# Application-owned aliases
Status: command- and flag-alias directions accepted; implementation has not
started. Consumer registration conventions are selected; the installer declaration
type and remaining routing details are open. This roadmap records
the target and work needed before implementation.
## 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 selection
→ applicable flag-alias resolution using canonical flag metadata
→ canonical parsing
→ ordinary invocation lifecycle
```
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 commands used in the examples
below. 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.
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", "--ver", "--version"),
)
```
This illustrates planned APIs. The declaration type accepted by the installer
still needs design; aliases remain routing records rather than fabricated
canonical command nodes. 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; overlap between flag scopes is addressed under OD-6.
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.
## 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 test full` | `promptkit test all` |
| `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 test full → promptkit test all
```
Here, `pk test full` resolves through `promptkit test full` to
`promptkit test all`. An explicitly declared `pk test full` 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.
### 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.
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 test all` | `promptkit test full` → `promptkit test all` | 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.
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 test full`, they can be `pk test full` and `promptkit test all`.
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. Exact accessor names and path
representation remain API design details under OD-1.
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 test` shows `full → promptkit test all`. 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.
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.
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 identify the canonical command before applying its
flag aliases. Use canonical 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`. At each step,
use the selected command's effective mappings, with command-scoped mappings
taking precedence over application-wide mappings. Resolve to the final
canonical flag and use its binding.
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. Value handling follows the target flag. Short-option clusters
and attached-value syntax must follow CommandKit's deliberately selected grammar;
verify that grammar before settling these forms under OD-6.
### 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.
## Work needed from the baseline
1. Finalize the installer declaration type and open routing details with consumer examples
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. Capture the durable separation between application alias routing and canonical
command and flag ownership in an ADR. Keep exact API, matching, and presentation
details in their appropriate contracts. No relaxation of canonical namespace
ownership is selected.
3. 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.
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, flag help and diagnostics, and unchanged typed binding semantics.
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).
Completion means 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. Remaining public API and short-option grammar details are
listed below.
## Open Decisions
### OD-1: Installer declaration type and composition details
The shared installation API, path strings, explicit flag scopes, identical
duplicate handling, and core-only availability are selected above. What public
declaration type lets `New` and `App.Register` accept commands and aliases
together? How do collections of reusable declarations and group composition
interact with application-root alias paths? What exact whitespace parsing and
path validation rules accompany the selected string representation? What
accessor names and representation expose the selected read-only entered and
canonical invocation paths?
### OD-6: Short-option grammar
Individual short aliases are selected above. Which short-option clusters and
attached-value forms does CommandKit's selected grammar support, and how should
short aliases participate in those forms?