From 333a630cf17d2dcb9ae7749e8bbebc1e27e0b087 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Mon, 19 Oct 2026 19:59:41 +0000 Subject: [PATCH] Document application-owned alias contracts --- app.go | 16 ++-- command.go | 74 +++++++++++++------ doc.go | 6 +- docs/cli.md | 68 +++++++++++++++++ docs/consumers/guide.md | 47 +++++++++++- docs/development.md | 7 ++ docs/internal/overview.md | 34 ++++++--- docs/policy/architecture.md | 14 +++- docs/roadmap/implementation.md | 9 +++ examples/core/README.md | 9 +++ identity.go | 15 ++-- integrations/ckconfig/doc.go | 9 ++- integrations/ckprompt/doc.go | 7 +- .../ckprompt/examples/composed/README.md | 8 ++ registry.go | 11 ++- 15 files changed, 270 insertions(+), 64 deletions(-) diff --git a/app.go b/app.go index 6b4b99f..041ae14 100644 --- a/app.go +++ b/app.go @@ -33,12 +33,18 @@ type commandNode struct { command Command } -// New creates an application with the supplied identity and initial -// declarations. +// New creates an application with the supplied identity and initial canonical +// 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 -// version commands and caller declarations as one batch; an invalid declaration -// returns an error without exposing an App. The root help and version names are -// reserved for generated commands. +// version commands and caller declarations as one batch; an invalid, nil, or +// zero declaration returns an error without exposing an App. Factory issues +// 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) { if identity.Name == "" { return nil, fmt.Errorf("application identity name must not be empty") diff --git a/command.go b/command.go index 4234450..d2904e5 100644 --- a/command.go +++ b/command.go @@ -10,21 +10,32 @@ import ( "gitea.maximumdirect.net/eric/CommandKit/internal/commandpath" ) -// Command is an opaque, reusable declaration captured by NewCommand, Group, -// RootGroup, or an integration. Capture copies declaration slices but retains -// callback and caller-owned object references. Installation validates the -// declaration before changing an App registry. +// Command is an opaque, reusable declaration of canonical commands and groups. +// NewCommand, Group, RootGroup, and integration command factories return +// Command values. Capture copies declaration slices but retains callback and +// caller-owned object references. Installation validates the declaration +// before changing an App registry. type Command struct{ record *commandRecord } -// Declaration is a captured command or application routing rule accepted by -// New and App.Register. Only CommandKit declaration types implement it. +// Declaration is a captured canonical command or application routing rule +// 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 { 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 } func (RoutingRule) commandkitDeclaration() {} @@ -236,9 +247,11 @@ func (c typedCommand[I]) prepare(flagValues, flagSpellings [][]string, operands }, nil } -// NewCommand captures a typed command declaration without constructing its -// input or running callbacks. Construction issues are retained and reported -// when the declaration is installed. The returned declaration can be reused. +// NewCommand captures a typed canonical command declaration without +// constructing its input or running callbacks. It copies the spec's flags, +// 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 { return captureCommand(spec, nil) } @@ -260,25 +273,29 @@ func captureCommand[I any](spec CommandSpec[I], privateRun func(commandExecution return Command{record: record} } -// Group captures a non-runnable command group and a copy of its relative child -// slice. Child declarations remain reusable and are not recursively copied. +// Group captures a non-runnable canonical command group and a copy of its +// 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 { record := &commandRecord{name: name, summary: summary, children: append([]Command(nil), children...)} captureComposition(record) return Command{record: record} } -// RootGroup captures a group that may only be installed at the application root. -// Installation rejects it below any other command, including through wrappers. +// RootGroup captures a group that may only be installed at the application +// 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 { command := Group(name, summary, children...) command.record.rootOnly = true return command } -// WithError returns a new declaration that also retains a non-nil err for -// installation. Earlier declaration and child errors remain available, and -// wrapped error identity is preserved. A nil err returns command unchanged. +// WithError returns a new Command declaration that also retains a non-nil err +// for installation. Earlier declaration and child errors remain available, +// and wrapped error identity is preserved. A nil err returns command unchanged. func WithError(command Command, err error) Command { if err == nil { return command @@ -294,14 +311,17 @@ func WithError(command Command, err error) Command { return Command{record: &record} } -// NoCommand explicitly represents an absent optional declaration. Installation -// skips it, but reports errors attached to it; a zero Command is invalid and is -// not an absence marker. +// NoCommand explicitly represents an absent optional command declaration. +// Installation skips it, but reports errors attached to it; a zero Command is +// invalid and is not an absence marker. Routing rules have no absence marker. func NoCommand() Command { return Command{record: &commandRecord{absent: true}} } // Alias captures a command-path alias from path to target. Paths are -// whitespace-separated sequences of command names; construction issues are -// retained for New or App.Register to report. +// whitespace-separated sequences of command names relative to the application +// 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 { record := &routingRuleRecord{ kind: commandAliasRule, @@ -314,7 +334,10 @@ func Alias(path, target string) RoutingRule { } // 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 { record := &routingRuleRecord{ kind: globalFlagAliasRule, @@ -327,7 +350,10 @@ func FlagAlias(spelling, target string) RoutingRule { } // 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 { record := &routingRuleRecord{ kind: scopedFlagAliasRule, diff --git a/doc.go b/doc.go index fc7e828..a28ee5e 100644 --- a/doc.go +++ b/doc.go @@ -1,5 +1,7 @@ // Package commandkit provides the core instance-owned CLI dispatcher, typed -// command declarations, input bindings, and invocation outcomes. Optional -// integration modules provide ConfigKit and PromptKit-specific declarations; +// command declarations, application-owned command and flag aliases, input +// 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. package commandkit diff --git a/docs/cli.md b/docs/cli.md index 95dc071..ad68d8f 100644 --- a/docs/cli.md +++ b/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 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 An application installs the declaration returned by diff --git a/docs/consumers/guide.md b/docs/consumers/guide.md index 52344f3..29ddc60 100644 --- a/docs/consumers/guide.md +++ b/docs/consumers/guide.md @@ -1,9 +1,11 @@ # Consumer guide 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 -returns core `commandkit.Command` declarations, which the application installs -through `commandkit.New` or `App.Register`. +integration module only when the application uses that library. Core and +integration command factories return canonical `commandkit.Command` values. +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 @@ -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 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 For an application with one ordinary app configuration loader, diff --git a/docs/development.md b/docs/development.md index af5be1a..d9c8730 100644 --- a/docs/development.md +++ b/docs/development.md @@ -11,6 +11,12 @@ The [README](../README.md) provides project orientation; the [documentation policy](policy/documentation.md#canonical-ownership) assigns 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 1. Inspect the worktree and preserve unrelated staged and unstaged changes. @@ -47,6 +53,7 @@ canonical documentation owners. | Task | Read | | --- | --- | | 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 | | 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) | diff --git a/docs/internal/overview.md b/docs/internal/overview.md index 53019d5..b5e28c3 100644 --- a/docs/internal/overview.md +++ b/docs/internal/overview.md @@ -8,18 +8,30 @@ this page inventories the implemented owners. ## Root `commandkit` -The root facade owns captured command declarations, binding conversion, -invocation coordination, the per-App registry, standard help/version commands, -and outcome/diagnostic delivery. Its dependency graph has no ConfigKit or -PromptKit integration dependency. Applications provide typed callbacks and -choose when to install declarations. +The root facade owns captured canonical command declarations, application-owned +routing rules, binding conversion, invocation coordination, the per-App +registries, standard help/version commands, and outcome/diagnostic delivery. +Its dependency graph has no ConfigKit or PromptKit integration dependency. +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. -The batch installer validates retained issues, placement, and collisions before -mutating the registry. 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. +Canonical commands own handlers and parsing/help metadata; routing rules resolve +entered command paths and flag spellings to those canonical records without +adding command nodes or handlers. The shared command compiler prepares supplied +values before constructing input. The batch installer validates retained +issues, placement, and collisions across both registries before publishing +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` diff --git a/docs/policy/architecture.md b/docs/policy/architecture.md index d058bfb..2b58dd4 100644 --- a/docs/policy/architecture.md +++ b/docs/policy/architecture.md @@ -83,18 +83,24 @@ describes the outcome and delivery model. Use explicit instance construction and composable command registration, not 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; construction errors remain attached for installation to report. Capture the metadata collections needed to keep later edits to a source declaration from 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 construction yields no usable application; a failed later registration leaves the existing registry intact. Separate successful registration calls are not one -transaction. Duplicate paths and attempts to replace reserved commands report -errors rather than letting registration order select a winner. +transaction. Duplicate canonical paths and attempts to replace reserved commands +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 namespace claims; attached errors are reported before skipping absence. An invalid diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index b425efd..3e10888 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -694,6 +694,15 @@ all newly documented alias commands also ran successfully. canonical owner; no future alias APIs are described as existing before their 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 **Work** diff --git a/examples/core/README.md b/examples/core/README.md index ec989f0..0846831 100644 --- a/examples/core/README.md +++ b/examples/core/README.md @@ -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 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 v0.2.0 does not include alias declarations; consumers can use them after the next core release. Repository contributors can run its test with diff --git a/identity.go b/identity.go index 57eebd0..2095d9e 100644 --- a/identity.go +++ b/identity.go @@ -28,7 +28,9 @@ type Streams struct { } // 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 { // Context carries caller cancellation and request-scoped values. Context context.Context @@ -39,10 +41,13 @@ type Invocation struct { canonicalPath string } -// EnteredPath returns the command path used to invoke the selected command. -// It is empty for an Invocation that was not created by command execution. +// EnteredPath returns the command path used to invoke the selected command, +// 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 } -// CanonicalPath returns the selected command's canonical path. -// It is empty for an Invocation that was not created by command execution. +// CanonicalPath returns the selected command's canonical path after resolving +// 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 } diff --git a/integrations/ckconfig/doc.go b/integrations/ckconfig/doc.go index 5df3247..2d3b01c 100644 --- a/integrations/ckconfig/doc.go +++ b/integrations/ckconfig/doc.go @@ -1,6 +1,7 @@ // Package ckconfig provides CommandKit's optional ConfigKit integration. Its -// declarations install the standard config command family through the core -// commandkit package. Source reports preserve ConfigKit's destination, -// placement, and version-check evidence; importing ckconfig alone installs no -// commands. +// canonical Command declaration installs the standard config command family +// through the core commandkit package. Applications can install CommandKit +// RoutingRule declarations alongside it through commandkit.New or +// App.Register. Source reports preserve ConfigKit's destination, placement, +// and version-check evidence; importing ckconfig alone installs no commands. package ckconfig diff --git a/integrations/ckprompt/doc.go b/integrations/ckprompt/doc.go index a120228..36fe3a1 100644 --- a/integrations/ckprompt/doc.go +++ b/integrations/ckprompt/doc.go @@ -1,5 +1,8 @@ // Package ckprompt provides optional PromptKit configuration comparison, // prompt catalogs, named inspections, requirements discovery and validation, -// and native smoke testing for CommandKit. Importing ckprompt installs neither -// family; callers compose its declarations with core and ckconfig explicitly. +// and native smoke testing for CommandKit. Its command factories return +// 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 diff --git a/integrations/ckprompt/examples/composed/README.md b/integrations/ckprompt/examples/composed/README.md index 3bb7298..a42eab7 100644 --- a/integrations/ckprompt/examples/composed/README.md +++ b/integrations/ckprompt/examples/composed/README.md @@ -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 `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 typed selector shared by the app and PromptKit configuration scopes. Their `ckconfig.Suite[*inspectionInput, *runCache]` uses `NewInput` to initialize each diff --git a/registry.go b/registry.go index d9d3697..ac94404 100644 --- a/registry.go +++ b/registry.go @@ -9,10 +9,13 @@ import ( "gitea.maximumdirect.net/eric/CommandKit/internal/commandpath" ) -// Register adds a batch of declarations to the application. The complete batch -// is validated before registry mutation; on error the registry and its derived -// command metadata remain unchanged. A successful nonempty batch becomes -// visible to later invocations. Declarations remain reusable. +// Register adds a batch of canonical commands and routing rules to the +// application. The complete batch is validated before registry mutation; on +// error the registry and its derived command metadata remain unchanged. A +// 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 { return a.installBatch(declarations...) }