Skip to content

Commands

This document defines the command vocabulary and execution model for Structyl.

Command Line Interface

Usage: structyl <command> <target> [args] [--docker]
       structyl <meta-command> [args] [--docker]
       structyl -h | --help | --version

Standard Commands

These commands form the standard vocabulary. Toolchains provide default implementations for each.

CommandPurposeIdempotentMutates
cleanRemove build artifactsYesYes
restoreInstall/restore dependenciesYesYes
checkStatic analysis, lint, format verificationYesNo
lintLinting onlyYesNo
formatAuto-fix formattingNoYes
format-checkVerify formatting (read-only)YesNo
buildCompile/build the projectNoYes
testRun unit testsNoNo
benchRun benchmarksNoNo
demoRun example/demo codeNoNo
packCreate distributable packageNoYes
docGenerate documentationNoYes

Command Semantics

clean

Removes all build artifacts and caches. After clean, a fresh build MUST produce semantically equivalent artifacts to a clean checkout. Byte-level identity is not required—file timestamps, embedded build IDs, and other non-functional metadata may differ.

bash
structyl clean cs

restore

Downloads and installs dependencies. MUST be idempotent given unchanged lock files—running twice on the same lock file state MUST have no additional effect. If lock files change between runs (e.g., due to manual edits or npm update), behavior is implementation-defined.

bash
structyl restore py  # uv sync
structyl restore ts  # pnpm install --frozen-lockfile

check

Runs all read-only validation commands. The exact composition is toolchain-specific.

Contract:

  • MUST NOT modify files
  • MUST NOT run tests
  • MAY include: lint, format-check, typecheck, vet
  • MUST exit with code 0 if all checks pass, non-zero otherwise
bash
structyl check rs  # → lint, format-check
structyl check py  # → lint, typecheck
structyl check go  # → lint, vet

See toolchains.md for each toolchain's check composition.

lint

Runs linting tools only:

bash
structyl lint rs   # cargo clippy -- -D warnings
structyl lint py   # ruff check .

format

Auto-fixes formatting issues. This command mutates files.

bash
structyl format go  # go fmt ./...

format-check

Verifies formatting without modifying files:

bash
structyl format-check rs  # cargo fmt --check

build

Compiles the project. Use build:release variant for optimized builds.

bash
structyl build rs          # cargo build
structyl build:release rs  # cargo build --release

test

Runs the test suite, including reference tests from tests/.

bash
structyl test py  # pytest

bench

Runs performance benchmarks.

bash
structyl bench go  # go test -bench=. ./...

demo

Executes demonstration code to verify the library works.

bash
structyl demo cs  # dotnet run --project Demo

pack

Creates a distributable package artifact.

bash
structyl pack cs  # dotnet pack
structyl pack ts  # pnpm pack

doc

Generates language-specific documentation (API docs, man pages) for a single target.

bash
structyl doc rs  # cargo doc --no-deps
structyl doc go  # go doc ./...

This is distinct from docs generate (see below).

doc vs docs generate

CommandScopeOutputPurpose
structyl doc <target>Single targetAPI documentationGenerate target-specific documentation (rustdoc, godoc, javadoc)
structyl docs generateAll language targetsREADME filesGenerate README.md files from templates

The doc command invokes toolchain-specific documentation generators. The docs generate utility command generates README files from the template system defined in documentation.md.

Meta Commands

These commands operate across all targets.

CommandDescription
buildBuild all targets (respects dependencies)
build:releaseBuild all targets with release optimization
testRun tests for all language targets
cleanClean all targets
restoreRun restore for all targets
checkRun check for all targets
ciRun full CI pipeline (see ci-integration.md)
version <subcommand>Version management (see version-management.md)

Utility Commands

CommandDescription
targetsList all configured targets (see targets.md)
release <version>Set version, commit, and tag (see version-management.md)
upgrade [version]Manage pinned CLI version (see version-management.md)
docs generateGenerate README files from templates (see documentation.md)
config validateValidate configuration without running commands
docker-build [targets]Build Docker images (see docker.md)
docker-cleanRemove Docker containers, images, and volumes

