CLI Reference

The sigil command-line tool provides all development and runtime functionality.

Running Programs

Running Scripts

sigil <filepath> [args…] runs in scripting mode: it loads the scripting prelude, exposes script arguments, and prints only explicit output. Executable files using #!/usr/bin/env sigil follow the same path. See Shell Scripting for the preloaded APIs and short names.

Uncaught errors are printed to stderr with concise details and a source location when available. Use sigil --error-history script.sgl to include expanded call history. Place this option before the script filename; later arguments belong to the script.

Evaluating Files

sigil eval -f <file.sgl>

Evaluates the specified Sigil source file with ordinary explicit imports, without the scripting prelude or script-argument context.

Example:

sigil eval -f hello.sgl

Evaluating Expressions

sigil eval '<expression>'

Evaluates a Sigil expression and prints the result. --quiet suppresses that result, while --error-history expands error diagnostics. Both options also work with eval -f. Errors still exit nonzero. Captured call history can include completed calls and macro expansion rather than a live stack.

Examples:

sigil eval '(+ 1 2 3)'
# Output: 6

sigil eval --quiet '(println "Hello!")'
# Output: Hello!

Adding Library Paths

Use -L to add directories to the library search path:

sigil eval -L lib -f main.sgl

Interactive REPL

sigil repl

Starts an interactive Read-Eval-Print Loop. Type expressions to evaluate them. Press Ctrl+D or type ,q to exit.

Building Projects

sigil build

Build a package and its dependencies:

sigil build                        # Build all workspace packages
sigil build sigil-json             # Build specific package with deps
sigil build sigil-cli --config release  # Build with specific config

Configurations:

  • dev — Development build (default)
  • debug — Debug build with symbols
  • release — Optimized release build with modules bundled into a self-contained executable

Running Your Application

sigil run

Build and run your application:

sigil run                          # Build dev config and run entry point
sigil run --config release         # Build release config and run
sigil run a --dry-run -t x         # Everything after run's own options goes to the program

The sigil run command automatically builds any out-of-date modules before running the application's entry point (defined by entry: in package.sgl).

-t / --task still runs a task instead of the entry point, and prints Deprecated: use 'sigil task' instead; it is removed before 1.0. Use `sigil task`. Until then, sigil run -t <task> a b gives a step task its arguments through (task-arguments) from (sigil build).

sigil run's own options (-t, --config, --redirects, -v, --profile) come first. The first argument that is not one of them, and everything after it, goes to the program unchanged, however much it looks like an option: sigil run app --dry-run -t x passes app --dry-run -t x. Use -- to pass an argument that starts with - first: sigil run -- --help. The entry point reads its arguments from (command-line), as the built program would.

Tasks

sigil task

Run a named job of the project, or one a dependency provides:

sigil task                         # List tasks (also: sigil task --list)
sigil task build                   # A step task of this project
sigil task stage-news site/        # A script task of this project, with arguments
sigil task substratic:publish-web build/web --dry-run   # A dependency's script task
sigil task substratic:            # List one dependency's tasks
sgx substratic:publish-web build/web --dry-run          # The same as sigil task ...

A task is one of two kinds:

  • A step task is a build pipeline declared in tasks: (build, test and clean exist by default). It runs in-process over the build context and takes no arguments: sigil task build extra is refused. --config and -v apply to it.
  • A script task is a Sigil shell script declared under provides: script-tasks: (below). Every argument after its name reaches the script unchanged, options included: sigil task t -v -- -L x gives the script ("-v" "--" "-L" "x").

sigil task's own options (--list, --names, -c/--config, -v, -h) come before the task name; parsing stops at the name.

Resolution. A bare name is always one of this project's own tasks: its step tasks (for a workspace of several packages, its workspace-level tasks) and its own script tasks. A dependency's tasks are reached only with its package prefix, <pkg>:<name>, where <pkg> is the dependency's name:. Only the project's direct dependencies qualify (listed in dependencies: or dev-dependencies:), and only their script tasks: a dependency's step tasks are its own build. A dependency can therefore never override one of the project's tasks or stand in for a missing one. An unknown bare name is an error that names any dependency providing a task of that name (Did you mean substratic:publish-web?) and runs nothing. Nothing is fetched: a dependency must be installed, and sigil task refuses missing or stale dependencies as sigil run does. Redirects apply: under SIGIL_REDIRECTS or a leading sigil --redirects F, a redirected dependency's tasks come from the redirect target.

Script tasks never run implicitly. sigil build, sigil test, dependency builds and sigil deps install never run one; a script task runs only when someone names it.

