Document application-owned alias contracts
This commit is contained in:
16
app.go
16
app.go
@@ -33,12 +33,18 @@ type commandNode struct {
|
|||||||
command Command
|
command Command
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates an application with the supplied identity and initial
|
// New creates an application with the supplied identity and initial canonical
|
||||||
// declarations.
|
// commands and routing rules. Use []Declaration to expand a mixed declaration
|
||||||
|
// collection; []Command cannot be expanded as ...Declaration without
|
||||||
|
// converting its elements.
|
||||||
|
//
|
||||||
// The identity name must not be empty. New installs its generated help and
|
// The identity name must not be empty. New installs its generated help and
|
||||||
// version commands and caller declarations as one batch; an invalid declaration
|
// version commands and caller declarations as one batch; an invalid, nil, or
|
||||||
// returns an error without exposing an App. The root help and version names are
|
// zero declaration returns an error without exposing an App. Factory issues
|
||||||
// reserved for generated commands.
|
// retained in Command and RoutingRule values are reported during installation.
|
||||||
|
// Identical duplicate routing mappings are accepted; conflicting mappings
|
||||||
|
// fail the batch. The root help and version names are reserved for generated
|
||||||
|
// commands.
|
||||||
func New(identity Identity, declarations ...Declaration) (*App, error) {
|
func New(identity Identity, declarations ...Declaration) (*App, error) {
|
||||||
if identity.Name == "" {
|
if identity.Name == "" {
|
||||||
return nil, fmt.Errorf("application identity name must not be empty")
|
return nil, fmt.Errorf("application identity name must not be empty")
|
||||||
|
|||||||
74
command.go
74
command.go
@@ -10,21 +10,32 @@ import (
|
|||||||
"gitea.maximumdirect.net/eric/CommandKit/internal/commandpath"
|
"gitea.maximumdirect.net/eric/CommandKit/internal/commandpath"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Command is an opaque, reusable declaration captured by NewCommand, Group,
|
// Command is an opaque, reusable declaration of canonical commands and groups.
|
||||||
// RootGroup, or an integration. Capture copies declaration slices but retains
|
// NewCommand, Group, RootGroup, and integration command factories return
|
||||||
// callback and caller-owned object references. Installation validates the
|
// Command values. Capture copies declaration slices but retains callback and
|
||||||
// declaration before changing an App registry.
|
// caller-owned object references. Installation validates the declaration
|
||||||
|
// before changing an App registry.
|
||||||
type Command struct{ record *commandRecord }
|
type Command struct{ record *commandRecord }
|
||||||
|
|
||||||
// Declaration is a captured command or application routing rule accepted by
|
// Declaration is a captured canonical command or application routing rule
|
||||||
// New and App.Register. Only CommandKit declaration types implement it.
|
// accepted by New and App.Register. Use []Declaration when collecting both
|
||||||
|
// kinds. Group and RootGroup children remain Commands; install routing rules
|
||||||
|
// alongside the completed command tree through New or Register. Only
|
||||||
|
// CommandKit declaration types implement Declaration.
|
||||||
|
//
|
||||||
|
// A zero Command, zero RoutingRule, nil declaration, or typed nil declaration
|
||||||
|
// pointer is invalid. Use NoCommand to represent an intentionally absent
|
||||||
|
// optional command.
|
||||||
type Declaration interface {
|
type Declaration interface {
|
||||||
commandkitDeclaration()
|
commandkitDeclaration()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (Command) commandkitDeclaration() {}
|
func (Command) commandkitDeclaration() {}
|
||||||
|
|
||||||
// RoutingRule is an opaque, reusable command or flag alias declaration.
|
// RoutingRule is an opaque, reusable command or flag alias declaration. Alias,
|
||||||
|
// FlagAlias, and FlagAliasFor capture routing rules eagerly and retain any
|
||||||
|
// construction issues for New or App.Register to report. Identical duplicate
|
||||||
|
// mappings are accepted; competing targets for the same source are errors.
|
||||||
type RoutingRule struct{ record *routingRuleRecord }
|
type RoutingRule struct{ record *routingRuleRecord }
|
||||||
|
|
||||||
func (RoutingRule) commandkitDeclaration() {}
|
func (RoutingRule) commandkitDeclaration() {}
|
||||||
@@ -236,9 +247,11 @@ func (c typedCommand[I]) prepare(flagValues, flagSpellings [][]string, operands
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCommand captures a typed command declaration without constructing its
|
// NewCommand captures a typed canonical command declaration without
|
||||||
// input or running callbacks. Construction issues are retained and reported
|
// constructing its input or running callbacks. It copies the spec's flags,
|
||||||
// when the declaration is installed. The returned declaration can be reused.
|
// arguments, and children slices. Construction issues are retained and
|
||||||
|
// reported when the declaration is installed; NewCommand itself does not
|
||||||
|
// return an error. The returned declaration can be reused across Apps.
|
||||||
func NewCommand[I any](spec CommandSpec[I]) Command {
|
func NewCommand[I any](spec CommandSpec[I]) Command {
|
||||||
return captureCommand(spec, nil)
|
return captureCommand(spec, nil)
|
||||||
}
|
}
|
||||||
@@ -260,25 +273,29 @@ func captureCommand[I any](spec CommandSpec[I], privateRun func(commandExecution
|
|||||||
return Command{record: record}
|
return Command{record: record}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Group captures a non-runnable command group and a copy of its relative child
|
// Group captures a non-runnable canonical command group and a copy of its
|
||||||
// slice. Child declarations remain reusable and are not recursively copied.
|
// relative Command child slice. Child declarations remain reusable and are not
|
||||||
|
// recursively copied. Routing rules cannot be children; install them alongside
|
||||||
|
// the root command tree through New or App.Register.
|
||||||
func Group(name, summary string, children ...Command) Command {
|
func Group(name, summary string, children ...Command) Command {
|
||||||
record := &commandRecord{name: name, summary: summary, children: append([]Command(nil), children...)}
|
record := &commandRecord{name: name, summary: summary, children: append([]Command(nil), children...)}
|
||||||
captureComposition(record)
|
captureComposition(record)
|
||||||
return Command{record: record}
|
return Command{record: record}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RootGroup captures a group that may only be installed at the application root.
|
// RootGroup captures a group that may only be installed at the application
|
||||||
// Installation rejects it below any other command, including through wrappers.
|
// root. Installation rejects it below any other command, including through
|
||||||
|
// wrappers. Its children are canonical Commands; install routing rules at the
|
||||||
|
// application root separately.
|
||||||
func RootGroup(name, summary string, children ...Command) Command {
|
func RootGroup(name, summary string, children ...Command) Command {
|
||||||
command := Group(name, summary, children...)
|
command := Group(name, summary, children...)
|
||||||
command.record.rootOnly = true
|
command.record.rootOnly = true
|
||||||
return command
|
return command
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithError returns a new declaration that also retains a non-nil err for
|
// WithError returns a new Command declaration that also retains a non-nil err
|
||||||
// installation. Earlier declaration and child errors remain available, and
|
// for installation. Earlier declaration and child errors remain available,
|
||||||
// wrapped error identity is preserved. A nil err returns command unchanged.
|
// and wrapped error identity is preserved. A nil err returns command unchanged.
|
||||||
func WithError(command Command, err error) Command {
|
func WithError(command Command, err error) Command {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return command
|
return command
|
||||||
@@ -294,14 +311,17 @@ func WithError(command Command, err error) Command {
|
|||||||
return Command{record: &record}
|
return Command{record: &record}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NoCommand explicitly represents an absent optional declaration. Installation
|
// NoCommand explicitly represents an absent optional command declaration.
|
||||||
// skips it, but reports errors attached to it; a zero Command is invalid and is
|
// Installation skips it, but reports errors attached to it; a zero Command is
|
||||||
// not an absence marker.
|
// invalid and is not an absence marker. Routing rules have no absence marker.
|
||||||
func NoCommand() Command { return Command{record: &commandRecord{absent: true}} }
|
func NoCommand() Command { return Command{record: &commandRecord{absent: true}} }
|
||||||
|
|
||||||
// Alias captures a command-path alias from path to target. Paths are
|
// Alias captures a command-path alias from path to target. Paths are
|
||||||
// whitespace-separated sequences of command names; construction issues are
|
// whitespace-separated sequences of command names relative to the application
|
||||||
// retained for New or App.Register to report.
|
// root. The target must resolve to a canonical command or group; aliases do not
|
||||||
|
// add operands or transform flag values. Construction issues are retained for
|
||||||
|
// New or App.Register to report. Identical mappings are accepted, while
|
||||||
|
// different targets for the same source path are installation errors.
|
||||||
func Alias(path, target string) RoutingRule {
|
func Alias(path, target string) RoutingRule {
|
||||||
record := &routingRuleRecord{
|
record := &routingRuleRecord{
|
||||||
kind: commandAliasRule,
|
kind: commandAliasRule,
|
||||||
@@ -314,7 +334,10 @@ func Alias(path, target string) RoutingRule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FlagAlias captures an application-wide flag spelling alias. Spellings must
|
// FlagAlias captures an application-wide flag spelling alias. Spellings must
|
||||||
// be one long flag name or one individual short spelling.
|
// be one long flag name or one individual short spelling. The mapping applies
|
||||||
|
// wherever its canonical target flag is available and contributes nothing on
|
||||||
|
// commands without that flag. Identical mappings are accepted; different
|
||||||
|
// targets for the same source spelling in this scope are installation errors.
|
||||||
func FlagAlias(spelling, target string) RoutingRule {
|
func FlagAlias(spelling, target string) RoutingRule {
|
||||||
record := &routingRuleRecord{
|
record := &routingRuleRecord{
|
||||||
kind: globalFlagAliasRule,
|
kind: globalFlagAliasRule,
|
||||||
@@ -327,7 +350,10 @@ func FlagAlias(spelling, target string) RoutingRule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FlagAliasFor captures a flag spelling alias scoped to a canonical command
|
// FlagAliasFor captures a flag spelling alias scoped to a canonical command
|
||||||
// path.
|
// path, not an alias path. The command and canonical target flag must be
|
||||||
|
// available in the complete installation. Scoped mappings take precedence over
|
||||||
|
// application-wide mappings with the same source spelling. Identical mappings
|
||||||
|
// are accepted; competing targets in the same scope are installation errors.
|
||||||
func FlagAliasFor(commandPath, spelling, target string) RoutingRule {
|
func FlagAliasFor(commandPath, spelling, target string) RoutingRule {
|
||||||
record := &routingRuleRecord{
|
record := &routingRuleRecord{
|
||||||
kind: scopedFlagAliasRule,
|
kind: scopedFlagAliasRule,
|
||||||
|
|||||||
6
doc.go
6
doc.go
@@ -1,5 +1,7 @@
|
|||||||
// Package commandkit provides the core instance-owned CLI dispatcher, typed
|
// Package commandkit provides the core instance-owned CLI dispatcher, typed
|
||||||
// command declarations, input bindings, and invocation outcomes. Optional
|
// command declarations, application-owned command and flag aliases, input
|
||||||
// integration modules provide ConfigKit and PromptKit-specific declarations;
|
// bindings, and invocation outcomes. New and App.Register accept canonical
|
||||||
|
// Command declarations and RoutingRule declarations together. Optional
|
||||||
|
// integration modules provide ConfigKit and PromptKit-specific commands;
|
||||||
// importing those modules does not install their commands.
|
// importing those modules does not install their commands.
|
||||||
package commandkit
|
package commandkit
|
||||||
|
|||||||
68
docs/cli.md
68
docs/cli.md
@@ -83,6 +83,74 @@ Typed inputs keep supplied state separate from defaults. An explicit empty,
|
|||||||
false, or zero value is supplied. Help and version are available without
|
false, or zero value is supplied. Help and version are available without
|
||||||
constructing application command inputs.
|
constructing application command inputs.
|
||||||
|
|
||||||
|
## Application-owned aliases
|
||||||
|
|
||||||
|
Applications can install command and flag aliases alongside their canonical
|
||||||
|
commands. Aliases route an invocation to existing behavior; they do not add
|
||||||
|
handlers, commands, or flags to a canonical command family.
|
||||||
|
|
||||||
|
Command alias paths are sequences of command tokens relative to the application
|
||||||
|
root. The executable name, flags, flag values, and operands are not part of an
|
||||||
|
alias path. Matching uses complete tokens and chooses the longest matching
|
||||||
|
path. A group alias preserves its remaining command tokens, and aliases can
|
||||||
|
compose through chains. For example, `list collections` can route to
|
||||||
|
`promptkit list collections`, while `pk` can route to the `promptkit` group.
|
||||||
|
The target must resolve to a canonical command or group.
|
||||||
|
|
||||||
|
Aliases do not rewrite operands or supply arguments. In particular,
|
||||||
|
`promptkit test KIND` remains one canonical command whose `KIND` is an operand;
|
||||||
|
`all` is an operand value, not a child command. `smoke → promptkit test` is a
|
||||||
|
valid shortcut because it preserves the caller's `KIND` value.
|
||||||
|
|
||||||
|
Flag aliases can be application-wide or scoped to a canonical command path.
|
||||||
|
An application-wide alias is effective wherever its canonical target flag is
|
||||||
|
available and is ignored elsewhere. A scoped alias requires its canonical
|
||||||
|
command and target flag to be installed. The selected canonical command
|
||||||
|
determines applicable scoped aliases, so the alias also works when that
|
||||||
|
command is reached through another command path. A scoped mapping takes
|
||||||
|
precedence over an application-wide mapping with the same spelling.
|
||||||
|
|
||||||
|
Only tokens parsed as flag names participate in flag aliasing. Values, operands,
|
||||||
|
and tokens following `--` remain unchanged. This includes values that look like
|
||||||
|
flags, such as `--message=--hlep`. Aliased flags use the target's canonical
|
||||||
|
binding, conversion, default, required-value check, presence, and repetition
|
||||||
|
behavior. Mixed spellings preserve occurrence order: singleton flags use the
|
||||||
|
last successfully parsed occurrence, every supplied occurrence is converted,
|
||||||
|
and repeatable flags accumulate values in order.
|
||||||
|
|
||||||
|
Flag aliases accept one long spelling or one individual short spelling. Short
|
||||||
|
aliases use the canonical short-option cluster and attached-value grammar:
|
||||||
|
|
||||||
|
| Input | Interpretation |
|
||||||
|
| --- | --- |
|
||||||
|
| `-vq` | Two boolean flags, `--verbose` and `--quiet`. |
|
||||||
|
| `-cblue` or `-c blue` | Value `blue` for a value-taking `-c` flag. |
|
||||||
|
| `-vqcblue` | `-v`, `-q`, then `--color=blue`. |
|
||||||
|
| `-cvq` | Value `vq` for a value-taking `-c` flag. |
|
||||||
|
| `-v=false` | Explicit false for the boolean flag `-v`. |
|
||||||
|
| `-c=` | An explicitly supplied empty string. |
|
||||||
|
|
||||||
|
Boolean flags use presence as true; an explicit boolean value requires `=`.
|
||||||
|
The first value-taking short flag consumes the remaining cluster suffix or the
|
||||||
|
next argument. A consumed value is never interpreted as an alias. The canonical
|
||||||
|
short-option empty-value form `-c=` supplies an empty value, consistently with
|
||||||
|
`--color=` and `-c ""`.
|
||||||
|
|
||||||
|
Help reached through an alias describes the canonical command's flags and
|
||||||
|
operands while using the entered route in usage. It identifies the canonical
|
||||||
|
target, lists applicable aliases separately, and supports global help aliases
|
||||||
|
such as `--hlep → --help`. Help for an alias-only prefix lists its routes
|
||||||
|
without creating a command group; the bare incomplete prefix points to that
|
||||||
|
help. Help and alias registration do not construct command inputs or invoke
|
||||||
|
acquisition callbacks.
|
||||||
|
|
||||||
|
Usage diagnostics preserve the command path and flag spelling the caller used.
|
||||||
|
When a command alias resolved successfully, usage identifies its canonical
|
||||||
|
target while retaining the entered path in the diagnostic. An unresolved route
|
||||||
|
receives no invented canonical label. A flag conversion error identifies the
|
||||||
|
entered flag spelling and the failing occurrence while preserving the original
|
||||||
|
cause.
|
||||||
|
|
||||||
## Configuration scopes
|
## Configuration scopes
|
||||||
|
|
||||||
An application installs the declaration returned by
|
An application installs the declaration returned by
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
# Consumer guide
|
# Consumer guide
|
||||||
|
|
||||||
CommandKit is distributed as three Go modules. Start with the core and add an
|
CommandKit is distributed as three Go modules. Start with the core and add an
|
||||||
integration module only when the application uses that library. Each module
|
integration module only when the application uses that library. Core and
|
||||||
returns core `commandkit.Command` declarations, which the application installs
|
integration command factories return canonical `commandkit.Command` values.
|
||||||
through `commandkit.New` or `App.Register`.
|
Applications may install those alongside application-owned `RoutingRule`
|
||||||
|
declarations through `commandkit.New` or `App.Register`; routing rules are
|
||||||
|
installed at the application root, not as group children.
|
||||||
|
|
||||||
## Module selection
|
## Module selection
|
||||||
|
|
||||||
@@ -83,6 +85,45 @@ values convert before input construction. A command's `NewInput` creates input
|
|||||||
only when execution reaches it; help does not construct caller input or run
|
only when execution reaches it; help does not construct caller input or run
|
||||||
provider callbacks.
|
provider callbacks.
|
||||||
|
|
||||||
|
## Application-owned aliases
|
||||||
|
|
||||||
|
Pass commands and aliases together as declarations. Integration factories can
|
||||||
|
be called directly in the installation list, and command-scoped flag aliases
|
||||||
|
name the canonical command path:
|
||||||
|
|
||||||
|
```go
|
||||||
|
app, err := commandkit.New(
|
||||||
|
identity,
|
||||||
|
ckprompt.Command(suite),
|
||||||
|
commandkit.Alias("pk", "promptkit"),
|
||||||
|
commandkit.FlagAliasFor("promptkit show prompt", "--fmt", "--format"),
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
For a reusable collection, use `[]commandkit.Declaration`. Go does not expand a
|
||||||
|
`[]commandkit.Command` into `...commandkit.Declaration`, so convert command-only
|
||||||
|
slices when composing them with routing rules:
|
||||||
|
|
||||||
|
```go
|
||||||
|
commands := []commandkit.Command{ckprompt.Command(suite)}
|
||||||
|
declarations := make([]commandkit.Declaration, 0, len(commands)+1)
|
||||||
|
for _, command := range commands {
|
||||||
|
declarations = append(declarations, command)
|
||||||
|
}
|
||||||
|
declarations = append(declarations, commandkit.Alias("pk", "promptkit"))
|
||||||
|
app, err := commandkit.New(identity, declarations...)
|
||||||
|
```
|
||||||
|
|
||||||
|
New and Register validate each supplied batch before publishing its commands
|
||||||
|
and routing rules. Factories capture their inputs eagerly; malformed or
|
||||||
|
conflicting rules are reported at installation. Identical rules are safe to
|
||||||
|
contribute from reusable helpers. The [core example](../../examples/core) and
|
||||||
|
[composed consumer](../../integrations/ckprompt/examples/composed) show complete
|
||||||
|
maintained wiring with ordinary command behavior alongside aliases. The
|
||||||
|
[application-owned aliases roadmap](../roadmap/aliases.md) records the routing
|
||||||
|
contract; the [CLI reference](../cli.md#application-owned-aliases) describes
|
||||||
|
operator-visible behavior.
|
||||||
|
|
||||||
## ConfigKit scopes
|
## ConfigKit scopes
|
||||||
|
|
||||||
For an application with one ordinary app configuration loader,
|
For an application with one ordinary app configuration loader,
|
||||||
|
|||||||
@@ -11,6 +11,12 @@ The [README](../README.md) provides project orientation; the
|
|||||||
[documentation policy](policy/documentation.md#canonical-ownership) assigns
|
[documentation policy](policy/documentation.md#canonical-ownership) assigns
|
||||||
canonical documentation owners.
|
canonical documentation owners.
|
||||||
|
|
||||||
|
While application-owned alias work is active, use its
|
||||||
|
[feature roadmap](roadmap/aliases.md), [implementation sequence](roadmap/implementation.md),
|
||||||
|
and accepted [ADR 0013](adr/0013-application-alias-routing.md) for scope,
|
||||||
|
progress, and architectural decisions. Keep deferred capabilities in
|
||||||
|
[deferred work](roadmap/deferred.md).
|
||||||
|
|
||||||
## Initial orientation
|
## Initial orientation
|
||||||
|
|
||||||
1. Inspect the worktree and preserve unrelated staged and unstaged changes.
|
1. Inspect the worktree and preserve unrelated staged and unstaged changes.
|
||||||
@@ -47,6 +53,7 @@ canonical documentation owners.
|
|||||||
| Task | Read |
|
| Task | Read |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Implemented CLI behavior | [CLI reference](cli.md) |
|
| Implemented CLI behavior | [CLI reference](cli.md) |
|
||||||
|
| Application-owned aliases | [Feature roadmap](roadmap/aliases.md), [implementation plan](roadmap/implementation.md), and [ADR 0013](adr/0013-application-alias-routing.md) while this work is active |
|
||||||
| Public API usage and standard-suite wiring | [Consumer guide](consumers/guide.md), [ckconfig package](../integrations/ckconfig/doc.go), [ckprompt package](../integrations/ckprompt/doc.go), and applicable ADRs |
|
| Public API usage and standard-suite wiring | [Consumer guide](consumers/guide.md), [ckconfig package](../integrations/ckconfig/doc.go), [ckprompt package](../integrations/ckprompt/doc.go), and applicable ADRs |
|
||||||
| Dispatcher, registration, or typed inputs | [Command declarations](../command.go), [bindings](../binding.go), [invocation](../execute.go), and [ADR 0001](adr/0001-command-dispatch-and-inputs.md), refined by [ADR 0011](adr/0011-captured-declarations-and-installation.md) |
|
| Dispatcher, registration, or typed inputs | [Command declarations](../command.go), [bindings](../binding.go), [invocation](../execute.go), and [ADR 0001](adr/0001-command-dispatch-and-inputs.md), refined by [ADR 0011](adr/0011-captured-declarations-and-installation.md) |
|
||||||
| Backend integration | [Internal overview](internal/overview.md), [adapter implementation](../internal/cobra/parse.go), and [ADR 0002](adr/0002-cobra-backend.md) |
|
| Backend integration | [Internal overview](internal/overview.md), [adapter implementation](../internal/cobra/parse.go), and [ADR 0002](adr/0002-cobra-backend.md) |
|
||||||
|
|||||||
@@ -8,18 +8,30 @@ this page inventories the implemented owners.
|
|||||||
|
|
||||||
## Root `commandkit`
|
## Root `commandkit`
|
||||||
|
|
||||||
The root facade owns captured command declarations, binding conversion,
|
The root facade owns captured canonical command declarations, application-owned
|
||||||
invocation coordination, the per-App registry, standard help/version commands,
|
routing rules, binding conversion, invocation coordination, the per-App
|
||||||
and outcome/diagnostic delivery. Its dependency graph has no ConfigKit or
|
registries, standard help/version commands, and outcome/diagnostic delivery.
|
||||||
PromptKit integration dependency. Applications provide typed callbacks and
|
Its dependency graph has no ConfigKit or PromptKit integration dependency.
|
||||||
choose when to install declarations.
|
Applications provide typed callbacks and install routing rules alongside the
|
||||||
|
complete canonical command tree through the same batch API.
|
||||||
|
|
||||||
The shared command compiler prepares supplied values before constructing input.
|
Canonical commands own handlers and parsing/help metadata; routing rules resolve
|
||||||
The batch installer validates retained issues, placement, and collisions before
|
entered command paths and flag spellings to those canonical records without
|
||||||
mutating the registry. Each invocation uses one routing snapshot and backend
|
adding command nodes or handlers. The shared command compiler prepares supplied
|
||||||
session; a successful registration invalidates the snapshot for a later
|
values before constructing input. The batch installer validates retained
|
||||||
invocation. The root package owns app input and callback execution, not provider
|
issues, placement, and collisions across both registries before publishing
|
||||||
configuration semantics.
|
either. Each invocation uses one routing snapshot and backend session; a
|
||||||
|
successful registration invalidates the snapshot for a later invocation. The
|
||||||
|
root package owns app input and callback execution, not provider configuration
|
||||||
|
semantics.
|
||||||
|
|
||||||
|
## `internal/alias` and `internal/commandpath`
|
||||||
|
|
||||||
|
`internal/commandpath` provides shared path parsing and token operations used by
|
||||||
|
declaration capture, routing, and backend selection. `internal/alias` compiles
|
||||||
|
application routing rules, resolves command prefixes while retaining token
|
||||||
|
provenance, and validates command-route termination. Neither package owns
|
||||||
|
handlers, application factories, or backend types.
|
||||||
|
|
||||||
## `internal/commandmeta`
|
## `internal/commandmeta`
|
||||||
|
|
||||||
|
|||||||
@@ -83,18 +83,24 @@ describes the outcome and delivery model.
|
|||||||
|
|
||||||
Use explicit instance construction and composable command registration, not
|
Use explicit instance construction and composable command registration, not
|
||||||
package-global or import-time registries. Applications and integrations supply
|
package-global or import-time registries. Applications and integrations supply
|
||||||
the same captured command declarations through the core installation API.
|
captured canonical command declarations through the core installation API.
|
||||||
Declaration factories capture metadata, bindings, and callback references eagerly;
|
Declaration factories capture metadata, bindings, and callback references eagerly;
|
||||||
construction errors remain attached for installation to report. Capture the
|
construction errors remain attached for installation to report. Capture the
|
||||||
metadata collections needed to keep later edits to a source declaration from
|
metadata collections needed to keep later edits to a source declaration from
|
||||||
changing a returned command. Application callback objects retain their ownership.
|
changing a returned command. Application callback objects retain their ownership.
|
||||||
One definition supplies routing, parsing, and help metadata.
|
Canonical command declarations own handlers, parsing metadata, and help
|
||||||
|
metadata. Applications may provide separate routing rules in the same
|
||||||
|
installation batch; these rules resolve invocations to canonical commands and
|
||||||
|
do not add command nodes, handlers, or flags.
|
||||||
|
|
||||||
Validate each installation batch before mutating the instance registry. A failed
|
Validate each installation batch before mutating the instance registry. A failed
|
||||||
construction yields no usable application; a failed later registration leaves the
|
construction yields no usable application; a failed later registration leaves the
|
||||||
existing registry intact. Separate successful registration calls are not one
|
existing registry intact. Separate successful registration calls are not one
|
||||||
transaction. Duplicate paths and attempts to replace reserved commands report
|
transaction. Duplicate canonical paths and attempts to replace reserved commands
|
||||||
errors rather than letting registration order select a winner.
|
report errors rather than letting registration order select a winner. Identical
|
||||||
|
routing rules are idempotent; competing targets for the same source and scope
|
||||||
|
report an error. Command-scoped flag routing refers to an exact canonical
|
||||||
|
command path.
|
||||||
|
|
||||||
Explicit absent-command declarations contribute no commands, help entries, or
|
Explicit absent-command declarations contribute no commands, help entries, or
|
||||||
namespace claims; attached errors are reported before skipping absence. An invalid
|
namespace claims; attached errors are reported before skipping absence. An invalid
|
||||||
|
|||||||
@@ -694,6 +694,15 @@ all newly documented alias commands also ran successfully.
|
|||||||
canonical owner; no future alias APIs are described as existing before their
|
canonical owner; no future alias APIs are described as existing before their
|
||||||
implementation is present. Prose checks and relevant examples agree.
|
implementation is present. Prose checks and relevant examples agree.
|
||||||
|
|
||||||
|
**Completed:** GoDoc now distinguishes canonical `Command` declarations from
|
||||||
|
root-installed routing rules and documents captured errors, invalid nil/zero
|
||||||
|
values, idempotent duplicates, canonical flag scopes, and invocation paths. The
|
||||||
|
CLI reference, consumer guide, maintained example READMEs, internal overview,
|
||||||
|
architecture policy, and active developer navigation describe the implemented
|
||||||
|
contracts and integration wiring. Local Markdown links and fragments resolved;
|
||||||
|
`git diff --check -- '*.md' COPYRIGHT` and the cached equivalent passed. The
|
||||||
|
deferred-work record and accepted ADR were left unchanged.
|
||||||
|
|
||||||
## Stage 16: Record compatibility impact for the next core release
|
## Stage 16: Record compatibility impact for the next core release
|
||||||
|
|
||||||
**Work**
|
**Work**
|
||||||
|
|||||||
@@ -16,6 +16,15 @@ alias. Its declaration slice mixes the canonical group and routing rules as
|
|||||||
`Invocation.CanonicalPath()` so the alternate route and its canonical target
|
`Invocation.CanonicalPath()` so the alternate route and its canonical target
|
||||||
are visible in the output.
|
are visible in the output.
|
||||||
|
|
||||||
|
Integration factories also return `commandkit.Command` and can be passed
|
||||||
|
directly to `New` beside `commandkit.Alias` or flag-alias declarations. For a
|
||||||
|
command-only `[]commandkit.Command`, convert its elements into a
|
||||||
|
`[]commandkit.Declaration` before appending routing rules; Go cannot expand the
|
||||||
|
command slice as `...commandkit.Declaration`. The
|
||||||
|
[consumer guide](../../docs/consumers/guide.md#application-owned-aliases)
|
||||||
|
shows both forms, and the [composed consumer](../../integrations/ckprompt/examples/composed)
|
||||||
|
is the maintained integration example.
|
||||||
|
|
||||||
These aliases use the current local CommandKit source. Published CommandKit
|
These aliases use the current local CommandKit source. Published CommandKit
|
||||||
v0.2.0 does not include alias declarations; consumers can use them after the
|
v0.2.0 does not include alias declarations; consumers can use them after the
|
||||||
next core release. Repository contributors can run its test with
|
next core release. Repository contributors can run its test with
|
||||||
|
|||||||
15
identity.go
15
identity.go
@@ -28,7 +28,9 @@ type Streams struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Invocation provides execution context and output streams to a command
|
// Invocation provides execution context and output streams to a command
|
||||||
// handler.
|
// handler. For command aliases, EnteredPath and CanonicalPath expose the route
|
||||||
|
// the caller used and the selected canonical command path. Both omit the
|
||||||
|
// executable name, flags, and operands.
|
||||||
type Invocation struct {
|
type Invocation struct {
|
||||||
// Context carries caller cancellation and request-scoped values.
|
// Context carries caller cancellation and request-scoped values.
|
||||||
Context context.Context
|
Context context.Context
|
||||||
@@ -39,10 +41,13 @@ type Invocation struct {
|
|||||||
canonicalPath string
|
canonicalPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnteredPath returns the command path used to invoke the selected command.
|
// EnteredPath returns the command path used to invoke the selected command,
|
||||||
// It is empty for an Invocation that was not created by command execution.
|
// excluding the executable name, flags, and operands. It is empty for an
|
||||||
|
// Invocation that was not created by command execution.
|
||||||
func (inv Invocation) EnteredPath() string { return inv.enteredPath }
|
func (inv Invocation) EnteredPath() string { return inv.enteredPath }
|
||||||
|
|
||||||
// CanonicalPath returns the selected command's canonical path.
|
// CanonicalPath returns the selected command's canonical path after resolving
|
||||||
// It is empty for an Invocation that was not created by command execution.
|
// aliases, excluding the executable name, flags, and operands. It equals
|
||||||
|
// EnteredPath for a direct canonical invocation and is empty for an Invocation
|
||||||
|
// that was not created by command execution.
|
||||||
func (inv Invocation) CanonicalPath() string { return inv.canonicalPath }
|
func (inv Invocation) CanonicalPath() string { return inv.canonicalPath }
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Package ckconfig provides CommandKit's optional ConfigKit integration. Its
|
// Package ckconfig provides CommandKit's optional ConfigKit integration. Its
|
||||||
// declarations install the standard config command family through the core
|
// canonical Command declaration installs the standard config command family
|
||||||
// commandkit package. Source reports preserve ConfigKit's destination,
|
// through the core commandkit package. Applications can install CommandKit
|
||||||
// placement, and version-check evidence; importing ckconfig alone installs no
|
// RoutingRule declarations alongside it through commandkit.New or
|
||||||
// commands.
|
// App.Register. Source reports preserve ConfigKit's destination, placement,
|
||||||
|
// and version-check evidence; importing ckconfig alone installs no commands.
|
||||||
package ckconfig
|
package ckconfig
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
// Package ckprompt provides optional PromptKit configuration comparison,
|
// Package ckprompt provides optional PromptKit configuration comparison,
|
||||||
// prompt catalogs, named inspections, requirements discovery and validation,
|
// prompt catalogs, named inspections, requirements discovery and validation,
|
||||||
// and native smoke testing for CommandKit. Importing ckprompt installs neither
|
// and native smoke testing for CommandKit. Its command factories return
|
||||||
// family; callers compose its declarations with core and ckconfig explicitly.
|
// canonical commandkit.Command declarations. Applications can install
|
||||||
|
// commandkit.RoutingRule aliases alongside them through commandkit.New or
|
||||||
|
// App.Register. Importing ckprompt installs neither family; callers compose its
|
||||||
|
// declarations with core and ckconfig explicitly.
|
||||||
package ckprompt
|
package ckprompt
|
||||||
|
|||||||
@@ -40,6 +40,14 @@ shortens the PromptKit group. `--fmt` is scoped to named prompt inspection and
|
|||||||
`--hlep` is available globally. The `smoke` shortcut retains the canonical
|
`--hlep` is available globally. The `smoke` shortcut retains the canonical
|
||||||
`promptkit test KIND` operand, including the special `all` aggregate.
|
`promptkit test KIND` operand, including the special `all` aggregate.
|
||||||
|
|
||||||
|
The composition calls `ckconfig.Command(suite)` and `ckprompt.Command(definitions)`
|
||||||
|
directly in a `[]commandkit.Declaration` with root-installed routing rules.
|
||||||
|
Group children remain canonical `Command` values. For command-only slices,
|
||||||
|
convert each `Command` to `Declaration` before appending aliases. The
|
||||||
|
[consumer guide](../../../../docs/consumers/guide.md#application-owned-aliases)
|
||||||
|
includes minimal direct-call and slice-conversion examples; this README documents
|
||||||
|
the maintained complete consumer.
|
||||||
|
|
||||||
The sources and session store are small in-memory fixtures. `--campaign` is a
|
The sources and session store are small in-memory fixtures. `--campaign` is a
|
||||||
typed selector shared by the app and PromptKit configuration scopes. Their
|
typed selector shared by the app and PromptKit configuration scopes. Their
|
||||||
`ckconfig.Suite[*inspectionInput, *runCache]` uses `NewInput` to initialize each
|
`ckconfig.Suite[*inspectionInput, *runCache]` uses `NewInput` to initialize each
|
||||||
|
|||||||
11
registry.go
11
registry.go
@@ -9,10 +9,13 @@ import (
|
|||||||
"gitea.maximumdirect.net/eric/CommandKit/internal/commandpath"
|
"gitea.maximumdirect.net/eric/CommandKit/internal/commandpath"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Register adds a batch of declarations to the application. The complete batch
|
// Register adds a batch of canonical commands and routing rules to the
|
||||||
// is validated before registry mutation; on error the registry and its derived
|
// application. The complete batch is validated before registry mutation; on
|
||||||
// command metadata remain unchanged. A successful nonempty batch becomes
|
// error the registry and its derived command metadata remain unchanged. A
|
||||||
// visible to later invocations. Declarations remain reusable.
|
// successful nonempty batch becomes visible to later invocations. Declarations
|
||||||
|
// remain reusable. Identical duplicate routing mappings are accepted, while
|
||||||
|
// competing targets for the same source are errors. Nil and zero declarations
|
||||||
|
// are invalid; use NoCommand for an intentionally absent optional command.
|
||||||
func (a *App) Register(declarations ...Declaration) error {
|
func (a *App) Register(declarations ...Declaration) error {
|
||||||
return a.installBatch(declarations...)
|
return a.installBatch(declarations...)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user