Global Flags

FlagDescription
--dockerRun command in Docker container
--no-dockerDisable Docker mode (overrides STRUCTYL_DOCKER env var)
--continueContinue on error (don't fail-fast)
--type=<type>Filter targets by type (language or auxiliary)
-h, --helpShow help message
--versionShow Structyl version

Null Commands

A command value of null indicates the command is not available for this target. Toolchains use null for commands that don't apply to their ecosystem.

json
{
  "targets": {
    "go": {
      "toolchain": "go",
      "commands": {
        "pack": null
      }
    }
  }
}

Behavior When Invoked

ConditionBehavior
Explicitly set to nullExit code 0 (no-op), warning: [{target}] command "{cmd}" is not available
Not defined and no toolchainExit code 1, error: [{target}] command "{cmd}" not defined

A null command is a deliberate "not applicable" marker. This differs from an undefined command, which is an error.

Use Cases

  • Override a toolchain command to disable it: "bench": null
  • Indicate a command doesn't apply to an ecosystem (Go has no pack equivalent)
  • Prevent accidental execution of inapplicable commands

Command Definition

Commands are defined declaratively in .structyl/config.json. There are three ways to define commands:

1. Toolchain Defaults

Specify a toolchain to inherit all standard commands:

json
{
  "targets": {
    "rs": {
      "toolchain": "cargo"
    }
  }
}

This provides clean, restore, build, test, check, lint, format, format-check, bench, pack, and doc commands automatically. See toolchains.md for all available toolchains.

2. Command Override

Override specific commands while inheriting others from the toolchain:

json
{
  "targets": {
    "cs": {
      "toolchain": "dotnet",
      "commands": {
        "test": "dotnet run --project Pragmastat.Tests",
        "demo": "dotnet run --project Pragmastat.Demo"
      }
    }
  }
}

3. Explicit Commands

For targets without a toolchain, define all commands explicitly:

json
{
  "targets": {
    "img": {
      "type": "auxiliary",
      "commands": {
        "build": "python scripts/generate_images.py",
        "clean": "rm -rf output/images"
      }
    }
  }
}

Command Composition

Commands can reference other commands using arrays:

json
{
  "commands": {
    "lint": "cargo clippy -- -D warnings",
    "format-check": "cargo fmt --check",
    "check": ["lint", "format-check"],
    "ci": ["clean", "restore", "check", "build", "test"]
  }
}

Array elements execute sequentially with fail-fast behavior.

Resolution Rules

When resolving an array element:

Element PatternResolution
Starts with $ Shell command (prefix stripped)
Matches defined command nameReference to that command
Contains whitespaceShell command
Single word, no matchShell command

Examples:

json
{
  "commands": {
    "lint": "cargo clippy",
    "format": "cargo fmt",

    "check": [
      "lint",              // reference → "cargo clippy"
      "format-check",      // reference → "cargo fmt --check"
      "$ lint",            // shell → execute /usr/bin/lint
      "cargo doc --test"   // shell (contains space)
    ]
  }
}

The $ prefix is an escape hatch for when a shell command name conflicts with a defined command.

Command Variants

Related commands are grouped using a colon (:) naming convention. The colon is part of the command name, not special syntax.

json
{
  "commands": {
    "build": "cargo build",
    "build:release": "cargo build --release",
    "test": "cargo test",
    "test:unit": "cargo test --lib",
    "test:integration": "cargo test --test '*'"
  }
}

Invocation:

bash
structyl build rs          # runs "build"
structyl build:release rs  # runs "build:release"
structyl test:unit rs      # runs "test:unit"

Standard Variants

Toolchains provide common variants. Override specific variants as needed:

json
{
  "targets": {
    "rs": {
      "toolchain": "cargo",
      "commands": {
        "test:integration": "cargo test --test '*' -- --test-threads=1"
      }
    }
  }
}

Composite Commands with Variants

Arrays can reference any command including variants:

json
{
  "commands": {
    "ci": ["check", "test:unit", "test:integration", "build:release", "pack"]
  }
}

Command Execution

When you run structyl <command> <target>:

  1. Load .structyl/config.json
  2. Find target configuration
  3. Resolve command:
    • Check target's commands overrides
    • Fall back to toolchain defaults
    • Error if command not found
  4. If command is an array, resolve each element recursively
  5. Execute shell command(s) in target directory

Working Directory

Commands execute in the target directory by default. Override with cwd:

json
{
  "targets": {
    "rs": {
      "toolchain": "cargo",
      "cwd": "rs/pragmastat"
    }
  }
}

Or per-command:

json
{
  "commands": {
    "build": {
      "run": "cargo build",
      "cwd": "rs/pragmastat"
    }
  }
}

Environment Variables

Target-level environment:

json
{
  "targets": {
    "py": {
      "toolchain": "python",
      "env": {
        "PYTHONPATH": "${root}/py"
      }
    }
  }
}

Per-command environment:

json
{
  "commands": {
    "test": {
      "run": "pytest",
      "env": {
        "PYTEST_TIMEOUT": "30"
      }
    }
  }
}

Command Object Validation

When a command is defined as an object, the following validation rules apply:

ConditionRequirement
run without unix/windowsValid—run is the cross-platform command
unix and windows without runValid—platform-specific commands
run with unix or windowsError—mutually exclusive
Neither run nor unix/windowsError if object has no executable command

Validation error:

target "{name}": command "{cmd}": cannot specify both "run" and platform-specific commands ("unix"/"windows")

Exit code: 2

Valid combinations:

json
{
  "commands": {
    "build": {"run": "make"},
    "deploy": {"unix": "deploy.sh", "windows": "deploy.ps1"},
    "test": {"run": "pytest", "cwd": "tests", "env": {"CI": "1"}}
  }
}

Invalid:

json
{
  "commands": {
    "build": {"run": "make", "unix": "make", "windows": "nmake"}
  }
}

Variables

Commands support variable interpolation:

VariableDescription
${target}Target slug (e.g., cs, py)
${target_dir}Target directory path
${root}Project root directory
${version}Project version from VERSION file

Custom variables via vars:

json
{
  "targets": {
    "cs": {
      "toolchain": "dotnet",
      "vars": {
        "test_project": "Pragmastat.Tests"
      },
      "commands": {
        "test": "dotnet run --project ${test_project}"
      }
    }
  }
}

Escaping Variable Syntax

To include a literal ${ in a command, use $${:

InputOutput
${version}Replaced with version value
$${version}Literal string ${version}
$$${version}Literal $ followed by version value

Example:

json
{
  "commands": {
    "echo-var": "echo 'Version is $${version}' && echo 'Actual: ${version}'"
  }
}

Output: Version is ${version} followed by Actual: 1.2.3

Argument Forwarding

Arguments after the command are appended to the shell command:

bash
structyl test cs --filter=Unit
# Executes: dotnet run --project Pragmastat.Tests --filter=Unit

Use -- to separate Structyl flags from command arguments:

bash
structyl build cs -- --help
# Executes: dotnet build --help

Exit Codes

See error-handling.md for exit code definitions.

Configuration Errors (Exit Code 2)

ErrorMessage
Unknown toolchaintarget "{name}": unknown toolchain "{toolchain}"
Undefined command referencetarget "{name}": command "{cmd}" references undefined command "{ref}"
Circular command referencetarget "{name}": circular command reference: {cycle}
Invalid variabletarget "{name}": unknown variable "{var}" in command "{cmd}"

Runtime Errors (Exit Code 1)

ErrorMessage
Command failed[{target}] {command} failed with exit code {code}
Command not found[{target}] command "{cmd}" not defined

Released under the MIT License.