Running a script task. It runs through the same door as sigil <file>: the shell prelude, (script-args), the caller's working directory. (script-dir) is the task file's directory, and (project-root) is the root of the project sigil task ran in (for a dependency's task, the consuming project). The task file's directory, and any load-path: directories, go first on the library path for that run, so a task can keep its library modules beside it (tasks/substratic/tasks/manifest.sgl as (substratic tasks manifest)). Before it runs, one line on stderr names its source:

sigil task: substratic:publish-web (substratic 0.3.0 @ 1a2b3c4d)

@ is followed by the locked commit (git), sha256: and the content address (registry), path DIR (path dependency), or redirected to DIR. The script's exit status is sigil task's.

Declaring script tasks. Script tasks are quoted data under provides: in package.sgl, so a package can declare them without requiring a newer Sigil:

provides: (list
  script-tasks: '((publish-web
                   file: "tasks/publish-web.sgl"
                   description: "Publish a checked site tree"
                   usage: "DIR [--preview | --dry-run]")
                  (stage-web
                   file: "tasks/stage-web.sgl"
                   description: "Stage the deploy tree")))
Key
namea symbol matching [a-z0-9-]+, not starting with -; not build, test or clean
file:required; relative to the package root, no .., and it must not resolve outside the package (a symlink out is refused when the task runs)
description:required; one line
usage:optional; one line, shown by --list
load-path:optional; a list of package-relative directories added to the library path for the run

Any other key is an error. An invalid declaration is refused with exit status 78 and a message naming the package, the task and the key. One name may not be both a step task and a script task of the same package. tasks/ is the convention for task files, not a rule; keeping them outside any directory a build task compiles (such as src/) keeps them out of every build.

Listing. sigil task --list prints each task's name, kind (steps or script), source and description, the project's first, then each dependency's with its prefix. --names prints only the names, one per line.

sgx

sgx is sigil task under a short name. sigil cli install, sigil cli use and sigil cli upgrade write $SIGIL_HOME/bin/sgx, an sh wrapper that runs the active version through the sigil wrapper:

#!/bin/sh
# sigil-managed: sgx (task runner)
SIGIL_HOME="${SIGIL_HOME:-$HOME/.sigil}"
exec "$SIGIL_HOME/bin/sigil" task "$@"

An existing bin/sgx without that marker line is left alone. A sigil binary started under the name sgx (a link or a copy) also behaves as sigil task.

The dependency index and sigil deps verify

sigil deps install and sigil deps update write .sigil/deps.index: for each entry in .sigil/deps, what its package.sgl declares (name, version, sigil: floor, script tasks, step task names, provides: keys, source directories), the sha256 of that package.sgl, and for a dependency in the shared store ($SIGIL_HOME/deps) the sha256 of its whole tree. sigil task reads dependencies' tasks from it, and every command's startup reads their source directories from it, instead of evaluating each dependency's package.sgl again. (A path dependency's package.sgl is still read by the missing-dependency check.)

The index is keyed by content, never by modification time. It is used only while sigil.lock hashes the same and the running sigil is the one that wrote it; otherwise running or listing a dependency's tasks refuses with exit status 78 and asks for sigil deps install, and startup reads package.sgl files as before. A path dependency or a workspace member changes without a reinstall, so its entry is used only while its package.sgl still hashes the same; a redirected dependency is always read from its redirect target.

sigil deps verify      # re-hash every store tree against the index

sigil deps verify re-hashes each store tree against what was recorded at install, and for a git checkout also asks git whether any tracked file differs from the pinned commit (which catches a change made before the install, when the recorded hash already includes it). It exits 0 when every store tree matches, 1 when one changed, differs from its commit or is missing, and 78 without a usable index. Path dependencies and workspace members are listed as skipped.

The index records absolute paths, so a project that was moved or copied does not trust it (dependency tasks ask for sigil deps install; startup reads package.sgl files as before).

sigil completions

eval "$(sigil completions bash)"   # in ~/.bashrc
eval "$(sigil completions zsh)"    # in ~/.zshrc, after compinit

Completes sigil's subcommands, and task names for sigil task and sgx (including pkg:name), from sigil task --names in the current project.

Testing

sigil test

Run project tests:

sigil test
sigil test <pattern>  # Run matching tests

Other Commands

sigil repl

Start an interactive REPL:

sigil repl

sigil format

Format Sigil source files:

sigil format <file.sgl>
sigil format --check <file.sgl>  # Check without modifying

sigil docs

Documentation tools:

sigil docs build <source> <output>
sigil docs serve <output>

sigil cli

CLI management commands:

sigil cli install     # Install sigil to ~/.sigil
sigil cli upgrade     # Download latest version
sigil cli use <ver>   # Switch to a specific version
sigil cli versions    # List installed versions
sigil cli prune       # Remove old versions
sigil cli env         # Print shell configuration

