Skir began as Tyler Fibonacci's TypeScript implementation of a compact schema language for generating strongly typed bindings. This repository keeps the original ISC license and gives credit to that work; the current branch is a speculative native-Aether rewrite meant to showcase Aether itself.
This is not a donation back to the original Skir project. It is a local experiment in replacing the Node/TypeScript core with:
- Aether for the CLI and schema authoring DSL.
- aeb for build and test targets.
- Aether's built-in
std.specfor BDD-style tests. - External generator binaries for language-specific bindings.
The port has changed the shape of the project substantially:
- No root npm package:
package.jsonandpackage-lock.jsonare gone. Package metadata now belongs to generated language bindings, not to Skir's own build. - No TypeScript core: the previous
src/*.tsimplementation and tests were removed. The old tree had 44 TypeScript files and about 21.5k TypeScript source lines. - No npm dependency graph: the pre-port lockfile was about 3,174 lines and
pulled runtime packages such as
glob,yaml,zod,watcher,skir-internal, and theskir-*-genpackages, plus TypeScript/Mocha/ESLint dev tooling. The native repo has no npm install step. - Native CLI:
bin/skirbuilds and runsaether/skir.ae. The current native binary is about 112 KiB locally. - Aether schema DSL:
skir_schemabuilds schema IR directly..skiris no longer the intended intermediate for this showcase path. - Native binding generators: each
generators/<language>/.build.aeleaf builds one native Aether generator binary. TypeScript and Python emit package metadata; every target emits language files fromskir-ir.json. - Binding smoke leaves: each
bindings/<language>/.build.aeleaf generates a fixture binding fromfixtures/greet/schema.ae, builds it with the matching aeb language SDK, and declares its toolchain withprereq(...)foraeb --prereqs/ remote-toolchain routing. - Reference-only Node sources: local
genrs/clones can be used to compare against the historical TypeScript generators, but they are ignored and not part of this repository. - aeb-native build/test targets:
.build.aebuilds the CLI leaf,.tests.aeruns thestd.specsuite, and.pre-commit.aeruns the pre-commit checks.
Local rough timing, not a benchmark: after the binary already exists,
bin/skir version runs in about 5-6 ms on this machine. A first wrapper run that
has to rebuild the binary took about 0.54s.
bin/skir init
bin/skir gen
bin/skir format --ci
bin/skir snapshot
bin/skir versionbin/skir builds aether/skir.ae with ae build on demand and runs
build/skir-ae.
aeb .build.ae
aeb .tests.ae
aeb .pre-commit.aeUse whichever aeb you have installed; programs build with the ae on your
PATH. pre_commit_setup.sh installs a git hook that runs aeb .pre-commit.ae.
The root build target builds the native CLI leaf. Generator binaries are built through explicit leaves such as:
aeb generators/typescript/.build.ae
aeb bindings/typescript/.build.ae
aeb --prereqs bindings/typescript/.build.aeThe test target compiles and runs tests/skir_spec.ae, a std.spec suite
that exercises the schema DSL, the native CLI smoke path, generator handoff for
every supported language target, and the C++, Go, Java, Python, Rust and
TypeScript binding smoke leaves. The other binding leaves are built on their
own, given their toolchains.
Schemas are Aether programs. A minimal schema program looks like this:
import skir_schema (document, structure, field, record_name, field_name,
string_type)
main() {
document("skirout/skir-ir.json") {
structure(record_name("Greeting")) {
field(field_name("message"), string_type())
}
}
}
The DSL emits schema IR JSON directly. That makes Aether the authoring language
for schemas, not a generator of .skir text. The benefit is that schemas can
still read declaratively while using normal Aether helpers, conditionals, loops,
and module imports when useful.
aether/
skir.ae
examples/
skir_schema/
generators/
skir_bindgen.ae
cpp/
csharp/
dart/
gleam/
go/
java/
kotlin/
moonbit/
python/
rust/
swift/
typescript/
zig/
bindings/
cpp/
csharp/
dart/
gleam/
go/
java/
kotlin/
moonbit/
python/
rust/
swift/
typescript/
zig/
fixtures/
greet/
binding_build/
tests/
skir_spec.ae
pre_commit.ae
skir init creates a project of its own, outside this layout:
skir.json
skir-src/
hello_world.ae
skir gen runs schema programs under skir-src/**/*.ae and writes:
<outDir>/skir-ir.json
The generated IR is the handoff point for language-specific binding generators.
{
"generators": [
{ "kind": "ir", "outDir": "./skirout" },
{
"kind": "external",
"outDir": "./bindings/typescript",
"command": "target/build/generators/typescript/bin/skir-typescript-gen"
},
{
"kind": "external",
"outDir": "./bindings/python",
"command": "target/build/generators/python/bin/skir-python-gen"
}
]
}When command is present, Skir runs:
<command> <outDir>/skir-ir.json <outDir>
That keeps the core native and lets each target-language generator evolve as a
separate executable. In this repo, generators/<language>/.build.ae builds the
generator binary and bindings/<language>/.build.ae validates generated output
with the matching aeb SDK. The TypeScript generator emits package.json; the
Python generator emits pyproject.toml.
This is speculative and intentionally incomplete. The current goal is to show a
coherent Aether-native toolchain shape: Aether CLI, Aether schema DSL, aeb
targets, std.spec tests, no Node runtime dependency, and no .skir text
intermediate on the main path.