CLI Usage#
This document describes how to invoke scafctl from the command line. The CLI follows a kubectl-style structure where verbs, kinds, and names are explicit and positional.
The general pattern is:
scafctl <verb> <kind> <name[@version(or constraint)]> [flags]<verb>describes what you want to do<kind>identifies the type of object<name>identifies the object@versionis optional and resolved via the catalog ( or constraint)
Implementation Status#
| Command | Status | Notes |
|---|---|---|
run solution | ✅ Implemented | Requires workflow (errors if no workflow defined; use run resolver for resolver-only) |
run resolver | ✅ Implemented | Resolver-only execution for debugging and inspection |
render solution | ✅ Implemented | Includes action-graph and snapshot modes |
get solution/provider/resolver | ✅ Implemented | |
explain <kind> | ✅ Implemented | Schema docs for resource kinds (provider, action, resolver) |
inspect solution [--usage] | ✅ Implemented | Instance structure; --usage for the user-facing usage view |
config * | ✅ Implemented | view, get, set, unset, add-catalog, remove-catalog, use-catalog, init, schema, validate |
get snapshot | ✅ Implemented | |
diff solution | ✅ Implemented | Structural comparison of two solution files |
secrets * | ✅ Implemented | list, get, set, delete, exists, export, import, rotate |
auth * | ✅ Implemented | login, logout, status, token |
resolver graph | ❌ Removed | Use run resolver --graph instead |
package solution | ✅ Implemented | Catalog feature |
catalog list/inspect/delete/prune | ✅ Implemented | Catalog management |
catalog save/load | ✅ Implemented | Offline distribution |
eval cel | ✅ Implemented | Evaluate CEL expressions from CLI |
eval template | ✅ Implemented | Evaluate Go templates from CLI |
eval validate | ✅ Implemented | Validate solution files from CLI |
validate solution | ✅ Implemented | Primary gate: loads a solution and runs lint (errors fail, warnings surface, --strict makes warnings fatal) |
validate resolver | ✅ Implemented | Validates resolvers, then runs lint as a gate (--strict makes lint warnings fatal) |
validate schema | ✅ Implemented | Validate arbitrary data (JSON/YAML, --data - for stdin) against a JSON Schema |
new solution | ✅ Implemented | Scaffold a new solution from template |
lint rules | ✅ Implemented | List all available lint rules |
lint rule | ✅ Implemented | Explain a specific lint rule |
lint | ✅ Implemented | Advisory subset of validate: reports authoring warnings/findings without being the pass/fail gate |
examples list | ✅ Implemented | List available example configurations |
examples get | ✅ Implemented | Get/download an example file |
push solution/plugin | 📋 Planned | Remote catalog feature |
pull solution/plugin | 📋 Planned | Remote catalog feature |
tag solution/plugin | 📋 Planned | Catalog feature |
--catalog flag | 📋 Planned | Catalog feature |
Version constraints (@^1.2) | 📋 Planned | Requires catalog |
Core Concepts#
Kinds#
solutionproviderresolvercatalog(planned)
Names and Versions#
Names identify an object within a kind.
Versions are optional and may be:
- an exact version (
1.0.0) - a constraint (
^1.2,>=1.0 <2.0) (planned - requires catalog) - omitted (default resolution rules apply)
Shell escaping: Complex version constraints with special characters should be quoted:
scafctl run solution "example@>=1.0 <2.0" # planned
scafctl run solution "example@^1.2" # plannedFile Paths vs. Catalog References#
When a command accepts both a positional name argument and a -f/--file flag, the CLI distinguishes catalog references from local file paths:
| Input | Interpretation | Example |
|---|---|---|
| Bare name | Catalog reference | my-app, deploy |
| Versioned name | Catalog reference | my-app@1.0.0, deploy@^1.2 |
| Registry reference | Catalog reference | ghcr.io/org/sol:v1, localhost:5000/sol |
| URL | Remote reference | https://example.com/sol.yaml |
Starts with / or . | Rejected — use -f | /tmp/sol.yaml, ./sol.yaml |
Ends with .yaml, .yml, .json | Rejected — use -f | solution.yaml |
| Path with separators, non-hostname first segment | Rejected — use -f | configs/solution, relative/path/sol |
| Windows path | Rejected — use -f | C:\dir\sol, dir\sol |
To pass a local file path, always use the -f/--file flag:
# Correct: file via -f flag
scafctl run solution -f ./solution.yaml
# Correct: catalog reference as positional arg
scafctl run solution my-app@1.0.0
# Error: local file path as positional arg
scafctl run solution solution.yaml # rejected with helpful errorThis separation keeps the CLI unambiguous — positional arguments are always catalog/registry lookups while -f is always a file path.
Running a Solution#
Execute a solution’s resolvers and perform its workflow actions. The solution must define a workflow section with actions — if no workflow is defined, the command errors and suggests using scafctl run resolver instead.
scafctl run solution exampleRun a specific version:
scafctl run solution example@1.0.0Run with a version constraint:
scafctl run solution example@^1.2Getting a Solution#
Show metadata of the latest example solution:
scafctl get solution exampleShow metadata of version 1.0.0 of the example solution:
scafctl get solution example@1.0.0Listing Resources#
Following kubectl conventions, use singular or plural forms:
# List all solutions in the catalog
scafctl get solutions
# List all providers (built-in + official)
scafctl get providers
# Get a specific solution
scafctl get solution exampleBoth singular and plural forms without a name will list all resources of that kind.
The get providers output includes a source column indicating whether each provider is builtin (compiled in) or official (auto-fetched from the OCI catalog). When filters (--capability, --category) are active, only built-in providers are shown since official providers do not expose capability/category metadata.#
Rendering a Solution#
Render executes resolvers and renders actions but does not perform side effects.
From Catalog (by name)#
scafctl render solution exampleFrom File#
Use -f or --file to specify a file path:
scafctl render solution -f mysolution.yamlFrom stdin:
cat solution1.yaml | scafctl render solution -f -Note: The -f flag is used consistently across commands (run, render, publish) to indicate a file source rather than a catalog lookup.
Render a specific version:
scafctl render solution example@1.0.0Typical uses:
- dry runs
- snapshot testing
- delegating execution to another system
- reviewing generated artifacts
Passing Resolver Parameters#
Resolver parameters are passed using -r or --resolver.
scafctl run solution example -r env=prodMultiple parameters:
scafctl run solution example \
-r env=prod \
-r region=us-east1Parameters participate in normal resolver resolution via the parameter provider.
Key-Value Format#
Resolver parameters (and other similar flags) use a key=value format where:
- Key: Simple string identifier (no spaces or newlines allowed)
- Value: Supports ALL characters including newlines, special characters, quotes, etc.
Basic Usage#
The flag can be repeated for each key-value pair:
scafctl run solution example \
-r someKey=sk_live_abc123 \
-r config='{"nested": "json"}' \
-r script="line1
line2
line3"CSV Support#
New: You can also pass multiple comma-separated key=value pairs in a single flag:
# Multiple pairs in one flag
scafctl run solution example \
-r "env=prod,region=us-east1,region=us-west1"To include commas in values, use quotes:
# Quoted values preserve commas as literal characters
scafctl run solution example \
-r "msg=\"Hello, world\"" \
-r "data='item1,item2,item3'"Escaped quotes are supported within quoted values:
scafctl run solution example \
-r "json=\"{\\\"key\\\":\\\"value\\\"}\""Multiple Values for Same Key#
Values for the same key are automatically combined, whether using CSV or repeated flags:
# Using repeated flags
scafctl run solution example \
-r region=us-east1 \
-r region=us-west1 \
-r region=eu-west1
# Using CSV in one flag
scafctl run solution example \
-r "region=us-east1,region=us-west1,region=eu-west1"
# Combining both approaches
scafctl run solution example \
-r "region=us-east1,region=us-west1" \
-r region=eu-west1
# All three produce: region = [us-east1, us-west1, eu-west1]Usage Patterns#
Separate flags (traditional):
scafctl run solution example \
-r key1=value1 \
-r key2=value2CSV in single flag (convenient for multiple pairs):
scafctl run solution example \
-r "key1=value1,key2=value2"Mixed approach:
scafctl run solution example \
-r "env=prod,region=us-east1" \
-r region=us-west1 \
-r apiKey=secretTechnical Note: The CLI uses StringArrayVarP with custom CSV parsing (via pkg/flags.ParseKeyValueCSV) to avoid Cobra’s built-in CSV issues while still supporting comma-separated values with proper quote handling
URI Scheme Support#
To simplify passing complex data like JSON or YAML without escaping, use URI scheme prefixes:
Supported schemes: json://, yaml://, base64://, http://, https://, file://
# JSON without quote escaping
scafctl run solution example \
-r "config=json://{\"key\":\"value\",\"count\":42}"
# JSON with commas in CSV context
scafctl run solution example \
-r "env=prod,data=json://[1,2,3],region=us-east1"
# YAML configuration
scafctl run solution example \
-r "config=yaml://items: [a, b, c]"
# Base64 encoded data
scafctl run solution example \
-r "token=base64://SGVsbG8sIFdvcmxkIQ=="Important: The scheme prefix is preserved and should be processed by your solution logic.
Validation: Values with URI schemes are automatically validated:
json://- Validated as well-formed JSONyaml://- Validated as well-formed YAMLbase64://- Validated as proper base64 encodingfile://- Verified that file exists and is not a directoryhttp://,https://- Validated as properly formatted URLs
Validation errors are reported immediately with helpful messages.
Parameter File References (@file)#
Load all parameters from a YAML or JSON file using the @ prefix:
# Load from a YAML file
scafctl run resolver -f solution.yaml -r @params.yaml
# Load from a JSON file
scafctl run resolver -f solution.yaml -r @params.json
# Mix file and inline parameters
scafctl run resolver -f solution.yaml -r @defaults.yaml -r env=prodStdin Parameters (@-)#
Read all parameters from stdin as YAML or JSON using @-, following the same convention as curl:
# Pipe JSON from echo
echo '{"env": "prod", "region": "us-east1"}' | scafctl run resolver -f solution.yaml -r @-
# Pipe from a file via cat
cat params.yaml | scafctl run solution example -r @-
# Use as positional argument
echo '{"env": "prod"}' | scafctl run resolver -f solution.yaml @-Per-Key Stdin and File References (key=@- / key=@file)#
Assign raw content from stdin or a file directly to a single key. Unlike standalone @- (which parses YAML/JSON into multiple keys), key=@- reads stdin as a raw string value:
# Pipe raw text into a single parameter
echo hello | scafctl run provider message message=@-
echo hello | scafctl run resolver -f solution.yaml -r message=@-
# Read a file's raw content into a parameter
scafctl run provider http url=https://example.com body=@request.json
scafctl run resolver -f solution.yaml -r config=@defaults.txt
# Mix with other parameter forms
scafctl run resolver -f solution.yaml -r name=Alice body=@template.txtA single trailing newline is trimmed automatically (matching shell
echobehavior).
Restrictions:
@-can only appear once (stdin is consumed on first read) — this applies across both standalone@-andkey=@-@-cannot be combined with-f -(both read from stdin)
Rendering With Parameters#
scafctl render solution example \
-r env=staging \
-r dryRun=trueRender Options#
The render command supports additional modes for debugging and testing:
Execution Snapshots#
Capture resolver execution state for testing and comparison:
# Save snapshot after rendering
scafctl render solution -f solution.yaml --snapshot output.json
# Redact sensitive values
scafctl render solution -f solution.yaml --snapshot output.json --redactSnapshots can be analyzed with dedicated commands:
# Display a saved snapshot
scafctl get snapshot output.json
# Compare two snapshots
scafctl diff snapshot before.json after.jsonWorking With the Catalog#
Status: ✅ Implemented - Local catalog with build, list, inspect, delete, prune, save, and load. Remote push/pull planned for Phase 2.
Run a solution directly from the catalog:
scafctl run solution example@1.7.0Building Artifacts#
Status: ✅ Implemented
Package a solution into the local catalog (analogous to docker build):
Note:
buildis a backward-compatible alias forpackage(e.g.scafctl build solutionstill works).
# Package a solution from file
scafctl package solution -f ./solution.yaml --version 1.0.0
# Package using version from metadata
scafctl package solution -f ./solution.yaml
# Overwrite existing version
scafctl package solution -f ./solution.yaml --version 1.0.0 --forceThe build process validates, resolves dependencies, bundles local files, vendors catalog dependencies, and packages artifacts into the local catalog. See catalog-build-bundling.md for the full bundling design.
Additional build flags:
# Dry-run: show what would be bundled without building
scafctl package solution -f ./solution.yaml --dry-run
# Skip file bundling (legacy single-layer artifact)
scafctl package solution -f ./solution.yaml --no-bundle
# Skip vendoring catalog dependencies
scafctl package solution -f ./solution.yaml --no-vendor
# Set max bundle size
scafctl package solution -f ./solution.yaml --bundle-max-size 100MB
# Re-resolve and update the lock file
scafctl package solution -f ./solution.yaml --update-lockPublishing Artifacts#
Status: 📋 Planned
Push artifacts to a remote catalog (analogous to docker push):
# Push a solution
scafctl push solution my-solution@1.7.0
# Push a plugin
scafctl push plugin aws-provider@1.5.0
# Push to a specific catalog
scafctl push solution my-solution@1.7.0 --catalog=productionPulling Artifacts#
Status: 📋 Planned
Pull artifacts from a remote catalog to local (analogous to docker pull):
# Pull a solution
scafctl pull solution example@1.7.0
# Pull a plugin
scafctl pull plugin aws-provider@1.5.0Inspecting Artifacts#
Status: ✅ Implemented
View artifact metadata, dependencies, and structure:
# Inspect a solution (latest version)
scafctl catalog inspect example
# Inspect specific version
scafctl catalog inspect example@1.7.0
# JSON output
scafctl catalog inspect example -o jsonTagging Artifacts#
Status: 📋 Planned
Create version aliases:
# Tag a solution
scafctl tag solution my-solution@1.2.3 my-solution:latest
# Tag a plugin
scafctl tag plugin aws-provider@1.5.0 aws-provider:stableOffline Distribution#
Status: ✅ Implemented
Export and import artifacts for air-gapped environments (analogous to docker save/load):
# Save a solution (exports latest version by default)
scafctl catalog save my-solution -o solution.tar
# Save specific version
scafctl catalog save my-solution@1.2.3 -o solution.tar
# Load from archive
scafctl catalog load --input solution.tar
# Force overwrite if artifact already exists
scafctl catalog load --input solution.tar --forceThe archive uses OCI Image Layout format, making it compatible with OCI registry tools.
Deleting Artifacts#
Status: ✅ Implemented
Remove an artifact from the local catalog:
# Delete specific version (version required)
scafctl catalog delete example@1.7.0
# Prune orphaned blobs after deletion
scafctl catalog pruneCatalog Resolution#
Status: 📋 Planned
By default, scafctl uses the local filesystem as the default catalog. Use --catalog to target a specific configured catalog:
scafctl run solution example --catalog=internal
scafctl get solutions --catalog=productionInspecting and Explaining Resources#
scafctl separates schema documentation from instance inspection (see
docs/design/information-command-taxonomy.md):
explain <kind>– schema documentation for a resource kind (from struct tags), e.g.explain provider,explain action,explain resolver. Drill into fields withexplain provider.schema.inspect <resource>– structure and metadata of a specific instance (kvx-native), e.g.inspect solution.
Inspect Solution (structure)#
# From file
scafctl inspect solution -f solution.yaml
# From catalog
scafctl inspect solution example
scafctl inspect solution example@1.0.0
# Structured output
scafctl inspect solution -f solution.yaml -o jsonOutputs the solution’s resolvers, actions, parameters, file dependencies, and the inferred run command.
Inspect Solution (usage view)#
Use --usage for the user-facing “how do I run this?” view – a synopsis, the
user-supplied parameters (with types, defaults, and discovered allowed values),
and each runnable action with the exact command to invoke it:
scafctl inspect solution -f solution.yaml --usage
# Structured output for tooling
scafctl inspect solution -f solution.yaml --usage -o jsonAuthors can enrich the view with an optional metadata.usage block:
synopsis– a one-line usage summary (overrides the metadata description)details– long-form prose about how the solution works and when to use itexamples– curateddescription/commandpairs
Along with the auto-projected displayName, tags, links, per-parameter
description/example, and per-action description. When metadata.usage is
absent, the view is generated entirely from parameters and actions.
Explain Schema Kinds#
# Provider descriptor schema
scafctl explain provider
# Drill into a field
scafctl explain provider.schema
# Action / resolver schema
scafctl explain action
scafctl explain resolverOutputs the struct definition, field types, validation rules, and documentation extracted from Go struct tags.
Global Flags#
These flags are available on all commands. Run scafctl options to see them:
| Flag | Short | Description | Status |
|---|---|---|---|
--cwd | -C | Change the working directory before executing the command (similar to git -C) | ✅ Implemented |
--quiet | -q | Suppress non-essential output | ✅ Implemented |
--no-color | Disable colored output | ✅ Implemented | |
--config | Path to config file (default: ~/.scafctl/config.yaml) | ✅ Implemented | |
--log-level | Set log level (none, error, warn, info, debug, trace, or numeric V-level) | ✅ Implemented | |
--debug | -d | Enable debug logging (shorthand for –log-level debug) | ✅ Implemented |
--log-format | Log format: console (default) or json | ✅ Implemented | |
--log-file | Write logs to a file path | ✅ Implemented | |
--catalog | Target a specific configured catalog | 📋 Planned |
Note: The -o/--output flag is available per-command (not global) on commands that support structured output.
Output format support:
get,render,explain,config view: Full support for-oflagrun: Supports-oflag for result outputauth status,secrets list: Support-oflag
Configuration#
scafctl uses a configuration file at ~/.scafctl/config.yaml managed via Viper
. Configuration can also be set via environment variables with the SCAFCTL_ prefix.
Config File Structure#
catalogs:
- name: default
type: filesystem
path: ./
- name: internal
type: oci
url: oci://registry.example.com/scafctl
settings:
defaultCatalog: default
action:
# Default output directory for action file operations
# CLI --output-dir flag overrides this setting
outputDir: "/path/to/output"Config Commands#
View the current configuration:
scafctl config viewGet a specific setting:
scafctl config get settings.defaultCatalogSet a configuration value:
scafctl config set settings.defaultCatalog=internalUnset a configuration value:
scafctl config unset settings.defaultCatalogCatalog Management#
Convenience commands for catalog configuration:
# Add a catalog
scafctl catalog remote add internal --type oci --url oci://registry.example.com/scafctl
# Remove a catalog
scafctl catalog remote remove internal
# Set the default catalog
scafctl catalog remote default internalEnvironment Variables#
All configuration can be overridden via environment variables:
export SCAFCTL_SETTINGS_DEFAULTCATALOG=internal
export SCAFCTL_CONFIG=/path/to/custom/config.yamlManaging Secrets#
Securely manage encrypted secrets for authentication and configuration:
# List all secrets
scafctl secrets list
# List all secrets including internal (auth tokens, etc.)
scafctl secrets list --all
# Get a secret value
scafctl secrets get my-api-key
# Get an internal secret (e.g. auth token metadata)
scafctl secrets get scafctl.auth.entra.metadata --all
# Set a secret (prompts for value)
scafctl secrets set my-api-key
# Set with value directly
scafctl secrets set my-api-key --value="secret-value"
# Delete a secret
scafctl secrets delete my-api-key
# Check if secret exists
scafctl secrets exists my-api-key
# Export secrets (encrypted)
scafctl secrets export -o secrets.enc
# Import secrets
scafctl secrets import -i secrets.enc
# Rotate encryption key
scafctl secrets rotateSecrets are encrypted with AES-256-GCM and stored in platform-specific locations:
- macOS:
~/.local/share/scafctl/secrets/ - Linux:
~/.local/share/scafctl/secrets/ - Windows:
%APPDATA%\scafctl\secrets\
Authentication#
Manage authentication for accessing protected resources:
# Login with an auth handler
scafctl auth login entra
# Check authentication status
scafctl auth status
scafctl auth status entra
# Get a token (for debugging)
scafctl auth token entra --scope "https://graph.microsoft.com/.default"
# Logout
scafctl auth logout entraSupported auth handlers:
entra- Microsoft Entra ID (formerly Azure AD)
Resolver Commands#
Note: The standalone
scafctl resolver graphcommand has been removed. Usescafctl run resolver --graphinstead.
Running Resolvers#
The run resolver command executes resolvers from a solution without running actions. This is designed for debugging and inspecting resolver execution.
# Run all resolvers
scafctl run resolver -f solution.yaml
# Run specific resolvers (with their transitive dependencies)
scafctl run resolver db config -f solution.yaml
# JSON output (resolver values only, no execution metadata)
scafctl run resolver -f solution.yaml -o json
# JSON output with __execution metadata (phases, timing, providers)
scafctl run resolver -f solution.yaml -o json
# Skip transform and validation phases
scafctl run resolver --skip-transform -f solution.yaml
# Dependency graph (ASCII, DOT, Mermaid, or JSON)
scafctl run resolver --graph -f solution.yaml
scafctl run resolver --graph --graph-format=dot -f solution.yaml
# Snapshot execution state
scafctl run resolver --snapshot --snapshot-file=out.json -f solution.yaml
scafctl run resolver --snapshot --snapshot-file=out.json --redact -f solution.yaml
# Interactive TUI for exploring results
scafctl run resolver -f solution.yaml -iAliases: res, resolvers
Help and Discovery#
List available verbs:
scafctl helpList supported kinds for a verb:
scafctl run --helpGet help for a specific kind:
scafctl run solution --helpBecause kinds are registered dynamically, help output always reflects what is available at runtime.
Summary#
The scafctl CLI follows a structured, extensible pattern:
- Verbs describe intent
- Kinds identify object types
- Names and versions identify concrete artifacts
This design enables dynamic extension, clear UX, and long-term scalability without breaking existing commands.
Evaluating Expressions#
The eval command group lets you test CEL expressions, Go templates, and validate solution files without running a full solution.
Evaluate CEL#
# Simple expression
scafctl eval cel "1 + 2"
# With JSON data
scafctl eval cel '_.name == "test"' --data '{"name": "test"}'
# From a data file
scafctl eval cel '_.items.size() > 0' --data-file data.json
# Output as JSON
scafctl eval cel '_.items.filter(i, i.active)' --data-file data.json -o jsonEvaluate Go Template#
# Simple template
scafctl eval template '{{.name}}' --data '{"name": "hello"}'
# Template from file
scafctl eval template --template-file template.txt --data-file data.json
# With output file
scafctl eval template --template-file template.txt --data-file data.json --output result.txtValidate Solution#
# Validate a solution YAML file
scafctl eval validate -f solution.yaml
# Output as JSON
scafctl eval validate -f solution.yaml -o jsonCreating New Solutions#
Scaffold a new solution from a built-in template:
# Interactive — prompts for name, description, providers
scafctl new solution
# With flags
scafctl new solution --name my-solution --description "My new solution" --output my-solution.yaml
# With specific providers
scafctl new solution --name my-solution --providers static,exec,celValidation Gate#
validate is THE gate that decides whether a definition is correct and ready.
It runs lint (which includes a JSON Schema conformance check) and turns findings
into a pass/fail result suitable for CI pipelines and pre-commit checks. lint
is the advisory subset – it reports the same authoring findings but is not the
pass/fail gate on its own.
Validate a Solution#
Loads a solution and runs lint. Lint errors (including schema violations) fail;
lint warnings are surfaced but do not fail unless --strict is passed:
# Validate the auto-discovered solution (errors fail, warnings surface)
scafctl validate solution
# Validate a specific file
scafctl validate solution -f ./my-solution.yaml
# Treat warnings as fatal too
scafctl validate solution -f ./my-solution.yaml --strictValidate Resolvers#
Executes the resolver phases (resolve, transform, validate) and, after they
pass, additionally runs lint as part of the gate. --strict makes lint
warnings fatal:
scafctl validate resolver -f ./my-solution.yaml
scafctl validate resolver -f ./my-solution.yaml --strictValidate Data Against a JSON Schema#
Validates arbitrary data (JSON or YAML) against a JSON Schema. Unlike
validate solution, this does NOT run lint – it checks raw data conformance
only. Pass --data - to read the data from stdin:
# Validate a data file against a schema (both JSON or YAML)
scafctl validate schema --schema schema.json --data data.json
# Read the data from stdin
cat data.yaml | scafctl validate schema --schema schema.json --data -Exit codes: 0 conforms, 2 violates the schema, 3 the schema itself is
invalid, 4 a file was not found.
Exploring Lint Rules#
List Rules#
List all available lint rules with severity, category, and descriptions:
# List all rules
scafctl lint rules
# Output as JSON
scafctl lint rules -o jsonExplain a Rule#
Get a detailed explanation of a specific lint rule:
# Show rule details, examples, and fix guidance
scafctl lint rule <rule-id>
# Output as JSON
scafctl lint rule <rule-id> -o jsonThe former
scafctl lint explain <rule-id>still works as a hidden, deprecated alias forscafctl lint rule <rule-id>.
Browsing Examples#
Discover and download built-in example configurations:
List Examples#
# List all examples
scafctl get examples
# Filter by category
scafctl get examples --category solutions
scafctl get examples --category resolvers
scafctl get examples --category actions
# Output as JSON
scafctl get examples -o json
# Output as YAML
scafctl get examples -o yamlGet an Example#
# Print example to stdout
scafctl get examples resolvers/hello-world.yaml
# Save to file
scafctl get examples resolvers/hello-world.yaml > output.yamlCommand Grammar: Verb-Noun vs Noun-Verb#
Current State of scafctl#
scafctl uses two distinct command grammar patterns:
Verb-Noun (kubectl-style) — the verb is the top-level command, the noun follows:
| Command | Verb | Noun |
|---|---|---|
scafctl run solution | run | solution |
scafctl run resolver | run | resolver |
scafctl get solution | get | solution |
scafctl render solution | render | solution |
scafctl inspect solution | inspect | solution |
scafctl package solution | package | solution |
scafctl new solution | new | solution |
scafctl push solution | push | solution |
scafctl pull solution | pull | solution |
scafctl tag solution | tag | solution |
Noun-Verb — the noun is the top-level command, the verb follows:
| Command | Noun | Verb |
|---|---|---|
scafctl secrets get | secrets | get |
scafctl secrets set | secrets | set |
scafctl secrets list | secrets | list |
scafctl secrets delete | secrets | delete |
scafctl auth login | auth | login |
scafctl auth logout | auth | logout |
scafctl auth status | auth | status |
scafctl config view | config | view |
scafctl config set | config | set |
scafctl config get | config | get |
scafctl catalog list | catalog | list |
scafctl catalog inspect | catalog | inspect |
scafctl get snapshot | get | snapshot |
scafctl diff snapshot | diff | snapshot |
scafctl diff solution | diff | solution |
scafctl lint rules | lint | rules |
scafctl lint rule | lint | rule |
scafctl eval cel | eval | cel |
scafctl eval template | eval | template |
scafctl validate solution | validate | solution |
scafctl validate resolver | validate | resolver |
scafctl validate schema | validate | schema |
scafctl get examples | get | examples |
scafctl cache clean | cache | clean |
scafctl plugins list | plugins | list |
scafctl vendor sync | vendor | sync |
Standalone (no sub-noun or sub-verb):
| Command | Notes |
|---|---|
scafctl version | informational |
scafctl mcp | launches MCP server |
scafctl test | runs solution tests |
What Major CLIs Do#
Most successful CLIs converge on the same hybrid pattern scafctl already uses:
| CLI | Core Domain Objects | Service/Infrastructure | Example |
|---|---|---|---|
| kubectl | verb-noun: get pods, delete svc, apply -f | noun-verb: config use-context, auth can-i | Domain is verb-noun; plumbing is noun-verb |
| docker | verb-noun: run, build, pull, push | noun-verb: network create, volume ls, system prune | Top-level verbs act on images/containers; subsystems are noun-verb |
| git | verb-first: clone, commit, push, pull | noun-verb: remote add, branch delete, stash pop | Core workflow is verbs; ancillary resource management is noun-verb |
| gh (GitHub CLI) | verb-noun: pr create, issue list | noun-verb: auth login, config set, secret set | Domain objects verb-noun; infrastructure noun-verb |
| az (Azure CLI) | noun-verb: az vm create, az storage blob upload | noun-verb throughout | Purely noun-verb (resource-group style) |
| gcloud | noun-verb: gcloud compute instances create | noun-verb throughout | Purely noun-verb (resource hierarchy) |
| terraform | verb-first: plan, apply, destroy | — | No sub-resources, single verb layer |
| helm | verb-noun: install, upgrade, rollback | noun-verb: repo add, plugin install | Charts are verb-noun; supporting systems noun-verb |
Observation: The most widely-used developer CLIs (kubectl, docker, git, gh, helm) all use a hybrid model. Only cloud-provider CLIs (az, gcloud) that model deep resource hierarchies go fully noun-verb. Purely verb-noun CLIs (terraform) tend to have a flat, single-resource domain.
Best Practice: The Delineation Rule#
The hybrid pattern is not arbitrary — it follows a clear principle:
Use verb-noun for domain operations on core business objects. Use noun-verb for infrastructure, plumbing, and service management.
The deciding question: “Is this a core workflow action the user came here to do, or is it managing supporting infrastructure?”
| Category | Pattern | Rationale | scafctl examples |
|---|---|---|---|
| Core domain operations | <verb> <kind> | The user thinks in terms of what they want to do: run, get, render, build. The kind is just a target. | run solution, get provider, render solution, package solution, new solution |
| Infrastructure / services | <noun> <action> | The user thinks in terms of which subsystem they need to manage. The subsystem is the anchor; actions within it are secondary. | config set, secrets get, auth login, catalog list, cache clean |
| Standalone utilities | <verb> or <noun> | Single-purpose commands that don’t need a sub-resource. | version, mcp, test |
Why this works:
- Discoverability — Users can type
scafctland immediately see the core verbs (run,get,render) alongside the subsystems (config,secrets,auth). The top-level command list reads like a table-of-contents. - Composability — Verb-noun allows the same verb to apply to multiple kinds (
run solution,run resolver). Noun-verb allows subsystems to have independent, self-documenting action sets. - Scalability — New kinds slot into existing verbs. New subsystem features slot into their noun group. Neither pollutes the other.
- Precedent — kubectl, docker, git, gh, and helm all draw the same line, so users already have muscle memory for the split.
Verdict on scafctl#
The current hybrid structure is correct. No changes needed.
scafctl already follows the established delineation:
- Verb-noun for core domain operations —
run,get,render,explain,build,new,push,pull,tagall act on domain kinds (solution, provider, resolver). - Noun-verb for infrastructure/services —
config,secrets,auth,catalog,snapshot,lint,eval,examples,cache,plugins,vendorare all subsystem groups with their own actions. - Standalone for single-purpose utilities —
version,mcp,test.
This matches how kubectl, docker, git, gh, and helm structure their CLIs. Attempting to “unify” to pure verb-noun or pure noun-verb would:
- Break user expectations from other tools.
- Create awkward commands —
scafctl manage secrets get(verb-noun forced) orscafctl solution run(noun-verb forced for domain ops) reads worse in both cases. - Lose discoverability — a flat verb-only top level hides subsystem structure; a flat noun-only top level hides the core workflow.
The rule of thumb going forward: if adding a new top-level command, ask “Is the user performing a core domain action on a kind, or managing a subsystem?” — verb-noun for the former, noun-verb for the latter.