upgrade and use read the release list from the Sigil package registry (https://pkg.usesigil.org, or the URL in SIGIL_REGISTRY) and download the binary by its content hash. Before anything is installed, sigil checks the whole chain itself: the registry's key list must be signed by the root key built into this sigil, the release list must be signed by a toolchain key from that list, and the downloaded binary must have the sha256 and size the release list gives. No minisign program is needed.

  • If the registry cannot be reached, or does not have the release list, sigil prints Registry unavailable, using Codeberg. and uses Codeberg's release metadata instead, which is checked by HTTPS and its sha256 list. If the registry's signed release list did arrive but the binary itself was not served, the Codeberg download must have exactly the sha256 and size the signed list gives.
  • If the registry answers but what it serves fails a check (a bad signature, a key without the right role, a release list older than one already seen, or a binary with the wrong hash), sigil prints Verification failed; nothing was installed. with the reason, and stops. It does not fall back to Codeberg in that case.
  • sigil cli use <ver> for a release the registry does not carry (releases from before the registry) prints a note and downloads it from Codeberg.

sigil remembers the newest sequence numbers it has verified, per root key, in $SIGIL_HOME/registry-seen-seq, so a mirror cannot serve an older release list either. Deleting the file resets that memory.

sigil changes

Release management:

sigil changes new              # Create a new changeset
sigil changes status           # Show pending changes
sigil changes version          # Bump version based on changes
sigil changes changelog        # Generate changelog

Global Options

OptionDescription
-v, --verboseVerbose output
-q, --quietMinimal output
--versionShow version
--helpShow help

Environment Variables

VariableDescriptionDefault
SIGIL_PATHLibrary search path (colon-separated)Current directory
SIGIL_HOMESigil installation and global dependency-state directory~/.sigil
SIGIL_REGISTRYPackage registry base URL, for packages and for sigil cli upgrade/use. A mirror changes where bytes come from, never which keys verify themhttps://pkg.usesigil.org
NO_COLORDisable colored outputNot set
SIGIL_DEPS_TRACE_DROPPEDSet to 1 to trace dependency constraints the resolver discardedNot set
SIGIL_ALLOW_STALE_DEPSSet to 1 to build, test or run even though .sigil/deps does not match sigil.lockNot set

Tracing discarded dependency constraints

The resolver deduplicates by package name: the first declaration of a package to be resolved wins, and any later version range for that same name is discarded. This is normal and usually correct, because a library declaring sigil: "^0.17" is stating what it was built against rather than pinning the host's runtime.

Note (2026-08-25): sigil: "^0.17" above is the RETIRED spelling — the sigil: field is now a bare minimum version (sigil: "0.17") and range operators there are refused on host manifests; installed copies of old releases still carry carets and are read as their floor. See docs/reference/package-sigil-field.md. Operators remain valid for dependency version: fields, which is what this trace is about.

It also means a version range you wrote in package.sgl can have no effect on the resolve, with nothing in the output saying so. SIGIL_DEPS_TRACE_DROPPED=1 makes those discards visible:

SIGIL_DEPS_TRACE_DROPPED=1 sigil deps install

Each discarded constraint prints one line on stderr:

DROPPED <site> <package> want=<range> have=<version> <verdict>
FieldMeaning
sitegit-dep, workspace-member, or transitive — which resolution path the discarded constraint arrived on
wantthe range that was discarded, or - when the arrival path carries no range
havethe version already resolved for that package
verdictSATISFIED (the resolved version meets the discarded range), UNSATISFIED (it does not), NO-RANGE, UNPARSEABLE, or UNKNOWN-INSTALLED-VERSION

UNSATISFIED on an ordinary package is the interesting case: something in your dependency graph asked for a version range that the resolve does not meet, and the build is proceeding anyway. UNSATISFIED on sigil-lib or sigil-stdlib is expected and not a problem — the runtime family follows the host project's sigil: field, so a dependency's own range for it is advisory.

The trace never changes what gets resolved. It only reports.

Installed dependencies that do not match sigil.lock

sigil build, sigil test and sigil run stop with exit code 78 when a link in .sigil/deps points at a different commit than sigil.lock names:

✗ sigil test refused: .sigil/deps does not match sigil.lock.
  project: /home/me/my-app
  [lock-vs-disk] sigil.lock and the installed motif are different commits
      package:   motif
      locked:    55c9e747…
      installed: 046651cf…

The links are written by the last sigil deps install in that checkout, and git checkout does not move them, so switching to a branch whose lock differs leaves the old code in place with nothing in git status to say so. The usual fix is sigil deps install. To run against what is installed anyway, set SIGIL_ALLOW_STALE_DEPS=1. Workspace members, from-path dependencies and dependencies redirected with SIGIL_REDIRECTS or sigil build --redirects are not compared; sigil test --redirects skips the check, and sigil run --redirects checks every dependency the redirects do not replace. SIGIL_BUILD_COHERENCE=off does not turn it off.

Exit Codes

CodeMeaning
0Success
1General error
2Command-line usage error
3Compilation error
4Runtime error
78Configuration error (EX_CONFIG) — e.g. a malformed sigil: field, .sigil/deps links that do not match sigil.lock, or a coherence refusal under SIGIL_BUILD_COHERENCE=enforce; retrying unchanged cannot succeed

Examples

Development Workflow

# Create and run a new project
sigil init my-app
cd my-app
sigil run                          # Build and run the app

# Run a standalone script
sigil eval -f myprogram.sgl

# Run with library path
sigil eval -L lib -f main.sgl

# Start REPL for experimentation
sigil repl

Building for Distribution

# Build a package
sigil build sigil-cli --config release

# Install the CLI
./build/release/bin/sigil cli install

Running Tests

# Run all tests
sigil test

# Run specific tests
sigil test "test-parser"