47 KiB
CommandKit CLI standard proposal
Status: proposed; no commands in this document are implemented by CommandKit.
Date: 2026-10-06.
This proposal defines the command suite, operator behavior, and ownership boundary recommended for CommandKit. Audita, Narratio, Notarius, and Scriptorium provide evidence, rather than compatibility requirements. Adoption is expected to produce breaking changes. The decisions recorded below are accepted; other recommendations remain proposed. Implementation details and exact Go API declarations remain future work.
Accepted planning decisions
- Use namespace/resource first:
app config showandapp promptkit show. Whether the first noun describes a resource or a library namespace does not change the command grammar or its ownership boundary. - Put PromptKit definition inspection under the single
promptkitfamily. CommandKit does not reserve the rootpromptandprofilenames. - Support
config show --scope all, with the application configuration followed by the PromptKit configuration when registered, as separate YAML documents. - Defer JSON output from the initial implementation. YAML supports automated consumption as well as operator inspection; JSON may never be needed and is not a promised later milestone. JSON Pointer remains the configuration path syntax and is independent of report format.
1. Purpose and ownership
Recommendation: CommandKit owns the entire config command family, root help,
and version reporting. It also offers the standard promptkit inspection family
for applications that integrate PromptKit. Applications own commands
outside these families, such as narratio session restore, narratio publish,
audita process, and scriptorium serve.
Ownership includes command names, accepted flags and arguments, defaults, help, report shapes, diagnostic presentation, streams, and exit behavior. Applications provide configuration schemas, source acquisition, domain resolution, validation, build metadata, and PromptKit engine construction. These collaborators supply data and perform domain work; they do not replace CommandKit's handlers or renderers.
An application's registration of a standard family adopts its complete contract.
It cannot redefine config show, add a private config artifacts operation, or
choose a different meaning for config sources. Domain reports belong outside
the reserved family. New shared operations become part of CommandKit through an
explicit standard change.
Applications may register context selectors and typed configuration overrides through the extension mechanism described in section 5. This is a defined input extension, not permission to alter standard operations or output.
Alternatives:
- Share helpers while applications own their handlers. Easy to integrate, but permits the same naming, scope, and output drift that motivates CommandKit.
- Allow arbitrary subcommands beneath standard families. Convenient initially, but makes the standard unpredictable and creates future name collisions.
- Own every command in every application. Unnecessary for the stated goals; domain operations have responsibilities that a shared inspection library cannot usefully prescribe. CommandKit can supply common parsing and help machinery without owning their business semantics.
2. Evidence and conclusions
The current checkouts demonstrate both reuse opportunities and choices that need to be replaced rather than preserved.
| Evidence | Conclusion for the standard |
|---|---|
Audita uses config print-effective; Notarius uses config effective; Narratio and Scriptorium use config show. |
Select one verb: show. |
Audita combines settings, selected profiles, and provenance under --view; Notarius uses --scope; Scriptorium has a separate framework configuration family. |
Separate the operation from the inspected configuration scope. |
Narratio's config sources reports field ownership; Scriptorium lists acquired source records. |
Give source inventory and field explanation distinct contracts. |
| Scriptorium's application and framework handlers repeat parsing, loading, warnings, dispatch, and output handling. | CommandKit should implement one inspection command family. |
| Notarius and Scriptorium inspect named PromptKit profiles; Scriptorium also inspects prompts. | Definition inspection is another demonstrated shared capability. |
| Narratio compares resolved overlays, rather than YAML file text. | Include semantic comparison of effective configuration. |
| Help, flag placement, validation success output, and error statuses differ. | Specify those behaviors centrally. |
Narratio invokes the other applications and interprets Scriptorium's status 2 as output validation failure. |
Breaking adoption requires coordinated consumer updates. |
Evidence sources, relative to this roadmap in the sibling workspace checkouts:
- Audita CLI contract and configuration parsing.
- Narratio CLI contract, configuration inspection, and comparison.
- Notarius CLI contract and configuration commands.
- Scriptorium CLI contract, application inspection, and framework inspection.
- ConfigKit inspection API, source records, and configuration contract.
- PromptKit configuration inspection and consumer guide.
These links document review evidence; they are not build dependencies. When the standard becomes implemented behavior, its durable reference should be owned by CommandKit rather than relying on sibling checkout links.
3. Recommended command inventory
Required core suite
| Command | Operator question or task |
|---|---|
app help [COMMAND ...] |
How do I use this command? |
app version |
Which application build am I running? |
app config scopes |
Which configuration scopes can I inspect? |
app config validate |
Is the selected configuration valid? |
app config show [--scope app|promptkit|all] |
What effective configuration will the application use? |
app config sources |
Which inputs contributed to this configuration? |
app config explain --path POINTER |
Why does this path have this value, or why was it removed? |
app config diff --against-config PATH or --against-overlay NAME |
How do two effective configurations differ? |
app is a placeholder for the application's executable name. Section 4 defines
scope defaults; section 5 defines selectors shared by configuration operations.
Optional PromptKit suite
| Command | Operator question or task |
|---|---|
app promptkit list prompts |
Which prompt IDs and versions are available? |
app promptkit list profiles |
Which execution profiles are available? |
app promptkit show prompt ID [--prompt-version VERSION] |
What is the selected prompt's definition metadata? |
app promptkit show profile ID |
What execution target does this named profile resolve to? |
Accepted: put this suite under promptkit, using the same namespace/resource
first pattern as config. The root names prompt and profile remain available
to applications.
Recommendation: use explicit resource operands after list and show.
Plural collection names select catalogs; singular names select one definition.
This keeps prompt IDs and execution profile IDs unambiguous even when they overlap.
Registering the suite reserves the complete promptkit family, supplies all four
operations, and registers the promptkit configuration scope.
Configuration inspection remains config show --scope promptkit; the definition
commands report metadata and resolved execution targets, not a second configuration
display path.
Definition catalogs require upstream API work; their absence today is an implementation dependency, not evidence against a useful command standard.
Alternatives:
- Bare
promptkit list. Could display a combined catalog with resource type, ID, and prompt version. Useful for discovery, but potentially noisy. An explicit resource gives each catalog a clear report shape; a combined listing can be considered as an additional operation later. promptkit show ID --type profile. Explicit and compact, but places the resource identity partly in a flag. Prefer the resource operand.promptkit show ID. Requires cross-collection lookup and ambiguity rules. Do not make inspection depend on prompt and profile IDs being globally unique.promptkit profiles list/show. Retains the accepted namespace boundary and is viable, but adds another resource grouping before the operation. The proposedpromptkit list/show RESOURCElayout puts the operation immediately after its owning namespace, as inconfig show.- Put definitions under
config, such asconfig profile. Mixes source configuration with backend-completed execution settings and givesconfigan unrelated resource vocabulary. - Include only the core suite initially. A reasonable delivery phase. Keep the PromptKit suite explicitly pending until its public APIs are available; avoid independent replacement inspection commands in the applications.
run, execute, render, serve, session, and artifact catalogs are not
CommandKit-owned commands in this proposal. Recommend run for a new application's
principal execution operation, but retain application ownership of that choice.
4. Configuration scopes
Recommendation: use --scope NAME on configuration operations, with these
standard identifiers:
app: the application's effective configuration, including defaults, imports, the selected overlay, explicit overrides, and recorded domain resolution. Each application's configuration schema remains its own.promptkit: the captured configuration of the PromptKit engine assembled from that application's selected inputs and registered assets, when integrated.all: a selector available toconfig validateandconfig show, meaning every registered scope. It is not itself a configuration scope or a merged document.
show, sources, explain, and diff default to app. validate defaults to
all: a bare validation command should establish that the configured application
and its integrated framework definitions are valid. --scope app provides the
explicit narrower check without constructing PromptKit. A non-PromptKit
application's all selection consists only of app. show --scope all is an
explicit request for a YAML stream of all selected scopes. sources, explain,
and diff continue to select one scope and reject all in this proposal.
The differing default for validation is intentional: routine inspection starts with the application configuration, while general validation performs a complete configuration check. An operator can request the combined inspection explicitly. Help must state these defaults prominently.
config scopes lists scope ID, description, and whether it is the default for
inspection. It uses registration metadata, so it works when files are missing or
configuration is invalid. List app first, promptkit second when registered,
then additional scopes sorted by ID; use this same order for show --scope all.
It accepts --format text|yaml and no loading selectors. Unsupported scope names
are usage errors, with the
available names in the diagnostic; there is no fallback to app.
Scopes use stable semantic names rather than executable names such as notarius.
Applications may register additional independently meaningful configuration scopes
through the same complete contract. A scope must supply an effective snapshot,
sources, explanations, and comparison support; a custom formatted report does not
qualify as a scope. Scope dependencies must be explicit, and one invocation must
reuse already loaded dependencies rather than reread them unnecessarily.
Alternatives:
- Default every operation to
app, requiringvalidate --scope all. Very uniform syntax, but easy to mistake application-only validation for validation of all definitions needed by the application. - Separate paths, such as
config promptkit show. Makes the scope visible in the command path; produces duplicated operation trees and complicates adding scopes. This remains a viable alternative if scope flags feel too indirect. - A scope-keyed wrapper for
show --scope all. Scope identity survives parsing in one document, but requires additional presentation work and indentation of the native reports. The accepted initial approach keeps separate native YAML documents; its output and ordering contract are defined below. - Permit partial scopes with different operations. Accommodates legacy
implementations, but makes
configbehave differently across applications. Adapt or improve the inspection provider instead.
5. Shared inputs and command syntax
Configuration selection and overrides
Recommendation: every loading configuration operation accepts --config PATH,
--overlay NAME, and --scope NAME. These have the same meanings for validation,
display, source inventory, explanation, and comparison.
--config selects the application root configuration even for promptkit scope;
the application constructs the selected framework inputs from that root and its
other explicit selectors. A supplied empty path is a usage error. A supplied
missing or invalid file is a load failure and never triggers discovery fallback.
Relative CLI paths use the invocation working directory. File-relative resources
retain the owning library's source-context semantics.
When --config is omitted, use the application's registered discovery policy
identically across these commands and ordinary execution. CommandKit must not
hard-code a second search policy. Discovery order, environment-variable support,
default-only operation, and packaging paths must be visible in help. This proposes
a uniform selection interface; a universal filesystem discovery convention is a
separate decision because the applications have different deployment layouts.
--overlay always selects a configuration overlay, never a PromptKit execution
profile. Follow ConfigKit's naming and resolution semantics: omission, an empty
name, and the exact name default select the base; other names select one overlay.
An overlay name is not a file path. Replace --config-overlay with this standard
spelling rather than silently maintaining two conventions.
Applications register domain context selectors, such as Narratio's --campaign
and --campaign-file, and typed configuration overrides. CommandKit owns their
registration, parsing, help exposure, and propagation; applications own their
field mappings and domain checks. A selector must be offered on each operation
where it is meaningful, with the same meaning. Invalid combinations fail before
loading. Selectors that do not affect a chosen scope must be rejected rather than
silently ignored. Registration cannot shadow a standard flag.
Preserve explicit flag presence. Omission, an empty string, false, zero, and null are not interchangeable. Send configuration changes through ConfigKit's override records and resolution mechanisms so effective output and provenance agree. Execution-only controls such as resume, output directories for generated artifacts, or lane execution do not belong on generic configuration commands.
Alternatives:
- Require
--configon every operation. Simple and explicit, but makes inspection use a different entry path from normal execution and limits defaults-only applications. - Prescribe one global discovery sequence now. Could improve consistency, but expands this command proposal into deployment and configuration discovery policy. Evaluate that separately rather than preserving current differences by assumption or inventing paths without deployment evidence.
- Only expose root, overlay, and scope flags. Keeps the surface small but cannot inspect Narratio's resolved campaign contributions or explicit settings used by execution. A controlled registration mechanism is more effective.
- Offer a generic
--set path=value. Convenient for advanced operators, but requires a new value syntax and typing contract and makes overrides harder to discover. Defer until typed, documented flags prove insufficient.
Flag placement and repeated flags
Recommendation: put the complete command path before its options; allow flags and operands to be interspersed within the selected leaf command. For example:
app config show --config ./config.yml --scope promptkit
app promptkit show profile team.summary --config ./config.yml
app promptkit show profile --config ./config.yml team.summary
Standard flags use --long-name; -h is the standard help shorthand. Accept
--name value and --name=value; boolean false uses --name=false. -- ends
option parsing and makes subsequent tokens operands. Standard configuration
operations reject positional operands; promptkit list accepts a collection name,
and promptkit show accepts a resource name and its one documented ID. Flags may
appear before or after these operands. help accepts command-path operands.
Singleton flags reject repeated occurrences, including mixed --name value and
--name=value spellings. Repeatable source or domain flags combine in documented
order. Do not infer singleton presence from its parsed value. Unknown flags are
usage errors. New names use hyphens rather than underscores.
This is the contract for CommandKit-owned commands. Recommend the same grammar for application commands and supply reusable parsing machinery, while leaving their operand counts and domain rules application-owned.
Alternatives:
- Verb first, such as
app show config. Considered and not selected. It groups operations by action but spreads one namespace across root verb families and requires ownership at individual routes. Namespace/resource first gives each standard family a clear ownership boundary. - Require flags before all operands. Fits Go's
flagparser directly but retains the ordering burden the user wants to remove. - Allow common flags before or between command-path components. Familiar in some command frameworks; increases command routing complexity. A single documented position after the complete path is sufficient initially.
- Let the last singleton occurrence win. Convenient for argument assembly, but conceals conflicting operator instructions. Applications already have useful repeatable inputs; declare those explicitly instead.
Go's standard flag parser
stops at the first operand; pflag
supports interspersed options. The accepted backend decision
selects Cobra with pflag for the initial implementation. Any required adaptation must use
registered flag metadata, honor --, and correctly handle values beginning with
-; do not replicate Notarius's hand-maintained flag-name lists in every consumer.
6. Configuration command contracts
config validate
Recommendation: validate all selected scopes completely through their owning loaders. Include acquisition, composition, defaults, typed decoding, recorded resolution, and domain configuration checks. PromptKit scope constructs the engine and validates all effective definitions, including unused ones. Application scope includes configured references and catalog relationships that can be checked without executing a request.
Validation does not execute a pipeline, prepare request-specific prompts, read
request inputs, contact a provider, or create runtime state. Do not add generic
pipeline or lane selectors to this operation. An application's operational
preflight, such as session validate, can perform additional context-dependent
checks outside the standard suite.
Default text output is one concise success line identifying the validated scopes.
--format yaml returns a structured success report containing the scope IDs.
Warnings go to stderr and do not alone make configuration invalid. Failure emits
no success report and returns status 1. Complete each scope's dependency loading
once; stop on a fatal loading failure rather than promising a report of every
possible defect across invalid inputs.
Credential requirements follow the genuine configuration lifecycle: ConfigKit loads can resolve typed secret references and therefore require the referenced environment values, whereas PromptKit inspection does not itself resolve provider credentials. Describe these requirements in scope help. Offline means no provider or remote execution; it does not promise credential-independent loading. Existing explicit credential-file bootstrap may run as part of acquisition, with its safe report on stderr; it must not create durable application runtime state.
Alternatives:
- Only check decoding/schema validity. Useful as a distinct lower-level operation, but too weak for an unqualified application validation command.
- Validate only definitions selected by a planned run. Audita provides useful evidence for selected-profile checks, but a generic configuration command should find invalid configured definitions before a later selection reaches them.
- Silent success. Scriptorium demonstrates this and scripts can use the exit status. A short scope summary is clearer for interactive use; structured output supports automation without parsing prose.
- Skip secret resolution by default. Valuable for inspection without runtime credentials, but requires an explicit upstream ConfigKit lifecycle design. Do not simulate credentials or bypass native validation to deliver it.
config show
Recommendation: show the complete final effective configuration for the selected
scope, or each registered scope when --scope all is supplied.
Use the captured inspection snapshot, including recorded resolution, rather than
raw file text, an application-selected subset, or a fresh marshal of mutable
runtime structs. Loading necessarily performs the validation required to obtain
a usable snapshot. Single-scope display loads only that scope and its dependencies;
all loads every registered scope and reuses their shared dependencies.
Output is native redacted YAML without a status line. A single-scope report has no heading or document marker added by CommandKit. Preserve ConfigKit's distinction between null, empty containers, and absent values, its stable mapping order, and sequence order. This is an inspection document, not a guaranteed reloadable configuration file.
Accepted: --scope all writes the application configuration followed by the
PromptKit configuration when registered, then any additional scopes in the order
reported by config scopes. Each scope is a separate document, introduced by
--- and a # scope: NAME comment. CommandKit adds those separators and labels
around the native writer output without merging mappings or serializing runtime
structs. Even a one-scope all report uses this labeled document form.
---
# scope: app
server:
addr: localhost:8080
---
# scope: promptkit
profiles: {}
The stream provides a complete inspection in one invocation, preserves native rendering and redaction, and avoids collisions between scopes' mapping keys. Its tradeoffs are larger output and the need for automated consumers to decode multiple documents. Comments are human labels, not parsed metadata; scope identity for automated consumers follows the documented order. This is suitable YAML for automation, but it is not one configuration document or a reloadable combined file.
Load and validate all selected scopes before starting output. A scope load failure
returns 1 without a partial successful report. Writer failures can still leave
partial output. A broken PromptKit configuration can therefore prevent --scope all
from displaying otherwise usable application settings; --scope app remains
available for that narrower inspection.
config show has one initial report format, YAML, and does not need a --format
selector. JSON is deferred without a delivery commitment. The combined display
uses existing native writers and does not require a new structured effective
snapshot API solely for formatting.
Alternatives:
- A scope-keyed single YAML document. Scope names become machine-readable keys, but the renderer must wrap and indent each native document. This could replace comment/order identification if consumers need scope identity in parsed data.
- Emit each scope as soon as it loads. Reduces waiting before output, but a later load failure would leave an incomplete combined report. Prefer loading all scopes first.
- Raw composed input. Shows directives and authored values, but omits defaults and resolution and therefore answers a different question.
- Application-defined compact views. Audita's selected-profile summary is useful domain reporting, but does not replace a complete effective snapshot.
- A reloadable export command. Potential future feature with a different contract; do not imply that inspection output supports round trips.
config sources
Recommendation: list the acquired and synthetic input sources for the selected scope in the owning library's recorded order. Preserve every source's ID, label, path when available, layer, kind, underlay index, and import parent location. Do not sort sources alphabetically or flatten them into one winning file per field. Composite and inheritance provenance belongs to explanation records.
Default text output is a readable table; --format yaml returns the common source
report with an ordered sources sequence. Defaults-only loading may legitimately
produce an empty sequence. Defaults and domain derivations are not fabricated file
sources. Both application and PromptKit scopes use the same complete record
shape, replacing Scriptorium's differing source projections.
Alternatives:
- Field ownership under
sources. Narratio's report is useful but answers why a field has its value. Useconfig explainfor that task. - Only list filenames. Short, but loses underlays, non-file sources, import relationships, and CLI contributions that help diagnose configuration.
- YAML by default. Suitable for automation and consistent with native configuration reports. Prefer a table for interactive discovery and an explicit YAML format for structured consumption. Scriptorium's current JSON default is evidence to evaluate, not a format requirement for CommandKit.
config explain
Recommendation: require --path POINTER; an explicitly supplied empty string
selects the root. Use ConfigKit JSON Pointer semantics, including ~0 and ~1
escaping, mapping keys containing dots, and effective or historical paths. Omitted
path and malformed pointer are usage errors; a well-formed unknown path is a
command failure. Do not impose additional canonical-spelling restrictions beyond
the owning library's supported path contract without a demonstrated need.
Default text output delegates to the native safe explanation writer. It includes the effective value and presence, effective origin, composite source locations, inheritance, chronological before/after values, resolution reasons and dependencies, and safe secret-reference metadata when available. Historical deleted paths remain explainable. Values receive native historical redaction.
--format yaml projects the native structured explanation into CommandKit's
structured report. Keep value snapshots as records with present, redacted, and
yaml fields, matching the upstream representation rather than inventing a typed
value model for historical values. The yaml field holds native snapshot text
as a string rather than decoding it into a new value. History remains chronological.
app config explain --path /server/addr
app config explain --scope promptkit --path '/profiles/team~1summary/model'
app config explain --path ''
Alternatives:
- One positional pointer. Notarius demonstrates this and it is concise, but a named flag makes the required empty root explicit and allows a future distinct operand without ambiguity. Either convention could work if used everywhere.
- Value-free provenance only. Audita and Narratio demonstrate useful compact reports; native redacted history gives a more complete explanation. A future summary format could omit values without changing this command's meaning.
explain --alllisting every field. Useful for audits but potentially very large and expensive. Root explanation provides a starting point; defer exhaustive field-origin reporting until its traversal and summary contract are designed.
config diff
Recommendation: compare two independently resolved effective configurations in
the same scope. The ordinary selectors define the left side. At least one of
--against-config PATH or --against-overlay NAME explicitly defines the right
side; together they can change both root and overlay. Unspecified selectors are
copied from the resolved left selection, rather than running discovery again.
An omitted right overlay inherits the left overlay; --against-overlay default
explicitly selects the base. Domain context selectors and typed overrides apply
identically to both sides in this version.
app config diff --config ./config.yml --overlay staging --against-overlay production
app config diff --config ./old.yml --against-config ./new.yml --against-overlay default
Both sides must load and validate. Compare semantic effective values, including
defaults and recorded domain resolution, rather than YAML formatting, source IDs,
file order, or overlay names. Recursively compare mappings; keep sequences atomic,
preserving their order. Distinguish absence, null, empty mappings, and empty
sequences. Sort differences by JSON Pointer and report added, removed, and
changed with safe before/after snapshots. Root or container replacement must not
produce redundant descendant differences.
Default text output is a readable change report, or no differences when
appropriate. --format yaml supplies a structured report with selection metadata,
an ordered differences sequence, and explicit information about redacted comparison
gaps. Successful comparison returns 0 whether differences exist or not.
Compare only values that the owning library makes available for inspection.
Secret bytes must never be fetched to improve a comparison. A masked value is
not an ordinary literal string and identical masks do not prove secret equality.
Apply the union of both sides' sensitivity classifications to before/after
values so a value classified on either side cannot leak from the other report.
When sensitivity prevents comparison, identify the path as not comparable; do
not print no differences as an unqualified equality claim. This needs upstream
structured sensitivity metadata, not string matching on [REDACTED].
Alternatives:
- Two positional overlay names. Narratio demonstrates a simple interface, but it cannot also compare different root files without another syntax.
- Fully separate left/right domain selector sets. More flexible for campaign comparisons; adds substantial flag duplication. Shared context covers the existing overlay use case. Add independent contexts later if needed.
- Diff rendered YAML lines. Easy to implement, but formatting changes obscure meaningful changes and identical masks conceal comparison gaps.
- Return a special nonzero status for differences. Useful for CI assertions, but makes a successful inspection look like a failed command. Consider an explicit future check mode; keep ordinary comparison successful.
7. PromptKit definition inspection
Recommendation: optional definition commands use the same application root,
overlay, and registered framework source selectors as config --scope promptkit.
They always target PromptKit, so they do not accept --scope. They load the engine
once and use its captured definitions without provider calls, capacity admission,
request preparation, artifact loading, or credential lookup by PromptKit.
promptkit list prompts reports available (ID, version) pairs sorted by ID then
version; promptkit list profiles reports exact profile IDs sorted lexically.
IDs are identifiers, not filesystem paths, and prompt versions need not be semantic
versions.
promptkit show prompt ID uses PromptKit's existing version-selection rules:
omission of --prompt-version works only when that ID has one available version.
Show the definition ID, selected
version, opaque equality hash, declared default profile, declared inputs, and
output contract. It does not resolve the declared default profile or render
request-specific message templates. A command to show fully rendered requests
would need inputs and belongs to application preparation such as render.
promptkit show profile ID reports the backend-completed named execution target:
profile ID, backend, endpoint, model, configured provider controls, credential
reference name,
and whether a direct credential will be required. Do not suggest that a named
profile is the profile selected by every request, that zero indicates a provider
default, or that credential references establish credential availability.
All four commands default to readable text and accept --format yaml. Report
fields and their structured YAML shapes are defined by CommandKit; arbitrary
public Go structs are not an output contract. Unknown IDs or unavailable versions
return 1; missing
or empty required IDs and invalid argument counts return 2.
Provider parameter redaction belongs upstream in PromptKit. Notarius's curated
profile report is useful evidence, while Scriptorium's profile output does not
share configuration inspection's redaction contract. Establish a supported safe
profile reporting API before adding arbitrary provider parameters to the standard
report. Do not duplicate credential-name heuristics in CommandKit or assume that
raw InspectProfile results have native configuration redaction.
Alternatives:
- Inspect definitions only through
config show/explain. Adequate for source presence and history, but does not clearly expose backend-completed targets or catalog discovery. - Show raw prompt message content by default. Can help authors, but the current public prompt inspection reports metadata, and request rendering is a different operation. Add content inspection deliberately upstream if needed.
- List definitions by parsing effective YAML in CommandKit. Avoids new APIs, but takes ownership of PromptKit's schema and version model. Public catalog methods are the better boundary.
8. Help and version
Help
Recommendation: app, app help, app --help, and app -h print root help
and succeed. A family invoked without an operation, such as app config, prints
family help and succeeds. app help config explain and
app config explain --help print the same leaf help with status 0.
Help never acquires configuration, constructs PromptKit, resolves credentials, or allocates runtime state. Generate it from registered commands and flags, including application-owned commands, available scopes, selector applicability, format choices, and examples. An unknown command path is a usage error. A help request bypasses missing required operands; it does not make an otherwise unknown command or unknown flag valid.
Alternatives:
- Only root help. Notarius's current leaf help errors demonstrate why this is insufficient for an operator trying to discover arguments.
- Error on a bare root or family. Narratio and Scriptorium do this in some cases. Printing help successfully makes exploration consistent; an explicitly malformed leaf invocation still fails.
- Maintain separate help strings. Straightforward, but they drift from parsing definitions. Registration should be the shared source.
Version
Recommendation: app version prints app VERSION and a newline. Standardize
app --version as a root-only shortcut, rather than retaining it merely as a
Notarius compatibility alias. Neither form loads configuration. Recommend
version --format yaml with application name/version, optional revision and build
time, and the CommandKit standard revision implemented by the binary.
Applications supply build identity, with tagged Go build information usable when
available. An unversioned development build reports dev; unknown revision or
build time stays absent rather than being invented. The CLI standard revision is
distinct from application version and from the CommandKit module version. It
identifies observable command/report contracts, not application artifact formats.
Alternatives:
- Only the command or only the flag. Either could be consistent; supporting both familiar discovery paths costs little and gives them one implementation.
- Emit all build and dependency information by default. Helpful for debugging, but unnecessarily verbose and unstable for a simple identity check. Structured output should expose the selected fields.
- Use the application version as the sole compatibility signal. Insufficient to distinguish independent CLI-standard and application-artifact changes.
9. Output, diagnostics, and failures
Recommendation: successful reports go to stdout; warnings, loading diagnostics, bootstrap reports, and errors go to stderr. Emit each diagnostic once, retain library source coordinates and source-ID legends, and use library-safe messages. No progress messages or banners may enter report output.
| Operation | Default | Other initial format |
|---|---|---|
help |
text | none |
version |
text | YAML |
config scopes, validate, sources, explain, diff |
text | YAML |
config show (one scope or all) |
YAML | none |
promptkit list/show |
text | YAML |
Format names are lowercase. Unsupported formats are usage errors. Use --format
consistently for commands that offer text and YAML; do not add a selector to
YAML-only config show. Structured reports other than the native config show
documents use a defined operation-specific schema and the applicable scope or
selection metadata. Define their exact YAML field shapes before implementation;
do not make native explanation prose or Go struct field spelling a machine
contract. Structured YAML reports contain one document with a trailing newline;
config show --scope all deliberately uses a multi-document stream.
Accepted: defer JSON output for the initial implementation across the suite. YAML is suitable for automated consumption, and the native inspection writers already preserve its value semantics. There are no initial JSON format flags, JSON-specific report schemas, or JSON-driven upstream API requirements. Adding JSON is optional future work only if a concrete need emerges; it may never be needed. JSON Pointer paths remain supported independently of output format.
Inspection formats use upstream secret classification; there is no unredacted output option in this standard. Redaction does not classify every sensitive prompt or arbitrary application value. The proposal preserves library confidentiality semantics rather than promising that every report is safe for public disclosure.
Default to stdout and shell redirection; do not initially add --out or --output
to standard reports. Ordinary writer failures may leave partial output and return
1; consumers must check status before decoding. This avoids adding filesystem
creation, replacement, and retention behavior to inspection.
| Status | CommandKit-owned command meaning |
|---|---|
0 |
Successful operation, including help, valid configuration, or comparison that found differences. |
1 |
Loading, validation, lookup, explanation, comparison, cancellation, diagnostics delivery, or output failed after valid syntax. |
2 |
Unknown command/flag/scope, invalid format, missing or malformed argument, repeated singleton, or invalid flag combination. |
Applications retain domain result statuses for their own commands. CommandKit must
not globally reinterpret Scriptorium's execution validation status or Notarius's
artifact rejection semantics. Common usage errors can use the shared status 2,
but business outcomes remain explicit application mappings.
Alternatives:
- Structured YAML everywhere by default. Works for automation, but text help, tables, and native explanations are useful for interactive inspection. Offer YAML explicitly where the default is text.
- JSON as an additional output format. Deferred by the accepted decision. It would add serializers, report contracts, and possible representation issues without a demonstrated benefit over the initial YAML reports.
- An output-file flag on every command. Convenient but duplicates shell redirection and introduces file semantics. Add it later if concrete consumers need behavior beyond redirection.
- One nonzero status for every failure. Narratio and Scriptorium demonstrate simplicity, but distinguishing malformed invocation from a failed valid command is useful and already demonstrated by Notarius.
- One exit policy for all application execution outcomes. Would exceed the ownership boundary. Coordinate changes to owned commands without claiming domain statuses for unrelated commands.
10. Implementation dependencies and delivery
Recommendation: implement the common command behavior against narrow inspection
providers, with ConfigKit and PromptKit retaining ownership of configuration
semantics. Publish upstream API improvements before adopting them in CommandKit
and the applications. Do not import sibling internal packages or turn source
checkout proximity into an architectural dependency.
Existing public APIs already support native YAML, native explanation text, structured explanation records, source records, and named definition inspection. They do not yet provide everything recommended here:
| Needed capability | Implementation work before the complete standard can ship |
|---|---|
| Semantic diff | A safe structured effective snapshot from ConfigKit, preserving value semantics and per-path sensitivity; expose it through PromptKit inspection. This serves comparison rather than JSON output. |
| Combined configuration display | Compose native writers as labeled separate YAML documents after all selected scopes load; no new snapshot API is needed just to concatenate these reports. |
| Prompt/profile catalogs | Public PromptKit methods enumerating captured IDs and prompt versions without reparsing source YAML. |
| Safe rich profile reports | PromptKit-owned redacted inspection data for provider parameters. |
| Narratio's final application snapshot | Adapt its configuration boundary so campaign-derived effective values and provenance are recorded in the inspected result; avoid an unrelated typed-struct dump. |
| Full cross-scope validation | Application integration registering dependencies and offline configuration checks, with clear credential requirements. |
Suggested sequence:
- Resolve the remaining proposed command contracts and alternatives, preserving the accepted planning decisions above.
- Define the structured YAML report schemas and minimal upstream inspection additions. Record accepted ownership decisions in durable architecture documentation.
- Implement core dispatch, syntax, help, version, scopes, validation, native
display (including
show --scope all), source inventory, and explanation using existing APIs. - Integrate Scriptorium and Notarius to exercise both scopes and remove duplicated handlers. Partial adoption remains development work, not conformance to the complete proposed standard.
- Add safe semantic comparison and the optional complete PromptKit definition suite once the upstream APIs are released.
- Integrate Audita and Narratio, moving domain reports outside reserved families and aligning their standard operations. Update consumer adapters and docs for the intentional breaking releases.
A complete core adopter exposes every required core command for every registered scope. A complete PromptKit-suite adopter additionally exposes all four definition commands. Do not ship placeholder commands, claim unsupported formats, or silently omit required operations.
Later implementation validation should exercise genuine contract risks: help with
invalid configuration, leaf flag placement and --, explicit empty/false/zero
overrides, source order, historical paths, independent diff selections, sensitivity
changes between sides, multi-document scope order, all-scope load failures before
output, warning delivery and failed writers, and Narratio's subprocess consumers.
Breaking adoption replaces divergent spellings and behavior. Compatibility aliases are not required; introduce one only for a concrete transition need with an explicit retirement decision. Application execution commands outside the reserved suite remain independently owned, even when their implementations use CommandKit helpers.
Alternatives:
- Extract one existing application's command family unchanged. Fast, but carries its omissions and incidental decisions into the standard. Use existing implementations to inform and exercise the selected contract instead.
- Build one universal runtime/application framework. Could absorb more code, but would expand ownership into lifecycle and domain execution without improving the demonstrated inspection boundary.
- Implement missing upstream semantics inside CommandKit. Avoids release coordination, but duplicates configuration projection, redaction, and definition knowledge. Those responsibilities should remain with the libraries that own them.
11. Explicitly deferred commands
Recommendation: defer configuration mutation (set, edit, init, migrate),
reloadable export, credential diagnostics, live provider probes, completion
installation, exhaustive field-origin inventories, request preparation, and domain
artifact catalogs from this standard. Their semantics are not established by the
repeated inspection code under review. Add a shared command when its operator
purpose and ownership can be specified as clearly as the suite above.
JSON output is also deferred, with no assumption that it will eventually be added.
Alternatives:
- Add a generic
doctorcommand now. Attractive for troubleshooting, but mixes offline configuration, credentials, network health, and domain resources. Specify those checks before choosing a shared name and success meaning. - Standardize
runin this first suite. Could further improve vocabulary, but requires comparing execution semantics and their consumer contracts. A recommended verb is useful now; reserving its complete contract is separate work.