diff --git a/README.md b/README.md index 98c5ccb..fd34bce 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,22 @@ # CommandKit -CommandKit is a Go library for typed command declarations, shared CLI behavior, -and application-owned execution. The core `commandkit` module is usable by -itself. ConfigKit and PromptKit support lives in separately versioned optional -modules, so applications choose which upstream libraries they add. +CommandKit helps Go applications build consistent command-line interfaces. It +provides typed flags and arguments, help, output formatting, and error reporting. +Optional integrations add commands for inspecting configuration and working with +PromptKit. -The core captures reusable command declarations, validates installation batches -before changing an application's registry, converts typed inputs before -constructing them, and returns outcomes without exiting the process. Its -[consumer guide](docs/consumers/guide.md) covers declaration, invocation -and reporting. Each handler receives one invocation reporter for -substantive output, categorized messages, and terminal explanations; `Execute` -returns delivery observations with the outcome. The [CLI reference](docs/cli.md) -defines the shared visibility policy. +CommandKit is in alpha. Start with the [consumer guide](docs/consumers/guide.md#core-commands) +to define a command, or run the [core example](examples/core/README.md). +Applications supply their own command handlers and decide when to exit; +CommandKit returns the result of each invocation. ## Modules | Module | Package | Provides | | --- | --- | --- | -| `go.maximumdirect.net/commandkit` | `commandkit` | Typed commands, bindings, execution reporting and outcomes, help, and version. | -| `go.maximumdirect.net/ckconfig` | `ckconfig` | ConfigKit-backed `config` scopes, including configuration source evidence. | -| `go.maximumdirect.net/ckprompt` | `ckprompt` | PromptKit engine configuration/comparison, five catalogs, named inspection, requirements discovery, concrete request validation, and native smoke tests. | +| `go.maximumdirect.net/commandkit` | `commandkit` | Commands, typed inputs, shared output and error reporting, help, and version. | +| `go.maximumdirect.net/ckconfig` | `ckconfig` | Commands to view, validate, explain, and compare ConfigKit configuration. | +| `go.maximumdirect.net/ckprompt` | `ckprompt` | Commands to list and inspect PromptKit resources, discover required inputs, validate and preview requests, and run smoke tests. | Importing an integration does not install commands. Core and the integrations are versioned separately; see the [consumer guide](docs/consumers/guide.md#module-selection) diff --git a/builtins.go b/builtins.go index 848c69f..25c3c39 100644 --- a/builtins.go +++ b/builtins.go @@ -32,8 +32,8 @@ func (a *App) builtinCommands() []Declaration { help := captureCommand(CommandSpec[helpInput]{ Name: "help", Summary: "Show help for commands.", - Description: "Describe registered commands and their inputs.", - Arguments: []Argument[helpInput]{RepeatArgument(ArgumentMeta{Name: "COMMAND", Usage: "registered command path"}, 0, -1, + Description: "Show available commands, flags, and arguments.", + Arguments: []Argument[helpInput]{RepeatArgument(ArgumentMeta{Name: "COMMAND", Usage: "Command path to describe."}, 0, -1, func(value string) (string, error) { return value, nil }, func(input *helpInput, value Supplied[[]string]) { input.Targets = value })}, }, func(current commandExecution, input helpInput) Outcome { @@ -43,9 +43,9 @@ func (a *App) builtinCommands() []Declaration { version := NewCommand(CommandSpec[versionInput]{ Name: "version", Summary: "Show application or CommandKit version.", - Description: "Report identity and build metadata without loading application configuration.", + Description: "Show the name, version, and revision without loading application configuration.", Flags: []Flag[versionInput]{ - ValueFlag(FlagMeta{Name: "scope", Usage: "version subject", Completion: CompleteChoices(CompletionChoice{Value: "app"}, CompletionChoice{Value: "commandkit"})}, &scopeDefault, parseVersionScope, func(input *versionInput, value Supplied[string]) { input.Scope = value.Value }), + ValueFlag(FlagMeta{Name: "scope", Usage: "Show the app or CommandKit version.", Completion: CompleteChoices(CompletionChoice{Value: "app"}, CompletionChoice{Value: "commandkit"})}, &scopeDefault, parseVersionScope, func(input *versionInput, value Supplied[string]) { input.Scope = value.Value }), }, Run: func(invocation Invocation, input versionInput) Outcome { identity := a.identity diff --git a/doc.go b/doc.go index 7b56c48..622c12c 100644 --- a/doc.go +++ b/doc.go @@ -1,8 +1,10 @@ -// Package commandkit provides the core instance-owned CLI dispatcher, typed -// command declarations, application-owned command and flag aliases, input -// bindings, invocation-scoped reporting, and outcomes with separate operation -// causes and presentation observations. 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 helps Go applications build consistent command-line +// interfaces with typed flags and arguments, help, and shared output formatting. +// Define commands with NewCommand and install them with New or App.Register. +// Commands and command or flag aliases can be installed together. +// +// App.Execute runs a command and returns an Outcome; the application decides +// when to exit. Handlers use Invocation.Reporter for output and messages. +// Optional ckconfig and ckprompt modules add inspection and testing commands; +// applications install those commands explicitly. package commandkit diff --git a/docs/cli.md b/docs/cli.md index 572a5fc..fe5d8ed 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -1,9 +1,18 @@ # Command line reference -CommandKit owns command routing and syntax for commands registered with its -`App`. The caller supplies argv without the executable name, a context, and the -output and diagnostic writers. `App.Execute` returns an `Outcome`; it does not -exit the process. +This reference describes the standard commands available to applications using +CommandKit. Replace `example` in commands with your application's executable +name. Applications choose which optional commands and aliases to install; use +`example --help` to see what yours provides. + +Start with [configuration inspection](#configuration-scopes), [PromptKit +resource listings](#promptkit-catalogs), or [PromptKit +inspection](#promptkit-definition-inspection). Shared [output +controls](#reporting) apply across the suite. Flags such as `--campaign` in +examples are application-defined, not built into CommandKit. + +Go application developers should use the [consumer guide](consumers/guide.md) +for installation and API details. ## Help @@ -24,12 +33,11 @@ an execution handler displays its help when invoked by itself. A runnable command with children runs its handler when invoked without help; add `--help` to inspect that command and its children. -Help uses registered descriptions, command summaries, local flags, defaults, -required and repeatable flag metadata, and operand names and counts. It does not -construct the selected command's input or invoke its handler. Supplied flag and -operand values are still parsed so malformed values and unknown flags remain -usage errors. Missing execution-required values and input-combination checks -are skipped for help. +Help shows available commands, flags, defaults, and positional arguments, +including which values are required or repeatable. It does not run the +selected command. Unknown flags and malformed supplied values still produce +usage errors, but you do not need to supply values required only for execution +or satisfy execution-only combinations of inputs. ## Version @@ -96,42 +104,49 @@ The application selects the executable basename at installation; use that same name to invoke the generated script. Completion offers canonical command and flag names, static values, and file or directory paths where declarations provide hints. A fully entered alias is resolved to its canonical command; -alias spellings and partial alias paths are not suggested. Completion queries -do not invoke input factories, providers, or the selected command; application -startup remains application-owned. See the -[consumer guide](consumers/guide.md#shell-completion) for installation and -hint declarations. Script generation follows the [reporting policy](#reporting), +alias spellings and partial alias paths are not suggested. Completion does not +run the selected command or load its data through CommandKit's callbacks. The +application may still do work during startup. See the [consumer +guide](consumers/guide.md#shell-completion) for installation and hint +declarations. Script generation follows the [reporting policy](#reporting), including quiet mode. ## Reporting -CommandKit applies one shared reporting policy to application handlers, -integrations, help, version, and CommandKit's own invocation diagnostics. The -caller passes result and diagnostic writers to `App.Execute`; substantive -results go to `Streams.Out`, and categorized messages, terminal explanations, -and usage guidance go to `Streams.Err`. +Use `--format` to choose report output and `--quiet`, `--verbose`, or +`--debug` to control which messages appear: -The shared `--format` flag selects `text`, `yaml`, or `json` for reports -submitted through the core reporter. When omitted, a report's text template -selects text; otherwise scalar values and nonempty string lists use text, while -structured values, empty collections, and null use YAML. Explicit text without a -template uses YAML for structured values. Invalid or empty format values are -usage errors. Raw output, help, completion, and diagnostics are not encoded as -reports. +```text +example config show --format json +example config validate --verbose +``` + +Command results and diagnostic messages use separate output destinations, +chosen by the application. Diagnostics include warnings, progress, errors, and +usage guidance. + +The shared `--format` flag accepts `text`, `yaml`, and `json`. Without it, +commands with a text template use text. Other reports use text for a single +string, number, boolean, or nonempty list of strings, and YAML for structured +values, empty collections, and null. Explicit `--format text` also uses YAML +for structured values without a text template. + +An invalid or empty format is a usage error. The flag does not reformat raw +application output, help, completion scripts, or diagnostic messages. ### Modes and visibility -Each invocation starts in ordinary mode. `--quiet`, `--verbose`, and `--debug` -are mutually exclusive; the last enabled occurrence selects the mode. They -accept presence or an explicit boolean value. A `false` occurrence clears the -selected mode only when it names the current mode, returning to ordinary mode; -it does not restore an earlier selection. Invalid boolean values are usage -errors, even if a later flag would select another mode. +Each invocation starts in ordinary mode. Only one of `--quiet`, `--verbose`, +and `--debug` can be active; if several are supplied, the last enabled one +wins. They accept presence or an explicit boolean value. A `false` occurrence +clears the selected mode only when it names the current mode, returning to +ordinary mode; it does not restore an earlier selection. Invalid boolean +values are usage errors, even if a later flag would select another mode. | Category | `--quiet` | No mode flag | `--verbose` | `--debug` | | --- | --- | --- | --- | --- | -| Substantive result | Suppressed | Shown | Shown | Shown | -| Terminal failure | Shown | Shown | Shown | Shown | +| Command output | Suppressed | Shown | Shown | Shown | +| Failure explanation | Shown | Shown | Shown | Shown | | Warning | Suppressed | Suppressed | Shown | Shown | | Info | Suppressed | Suppressed | Shown | Shown | | Debug | Suppressed | Suppressed | Suppressed | Shown | @@ -170,33 +185,34 @@ the same ordering. Thus `example export --verbose -q` selects quiet mode. ### Channels and messages -Substantive reports use the result destination. Warnings, Info, Progress, -Debug, terminal explanations, and usage guidance use the diagnostic destination. -Simple Warning, Debug, and terminal failure messages begin with `WARN:`, -`DEBUG:`, and `ERROR:` respectively. Info, Progress, and substantive results -have no prefix. For multiline messages, the prefix appears on the opening line; -native structured details keep their own formatting without duplicate labels. -Progress is streamed as unprefixed lines; terminal redraw and progress bars are -not provided. A failed outcome with no message or detail uses the fallback -`ERROR: Command failed.`. +Reports use the result destination. Warnings, information, progress, debug +messages, failure explanations, and usage guidance use the diagnostic +destination. Simple Warning, Debug, and terminal failure messages begin with +`WARN:`, `DEBUG:`, and `ERROR:` respectively. Info, Progress, and command +output have no prefix. For multiline messages, the prefix appears on the +opening line; structured details keep their own formatting without duplicate +labels. Progress is streamed as unprefixed lines; terminal redraw and progress +bars are not provided. A failed outcome with no message or detail uses the +fallback `ERROR: Command failed.`. Quiet mode suppresses successful help and version output, as it does other -substantive results. Usage guidance and routing, parsing, and operational -failures remain visible in every mode. A recovered problem is reported only if -the application chooses to submit a Warning; an error cause by itself does not -create a message. +command output. Usage guidance and routing, parsing, and operational failures +remain visible in every mode. The application decides whether to report a +recovered problem as a warning. -### Presentation failures + -CommandKit continues rendering substantive output under quiet mode through a -discard destination. This suppresses writes to the caller's result writer while -still detecting report preparation, encoding, or rendering failures. An -intentional suppression is not a delivery failure. A failure while rendering or -writing requested content is retained in the returned `Outcome` and makes an -otherwise successful or usage invocation operational with status `1`. A domain -classification and its nonzero status are preserved; a zero-status domain -outcome becomes status `1` when presentation fails. Earlier bytes already -written remain visible. +### Output failures + +Quiet mode hides output but still prepares and renders it, so formatting +errors can still fail a command. Suppression itself is not a delivery failure. + +A formatting or write failure changes an otherwise successful or usage result +to status `1`. Application-specific results retain their classification and +nonzero status; a zero status becomes `1` on output failure. Output already +written remains visible. See [outcome status](#outcome-status) for status +conventions and the [consumer guide](consumers/guide.md#invocation-reporting) +for Go error access. ## Registered command syntax @@ -211,15 +227,14 @@ supplies its value. Every supplied occurrence is converted, so an earlier malformed value remains an error. Repeatable flags preserve occurrence order, with each occurrence treated as one value, including strings containing commas. -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. +An explicitly supplied empty, false, or zero value is different from omitting +a flag and using its default. Help and version do not require 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. +commands. Aliases are shortcuts to existing commands or flags. In this +reference, “canonical” means the original command or flag name. 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 @@ -242,13 +257,13 @@ 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. +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`. An alias has the same value +parsing, default, required-value rules, and repetition behavior as its +original flag. 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: @@ -273,63 +288,70 @@ 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. +help. Displaying this help does not run the target command or load its data +through CommandKit's 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. +receives no invented canonical label. An invalid flag value is reported using +the spelling you entered and identifies the occurrence that failed. ## Configuration scopes -An application installs the declaration returned by -`ckconfig.Command(ckconfig.Suite[Input, State]{...})` through -`commandkit.New` or `App.Register`. For the common single application scope, -`ckconfig.AppCommand` returns a convenience declaration. The installed -`config scopes` command lists scope names and the configuration operations -currently available for those scopes: +A scope is a named configuration, such as `app` for application settings or +`promptkit` for engine settings. List the scopes and operations your +application provides: ```text example config scopes example config scopes --format yaml ``` -Text output uses one `scope: operation, operation` line per scope and lists any -declared optional features. Structured output uses a `scopes` sequence with -`name`, `operations`, and any supported `features`. Scope order is -`app`, then `promptkit`, then other names alphabetically. A metadata-only scope -can be listed without an acquisition callback. Discovery does not construct a -runtime context or acquire configuration. It reports the intersection between -declared scope capabilities and installed handlers, so an operation is listed -only when the corresponding command is available. +The configuration suite supports these tasks where enabled by the application: -Scopes can opt in to transient overrides. For an opted-in scope, acquisition -commands accept repeatable `--set POINTER=JSON`; comparison also accepts -`--against-set POINTER=JSON` for the right side. Each assignment is passed to -the loader in order, with `--set` or `--against-set` as its source label. -The pointer and value are split at the first `=`, so pointer keys containing -`=` cannot be addressed by these flags; JSON strings can contain `=`. Values -use JSON types, including null, false, zero, and empty containers. ConfigKit -owns override path validation and conflict handling. A selected scope must opt -in before receiving overrides; for `--scope all`, every selected scope must -support them. +| Command | Task | +| --- | --- | +| `config scopes` | Find available configurations and operations. | +| `config validate` | Check configuration and inspect validation findings. | +| `config show` | View the effective settings. | +| `config sources` | Find the files and other sources used to load settings. | +| `config explain` | Discover paths or find where a value came from. | +| `config diff` | Compare two configurations. | -Applications can add `ckprompt.ConfigScope(loadEngine)` to the ConfigKit suite -to expose PromptKit engine configuration through these handlers. The scope -declares validate, show, sources, explain, and native diff. It does not install -PromptKit prompt or profile definition commands. +Scope discovery does not load configuration. It lists only operations +installed for each scope, along with supported optional features. A scope can +appear even if it has no configuration loader. Text output uses one `scope: +operation, operation` line per scope; structured output contains a `scopes` +list with `name`, `operations`, and any supported `features`. Scopes appear in +this order: `app`, `promptkit`, then other names alphabetically. + +### Temporary overrides + +For scopes that support them, repeat `--set POINTER=JSON` to override values +for one invocation. For example: + +```text +example config show --set '/server/port=8080' +``` + +`config diff` also accepts `--against-set POINTER=JSON` for its right side. +Assignments reach the loader in order, labeled with the corresponding flag. +The first `=` separates the JSON Pointer from its value, so these flags cannot +address keys containing `=`; JSON string values can contain it. Values may be +any JSON type, including null, false, zero, and empty containers. ConfigKit +checks override paths and handles conflicts during loading. + +For `--scope all`, every selected scope must support overrides. The bundled +PromptKit configuration scope does not currently support them. Applications +choose supported features and loaders as described in the [consumer +guide](consumers/guide.md#configkit-scopes). ### Validate configuration -`config validate` loads and validates the selected scope. The default scope is -`app`; `--scope all` validates every registered scope and is available when each -scope declares validation. The command checks all selected capabilities before -calling scope providers. -Automatic output is concise text; structured formats emit a `scopes` sequence -with each reached scope and its outcome (`valid`, `failed`, or `canceled`). +`config validate` checks the selected configuration. It uses the `app` scope +by default. Use `--scope all` to check every scope, when all support +validation. ```text example config validate @@ -338,29 +360,33 @@ example config validate --scope all example config validate --scope all --details --severity warning --code schema.version ``` -Each successful scope is reported as `SCOPE: valid`. Aggregate validation stops -at the first load failure or cancellation, then submits one report containing -the reached scopes. Output errors occur after acquisition has stopped. Provider -warnings go to the diagnostic stream and do not alone make validation fail. -`--details` includes native findings and the source references needed to -interpret them. Each scope's detail state is `unavailable`, `empty`, `filtered`, -or `present`. Repeat `--severity error|warning` or `--code CODE` to filter -findings: values within a filter match any, and the two filters narrow together. -Filters require `--details` and never change the validation outcome. Detailed -output suppresses ordinary warnings associated with its native findings; other -messages and terminal failure explanations remain visible. -`--config` and `--overlay` preserve whether an -empty value was explicitly supplied, so a scope callback can map those inputs to -its own ConfigKit load policy. Help and invalid syntax do not call scope -providers. +The default output is concise text: `SCOPE: valid` for each successful scope. +YAML and JSON reports contain a `scopes` list with each attempted scope and +its outcome: `valid`, `failed`, or `canceled`. + +The command checks that all selected scopes support validation before loading +any of them. With `--scope all`, it stops at the first load failure or +cancellation and reports the scopes reached. Reporting happens after loading +has stopped. Warnings use the diagnostic destination and do not by themselves +make validation fail. + +#### Detailed validation findings + +`--details` includes validation findings and the source references needed to +interpret them. Each scope's detail state is `unavailable`, `empty`, +`filtered`, or `present`. Repeat `--severity error|warning` or `--code CODE` +to filter findings: values within a filter match any, and the two filters +narrow together. Filters require `--details` and never change the validation +outcome. Detailed output suppresses ordinary warnings associated with its +native findings; other messages and terminal failure explanations remain +visible. An explicitly empty `--config` or `--overlay` differs from omission; +the application's loader decides how to interpret it. Help and invalid syntax +do not load configuration through the scope providers. ### Show effective configuration -`config show` displays the provider's safe effective configuration. The default -scope is `app`. The native inspection YAML is converted to a logical report -value, preserving meaningful scalar types, empty collections, and redaction. -Automatic formatting uses text for a single scalar or nonempty string list and -YAML for structured values; `--format` selects text, YAML, or JSON. +`config show` displays the settings after loading and resolution, using the +provider's safe view with secrets redacted. The default scope is `app`. ```text example config show @@ -369,10 +395,15 @@ example config show --scope all example config show --path /server/addr --format json ``` -`--scope all` acquires every selected scope before producing one mapping keyed -by scope name, including when only one scope is registered. A single selected -scope is unwrapped. A provider failure prevents report output. Conversion or -output failures are recorded in `Outcome.Presentation.Output`. +The report preserves value types, empty collections, and redaction. Automatic formatting +uses text for a string, number, boolean, or nonempty string list and YAML for +other values; `--format` selects text, YAML, or JSON. + +`--scope all` loads every selected scope before producing one mapping keyed by +scope name, even when only one scope is registered. Selecting one scope +directly returns its configuration without a scope wrapper. A load failure +prevents report output; conversion and write failures follow the shared +[output failure rules](#output-failures). For a scope that supports path-selected show, `--path POINTER` displays only that safe effective value. An explicitly empty pointer selects the root, and a @@ -381,14 +412,24 @@ selection cannot be combined with `--scope all`. ### List configuration sources -`config sources` reports the source inventory captured during the selected -scope's acquisition. It reads the source facet once and does not reload -configuration. The default scope is `app`; `--scope all` is unsupported. Text is -the automatic format, and `--format yaml` or `--format json` returns a structured report. -`--source-id ID` selects one entry by the ID from that acquisition; an unknown -ID fails. If acquisition fails but returns a captured source inventory, the -command reports that evidence and preserves the acquisition error. Unavailable -inventory is reported as such. +`config sources` lists the files and other sources used to load the selected +configuration. The default scope is `app`; `--scope all` is unsupported. + +```text +example config sources +example config sources --source-id source-1 --format yaml +``` + +Use an ID from the source list with `--source-id` to select one entry; an +unknown ID fails. Source IDs belong to that load. The command reads the +recorded list once, without reloading configuration. If loading fails but a +source list is available, it still reports the list and retains the failure. +Otherwise it reports that source information is unavailable. + +Automatic output is text. Use `--format yaml` or `--format json` for +structured output. + +#### Source report fields ```yaml scope: app @@ -421,19 +462,20 @@ Each source includes `id`, `label`, `path`, `layer`, `kind`, `underlay_index`, and `imported_from`, plus the native destination pointer, placement, `destination_is_fallback`, nullable `version_evidence`, and `empty_import_selections`. Placement is `mapping` or `sequence_item`. The -import location is null when absent; otherwise it has -`source_id`, `line`, and `column`. Version evidence preserves `path`, `present`, -`null`, `text`, optional source `location`, and `redacted` as returned by -ConfigKit. Owning values and source order are preserved, including empty fields, -zero coordinates, false and null observations, and synthetic sources without -paths. Absent version evidence is null. Each empty-import record has a source-local -`path`, a `location` mapping (`source_id`, `line`, `column`), and a `selector`; -records preserve their returned order. No records are `empty_import_selections: []`. -An empty inventory is `sources: []`. -Text output begins with the scope heading and prints the same source fields, -quoting labels and paths. These fields project the selected ConfigKit source -records; CommandKit does not reload files or rerun version checks. See the -[ConfigKit v1.1.0 source records](https://forge.maximumdirect.net/base-layer/configkit/src/commit/191015e289d7f2b91abb9fdd9c4f2f5cf75b4de9/records.go) +import location is null when absent; otherwise it has `source_id`, `line`, and +`column`. Version evidence preserves `path`, `present`, `null`, `text`, +optional source `location`, and `redacted` as returned by ConfigKit. Values +and source order are preserved, including empty fields, zero coordinates, +false and null observations, and synthetic sources without paths. Absent +version evidence is null. Each empty-import record has a source-local `path`, +a `location` mapping (`source_id`, `line`, `column`), and a `selector`; +records preserve their returned order. No records are +`empty_import_selections: []`. An empty inventory is `sources: []`. Text +output begins with the scope heading and prints the same source fields, +quoting labels and paths. These fields come from ConfigKit's source records; +producing the report does not reload files or rerun version checks. See the +[ConfigKit v1.1.0 source +records](https://forge.maximumdirect.net/base-layer/configkit/src/commit/191015e289d7f2b91abb9fdd9c4f2f5cf75b4de9/records.go) for native evidence definitions. ### Explain a configuration path @@ -461,10 +503,12 @@ when present, its explicitness. Path discovery does not include values or histor Pointer tokens use JSON Pointer escaping: `~1` represents `/` and `~0` represents `~`. A malformed pointer is a usage error with status `2` and is -rejected before acquisition. A well-formed path that is unknown in the selected -configuration is an operational error with status `1`. Text, YAML, and JSON -render the same acquired explanation, available field metadata and metadata history, and source labels. Text uses CommandKit's -explanation template; the handler does not call the provider's text writer. +rejected before loading configuration. A well-formed path that is unknown in +the selected configuration is an operational error with status `1`. Text, +YAML, and JSON show the same explanation, available field metadata and +metadata history, and source labels. + +#### Explanation report fields Structured output uses these root fields: @@ -496,59 +540,70 @@ serialized as `[]`. ### Compare configuration selections -`config diff` prepares two selections for one scope that declares comparison -and invokes its comparison provider after input checks pass. The default scope -is `app`; `--scope all` is unsupported. -When an application installs `ckprompt.ConfigScope(loadEngine)`, the PromptKit -scope declares comparison and delegates it to PromptKit's captured -configuration inspection API. PromptKit owns the meaning of that native -comparison; CommandKit uses the existing `config diff` preparation and report -path. +`config diff` shows the differences between two configurations of the same +scope. It uses `app` by default; `--scope all` is unsupported. + +```text +example config diff --config before.yml --against-config after.yml +example config diff --config candidate.yml +``` + +The second example compares `candidate.yml` with the configuration the +application ordinarily loads without overrides. + `--config` and `--overlay` select the left side. `--against-config` and -`--against-overlay` select the right side. An omitted field keeps that side's -ordinary application loading behavior and unsupplied state; an explicit empty -string remains supplied. Application selectors affect the left side, while -declared `--against-*` counterparts affect the right. Equal selections are -allowed and do not cause CommandKit to skip the provider. +`--against-overlay` select the right side. Each omitted field uses that side's +ordinary application loading behavior; the right side does not inherit the +left side's flags. An explicitly empty string remains an explicit selection. +Application-defined selectors affect the left side, and their `--against-*` +counterparts affect the right side when available. Equal selections are +allowed and still run the comparison. + +The application must enable comparison for the selected scope. The bundled +ConfigKit and PromptKit configuration adapters both support it. Automatic output is YAML. The shared `--format` also accepts YAML, JSON, and text; text uses the same structured representation. For example: ```text -example config diff --config before.yml --against-config after.yml example config diff --config base.yml --against-overlay candidate example config diff --config base.yml --overlay current --against-overlay "" example config diff --scope promptkit --config before.yml --against-config after.yml example config diff --config before.yml --against-config after.yml --details --check ``` -`--details` replaces the concise report with `{scope, differences}`, containing -one entry per native difference. Each entry has `path`, `before`, `after`, and -`hidden_changed`; each snapshot has `present`, `redacted`, and `value`. An -absent snapshot has `present: false` and a null value, distinct from a present -null. Empty comparisons contain `differences: []`. +#### Detailed differences and check mode -`--check` returns status `3` when native differences exist and `0` when they do -not. Without it, a completed comparison succeeds even when differences exist. -Usage errors retain status `2`, and operational failures retain status `1`. -The report is still delivered in check mode; a presentation failure remains +`--details` replaces the concise report with `{scope, differences}`, +containing one entry per difference returned by the provider. Each entry has +`path`, `before`, `after`, and `hidden_changed`; each snapshot has `present`, +`redacted`, and `value`. An absent snapshot has `present: false` and a null +value, distinct from a present null. Empty comparisons contain `differences: +[]`. + +`--check` returns status `3` when differences exist and `0` when they do not. +Without it, a completed comparison succeeds even when differences exist. Usage +errors retain status `2`, and operational failures retain status `1`. The +report is still delivered in check mode; a presentation failure remains separate evidence and does not replace a nonzero difference outcome. -For ConfigKit-backed scopes, ConfigKit compares frozen effective typed values -after defaults and resolution; source or provenance differences alone do not -create a change. PromptKit delegates its native comparison to ConfigKit. The -concise report contains `additions`, `changes`, and `deletions`. Additions and changes -preserve semantic paths and contain right-side values provided by the native -comparison. Deletions contain canonical JSON -Pointer paths. Explicit null is a value; it is not a deletion. Changed sequences -and native structural replacement regions stay atomic. +#### Interpreting the comparison -ConfigKit pairs sensitivity across both loads. A concealed change can therefore -appear even when its two safe snapshots are equal. For those ConfigKit-backed -comparisons, the concise report omits separate redaction and hidden-change metadata: a -reported masked region changed, but that does not mean every member inside it -changed. Marker-looking public strings stay ordinary strings. An empty -comparison succeeds with this shape: +For ConfigKit-backed scopes, comparison uses the effective values after +defaults and resolution. A change only in where a value came from does not +count as a value change. PromptKit uses ConfigKit for its comparison too. The +concise report contains `additions`, `changes`, and `deletions`. Additions and +changes keep the configuration's path structure and show right-side values +from the comparison. Deletions are JSON Pointer paths. Explicit null is a +value, not a deletion. Changed lists and regions replaced as a whole are shown +as whole values. + +ConfigKit considers sensitivity on both sides. A secret value can change even +when both redacted snapshots look the same. For those ConfigKit-backed +comparisons, the concise report omits separate redaction and hidden-change +fields: a reported masked region changed, but not necessarily every value in +it. Public strings that look like redaction markers remain ordinary strings. +An empty comparison succeeds with this shape: ```yaml additions: {} @@ -556,38 +611,34 @@ changes: {} deletions: [] ``` -The reduced report is a partial safe view, not a reloadable configuration or an -automatically applicable patch. Exceptional whole-root empty/null transitions -can be ambiguous because the report has no root envelope; ordinary field-level -null values and deletions retain their distinction. +The concise report is a partial view with secrets concealed. Do not load it as +configuration or apply it as a patch. Changes involving an empty or null value +at the configuration root can be ambiguous because the report has no wrapper +identifying the whole-root value. Null values and deletions of individual +fields remain distinct. -Without `--check`, successful comparisons return status `0`, including empty results. -Unsupported scopes and format errors are usage errors; input checks retain the -existing usage/operational classification. Load and native comparison failures -are operational and preserve supported error identity. +Without `--check`, successful comparisons return status `0`, including empty +results. Unsupported scopes and formats are usage errors. Application input +checks can report usage or operational failures. Load and comparison failures +use status `1`. -The bundled `FromConfigKit` and `AppCommand` adapters invoke the supplied loader -for each reached side, left before right, then call native comparison. They label -native diagnostics with their left/right side and source legend, retain left -diagnostics if right loading fails, and gather both loads' diagnostics before -native comparison. Custom pair providers own their loading and diagnostics; -application loaders and caches determine whether a loader call reads sources -again. +The bundled adapters load the left side before the right. Diagnostics identify +the side and its sources; left-side diagnostics are retained if right loading +fails. Custom application loaders and caches determine whether sources are +read again for each side. See the [consumer +guide](consumers/guide.md#configkit-scopes) for adapter and loader +responsibilities. -The complete report is prepared before writing. Report preparation failures and -cancellation observed before report delivery starts leave CommandKit's report -output empty. Once writing starts, CommandKit does not interrupt the supplied -writer or roll back delivered bytes. A write error or short write is recorded -as incomplete delivery in `Outcome.Presentation.Output` and can leave partial -output. +The complete report is prepared before writing. Preparation failures and +cancellation noticed before delivery leave the report output empty. Once +writing starts, cancellation does not interrupt the writer or undo delivered +bytes; a write failure can leave partial output. ## PromptKit definition inspection -Applications can install prompt, profile, and backend inspection independently -with `ckprompt.Command(suite)`. `ckprompt.FromEngine(loadEngine)` supplies these -callbacks alongside requirements discovery and the five catalogs. Its loader -receives invocation context, configuration selection, typed input, and shared -state. Direct callbacks retain their own lifecycle. +Use `promptkit show` to inspect a stored prompt, profile, or backend without +running a prompt or probing the model provider. Applications can enable these +commands independently. ```text example promptkit show prompt customer/summary @@ -596,27 +647,32 @@ example promptkit show backend local --format yaml ``` Each command takes one resource ID and accepts `--config` and `--overlay`. -Reports use the native PromptKit inspection value through the shared reporting -formats; automatic and explicit text use YAML for these structured values. Native -JSON field names and omission rules apply. Absent optional fields are omitted; -explicit zero values and empty collections remain present. Prompt messages are -captured content, not rendered requests, and the prompt hash is opaque. +Reports show PromptKit's inspection data. Automatic output and explicit text +use YAML for these structured values; `--format json` selects JSON. + +#### Inspection report details + +PromptKit's JSON field names and omission rules apply. Absent optional fields +are omitted; explicit zero values and empty collections remain present. Prompt +messages are captured content, not rendered requests, and the prompt hash is +opaque. Profile reports describe the selected profile before backend completion or request overrides. Backend reports describe configured backend fields. An `api_key_env` value names an environment variable; it is not a credential value or a claim that one is available. Neither report resolves references or -performs a network probe. See the [PromptKit resource inspection contracts](https://forge.maximumdirect.net/base-layer/promptkit/src/commit/8ff9aa3efdc9f16b9d2729e0a29ea083eb3c3eaf/resource_inspection.go). -A missing ID is a usage error; native lookup errors and cancellation retain -supported cause identity. +performs a network probe. See the [PromptKit resource inspection +contracts](https://forge.maximumdirect.net/base-layer/promptkit/src/commit/8ff9aa3efdc9f16b9d2729e0a29ea083eb3c3eaf/resource_inspection.go). +Omitting the ID is a usage error. Lookup failures and cancellation follow the +shared operational failure rules. ### Resource namespace selection -An application can enable resource namespace flags with `ckprompt.WithNamespaces` -and provide prompt, profile, and backend defaults. Omitted flags retain those -defaults; an explicitly empty value clears the corresponding default. Qualified -resource IDs pass through unchanged. Without the option, the application loader -controls the engine view and these flags are unavailable. +When enabled by the application, namespace flags choose the namespace for a +short resource ID. Omitting a flag keeps the application's default; an +explicitly empty value clears that default. Already qualified IDs pass through +unchanged. If these flags are absent from help, the application controls +namespace selection through its engine loader. | Operation | Available namespace flags | | --- | --- | @@ -631,42 +687,44 @@ resource namespace selectors. ### PromptKit convenience aliases -Applications that install `ckprompt.Declarations(suite)` provide scoped -convenience spellings for the enabled PromptKit commands. Each applicable -`--llm-NAME` flag also accepts `--NAME`; `--llm-input-text` additionally accepts -`--llm-input` and `--input` for literal text. Use `--llm-input-file` or -`--input-file` for a file reference. Canonical and alias spellings share the -same binding, including their occurrence order and explicit-value semantics. +Applications can enable shorter flag spellings within the PromptKit commands. +Each applicable `--llm-NAME` flag also accepts `--NAME`; `--llm-input-text` +additionally accepts `--llm-input` and `--input` for literal text. Use +`--llm-input-file` or `--input-file` for a file reference. The original name +and its aliases behave identically, including when repeated or supplied with +an empty value. -Applications that install `ckprompt.Command(suite)` receive the canonical -spellings only. The aliases do not apply to application-owned commands unless -the application installs them separately. +Use help to see whether your application enables these aliases. They do not +apply to commands outside `promptkit` unless the application installs them +there as well. See the [consumer +guide](consumers/guide.md#promptkit-configuration-definitions-and-requirements) +for installation choices. ## PromptKit prepared-request preview -When `Suite.PrepareRequest` is present, `ckprompt.Command(suite)` installs -`promptkit show prepared PROMPT_ID` independently of validation and other -inspection commands. It accepts the same configuration, selector, profile, -session, variable, input, and execution flags as -[request validation](#concrete-request-validation): +`promptkit show prepared PROMPT_ID` previews the rendered messages and request +settings without calling the model. When enabled by the application, it +accepts the same configuration, application selector, profile, session, +variable, input, and execution flags as [request +validation](#concrete-request-validation): ```text example promptkit show prepared briefing --llm-input-text topic=roadmap --format json ``` -The command returns PromptKit's native prepared-run report using the shared -formats; automatic and explicit text use YAML. Preparation can read resources -and report rendered application content. It makes no model call and does not -guarantee a subsequent execution will succeed. Application callbacks and -loaders retain their own effects. Native preparation errors retain supported -cause identity. The [PromptKit Prepare API](https://forge.maximumdirect.net/base-layer/promptkit/src/commit/8ff9aa3efdc9f16b9d2729e0a29ea083eb3c3eaf/engine.go) +The command returns PromptKit's prepared-run report; automatic and explicit +text output use YAML. Preparation can read resources and include rendered +application content in the report. It does not guarantee that a later model +call will succeed. Application loaders and callbacks may have their own +effects. The [PromptKit Prepare +API](https://forge.maximumdirect.net/base-layer/promptkit/src/commit/8ff9aa3efdc9f16b9d2729e0a29ea083eb3c3eaf/engine.go) owns the prepared value and upstream preparation behavior. ## PromptKit requirements -When `Suite.InspectRequirements` is present, `ckprompt.Command(suite)` installs -`promptkit requirements show`. It accepts one prompt ID and uses PromptKit's -native selection and discovery rules: +`promptkit requirements show` lists the inputs and variables a prompt may +need. Supply one prompt ID. Variables can help PromptKit determine which +conditional inputs are needed: ```text example promptkit requirements show briefing --campaign spring @@ -680,23 +738,24 @@ The command accepts: | --- | --- | --- | --- | | `--config` | path string | omitted | Select the configuration input; explicit presence is retained. | | `--overlay` | path string | omitted | Select one overlay; explicit presence is retained. Repeated occurrences use the last value. | -| Suite selectors | application-defined | per selector | The flags captured from `Suite.Selectors`. | -| `--llm-profile` | string | empty | Pass the profile ID to PromptKit; empty uses its native selection rule. | +| Application selectors | application-defined | per selector | Additional flags provided by your application. | +| `--llm-profile` | string | empty | Pass the profile ID to PromptKit; empty uses PromptKit's selection rule. | | `--llm-session-id` | string | empty | Pass session context to PromptKit. | -| `--llm-var` | repeatable `KEY=VALUE` | none | Supply caller-variable context for native discovery. | +| `--llm-var` | repeatable `KEY=VALUE` | none | Supply a variable to help discover required inputs. | | `--format` | `text`, `yaml`, `json` | automatic | Select the requirements report format; automatic and text use YAML for this structured report. | -The `promptkit` namespace identifies the operation, so there is no built-in -`--scope` flag. An explicitly empty profile or session value retains PromptKit's -native empty-string meaning. Profile selection establishes the reported profile +These commands work with PromptKit directly and have no built-in `--scope` +flag. PromptKit interprets an empty profile or session value according to its +own selection rules. Profile selection establishes the reported profile identity rather than changing the discovered requirements. An enabled profile -namespace qualifies an explicitly supplied short profile ID; it does not rewrite -the prompt's stored default reference. Requirements inspection does not require -the selected profile to be execution-ready. See the -[PromptKit v0.20.0 consumer guide](https://forge.maximumdirect.net/base-layer/promptkit/src/commit/8ff9aa3efdc9f16b9d2729e0a29ea083eb3c3eaf/docs/consumers/pkg-promptkit.md) -for native configuration, selection, and requirements behavior. The built-in -flags other than `--llm-var` are singleton bindings; when repeated, the last value -is used. Suite selectors retain their declared binding behavior. +namespace qualifies an explicitly supplied short profile ID; it does not +rewrite the prompt's stored default reference. Requirements inspection does +not require the selected profile to be execution-ready. See the [PromptKit +v0.20.0 consumer +guide](https://forge.maximumdirect.net/base-layer/promptkit/src/commit/8ff9aa3efdc9f16b9d2729e0a29ea083eb3c3eaf/docs/consumers/pkg-promptkit.md) +for configuration, selection, and requirements behavior. For built-in flags +other than `--llm-var`, repeating a flag uses its last value. +Application-defined flags follow their own repetition rules. Each `--llm-var` occurrence must contain `=`. CommandKit splits at the first separator and preserves the exact key and remaining value. Empty keys and @@ -704,12 +763,15 @@ values, whitespace, commas, and additional `=` characters are retained. All occurrences are parsed; duplicate keys use the last value, while any malformed occurrence is a usage error even if a later assignment is valid. -The report contains selected identities, native completeness, and input, -variable, and issue inventories. False values and empty inventories are -retained. Input metadata preserves null versus an empty string. Variable entries -contain `name`, `kind`, `supplied`, and `explanation`. `supplied` reports native -exact-key presence, including an explicitly empty value, independently of -`kind` and `complete`. Explanations and issue descriptions are upstream-owned +### Requirements report + +The report identifies the selected prompt and profile, whether discovery is +complete, and the inputs, variables, and issues found. False values and empty +inventories are retained. Input metadata preserves null versus an empty +string. Variable entries contain `name`, `kind`, `supplied`, and +`explanation`. `supplied` reports whether the exact variable name was +provided, including an explicitly empty value, independently of `kind` and +`complete`. Explanations and issue descriptions are upstream-owned human-readable summaries, not machine-readable details. For a prompt with the template `{{if .mode}}{{input "notes"}}{{end}}` and no @@ -738,20 +800,18 @@ issues: description: A missing variable prevents selecting a dependency-bearing branch. ``` -PromptKit owns the inventory, classifications, explanations, completeness, and -structured issues. Successfully acquired and delivered partial reports return -status `0`; structured issues do not independently fail the command. Provider -errors, native selection errors, cancellation, and report delivery failures use -the operational path (status `1`) with supported cause identity. Syntax, -operand, variable-assignment, and format errors use usage status `2`. +PromptKit determines these findings and explanations. A partial report can +still be a successful command result (status `0`); read `complete` and +`issues` to understand what remains unknown. A reported issue alone does not +fail this command. Load or selection failures, cancellation, and report +delivery failures use status `1`. Invalid syntax, arguments, variable +assignments, or formats use status `2`. ### Concrete request validation -When `Suite.ValidateRequest` is present, `ckprompt.Command(suite)` also installs -`promptkit requirements validate`. It can be installed without discovery, and -discovery can remain installed without validation. The command accepts one -prompt ID and passes a concrete request to PromptKit's native -`Engine.ValidateRequest` preparation path: +`promptkit requirements validate` checks whether PromptKit can prepare a +request with the inputs you supply. It accepts one prompt ID and does not call +the model. Applications can enable it independently of requirements discovery: ```text example promptkit requirements validate briefing --campaign spring --llm-input-file topic=topic.txt @@ -764,30 +824,28 @@ The command accepts: | --- | --- | --- | --- | | `--config` | path string | omitted | Select the configuration input; explicit presence is retained. | | `--overlay` | path string | omitted | Select one overlay; explicit presence is retained. Repeated occurrences use the last value. | -| Suite selectors | application-defined | per selector | The flags captured from `Suite.Selectors`. | +| Application selectors | application-defined | per selector | Additional flags provided by your application. | | `--llm-profile` | string | empty | Pass the profile ID to PromptKit. | | `--llm-session-id` | string | empty | Pass session context to PromptKit. | | `--llm-var` | repeatable `KEY=VALUE` | none | Supply caller variables for the concrete request. | -| `--llm-input-text` | repeatable `NAME=TEXT` | none | Bind a literal PromptKit artifact. | -| `--llm-input-file` | repeatable `NAME=PATH` | none | Bind a file-backed PromptKit artifact. | +| `--llm-input-text` | repeatable `NAME=TEXT` | none | Supply text for a named prompt input. | +| `--llm-input-file` | repeatable `NAME=PATH` | none | Supply a file for a named prompt input. | | `--format` | `text`, `yaml`, `json` | automatic | Select the success confirmation format; automatic and text use YAML for this structured report. | Validation also accepts the execution controls from `--llm-endpoint` through `--llm-extra-params` listed under [smoke testing](#promptkit-smoke-testing). -The built-in flags other than -`--llm-var`, `--llm-input-text`, and `--llm-input-file` are singleton bindings; -when repeated, the last value is used. Explicit empty `--config` or -`--overlay` values keep their supplied-presence information. The profile and -session fields use native empty-string semantics. PromptKit owns their -interpretation and selection errors. +For built-in flags other than `--llm-var`, `--llm-input-text`, and +`--llm-input-file`, repeating a flag uses its last value. Explicitly empty +`--config` and `--overlay` values remain distinct from omitted flags. +PromptKit decides how empty profile and session values affect selection. Each `--llm-var`, `--llm-input-text`, and `--llm-input-file` occurrence must contain `=`. CommandKit splits at the first separator and preserves the exact -key/name and remaining value. Empty names and values are passed to native -validation. Text becomes a `promptkit.Inline` reference; file paths become -`promptkit.File` references without being read, normalized, expanded, or -rebased during argument binding. Every occurrence is parsed; for each input -name the last text or file occurrence wins, regardless of form. +key/name and remaining value. PromptKit validates empty names and values. Text +is passed as a literal input. File paths are passed unchanged to PromptKit; +argument parsing does not read the files or normalize, expand, or rebase +paths. Every occurrence is parsed; for each input name the last text or file +occurrence wins, regardless of form. On success, the confirmation contains only the requested prompt ID and `valid: true`: @@ -797,26 +855,25 @@ prompt_id: briefing valid: true ``` -Validation delegates concrete request preparation to PromptKit. Native -preparation may resolve prompt/profile resources, load artifact and schema -resources, render messages and session context, and perform applicable -configuration and credential checks. It does not make a model call or retain a -prepared execution snapshot. A successful report describes this preparation -invocation; it does not certify provider acceptance or guarantee later -execution. See PromptKit's -[`Engine.ValidateRequest` API](https://forge.maximumdirect.net/base-layer/promptkit/src/commit/8ff9aa3efdc9f16b9d2729e0a29ea083eb3c3eaf/engine.go). +Validation delegates concrete request preparation to PromptKit. Preparation +may resolve prompts and profiles, read input files and schemas, render +messages and session context, and check configuration and credentials where +applicable. It does not make a model call or retain a prepared execution +snapshot. A successful report describes this preparation invocation; it does +not certify provider acceptance or guarantee later execution. See PromptKit's +[`Engine.ValidateRequest` +API](https://forge.maximumdirect.net/base-layer/promptkit/src/commit/8ff9aa3efdc9f16b9d2729e0a29ea083eb3c3eaf/engine.go). -Builder, engine-loading, and native validation errors use the operational path -(status `1`) with supported error identity and acquired diagnostics retained. -Malformed syntax, missing operands, and unsupported formats use usage status -`2`. Successful diagnostics use the common reporting policy. Report delivery -failures are recorded in `Outcome.Presentation.Output`, separately from the -operation cause. +Request construction, engine loading, and validation failures use status `1`, +retaining available diagnostics. Malformed syntax, missing arguments, and +unsupported formats use status `2`. Messages follow the common reporting +policy; report delivery failures follow the shared [output failure +rules](#output-failures). -### PromptKit catalogs +## PromptKit catalogs -An installed `ckprompt.Command(suite)` exposes each catalog whose callback is -present: +Use `promptkit list` to find available resources. Your application chooses +which listings to enable: ```text example promptkit list prompts @@ -826,34 +883,36 @@ example promptkit list namespaces --kind prompt --kind profile example promptkit list collections ``` -All catalogs accept `--config`, `--overlay`, applicable flags from -`Suite.Selectors`, and the shared `--format` flag. Automatic output uses text -for nonempty string lists and YAML for other inventories; explicit `text` uses -the same rendering for structured inventories. Prompt, profile, and backend -listings accept namespace selection: +All listings accept `--config`, `--overlay`, application-defined selector +flags, and the shared `--format` flag. Automatic output uses text for nonempty +string lists and YAML for other inventories; explicit `text` uses the same +rendering for structured inventories. Prompt, profile, and backend listings +accept namespace selection: | Binding | Effect | | --- | --- | -| omitted `--namespace` and `--all-namespaces` | Forward PromptKit's native default namespace filter. | -| `--namespace team` | Forward the named namespace filter. | +| omitted `--namespace` and `--all-namespaces` | Use PromptKit's default namespace filter. | +| `--namespace team` | List resources in that namespace. | | `--namespace ""` | Select the unqualified namespace. | -| `--all-namespaces` | Forward the all-namespaces filter. | -| `--all-namespaces=false` | Do not select an override; a supplied namespace still applies, otherwise native default is used. | +| `--all-namespaces` | List resources from all namespaces. | +| `--all-namespaces=false` | Do not select an override; a supplied namespace still applies, otherwise use PromptKit's default. | -The namespace binding is a singleton and its last occurrence is used. -Supplying a namespace while `--all-namespaces=true` is active is a usage error -before provider work. Namespace inventory accepts repeatable `--kind KIND`; -omitting `--kind` forwards PromptKit's native default kind selection. Repeated -values are forwarded in order. Collections have no namespace or kind filter. +If `--namespace` is repeated, its last value is used. Supplying a namespace +while `--all-namespaces=true` is active is a usage error before loading +resources. Namespace listings accept repeatable `--kind KIND`; omitting +`--kind` uses PromptKit's default kind selection. Repeated values are +forwarded in order. Collections have no namespace or kind filter. -Each operation reports one bare sequence, retaining provider order and native -identities. YAML examples follow: +### Listing results + +Each command returns a list in the provider's order, without an outer wrapper. +The entries are: - Prompt entries are `{id}`. - Profiles, backends, and namespaces are strings. - Collection entries are `{namespace, version?}`, where `version` projects PromptKit's optional release version. -- Nil and allocated-empty inventories both render as `[]`. +- An empty list renders as `[]`. ```yaml - id: team/briefing @@ -865,19 +924,18 @@ identities. YAML examples follow: ``` A catalog result is an inventory, not a readiness check or execution result. -The owning provider controls membership and order. Namespace filters are passed -to PromptKit without local membership interpretation. See the -[PromptKit v0.20.0 consumer guide](https://forge.maximumdirect.net/base-layer/promptkit/src/commit/8ff9aa3efdc9f16b9d2729e0a29ea083eb3c3eaf/docs/consumers/pkg-promptkit.md) -for native resource and selection definitions. +The owning provider controls membership and order. Namespace filters are +passed to PromptKit without local membership interpretation. See the +[PromptKit v0.20.0 consumer +guide](https://forge.maximumdirect.net/base-layer/promptkit/src/commit/8ff9aa3efdc9f16b9d2729e0a29ea083eb3c3eaf/docs/consumers/pkg-promptkit.md) +for resource and selection definitions. -### PromptKit smoke testing +## PromptKit smoke testing -When `Suite.SmokeTest` is present, `ckprompt.Command(suite)` installs -`promptkit test KIND`. The special kind `all` runs the four concrete operations -sequentially in this order: `reachability`, `authentication`, `text_inference`, -and `structured_output`. Both forms use the same single-operation callback; -each aggregate call changes the seed kind before invoking it. The command -accepts: +`promptkit test KIND` runs a PromptKit smoke test using the application's +configured client or network probes. Unlike inspection and validation, these +tests can contact a provider and request model output. The application must +enable smoke testing. ```text example promptkit test text_inference --campaign spring --llm-prompt briefing --llm-profile spring @@ -885,13 +943,39 @@ example promptkit test all --campaign spring --llm-prompt briefing --llm-profile example promptkit test authentication --llm-backend local ``` +The test kinds are: + +| Kind | Purpose | +| --- | --- | +| `reachability` | Check whether the endpoint can be reached. | +| `authentication` | Check authentication using the configured diagnostic support. | +| `text_inference` | Check text generation. | +| `structured_output` | Check structured output. | +| `all` | Run the four tests above in that order. | + +Support and applicability depend on PromptKit and the configured provider. An +unsupported or unknown observation is not a positive result. + +### Interpreting smoke results + +Status `0` means the requested operation and reporting completed without +error. It does not mean every check passed: inspect the reported facts, +`reason`, and whether the test was supported, applicable, and completed. For +`all`, all four operations must return without error for status `0`. +Individual errors use status `1`; the sequence continues after them but stops +on cancellation, preserving results already obtained. A report returned with +an error is still shown. Invalid syntax, arguments, formats, or conflicting +namespace flags use status `2`. + +### Smoke-test flags + | Flag | Values | Default | Meaning | | --- | --- | --- | --- | | `--config`, `--overlay` | path strings | omitted | Select PromptKit configuration; supplied presence is retained. | -| Suite selectors | application-defined | per selector | Captured flags from `Suite.Selectors`. | -| `--llm-backend` | string | empty | Native backend ID. | -| `--llm-prompt` | string | empty | Native prompt ID. | -| `--llm-profile` | string | empty | Native profile ID. | +| Application selectors | application-defined | per selector | Additional flags provided by your application. | +| `--llm-backend` | string | empty | Select a backend by ID. | +| `--llm-prompt` | string | empty | Select a prompt by ID. | +| `--llm-profile` | string | empty | Select a profile by ID. | | `--llm-endpoint` | string | omitted | Override the execution endpoint. | | `--llm-model` | string | omitted | Override the execution model. | | `--llm-temperature` | float | omitted | Override execution temperature. | @@ -900,34 +984,34 @@ example promptkit test authentication --llm-backend local | `--llm-timeout-seconds` | integer | omitted | Override the execution timeout. | | `--llm-service-tier` | string | omitted | Override the execution service tier. | | `--llm-reasoning-effort` | string | omitted | Override execution reasoning effort. | -| `--llm-api-key-env` | string | omitted | Supply an API-key environment-variable name to native configuration. | -| `--llm-transient-retries` | integer | omitted | Set the native inference transient retry count. | +| `--llm-api-key-env` | string | omitted | Name the environment variable containing the API key. | +| `--llm-transient-retries` | integer | omitted | Set the retry count for transient inference failures. | | `--llm-extra-params` | JSON object | omitted | Supply provider parameters as a whole map. | | `--format` | `text`, `yaml`, `json` | automatic | Select the report format; automatic and text use YAML for these structured reports. | -The execution flags populate PromptKit's native execution override. The +These execution flags override settings for the selected operation. The `--llm-api-key-env` value is a variable name, not a credential value. The extra-parameters flag accepts one JSON object, including an empty object; -PromptKit owns its native replacement semantics and validation. Integer JSON -numbers use signed or unsigned 64-bit integers where representable; decimal -and exponent numbers use floating point. +PromptKit determines how it replaces existing parameters and validates it. +Integer JSON numbers use signed or unsigned 64-bit integers where +representable; decimal and exponent numbers use floating point. -Application selectors, loader policy, credentials, and client wiring remain application -owned. The `WithSmokeTesting` helper accepts an optional typed builder. A nil -builder forwards the seed; a supplied builder's complete native request is -authoritative. The helper retains a native report when PromptKit returns both a -report and an error. +The application chooses how to load configuration, supply credentials, and +configure its client and probes. It may customize the request before passing +it to PromptKit; consult its documentation for application-specific behavior. -A single report projects the native smoke report. It preserves native -false, unknown/null, and incomplete values and does not add a readiness score. +### Smoke report fields + +A single report contains PromptKit's findings. False, unknown/null, and +incomplete values are preserved; CommandKit does not add a readiness score. The report includes `kind`, `supported`, `applicable`, `completed`, `reason`, `target`, `facts`, `timing`, `attempts`, `usage`, and `failure`. Its target -contains selected IDs, selection source, endpoint, effective -settings, and settings-presence flags. Attempt entries retain their own facts, -timing, failure, and token-usage evidence. Durations use Go duration strings; -token counts preserve `value`, `known`, and `complete`. +contains selected IDs, selection source, endpoint, effective settings, and +settings-presence flags. Attempt entries retain their own facts, timing, +failure, and token-usage evidence. Durations use Go duration strings; token +counts preserve `value`, `known`, and `complete`. -Here is a native projection as rendered by the command: +For example, this report says the configured HTTP diagnostic is unavailable: ```yaml kind: reachability @@ -988,11 +1072,11 @@ usage: failure: "" ``` -Aggregate output is one mapping with ordered `tests` entries. Each entry -has selected `kind`, nullable `report`, and optional `error` text. The inner -report keeps its native `kind`. This cancellation example shows two attempted -operations with no reports; later kinds are absent because cancellation stops -the sequence: +Aggregate output is one mapping with ordered `tests` entries. Each entry has +selected `kind`, nullable `report`, and optional `error` text. The inner +report keeps the `kind` returned by PromptKit. This cancellation example shows +two attempted operations with no reports; later kinds are absent because +cancellation stops the sequence: ```yaml tests: @@ -1004,16 +1088,10 @@ tests: error: "context canceled" ``` -A successful delivered single operation returns status `0`. For `all`, all -four callbacks must return without error for status `0`. Native false, unknown, -unsupported, or incomplete observations do not independently change the -outcome. Operation errors use operational status `1`; aggregate runs continue -after individual errors, retain their reports and ordered error text, and join -causes for supported `errors.Is`/`errors.As` access. Cancellation stops later -operations and preserves results already acquired. Report encoding or delivery -failures are recorded in `Outcome.Presentation.Output`, separately from -operation causes. Syntax, operand, unsupported format, and conflicting -namespace selection are usage status `2`. +Report formatting and write failures follow the shared [output failure +rules](#output-failures). Application developers can inspect operation causes +separately from delivery failures through the [reporting +API](consumers/guide.md#invocation-reporting). Native configuration, selection, and diagnostic meanings remain owned by PromptKit. See the [v0.20.0 consumer guide](https://forge.maximumdirect.net/base-layer/promptkit/src/commit/8ff9aa3efdc9f16b9d2729e0a29ea083eb3c3eaf/docs/consumers/pkg-promptkit.md), @@ -1022,12 +1100,20 @@ and [engine API](https://forge.maximumdirect.net/base-layer/promptkit/src/commit ## Outcome status -CommandKit usage failures use status `2`; operational failures use status `1`. -Successful operations use status `0`. Application handlers may return a domain -classification and status of their choice, which `Execute` preserves. The caller -chooses how to map an outcome to process exit. A rendering or delivery failure -changes a successful or usage outcome to operational status `1`. A domain -outcome keeps its classification and nonzero status; a zero-status domain -outcome becomes `1` if presentation fails. The returned `Outcome.Presentation` -records output and diagnostic delivery observations separately from its -operation `Cause`. +| Status | Standard meaning | +| --- | --- | +| `0` | The operation succeeded. | +| `1` | Loading, execution, cancellation, or output failed. | +| `2` | The command syntax or supplied inputs were invalid. | +| `3` | `config diff --check` found differences. | + +Applications may define additional results and decide how CommandKit's status +maps to the process exit code. A command's successful status does not replace +reading its report: partial requirements discovery and unsupported smoke +checks can return status `0`. + +An output failure changes a successful or usage result to status `1`. An +application-specific result retains its classification and nonzero status; a +zero status becomes `1` on output failure. Go callers can inspect operation +errors and output errors separately; see the [consumer +guide](consumers/guide.md#outcomes-and-ownership). diff --git a/docs/consumers/guide.md b/docs/consumers/guide.md index 8187a57..a2b0e12 100644 --- a/docs/consumers/guide.md +++ b/docs/consumers/guide.md @@ -1,40 +1,46 @@ # 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. Core and -compatible 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. +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` | The application needs typed command declarations and common CLI behavior. | -| `go.maximumdirect.net/ckconfig` | The application wants ConfigKit-backed configuration scopes and reports. | -| `go.maximumdirect.net/ckprompt` | The application wants PromptKit engine configuration/comparison, five catalogs, named inspection, requirements discovery, request validation, or native smoke testing. | +| `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. | -Core v0.4.0 is published. The previously published matched releases are core -v0.3.0, ckconfig v0.2.0, and ckprompt v0.2.0. -The published ckconfig v0.3.0 uses ConfigKit v1.1.0 and does not compose -with ckprompt v0.2.0, which retains ckconfig v0.2.0 and ConfigKit v0.8.0. -Install the current core with `go get go.maximumdirect.net/commandkit@v0.4.0`. -For the older matched integration set, select core v0.3.0 and the corresponding -ckconfig or ckprompt v0.2.0 release together. +The following distinguishes published combinations from the current checkout: -Published ckconfig v0.4.0 uses core v0.4.0 and ConfigKit v1.1.0. The ckprompt -v0.3.0 candidate targets those releases and PromptKit v0.20.0. A Go 1.27.0 -workspace may compose the three local modules. Current ckprompt source uses a -diagnostic helper introduced in local ckconfig source that is absent from -released ckconfig v0.4.0; independent ckprompt builds require release and -dependency alignment described in [future work](../roadmap/future.md#pending-release-and-integration-work). +| Selection | Core | ckconfig | ckprompt | Upstream libraries and status | +| --- | --- | --- | --- | --- | +| Published core | v0.4.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. | -The two integrations are independent optional imports. `ckprompt` uses the -public scope and error contracts from `ckconfig`; importing it does not add the -`config` family. An application that wants both families passes both returned -declarations to core. The [maintained examples](../../README.md#examples) show -core-only, ConfigKit-only, and composed applications. +Install the published core with: + +```sh +go get go.maximumdirect.net/commandkit@v0.4.0 +``` + +Core v0.5.0 and ckprompt v0.3.0 have unpublished candidate notes. 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 @@ -57,7 +63,6 @@ default from an explicitly supplied empty, false, or zero value. package example import ( - "fmt" "strconv" commandkit "go.maximumdirect.net/commandkit" @@ -81,8 +86,12 @@ func Greeting() commandkit.Command { ), }, Run: func(invocation commandkit.Invocation, input GreetInput) commandkit.Outcome { - if _, err := fmt.Fprintln(invocation.Reporter().Output(), input.Count.Value); err != nil { - return commandkit.Failure("Could not write the greeting.").WithCause(err) + 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() }, @@ -90,6 +99,10 @@ func Greeting() commandkit.Command { } ``` +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 @@ -134,15 +147,13 @@ installation alongside aliases and integration commands. ## Invocation reporting -`Invocation.Reporter()` supplies the reporter for the current handler. Use -`Report()` for formatted substantive values and optional text templates; -use `Output()` or `RenderOutput()` for deliberate raw 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. +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, @@ -159,22 +170,29 @@ 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 -Run: func(invocation commandkit.Invocation, input GreetInput) commandkit.Outcome { - if _, err := fmt.Fprintln(invocation.Reporter().Output(), input.Count.Value); err != nil { - return commandkit.Failure("Could not write the greeting.").WithCause(err) - } - return commandkit.Success() +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. -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. +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 @@ -235,18 +253,22 @@ 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 +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 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 @@ -274,6 +296,8 @@ 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, @@ -308,6 +332,8 @@ suite := ckprompt.WithRequestValidation( 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 @@ -357,6 +383,8 @@ 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. diff --git a/docs/development.md b/docs/development.md index d323e9a..3bab916 100644 --- a/docs/development.md +++ b/docs/development.md @@ -62,7 +62,7 @@ capability proposals in [future work](roadmap/future.md). | Public facade or adapter boundaries | [Internal overview](internal/overview.md), [ADR 0002](adr/0002-public-facade-and-cli-backend.md), [ADR 0004](adr/0004-optional-integration-modules.md), and the architecture policy | | Module ownership and public contracts | [Consumer guide](consumers/guide.md), [internal overview](internal/overview.md), [core GoDoc](../doc.go), [ckconfig GoDoc](../integrations/ckconfig/doc.go), [ckprompt GoDoc](../integrations/ckprompt/doc.go), and applicable ADRs | | Unfinished work and capability proposals | [Future work](roadmap/future.md) | -| Documentation refresh sprint | [Documentation roadmap](roadmap/documentation-refresh.md) | +| Documentation refresh record | [Documentation roadmap](roadmap/documentation-refresh.md) | | Completed planning and implementation history | [Completed dependency upgrades, PromptKit validation, discovery, smoke testing, and remediation](https://gitea.maximumdirect.net/eric/CommandKit/src/commit/a6278a6d9f940948ec7745955d745523aaff0734/docs/roadmap), [earlier ConfigKit and PromptKit adoption and remediation](https://gitea.maximumdirect.net/eric/CommandKit/src/commit/8a82620dfcd34c0ae56bbecb0d014eb49786bc92/docs/roadmap), [earlier retired records](https://gitea.maximumdirect.net/eric/CommandKit/src/commit/6bc37e7ba64ca0920b4d0cc77039a18d4006b24e/docs/roadmap), and [completed module implementation and remediation](https://gitea.maximumdirect.net/eric/CommandKit/src/commit/bb2528532b78d30e52f5082e491a5e1fd9b0fb99/docs/roadmap) in Git history | | Release tooling | [Maintainer release procedure](release.md) | | Performance maintenance | [Internal overview](internal/overview.md), existing benchmarks, and [possible further efficiency work](roadmap/future.md#possible-further-efficiency-work) | @@ -107,8 +107,16 @@ Sibling-checkout references in retired planning records are historical review evidence, not build dependencies. Implemented contracts belong in CommandKit under their canonical owners. -The root module does not traverse nested modules. Preserve the ignored `go.work` -and `go.work.sum`; use `GOWORK=off GOTOOLCHAIN=local` for dependency preparation +The root module does not traverse nested modules. With Go 1.27.0 on `PATH`, +create the local workspace from the repository root if `go.work` is absent: + +```sh +go work init . ./integrations/ckconfig ./integrations/ckprompt +``` + +If a workspace already exists, ensure it includes these three modules. Preserve +the ignored `go.work` and `go.work.sum`; do not commit them. Use +`GOWORK=off GOTOOLCHAIN=local` for dependency preparation and independent module suites. The integrations select released core v0.4.0; ckprompt also selects released ckconfig v0.4.0. Current ckprompt source requires a ckconfig helper absent from that release, so diff --git a/docs/releases/v0.5.0.md b/docs/releases/v0.5.0.md index 5184b81..d3c6209 100644 --- a/docs/releases/v0.5.0.md +++ b/docs/releases/v0.5.0.md @@ -1,32 +1,28 @@ # CommandKit v0.5.0 -CommandKit v0.5.0 fixes defects in deferred reporting and YAML value preservation, -reduces repeated work in command installation and alias help, and simplifies -typed input preparation and replay of captured flags. +CommandKit v0.5.0 fixes lost deferred messages and preserves YAML value types +more accurately. It also reduces repeated work when installing commands and +showing alias help, and refreshes documentation and help text. ## Changes -- Acceptance of deferred messages now closes atomically when invocation finalization - begins. Concurrent submissions are included in the final batch or rejected - with `ErrReporterClosed`; accepted messages can no longer be silently lost - between batch detachment and closure. -- Finalized reporting state releases its direct references to borrowed output - and diagnostic writers while retaining presentation results. Writers remain - caller-owned. -- `DecodeYAMLReport` follows resolved YAML scalar tags, preserving numeric-looking - strings as strings. YAML report rendering preserves explicit numeric meaning - for values beyond the resolver's ordinary numeric range. -- Compilation of flag aliases reuses resolved chain suffixes. Alias discovery and - help reuse the existing source tree, and declaration validation reuses one - scratch path across siblings, formatting paths only for diagnostics. -- Typed input preparation reconstructs occurrence order without general sorting; - replay of captured flags merges already ordered streams. Parsing and completion - share flag-consumption mechanics while retaining their distinct handling of - errors and incomplete input. -- The core reporting example preserves its default scenario when the optional - operand is omitted. The interactive completion fixture no longer collides with - the reserved `--format` flag. `FlagMeta` documentation now states the existing - naming and reserved-spelling rules. +- Deferred messages accepted before finalization are retained for final reporting; + they can no longer be silently lost when a handler finishes. Later submissions return + `ErrReporterClosed`. +- Finished invocations release their direct references to output and diagnostic + writers while keeping delivery results available. Applications still own the + writers. +- `DecodeYAMLReport` preserves numeric-looking strings as strings. YAML report + output also preserves explicitly tagged numbers beyond the YAML resolver's + ordinary numeric range. +- Command installation, alias help, and typed input handling avoid repeated + work. Parsing and completion share flag handling while retaining their + different treatment of errors and unfinished input. +- The reporting example now works when its optional scenario is omitted, and + the interactive completion fixture no longer conflicts with `--format`. + `FlagMeta` documents existing naming and reserved-spelling rules. +- READMEs, CLI instructions, examples, and generated help use clearer language + and explain common tasks before implementation details. ## Compatibility and upgrade diff --git a/docs/roadmap/documentation-refresh.md b/docs/roadmap/documentation-refresh.md index aba59ce..3191f90 100644 --- a/docs/roadmap/documentation-refresh.md +++ b/docs/roadmap/documentation-refresh.md @@ -1,7 +1,7 @@ # Documentation refresh -Status: agreed scope; ready for implementation planning. The refresh has not -been implemented. +Status: implemented and verified. Release/dependency alignment remains separate +work in [future.md](future.md#pending-release-and-integration-work). ## Purpose @@ -197,18 +197,57 @@ Do not add wording snapshots, readability gates, or a new checker framework. Record workspace and independent-module results separately, including any dependency limitation. This work adds no release gates. -- [ ] README openings explain purpose and give a clear starting path. -- [ ] CLI sections explain operator tasks before reference detail; necessary +- [x] README openings explain purpose and give a clear starting path. +- [x] CLI sections explain operator tasks before reference detail; necessary behavior, report fields, status rules, and limitations remain discoverable. -- [ ] Example setup is explicit, commands have nearby explanations, and intended +- [x] Example setup is explicit, commands have nearby explanations, and intended failures are labeled. -- [ ] Consumer guidance demonstrates formatted reporting, has a coherent initial +- [x] Consumer guidance demonstrates formatted reporting, has a coherent initial example, and distinguishes published compatibility from current source needs. -- [ ] Package introductions and generated help use clear, accurate language; +- [x] Package introductions and generated help use clear, accurate language; precise API contracts remain intact. -- [ ] Unpublished release notes explain benefits and consumer impact while +- [x] Unpublished release notes explain benefits and consumer impact while historical records and required notices remain preserved. -- [ ] Changed commands, snippets, and help have appropriate verification; local +- [x] Changed commands, snippets, and help have appropriate verification; local links and Markdown whitespace checks pass. -- [ ] Final review confirms consistent terminology, canonical ownership, and no +- [x] Final review confirms consistent terminology, canonical ownership, and no unintended behavior or contract changes. + +## Implementation and verification + +Completed the six work areas above. READMEs and package introductions now lead +with purpose and starting points; the CLI reference separates operator tasks +from report detail and developer wiring. Examples are grouped by task with +setup, expected results, and intentional failures explained nearby. The consumer +guide demonstrates shared report formatting and distinguishes published modules +from current source. Help strings and unpublished candidate notes use the same +plain language. Exact symbol contracts and runtime behavior were preserved. + +Verification used Go 1.27.0 and existing cached dependencies: + +- The aligned workspace suites passed for core, ckconfig, and ckprompt, including + their maintained examples. +- Independent core and ckconfig suites passed with `GOWORK=off`. +- Independent ckprompt compilation confirmed the existing dependency limitation: + released ckconfig v0.4.0 lacks `AdaptDiagnostics`. No module requirements, + checksums, or workspace files were changed to hide it. Its README, example, + consumer guidance, and candidate note identify the pending alignment. +- All 48 `go run` invocations in the three example READMEs were exercised with + binaries built from the aligned workspace, from the documented directories. + Results matched the walkthroughs, including two failed-load examples, missing + request input, and comparison status 3. Shared help and completion behavior + also passed the existing suites; representative rendered help was inspected. +- Both new consumer-guide report snippets compiled in a temporary consumer using + local core. Six checks confirmed greeting counts, empty-list output, and YAML + and JSON formatting. +- Local Markdown links, including incoming links to renamed sections, and + whitespace were checked. Go source changes are limited to help strings, + package comments, and formatting; no new tests or validation framework were + added. +- The authoritative remote had neither the core v0.5.0 nor ckprompt v0.3.0 tag + when their candidate notes were revised. Published notes, accepted ADRs, + policies, and license/attribution records were left intact. + +The updated ckprompt candidate still requires dependency guidance to be finalized +when release alignment occurs. This is release preparation, not unfinished +editorial work or evidence of an independently buildable candidate. diff --git a/docs/roadmap/future.md b/docs/roadmap/future.md index 7b2266a..b5f42f7 100644 --- a/docs/roadmap/future.md +++ b/docs/roadmap/future.md @@ -1,7 +1,7 @@ # Future work -Status: documentation refresh, pending release/dependency alignment, optional -shell validation, and future capability proposals. +Status: pending release/dependency alignment, optional shell validation, and +future capability proposals. No delivery dates are selected here. ## Purpose and authority @@ -30,13 +30,6 @@ an idea here does not select its syntax, API, priority, or delivery date. ## Pending release and integration work -### Documentation refresh - -The agreed [documentation roadmap](documentation-refresh.md) owns the upcoming -clarity refresh, its scope, sequence, and completion checklist. The work covers -operator instructions and public introductions while retaining necessary -technical detail in developer documentation. - ### Audit remediation release alignment The audit remediation is complete in source. The released ckconfig v0.4.0 does diff --git a/examples/core/README.md b/examples/core/README.md index 82b600a..fdddbf8 100644 --- a/examples/core/README.md +++ b/examples/core/README.md @@ -1,38 +1,70 @@ # Core example -Run the example from the repository root: +This example shows typed flags, command shortcuts, shared reporting, and shell +completion. It needs no external service. See the [consumer guide](../../docs/consumers/guide.md#go-compatibility) +for Go requirements; maintainers use the toolchain in the +[developer guide](../../docs/development.md#documentation-validation). + +Run from the repository root, with that Go toolchain on `PATH`: ```sh +export GOWORK=off GOTOOLCHAIN=local +go run ./examples/core workspace greet --count=2 +``` + +The result shows the selected count and whether it was supplied. It also shows +the entered command path and the original command path, which differ when you +use a shortcut. + +## Try shortcuts and explicit values + +```sh +# The hello shortcut runs workspace greet; --times means --count. go run ./examples/core hello --times=0 --tag=first +# ws is a shortcut for the workspace group. go run ./examples/core ws greet --count=2 +# Inspect the group or use the example's global help alias. go run ./examples/core ws --help go run ./examples/core hello --hlep +``` + +An explicit zero remains a supplied value. The handler prints both +`Invocation.EnteredPath()` and `Invocation.CanonicalPath()` so you can see how +shortcuts resolve. + +## Try reporting + +```sh +go run ./examples/core report +go run ./examples/core report --verbose +go run ./examples/core report --quiet +``` + +The default scenario succeeds and writes its result. Verbose mode also shows +warnings and information; quiet mode hides the result. The +[CLI reference](../../docs/cli.md#reporting) defines the shared visibility rules. + +## Try shell completion + +```sh go build -o core-example ./examples/core ./core-example completion bash ``` -The example installs a canonical `workspace greet` command together with a -command shortcut, a group shortcut, a scoped flag alias, and a global help -alias. Its declaration slice mixes the canonical group and routing rules as -`[]commandkit.Declaration`. The handler prints `Invocation.EnteredPath()` and -`Invocation.CanonicalPath()` so the alternate route and its canonical target -are visible in the output. +This prints the Bash completion script. Initialize `bash-completion`, then load +the script as described in the [CLI reference](../../docs/cli.md#shell-completion). +Use the executable basename `core-example` and try completing +`core-example report d` or `core-example ws gr`. Other supported shells are +listed in the reference. -The example also installs `Completion("core-example")` and gives the `report` -scenario argument static choices. Initialize `bash-completion` first if using -Bash. Build it with the basename shown above, load -the script for your shell using the [CLI reference](../../docs/cli.md#shell-completion), -and try completing `core-example report d` or `core-example ws gr`. +## How it is wired -Compatible integration factories also return `commandkit.Command` and can be -passed directly to `New` beside `commandkit.Alias` or flag-alias declarations. -The matched ckconfig and ckprompt releases use the new core identity. 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 +The example installs `workspace greet` alongside command and flag aliases in a +`[]commandkit.Declaration`. It also installs `Completion("core-example")` and +provides static choices for the reporting scenario argument. 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. +explains declaration composition; the [composed example](../../integrations/ckprompt/examples/composed) +adds integration commands. -These aliases require core v0.3.0. Repository contributors can run its test with -`go test ./examples/core` from the root. +Run `GOWORK=off GOTOOLCHAIN=local go test ./examples/core` from the repository +root to check this example. diff --git a/integrations/ckconfig/README.md b/integrations/ckconfig/README.md index 1ccc00e..36d2406 100644 --- a/integrations/ckconfig/README.md +++ b/integrations/ckconfig/README.md @@ -1,36 +1,30 @@ # CommandKit ckconfig -`ckconfig` is the optional ConfigKit integration for CommandKit. It adapts -ConfigKit loaders and scopes into reusable `commandkit.Command` declarations; -installing the integration is explicit and does not modify an application by -itself. +`ckconfig` adds configuration inspection commands to Go applications that use +CommandKit and ConfigKit. Operators can view settings, validate configuration, +find where values came from, and compare two configurations. + +The application chooses which commands to install. Importing the package alone +does not install commands. ## Module -The module path is `go.maximumdirect.net/ckconfig`. Version v0.4.0 requires Go -1.27.0 and uses `go.maximumdirect.net/commandkit v0.4.0` and -`go.maximumdirect.net/configkit v1.1.0`; it does not import PromptKit. See the -[consumer guide](https://gitea.maximumdirect.net/eric/CommandKit/src/branch/main/docs/consumers/guide.md#configkit-scopes) -for exact callback and lifetime contracts. +The module path is `go.maximumdirect.net/ckconfig`. It does not import PromptKit. +See the [consumer guide](https://forge.maximumdirect.net/base-layer/commandkit/src/branch/main/docs/consumers/guide.md#module-selection) +for compatible releases and Go requirements. ## Example -For one application configuration scope, install the convenience declaration: +To inspect one application configuration, pass your loader to `AppCommand`: ```go app, err := commandkit.New(identity, ckconfig.AppCommand(loadAppConfig)) ``` -Use `ckconfig.Command(ckconfig.Suite[Input, State]{...})` to define named -scopes, selectors, input checks, and state shared by the selected acquisitions -or comparison in a config invocation. `ckconfig.FromConfigKit` adapts a typed -ConfigKit loader. The `config` command family exposes scope discovery and the -validate, show, sources, explain, and diff operations supported by the declared -scopes. Source reports include native destination, placement, destination -fallback, version evidence, and empty-import selections. Comparison returns -ConfigKit's native records to shared report rendering; the -[CLI reference](https://gitea.maximumdirect.net/eric/CommandKit/src/branch/main/docs/cli.md#compare-configuration-selections) -owns its operator details. +For multiple named configurations or application-specific flags, use +`ckconfig.Command` with a `ckconfig.Suite`. The +[integration guide](https://forge.maximumdirect.net/base-layer/commandkit/src/branch/main/docs/consumers/guide.md#configkit-scopes) +explains loader setup, inputs, and shared state. The maintained [simple example](examples/simple) shows a complete consumer. The [CommandKit CLI reference](https://gitea.maximumdirect.net/eric/CommandKit/src/branch/main/docs/cli.md#configuration-scopes) diff --git a/integrations/ckconfig/doc.go b/integrations/ckconfig/doc.go index 3e28f2b..c50decb 100644 --- a/integrations/ckconfig/doc.go +++ b/integrations/ckconfig/doc.go @@ -1,10 +1,11 @@ -// Package ckconfig, provided by go.maximumdirect.net/ckconfig, provides -// CommandKit's optional ConfigKit v1.1.0 integration, requiring Go 1.27.0. Its -// 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, -// version, and empty-import evidence. Invocation reporting applies CommandKit's shared -// visibility and delivery policy; provider warnings remain independent of -// failure explanations. Importing ckconfig alone installs no commands. +// Package ckconfig adds commands to view, validate, explain, and compare +// ConfigKit configuration in CommandKit applications. +// +// Use AppCommand for one application configuration loader, or Command with a +// Suite for multiple named configurations and application-specific flags. +// Install the returned command with commandkit.New or App.Register. +// Importing this package alone installs no commands. +// +// ConfigKit owns configuration loading and interpretation. CommandKit formats +// reports and messages using its shared reporting policy. package ckconfig diff --git a/integrations/ckconfig/examples/simple/README.md b/integrations/ckconfig/examples/simple/README.md index 1608ce1..ef396c8 100644 --- a/integrations/ckconfig/examples/simple/README.md +++ b/integrations/ckconfig/examples/simple/README.md @@ -1,37 +1,90 @@ -# Simple consumer +# ConfigKit example -This example imports `go.maximumdirect.net/ckconfig` and -`go.maximumdirect.net/commandkit`; ckconfig v0.4.0 uses core -v0.4.0, ConfigKit v1.1.0, and Go 1.27.0. Set `GOWORK=off GOTOOLCHAIN=local` -for the commands below. +This example adds configuration inspection and a custom `greet` command to a +small application. It uses local fixtures and in-memory defaults, with no +external services. See [module selection](../../../../docs/consumers/guide.md#module-selection) +for dependencies and Go requirements. Use Go 1.27.0 on `PATH` for this checkout. -Run the maintained example with: +From the repository root: ```sh +cd integrations/ckconfig/examples/simple +export GOWORK=off GOTOOLCHAIN=local go run . config show -go run . config show --config fixtures/before.yml --verbose -go run . config show --config fixtures/before.yml --quiet +``` + +The output includes the default greeting, `Hello`, and schema version 2. +This example can run against its declared released dependencies with the +workspace disabled. + +## Inspect and override a value + +```sh +# Discover configuration paths under schema. go run . config explain --list --under /schema +# Change the greeting for this invocation and show just that value. go run . config show --path /greeting --set '/greeting="Hi"' +``` + +The second command prints `Hi`. It does not edit a file. + +## Compare configurations + +```sh go run . config diff --config fixtures/before.yml --against-config fixtures/after.yml +``` + +The report includes `changes.greeting: Goodbye`. + +```sh +# Expected status 3: the overridden greeting differs from the ordinary default. go run . config diff --set '/greeting="Hi"' --details --check +``` + +This compares the overridden left side with ordinary configuration on the +right. `go run` prints `exit status 3` and itself exits with status 1; a built +executable returns the application's status 3 directly. + +## Inspect warnings and failed loads + +```sh +# Schema version 1 produces a warning, visible in verbose mode. +go run . config show --config fixtures/before.yml --verbose +# Quiet mode hides the configuration report and warning. +go run . config show --config fixtures/before.yml --quiet +``` + +Ordinary mode hides the warning but still displays configuration. + +```sh +# Intentional failure: show detailed validation findings for the invalid fixture. go run . config validate --config fixtures/invalid.yml --details --severity error +# Intentional failure: still show the source recorded before loading failed. go run . config sources --config fixtures/invalid.yml --source-id source-1 +``` + +Both commands retain the load failure. `source-1` belongs to this fixture's +load; other configurations can have different source IDs. See the +[CLI reference](../../../../docs/cli.md#configuration-scopes) for report and status rules. + +## Try a custom command + +```sh go run . greet go run . greet --count=0 --tag=first --tag second ``` -The `config` family comes from one typed ConfigKit scope. The custom `greet` -command uses CommandKit's typed bindings and shows the difference between an -omitted `--count` and an explicitly supplied zero, plus ordered repeated tags. -With no `--config`, application settings use the in-memory default. A supplied -configuration and overlay are passed to ConfigKit unchanged. The comparison -command loads the two local fixtures and reports `changes.greeting: Goodbye`. -The before fixture uses schema version 1, which ConfigKit reports as a provider -warning in verbose mode. Ordinary mode hides the warning while preserving the -effective YAML report; quiet mode suppresses that report. -The invalid fixture demonstrates a failed-load report and captured source -inventory; `source-1` is the ID from that fixture's acquisition. The scope opts -in to transient overrides and passes them into its normal ConfigKit load. The -diff example compares an overridden left side with ordinary configuration on -the right and returns status 3 because the values differ. +The handler reports its greeting, count, and tags. It distinguishes an omitted +count from an explicitly supplied zero and preserves repeated tag order. + +## How it is wired + +One ConfigKit scope supplies the standard `config` commands. Without `--config`, +its loader uses an in-memory default. Supplied configuration and overlay values +are passed to ConfigKit unchanged. The scope enables temporary overrides and +passes them into its ordinary ConfigKit load. The +[consumer guide](../../../../docs/consumers/guide.md#configkit-scopes) explains +loader and scope setup. + +Run `GOWORK=off GOTOOLCHAIN=local go test ./...` from this example directory to +check it against its declared dependencies. diff --git a/integrations/ckconfig/input.go b/integrations/ckconfig/input.go index fed78d2..e362d04 100644 --- a/integrations/ckconfig/input.go +++ b/integrations/ckconfig/input.go @@ -73,13 +73,13 @@ func newConfigOperationCommand[I, S any](prepared *preparedSuite[I, S], operatio scopeChoices = append(scopeChoices, commandkit.CompletionChoice{Value: "all"}) } flags := []commandkit.Flag[*configCommandInput[I, S]]{ - commandkit.ValueFlag(commandkit.FlagMeta{Name: "scope", Usage: "Configuration scope name or all where supported.", Completion: commandkit.CompleteChoices(scopeChoices...)}, &defaultScope, parseConfigString, + commandkit.ValueFlag(commandkit.FlagMeta{Name: "scope", Usage: "Select a named configuration, or all where supported.", Completion: commandkit.CompleteChoices(scopeChoices...)}, &defaultScope, parseConfigString, func(input **configCommandInput[I, S], value commandkit.Supplied[string]) { (*input).scope = value }), - commandkit.ValueFlag(commandkit.FlagMeta{Name: "config", Usage: "Configuration source.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseConfigString, + commandkit.ValueFlag(commandkit.FlagMeta{Name: "config", Usage: "Select the configuration source.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseConfigString, func(input **configCommandInput[I, S], value commandkit.Supplied[string]) { (*input).selection.Config = value }), - commandkit.ValueFlag(commandkit.FlagMeta{Name: "overlay", Usage: "Overlay source.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseConfigString, + commandkit.ValueFlag(commandkit.FlagMeta{Name: "overlay", Usage: "Select the configuration overlay.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseConfigString, func(input **configCommandInput[I, S], value commandkit.Supplied[string]) { (*input).selection.Overlay = value }), @@ -89,11 +89,11 @@ func newConfigOperationCommand[I, S any](prepared *preparedSuite[I, S], operatio } if operation == Validate { flags = append(flags, - commandkit.BoolFlag(commandkit.FlagMeta{Name: "details", Usage: "Show native validation findings."}, false, func(input **configCommandInput[I, S], value commandkit.Supplied[bool]) { (*input).details = value }), + commandkit.BoolFlag(commandkit.FlagMeta{Name: "details", Usage: "Show detailed validation findings."}, false, func(input **configCommandInput[I, S], value commandkit.Supplied[bool]) { (*input).details = value }), commandkit.RepeatFlag(commandkit.FlagMeta{Name: "severity", Usage: "Filter detailed findings by error or warning."}, []configkit.Severity(nil), parseValidationSeverity, func(input **configCommandInput[I, S], value commandkit.Supplied[[]configkit.Severity]) { (*input).severities = value.Value }), - commandkit.RepeatFlag(commandkit.FlagMeta{Name: "code", Usage: "Filter detailed findings by exact native code."}, []string(nil), parseConfigString, func(input **configCommandInput[I, S], value commandkit.Supplied[[]string]) { + commandkit.RepeatFlag(commandkit.FlagMeta{Name: "code", Usage: "Filter detailed findings by exact diagnostic code."}, []string(nil), parseConfigString, func(input **configCommandInput[I, S], value commandkit.Supplied[[]string]) { (*input).codes = value.Value }), ) @@ -114,16 +114,16 @@ func newConfigOperationCommand[I, S any](prepared *preparedSuite[I, S], operatio flags = append(flags, commandkit.ValueFlag(commandkit.FlagMeta{Name: "path", Usage: "JSON Pointer path to explain."}, (*string)(nil), parseConfigString, func(input **configCommandInput[I, S], value commandkit.Supplied[string]) { (*input).path = value }), commandkit.BoolFlag(commandkit.FlagMeta{Name: "list", Usage: "List configuration paths."}, false, func(input **configCommandInput[I, S], value commandkit.Supplied[bool]) { (*input).list = value }), - commandkit.BoolFlag(commandkit.FlagMeta{Name: "historical", Usage: "Include historical-only paths when listing."}, false, func(input **configCommandInput[I, S], value commandkit.Supplied[bool]) { (*input).historical = value }), + commandkit.BoolFlag(commandkit.FlagMeta{Name: "historical", Usage: "Include paths that exist only in configuration history."}, false, func(input **configCommandInput[I, S], value commandkit.Supplied[bool]) { (*input).historical = value }), commandkit.ValueFlag(commandkit.FlagMeta{Name: "under", Usage: "List paths under a JSON Pointer."}, (*string)(nil), parseConfigString, func(input **configCommandInput[I, S], value commandkit.Supplied[string]) { (*input).under = value }), - commandkit.BoolFlag(commandkit.FlagMeta{Name: "explicit", Usage: "Filter listed current paths by explicitness."}, false, func(input **configCommandInput[I, S], value commandkit.Supplied[bool]) { (*input).explicit = value }), + commandkit.BoolFlag(commandkit.FlagMeta{Name: "explicit", Usage: "Filter current paths by whether ConfigKit marks them explicit."}, false, func(input **configCommandInput[I, S], value commandkit.Supplied[bool]) { (*input).explicit = value }), ) } if operation == Show { flags = append(flags, commandkit.ValueFlag(commandkit.FlagMeta{Name: "path", Usage: "JSON Pointer to one effective value."}, (*string)(nil), parseConfigString, func(input **configCommandInput[I, S], value commandkit.Supplied[string]) { (*input).path = value })) } if operation == Sources { - flags = append(flags, commandkit.ValueFlag(commandkit.FlagMeta{Name: "source-id", Usage: "Select one source ID from this acquisition."}, (*string)(nil), parseConfigString, func(input **configCommandInput[I, S], value commandkit.Supplied[string]) { (*input).sourceID = value })) + flags = append(flags, commandkit.ValueFlag(commandkit.FlagMeta{Name: "source-id", Usage: "Show one source ID from this configuration load."}, (*string)(nil), parseConfigString, func(input **configCommandInput[I, S], value commandkit.Supplied[string]) { (*input).sourceID = value })) } flags = append(flags, commandkit.CaptureFlags(prepared.bindingsByOperation[operation], func(input **configCommandInput[I, S]) *commandkit.FlagValues[I] { if input == nil || *input == nil { @@ -160,7 +160,7 @@ func newConfigOperationCommand[I, S any](prepared *preparedSuite[I, S], operatio func comparisonOperationFlags[I, S any]() []commandkit.Flag[*configCommandInput[I, S]] { return []commandkit.Flag[*configCommandInput[I, S]]{ - commandkit.BoolFlag(commandkit.FlagMeta{Name: "details", Usage: "Show native before and after difference evidence."}, false, func(input **configCommandInput[I, S], value commandkit.Supplied[bool]) { (*input).details = value }), + commandkit.BoolFlag(commandkit.FlagMeta{Name: "details", Usage: "Show before and after values with presence and redaction details."}, false, func(input **configCommandInput[I, S], value commandkit.Supplied[bool]) { (*input).details = value }), commandkit.BoolFlag(commandkit.FlagMeta{Name: "check", Usage: "Return status 3 when differences are found."}, false, func(input **configCommandInput[I, S], value commandkit.Supplied[bool]) { (*input).check = value }), commandkit.ValueFlag(commandkit.FlagMeta{Name: "against-config", Usage: "Configuration source for the right side.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseConfigString, func(input **configCommandInput[I, S], value commandkit.Supplied[string]) { @@ -191,7 +191,7 @@ func operationDeclaration(operation Operation) (string, string) { case Explain: return "explain", "Explain a configuration path." case Diff: - return "diff", "Compare two configuration selections." + return "diff", "Compare two configurations." default: return string(operation), "Inspect configuration." } diff --git a/integrations/ckprompt/README.md b/integrations/ckprompt/README.md index 0001e67..88284c9 100644 --- a/integrations/ckprompt/README.md +++ b/integrations/ckprompt/README.md @@ -1,37 +1,40 @@ # CommandKit ckprompt -`ckprompt` is the optional PromptKit integration for CommandKit. It adapts -PromptKit engine configuration, native comparison, five catalogs, named -inspection, requirements discovery and validation, and native smoke tests. -Importing it does not install either command family. +`ckprompt` adds PromptKit inspection and testing commands to Go applications +that use CommandKit. Operators can list prompts, profiles, and backends, inspect +their settings, discover required inputs, validate and preview requests, and +run smoke tests. + +The application chooses which commands to install. Importing the package alone +does not install commands. ## Module -The module path is `go.maximumdirect.net/ckprompt`. This source uses Go 1.27.0, -`go.maximumdirect.net/commandkit v0.4.0`, `go.maximumdirect.net/ckconfig v0.4.0`, -`go.maximumdirect.net/configkit v1.1.0`, and `go.maximumdirect.net/promptkit v0.20.0`. See the -[consumer guide](https://gitea.maximumdirect.net/eric/CommandKit/src/branch/main/docs/consumers/guide.md#promptkit-configuration-definitions-and-requirements) -for callback, input, state, and acquisition contracts. +The module path is `go.maximumdirect.net/ckprompt`. See the +[consumer guide](https://forge.maximumdirect.net/base-layer/commandkit/src/branch/main/docs/consumers/guide.md#module-selection) +for compatible releases, Go requirements, and the current source's dependency +alignment requirement. ## Example -Add `ckprompt.ConfigScope(loadEngine)` to the scopes passed to -`ckconfig.Command` when the application wants PromptKit engine configuration -and native comparison in the `config` family. Install -`ckprompt.Command(ckprompt.FromEngine(loadEngine))` separately for catalogs, -named inspections, and requirements discovery. `FromEngine` supplies nine -PromptKit-backed callbacks: four inspections and five catalogs. Applications -may narrow, wrap, or replace them before capture or provide custom callbacks -with `ckprompt.Suite`. Add concrete request validation, prepared-request preview, -and smoke testing explicitly with `WithRequestValidation`, -`WithRequestPreparation`, and `WithSmokeTesting`. Inspection, -requirements, and catalog operations do not execute prompts or contact model -providers; application loaders and callbacks retain their own effects. Smoke -testing invokes the selected native PromptKit operation; the -application retains ownership of client and configuration choices. See the -[CLI reference](https://gitea.maximumdirect.net/eric/CommandKit/src/branch/main/docs/cli.md#promptkit-smoke-testing) -for operator commands and report semantics. The maintained -[composed example](examples/composed) shows complete application wiring. +Pass your engine loader to `FromEngine` to add resource listings, inspection, +and requirements discovery: + +```go +app, err := commandkit.New(identity, ckprompt.Command(ckprompt.FromEngine(loadEngine))) +``` + +Add request validation, preview, and smoke tests with `WithRequestValidation`, +`WithRequestPreparation`, and `WithSmokeTesting`. To expose engine configuration +under `config`, add `ckprompt.ConfigScope(loadEngine)` to a `ckconfig.Suite`. +The [integration guide](https://forge.maximumdirect.net/base-layer/commandkit/src/branch/main/docs/consumers/guide.md#promptkit-configuration-definitions-and-requirements) +explains these choices; the [composed example](examples/composed) shows them together. + +Inspection commands do not call the model. Smoke tests can use the application's +configured client or network probes. Applications own engine setup, credentials, +and any effects of their loaders and callbacks. See the +[CLI reference](https://forge.maximumdirect.net/base-layer/commandkit/src/branch/main/docs/cli.md#promptkit-smoke-testing) +for commands and results. ## License and attribution diff --git a/integrations/ckprompt/command.go b/integrations/ckprompt/command.go index 085b021..838e191 100644 --- a/integrations/ckprompt/command.go +++ b/integrations/ckprompt/command.go @@ -91,7 +91,7 @@ func buildCommand[I, S any](suite Suite[I, S], aliases *[]commandkit.Declaration showCommands = append(showCommands, newPreparationCommand(suite, aliases)) } if len(showCommands) != 0 { - rootCommands = append(rootCommands, commandkit.Group("show", "Show a named PromptKit definition.", showCommands...)) + rootCommands = append(rootCommands, commandkit.Group("show", "Show PromptKit definitions or preview a prepared request.", showCommands...)) } listCommands := make([]commandkit.Command, 0, 5) if suite.ListPrompts != nil { @@ -110,7 +110,7 @@ func buildCommand[I, S any](suite Suite[I, S], aliases *[]commandkit.Declaration listCommands = append(listCommands, newCatalogListCommand(suite, "collections")) } if len(listCommands) != 0 { - rootCommands = append(rootCommands, commandkit.Group("list", "List PromptKit catalogs.", listCommands...)) + rootCommands = append(rootCommands, commandkit.Group("list", "List available PromptKit resources.", listCommands...)) } requirementsCommands := make([]commandkit.Command, 0, 2) if suite.InspectRequirements != nil { @@ -125,7 +125,7 @@ func buildCommand[I, S any](suite Suite[I, S], aliases *[]commandkit.Declaration if suite.SmokeTest != nil { rootCommands = append(rootCommands, newSmokeTestCommand(suite, aliases)) } - command := commandkit.RootGroup("promptkit", "Inspect, list, validate requests, and run PromptKit smoke tests.", rootCommands...) + command := commandkit.RootGroup("promptkit", "Inspect PromptKit resources, check requests, and run smoke tests.", rootCommands...) for _, issue := range suite.issues { command = commandkit.WithError(command, issue) } @@ -140,16 +140,16 @@ func newCatalogListCommand[I, S any](suite Suite[I, S], name string) commandkit. return &(*input).captured }) flags = append(flags, - commandkit.ValueFlag(commandkit.FlagMeta{Name: "config", Usage: "Configuration input path.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseDefinitionString, + commandkit.ValueFlag(commandkit.FlagMeta{Name: "config", Usage: "Select the configuration input path.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseDefinitionString, func(input **catalogInput[I], value commandkit.Supplied[string]) { (*input).selection.Config = value }), - commandkit.ValueFlag(commandkit.FlagMeta{Name: "overlay", Usage: "Configuration overlay path.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseDefinitionString, + commandkit.ValueFlag(commandkit.FlagMeta{Name: "overlay", Usage: "Select the configuration overlay path.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseDefinitionString, func(input **catalogInput[I], value commandkit.Supplied[string]) { (*input).selection.Overlay = value }), ) if name == "prompts" || name == "profiles" || name == "backends" { flags = append(flags, - commandkit.ValueFlag(commandkit.FlagMeta{Name: "namespace", Usage: "Select one native namespace."}, (*string)(nil), parseDefinitionString, + commandkit.ValueFlag(commandkit.FlagMeta{Name: "namespace", Usage: "List resources in one namespace."}, (*string)(nil), parseDefinitionString, func(input **catalogInput[I], value commandkit.Supplied[string]) { (*input).namespace = value }), - commandkit.BoolFlag(commandkit.FlagMeta{Name: "all-namespaces", Usage: "Select identities from every namespace."}, false, + commandkit.BoolFlag(commandkit.FlagMeta{Name: "all-namespaces", Usage: "List resources from every namespace."}, false, func(input **catalogInput[I], value commandkit.Supplied[bool]) { (*input).allNamespaces = value }), ) } @@ -243,9 +243,9 @@ func newDefinitionCommand[I, S any](suite Suite[I, S], name string, aliases *[]c return &(*input).captured }) flags = append(flags, - commandkit.ValueFlag(commandkit.FlagMeta{Name: "config", Usage: "Configuration input path.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseDefinitionString, + commandkit.ValueFlag(commandkit.FlagMeta{Name: "config", Usage: "Select the configuration input path.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseDefinitionString, func(input **definitionInput[I], value commandkit.Supplied[string]) { (*input).selection.Config = value }), - commandkit.ValueFlag(commandkit.FlagMeta{Name: "overlay", Usage: "Configuration overlay path.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseDefinitionString, + commandkit.ValueFlag(commandkit.FlagMeta{Name: "overlay", Usage: "Select the configuration overlay path.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseDefinitionString, func(input **definitionInput[I], value commandkit.Supplied[string]) { (*input).selection.Overlay = value }), @@ -288,11 +288,11 @@ func newRequirementsCommand[I, S any](suite Suite[I, S], aliases *[]commandkit.D return &(*input).captured }) flags = append(flags, - commandkit.ValueFlag(commandkit.FlagMeta{Name: "config", Usage: "Configuration input path.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseDefinitionString, + commandkit.ValueFlag(commandkit.FlagMeta{Name: "config", Usage: "Select the configuration input path.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseDefinitionString, func(input **requirementsInput[I], value commandkit.Supplied[string]) { (*input).selection.Config = value }), - commandkit.ValueFlag(commandkit.FlagMeta{Name: "overlay", Usage: "Configuration overlay path.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseDefinitionString, + commandkit.ValueFlag(commandkit.FlagMeta{Name: "overlay", Usage: "Select the configuration overlay path.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseDefinitionString, func(input **requirementsInput[I], value commandkit.Supplied[string]) { (*input).selection.Overlay = value }), @@ -308,8 +308,8 @@ func newRequirementsCommand[I, S any](suite Suite[I, S], aliases *[]commandkit.D spec := commandkit.CommandSpec[*requirementsInput[I]]{ Name: "show", - Summary: "Inspect requirements for a named PromptKit prompt.", - Description: "Profile selection establishes the reported profile identity, not a different set of requirements. A profile namespace qualifies an explicitly supplied short profile ID, not the prompt’s stored default reference.", + Summary: "Discover the inputs and variables a prompt needs.", + Description: "List required inputs without running the prompt. Choosing a profile changes the reported profile ID, not the requirements. A profile namespace applies to a short profile ID supplied here; it does not change the prompt’s stored default reference.", NewInput: func() *requirementsInput[I] { return &requirementsInput[I]{} }, diff --git a/integrations/ckprompt/doc.go b/integrations/ckprompt/doc.go index b0c10ec..8ac4552 100644 --- a/integrations/ckprompt/doc.go +++ b/integrations/ckprompt/doc.go @@ -1,13 +1,13 @@ -// Package ckprompt, provided by go.maximumdirect.net/ckprompt, provides optional -// PromptKit configuration comparison, -// prompt catalogs, named inspections, requirements discovery and validation, -// prepared-request preview, and native smoke testing for CommandKit. Command -// returns a canonical declaration; Declarations adds scoped flag aliases. -// Applications can install those alongside other declarations through -// commandkit.New or App.Register. Importing ckprompt installs neither family; callers compose its -// declarations with core and ckconfig explicitly. Provider diagnostics remain -// passive until the owning command submits them through the invocation -// reporter, separately from any terminal explanation. This module uses -// PromptKit v0.20.0 and its native engine inventory, including configured -// bundled resources. +// Package ckprompt adds PromptKit inspection and testing commands to CommandKit +// applications. Operators can list resources, inspect definitions, discover +// required inputs, validate and preview requests, and run smoke tests. +// +// Start with FromEngine and add optional operations with WithRequestValidation, +// WithRequestPreparation, and WithSmokeTesting. Command returns the command +// family; Declarations also includes its convenience flag aliases. Install +// these with commandkit.New or App.Register. +// +// ConfigScope separately exposes engine configuration through ckconfig. +// Importing this package installs neither command family. Applications own +// engine construction, credentials, and client configuration. package ckprompt diff --git a/integrations/ckprompt/docs/releases/v0.3.0.md b/integrations/ckprompt/docs/releases/v0.3.0.md index d8cf524..3e02304 100644 --- a/integrations/ckprompt/docs/releases/v0.3.0.md +++ b/integrations/ckprompt/docs/releases/v0.3.0.md @@ -1,27 +1,40 @@ # CommandKit ckprompt v0.3.0 -CommandKit ckprompt v0.3.0 adopts PromptKit v0.20.0 and ConfigKit v1.1.0, -and requires CommandKit core v0.4.0, ckconfig v0.4.0, and Go 1.27.0. -The integration uses shared core reporting for text, YAML, and JSON output. +This unpublished candidate adds PromptKit v0.20.0 support, including request +preview, namespace selection, and shared text, YAML, and JSON output. It uses +ConfigKit v1.1.0 and requires Go 1.27.0. -Prompt, profile, and backend commands report PromptKit's native resource -inspections. Applications may opt into prepared-request preview, concrete -request validation, smoke testing, and namespace selectors independently. -Reusable request flag collections support application-owned commands; -`ckprompt.Declarations` installs the standard family with scoped aliases. -Literal and file inputs retain their command-line order. Provider parameters -can be supplied for preparation and applicable diagnostics. +## Operator features -The PromptKit configuration scope exposes native metadata and load-failure -evidence through ckconfig, including independent comparison selections, -detailed differences, and validation findings. Smoke reports retain native -evidence and operation failures separately from presentation failures. +- Inspect prompts, profiles, and backends, and list available resources. +- Preview rendered requests without calling the model, or validate that a + request can be prepared with the supplied inputs. +- Choose namespaces and override request settings for supported commands. +- Run smoke tests and inspect their results, including incomplete or unsupported + checks and reports returned alongside errors. +- Inspect engine configuration through `config`, including detailed validation + findings, source information after failed loads, and configuration comparisons. -This pre-1.0 release changes resource report shapes, input flags, formatting, -and configuration comparison behavior. Applications should update declarations -and report consumers to the current [CLI reference](../../../../docs/cli.md#promptkit-definition-inspection) -and [consumer guide](../../../../docs/consumers/guide.md#promptkit-configuration-definitions-and-requirements). -Applications continue to own engine construction, credentials, and execution. +Applications choose which commands to enable. Reusable flag collections also +let application commands offer the same request flags. `ckprompt.Declarations` +adds the standard commands with their shorter flag aliases. Literal and file +inputs keep their command-line order. + +## Compatibility and preparation + +This candidate changes resource report shapes, input flags, formatting, and +configuration comparison behavior. Review the current +[CLI reference](../../../../docs/cli.md#promptkit-definition-inspection) and +[consumer guide](../../../../docs/consumers/guide.md#promptkit-configuration-definitions-and-requirements) +when updating declarations or code that reads reports. Applications continue to +own engine construction, credentials, and execution. + +The current source requires `ckconfig.AdaptDiagnostics`, which is absent from +its selected ckconfig v0.4.0 dependency. Before publication, complete +[dependency alignment](../../../../docs/roadmap/future.md#pending-release-and-integration-work) +and update this candidate's dependency guidance to the selected releases. +The [module selection table](../../../../docs/consumers/guide.md#module-selection) +distinguishes published combinations from the current workspace. [COPYRIGHT](https://forge.maximumdirect.net/base-layer/commandkit/src/tag/integrations/ckprompt/v0.3.0/integrations/ckprompt/COPYRIGHT) | [LICENSE](https://forge.maximumdirect.net/base-layer/commandkit/src/tag/integrations/ckprompt/v0.3.0/integrations/ckprompt/LICENSE) | diff --git a/integrations/ckprompt/examples/composed/README.md b/integrations/ckprompt/examples/composed/README.md index b1e775b..dc6009f 100644 --- a/integrations/ckprompt/examples/composed/README.md +++ b/integrations/ckprompt/examples/composed/README.md @@ -1,141 +1,172 @@ -# Composed consumer +# ConfigKit and PromptKit example -This example is part of the ckprompt module and uses core v0.4.0, ckconfig -v0.4.0, ConfigKit v1.1.0, and PromptKit v0.20.0. It requires Go 1.27.0. +This example combines application and PromptKit configuration, resource +inspection, request checks, and smoke tests. It uses local fixtures and an +in-memory model client. **No model service or credentials are required.** -This example composes application configuration, PromptKit configuration, -PromptKit comparison and catalogs, definition and requirements inspection, and -one application-owned command: +Use Go 1.27.0 on `PATH` and an aligned workspace containing all three local +modules. Follow the [workspace setup](../../../../docs/development.md#documentation-validation) +from the repository root first. Current ckprompt source needs a helper in local +ckconfig source that its released dependency does not yet contain; independent +builds remain pending [release alignment](../../../../docs/roadmap/future.md#pending-release-and-integration-work). +The [consumer guide](../../../../docs/consumers/guide.md#module-selection) owns +version and compatibility details. + +From the repository root: ```sh +cd integrations/ckprompt/examples/composed +export GOWORK=auto GOTOOLCHAIN=local go run . config scopes +go run . promptkit list prompts --campaign spring +go run . promptkit show prompt briefing --campaign spring --format yaml +``` + +The first command lists `app` and `promptkit` configurations and their available +operations. The next commands find and inspect the `briefing` prompt. +`--campaign` is a selector supplied by this example, not a standard PromptKit flag. + +## Validate and compare configuration + +```sh go run . config validate --scope all --campaign spring --limit=0 go run . config diff --scope app --config fixtures/before.yml --against-config fixtures/after.yml -go run . config diff --scope app --config fixtures/before.yml --against-config fixtures/after.yml --campaign spring --limit=0 -go run . config diff --scope app --campaign spring --limit=0 --against-campaign autumn --against-limit=0 --set '/campaigns/spring/model="left"' --against-set '/campaigns/spring/model="right"' --details go run . config diff --scope promptkit --config fixtures/before.yml --against-config fixtures/after.yml --campaign spring --against-campaign spring -go run . promptkit list prompts --config fixtures/before.yml --campaign spring +``` + +The application scope requires `--limit=0` when `--campaign` is supplied. +The app comparison reports a changed default campaign and `campaigns.spring.model`. +The PromptKit comparison selects spring on both sides and reports the changed +profiles list as one value. + +The two sides have independent selectors and overrides: + +```sh +go run . config diff --scope app --campaign spring --limit=0 --against-campaign autumn --against-limit=0 --set '/campaigns/spring/model="left"' --against-set '/campaigns/spring/model="right"' --details +``` + +Only the application scope supports `--set` here. The released PromptKit loader +has no override handoff, so its configuration scope does not enable the feature. +An aggregate operation that includes that scope rejects overrides. + +## List resources and select namespaces + +```sh go run . promptkit list profiles --config fixtures/after.yml --campaign autumn --format yaml go run . promptkit list backends --campaign spring go run . promptkit list namespaces --campaign spring go run . promptkit list collections --campaign spring -go run . list collections --campaign spring -go run . pk list bundles --campaign spring go run . promptkit list prompts --namespace field-notes --campaign spring -go run . promptkit show prompt briefing --campaign spring --format yaml go run . promptkit show prompt briefing --campaign spring --prompt-namespace field-notes -go run . promptkit show prepared briefing --campaign spring --input topic=sprint --profile spring --extra-params '{"campaign":"preview"}' -go run . pk show prompt briefing --campaign spring --fmt yaml go run . promptkit show profile spring --campaign spring +``` + +The profile list includes the selected `autumn` profile. PromptKit's default +bundle contributes additional resources. The example also supplies a +`field-notes` collection containing a backend, profile, and prompt. Listing +filters and resource namespace flags let you inspect that collection. + +## Discover inputs, then check a request + +```sh go run . promptkit requirements show briefing --campaign spring go run . promptkit requirements show briefing --campaign spring --var mode=full --format yaml +``` + +The first report is incomplete: the `notes` input is conditional and `mode` is +missing. Supplying `mode=full` selects that branch and makes `notes` required. +Both commands succeed; discovery alone does not establish that a request can run. + +```sh go run . promptkit requirements validate briefing --campaign spring --llm-input-file topic=fixtures/topic.txt go run . promptkit requirements validate briefing --campaign spring --llm-input-file topic=fixtures/topic.txt --llm-var mode=full --llm-input-file notes=fixtures/notes.txt --format yaml +``` + +Both commands check request preparation and report `prompt_id` and `valid: true`. +The builder provides campaign-specific variable defaults; the operator supplies +the input files. For comparison, this command intentionally fails because the +required `topic` input is missing: + +```sh +# Intentional failure: missing topic input. go run . promptkit requirements validate briefing --campaign spring +``` + +Preview a request with literal input instead of a file: + +```sh +go run . promptkit show prepared briefing --campaign spring --input topic=sprint --profile spring --extra-params '{"campaign":"preview"}' +``` + +The preview includes rendered messages and effective provider settings. +Validation and preview do not call the model or guarantee later execution. + +## Run smoke tests + +```sh go run . promptkit test text_inference --campaign spring --prompt briefing --profile spring -go run . smoke text_inference --campaign spring --llm-prompt briefing --llm-profile spring +# Some diagnostics can fail or be unsupported; inspect the retained report. go run . promptkit test all --campaign spring --llm-prompt briefing --llm-profile spring +``` + +Inference uses the example's in-memory client. HTTP checks may be unsupported +or not applicable because the local backend has no network probe. The aggregate +can return a failure while still showing results. Read the facts in the report; +a successful command status does not mean every observation is positive. + +For any command that returns nonzero, `go run` prints `exit status N` and itself +exits with status 1. A built executable returns the application's status directly. +The [CLI reference](../../../../docs/cli.md#promptkit-smoke-testing) explains the +standard test kinds, results, and statuses. + +## Try aliases and application commands + +```sh +go run . list collections --campaign spring +go run . pk list bundles --campaign spring +go run . pk show prompt briefing --campaign spring --fmt yaml +go run . smoke text_inference --campaign spring --llm-prompt briefing --llm-profile spring go run . pk --hlep go run . session restore session-42 --campaign spring go run . request inspect --llm-prompt briefing --llm-profile= --llm-model example --llm-input-text topic=sprint --format json ``` -`newApp` installs application-owned command and flag aliases alongside the -complete `ckconfig` and `ckprompt` declarations. `list collections` and -`promptkit list bundles` both reach the canonical collection command; `pk` -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 first two commands reach `promptkit list collections`. `pk` shortens the +PromptKit group; `--fmt` is an alias on prompt inspection and `--hlep` is a global +help alias. `smoke` shortens `promptkit test` while leaving the test kind as an +argument. The session command uses an in-memory store. `request inspect` shows +how an application command can reuse canonical `--llm-*` flag collections without +installing PromptKit's short aliases. -The example installs `Completion("composed-example")`. Build it as -`go build -o composed-example .`. Initialize `bash-completion` first if using -Bash, then load a shell script as described in the -[CLI reference](../../../../docs/cli.md#shell-completion). Try completion after -`composed-example pk show prompt briefing --fmt=` or after the filename prefix -in `composed-example config show --config=fixtures/`. The already-entered `pk` -alias selects the PromptKit command context without loading an engine. +## Try shell completion -The composition installs `ckconfig.Command(suite)` and the complete -`ckprompt.Declarations(definitions)` in a `[]commandkit.Declaration` with -root-installed routing rules. The PromptKit declarations include scoped aliases; -the application-owned `request inspect` command reuses the flag collections -with their canonical names and does not install those aliases. -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 -selected scope's input separately and `NewState` to create one cache shared by -that invocation's acquisitions. Prompt/profile inspection prepares its input and -cache independently for its invocation, using the same application and engine -loader functions. See the -[consumer guide](https://gitea.maximumdirect.net/eric/CommandKit/src/branch/main/docs/consumers/guide.md) -and the suites' GoDoc for initialization timing and ownership. - -The application-scope comparison reports the changed default campaign and the -updated `campaigns.spring.model`. The PromptKit-scope comparison selects spring -on both sides and reports the native engine change in the atomic profiles list. -The application input check requires `--limit=0` when `--campaign` is supplied. -Catalogs use the same selection-aware loader: the prompt catalog includes -`briefing`, and the profile catalog includes the selected `autumn` profile. -Prompt inspection uses an ID only. The default PromptKit bundle also contributes -profiles and a backend to the engine inventory and source reports. The synthetic `field-notes` collection -provides one backend, profile, and prompt for collection discovery and native -namespace filtering. The example's shared run cache keys successful results by -the complete supplied configuration/overlay selection, CLI overrides, and the -supplied campaign, including presence. The campaign override and application -scope's `--set` records are applied through ConfigKit before loading. The -PromptKit configuration scope does not enable `--set` because its released -configuration loader has no override handoff. An aggregate operation therefore -rejects overrides when that scope is selected. These are choices for this -example's loaders and do not define a CommandKit cache policy. - -Smoke testing is explicitly wired with `ckprompt.WithSmokeTesting` and its -optional request builder is nil, so CommandKit forwards the selected native -request. Applications that need to adjust the request can provide a typed -builder that starts with the seed and returns the chosen native request: - -```go -definitions = ckprompt.WithSmokeTesting(definitions, loadEngine, - func(ctx context.Context, seed ckprompt.SmokeRequest, input *inspectionInput, state *runCache) (promptkit.SmokeTestRequest, error) { - request := seed.Request - request.ProfileID = "spring" - return request, nil - }) +```sh +go build -o composed-example . +./composed-example completion bash ``` -The example injects a small in-memory `promptkit.LLMClient` through its shared -run state. Catalog, inspection, and requirements commands do not call it; -inference smoke commands do. No model service or credentials are required. -Native HTTP checks may retain unsupported or not-applicable observations because -the selected local backend does not configure a network probe. An aggregate smoke command can -return an operational outcome for a native diagnostic while still delivering -the complete aggregate report. +Initialize `bash-completion` if using Bash, then load the generated script using +the [CLI reference](../../../../docs/cli.md#shell-completion). Try completing +`composed-example pk show prompt briefing --fmt=` or +`composed-example config show --config=fixtures/`. The already-entered `pk` alias +selects the command context without loading an engine. -The first requirements command reports incomplete discovery: PromptKit records -the conditional `notes` input and a missing `mode` issue. Supplying `mode=full` -selects that branch, classifies the undeclared `notes` input as required, and -completes discovery. Both commands return successful reports; the result does -not execute the prompt or establish input, credential, or execution readiness. -See the [CLI reference](../../../../docs/cli.md#promptkit-requirements) for the -command and report contract. +## How it is wired -The validation and preview commands exercise concrete PromptKit request preparation. -The first uses the builder's campaign-specific default topic variable and the -operator's topic file. The second also selects the notes branch and supplies its -file. Validation reports only `prompt_id` and `valid: true`; preview returns the -prepared request, including rendered messages and effective provider settings. -Neither executes the prompt. The validation command without inputs intentionally -fails because the prompt's -required `topic` artifact is not supplied. The builder defaults variables from -the selected campaign but leaves artifact bindings to the operator. The -`requirements show` commands remain discovery reports: partial discovery can -succeed even when a concrete request would fail validation. +The example installs `ckconfig.Command(suite)` and +`ckprompt.Declarations(definitions)` with application-owned aliases in a +`[]commandkit.Declaration`. `NewInput` initializes scope inputs; `NewState` +creates a cache shared within an invocation. The loaders cache successful +configuration results by the complete configuration/overlay selection, +overrides, and supplied campaign, including whether each value was supplied. +These are example choices, not a CommandKit cache policy. -Run `GOWORK=off go test ./...` from this example directory to check it against -its declared dependencies. The [developer guide](../../../../docs/development.md#documentation-validation) -also describes aligned workspace tests. +Smoke testing uses `WithSmokeTesting` with a nil builder, forwarding the request +selected by flags. Applications can supply a builder to customize that request. +See the [consumer guide](../../../../docs/consumers/guide.md#promptkit-configuration-definitions-and-requirements) +for suite composition, builders, and ownership, and the [source](main.go) for +the complete application. + +Run `GOWORK=auto GOTOOLCHAIN=local go test ./...` from this example directory +for an aligned workspace check. Once dependency alignment is complete, also use +`GOWORK=off GOTOOLCHAIN=local go test ./...` to check the declared releases. diff --git a/integrations/ckprompt/namespaces.go b/integrations/ckprompt/namespaces.go index 9901c9c..fcf9736 100644 --- a/integrations/ckprompt/namespaces.go +++ b/integrations/ckprompt/namespaces.go @@ -39,9 +39,9 @@ func namespaceFlags[I any](destination func(*I) *namespaceInput, kinds ...string } var namespaceFlagDefinitions = struct{ prompt, profile, backend requestFlagDefinition }{ - prompt: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-prompt-namespace", Usage: "Prompt resource namespace."}, []string{"prompt-namespace"}}, - profile: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-profile-namespace", Usage: "Profile resource namespace."}, []string{"profile-namespace"}}, - backend: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-backend-namespace", Usage: "Backend resource namespace."}, []string{"backend-namespace"}}, + prompt: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-prompt-namespace", Usage: "Use this namespace for unqualified prompt IDs."}, []string{"prompt-namespace"}}, + profile: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-profile-namespace", Usage: "Use this namespace for unqualified profile IDs."}, []string{"profile-namespace"}}, + backend: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-backend-namespace", Usage: "Use this namespace for unqualified backend IDs."}, []string{"backend-namespace"}}, } func selectedNamespaces(defaults *promptkit.Namespaces, selected namespaceInput) *promptkit.Namespaces { diff --git a/integrations/ckprompt/request_flags.go b/integrations/ckprompt/request_flags.go index 21b04ff..7eb1f08 100644 --- a/integrations/ckprompt/request_flags.go +++ b/integrations/ckprompt/request_flags.go @@ -35,24 +35,24 @@ var requestFlags = struct { InputText requestFlagDefinition InputFile requestFlagDefinition }{ - Endpoint: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-endpoint", Usage: "Execution endpoint override."}, []string{"endpoint"}}, - Model: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-model", Usage: "Execution model override."}, []string{"model"}}, - Temperature: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-temperature", Usage: "Execution temperature override."}, []string{"temperature"}}, - MaxTokens: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-max-tokens", Usage: "Execution maximum tokens override."}, []string{"max-tokens"}}, - TopP: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-top-p", Usage: "Execution top-p override."}, []string{"top-p"}}, - TimeoutSeconds: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-timeout-seconds", Usage: "Execution timeout override in seconds."}, []string{"timeout-seconds"}}, - ServiceTier: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-service-tier", Usage: "Execution service tier."}, []string{"service-tier"}}, - ReasoningEffort: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-reasoning-effort", Usage: "Execution reasoning effort override."}, []string{"reasoning-effort"}}, - APIKeyEnv: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-api-key-env", Usage: "Execution API key environment variable."}, []string{"api-key-env"}}, - TransientRetries: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-transient-retries", Usage: "Inference transient retry count."}, []string{"transient-retries"}}, - ExtraParams: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-extra-params", Usage: "Execution provider parameters as a JSON object."}, []string{"extra-params"}}, - Prompt: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-prompt", Usage: "PromptKit prompt ID."}, []string{"prompt"}}, - Profile: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-profile", Usage: "PromptKit profile ID."}, []string{"profile"}}, - Backend: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-backend", Usage: "PromptKit backend ID."}, []string{"backend"}}, - SessionID: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-session-id", Usage: "PromptKit session ID."}, []string{"session-id"}}, - Var: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-var", Usage: "Caller variable assignment KEY=VALUE."}, []string{"var"}}, - InputText: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-input-text", Usage: "Literal prompt input assignment NAME=TEXT."}, []string{"input-text", "llm-input", "input"}}, - InputFile: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-input-file", Usage: "Prompt input file assignment NAME=PATH."}, []string{"input-file"}}, + Endpoint: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-endpoint", Usage: "Use this endpoint."}, []string{"endpoint"}}, + Model: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-model", Usage: "Use this model."}, []string{"model"}}, + Temperature: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-temperature", Usage: "Use this temperature."}, []string{"temperature"}}, + MaxTokens: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-max-tokens", Usage: "Use this maximum token count."}, []string{"max-tokens"}}, + TopP: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-top-p", Usage: "Use this top-p value."}, []string{"top-p"}}, + TimeoutSeconds: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-timeout-seconds", Usage: "Set the request timeout in seconds."}, []string{"timeout-seconds"}}, + ServiceTier: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-service-tier", Usage: "Use this service tier."}, []string{"service-tier"}}, + ReasoningEffort: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-reasoning-effort", Usage: "Use this reasoning effort."}, []string{"reasoning-effort"}}, + APIKeyEnv: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-api-key-env", Usage: "Name the environment variable containing the API key."}, []string{"api-key-env"}}, + TransientRetries: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-transient-retries", Usage: "Set the retry count for transient inference failures."}, []string{"transient-retries"}}, + ExtraParams: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-extra-params", Usage: "Supply provider parameters as a JSON object."}, []string{"extra-params"}}, + Prompt: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-prompt", Usage: "Select a prompt by ID."}, []string{"prompt"}}, + Profile: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-profile", Usage: "Select a profile by ID."}, []string{"profile"}}, + Backend: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-backend", Usage: "Select a backend by ID."}, []string{"backend"}}, + SessionID: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-session-id", Usage: "Supply the session ID."}, []string{"session-id"}}, + Var: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-var", Usage: "Supply a prompt variable as KEY=VALUE."}, []string{"var"}}, + InputText: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-input-text", Usage: "Supply text for a prompt input as NAME=TEXT."}, []string{"input-text", "llm-input", "input"}}, + InputFile: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-input-file", Usage: "Supply a file for a prompt input as NAME=PATH."}, []string{"input-file"}}, } // ExecutionInput contains request-scoped execution controls supplied on the CLI. diff --git a/integrations/ckprompt/smoke_command.go b/integrations/ckprompt/smoke_command.go index d80c46d..f230551 100644 --- a/integrations/ckprompt/smoke_command.go +++ b/integrations/ckprompt/smoke_command.go @@ -29,11 +29,11 @@ func newSmokeTestCommand[I, S any](suite Suite[I, S], aliases *[]commandkit.Decl return &(*input).captured }) flags = append(flags, - commandkit.ValueFlag(commandkit.FlagMeta{Name: "config", Usage: "Configuration input path.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseDefinitionString, + commandkit.ValueFlag(commandkit.FlagMeta{Name: "config", Usage: "Select the configuration input path.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseDefinitionString, func(input **smokeCommandInput[I], value commandkit.Supplied[string]) { (*input).selection.Config = value }), - commandkit.ValueFlag(commandkit.FlagMeta{Name: "overlay", Usage: "Configuration overlay path.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseDefinitionString, + commandkit.ValueFlag(commandkit.FlagMeta{Name: "overlay", Usage: "Select the configuration overlay path.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseDefinitionString, func(input **smokeCommandInput[I], value commandkit.Supplied[string]) { (*input).selection.Overlay = value }), @@ -53,14 +53,15 @@ func newSmokeTestCommand[I, S any](suite Suite[I, S], aliases *[]commandkit.Decl } return commandkit.NewCommand(commandkit.CommandSpec[*smokeCommandInput[I]]{ - Name: "test", - Summary: "Run one PromptKit smoke test.", + Name: "test", + Summary: "Run one or all PromptKit smoke tests.", + Description: "Tests use the configured client or probes and may contact a provider. The all option runs the four tests in order. Read the report: a successful command status does not mean every observation is positive.", NewInput: func() *smokeCommandInput[I] { return &smokeCommandInput[I]{} }, Flags: flags, Arguments: []commandkit.Argument[*smokeCommandInput[I]]{ - commandkit.ValueArgument(commandkit.ArgumentMeta{Name: "kind", Usage: "Native smoke-test kind.", Completion: smokeKindCompletion()}, true, parseDefinitionString, + commandkit.ValueArgument(commandkit.ArgumentMeta{Name: "kind", Usage: "Test kind: reachability, authentication, text_inference, structured_output, or all.", Completion: smokeKindCompletion()}, true, parseDefinitionString, func(input **smokeCommandInput[I], value commandkit.Supplied[string]) { (*input).kind = value.Value }), }, CheckInput: func(input *smokeCommandInput[I]) error { diff --git a/integrations/ckprompt/validation_command.go b/integrations/ckprompt/validation_command.go index d141989..d3f1c5b 100644 --- a/integrations/ckprompt/validation_command.go +++ b/integrations/ckprompt/validation_command.go @@ -31,11 +31,11 @@ func newConcreteRequestSpec[I, S any](suite Suite[I, S], path string, aliases *[ return &(*input).captured }) flags = append(flags, - commandkit.ValueFlag(commandkit.FlagMeta{Name: "config", Usage: "Configuration input path.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseDefinitionString, + commandkit.ValueFlag(commandkit.FlagMeta{Name: "config", Usage: "Select the configuration input path.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseDefinitionString, func(input **concreteRequestInput[I], value commandkit.Supplied[string]) { (*input).request.Selection.Config = value }), - commandkit.ValueFlag(commandkit.FlagMeta{Name: "overlay", Usage: "Configuration overlay path.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseDefinitionString, + commandkit.ValueFlag(commandkit.FlagMeta{Name: "overlay", Usage: "Select the configuration overlay path.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseDefinitionString, func(input **concreteRequestInput[I], value commandkit.Supplied[string]) { (*input).request.Selection.Overlay = value }), @@ -93,7 +93,7 @@ func newConcreteRequestSpec[I, S any](suite Suite[I, S], path string, aliases *[ func newValidationCommand[I, S any](suite Suite[I, S], aliases *[]commandkit.Declaration) commandkit.Command { spec := newConcreteRequestSpec(suite, "promptkit requirements validate", aliases) spec.Name = "validate" - spec.Summary = "Validate a concrete PromptKit request without executing it." + spec.Summary = "Check whether a request can be prepared without calling the model." spec.Run = func(invocation commandkit.Invocation, input *concreteRequestInput[I]) commandkit.Outcome { return runRequestValidation(invocation, suite, input) } diff --git a/internal/commandmeta/metadata.go b/internal/commandmeta/metadata.go index 8104c0e..3b5452c 100644 --- a/internal/commandmeta/metadata.go +++ b/internal/commandmeta/metadata.go @@ -70,7 +70,7 @@ type CompletionResult struct { } var reportingFlags = [...]Flag{ - {Name: "quiet", Usage: "suppress substantive output and optional messages; keep failures visible", Default: "false", Boolean: true}, + {Name: "quiet", Usage: "hide command output and optional messages; keep failures visible", Default: "false", Boolean: true}, {Name: "verbose", Usage: "show warning and informational messages", Default: "false", Boolean: true}, {Name: "debug", Usage: "show debugging messages", Default: "false", Boolean: true}, {Name: "progress", Usage: "show progress messages", Default: "false", Boolean: true},