453 lines
22 KiB
Markdown
453 lines
22 KiB
Markdown
# Consumer guide
|
|
|
|
Start with core to build commands with typed inputs and shared output
|
|
formatting. Add an integration when your application uses ConfigKit or
|
|
PromptKit. The [core command example](#core-commands) below shows the basic
|
|
API; the [maintained examples](../../README.md#examples) show complete
|
|
applications.
|
|
|
|
## Module selection
|
|
|
|
| Import | Use when |
|
|
| --- | --- |
|
|
| `go.maximumdirect.net/commandkit` | You need commands, typed inputs, help, and common output handling. |
|
|
| `go.maximumdirect.net/ckconfig` | You want operators to view, validate, explain, and compare ConfigKit configuration. |
|
|
| `go.maximumdirect.net/ckprompt` | You want operators to inspect PromptKit resources, check and preview requests, or run smoke tests. |
|
|
|
|
The following distinguishes published combinations from the current checkout:
|
|
|
|
| Selection | Core | ckconfig | ckprompt | Upstream libraries and status |
|
|
| --- | --- | --- | --- | --- |
|
|
| Published core | v0.5.0 | — | — | Usable independently. |
|
|
| Published ConfigKit integration | v0.4.0 | v0.4.0 | — | ConfigKit v1.1.0. |
|
|
| Earlier matched integration set | v0.3.0 | v0.2.0 | v0.2.0 | ConfigKit v0.8.0 and PromptKit v0.16.0, using their earlier import paths. |
|
|
| Current source | Local | Local | Local | ConfigKit v1.1.0 and PromptKit v0.20.0; use an aligned workspace until release dependencies are updated. |
|
|
|
|
Install the published core with:
|
|
|
|
```sh
|
|
go get go.maximumdirect.net/commandkit@v0.5.0
|
|
```
|
|
|
|
The integration releases being prepared are ckconfig v0.5.0 and ckprompt v0.5.0. Current
|
|
ckprompt source calls `ckconfig.AdaptDiagnostics`, which is absent from its
|
|
selected released ckconfig v0.4.0 dependency. It therefore needs local
|
|
ckconfig source in the workspace until [release
|
|
alignment](../roadmap/future.md#pending-release-and-integration-work) is
|
|
complete. Published ckconfig v0.3.0 also uses ConfigKit v1.1.0 and does not
|
|
compose with ckprompt v0.2.0's earlier ConfigKit dependency.
|
|
|
|
Importing a module does not install its commands. Pass commands to
|
|
`commandkit.New` or `App.Register` explicitly. ckprompt depends on ckconfig's
|
|
public APIs, but you can install its `promptkit` commands without installing
|
|
the `config` family.
|
|
|
|
## Go compatibility
|
|
|
|
Core declares a consumer minimum of Go 1.26.0; both current integration
|
|
modules require Go 1.27.0. The root module also
|
|
selects ReleaseKit v0.9.0 (`go.maximumdirect.net/releasekit`) as a Go tool, which
|
|
makes that tool's module graph part of the root consumer module requirements
|
|
even though production packages do not import ReleaseKit. The
|
|
[release policy](../policy/release.md#consumer-minimum-and-release-toolchain)
|
|
defines the support assumption. Maintainers use Go 1.27.0 for validation and
|
|
publication; see the [release procedure](../release.md#release-toolchain).
|
|
|
|
## Core commands
|
|
|
|
Declare typed flags and operands with `CommandSpec[I]`, then capture them with
|
|
`NewCommand`. Bindings deliver `Supplied[T]`, which distinguishes an omitted
|
|
default from an explicitly supplied empty, false, or zero value.
|
|
|
|
```go
|
|
package example
|
|
|
|
import (
|
|
"strconv"
|
|
|
|
commandkit "go.maximumdirect.net/commandkit"
|
|
)
|
|
|
|
type GreetInput struct {
|
|
Count commandkit.Supplied[int]
|
|
}
|
|
|
|
func Greeting() commandkit.Command {
|
|
countDefault := 1
|
|
return commandkit.NewCommand(commandkit.CommandSpec[GreetInput]{
|
|
Name: "greet", Summary: "Write a greeting.",
|
|
Flags: []commandkit.Flag[GreetInput]{
|
|
commandkit.ValueFlag(
|
|
commandkit.FlagMeta{Name: "count", Usage: "Number of greetings."},
|
|
&countDefault, strconv.Atoi,
|
|
func(input *GreetInput, value commandkit.Supplied[int]) {
|
|
input.Count = value
|
|
},
|
|
),
|
|
},
|
|
Run: func(invocation commandkit.Invocation, input GreetInput) commandkit.Outcome {
|
|
greetings := []string{}
|
|
for i := 0; i < input.Count.Value; i++ {
|
|
greetings = append(greetings, "Hello!")
|
|
}
|
|
if err := invocation.Reporter().Report(commandkit.Report{Value: greetings}); err != nil {
|
|
return commandkit.Failure("Could not write the greetings.").WithCause(err)
|
|
}
|
|
return commandkit.Success()
|
|
},
|
|
})
|
|
}
|
|
```
|
|
|
|
Install `Greeting()` with `commandkit.New(identity, Greeting())`. By default,
|
|
`greet` prints `Hello!`; `greet --count=2` prints it twice. Add `--format
|
|
json` to receive a JSON list. A count of zero produces an empty list.
|
|
|
|
The application supplies `context.Context`, argv without the executable name,
|
|
and output streams to `App.Execute`; it decides how the returned status maps to
|
|
process exit. `New` installs generated help/version and caller declarations as
|
|
one batch. `Register` validates each call as a batch before changing that App.
|
|
Invalid declaration errors retain their wrapped identity for `errors.Is` and
|
|
`errors.As`.
|
|
|
|
Declarations copy their metadata and slice inputs when captured, retain callback
|
|
references, and can be reused across applications. Copies of a typed binding
|
|
preserve its identity; a separately constructed lookalike does not. Supplied
|
|
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.
|
|
|
|
## Shell completion
|
|
|
|
Install `commandkit.Completion("app")` alongside the application's other
|
|
declarations, using the basename operators actually type. The basename accepts
|
|
letters, digits, underscores, dots, hyphens, and colons, except for a leading
|
|
hyphen. Add a hint to a flag or operand's metadata when a value has useful
|
|
static choices or is a filesystem path:
|
|
|
|
```go
|
|
commandkit.FlagMeta{
|
|
Name: "view",
|
|
Completion: commandkit.CompleteChoices(
|
|
commandkit.CompletionChoice{Value: "text", Description: "Readable text"},
|
|
commandkit.CompletionChoice{Value: "yaml", Description: "YAML output"},
|
|
),
|
|
}
|
|
commandkit.FlagMeta{Name: "config", Completion: commandkit.CompleteFiles()}
|
|
commandkit.ArgumentMeta{Name: "directory", Completion: commandkit.CompleteDirectories()}
|
|
```
|
|
|
|
Hints are suggestions: converters and input validation still decide which
|
|
values are accepted. An omitted hint or `NoCompletion()` gives no value
|
|
suggestions. `CompleteChoices()` with no choices also gives no suggestions.
|
|
Declarations retain copies of supplied choices. The [CLI reference](../cli.md#shell-completion)
|
|
describes supported shells and loading scripts; the [core example](../../examples/core)
|
|
and [composed example](../../integrations/ckprompt/examples/composed) show
|
|
installation alongside aliases and integration commands.
|
|
|
|
## Invocation reporting
|
|
|
|
Use `Invocation.Reporter().Report` to print a value in the selected output
|
|
format. Pass ordinary Go values and, optionally, a `text/template` for text
|
|
output. Use `Warn`, `Info`, `Progress`, and `Debug` for categorized messages.
|
|
Queue a summary with `AtEnd().Info` when it belongs after the handler returns.
|
|
Deferred submissions are accepted until finalization begins; later submissions
|
|
return `ErrReporterClosed`. The [CLI reference](../cli.md#reporting) owns flag
|
|
grammar, visibility, prefixes, and status behavior.
|
|
|
|
Reports use JSON serialization to establish the common value representation
|
|
for YAML and JSON. A text template receives the original value; without one,
|
|
text uses readable scalar or string-list output and YAML for structured values.
|
|
The CLI's shared format selection and the writer-level `RenderReport` use the
|
|
same rules. Use `DecodeYAMLReport` only for safe upstream inspection YAML that
|
|
must become a report value; ordinary strings are not parsed as YAML.
|
|
|
|
Return terminal text with `Failure(message, details...)`, `Usage(message,
|
|
details...)`, or `WithExplanation` on a domain outcome. Attach an optional Go
|
|
error separately with `WithCause`. A cause is preserved for inspection, but it
|
|
does not choose the outcome or become user-facing text. Rendering callbacks
|
|
write to their supplied writer only while the callback runs; they must not
|
|
submit another message or start another rendering operation through the same
|
|
reporter.
|
|
|
|
For example, a handler can report a count as a small mapping:
|
|
|
|
```go
|
|
if err := invocation.Reporter().Report(commandkit.Report{
|
|
Value: map[string]int{"count": input.Count.Value},
|
|
}); err != nil {
|
|
return commandkit.Failure("Could not write the count.").WithCause(err)
|
|
}
|
|
return commandkit.Success()
|
|
```
|
|
|
|
This uses YAML automatically and supports `--format json` without a separate
|
|
encoder. Use `Output()` or `RenderOutput()` instead when you deliberately need
|
|
raw output; `--format` does not reformat those bytes.
|
|
|
|
After `App.Execute`, inspect the returned `Outcome`. Its `Cause` retains the
|
|
operation error; `Presentation.Output` and `Presentation.Diagnostics` report
|
|
delivery state and the first rendering or write errors for their destinations.
|
|
Joined operation errors support `errors.Is` and `errors.As` where the
|
|
underlying errors do. CommandKit does not retain a message transcript.
|
|
Reporter operations for one invocation are safe concurrently, but concurrent
|
|
submission order is not specified. The caller owns the writers passed to
|
|
`Execute`; CommandKit does not close them.
|
|
|
|
## 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
|
|
[Go declarations and GoDoc](../../command.go) own the public API contracts;
|
|
the [CLI reference](../cli.md#application-owned-aliases) owns routing and
|
|
operator-visible behavior.
|
|
|
|
## ConfigKit scopes
|
|
|
|
For an application with one ordinary app configuration loader,
|
|
`ckconfig.AppCommand` supplies the state-free convenience declaration:
|
|
|
|
```go
|
|
app, err := commandkit.New(
|
|
commandkit.Identity{Name: "Example", Version: "dev"},
|
|
ckconfig.AppCommand(loadAppConfig),
|
|
)
|
|
```
|
|
|
|
`loadAppConfig` has the signature
|
|
`func(context.Context, ckconfig.Selection) (*configkit.Result[T], error)`.
|
|
Construction, installation, and help do not call it. `Selection` preserves
|
|
whether config and overlay paths were explicitly supplied, including empty
|
|
values.
|
|
|
|
Use `ckconfig.Command(ckconfig.Suite[I, S]{...})` when the application needs
|
|
multiple named scopes, selector flags, scope-aware input checks, or shared
|
|
acquisition state or a custom comparison provider. A declared `Diff`
|
|
capability uses `Scope.Compare`; `ckconfig.FromConfigKit` and `AppCommand`
|
|
provide the ConfigKit-backed implementation. `Selector.AgainstName` opts a
|
|
selector into an independent right-side flag. Each comparison side gets its
|
|
own initialized and checked input, with only its supplied selectors applied;
|
|
state is shared by the invocation. Factory results remain caller-owned, so
|
|
separate initialization calls do not guarantee distinct objects. See
|
|
[ckconfig.Suite's GoDoc](../../integrations/ckconfig/contracts.go) for factory
|
|
timing, omitted-factory zero values (including nil pointers), and ownership.
|
|
The application owns the state type and what it caches.
|
|
`ckconfig.FromConfigKit` adapts a typed ConfigKit loader without calling it
|
|
during construction. The bundled comparison adapters call the loader for the
|
|
left side before the right, retain left diagnostics if right loading fails,
|
|
and collect diagnostics from both loads before native comparison. Custom
|
|
comparison callbacks own their loading and diagnostics; loader calls need not
|
|
reread sources if the application caches them.
|
|
|
|
The `config` family provides scope discovery, validation, safe effective configuration display,
|
|
source inventory, path explanation, and ConfigKit-backed comparison for the
|
|
capabilities declared by its scopes. The [CLI reference](../cli.md#configuration-scopes)
|
|
owns operator syntax and report details. `Scope.Features` declares optional modes; acquired
|
|
`View.Metadata` and `View.Findings` carry native path metadata and diagnostic
|
|
records when available. `Findings.Messages` contains presentations associated
|
|
with those records, while `Acquired.Diagnostics` carries independent messages.
|
|
Custom explanation providers implement `Explain`; CommandKit renders the
|
|
acquired evidence. See `EffectiveWriter` and `ExplanationReader` in the
|
|
[ckconfig GoDoc](../../integrations/ckconfig/contracts.go) for the provider boundary.
|
|
`ckconfig.AdaptDiagnostics` captures native diagnostic presentation evidence
|
|
for applications providing their own ConfigKit-backed CLI integration.
|
|
Set `Scope.Features.Overrides` only when the loader passes
|
|
`Selection.Overrides` to ConfigKit as part of its normal load. The integration
|
|
parses CLI records but cannot add them to an already-loaded result. The
|
|
[CLI reference](../cli.md#configuration-scopes) describes the operator syntax.
|
|
|
|
## PromptKit configuration, definitions, and requirements
|
|
|
|
`ckprompt.ConfigScope(loadEngine)` returns a `ckconfig.Scope` for the PromptKit
|
|
engine's captured configuration. Add that scope to a `ckconfig.Suite` to expose
|
|
it under `config` with validate/show/sources/explain and native diff operations;
|
|
it does not install PromptKit catalog, definition, or requirements commands.
|
|
Its override feature is disabled because the released PromptKit configuration
|
|
loader does not accept ConfigKit override records.
|
|
|
|
### Install resource commands and optional checks
|
|
|
|
`ckprompt.FromEngine(loadEngine)` supplies nine callbacks: prompt, profile,
|
|
backend, namespace, and collection catalogs; named prompt, profile, and backend
|
|
inspection; and native requirements discovery. Concrete request validation,
|
|
prepared-request preview, and smoke testing are separate capabilities, added
|
|
with `ckprompt.WithRequestValidation`, `ckprompt.WithRequestPreparation`, and
|
|
`ckprompt.WithSmokeTesting`. The suite has twelve independently optional
|
|
callbacks in total. `ckprompt.Command(suite)` creates only operations whose
|
|
callbacks are present, with canonical flags. `ckprompt.Declarations(suite)`
|
|
returns the command and its scoped convenience aliases for installation with
|
|
other declarations. These capabilities can be installed independently. A
|
|
composed suite can enable the additional operations:
|
|
|
|
```go
|
|
suite := ckprompt.FromEngine(loadEngine)
|
|
suite = ckprompt.WithRequestValidation(suite, loadEngine, buildRequest)
|
|
suite = ckprompt.WithRequestPreparation(suite, loadEngine, nil)
|
|
suite = ckprompt.WithSmokeTesting(suite, loadEngine, nil)
|
|
suite.NewInput = newInput
|
|
suite.NewState = newState
|
|
suite.Selectors = selectors
|
|
definitionCommand := ckprompt.Command(suite)
|
|
```
|
|
|
|
A validation-only suite can start empty. With application types named `Input`
|
|
and `State`, a nil builder forwards the request fields populated by standard
|
|
flags directly to PromptKit:
|
|
|
|
```go
|
|
suite := ckprompt.WithRequestValidation(
|
|
ckprompt.Suite[Input, State]{}, loadEngine, nil,
|
|
)
|
|
validationCommand := ckprompt.Command(suite)
|
|
```
|
|
|
|
### Request builders and loading
|
|
|
|
If the loader is nil, the returned suite retains a wiring issue for installation
|
|
to report, even if capabilities are narrowed later. A builder runs before the
|
|
engine loader and receives the seed request, selection, typed input, shared
|
|
state, and invocation context. It returns the
|
|
concrete request; the adapter does not merge seed fields back afterward. The
|
|
loader receives the original selection. Builders should start from the seed,
|
|
preserve operator values and bindings, and apply defaults only when a map key is
|
|
absent, so explicitly supplied empty values stay meaningful. Applications that
|
|
validate and then execute a request are encouraged to share their request
|
|
construction logic between those paths.
|
|
|
|
The catalog and inspection callbacks and the explicit additions are
|
|
independently optional. For catalogs without named or requirements inspection,
|
|
narrow the suite before command capture:
|
|
|
|
```go
|
|
definitions := ckprompt.FromEngine(loadEngine)
|
|
definitions.InspectPrompt = nil
|
|
definitions.InspectProfile = nil
|
|
definitions.InspectBackend = nil
|
|
definitions.InspectRequirements = nil
|
|
definitionCommand := ckprompt.Command(definitions)
|
|
```
|
|
|
|
Each selected inspection, requirements, validation, preparation, smoke, or catalog operation
|
|
prepares one input, applies its captured selectors, checks that input, and
|
|
initializes one state value when those steps are reached. The bundled validation
|
|
adapter delegates concrete request preparation to PromptKit and carries its
|
|
native errors plus engine-loading diagnostics through the acquisition. It does
|
|
not call the model or retain a prepared execution snapshot. See
|
|
[ckprompt.Suite's GoDoc](../../integrations/ckprompt/adapters.go) for factory
|
|
timing, omitted-factory zero values, and caller-owned objects and effects.
|
|
The loader receives invocation context, explicitly supplied selection, input,
|
|
and state. `FromEngine` uses PromptKit's public listing and inspection methods,
|
|
including `Engine.InspectRequirements`; validation must be opted into separately
|
|
and uses `Engine.ValidateRequest`.
|
|
|
|
`ckprompt.WithRequestPreparation` adds a separate callback backed by
|
|
`Engine.Prepare`. Its optional builder receives the seeded request and returns
|
|
the complete request to prepare. The resulting native report can contain
|
|
rendered application content. See the [CLI reference](../cli.md#promptkit-prepared-request-preview)
|
|
for operator behavior.
|
|
|
|
Application-owned commands can use `ckprompt.NewExecutionFlags`,
|
|
`ckprompt.NewResourceFlags`, and `ckprompt.NewPromptInputFlags` to bind the
|
|
same canonical request flags into their own input types. Each collection exposes
|
|
individual handles and an `All` method, so a command can choose its applicable
|
|
subset. The application decides how to apply the resulting values to its request.
|
|
|
|
### Enable namespace flags
|
|
|
|
`ckprompt.WithNamespaces` enables standard resource namespace selection from
|
|
application-supplied defaults. Loaders used with this option should leave view
|
|
selection to the adapter. Without it, a loader can supply its own engine view.
|
|
Direct callbacks receive the composed selection in their request. The
|
|
[CLI reference](../cli.md#resource-namespace-selection) lists applicable flags.
|
|
|
|
Catalog, named definition inspection, and requirements discovery do not render
|
|
messages or load request artifacts. Concrete request validation follows native
|
|
PromptKit preparation, which can resolve prompt/profile resources, load
|
|
artifacts and schemas, render messages, and perform applicable configuration
|
|
and credential checks. It does not make a model call or certify that a later
|
|
execution will succeed. Application loaders and direct callbacks retain their
|
|
own effects. Smoke testing explicitly invokes one native PromptKit diagnostic
|
|
or all four in order. Native inference checks use the configured public
|
|
`promptkit.LLMClient`; applications retain ownership of client and probe
|
|
configuration. A builder may customize the seeded native request, and its
|
|
complete return value is authoritative. The [CLI reference](../cli.md#promptkit-smoke-testing),
|
|
[definition inspection](../cli.md#promptkit-definition-inspection), and
|
|
[requirements contract](../cli.md#promptkit-requirements) own operator syntax
|
|
and reports.
|
|
|
|
The [composed example](../../integrations/ckprompt/examples/composed/README.md)
|
|
combines app and PromptKit config scopes, comparison and catalogs, named and
|
|
requirements inspection, request validation, smoke testing, and an
|
|
application-owned `session restore` command.
|
|
|
|
### Namespace views
|
|
|
|
An application can return a scoped PromptKit view from its own engine loader:
|
|
|
|
```go
|
|
func loadScopedEngine(ctx context.Context, selection ckconfig.Selection, input Input, state State) (*promptkit.Engine, error) {
|
|
engine, err := loadEngine(ctx, selection, input, state)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return engine.WithNamespaces(promptkit.Namespaces{
|
|
Prompt: "editorial", Profile: "team", Backend: "providers",
|
|
})
|
|
}
|
|
```
|
|
|
|
These are independent defaults for unqualified caller-supplied prompt, profile,
|
|
and backend names; qualified IDs pass through. Stored references and configuration
|
|
inspection are unchanged, while namespace and collection inventories remain global.
|
|
Catalog `--namespace` filters are separate from these defaults. See the released
|
|
[namespace API](https://forge.maximumdirect.net/base-layer/promptkit/src/commit/8ff9aa3efdc9f16b9d2729e0a29ea083eb3c3eaf/namespaces.go).
|
|
|
|
## Outcomes and ownership
|
|
|
|
Core classifies usage and operational failures while preserving application
|
|
domain outcomes and supported error identity. Applications choose domain
|
|
classification and status. A terminal explanation is presented separately from
|
|
the optional operation cause, while report and diagnostic delivery observations
|
|
are available in `Outcome.Presentation`. The caller owns process startup,
|
|
signal handling, and exit mapping; `Execute` never exits the process.
|
|
|
|
Go declarations and GoDoc own exact exported contracts. ConfigKit owns
|
|
configuration decoding, validation, and provenance. PromptKit owns prompt
|
|
formats, profile resolution, and inspection details. See the
|
|
[ConfigKit v1.1.0 source records](https://forge.maximumdirect.net/base-layer/configkit/src/commit/191015e289d7f2b91abb9fdd9c4f2f5cf75b4de9/records.go),
|
|
[PromptKit engine methods](https://forge.maximumdirect.net/base-layer/promptkit/src/commit/8ff9aa3efdc9f16b9d2729e0a29ea083eb3c3eaf/engine.go),
|
|
[configuration inspection](https://forge.maximumdirect.net/base-layer/promptkit/src/commit/8ff9aa3efdc9f16b9d2729e0a29ea083eb3c3eaf/configuration_inspection.go),
|
|
[smoke diagnostics](https://forge.maximumdirect.net/base-layer/promptkit/src/commit/8ff9aa3efdc9f16b9d2729e0a29ea083eb3c3eaf/smoke_diagnostics.go),
|
|
and [PromptKit v0.20.0 consumer guidance](https://forge.maximumdirect.net/base-layer/promptkit/src/commit/8ff9aa3efdc9f16b9d2729e0a29ea083eb3c3eaf/docs/consumers/pkg-promptkit.md).
|