speckitconstitution
Create or update the project constitution from interactive or provided principle inputs, ensuring all dependent templates stay in sync.
SKILL.md
| Name | speckitconstitution |
| Description | Create or update the project constitution from interactive or provided principle inputs, ensuring all dependent templates stay in sync. |
invowk™
A dynamically extensible, CLI-based command runner similar to just, written in Go.
Features
-
Four Runtime Modes:
- native: Execute commands using the system's default shell (bash, sh, powershell, etc.)
- virtual-sh: Execute commands using the built-in mvdan/sh interpreter with 28 u-root utilities (cat, cp, ls, grep, sort, seq, tar, etc.). Note: virtual-sh is not a sandbox; host binaries run only when explicitly allowed and still execute as native host processes.
- virtual-lua: Execute Lua scripts in an embedded Lua runtime with the shared virtual safety harness. Like virtual-sh, it is not process isolation; explicitly allowed host binaries still execute as native host processes.
- container: Execute commands inside a disposable Docker/Podman container
-
CUE Configuration: Define commands in
invowkfile.cuefiles using CUE - a powerful configuration language with validation -
Cross-Platform: Works on Linux, Windows, and macOS
-
Hierarchical Commands: Use spaces in command names to create subcommand-like hierarchies (e.g.,
invowk cmd test unit) -
Command Dependencies: Commands can require other commands to be discoverable
-
Multiple Command Sources: Discover commands from:
- Current directory
invowkfile.cue(highest priority) - Sibling
*.invowkmodmodules in current directory - Configured includes (module paths from config)
~/.invowk/cmds/(modules only, non-recursive)
- Current directory
-
Transparent Namespace: Commands from different sources use simple names when unique. When command names conflict across sources, use
@<source>prefix or--ivk-fromflag to disambiguate -
Shell Completion: Full tab completion support for bash, zsh, fish, and PowerShell
-
Beautiful CLI: Styled output using Cobra with Lip Gloss styling
-
Interactive TUI Components: Built-in gum-like terminal UI components for creating interactive shell scripts (input, write, choose, confirm, filter, file, table, spin, pager, format, style)
-
Module Dependencies: Modules can import dependencies from remote Git repositories (GitHub, GitLab) with semantic versioning support and lock files for reproducibility
-
Security Auditing: Inspect modules and lock files with
invowk audit, including optional LLM-assisted analysis -
LLM-Assisted Command Authoring: Use
invowk agent cmd promptandinvowk agent cmd createto give agents the current schemas or generate validated custom commands with the same LLM providers asinvowk audit
Installation
Shell Script (Linux/macOS)
curl -fsSL https://raw.githubusercontent.com/invowk/invowk/main/scripts/install.sh | sh
This downloads the latest release, verifies its SHA256 checksum, and installs to ~/.local/bin. Customize with environment variables:
curl -fsSL https://raw.githubusercontent.com/invowk/invowk/main/scripts/install.sh | INSTALL_DIR=/usr/local/bin INVOWK_VERSION=v1.0.0 sh
PowerShell (Windows)
irm https://raw.githubusercontent.com/invowk/invowk/main/scripts/install.ps1 | iex
This downloads the latest release, verifies its SHA256 checksum, installs to %LOCALAPPDATA%\Programs\invowk, and adds it to your User PATH. Customize with environment variables:
# Install a specific version to a custom directory
$env:INSTALL_DIR='C:\tools\invowk'; $env:INVOWK_VERSION='v1.0.0'; irm https://raw.githubusercontent.com/invowk/invowk/main/scripts/install.ps1 | iex
Homebrew (macOS/Linux)
brew install --cask invowk/tap/invowk
WinGet (Windows)
winget install Invowk.Invowk
To upgrade: winget upgrade Invowk.Invowk
Go Install
go install github.com/invowk/invowk@latest
Requires Go 1.26+. The binary is installed to $GOBIN (or $GOPATH/bin).
From Source
git clone https://github.com/invowk/invowk
cd invowk
make build
make install # Installs to $GOPATH/bin
Note: On x86-64 systems, the default build targets the x86-64-v3 microarchitecture (Haswell+ CPUs from 2013+) for optimal performance. For maximum compatibility with older CPUs, use
make build GOAMD64=v1.
Verify Installation
invowk --version
Upgrading
Upgrade using the same method you used to install:
-
Shell script: Re-run the install command (it overwrites the existing binary):
curl -fsSL https://raw.githubusercontent.com/invowk/invowk/main/scripts/install.sh | shPin to a specific version with
INVOWK_VERSION=v1.2.0. -
PowerShell script: Re-run the install command (it overwrites the existing binary):
irm https://raw.githubusercontent.com/invowk/invowk/main/scripts/install.ps1 | iexPin to a specific version with
$env:INVOWK_VERSION='v1.2.0'. -
Homebrew:
brew upgrade invowk -
WinGet:
winget upgrade Invowk.Invowk -
Go install:
go install github.com/invowk/invowk@latest -
From source:
git pull && make build && make install
Platform Support
| Method | Linux | macOS | Windows |
|---|---|---|---|
| Shell script | amd64, arm64 | amd64 (Intel), arm64 (Apple Silicon) | — |
| PowerShell script | — | — | amd64 |
| Homebrew | amd64, arm64 | amd64, arm64 | — |
| WinGet | — | — | amd64 |
| Go install | all | all | all |
| From source | all | all | all |
Verifying Signatures
Release artifacts are signed with Cosign (keyless, via GitHub Actions OIDC). To verify the checksums file:
cosign verify-blob \
--bundle checksums.txt.sigstore.json \
--certificate-identity-regexp='https://github\.com/invowk/invowk/.*' \
--certificate-oidc-issuer='https://token.actions.githubusercontent.com' \
checksums.txt
Then verify individual archives against checksums.txt using sha256sum -c checksums.txt.
Quick Start
- Create an invowkfile in your project directory:
invowk init
- List available commands:
invowk cmd
The list shows all commands grouped by source with allowed runtimes (default marked with *) and supported platforms:
Available Commands
(* = default runtime)
From invowkfile:
hello - Print a greeting [native*, virtual-sh, container] (linux, macos, windows)
- Run a command:
invowk cmd hello
# Output: Hello, World!
- Pass an argument:
invowk cmd hello Alice
# Output: Hello, Alice!
- Use a different runtime:
# Use the virtual-sh runtime (built-in cross-platform shell)
invowk cmd hello --ivk-runtime virtual-sh
# Use the container runtime (requires Docker/Podman, Linux only)
invowk cmd hello --ivk-runtime container
LLM-Assisted Command Authoring
Invowk can provide an agent-ready system prompt for custom command authoring:
invowk agent cmd prompt
invowk agent cmd prompt --format json
It can also ask a configured LLM provider to generate one command, validate the CUE, and patch invowkfile.cue:
# Configure once, then omit LLM flags on create
invowk config set llm.provider codex
invowk agent cmd create 'add a lint command that runs golangci-lint'
# Auto-detect Ollama, cloud credentials, or Claude/Codex/Gemini CLIs
invowk agent cmd create --llm-provider auto 'add a lint command that runs golangci-lint'
# Preview without writing
invowk agent cmd create --llm-provider codex --dry-run 'add a test command'
# Print only the generated command object
invowk agent cmd create --llm-provider claude --print 'add a docs build command'
# Write, then verify the generated command with a dry-run execution plan
invowk agent cmd create --llm-provider codex --verify 'add a release command'
agent cmd create uses the global llm config when present, and the same LLM flags as invowk audit for per-run overrides: --llm-provider, --llm, --llm-url, --llm-model, --llm-api-key, --llm-timeout, and --llm-concurrency. The command retries once with validation feedback when a model returns invalid output, uses structured JSON output with compatible OpenAI API backends, and rejects malformed JSON, invalid CUE, and full cmds arrays. When writing to invowkfile.cue, duplicate command names are rejected unless --replace is set.
When agent cmd create uses an LLM provider, it sends the command-authoring system prompt and schemas, your request, the target path, the current target invowkfile content or missing/empty state, and repair feedback if validation retries. For cloud providers, review your provider's data handling policies before sending private scripts or workspace details.
Invowkfile Format
Invowkfiles are written in CUE format. CUE provides powerful validation, templating, and a clean syntax. Invowkfiles contain commands only; module metadata lives in invowkmod.cue when you build a module. Here's an example:
// Define commands
cmds: [
{
name: "build"
description: "Build the project"
implementations: [
{
// Multi-line scripts use triple quotes
script: {content: """
echo "Building ${PROJECT_NAME}..."
echo "Build complete"
"""}
// Allowed runtimes (first is default). Container runtime requires image or containerfile.
runtimes: [
{name: "native"},
{name: "container", image: "debian:stable-slim"},
]
platforms: [{name: "linux"}, {name: "macos"}, {name: "windows"}]
// Environment variables for this implementation
env: {vars: {PROJECT_NAME: "myproject"}}
}
]
},
// Use spaces in names for subcommand-like behavior
// This creates: invowk cmd test unit
{
name: "test unit"
description: "Run unit tests"
implementations: [
{
script: {content: "go test ./..."}
runtimes: [
{name: "native"},
{name: "virtual-sh", allowed_binaries: ["go"], binary_lookup_mode: "host"},
] // Can run in native or virtual-sh runtime
platforms: [{name: "linux"}, {name: "macos"}, {name: "windows"}]
}
]
},
// Root invowkfiles invoke project scripts from inline content
{
name: "deploy"
description: "Deploy the application"
implementations: [
{
script: {content: "./scripts/deploy.sh"}
runtimes: [{name: "native"}]
platforms: [{name: "linux"}, {name: "macos"}]
}
]
},
// Dependencies: tools (binaries in PATH) and commands (other invowk commands)
{
name: "release"
description: "Create a release"
implementations: [
{
script: {content: "echo 'Creating release...'"}
runtimes: [{name: "native"}]
platforms: [{name: "linux"}, {name: "macos"}]
}
]
depends_on: {
tools: [
{alternatives: ["git"]},
]
cmds: [
{alternatives: ["build"]},
{alternatives: ["test unit"]},
]
}
},
// Run commands in a container (requires image or containerfile)
{
name: "container hello-invowk"
description: "Print a greeting from a container"
implementations: [
{
script: {content: "echo \"Hello, Invowk!\""}
runtimes: [{name: "container", image: "debian:stable-slim"}]
platforms: [{name: "linux"}]
}
]
},
]
Root-Level Settings
Invowkfiles support several root-level settings that apply to all commands:
// Override the default shell for native runtime (optional)
default_shell: "/bin/bash"
// Set a default working directory for all commands (optional)
// Can be absolute or relative to the invowkfile location
workdir: "./src"
// Global environment configuration (optional)
// Applied to all commands; command-level and implementation-level env override these
env: {
files: [".env", ".env.local?"] // Load from .env files ('?' suffix = optional)
vars: {
PROJECT_NAME: "myapp"
}
}
// Global dependencies that apply to all commands (optional)
depends_on: {
tools: [{alternatives: ["git"]}]
}
cmds: [...]
Root-level fields:
| Field | Description |
|---|---|
default_shell | Override the default shell for native runtime (e.g., /bin/bash, pwsh) |
workdir | Default working directory for all commands (overridable per command/implementation) |
env | Global environment config with files (dotenv loading) and vars (key-value pairs) |
depends_on | Global dependencies validated for every command in this invowkfile |
Env Files
Load environment variables from .env files at any scope level:
env: {
// Files are loaded in order; later files override earlier ones
// Suffix with '?' to make a file optional (no error if missing)
files: [".env", ".env.local?", ".env.${ENV}?"]
vars: {
// Direct variables override values from files
APP_NAME: "myapp"
}
}
Working Directory
Control where commands execute using workdir at root, command, or implementation level. Implementation-level overrides command-level, which overrides root-level:
cmds: [
{
name: "test"
workdir: "./packages/api" // Command-level workdir
implementations: [
{
script: {content: "go test ./..."}
runtimes: [{name: "native"}]
platforms: [{name: "linux"}, {name: "macos"}]
workdir: "./packages/api/v2" // Implementation-level override
}
]
}
]
You can also override the working directory at runtime with the --ivk-workdir / -w flag:
invowk cmd test --ivk-workdir=./packages/frontend
Module Metadata (invowkmod.cue)
Modules (directories ending in .invowkmod) use a separate metadata file named invowkmod.cue. It defines the module identifier, version, optional descriptive metadata, and dependencies.
module: "mymodule"
version: "1.0.0"
description: "Reusable build tools"
author: "Acme Build Team <build@example.com>"
license: "MPL-2.0"
repository: "https://github.com/acme/mymodule.invowkmod"
Module Field Format
module (mandatory) — The module identifier:
module: "mymodule" // Simple module name
module: "my.nested.module" // Nested module using dot notation (RDNS style)
Validation rules:
- Must start with a letter (a-z, A-Z)
- Can contain letters and numbers
- Nested modules use dots (
.) as separators - Each segment must start with a letter
Valid examples: mymodule, my.module, my.nested.module, Module1, a.b.c
Invalid examples: .module, module., my..module, my-module, my_module, 1module
version (mandatory) — The module version using semantic versioning:
version: "1.0.0"
version: "2.1.0-alpha.1"
Validation rules:
- Must follow semver format:
MAJOR.MINOR.PATCHwith optional pre-release label - No
vprefix (use"1.0.0", not"v1.0.0") - No build metadata
- No leading zeros on numeric segments
Optional metadata fields:
description: "Reusable build tools"
author: "Acme Build Team <build@example.com>"
license: "MPL-2.0"
repository: "https://github.com/acme/mymodule.invowkmod"
descriptionsummarizes the module's purpose.authoridentifies the maintainer or organization.licenseshould use an SPDX identifier such asMIT,Apache-2.0, orMPL-2.0.repositorymust use an accepted Git URL scheme:https://,git@, orssh://.
How Multi-Source Discovery Works
When you run invowk cmd in a directory, invowk discovers commands from 4 sources in priority order:
- Root invowkfile:
invowkfile.cuein the current directory (highest priority) - Sibling modules: All
*.invowkmoddirectories at the same level (not their dependencies) - Configured includes: Module paths listed in
includesin your config file - User directory:
~/.invowk/cmds/(modules only, non-recursive)
Commands from all sources are aggregated and displayed with their simple names. The transparent namespace system means you don't need module prefixes unless there's a naming conflict.
# Directory structure:
# .
# ├── invowkfile.cue (contains: build, deploy)
# ├── tools.invowkmod/ (contains: lint, deploy)
# └── testing.invowkmod/ (contains: test)
# Run commands with simple names (when unique)
invowk cmd build # Runs build from invowkfile
invowk cmd lint # Runs lint from tools.invowkmod
invowk cmd test # Runs test from testing.invowkmod
# Ambiguous commands require disambiguation (deploy exists in both sources)
invowk cmd @invowkfile deploy # Using @source prefix
invowk cmd @tools deploy # Using @source prefix
invowk cmd --ivk-from invowkfile deploy # Using --ivk-from flag
Command Disambiguation
When a command name exists in multiple sources, invowk requires explicit disambiguation:
Using @source prefix (appears before the command name):
invowk cmd @invowkfile deploy # Run deploy from invowkfile
invowk cmd @tools deploy # Run deploy from tools.invowkmod
invowk cmd @tools.invowkmod deploy # Full name also works
Using --ivk-from / -f flag (must appear after invowk cmd):
invowk cmd --ivk-from invowkfile deploy
invowk cmd -f tools deploy # short alias
If you try to run an ambiguous command without disambiguation, invowk shows an error with available sources:
Error: 'deploy' is ambiguous. Found in:
- @invowkfile: Deploy the application
- @tools: Deploy to staging
Use 'invowk cmd @<source> deploy' or 'invowk cmd --ivk-from <source> deploy'
Explicit Source for Non-Ambiguous Commands
You can always specify a source explicitly, even for unique command names:
invowk cmd @tools lint # Works even though lint is not ambiguous
Benefits of Transparent Namespaces
- Simple by default: Use short command names when possible
- Explicit when needed: Disambiguation syntax is clear and consistent
- Clear provenance: Listing shows source for each command
- Tab completion: Sources provide natural completion boundaries
Command Dependencies and Namespaces
Command dependencies refer to other invowk commands by name. Invowk validates that the referenced commands are discoverable (it does not execute them automatically).
- Same invowkfile: use unqualified names like
buildortest unit - Module commands: use explicit source-qualified refs like
@tools deploy
cmds: [
{
name: "release"
depends_on: {
cmds: [
{alternatives: ["build"]}, // Same-file command
{alternatives: ["test unit"]}, // Same-file nested command
{alternatives: ["@tools deploy"]}, // Command from a module
]
}
}
]
Dependencies
Commands can specify dependencies that must be satisfied before running:
Tool Dependencies
Verify that required binaries are available in PATH. You can specify alternatives with OR semantics (any alternative found satisfies the dependency):
depends_on: {
tools: [
// Simple check - just verify tool is in PATH
{alternatives: ["git"]},
// Multiple alternatives - any one satisfies the dependency
{alternatives: ["podman", "docker"]},
]
}
Tool validation options:
alternatives(required): List of binary names that can satisfy this dependency (OR semantics)
Command Dependencies
Require other invowk commands to be discoverable. Use bare command names for the declaring source, or @source command for another module/source:
depends_on: {
cmds: [
{alternatives: ["clean"]},
{alternatives: ["build"]},
// Multiple alternatives - any one satisfies the dependency
{alternatives: ["test unit", "test integration"]},
]
}
Filepath Dependencies
Check that required files or directories exist with proper permissions. You can specify multiple alternative paths where if any one exists, the dependency is satisfied:
depends_on: {
filepaths: [
// Simple existence check - any of these files satisfies the dependency
{alternatives: ["go.mod", "go.sum", "Gopkg.lock"]},
// Check with read permission - any of these READMEs works
{alternatives: ["README.md", "README", "readme.md"], readable: true},
// Check with write permission
{alternatives: ["output", "dist", "build"], writable: true},
// Check with execute permission
{alternatives: ["scripts/deploy.sh"], executable: true},
// Absolute paths are also supported
{alternatives: ["/etc/app/config.yaml", "./config.yaml"], readable: true},
]
}
Filepath validation options:
alternatives(required): List of file or directory paths (at least one). If any path exists and satisfies the permission requirements, the dependency is considered satisfied.readable(optional): Check if path is readablewritable(optional): Check if path is writableexecutable(optional): Check if path is executable
Permission checks are cross-platform compatible (Linux, macOS, Windows).
When dependencies are not satisfied, invowk displays a styled error message listing all issues at once.
Environment Variable Dependencies
Validate that required environment variables exist in the user's environment before the command runs. This check happens first, before all other dependency checks, ensuring validation against the user's actual environment (not variables that invowk might set via the env construct).
depends_on: {
env_vars: [
// Simple check - just verify the env var exists
{alternatives: [{name: "HOME"}]},
// Multiple alternatives - any one satisfies the dependency
{alternatives: [{name: "AWS_ACCESS_KEY_ID"}, {name: "AWS_PROFILE"}]},
// With regex validation - env var must exist AND match the pattern
{alternatives: [{name: "GO_VERSION", validation: "^[0-9]+\\.[0-9]+\\.[0-9]+$"}]},
]
}
Environment variable validation options:
alternatives(required): List of environment variable checks (OR semantics)name(required): Environment variable name to checkvalidation(optional): Regex pattern the value must match
Key behavior:
- OR semantics: If any alternative exists (and passes validation if specified), the dependency is satisfied
- Early validation: Runs before tools, commands, filepaths, capabilities, and custom checks
- User environment: Validates against the user's actual environment, not variables set by invowk's
envconstruct
Example - AWS credentials check:
cmds: [
{
name: "deploy"
description: "Deploy to AWS"
implementations: [
{
script: {content: """
echo "Deploying with AWS credentials..."
aws s3 sync ./dist s3://my-bucket
"""}
runtimes: [{name: "native"}]
platforms: [{name: "linux"}, {name: "macos"}]
}
]
depends_on: {
env_vars: [
// Require either AWS_ACCESS_KEY_ID or AWS_PROFILE for authentication
{alternatives: [{name: "AWS_ACCESS_KEY_ID"}, {name: "AWS_PROFILE"}]},
]
tools: [
{alternatives: ["aws"]},
]
}
}
]
Example - Version format validation:
depends_on: {
env_vars: [
// GO_VERSION must be set and match semver format (e.g., "1.25.0")
{alternatives: [{name: "GO_VERSION", validation: "^[0-9]+\\.[0-9]+\\.[0-9]+$"}]},
]
}
When environment variable dependencies are not satisfied, invowk displays a styled error message:
✗ Dependencies not satisfied
Command 'deploy' has unmet dependencies:
Missing or Invalid Environment Variables:
• AWS_ACCESS_KEY_ID - not set in environment
Install the missing tools and try again, or update your invowkfile to remove unnecessary dependencies.
Capability Dependencies
Verify that required system capabilities are available. Invowk supports checking for network connectivity, container engine availability, and interactive TTY:
depends_on: {
capabilities: [
// Check for internet connectivity
{alternatives: ["internet"]},
// Check that Docker or Podman is installed and responding
{alternatives: ["containers"]},
// Check for interactive TTY
{alternatives: ["tty"]},
// OR semantics: either internet or LAN connectivity
{alternatives: ["internet", "local-area-network"]},
]
}
Available capabilities:
| Capability | Description |
|---|---|
local-area-network | Checks for LAN connectivity |
internet | Checks for internet connectivity |
containers | Checks that Docker or Podman is installed and responding |
tty | Checks that invowk is running in an interactive TTY |
Custom Check Dependencies
Write custom validation scripts for requirements that don't fit built-in dependency types. Check tool versions, configuration validity, or any other custom requirement:
depends_on: {
custom_checks: [
// Simple exit code check (passes if script exits with 0)
{
name: "docker-running"
script: {content: "docker info > /dev/null 2>&1"}
},
// Exit code + output validation
{
name: "go-version"
script: {content: "go version"}
expected_code: 0
expected_output: "go1\\.2[6-9]" // Must be Go 1.26+
},
// Alternatives (OR semantics)
{
alternatives: [
{
name: "python-3.11"
script: {content: "python3 --version"}
expected_output: "^Python 3\\.11"
},
{
name: "python-3.12"
script: {content: "python3 --version"}
expected_output: "^Python 3\\.12"
},
]
},
]
}
Custom check properties:
| Property | Required | Description |
|---|---|---|
name | Yes | Identifier for error messages |
script.content | Yes | Inline script content to execute for validation |
script.file | Module only | Script file contained in the source invowkmod |
expected_code | No | Expected exit code (default: 0) |
expected_output | No | Regex pattern to match against script output |
Command Flags
Commands can define flags that are passed at runtime. Flags are made available to scripts as environment variables with the INVOWK_FLAG_ prefix.
Defining Flags
cmds: [
{
name: "deploy"
description: "Deploy the application"
implementations: [
{
script: {content: """
echo "Deploying to ${INVOWK_FLAG_ENV}..."
if [ "$INVOWK_FLAG_DRY_RUN" = "true" ]; then
echo "DRY RUN - no changes made"
else
./scripts/deploy.sh "$INVOWK_FLAG_ENV"
fi
"""}
runtimes: [{name: "native"}]
platforms: [{name: "linux"}, {name: "macos"}]
}
]
// Define flags for this command
flags: [
{name: "env", description: "Target environment"},
{name: "dry-run", description: "Perform a dry run", default_value: "false"},
{name: "retry-count", description: "Number of retries", default_value: "3"},
]
}
]
Flag Properties
| Property | Required | Description |
|---|---|---|
name | Yes | Flag name (POSIX-compliant: starts with letter, alphanumeric/hyphen/underscore) |
description | Yes | Description shown in help text |
default_value | No | Default value if flag is not provided (cannot be used with required) |
type | No | Data type: string (default), bool, int, or float |
required | No | If true, the flag must be provided (cannot have default_value) |
short | No | Single-letter alias (e.g., v for -v shorthand) |
validation | No | Regex pattern to validate flag values |
Reserved prefixes: The
ivk-,invowk-, andi-prefixes are reserved for system flags. Additionally,helpandversionare reserved built-in flag names.
Typed Flags
Flags can specify a type for better validation:
flags: [
{name: "verbose", description: "Enable verbose output", type: "bool", default_value: "false"},
{name: "count", description: "Number of iterations", type: "int", default_value: "5"},
{name: "threshold", description: "Confidence threshold", type: "float", default_value: "0.95"},
{name: "message", description: "Custom message", type: "string"},
]
- string (default): Any value is accepted
- bool: Only
trueorfalseare accepted - int: Only valid integers are accepted (including negative numbers)
- float: Only valid floating-point numbers are accepted (e.g.,
3.14,-2.5,1.5e-3)
Required Flags
Mark a flag as required to ensure it must be provided:
flags: [
{name: "target", description: "Deployment target", required: true},
{name: "confirm", description: "Skip confirmation", type: "bool", default_value: "false"},
]
Required flags cannot have a default_value. If a required flag is not provided, the command will fail with an error.
Short Aliases
Add single-letter shortcuts for frequently used flags:
flags: [
{name: "verbose", description: "Enable verbose output", type: "bool", short: "v"},
{name: "output", description: "Output file path", short: "o"},
{name: "force", description: "Force overwrite", type: "bool", short: "f"},
]
Usage:
invowk cmd build -v -o=./dist/output.txt -f
# Equivalent to:
invowk cmd build --verbose --output=./dist/output.txt --force
Validation Patterns
Use regex patterns to validate flag values:
flags: [
{name: "env", description: "Environment", validation: "^(dev|staging|prod)$", default_value: "dev"},
{name: "release-version", description: "Version (semver)", validation: "^[0-9]+\\.[0-9]+\\.[0-9]+$"},
]
If a value doesn't match the pattern, the command fails before execution:
Error: flag 'env' value 'invalid' does not match required pattern '^(dev|staging|prod)$'
Complete Flag Example
Here's a command using all flag features:
cmds: [
{
name: "deploy"
description: "Deploy the application"
implementations: [...]
flags: [
{
name: "env"
description: "Target environment"
type: "string"
required: true
short: "e"
validation: "^(dev|staging|prod)$"
},
{
name: "replicas"
description: "Number of replicas"
type: "int"
short: "n"
default_value: "1"
},
{
name: "dry-run"
description: "Perform a dry run"
type: "bool"
short: "d"
default_value: "false"
},
]
}
]
Usage:
invowk cmd deploy -e=prod -n=3 -d
Using Flags
Flags are passed using standard --flag=value or --flag value syntax:
# Pass flags to a command
invowk cmd deploy --env=production --dry-run=true
# Use default values
invowk cmd deploy --env=staging # dry-run defaults to "false"
# View flag help
invowk cmd deploy --help
Environment Variable Naming
Flag names are converted to environment variables:
- Prefix:
INVOWK_FLAG_ - Hyphens (
-) become underscores (_) - Converted to uppercase
| Flag Name | Environment Variable |
|---|---|
env | INVOWK_FLAG_ENV |
dry-run | INVOWK_FLAG_DRY_RUN |
output-file | INVOWK_FLAG_OUTPUT_FILE |
retry-count | INVOWK_FLAG_RETRY_COUNT |
Flags in Scripts
Access flag values in your scripts using the environment variables:
#!/bin/bash
# Access flags as environment variables
echo "Environment: $INVOWK_FLAG_ENV"
echo "Dry run: $INVOWK_FLAG_DRY_RUN"
# Conditional logic based on flags
if [ "$INVOWK_FLAG_VERBOSE" = "true" ]; then
set -x # Enable debug output
fi
# Use default value pattern if needed
RETRIES="${INVOWK_FLAG_RETRY_COUNT:-3}"
Metadata Variables
Invowk injects metadata variables during command execution:
| Variable | Description | Always Set |
|---|---|---|
INVOWK_CMD_NAME | Current command name | Yes |
INVOWK_RUNTIME | Resolved runtime name (native, virtual-sh, virtual-lua, container) | Yes |
INVOWK_SOURCE | Source origin (invowkfile for root commands, module name for module commands) | Yes |
INVOWK_PLATFORM | Resolved platform (linux, macos, windows) | Yes |
Script-Level Dependencies
Dependencies can also be specified at the script level, which is especially useful for container-based implementations:
cmds: [
{
name: "container-check"
implementations: [
{
script: {content: "test -f /workspace/invowkfile.cue && echo ready"}
runtimes: [{
name: "container"
image: "debian:stable-slim"
// Runtime-level depends_on — validated inside the container
depends_on: {
tools: [
{alternatives: ["sh"]},
]
filepaths: [
{alternatives: ["/workspace/invowkfile.cue"]},
]
}
}]
platforms: [{name: "linux"}]
// Implementation-level depends_on — always validated on the HOST
depends_on: {
tools: [
{alternatives: ["docker"]},
]
}
}
]
}
]
Two-Phase Dependency Validation:
Dependencies are validated in two phases depending on where they are declared:
-
Host dependencies (root, command, or implementation level
depends_on): Always validated against the host system, regardless of the selected runtime. Use these to ensure host-side prerequisites are met (e.g.,dockeris installed on the host). -
Container runtime dependencies (
depends_oninside a container runtime block): Validated inside the container (verifies the container environment is correctly set up). This is only available for the container runtime — native, virtual-sh, and virtual-lua runtimes do not support runtime-leveldepends_on.
Only the selected runtime's depends_on is checked at execution time.
Command Arguments (Positional Arguments)
Commands can define typed positional arguments that are validated at runtime. Arguments are passed to scripts via INVOWK_ARG_ environment variables.
Defining Arguments
cmds: [
{
name: "deploy"
description: "Deploy the application"
implementations: [
{
script: {content: """
echo "Deploying to ${INVOWK_ARG_ENV}..."
echo "Replicas: ${INVOWK_ARG_REPLICAS}"
echo "Services: ${INVOWK_ARG_SERVICES}"
"""}
runtimes: [{name: "native"}]
platforms: [{name: "linux"}, {name: "macos"}]
}
]
// Define positional arguments
args: [
{name: "env", description: "Target environment", required: true},
{name: "replicas", description: "Number of replicas", type: "int", default_value: "1"},
{name: "services", description: "Services to deploy", variadic: true},
]
}
]
Argument Properties
| Property | Required | Description |
|---|---|---|
name | Yes | Argument name (starts with a letter, then letters, digits, underscores, or hyphens) |
description | Yes | Description shown in help text |
required | No | If true, the argument must be provided (cannot have default_value) |
default_value | No | Default value if argument is not provided |
type | No | Data type: string (default), int, or float |
validation | No | Regex pattern to validate argument values |
variadic | No | If true, accepts multiple values (must be last argument) |
Typed Arguments
Arguments can specify a type for validation:
args: [
{name: "env", description: "Target environment", type: "string"},
{name: "replicas", description: "Number of replicas", type: "int", default_value: "3"},
{name: "threshold", description: "Threshold value", type: "float", default_value: "0.5"},
]
- string (default): Any value is accepted
- int: Only valid integers are accepted
- float: Only valid floating-point numbers are accepted
Required vs Optional Arguments
Arguments can be required or optional:
args: [
// Required: must be provided
{name: "env", description: "Target environment", required: true},
// Optional with default: uses default if not provided
{name: "replicas", description: "Number of replicas", default_value: "1"},
// Optional without default: empty if not provided
{name: "tag", description: "Image tag"},
]
Rules:
- Required arguments cannot have
default_value - Required arguments must come before optional arguments
- Only one variadic argument is allowed (must be last)
Validation Patterns
Use regex patterns to validate argument values:
args: [
{name: "env", description: "Environment", required: true, validation: "^(dev|staging|prod)$"},
{name: "version", description: "Semantic version", validation: "^[0-9]+\\.[0-9]+\\.[0-9]+$"},
]
If a value doesn't match the pattern, the command fails with a styled error:
✗ Invalid argument value!
Command 'deploy' received an invalid value for argument 'env'.
Value: "invalid"
Error: value does not match pattern '^(dev|staging|prod)$'
Variadic Arguments
The last argument can be variadic, accepting multiple values:
args: [
{name: "env", description: "Target environment", required: true},
{name: "services", description: "Services to deploy", variadic: true},
]
Usage:
invowk cmd deploy prod api web worker
This provides multiple environment variables:
INVOWK_ARG_SERVICES: Space-joined values (api web worker)INVOWK_ARG_SERVICES_COUNT: Number of values (3)INVOWK_ARG_SERVICES_1,INVOWK_ARG_SERVICES_2, etc.: Individual values
Using Arguments
Arguments are passed after the command name:
# Required argument only
invowk cmd deploy prod
# With optional argument
invowk cmd deploy prod 3
# With variadic arguments
invowk cmd deploy prod 3 api web worker
# View argument help
invowk cmd deploy --help
The help output shows argument documentation:
Usage:
invowk cmd deploy <env> [replicas] [services]...
Arguments:
env (required) - Target environment
replicas (default: "1") [int] - Number of replicas
services (optional) (variadic) - Services to deploy
Environment Variable Naming
Argument names are converted to environment variables:
- Prefix:
INVOWK_ARG_ - Hyphens (
-) become underscores (_) - Converted to uppercase
| Argument Name | Environment Variable |
|---|---|
env | INVOWK_ARG_ENV |
replica-count | INVOWK_ARG_REPLICA_COUNT |
output-file | INVOWK_ARG_OUTPUT_FILE |
Arguments in Scripts
Invowk provides two ways to access command arguments in your scripts:
1. Shell Positional Parameters (Recommended)
Arguments are passed as shell positional parameters ($1, $2, $@, etc.), allowing you to use native shell syntax:
#!/bin/bash
# Access arguments using standard shell syntax
echo "Environment: $1"
echo "Replicas: $2"
echo "All args: $@"
echo "Number of args: $#"
# Loop over all arguments
for arg in "$@"; do
echo "Argument: $arg"
done
Shell Compatibility:
| Shell | Positional Access | Notes |
|---|---|---|
| bash, sh, zsh | $1, $2, $@, $# | Standard POSIX syntax |
| PowerShell | $args[0], $args[1] | Zero-indexed array |
| cmd.exe | N/A | Use environment variables instead |
| virtual-sh (mvdan/sh) | $1, $2, $@, $# | Standard POSIX syntax |
| container | $1, $2, $@, $# | Standard POSIX syntax (uses /bin/sh) |
2. Environment Variables
Arguments are also available as INVOWK_ARG_* environment variables, which work across all shells and runtimes:
#!/bin/bash
# Access arguments via environment variables
echo "Environment: $INVOWK_ARG_ENV"
echo "Replicas: $INVOWK_ARG_REPLICAS"
# Check if variadic args were provided
if [ -n "$INVOWK_ARG_SERVICES" ]; then
echo "Services: $INVOWK_ARG_SERVICES"
echo "Count: $INVOWK_ARG_SERVICES_COUNT"
# Iterate over individual values
for i in $(seq 1 $INVOWK_ARG_SERVICES_COUNT); do
eval "SERVICE=\$INVOWK_ARG_SERVICES_$i"
echo "Processing service: $SERVICE"
done
fi
Choosing Between Methods
| Use Case | Recommended Method |
|---|---|
| Simple scripts | Positional parameters ($1, $2) |
| Complex arg handling | Environment variables (INVOWK_ARG_*) |
| Cross-platform scripts | Environment variables |
| Variadic arguments | Both work (env vars provide _COUNT and indexed access) |
| PowerShell scripts | Either $args[0] or environment variables |
| cmd.exe scripts | Environment variables only |
Arguments with Flags
Commands can have both arguments and flags. Flags use --name=value syntax and can appear anywhere:
# Flags before arguments
invowk cmd deploy --dry-run prod 3
# Flags after arguments
invowk cmd deploy prod 3 --verbose
# Flags mixed with arguments
invowk cmd deploy prod --dry-run 3 api web --verbose
Complete Argument Example
cmds: [
{
name: "deploy"
description: "Deploy services to an environment"
implementations: [
{
script: {content: """
echo "=== Deployment ==="
echo "Environment: $INVOWK_ARG_ENV"
echo "Replicas: $INVOWK_ARG_REPLICAS"
echo "Services: $INVOWK_ARG_SERVICES"
if [ "$INVOWK_FLAG_DRY_RUN" = "true" ]; then
echo "[DRY RUN] Would deploy..."
else
for i in $(seq 1 $INVOWK_ARG_SERVICES_COUNT); do
eval "SERVICE=\$INVOWK_ARG_SERVICES_$i"
echo "Deploying $SERVICE with $INVOWK_ARG_REPLICAS replicas..."
done
fi
"""}
runtimes: [{name: "native"}]
platforms: [{name: "linux"}, {name: "macos"}]
}
]
args: [
{
name: "env"
description: "Target environment"
required: true
validation: "^(dev|staging|prod)$"
},
{
name: "replicas"
description: "Number of replicas"
type: "int"
default_value: "1"
},
{
name: "services"
description: "Services to deploy"
variadic: true
},
]
flags: [
{name: "dry-run", description: "Perform a dry run", type: "bool", default_value: "false"},
]
}
]
Usage:
invowk cmd deploy prod 3 api web worker --dry-run
Environment Variables in Nested Commands
When a command's script invokes another invowk command (e.g., invowk cmd other-command), the following environment variable behavior applies:
Isolated Variables (NOT inherited by child commands):
INVOWK_ARG_*- Argument valuesINVOWK_FLAG_*- Flag valuesARGC,ARG1,ARG2, etc. - Positional argument shorthand variables (injected alongsideINVOWK_ARG_*)
This isolation prevents the parent command's arguments and flags from accidentally leaking into child commands, which could cause unexpected behavior.
Inherited Variables (standard UNIX behavior):
- Variables defined in the
envconstruct of a command or implementation - Any other environment variables in the process environment
This follows standard UNIX semantics where child processes inherit their parent's environment. If you define env: {vars: {MY_VAR: "value"}} in a command and that command calls another invowk command, the child will see MY_VAR in its environment. This is intentional and allows commands to set up environment context for nested invocations.
Example:
cmds: [
{
name: "parent"
env: {vars: {SHARED_CONFIG: "/etc/app/config.yaml"}} // Inherited by children
implementations: [
{
script: {content: """
echo "Parent's INVOWK_ARG_NAME: $INVOWK_ARG_NAME" # "parent-value"
invowk cmd examples child # Child will NOT see INVOWK_ARG_NAME
# But child WILL see SHARED_CONFIG
"""}
runtimes: [{name: "native"}]
platforms: [{name: "linux"}, {name: "macos"}, {name: "windows"}]
}
]
args: [{name: "name", default_value: "parent-value"}]
},
{
name: "child"
implementations: [
{
script: {content: """
echo "Child's INVOWK_ARG_NAME: ${INVOWK_ARG_NAME:-<not set>}" # "<not set>"
echo "Child's SHARED_CONFIG: $SHARED_CONFIG" # "/etc/app/config.yaml"
"""}
runtimes: [{name: "native"}]
platforms: [{name: "linux"}, {name: "macos"}, {name: "windows"}]
}
]
args: [{name: "name", default_value: "child-value"}]
},
]
Arguments vs Subcommands
A command cannot have both positional arguments and subcommands. If a command defines args but also has subcommands (commands with the same prefix), invowk will fail with an error:
✖ Invalid command structure
Command 'deploy' has both args and subcommands in invowkfile.cue
defined args: env
subcommands: deploy status, deploy logs
Remove either the 'args' field or the subcommands to resolve this conflict.
This is enforced because CLI parsers interpret positional arguments after a command as potential subcommand names, making the combination ambiguous.
Platform Compatibility
Every implementation must specify which operating systems it supports using the platforms field. At least one platform is required.
Basic Platform Configuration
cmds: [
{
name: "build"
description: "Build the project"
implementations: [
{
script: {content: "make build"}
runtimes: [{name: "native"}]
platforms: [{name: "linux"}, {name: "macos"}, {name: "windows"}]
}
]
},
{
name: "clean"
description: "Clean build artifacts"
implementations: [
{
script: {content: "rm -rf bin/"}
runtimes: [{name: "native"}]
// Unix-only command
platforms: [{name: "linux"}, {name: "macos"}]
}
]
},
]
Supported Platform Values
| Value | Description |
|---|---|
linux | Linux operating systems |
macos | macOS (Darwin) |
windows | Windows |
Platform-Specific Implementations
You can provide different implementations for different platforms:
cmds: [
{
name: "system info"
description: "Display system information"
implementations: [
// Unix implementation (Linux and macOS)
{
script: {content: """
echo "Hostname: $(hostname)"
echo "Kernel: $(uname -r)"
"""}
runtimes: [{name: "native"}]
platforms: [{name: "linux"}, {name: "macos"}]
},
// Windows implementation
{
script: {content: """
echo Hostname: %COMPUTERNAME%
echo User: %USERNAME%
"""}
runtimes: [{name: "native"}]
platforms: [{name: "windows"}]
}
]
}
]
Platform-Specific Environment Variables
Each platform can define its own environment variables by creating separate implementations for each platform:
cmds: [
{
name: "deploy"
description: "Deploy with platform-specific config"
implementations: [
{
script: {content: "echo \"Platform: $PLATFORM_NAME, Config: $CONFIG_PATH\""}
runtimes: [{name: "native"}]
platforms: [{name: "linux"}]
env: {vars: {PLATFORM_NAME: "Linux", CONFIG_PATH: "/etc/app/config.yaml"}}
},
{
script: {content: "echo \"Platform: $PLATFORM_NAME, Config: $CONFIG_PATH\""}
runtimes: [{name: "native"}]
platforms: [{name: "macos"}]
env: {vars: {PLATFORM_NAME: "macOS", CONFIG_PATH: "/usr/local/etc/app/config.yaml"}}
},
]
}
]
Command Listing
When you run invowk cmd, the supported platforms are displayed for each command:
Available Commands
(* = default runtime)
From invowkfile:
build - Build the project [native*] (linux, macos, windows)
clean - Clean build artifacts [native*] (linux, macos)
system info - Display system information [native*] (linux, macos, windows)
Unsupported Platform Error
If you try to run a command on an unsupported platform, invowk displays a styled error message:
✗ Host not supported
Command 'clean' cannot run on this host.
Current host: windows
Supported hosts: linux, macos
This command is only available on the platforms listed above.
Script Sources
Commands can use either inline scripts or script files:
Inline Scripts
Use single-line or multi-line (with triple quotes """) CUE strings:
cmds: [
{
name: "build"
implementations: [
{
script: {content: """
#!/bin/bash
set -e
echo "Building..."
go build ./...
"""}
runtimes: [{name: "native"}]
platforms: [{name: "linux"}, {name: "macos"}]
},
]
},
]
Script Files
script.file is for module-contained files only. In a root/project invowkfile.cue, invoke project-local scripts from script.content. In an invowkmod, use script.file for files contained in that same module.
// Root/project invowkfile.cue
cmds: [
{
name: "deploy"
implementations: [
{
script: {content: "./scripts/deploy.sh"}
runtimes: [{name: "native"}]
platforms: [{name: "linux"}, {name: "macos"}]
},
]
},
]
// Inside mytools.invowkmod/invowkfile.cue
cmds: [
{
name: "test"
implementations: [
{
script: {file: "scripts/run-tests.sh"}
runtimes: [{name: "native"}]
platforms: [{name: "linux"}, {name: "macos"}]
},
]
},
]
Recognized script extensions: .sh, .bash, .ps1, .bat, .cmd, .py, .rb, .pl, .zsh, .fish
Interpreter Support
By default, invowk executes scripts using a shell. The native runtime uses $SHELL on Unix-like systems, then falls back to bash and then sh; on Windows it tries PowerShell before cmd. Container runtime scripts use the container's configured shell. You can also run scripts with other interpreters like Python, Ruby, Node.js, Perl, etc.
Auto-Detection from Shebang (Default)
When a script starts with a shebang line (#!/...), invowk automatically detects and uses that interpreter:
cmds: [
{
name: "python-script"
description: "Python script with auto-detected interpreter"
implementations: [
{
script: {content: """
#!/usr/bin/env python3
import sys
print(f"Hello from Python {sys.version_info.major}.{sys.version_info.minor}!")
"""}
runtimes: [{name: "native"}]
platforms: [{name: "linux"}, {name: "macos"}]
}
]
}
]
The shebang is parsed to extract both the interpreter and any arguments:
#!/usr/bin/env python3→ runs withpython3#!/usr/bin/env -S python3 -u→ runs withpython3 -u(unbuffered)#!/usr/bin/perl -w→ runs withperl -w(warnings enabled)
Explicit Interpreter
You can explicitly specify an interpreter using the interpreter field on the script object:
cmds: [
{
name: "ruby-script"
description: "Ruby script with explicit interpreter"
implementations: [
{
// No shebang needed when interpreter is explicit
script: {
content: """
puts "Hello from Ruby!"
puts "Ruby version: #{RUBY_VERSION}"
"""
interpreter: "ruby"
}
runtimes: [{name: "native"}]
platforms: [{name: "linux"}, {name: "macos"}]
}
]
}
]
The explicit interpreter takes precedence over shebang detection. If the resolved script has both a concrete script.interpreter and a different shebang interpreter or argument list, invowk keeps script.interpreter authoritative and emits an advisory warning from invowk validate and --ivk-dry-run. Omitted script.interpreter and interpreter: "auto" are shebang-driven and do not warn.
Interpreter in Containers
The interpreter feature works with container runtimes as well:
cmds: [
{
name: "container-python"
description: "Python script in container"
implementations: [
{
// The interpreter is auto-detected from the shebang in script.content.
script: {content: """
#!/usr/bin/env python3
import os
print("Hello from Python in a container!")
print(f"Working directory: {os.getcwd()}")
"""}
runtimes: [{
name: "container"
image: "python:3-slim"
}]
platforms: [{name: "linux"}]
}
]
},
{
name: "container-python-explicit"
description: "Python script with explicit interpreter in container"
implementations: [
{
// No shebang needed
script: {
content: """
import sys
print(f"Python {sys.version}")
"""
interpreter: "python3"
}
runtimes: [{
name: "container"
image: "python:3-slim"
}]
platforms: [{name: "linux"}]
}
]
}
]
Interpreter with Arguments
Positional arguments are passed to interpreter scripts just like shell scripts:
cmds: [
{
name: "greet-python"
description: "Python script with arguments"
implementations: [
{
script: {content: """
#!/usr/bin/env python3
import sys
name = sys.argv[1] if len(sys.argv) > 1 else "World"
print(f"Hello, {name}!")
"""}
runtimes: [{name: "native"}]
platforms: [{name: "linux"}, {name: "macos"}]
}
]
args: [
{name: "name", description: "Name to greet", default_value: "World"}
]
}
]
Fallback Behavior
When no shebang is found and no script.interpreter is specified, invowk falls back to shell execution:
- Native runtime: Uses the system's default shell
- Container runtime: Uses
/bin/sh -c
--ivk-dry-run prints the resolved interpreter provenance, such as explicit interpreter, shebang-detected interpreter, or default shell behavior, before the command is executed.
Virtual-Sh Runtime Restriction
The virtual-sh runtime always uses the built-in mvdan/sh interpreter. Non-shell script.interpreter values such as python3, ruby, or node are rejected for virtual-sh implementations; use native, virtual-lua, or container runtime for those scripts. Runtime configs do not accept an interpreter field for any runtime.
Supported Interpreters
Explicit interpreters are validated against a safety allowlist. The interpreter may be auto
(detect from shebang) or one of these known interpreter base names:
- POSIX shells:
sh,bash,zsh,fish,dash,ksh,mksh - Python:
python,python3,python2 - JavaScript runtimes:
node,deno,bun - Other scripting languages:
ruby,perl,php,lua,Rscript - Windows shells:
pwsh,powershell,cmd
Invowk strips directory prefixes and a Windows .exe suffix before checking the name, so
/usr/bin/python3 and python3.exe are valid. /usr/bin/env <interpreter> and
/bin/env <interpreter> are also accepted when the interpreter argument is allowlisted.
Arbitrary custom interpreter names are rejected as unsafe.
Modules
Modules are self-contained folders that bundle module metadata with command definitions and scripts for easy distribution and portability.
What is a Module?
A module is a directory with the .invowkmod suffix that contains:
- Required
invowkmod.cueat the root (module metadata and dependencies) - Optional
invowkfile.cueat the root (command definitions) - Optional script files referenced by command implementations
- No nested modules (modules cannot contain other modules, except vendored modules in the
invowk_modules/subdirectory)
invowkfile.cue is optional for library-only modules that exist to declare dependencies.
Module Naming
Module folder names follow these rules:
- Must end with
.invowkmod - The prefix (before
.invowkmod) must:- Start with a letter (a-z, A-Z)
- Contain only alphanumeric characters
- Support dot-separated segments for namespacing
- Compatible with RDNS (Reverse Domain Name System) naming conventions
Valid module names:
mycommands.invowkmodcom.example.mytools.invowkmodorg.company.project.invowkmodUtils.invowkmod
Invalid module names:
.hidden.invowkmod(starts with dot)my-commands.invowkmod(contains hyphen)my_commands.invowkmod(contains underscore)123commands.invowkmod(starts with number)com..example.invowkmod(empty segment)
Module Structure
com.example.mytools.invowkmod/
├── invowkmod.cue # Required: module metadata + dependencies
├── invowkfile.cue # Optional: command definitions
├── scripts/ # Optional: script files
│ ├── build.sh
│ ├── deploy.sh
│ └── utils/
│ └── helper.sh
└── templates/ # Optional: other resources
└── config.yaml
Script Paths in Modules
When referencing script files in a module's invowkfile, use paths relative to the module root with forward slashes for cross-platform compatibility:
// invowkfile.cue
cmds: [
{
name: "build"
description: "Build the project"
implementations: [
{
// Path relative to module root, using forward slashes
script: {file: "scripts/build.sh"}
runtimes: [{name: "native"}]
platforms: [{name: "linux"}, {name: "macos"}]
}
]
},
{
name: "deploy"
description: "Deploy the application"
implementations: [
{
// Nested script path
script: {file: "scripts/utils/helper.sh"}
runtimes: [{name: "native"}]
platforms: [{name: "linux"}, {name: "macos"}]
}
]
}
]
Important:
- Always use forward slashes (
/) in script paths, even on Windows - Paths are automatically converted to the native format at runtime
- Absolute paths are not allowed in modules
- Paths cannot escape the module directory (e.g.,
../outside.shis invalid)
Validation
Use invowk validate to validate invowkfiles, modules, or the entire workspace:
# Validate the entire workspace (discovery + diagnostics)
invowk validate
# Validate a single invowkfile
invowk validate ./invowkfile.cue
# Validate a module (always includes deep validation)
invowk validate ./mymod.invowkmod
The unified validate command auto-detects the target type and always performs deep validation (CUE schema, structural checks, and command tree validation).
Validating Modules
Use invowk validate to check a module's structure (always performs full validation including invowkfile parsing):
invowk validate ./com.example.mytools.invowkmod
Example output for a valid module:
Module Validation
• Path: /home/user/com.example.mytools.invowkmod
• Name: com.example.mytools
✓ Module is valid
✓ Structure check passed
✓ Naming convention check passed
✓ Required files present
✓ Invowkfile parses successfully
Example output for an invalid module:
Module Validation
• Path: /home/user/invalid.invowkmod
✗ Module validation failed with 2 issue(s)
1. [structure] missing required invowkmod.cue
2. [structure] nested.invowkmod: nested modules are not allowed
Creating Modules
Use the module create command to scaffold a new module:
# Create a simple module in the current directory
invowk module create mycommands
# Create a module with RDNS naming
invowk module create com.example.mytools
# Create a module in a specific directory
invowk module create mytools --path /path/to/modules
# Create with a scripts directory
invowk module create mytools --scripts
# Create with custom module identifier and description
invowk module create mytools --module-id "com.example.mytools" --description "A collection of useful commands"
The created module will contain invowkmod.cue metadata and a template invowkfile.cue with a sample "hello" command.
Listing Modules
Use the module list command to see all discovered modules:
invowk module list
Example output:
Discovered Modules
• Found 3 module(s)
• current directory:
✓ mytools
/home/user/project/mytools.invowkmod
• user commands (~/.invowk/cmds):
✓ com.example.utilities
/home/user/.invowk/cmds/com.example.utilities.invowkmod
✓ org.company.tools
/home/user/.invowk/cmds/org.company.tools.invowkmod
Archiving Modules
Use the module archive command to create a ZIP archive for distribution:
# Archive a module (creates <module-name>.invowkmod.zip in current directory)
invowk module archive ./mytools.invowkmod
# Archive with custom output path
invowk module archive ./mytools.invowkmod --output ./dist/mytools.zip
Example output:
Archive Module
✓ Module archived successfully
• Output: /home/user/dist/mytools.zip
• Size: 2.45 KB
Importing Modules
Use the module import command to install a module from a ZIP file or URL:
# Import from a local ZIP file (installs to ~/.invowk/cmds/)
invowk module import ./mytools.invowkmod.zip
# Import from a URL
invowk module import https://example.com/modules/mytools.zip
# Import to a custom directory
invowk module import ./module.zip --path ./local-modules
# Overwrite existing module
invowk module import ./module.zip --overwrite
Example output:
Import Module
✓ Module imported successfully
• Name: mytools
• Path: /home/user/.invowk/cmds/mytools.invowkmod
• The module commands are now available via invowk
Benefits of Modules
- Portability: Share a complete command set as a single folder
- Self-contained: Module metadata and scripts travel together
- Cross-platform: Forward slash paths work on all operating systems
- Namespace isolation: RDNS naming prevents conflicts between modules
- Validation: Built-in validation ensures module integrity
Using Modules
Modules are automatically discovered and loaded from all configured sources:
- Current directory
invowkfile.cue(highest priority) - Sibling
*.invowkmodmodules in current directory - Configured includes (module paths from config)
~/.invowk/cmds/(modules only, non-recursive)
When invowk discovers a module, it:
- Validates the module structure and naming
- Loads
invowkfile.cuefrom within the module (if present) - Resolves script paths relative to the module root
- Makes all commands available (use
@sourceprefix or--ivk-fromfor disambiguation)
Commands from modules appear in invowk cmd with the source indicated as "module".
Module Dependencies
Modules can declare dependencies on other modules hosted in remote Git repositories (GitHub, GitLab, etc.). This enables code reuse and sharing of common command definitions across projects.
Declaring Dependencies
Add a requires field to invowkmod.cue to declare module dependencies:
// invowkmod.cue
module: "myproject"
version: "1.0.0"
// Declare module dependencies
requires: [
{
git_url: "https://github.com/user/common-tools.git"
version: "^1.0.0" // Compatible with 1.x.x
},
{
git_url: "https://github.com/user/deploy-utils.git"
version: "~2.1.0" // Approximately 2.1.x
alias: "deploy" // Custom command source ID (for collision disambiguation)
},
{
git_url: "https://github.com/user/monorepo.git"
version: ">=1.0.0"
path: "packages/io.example.cli.invowkmod" // Module directory within repo
},
]
The Git URL is only the source location. Repository names do not need to end in .invowkmod, and the repository basename does not define the module identity. When path is omitted, the repository root must contain both invowkmod.cue and invowkfile.cue. When path is set, it must point to a relative, traversal-safe directory whose basename ends in .invowkmod and whose prefix matches the selected module's module value. Ordinary segment names containing consecutive dots are allowed, but parent-directory segments such as .., absolute paths, drive-qualified paths, and UNC/rooted paths are rejected after Invowk normalizes path separators.
The canonical module identity is the module value inside invowkmod.cue. Synced and vendored dependencies are materialized locally as <module-id>.invowkmod, even when the source repository is named something ordinary like tools.git.
Commands in a module can only call commands from direct dependencies or globally installed modules (transitive dependencies are not available).
Explicit-Only Dependency Model: Every module in the dependency tree must be declared in the root
invowkmod.cue. Transitive dependencies are NOT resolved automatically — if module A requires module B, and B requires C, then C must also be declared in the rootinvowkmod.cue. Useinvowk module tidyto auto-add missing transitive deps, orinvowk module syncto detect missing ones with actionable error messages.
Version Constraints
| Format | Description | Example |
|---|---|---|
^1.2.0 | Compatible with 1.x.x (>=1.2.0 <2.0.0) | Major version locked |
~1.2.0 | Approximately 1.2.x (>=1.2.0 <1.3.0) | Minor version locked |
>=1.0.0 | Greater than or equal | Minimum version |
<2.0.0 | Less than | Maximum version |
1.2.3 | Exact version | Pinned version |
Constraint values must be a single expression. Tags may have a v prefix, but the constraint value must not: write ^1.2.3, not ^v1.2.3, and do not use compound ranges like >=1.0.0 <2.0.0.
Module Dependency CLI Commands
The module command provides subcommands for managing dependencies. Note that add and remove automatically update both the invowkmod.lock.cue lock file and the requires section in your invowkmod.cue file.
# Add a new module dependency
invowk module add https://github.com/user/module.git ^1.0.0
# Add with custom alias (for collision disambiguation)
invowk module add https://github.com/user/module.git ^1.0.0 --alias myalias
# Add from monorepo subdirectory
invowk module add https://github.com/user/monorepo.git ^1.0.0 --path packages/io.example.tools.invowkmod
# List all resolved dependencies
invowk module deps
# Sync dependencies from invowkmod.cue (resolve and download)
# Fails with actionable errors if transitive deps are missing
invowk module sync
# Auto-add missing transitive dependencies to invowkmod.cue
invowk module tidy
# Update all dependencies to latest matching versions
invowk module update
# Update a specific dependency
invowk module update https://github.com/user/module.git
# Remove a dependency
invowk module remove https://github.com/user/module.git
Lock File
Module resolution creates an invowkmod.lock.cue file that records the exact versions resolved. This ensures reproducible builds across environments:
// invowkmod.lock.cue - Auto-generated lock file
// DO NOT EDIT MANUALLY
version: "2.0"
generated: "2025-01-12T10:30:00Z"
modules: {
"https://github.com/user/common-tools.git": {
git_url: "https://github.com/user/common-tools.git"
version: "^1.0.0"
resolved_version: "1.2.3"
git_commit: "abc123def456789012345678901234567890abcd"
module_id: "com.example.commontools"
namespace: "com.example.commontools@1.2.3"
command_source_id: "com.example.commontools"
content_hash: "sha256:a1b2c3d4e5f6..."
}
}
Command Namespacing
When dependency modules are installed or vendored, their commands are published under a command source ID to prevent conflicts:
- Default: the parsed module ID (for example,
com.example.commontools) - With alias: the specified alias (for example,
deploy)
The lock file key preserves the Git URL plus optional normalized path; module_id stores the canonical module identity separately. The lock file's namespace field may include the resolved version for display and compatibility, but command execution uses command_source_id or the alias. Access dependency commands with a simple name when unique, or disambiguate with @<source> or --ivk-from:
# Run a command from a dependency
invowk cmd build
# Disambiguate when another source also defines "build"
invowk cmd @com.example.commontools build
invowk cmd --ivk-from com.example.commontools build
# With alias
invowk cmd deploy production
Authentication
Module dependencies support both HTTPS and SSH authentication:
- SSH: Uses keys from
~/.ssh/(id_ed25519, id_rsa, id_ecdsa) - HTTPS: Uses environment variables:
GITHUB_TOKENfor GitHubGITLAB_TOKENfor GitLabGIT_TOKENfor generic Git servers
Module Cache
Module dependencies are cached in ~/.invowk/modules/ by default. Override with:
export INVOWK_MODULES_PATH=/custom/path/to/modules
Each module version is cached in a separate source/version directory, and the module payload directory itself is named <module-id>.invowkmod. This keeps local installed modules compatible with Invowk's module directory rules while leaving the lock key tied to the original Git URL plus optional path.
Vendoring
Modules can include their dependencies in an invowk_modules/ subfolder for self-contained distribution:
mymodule.invowkmod/
├── invowkmod.cue
├── invowkfile.cue
├── scripts/
└── invowk_modules/ # Vendored dependencies
├── io.example.utils.invowkmod/
│ ├── invowkmod.cue
│ └── invowkfile.cue
└── com.other.tools.invowkmod/
├── invowkmod.cue
└── invowkfile.cue
Vendored modules are resolved first, before checking the global cache. Use the vendor commands:
# Fetch dependencies into invowk_modules/
invowk module vendor
# Update vendored modules
invowk module vendor --update
# Remove unused vendored modules
invowk module vendor --prune
Note:
invowk module vendorresolves dependencies and copies them intoinvowk_modules/. Ifinvowkmod.lock.cueexists, locked versions are used by default; use--updateto force re-resolution and--pruneto remove stale vendored modules.
Collision Handling
When two modules have the same name, invowk reports a collision error with guidance on how to disambiguate using aliases:
module name collision: 'io.example.tools' defined in both
'/path/to/module1.invowkmod' and '/path/to/module2.invowkmod'
Use an alias to disambiguate:
- For requires: add 'alias' field to the requirement in invowkmod.cue
- For config includes: add 'alias' field to the include entry in config.cue
Runtime Modes
Native Runtime
Uses the system's default shell:
- Linux/macOS: Uses
$SHELL, or falls back tobash→sh - Windows: Uses
pwsh→powershell→cmd
cmds: [
{
name: "build"
implementations: [
{
script: {content: "go build ./..."}
runtimes: [{name: "native"}]
platforms: [{name: "linux"}, {name: "macos"}, {name: "windows"}]
},
]
},
]
Virtual-Sh Runtime
Uses the built-in mvdan/sh shell interpreter. This provides a consistent POSIX-like shell experience across platforms.
cmds: [
{
name: "build"
implementations: [
{
script: {content: """
echo "Building..."
go build ./...
"""}
runtimes: [{name: "virtual-sh", allowed_binaries: ["go"]}]
platforms: [{name: "linux"}, {name: "macos"}, {name: "windows"}]
},
]
},
]
Virtual Runtime Filesystem Access
The virtual-sh and virtual-lua runtimes share a Go-native filesystem safety harness for VM-controlled file operations, shell redirection, Lua file I/O, and built-in utility commands. It is not a kernel sandbox: use the container runtime when you need process-level isolation.
Virtual filesystem settings live on the selected platform, because host paths are OS-specific:
cmds: [
{
name: "write-cache"
implementations: [
{
script: {content: """
local path = invowk.path("CACHE/report.txt")
local file = assert(io.open(path, "w"))
file:write("ok")
file:close()
"""}
runtimes: [{name: "virtual-lua"}]
platforms: [
{
name: "linux"
virtual: {
filesystem: {
access: "restricted"
paths: {
CACHE: "@cache/reports"
}
}
}
},
]
},
]
},
]
virtual.filesystem.access defaults to "restricted", which allows VM-controlled file operations only under implicit safe roots plus virtual.filesystem.paths roots. Set it to "full" only when those operations should be allowed to access normalized host filesystem paths after resolver checks. Path entries are named bridge handles: virtual-sh receives INVOWK_PATH_<NAME>, and virtual-lua resolves them with invowk.path("<NAME>/file").
Host binary policy remains runtime-scoped. allowed_binaries and binary_lookup_mode belong inside the selected runtimes[] entry and are not affected by filesystem access mode.
Container Runtime
⚠️ CRITICAL: Linux Containers Only
The container runtime requires Linux-based container images (e.g.,
debian:stable-slim).NOT supported:
- Alpine-based images - musl-based environments have subtle behavioral differences that reduce runtime reliability
- Windows container images - No POSIX shell available
Platform compatibility:
- Linux with Docker/Podman: Works natively
- macOS with Docker Desktop: Works (Docker Desktop uses Linux VMs)
- Windows with Docker Desktop: Requires WSL2 backend with Linux containers mode
Scripts are executed using
/bin/shinside the container. Windows containers and Alpine images lack the required shell compatibility.
Runs commands inside a Docker or Podman container. Requires exactly one container source, either image or containerfile, in the runtime config.
cmds: [
{
name: "build"
implementations: [
{
script: {content: "echo 'Running inside container'"}
// Container config is specified in the runtime
runtimes: [{
name: "container",
image: "debian:stable-slim",
volumes: ["./data:/data"],
ports: ["8080:8080"],
}]
platforms: [{name: "linux"}]
}
]
},
]
Persistent container targets can be enabled per container runtime:
runtimes: [{
name: "container",
image: "debian:stable-slim",
persistent: {
create_if_missing: true,
// Optional. If omitted, invowk derives a stable invowk-* name
// from the fully-qualified command namespace.
name: "myproject-build",
},
}]
By default, container runs are ephemeral. With persistent, invowk reuses a long-lived container and executes commands with docker exec or podman exec. You can target a pre-existing running container with --ivk-container-name <name>; explicit names must use portable Docker/Podman naming.
Control host environment inheritance per runtime:
cmds: [
{
name: "build"
implementations: [
{
script: {content: "make build"}
runtimes: [{
name: "container",
image: "debian:stable-slim",
env_inherit_mode: "allow",
env_inherit_allow: ["TERM", "LANG"],
env_inherit_deny: ["AWS_SECRET_ACCESS_KEY"],
}]
platforms: [{name: "linux"}]
}
]
},
]
Host SSH Access from Containers
Container commands can optionally SSH back into the host system. When enable_host_ssh: true is set inside the container runtime configuration, invowk starts a secure SSH server using the Wish library and provides connection credentials to the container via environment variables.
Security: The SSH server only accepts token-based authentication. Each command execution gets a unique, time-limited token that is automatically revoked after the command completes.
cmds: [
{
name: "deploy from container"
implementations: [
{
script: {content: """
# Connection info is available via environment variables:
# - INVOWK_SSH_ENABLED: Set to "true" when host SSH is active
# - INVOWK_SSH_HOST: Host address (host.docker.internal or host.containers.internal)
# - INVOWK_SSH_PORT: SSH server port
# - INVOWK_SSH_USER: Username (invowk)
# - INVOWK_SSH_TOKEN: Execution-scoped authentication token
# Example: Run a command on the host
sshpass -p $INVOWK_SSH_TOKEN ssh -o StrictHostKeyChecking=no \
$INVOWK_SSH_USER@$INVOWK_SSH_HOST -p $INVOWK_SSH_PORT \
'echo "Hello from host!"'
"""}
// enable_host_ssh and image are specified inside the container runtime config
runtimes: [{name: "container", image: "debian:stable-slim", enable_host_ssh: true}]
platforms: [{name: "linux"}]
}
]
},
]
Note: The container needs sshpass or similar tools to use password-based SSH authentication. You may need to install it in your container image.
Configuration
invowk uses a global configuration file (CUE format):
- Linux/other Unix-like:
$XDG_CONFIG_HOME/invowk/config.cuewhen set, otherwise~/.config/invowk/config.cue - macOS:
~/Library/Application Support/invowk/config.cue - Windows:
%APPDATA%\invowk\config.cue
Create Default Configuration
invowk config init
View Current Configuration
invowk config show
Show Config File Path
invowk config path
Set a Configuration Value
invowk config set <key> <value>
Dump Raw Configuration
invowk config dump
Configuration Options
// Container engine preference: "podman" or "docker"
container_engine: "podman"
// Default runtime mode: "native", "virtual-sh", "virtual-lua", or "container"
default_runtime: "native"
// Include additional modules in command discovery
includes: [
{path: "/home/user/my-tools.invowkmod"},
{path: "/path/to/module.invowkmod", alias: "myalias"},
]
// Shared virtual runtime options
virtual: {
utilities: {
enabled: true
}
}
// Container runtime options
container: {
auto_provision: {
enabled: true // Enable/disable auto-provisioning of invowk into containers (default: true)
strict: false // Hard error on provisioning failure instead of warning (default: false)
binary_path: "" // Empty default auto-detects via os.Executable()
includes: [{path: "/extra/modules.invowkmod"}] // Modules to provision into containers (optional)
inherit_includes: true // Inherit root-level includes for provisioning (default: true)
cache_dir: "" // Empty default uses the platform cache directory
}
}
// UI options
ui: {
color_scheme: "auto" // "auto", "dark", "light"
verbose: false
interactive: false // Enable alternate screen buffer mode for command execution
}
// LLM provider defaults for LLM-aware commands (optional)
// Default config uses llm: {}. `invowk agent cmd create`
// uses configured LLM settings automatically; `invowk audit` still
// requires explicit opt-in with --llm or --llm-provider.
llm: {
provider: "codex" // "auto", "claude", "codex", "gemini", or "ollama"
model: "gpt-5.1-codex" // Optional for CLI providers
timeout: "2m" // Optional; built-in resolver fallback when omitted
concurrency: 2 // Optional; built-in resolver fallback when omitted
}
// Or configure an OpenAI-compatible API without storing raw secrets:
// llm: {
// api: {
// base_url: "https://api.openai.com/v1"
// model: "gpt-5.1"
// api_key_env: "OPENAI_API_KEY"
// }
// }
Shell Completion
Bash
# Add to ~/.bashrc:
eval "$(invowk completion bash)"
# Or install system-wide:
invowk completion bash > /etc/bash_completion.d/invowk
Zsh
# Add to ~/.zshrc:
eval "$(invowk completion zsh)"
# Or install to fpath:
invowk completion zsh > "${fpath[1]}/_invowk"
Fish
invowk completion fish > ~/.config/fish/completions/invowk.fish
PowerShell
invowk completion powershell | Out-String | Invoke-Expression
# Or add to $PROFILE:
invowk completion powershell >> $PROFILE
Command Examples
List Commands
invowk cmd
Run a Command
invowk cmd build
Run a Command with Spaces in Name
invowk cmd test unit
Override Runtime
invowk cmd build --ivk-runtime virtual-sh
# or
invowk cmd build -r virtual-sh
Force Container Image Rebuild
invowk cmd build --ivk-force-rebuild
Verbose Mode
invowk --ivk-verbose cmd build
# or
invowk -v cmd build
Interactive Mode (Alternate Screen Buffer)
invowk --ivk-interactive cmd build
# or
invowk -i cmd build
Override Config File
invowk --ivk-config /path/to/custom/config.cue cmd build
# or
invowk -c /path/to/custom/config.cue cmd build
Environment Overrides
# Load additional env file at runtime
invowk cmd deploy --ivk-env-file .env.production
invowk cmd deploy -e .env.production # short alias
# Set an environment variable
invowk cmd deploy --ivk-env-var API_KEY=secret123
invowk cmd deploy -E API_KEY=secret123 # short alias
# Control host environment inheritance (allowlist requires allow mode)
invowk cmd build --ivk-env-inherit-mode allow --ivk-env-inherit-allow TERM --ivk-env-inherit-allow LANG
invowk cmd build --ivk-env-inherit-mode all --ivk-env-inherit-deny AWS_SECRET_ACCESS_KEY
Override Working Directory
invowk cmd test --ivk-workdir ./packages/api
# or
invowk cmd test -w ./packages/api
Dry-Run Mode
# Print resolved execution plan without running the command
invowk cmd build --ivk-dry-run
Watch Mode
# Re-execute command on file changes (uses watch config from invowkfile, or all files)
invowk cmd dev --ivk-watch
# or
invowk cmd dev -W
Configure watched files in your invowkfile:
cmds: [{
name: "dev"
implementations: [{
runtimes: [{name: "native"}]
platforms: [{name: "linux"}, {name: "macos"}]
script: {content: "npm run dev"}
}]
watch: {
patterns: ["src/**/*.go", "*.ts"]
debounce: "1s"
clear_screen: true
ignore: ["dist/**"]
}
}]
Interactive TUI Components
invowk includes a set of interactive terminal UI components inspired by gum. These can be used in shell scripts to create interactive prompts, selections, and styled output.
Input
Prompt for single-line text input:
# Basic input
invowk tui input --title "What is your name?"
# With placeholder
invowk tui input --title "Email" --placeholder "user@example.com"
# Password input (hidden)
invowk tui input --title "Password" --password
# With character limit
invowk tui input --title "Username" --char-limit 20
# Use in shell script
NAME=$(invowk tui input --title "Enter your name:")
echo "Hello, $NAME!"
Write
Multi-line text editor for longer input:
# Basic editor
invowk tui write --title "Enter description"
# With line numbers
invowk tui write --title "Code" --show-line-numbers
# Use for commit messages
MESSAGE=$(invowk tui write --title "Commit message")
git commit -m "$MESSAGE"
Choose
Select one or more options from a list:
# Single selection
invowk tui choose "Option 1" "Option 2" "Option 3"
# With title
invowk tui choose --title "Pick a color" red green blue
# Multi-select (up to 3)
invowk tui choose --limit 3 "One" "Two" "Three" "Four"
# Unlimited multi-select
invowk tui choose --no-limit "One" "Two" "Three"
# Use in shell script
COLOR=$(invowk tui choose --title "Pick a color" red green blue)
echo "You picked: $COLOR"
Confirm
Yes/no confirmation prompt (exits with code 0 for yes, 1 for no):
# Basic confirmation
invowk tui confirm "Are you sure?"
# With custom labels
invowk tui confirm --affirmative "Delete" --negative "Cancel" "Delete this file?"
# Default to yes
invowk tui confirm --default "Proceed?"
# Use in shell conditionals
if invowk tui confirm "Continue?"; then
echo "Continuing..."
else
echo "Cancelled."
fi
Filter
Fuzzy filter a list of options:
# Filter from arguments
invowk tui filter "apple" "banana" "cherry" "date"
# Filter from stdin
ls | invowk tui filter --title "Select a file"
# Multi-select filter
cat files.txt | invowk tui filter --no-limit
# With placeholder
invowk tui filter --placeholder "Type to search..." opt1 opt2 opt3
File
File picker for browsing and selecting files:
# Pick any file from current directory
invowk tui file
# Start in specific directory
invowk tui file /home/user/documents
# Only show directories
invowk tui file --directory
# Show hidden files
invowk tui file --hidden
# Filter by extension
invowk tui file --allowed ".go,.md,.txt"
Table
Display and select from tabular data:
# Display a CSV file
invowk tui table --file data.csv
# Pipe data with custom separator
echo -e "name|age|city\nAlice|30|NYC\nBob|25|LA" | invowk tui table --separator "|"
# Selectable rows (prints selected row)
cat data.csv | invowk tui table --selectable
Spin
Show a spinner while running a command:
# Run a command with spinner
invowk tui spin --title "Installing..." -- npm install
# Different spinner types
invowk tui spin --type globe --title "Downloading..." -- curl -O https://example.com/file
# Available types: line, dot, minidot, jump, pulse, points, globe, moon, monkey, meter, hamburger, ellipsis
Pager
Scroll through long content:
# View a file
invowk tui pager README.md
# Pipe content
cat long-file.txt | invowk tui pager
# With line numbers
invowk tui pager --line-numbers myfile.go
# With title
git log | invowk tui pager --title "Git History"
# Soft-wrap long lines
make build 2>&1 | invowk tui pager --title "Build Output" --soft-wrap
Format
Format and render text:
# Format markdown
echo "# Hello World" | invowk tui format --type markdown
# Syntax highlight code
cat main.go | invowk tui format --type code --language go
# Convert emoji shortcodes
echo "Hello :wave: World :smile:" | invowk tui format --type emoji
# Pass template-formatted text through the template formatter
echo "Hello {{.Name}}" | invowk tui format --type template
# Pick a Glamour theme for markdown/code output
cat README.md | invowk tui format --type markdown --theme dark
Style
Apply terminal styling to text:
# Colored text
invowk tui style --foreground "#FF0000" "Red text"
# Bold and italic
echo "Styled" | invowk tui style --bold --italic
# With background and padding
invowk tui style --background "#333" --foreground "#FFF" --padding-left 1 --padding-right 1 "Box"
# Centered with border
invowk tui style --border rounded --align center --width 40 "Centered Title"
# Multiple styles
invowk tui style --bold --foreground "#00FF00" --background "#000" "Matrix"
Using TUI in Invowkfiles
The TUI components can be used within invowkfile scripts to create interactive commands:
cmds: [
{
name: "interactive setup"
description: "Interactive project setup wizard"
implementations: [
{
script: {content: """
#!/bin/bash
NAME=$(invowk tui input --title "Project name:")
TYPE=$(invowk tui choose --title "Project type" cli library api)
if invowk tui confirm "Create project '$NAME' of type '$TYPE'?"; then
invowk tui spin --title "Creating project..." -- mkdir -p "$NAME"
echo "Project created!" | invowk tui style --foreground "#00FF00" --bold
fi
"""}
runtimes: [{name: "native"}]
platforms: [{name: "linux"}, {name: "macos"}]
}
]
}
]
Security Auditing
Invowk includes a built-in security scanner that analyzes invowkfiles, modules, vendored dependencies, and script content for supply-chain vulnerabilities, script injection, path traversal, suspicious patterns, and lock file integrity issues.
Basic Usage
# Scan current directory
invowk audit
# Scan a specific path
invowk audit ./my-project
# Scan a single module
invowk audit ./tools.invowkmod
# Scan a single invowkfile
invowk audit ./invowkfile.cue
# Include global modules (~/.invowk/cmds/)
invowk audit --include-global
# JSON output for CI integration
invowk audit --format json
# Only show high and critical findings
invowk audit --severity high
Exit Codes
| Code | Meaning |
|---|---|
0 | No findings at or above the severity threshold |
1 | Findings detected |
2 | Scan error (e.g., path not found, parse failure) |
What Gets Scanned
The audit scanner runs 7 built-in security checkers concurrently:
| Checker | Category | What It Detects |
|---|---|---|
| Script | execution, path-traversal, obfuscation | Remote code execution (curl | bash), path traversal (../), base64 obfuscation, eval patterns, hex sequences |
| Lua | execution, exfiltration, path-traversal | Disabled virtual-lua API references, sensitive environment reads, wildcard or network-capable host binary allowances, broad virtual filesystem exposure |
| Network | exfiltration | Reverse shells, DNS exfiltration, encoded URLs, suspicious network commands |
| Environment | exfiltration | Risky env_inherit_mode: "all", unset native/virtual-sh env_inherit_mode defaults that inherit all host variables, sensitive variable access (AWS keys, tokens, passwords), credential extraction patterns |
| Lock File | integrity | Hash mismatches, orphaned/missing entries, ambiguous versions, tamper detection |
| Symlink | path-traversal | Any symlink in a module directory, symlinks pointing outside module boundaries, symlink chains, dangling or unreadable symlinks, incomplete directory walks |
| Module Metadata | trust | Typosquatting detection (Levenshtein distance), excessive fan-out, missing version pins, undeclared transitive dependencies, vendored modules missing from requires, module invowkfile parse failures, global module trust |
Compound Threat Detection
After individual checkers run, the correlator cross-references findings from different checkers within the same attack surface. When multiple security concerns appear together, they indicate coordinated threats:
| Compound Threat | Trigger | Escalated Severity |
|---|---|---|
| Credential exfiltration | env access + network access | Critical |
| Path + symlink escape | path traversal + external symlink | Critical |
| Obfuscated exfiltration | obfuscation + network access | Critical |
| Trust chain weakness | metadata issues + lock file issues | High |
| Interpreter traversal | unusual interpreter + path traversal | Critical |
Additional automatic escalation rules:
- 3+ distinct security categories in the same surface → Critical
- High + any other finding in the same surface → Critical
- 2+ Medium findings in the same surface → High
LLM-Powered Analysis
For deeper semantic analysis beyond regex patterns, enable LLM-powered auditing with --llm-provider or --llm. This sends script content to a local CLI tool or remote/local OpenAI-compatible API for reasoning about novel attack vectors, subtle logic flaws, and context-dependent security issues. API-backed providers must support OpenAI-compatible chat completions and model listing because invowk verifies the configured model before scanning. Bare invowk audit never uses global LLM config by itself; pass --llm to opt in to a configured backend.
# Configure once, then explicitly opt in per audit run
invowk config set llm.provider codex
invowk audit --llm
# Auto-detect best available provider (local Ollama first, then env-var APIs, then CLIs)
invowk audit --llm-provider auto
# Use a specific CLI provider (uses that tool's current default model)
invowk audit --llm-provider claude
invowk audit --llm-provider codex
invowk audit --llm-provider gemini
# Override the model passed to a CLI provider
invowk audit --llm-provider claude --llm-model claude-opus-4-6
# Cloud API credentials require an explicit model
OPENAI_API_KEY=sk-... invowk audit --llm-provider codex --llm-model <model>
# Manual configuration (Ollama, LM Studio, or any OpenAI-compatible server)
invowk audit --llm
invowk audit --llm --llm-url http://localhost:1234/v1
invowk audit --llm --llm-url https://api.openai.com/v1 --llm-api-key sk-... --llm-model <model>
LLM Flags:
| Flag | Default | Description |
|---|---|---|
--llm-provider | Auto-detect or use specific provider: auto, claude, codex, gemini, ollama | |
--llm | false | Enable LLM using configured or OpenAI-compatible API settings |
--llm-url | http://localhost:11434/v1 | OpenAI-compatible API base URL (env: INVOWK_LLM_URL) |
--llm-model | qwen2.5-coder:7b for API/Ollama; CLI provider default when omitted | Model name; required when provider detection selects cloud API credentials and optional for CLI providers |
--llm-api-key | (empty) | API key (env: INVOWK_LLM_API_KEY) |
--llm-timeout | 2m | Per-request timeout (env: INVOWK_LLM_TIMEOUT) |
--llm-concurrency | 2 | Max parallel LLM requests (env: INVOWK_LLM_CONCURRENCY) |
Global LLM config lives in the standard Invowk config file. On Linux and other
Unix-like systems, that is $XDG_CONFIG_HOME/invowk/config.cue when
XDG_CONFIG_HOME is set, otherwise ~/.config/invowk/config.cue. Use a provider
harness:
llm: {
provider: "codex"
model: "gpt-5.1-codex" // optional for CLI harnesses
timeout: "2m"
concurrency: 2
}
Or use an OpenAI-compatible endpoint without storing secrets in config:
llm: {
api: {
base_url: "https://api.openai.com/v1"
model: "gpt-5.1"
api_key_env: "OPENAI_API_KEY"
}
}
--llm-provider seamlessly integrates with Claude Code, Codex CLI, and Gemini CLI. If you're already logged in via OAuth, it just works and invowk lets the tool choose its current default model unless you pass --llm-model. When provider detection finds cloud API credentials such as OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, or GOOGLE_API_KEY, you must pass --llm-model so the cloud model choice is explicit.
Compatible LLM servers:
| Server | Default Port | Install |
|---|---|---|
| Ollama | 11434 | Install from your package manager or ollama.com, then run ollama pull qwen2.5-coder:7b |
| LM Studio | 1234 | Download from website, load a model |
| llamafile | 8080 | Download single executable, run |
| vLLM | 8000 | pip install vllm |
Recommended models for security analysis:
| Model | RAM Required | Quality |
|---|---|---|
qwen2.5-coder:7b | 8 GB | Good (default) |
qwen2.5-coder:14b | 16 GB | Better |
qwen2.5-coder:32b | 24 GB | Best (GPT-4o level for code) |
deepseek-coder:33b | 24 GB | Excellent for chain-of-thought reasoning |
Model auto-detection: When --llm is enabled, invowk verifies the configured model is available on the server before scanning. If the model is not found, it shows the available models and suggests the best code-focused alternative (detected by pattern matching — qwen2.5-coder, deepseek-coder, codellama, etc.).
LLM findings flow through the same pipeline as built-in checker findings: they are filtered by --severity, rendered in text/JSON format, and cross-referenced by the correlator for compound threat detection.
CI Integration
# Fail CI if any high/critical findings
invowk audit --severity high --format json
# Include LLM analysis in CI (requires LLM server accessible from CI runner)
invowk audit --llm --severity high --format json
# Parse JSON output without masking audit's exit code 1 for findings
set +e
invowk audit --format json > audit-results.json
status=$?
set -e
jq '.summary' audit-results.json
exit "$status"
The JSON output structure:
{
"findings": [
{
"code": "script-execution-script-downloads-and-executes-remote-code",
"severity": "critical",
"category": "execution",
"surface_id": "tools.invowkmod",
"surface_kind": "local_module",
"checker_name": "script",
"file_path": "tools.invowkmod/invowkfile.cue",
"line": 15,
"title": "Script downloads and executes remote code",
"description": "Command \"bootstrap\" contains a remote code download and execution pattern (pipe, process substitution, or download-then-execute)",
"recommendation": "Download to a temporary file, verify its checksum, then execute"
}
],
"summary": {
"total": 1,
"suppressed": 0,
"critical": 1,
"high": 0,
"medium": 0,
"low": 0,
"info": 0,
"modules_scanned": 2,
"invowkfiles_scanned": 1,
"scripts_scanned": 5,
"duration_ms": 42
}
}
Project Structure
invowk/
├── main.go # Entry point
├── cmd/invowk/ # CLI commands (Cobra command tree)
│ ├── root.go # Root command and global flags
│ ├── app.go # CLI adapter: bridges CommandService to Cobra
│ ├── cmd.go # cmd subcommand (command execution)
│ ├── cmd_discovery.go # Dynamic command registration and discovery
│ ├── cmd_dryrun.go # Dry-run output rendering
│ ├── cmd_execute_helpers.go # Execution helpers (runtime registry, disambiguation)
│ ├── cmd_render.go # Command output rendering
│ ├── cmd_watch.go # File-watching mode (--ivk-watch)
│ ├── service_error.go # ServiceError type and rendering
│ ├── module.go # module subcommand tree
│ ├── init.go # init command
│ ├── config.go # config commands
│ ├── validate.go # validate command
│ ├── agent.go # LLM-assisted command authoring helpers
│ ├── audit.go # audit command (security scanning + LLM analysis)
│ ├── completion.go # Shell completion generation
│ ├── tui.go # tui parent command
│ ├── tui_*.go # TUI subcommands (input, write, choose, confirm, filter, file, table, spin, pager, format, style)
│ └── internal.go # Hidden internal commands
├── internal/
│ ├── app/ # Hexagonal domain layer
│ │ ├── commandadapters/ # Application adapters for discovery and dependency services
│ │ ├── commandsvc/ # Command execution service (discovery, validation, dispatch)
│ │ ├── deps/ # Dependency validation domain logic
│ │ ├── execute/ # Execution orchestration (runtime resolution, context construction)
│ │ ├── llmconfig/ # Shared LLM configuration resolution
│ │ ├── modulecache/ # Module cache domain service
│ │ ├── moduleops/ # Module create/import/archive/vendor operations
│ │ └── modulesync/ # Module sync and tidy orchestration
│ ├── benchmark/ # Benchmarks for PGO profile generation
│ ├── config/ # Configuration management with CUE schema
│ ├── container/ # Container engine abstraction (Docker, Podman, sandbox)
│ ├── containerplan/ # Pure container execution policy and target planning
│ ├── core/serverbase/ # Shared server state machine base
│ ├── discovery/ # Invowkfile and module discovery
│ ├── audit/ # Security scanning (7 checkers + LLM + correlator)
│ ├── auditllm/ # LLM provider adapters for audit analysis
│ ├── agentcmd/ # LLM-assisted custom-command authoring pipeline
│ ├── llm/ # Shared LLM completion interface and adapters
│ ├── issue/ # Error types and ActionableError
│ ├── provision/ # Container provisioning (ephemeral layer attachment)
│ ├── runtime/ # Runtime implementations (native, virtual-sh, virtual-lua, container)
│ ├── sshserver/ # SSH server for host access from containers
│ ├── testutil/ # Test utilities
│ ├── tui/ # TUI component library and interactive execution
│ ├── tuiwire/ # Shared TUI wire protocol types
│ ├── tuiserver/ # TUI server for interactive sessions
│ ├── uroot/ # u-root utilities for virtual shell built-ins
│ └── watch/ # File-watching with debounced re-execution
├── pkg/
│ ├── containerargs/ # Shared Docker/Podman argument validation
│ ├── cueutil/ # Shared CUE parsing utilities
│ ├── fspath/ # Filesystem path wrappers (typed paths)
│ ├── invowkmod/ # Module validation and structure
│ ├── invowkfile/ # Invowkfile parsing and validation
│ ├── platform/ # Cross-platform utilities
│ └── types/ # DDD value-type catalog (shared typed primitives)
├── tests/cli/ # CLI integration tests (testscript .txtar files)
├── samples/invowkmods/ # Sample invowk modules and audit fixtures
├── scripts/ # Build, install, and release scripts
├── specs/ # Feature specifications and research
├── tasks/ # Pending analysis documents and planning notes
├── tools/ # Development tools (separate Go modules)
│ └── goplint/ # Custom go/analysis analyzer for DDD value type enforcement
├── docs/ # Architecture diagrams and design docs
├── examples/ # Example invowkfiles and modules
├── vhs/ # VHS tape files for terminal demo recordings
├── Dockerfile # Container image for development
└── website/ # Docusaurus documentation site
Dependencies
Invowk requires Go 1.26+. Exact direct and transitive dependency versions are pinned in go.mod; the list below calls out the main direct dependencies by role.
Core:
- Cobra - CLI framework
- CUE - Configuration language
- mvdan/sh - Virtual shell interpreter
- Fang - Signal handling and graceful shutdown
TUI & Styling:
- Lip Gloss - Terminal styling
- Glamour - Markdown rendering
- Bubbles - TUI components
- Bubbletea - TUI framework
SSH & PTY:
- Wish - SSH server framework (for host SSH access from containers)
- Charmbracelet SSH - SSH transport layer
- creack/pty - PTY handling
Module Dependencies:
- go-git - Git operations for remote module resolution
File Watching:
- fsnotify - Cross-platform file system notifications for
--ivk-watchmode
Security:
- openai-go - OpenAI API client for
invowk audit --llmLLM-powered analysis
Virtual Shell:
- u-root - Core utilities for virtual shell built-ins (28 utilities: cat, cp, ls, grep, sort, tar, seq, etc.)
Performance and PGO
Invowk ships with a committed default.pgo profile for Go Profile-Guided Optimization.
Go automatically applies this profile during builds when it is present in the repository root.
make pgo-profile # Full profile (includes container benchmarks)
make pgo-profile-short # Short profile (skips container benchmarks)
make pgo-profile-parse-discovery # Focused profile for CUE/invowkfile/invowkmod/discovery hot paths
make pgo-audit # Validate profile freshness + required hot-path symbols
PGO profile generation runs benchmark training with -pgo=off to avoid bias from
a previously committed profile.
Regenerate default.pgo when:
- CUE parsing or schema decode hot paths change (
pkg/cueutil,pkg/invowkfile,pkg/invowkmod) - Discovery behavior changes (
internal/discovery) - Runtime execution paths materially change
- Preparing a major release
Local SonarCloud Status Check
Invowk includes a local Sonar workflow that fetches quality gate status and unresolved issues from SonarCloud via REST API, using results from SonarCloud's Automatic Analysis (GitHub App).
# Fetch quality gate + unresolved issues (no auth needed for public projects)
make sonar-local
# Optional: set token for private projects or higher rate limits
export SONAR_TOKEN=your_token
# Optional overrides (defaults shown)
export SONAR_HOST_URL=https://sonarcloud.io
export SONAR_PROJECT_KEY=invowk_invowk
The command writes reports to .sonar/reports/ (quality-gate.json, issues.json)
and fails if the Sonar quality gate is red for the analyzed branch or if
SonarCloud reports unresolved issues.
When pre-commit hooks are installed, the sonar-local hook runs on changes to
Sonar configuration files and blocks the commit on quality gate failures.
Contributing
See CONTRIBUTING.md for how to participate.
License
This project is licensed under the Mozilla Public License 2.0 (MPL-2.0) - see the LICENSE file for details.
SPDX-License-Identifier: MPL-2.0
Trademark
invowk™ is a trademark of Danilo Cominotti Marques. See TRADEMARK.md for usage guidelines.