Sigil 0.14.0
The compiler, runtime, build system, and standard libraries have been split into their own repositories. codeberg:sigil/sigil is the user-facing CLI; codeberg:sigil/sigil-lang is the compiler and runtime; codeberg:sigil/sigil-build is the build system; and twenty-plus standalone library repos make up the rest of the ecosystem. To match this, packages declare a single sigil: field that the build system uses to specify which version range of Sigil they are compatible with. Most package manifests get smaller as a result.
This is a breaking change for the ecosystem. Existing packages that pull from the old monorepo URLs (e.g. (from-git url:"codeberg:sigil/sigil" package:"sigil-stdlib" ...)) need to migrate to the new extracted-repo URLs (codeberg:sigil/sigil-lang, codeberg:sigil/sigil-build, codeberg:sigil/sigil-X for individual libraries). The new URL conventions are listed in the extraction table below. Going forward, packages target the extracted repos directly and bump them on their own cadence; the monorepo no longer hosts library code.
The sigil: package field
Every Sigil package now declares which language version it targets:
(package
name: "my-app"
version: "0.1.0"
sigil: "^0.14"
...)The sigil: field is load-bearing build-system input. From it, the build system derives:
sigil-stdlib(always, for any package that imports anything from(sigil ...))sigil-lib(when the package declaresc-sources:, for the C runtime headers)
Existing packages that explicitly declare those deps continue to work; the auto-derive only injects when no explicit declaration is present. The win is in new packages and library refreshes: a typical pure-Scheme package.sgl no longer needs the (from-git url:"codeberg:sigil/sigil-lang" package:"sigil-stdlib" version:"^0.13.1") boilerplate every previous version required.
The field is now required for every package. Build commands fail loudly if a package manifest is missing sigil:; the error names the file and suggests the fix.
For multi-package workspaces, sigil: ranges across member packages must intersect. The build system pairwise-intersects member ranges and reports an honest error when they conflict, naming both members and their declared ranges.
The default-package: workspace field
Multi-package workspace repos can now declare a default package for cross-repo dependency references:
(workspace
default-package: "sigil-build"
packages: (list
"packages/sigil-package"
"packages/sigil-deps"
"packages/sigil-build"))When a downstream package writes (from-git url: "codeberg:sigil/sigil-build") without a package: selector, the resolver picks up the workspace's default-package: and uses it to disambiguate which sub-package to install. This makes single-package consumers of multi-package repos look like single-package consumers of single-package repos: no package: keyword needed for the common case.
The field is explicit-only; the build system does not infer a default. If a downstream package refers to a multi-package workspace via from-git without package: and the workspace doesn't declare default-package:, resolution errors loudly with the list of member packages so the consumer can pick.
The completed extraction
The package set extracted from codeberg:sigil/sigil over 0.12.x and 0.13.x is now closed. What stays in the monorepo (for now):
sigil-cli(the user-facing tool)sigil-run(the bytecode runtime ship unit)sigil-wasm-runtime(the WASM runtime variant)
What lives in its own repo:
| Repository | Contents |
|---|---|
codeberg:sigil/sigil-lang | sigil-lib (C runtime), sigil-stdlib (standard library), compiler |
codeberg:sigil/sigil-build | sigil-package, sigil-deps, sigil-build (build system + manifest + dep resolver) |
codeberg:sigil/sigil-ansi | terminal styling |
codeberg:sigil/sigil-args | command-line argument parsing |
codeberg:sigil/sigil-crypto | cryptography primitives (vendored mbedTLS) |
codeberg:sigil/sigil-docs | documentation generation |
codeberg:sigil/sigil-ffi | foreign function interface |
codeberg:sigil/sigil-format | code formatting |
codeberg:sigil/sigil-git | git integration |
codeberg:sigil/sigil-hooks | lifecycle hooks |
codeberg:sigil/sigil-http | HTTP client + server |
codeberg:sigil/sigil-json | JSON parsing + encoding |
codeberg:sigil/sigil-log | structured logging |
codeberg:sigil/sigil-lsp | language server |
codeberg:sigil/sigil-mcp | MCP (Model Context Protocol) library |
codeberg:sigil/sigil-nrepl | network REPL protocol |
codeberg:sigil/sigil-repl | REPL library |
codeberg:sigil/sigil-socket | socket primitives |
codeberg:sigil/sigil-sqlite | SQLite (vendored amalgamation) |
codeberg:sigil/sigil-store | content-addressed storage |
codeberg:sigil/sigil-test | test runner |
codeberg:sigil/sigil-tls | TLS layer (consumes sigil-crypto) |
codeberg:sigil/sigil-version | version helpers |
codeberg:sigil/sigil-web | web framework |
Each library now releases on its own cadence. Mixed-version ecosystems are by design: sigil-http at v0.14.0 may pin sigil-stdlib at ^0.13, and that is fine. The extraction was about decoupling release cadences, not enforcing lockstep.
sigil cli upgrade and sigil cli use
sigil cli upgrade is faster, more reliable, and verifies binary integrity. Downloads now ship with mandatory sha256 checks; mismatched downloads are rejected and cleaned up. sigil cli use <version> lets you pin to a specific historical version on demand. The upgrade flow is now decoupled from any specific release-repo URL, so future hosting moves won't require a new sigil binary.
init templates refreshed
sigil init templates were updated for the post-pivot ecosystem. Each template's package.sgl now references the new extracted-repo URLs (codeberg:sigil/sigil-args, codeberg:sigil/sigil-http, codeberg:sigil/sigil-mcp, etc.) rather than the previous codeberg:sigil/sigil#vX.Y.Z + package: shape. The default *sigil-version* baked into newly-scaffolded projects bumped from v0.7.0 (badly stale) to ^0.14 and tracks current stable going forward.
Hygienic macros: syntax-case and quasisyntax
Sigil's hygienic macro implementation is now feature-complete enough to use as the primary macro system for any project. R6RS syntax-case with full hygiene, ellipsis patterns, and quasisyntax templates all work end-to-end. This matters because hygienic macros are the difference between "macros that occasionally surprise you with variable-capture bugs" and "macros that compose cleanly without a thought" — and well-written code in this style relies on the corner cases being correct.
- Quasisyntax shorthand — the reader now accepts `
#`,#,, and#,@as shorthand prefixes forquasisyntax,unsyntax, andunsyntax-splicing` (matching R6RS). - Quasisyntax expansion — full hygienic template expansion lands. Templates can use
unsyntaxto splice values back into a syntax object during macro expansion. syntax-local-value— R6RS compile-time environment query, available inside macros.syntax/#'outsidesyntax-case/with-syntax— fixed; previously raised an "unbound variable" error when used at the top of a macro body without an enclosing pattern matcher.syntax-caseellipsis patterns — fixed a long-standing bug where the ellipsis bound to the wrong template variable in nested patterns.
Build system and dependency resolver
The dep resolver fails loudly where it used to fail silently. The 0.13.x resolver had several "report success and break the consumer at build time" failure modes; those are gone. New behavior is loud and named: when a fetch errors, when a lockfile entry diverges from the manifest, when a config name doesn't exist. The motivating goal: turn unreproducible build mysteries into immediate, actionable errors at the source.
- Lock-entry staleness —
sigil deps installnow invalidates a lock entry when itsfrom-gitURL or package selector drifts from the manifest, not just when its version range changes. Previously, swapping a from-git dep's URL while keeping the same name would silently reuse the old SHA from the lockfile. - Loud-fail on any fetch error —
sigil deps installnow exits non-zero when any fetch errors out. Previous behavior printed the error in red but still returned success and wrote the lockfile. --configvalidation —sigil build --config <name>now fails loudly when the config name is undeclared inpackage.sgl. Previously the command silently fell through to the default config.- Improved diagnostic on path-resolve failures — the "Failed to resolve redirected path" error now names the missing package and suggests the fix.
Native codegen
Native builds are smaller, faster, and more visible. The CPS variable-declaration packing alone took the worst-case per-module zig-cc compile from "stuck at 24 minutes" to "completes cleanly," which makes native builds practical at the upper end of module size. Cumulative effect: native is now a comfortable default, not a "use at your own risk" path.
- CPS variable declaration packing — emitted C now packs CPS variable declarations 8 per line instead of 1 per line. On large modules (e.g. the
live-craftertheme module) this reduced emitted C from ~109k lines to ~77k lines, taking the per-module zig-cc compile time from "stuck after 24 minutes" to "completes cleanly." PUSH_BINDINGcache-miss path factored out of line — the binding-cache miss path was inlined into every binding access; pulling it out of line cut both binary size and per-binding hot-path code size.- Case-lambda runtime workaround retired — 332 lines of C in
packages/sigil-lib/src/macro.cwere removed after the underlyingcase-lambdahygiene bug was fixed in the syntax-case path. - Visible
libsigil.abuild progress — sigil-build's libsigil compile path now prints one line per call (libsigil.a: cache hit (...)orBuilding libsigil.a from source (...)) so users can see the C runtime being processed during native builds. Previously this was guarded behind--verbose.
Bug fixes
dep-namefalls back tofrom-git-package— previously, deps installed via(from-git url: ... package: ...)shape resolved to a name of#fin error messages.fetch-sglsymlink race —create-package-symlinknow tolerates the dangling-symlink edge where a previousdelete-fileraced withfile-exists?returning true.- WASM-incompatible code excluded from WASM builds —
sigil-lib'ssetjmp-based exception scaffolding andtzset()-based time code are now#ifndef __wasm__-gated so WASM-musl cross-compiles cleanly. - Stale lock reuse on version-range change — fixed the case where
sigil deps installwould reuse a lockfile entry whose pinned SHA no longer matches the new version range.