Agent Skill
2/7/2026

speckitconstitution

Create or update the project constitution from interactive or provided principle inputs, ensuring all dependent templates stay in sync.

I
invowk
0GitHub Stars
1Views
npx skills add invowk/invowk

SKILL.md

Namespeckitconstitution
DescriptionCreate 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.cue files 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:

    1. Current directory invowkfile.cue (highest priority)
    2. Sibling *.invowkmod modules in current directory
    3. Configured includes (module paths from config)
    4. ~/.invowk/cmds/ (modules only, non-recursive)
  • Transparent Namespace: Commands from different sources use simple names when unique. When command names conflict across sources, use @<source> prefix or --ivk-from flag 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 prompt and invowk agent cmd create to give agents the current schemas or generate validated custom commands with the same LLM providers as invowk 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 | sh
    

    Pin 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 | iex
    

    Pin 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

MethodLinuxmacOSWindows
Shell scriptamd64, arm64amd64 (Intel), arm64 (Apple Silicon)
PowerShell scriptamd64
Homebrewamd64, arm64amd64, arm64
WinGetamd64
Go installallallall
From sourceallallall

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

  1. Create an invowkfile in your project directory:
invowk init
  1. 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)
  1. Run a command:
invowk cmd hello
# Output: Hello, World!
  1. Pass an argument:
invowk cmd hello Alice
# Output: Hello, Alice!
  1. 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:

FieldDescription
default_shellOverride the default shell for native runtime (e.g., /bin/bash, pwsh)
workdirDefault working directory for all commands (overridable per command/implementation)
envGlobal environment config with files (dotenv loading) and vars (key-value pairs)
depends_onGlobal 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.PATCH with optional pre-release label
  • No v prefix (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"
  • description summarizes the module's purpose.
  • author identifies the maintainer or organization.
  • license should use an SPDX identifier such as MIT, Apache-2.0, or MPL-2.0.
  • repository must use an accepted Git URL scheme: https://, git@, or ssh://.

How Multi-Source Discovery Works

When you run invowk cmd in a directory, invowk discovers commands from 4 sources in priority order:

  1. Root invowkfile: invowkfile.cue in the current directory (highest priority)
  2. Sibling modules: All *.invowkmod directories at the same level (not their dependencies)
  3. Configured includes: Module paths listed in includes in your config file
  4. 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

  1. Simple by default: Use short command names when possible
  2. Explicit when needed: Disambiguation syntax is clear and consistent
  3. Clear provenance: Listing shows source for each command
  4. 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 build or test 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 readable
  • writable (optional): Check if path is writable
  • executable (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 check
    • validation (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 env construct

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:

CapabilityDescription
local-area-networkChecks for LAN connectivity
internetChecks for internet connectivity
containersChecks that Docker or Podman is installed and responding
ttyChecks 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:

PropertyRequiredDescription
nameYesIdentifier for error messages
script.contentYesInline script content to execute for validation
script.fileModule onlyScript file contained in the source invowkmod
expected_codeNoExpected exit code (default: 0)
expected_outputNoRegex 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

PropertyRequiredDescription
nameYesFlag name (POSIX-compliant: starts with letter, alphanumeric/hyphen/underscore)
descriptionYesDescription shown in help text
default_valueNoDefault value if flag is not provided (cannot be used with required)
typeNoData type: string (default), bool, int, or float
requiredNoIf true, the flag must be provided (cannot have default_value)
shortNoSingle-letter alias (e.g., v for -v shorthand)
validationNoRegex pattern to validate flag values

Reserved prefixes: The ivk-, invowk-, and i- prefixes are reserved for system flags. Additionally, help and version are 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 true or false are 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 NameEnvironment Variable
envINVOWK_FLAG_ENV
dry-runINVOWK_FLAG_DRY_RUN
output-fileINVOWK_FLAG_OUTPUT_FILE
retry-countINVOWK_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:

VariableDescriptionAlways Set
INVOWK_CMD_NAMECurrent command nameYes
INVOWK_RUNTIMEResolved runtime name (native, virtual-sh, virtual-lua, container)Yes
INVOWK_SOURCESource origin (invowkfile for root commands, module name for module commands)Yes
INVOWK_PLATFORMResolved 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:

  1. 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., docker is installed on the host).

  2. Container runtime dependencies (depends_on inside 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-level depends_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

PropertyRequiredDescription
nameYesArgument name (starts with a letter, then letters, digits, underscores, or hyphens)
descriptionYesDescription shown in help text
requiredNoIf true, the argument must be provided (cannot have default_value)
default_valueNoDefault value if argument is not provided
typeNoData type: string (default), int, or float
validationNoRegex pattern to validate argument values
variadicNoIf 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 NameEnvironment Variable
envINVOWK_ARG_ENV
replica-countINVOWK_ARG_REPLICA_COUNT
output-fileINVOWK_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:

ShellPositional AccessNotes
bash, sh, zsh$1, $2, $@, $#Standard POSIX syntax
PowerShell$args[0], $args[1]Zero-indexed array
cmd.exeN/AUse 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 CaseRecommended Method
Simple scriptsPositional parameters ($1, $2)
Complex arg handlingEnvironment variables (INVOWK_ARG_*)
Cross-platform scriptsEnvironment variables
Variadic argumentsBoth work (env vars provide _COUNT and indexed access)
PowerShell scriptsEither $args[0] or environment variables
cmd.exe scriptsEnvironment 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 values
  • INVOWK_FLAG_* - Flag values
  • ARGC, ARG1, ARG2, etc. - Positional argument shorthand variables (injected alongside INVOWK_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 env construct 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

ValueDescription
linuxLinux operating systems
macosmacOS (Darwin)
windowsWindows

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 with python3
  • #!/usr/bin/env -S python3 -u → runs with python3 -u (unbuffered)
  • #!/usr/bin/perl -w → runs with perl -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.cue at the root (module metadata and dependencies)
  • Optional invowkfile.cue at 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.invowkmod
  • com.example.mytools.invowkmod
  • org.company.project.invowkmod
  • Utils.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.sh is 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

  1. Portability: Share a complete command set as a single folder
  2. Self-contained: Module metadata and scripts travel together
  3. Cross-platform: Forward slash paths work on all operating systems
  4. Namespace isolation: RDNS naming prevents conflicts between modules
  5. Validation: Built-in validation ensures module integrity

Using Modules

Modules are automatically discovered and loaded from all configured sources:

  1. Current directory invowkfile.cue (highest priority)
  2. Sibling *.invowkmod modules in current directory
  3. Configured includes (module paths from config)
  4. ~/.invowk/cmds/ (modules only, non-recursive)

When invowk discovers a module, it:

  • Validates the module structure and naming
  • Loads invowkfile.cue from within the module (if present)
  • Resolves script paths relative to the module root
  • Makes all commands available (use @source prefix or --ivk-from for 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 root invowkmod.cue. Use invowk module tidy to auto-add missing transitive deps, or invowk module sync to detect missing ones with actionable error messages.

Version Constraints

FormatDescriptionExample
^1.2.0Compatible with 1.x.x (>=1.2.0 <2.0.0)Major version locked
~1.2.0Approximately 1.2.x (>=1.2.0 <1.3.0)Minor version locked
>=1.0.0Greater than or equalMinimum version
<2.0.0Less thanMaximum version
1.2.3Exact versionPinned 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_TOKEN for GitHub
    • GITLAB_TOKEN for GitLab
    • GIT_TOKEN for 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 vendor resolves dependencies and copies them into invowk_modules/. If invowkmod.lock.cue exists, locked versions are used by default; use --update to force re-resolution and --prune to 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 to bashsh
  • Windows: Uses pwshpowershellcmd
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/sh inside 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.cue when 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

CodeMeaning
0No findings at or above the severity threshold
1Findings detected
2Scan error (e.g., path not found, parse failure)

What Gets Scanned

The audit scanner runs 7 built-in security checkers concurrently:

CheckerCategoryWhat It Detects
Scriptexecution, path-traversal, obfuscationRemote code execution (curl | bash), path traversal (../), base64 obfuscation, eval patterns, hex sequences
Luaexecution, exfiltration, path-traversalDisabled virtual-lua API references, sensitive environment reads, wildcard or network-capable host binary allowances, broad virtual filesystem exposure
NetworkexfiltrationReverse shells, DNS exfiltration, encoded URLs, suspicious network commands
EnvironmentexfiltrationRisky 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 FileintegrityHash mismatches, orphaned/missing entries, ambiguous versions, tamper detection
Symlinkpath-traversalAny symlink in a module directory, symlinks pointing outside module boundaries, symlink chains, dangling or unreadable symlinks, incomplete directory walks
Module MetadatatrustTyposquatting 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 ThreatTriggerEscalated Severity
Credential exfiltrationenv access + network accessCritical
Path + symlink escapepath traversal + external symlinkCritical
Obfuscated exfiltrationobfuscation + network accessCritical
Trust chain weaknessmetadata issues + lock file issuesHigh
Interpreter traversalunusual interpreter + path traversalCritical

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:

FlagDefaultDescription
--llm-providerAuto-detect or use specific provider: auto, claude, codex, gemini, ollama
--llmfalseEnable LLM using configured or OpenAI-compatible API settings
--llm-urlhttp://localhost:11434/v1OpenAI-compatible API base URL (env: INVOWK_LLM_URL)
--llm-modelqwen2.5-coder:7b for API/Ollama; CLI provider default when omittedModel 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-timeout2mPer-request timeout (env: INVOWK_LLM_TIMEOUT)
--llm-concurrency2Max 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:

ServerDefault PortInstall
Ollama11434Install from your package manager or ollama.com, then run ollama pull qwen2.5-coder:7b
LM Studio1234Download from website, load a model
llamafile8080Download single executable, run
vLLM8000pip install vllm

Recommended models for security analysis:

ModelRAM RequiredQuality
qwen2.5-coder:7b8 GBGood (default)
qwen2.5-coder:14b16 GBBetter
qwen2.5-coder:32b24 GBBest (GPT-4o level for code)
deepseek-coder:33b24 GBExcellent 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:

SSH & PTY:

Module Dependencies:

  • go-git - Git operations for remote module resolution

File Watching:

  • fsnotify - Cross-platform file system notifications for --ivk-watch mode

Security:

  • openai-go - OpenAI API client for invowk audit --llm LLM-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.

Skills Info
Original Name:speckitconstitutionAuthor:invowk