Convert source to Jerboa .ss; build via jerbuild (+ cross targets)

ober

0a799203628f01acb078f24b2ecd36998ce045e2

diff --git a/.gitignore b/.gitignore
index 30695d0..2e0fe0f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,16 +1,28 @@
-# Compiled Chez Scheme artifacts
-lib/**/*.so
-lib/**/*.wpo
-lib/**/*.hash
+# Compiled Chez Scheme artifacts (jerbuild emits these next to sources)
+**/*.so
+**/*.wpo
+**/*.hash
+!vendor/**/*.so
 
-# Build artifacts
+# jerbuild whole-program build scratch
+.build/
+
+# Native binary + per-applet symlinks (rebuilt with `make binary`)
+/jerboa-coreutils
+/jerboa-coreutils-linux-amd64
+/jerboa-coreutils-linux-arm64
+/jerboa-coreutils-freebsd-amd64
+
+# Vendored dependencies — fetched at build time by `make vendor-deps`,
+# not committed (matches jerboa-code).
+/vendor/
+
+# Local tool cache (jcode)
+/.jcode/
+
+# Legacy interpreter-wrapper build (superseded by jerbuild)
+bin/
 support/cu_*.h
 support/coreutils-main.o
 support/libcoreutils-obj.o
-dispatch.so
-dispatch.wpo
-dispatch-all.so
-coreutils.boot
-
-# Binary output (rebuilt with make binary)
-bin/jerboa-coreutils
+support/libcoreutils.so
diff --git a/.jerbuild b/.jerbuild
new file mode 100644
index 0000000..99b1f73
--- /dev/null
+++ b/.jerbuild
@@ -0,0 +1,41 @@
+;; .jerbuild — build the jerboa-coreutils multi-call binary with `jerbuild build`.
+;;
+;; jerbuild bundles Chez Scheme + the jerboa stdlib, so this needs only
+;; jerbuild + a C compiler + cargo — no jerboa source checkout and no
+;; separately built Chez. The vendored jerboa-native-rs crate (crypto feature)
+;; backs the checksum applets' (std crypto digest) FFI; without it the
+;; compiled whole-program would fail at startup resolving jerboa_* symbols
+;; (compiled programs invoke every imported library when the binary loads).
+;;
+;; The transpiled libraries must already exist under lib/ (run `make gen`, i.e.
+;; `jerbuild transpile src lib`, first — the Makefile's `binary` target does).
+;; All 113 utility libraries are reachable from the entry, so the whole-program
+;; compile pulls them into the single self-contained binary.
+
+(entry  "main-binary.ss")
+(output "jerboa-coreutils")
+
+;; Transpiled libraries (src → lib via `make gen`). do-binary-build
+;; auto-appends the bundled stdlib, so list only the project lib dir.
+(libdirs "lib")
+
+;; Custom main(): stock jerbuild main.c + setenv(_CU_CMD, argv[0]) so the
+;; embedded entry program can dispatch on the multi-call command name.
+(main-c "support/coreutils-jerbuild-main.c")
+
+;; C symbols registered via Sforeign_symbol after Sbuild_heap (static binaries
+;; can't dlsym RTLD_DEFAULT). Covers the libcoreutils.c shim + bare libc.
+;; pcre2 is excluded on purpose — grep dlopens libpcre2-8 at runtime.
+(ffi-symbols "support/coreutils-ffi-symbols.list")
+
+;; FFI shim compiled into the binary (stat/lstat, terminal size, raw mode,
+;; uid/gid/time formatting, temp-file helpers).
+(extra-sources "support/libcoreutils.c")
+
+;; jerboa-native-rs (Rust): crypto only — provides jerboa_sha*, jerboa_hmac_*,
+;; jerboa_last_error, etc. used by (std crypto native-rust) behind the
+;; cksum/md5sum/sha*sum/b2sum applets. No tls/sqlite/duckdb/etc.
+(rust-crates
+  ("vendor/jerboa-native-rs/Cargo.toml"
+   features: "crypto"
+   no-default-features: #t))
diff --git a/CLAUDE.md b/CLAUDE.md
index b6fe6c8..36475a9 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -1,6 +1,14 @@
 # jerboa-coreutils
 
+## Layout
+
+- Source is **Jerboa `.ss`** in `src/jerboa-coreutils/`. Never hand-edit `lib/*.sls` — those are **generated** by `jerbuild transpile src lib` (run by `make gen`). Edit `src/`, then re-transpile.
+- `main-binary.ss` (repo root) is the multi-call dispatch entry; it dispatches on the `argv[0]` basename passed via `_CU_CMD` (set by `support/coreutils-jerbuild-main.c`).
+- Verify any `.ss` change with `mcp__jerboa__jerboa_compile_check` before building.
+
 ## Build
 
-- **Always use `gmake` on FreeBSD**, never `make`. The Makefile uses GNU make syntax (`$(CURDIR)`, `$(wildcard ...)`, `$<` in explicit rules) which is incompatible with FreeBSD's BSD make.
-- Set `CHEZ_DIR` if Chez Scheme is not installed in `~/.local/lib/`. On FreeBSD: `CHEZ_DIR=/usr/local/lib/csv10.3.0/ta6fb gmake binary`
+- **Always use `gmake` on FreeBSD**, never `make` (the Makefile uses GNU make syntax).
+- `make binary` = `jerbuild transpile` + `jerbuild build` (reads `.jerbuild`). No `CHEZ_DIR` / external Chez needed — jerbuild bundles Chez + the stdlib. Needs `jerbuild`, a C compiler, and `cargo` (the vendored crypto crate).
+- Cross: `make linux-amd64` / `linux-arm64` / `freebsd-amd64` use the per-target cross Chez in `$(JERBOA_HOME)` + the matching C toolchain (and rustup cross targets for the crate).
+- FFI symbols (shim + `jerboa_*` crypto) live in `support/coreutils-ffi-symbols.list`; the compiled whole-program invokes every library at startup, so any top-level `foreign-procedure` symbol must be registered there or dlopen-able.
diff --git a/Makefile b/Makefile
index 50a449a..02351cb 100644
--- a/Makefile
+++ b/Makefile
@@ -1,128 +1,204 @@
-# jerboa-coreutils Makefile
-# Gerbil-like coreutils on Chez Scheme via Jerboa
+# jerboa-coreutils — GNU coreutils in Jerboa (Chez Scheme via Jerboa).
+#
+# Source lives in src/jerboa-coreutils/*.ss (Jerboa). `make gen` transpiles it
+# to lib/jerboa-coreutils/*.sls; `make binary` whole-program-compiles the
+# multi-call entry (main-binary.ss) into one self-contained executable using
+# jerbuild + the shared .jerbuild config.
+#
+# jerbuild bundles Chez Scheme + the jerboa stdlib, so the native `binary`
+# target needs only jerbuild + a C compiler — no jerboa source checkout and no
+# separately built Chez. Cross targets additionally need the per-target cross
+# Chez (built once in $(JERBOA_HOME)) and the matching C cross toolchain.
 
-# Use the scheme-script wrapper from /usr/local/bin if available, otherwise use 'scheme'
-SCHEME ?= scheme
+JERBOA_HOME ?= $(HOME)/mine/jerboa
+JERBUILD    ?= jerbuild
 
-# Override CHEZ_DIR if Chez is not in default location
-CHEZ_DIR ?= 
+# Bundled stdlib (used by the host `exec`/test helpers).
+JH := $(shell $(JERBUILD) --jerboa-home 2>/dev/null)
 
-JERBOA_LIB = $(HOME)/mine/jerboa/lib
-LIB_DIR = $(CURDIR)/lib
-BIN_DIR = $(CURDIR)/bin
-SUPPORT_DIR = $(CURDIR)/support
+BIN     := jerboa-coreutils
+BIN_DIR := $(HOME)/.local/bin
 
-# All utility names (matches gerbil-coreutils)
+# Vendored Rust crate (crypto FFI for the checksum applets), linked by jerbuild
+# per .jerbuild. Sparse-cloned from the jerboa monorepo by `make vendor-deps`.
+NATIVE_DIR    := vendor/jerboa-native-rs
+JERBOA_REMOTE ?= https://git.sr.ht/~lisp/jerboa
+
+# Multi-call applet names. The binary dispatches on argv[0]; these are the
+# symlink names created next to $(BIN) by `make symlinks`.
 UTILS = true false yes echo printenv sleep whoami logname pwd \
         basename dirname link unlink hostname nproc tty sync \
         cat head tail wc tee tac nl fold expand unexpand \
         cut paste join comm sort uniq tr numfmt \
-        mkdir rmdir mktemp touch ln readlink realpath \
-        truncate \
-        uname arch id groups \
-        seq factor \
-        env timeout kill nice nohup \
-        shuf base64 base32 \
-        tsort \
-        hostid users uptime \
-        chroot mkfifo mknod \
-        pathchk fmt printf \
+        mkdir rmdir mktemp touch ln readlink realpath truncate \
+        uname arch id groups seq factor \
+        env timeout kill nice nohup shuf base64 base32 tsort \
+        hostid users uptime chroot mkfifo mknod pathchk fmt printf \
         cksum md5sum sha1sum sha256sum sha512sum sha224sum sha384sum \
-        chmod chown chgrp \
-        stat du df \
-        date \
-        expr test \
-        who \
-        split \
-        dircolors \
+        chmod chown chgrp stat du df date expr test who split dircolors \
         install shred pinky basenc b2sum sum od csplit pr chcon runcon ptx \
-        cp mv rm ls dd stty stdbuf \
-        dir vdir rev top \
-        grep
-
-export CHEZSCHEMELIBDIRS = $(LIB_DIR):$(JERBOA_LIB)
-export PROJECT_DIR = $(CURDIR)
-
-# Default: build native multi-call binary
-.PHONY: all build binary clean test install help
+        cp mv rm ls dd stty stdbuf dir vdir rev top grep egrep fgrep
 
-all: binary
+# --- Cross targets: machine type + C toolchain + jerbuild os-libs ---
+# csv-dir / xpatch are produced by `make chez-cross` in $(JERBOA_HOME).
+CSV_VER     ?= $(notdir $(patsubst %/,%,$(dir $(firstword $(wildcard $(JERBOA_HOME)/.chez-cross-ta6le/lib/csv*/ta6le)))))
+LINUX_AMD64_CC ?= x86_64-linux-musl-gcc
+LINUX_ARM64_CC ?= aarch64-linux-musl-gcc
+FREEBSD_AMD64_CC ?= x86_64-unknown-freebsd14-clang
 
-# Native multi-call binary (BusyBox-style, self-contained ELF)
-binary: $(SUPPORT_DIR)/libcoreutils.so
-	@mkdir -p $(BIN_DIR)
-	CHEZ_DIR=$(CHEZ_DIR) PROJECT_DIR=$(CURDIR) $(SCHEME) --libdirs "$(CHEZSCHEMELIBDIRS)" -q < build-binary.ss
+define cross_csvdir
+$(JERBOA_HOME)/.chez-cross-$(1)/lib/$(CSV_VER)/$(1)
+endef
+define cross_xpatch
+$(JERBOA_HOME)/build/chez/xc-$(1)/s/xpatch
+endef
 
-# Legacy: build C shim + generate interpreter wrapper scripts
-build: $(SUPPORT_DIR)/libcoreutils.so $(BIN_DIR)/.generated
+# Cross cargo (run by jerbuild for the vendored crate) must use rustup's
+# toolchain, which has the musl/freebsd targets installed — Homebrew's rustc on
+# PATH does not, and fails with "can't find crate for core". Prepend rustup's
+# bin dir so jerbuild's internal `cargo`/`rustc` resolve to it.
+RUSTUP_BIN := $(shell dirname "$$(rustup which rustc 2>/dev/null)" 2>/dev/null)
+CROSS_ENV  := PATH="$(RUSTUP_BIN):$$PATH"
 
-$(SUPPORT_DIR)/libcoreutils.so: $(SUPPORT_DIR)/libcoreutils.c
-	gcc -shared -fPIC -O2 -o $@ $<
+# The crypto crate (ring) compiles C; for FreeBSD, cc-rs must use the
+# sysroot-aware cross cc (not Apple's bare clang, which lacks FreeBSD headers
+# like assert.h) and LLVM's ar. Mirrors jerboa-code's freebsd cross env.
+LLVM_AR        ?= /opt/homebrew/opt/llvm/bin/llvm-ar
+FREEBSD_CC_ENV := CC_x86_64_unknown_freebsd="$(FREEBSD_AMD64_CC)" \
+                  AR_x86_64_unknown_freebsd="$(LLVM_AR)" \
+                  CARGO_TARGET_X86_64_UNKNOWN_FREEBSD_LINKER="$(FREEBSD_AMD64_CC)"
 
-$(BIN_DIR)/.generated: $(wildcard $(LIB_DIR)/jerboa-coreutils/*.sls)
-	@mkdir -p $(BIN_DIR)
-	@for util in $(UTILS); do \
-		echo '#!/bin/sh' > $(BIN_DIR)/$$util; \
-		echo 'SCRIPT_DIR=$$(cd "$$(dirname "$$0")/.." && pwd)' >> $(BIN_DIR)/$$util; \
-		echo 'export CHEZSCHEMELIBDIRS="$$SCRIPT_DIR/lib:$(JERBOA_LIB)"' >> $(BIN_DIR)/$$util; \
-		echo 'export LD_LIBRARY_PATH="$$SCRIPT_DIR/support:$$LD_LIBRARY_PATH"' >> $(BIN_DIR)/$$util; \
-		if [ -n "$(CHEZ_DIR)" ]; then \
-			echo "export CHEZ_DIR='$(CHEZ_DIR)'" >> $(BIN_DIR)/$$util; \
-		fi; \
-		echo 'exec $(SCHEME) --libdirs "$$CHEZSCHEMELIBDIRS" --program "$$SCRIPT_DIR/bin/'"$$util"'.sps" "$$@"' >> $(BIN_DIR)/$$util; \
-	(chmod +x $(BIN_DIR)/$$util); \
-		if [ ! -f $(BIN_DIR)/$$util.sps ]; then \
-			echo '#!chezscheme' > $(BIN_DIR)/$$util.sps; \
-			echo "(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))" >> $(BIN_DIR)/$$util.sps; \
-			echo "(import (jerboa-coreutils $$util))" >> $(BIN_DIR)/$$util.sps; \
-			echo "(apply main (cdr (command-line)))" >> $(BIN_DIR)/$$util.sps; \
-		fi; \
-	done
-	@# Create egrep/fgrep as aliases for grep
-	@cd $(BIN_DIR) && ln -sf grep egrep && ln -sf grep fgrep
-	@if [ ! -f $(BIN_DIR)/egrep.sps ]; then \
-		echo '#!chezscheme' > $(BIN_DIR)/egrep.sps; \
-		echo "(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))" >> $(BIN_DIR)/egrep.sps; \
-		echo "(import (jerboa-coreutils grep))" >> $(BIN_DIR)/egrep.sps; \
-		echo "(apply main (cdr (command-line)))" >> $(BIN_DIR)/egrep.sps; \
-	fi
-	@if [ ! -f $(BIN_DIR)/fgrep.sps ]; then \
-		echo '#!chezscheme' > $(BIN_DIR)/fgrep.sps; \
-		echo "(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))" >> $(BIN_DIR)/fgrep.sps; \
-		echo "(import (jerboa-coreutils grep))" >> $(BIN_DIR)/fgrep.sps; \
-		echo "(apply main (cdr (command-line)))" >> $(BIN_DIR)/fgrep.sps; \
-	fi
-	@touch $@
-
-# Run a single utility: make run UTIL=true ARGS="--help"
-run:
-	@$(BIN_DIR)/$(UTIL) $(ARGS)
-
-# Quick test of basic utilities
-test:
-	@echo "=== Testing basic utilities ==="
-	@$(BIN_DIR)/true && echo "PASS: true"
-	@! $(BIN_DIR)/false 2>/dev/null && echo "PASS: false" || echo "PASS: false"
-	@echo "hello" | $(BIN_DIR)/cat && echo "PASS: cat"
-	@$(BIN_DIR)/echo "hello world" && echo "PASS: echo"
-	@$(BIN_DIR)/basename /foo/bar/baz.txt && echo "PASS: basename"
-	@$(BIN_DIR)/dirname /foo/bar/baz.txt && echo "PASS: dirname"
-	@$(BIN_DIR)/seq 1 5 && echo "PASS: seq"
-	@echo "=== Done ==="
+.PHONY: all help gen binary symlinks test clean vendor-deps vendor-clean \
+        linux linux-amd64 linux-arm64 freebsd freebsd-amd64 \
+        cross-check-amd64 cross-check-arm64 cross-check-freebsd
 
-clean:
-	rm -rf $(BIN_DIR) $(SUPPORT_DIR)/libcoreutils.so $(SUPPORT_DIR)/cu_*.h
-	find $(LIB_DIR) -name "*.so" -o -name "*.wpo" -o -name "*.hash" | xargs rm -f 2>/dev/null || true
-	rm -f dispatch.so dispatch.wpo dispatch-all.so coreutils.boot
+# Per AGENTS.md: bare `make` prints targets and mutates nothing.
+all: help
 
 help:
-	@echo "jerboa-coreutils - GNU coreutils in Jerboa (Gerbil on Chez Scheme)"
+	@echo "jerboa-coreutils — GNU coreutils in Jerboa"
 	@echo ""
-	@echo "Usage:"
-	@echo "  make binary    Build self-contained native multi-call ELF binary (default)"
-	@echo "  make build     Build C shim + interpreter wrapper scripts (legacy)"
-	@echo "  make test      Run basic smoke tests"
-	@echo "  make clean     Remove build artifacts"
-	@echo "  make run UTIL=name ARGS='...'  Run a utility"
+	@echo "  make vendor-deps   Sparse-clone vendored jerboa-native-rs crate"
+	@echo "  make gen           Transpile src/ .ss -> lib/ .sls (jerbuild transpile)"
+	@echo "  make binary        Build native ./$(BIN) for this host (default applet build)"
+	@echo "  make symlinks      Create per-applet symlinks (cat, ls, ...) next to ./$(BIN)"
+	@echo "  make linux-amd64   Cross-build $(BIN)-linux-amd64  (static musl)"
+	@echo "  make linux-arm64   Cross-build $(BIN)-linux-arm64  (static musl)"
+	@echo "  make freebsd-amd64 Cross-build $(BIN)-freebsd-amd64"
+	@echo "  make test          Build + smoke-test core applets"
+	@echo "  make install       Install ./$(BIN) + applet symlinks to $(BIN_DIR)"
+	@echo "  make clean         Remove build artifacts"
 	@echo ""
-	@echo "Utilities: $(words $(UTILS)) total"
+	@echo "  Applets ($(words $(UTILS))): dispatched by argv[0] basename."
+
+# Sparse-clone just the jerboa-native-rs crate from the jerboa monorepo so a
+# fresh checkout populates vendor/. No-op once vendor/jerboa-native-rs exists.
+vendor-deps: $(NATIVE_DIR)
+$(NATIVE_DIR):
+	@mkdir -p vendor
+	@echo "=== Sparse-cloning jerboa-native-rs from $(JERBOA_REMOTE) ==="
+	@git clone --depth 1 --filter=blob:none --sparse $(JERBOA_REMOTE) vendor/.jerboa-monorepo
+	@git -C vendor/.jerboa-monorepo sparse-checkout set jerboa-native-rs
+	@mv vendor/.jerboa-monorepo/jerboa-native-rs $(NATIVE_DIR)
+	@rm -rf vendor/.jerboa-monorepo
+
+vendor-clean:
+	rm -rf vendor/.jerboa-monorepo
+
+# Transpile Jerboa source to R6RS libraries jerbuild can whole-program compile.
+gen: vendor-deps
+	$(JERBUILD) transpile src lib
+
+# Native multi-call binary for the host OS/arch. Reads .jerbuild.
+binary: gen
+	$(JERBUILD) build
+	@$(MAKE) --no-print-directory symlinks
+
+# Per-applet symlinks in bin/ pointing at the host ./$(BIN). Invoke applets as
+# bin/cat, bin/ls, ... (the binary dispatches on argv[0] basename).
+symlinks:
+	@test -f $(BIN) || { echo "symlinks: ./$(BIN) not built yet (run make binary)"; exit 1; }
+	@mkdir -p bin
+	@for u in $(UTILS); do ln -sf ../$(BIN) bin/$$u; done
+	@echo "Created $(words $(UTILS)) applet symlinks in bin/ -> ../$(BIN)"
+
+# --- Cross builds: same .jerbuild, retargeted Chez + C toolchain. ---
+linux: linux-amd64
+
+# Each cross build reuses the shared .jerbuild (output: jerboa-coreutils),
+# then renames the produced binary to the per-target name.
+linux-amd64: gen cross-check-amd64
+	$(CROSS_ENV) $(JERBUILD) build \
+	  --cc $(LINUX_AMD64_CC) \
+	  --rust-target x86_64-unknown-linux-musl \
+	  --csv-dir "$(call cross_csvdir,ta6le)" \
+	  --xpatch  "$(call cross_xpatch,ta6le)" \
+	  --os-libs "-lm -ldl -lpthread"
+	mv $(BIN) $(BIN)-linux-amd64
+	@file $(BIN)-linux-amd64
+
+linux-arm64: gen cross-check-arm64
+	$(CROSS_ENV) $(JERBUILD) build \
+	  --cc $(LINUX_ARM64_CC) \
+	  --rust-target aarch64-unknown-linux-musl \
+	  --csv-dir "$(call cross_csvdir,tarm64le)" \
+	  --xpatch  "$(call cross_xpatch,tarm64le)" \
+	  --os-libs "-lm -ldl -lpthread"
+	mv $(BIN) $(BIN)-linux-arm64
+	@file $(BIN)-linux-arm64
+
+freebsd: freebsd-amd64
+
+freebsd-amd64: gen cross-check-freebsd
+	$(CROSS_ENV) $(FREEBSD_CC_ENV) $(JERBUILD) build \
+	  --cc "$(FREEBSD_AMD64_CC)" \
+	  --rust-target x86_64-unknown-freebsd \
+	  --csv-dir "$(call cross_csvdir,ta6fb)" \
+	  --xpatch  "$(call cross_xpatch,ta6fb)" \
+	  --os-libs "-lm -lpthread -lutil"
+	mv $(BIN) $(BIN)-freebsd-amd64
+	@file $(BIN)-freebsd-amd64
+
+# Toolchain preflight: report the missing dependency instead of half-building.
+cross-check-amd64:
+	@command -v $(LINUX_AMD64_CC) >/dev/null 2>&1 || { \
+	  echo "ERROR: $(LINUX_AMD64_CC) not found. Install: brew install FiloSottile/musl-cross/musl-cross" >&2; exit 1; }
+	@test -f "$(call cross_xpatch,ta6le)" || { \
+	  echo "ERROR: cross Chez (ta6le) missing. Build once: make -C $(JERBOA_HOME) chez-cross CHEZ_TARGET_MACHINE=ta6le CROSS_CC=$(LINUX_AMD64_CC)" >&2; exit 1; }
+
+cross-check-arm64:
+	@command -v $(LINUX_ARM64_CC) >/dev/null 2>&1 || { \
+	  echo "ERROR: $(LINUX_ARM64_CC) not found. Install: brew install FiloSottile/musl-cross/musl-cross" >&2; exit 1; }
+	@test -f "$(call cross_xpatch,tarm64le)" || { \
+	  echo "ERROR: cross Chez (tarm64le) missing. Build once: make -C $(JERBOA_HOME) chez-cross CHEZ_TARGET_MACHINE=tarm64le CROSS_CC=$(LINUX_ARM64_CC)" >&2; exit 1; }
+
+cross-check-freebsd:
+	@command -v $(firstword $(FREEBSD_AMD64_CC)) >/dev/null 2>&1 || { \
+	  echo "ERROR: $(FREEBSD_AMD64_CC) not found (FreeBSD amd64 cross cc)." >&2; exit 1; }
+	@test -f "$(call cross_xpatch,ta6fb)" || { \
+	  echo "ERROR: cross Chez (ta6fb) missing. Build once: make -C $(JERBOA_HOME) chez-cross CHEZ_TARGET_MACHINE=ta6fb CROSS_CC=\"$(FREEBSD_AMD64_CC)\"" >&2; exit 1; }
+
+install: binary
+	@mkdir -p $(BIN_DIR)
+	install -m 0755 $(BIN) $(BIN_DIR)/$(BIN)
+	@for u in $(UTILS); do ln -sf $(BIN) $(BIN_DIR)/$$u; done
+	@echo "Installed $(BIN) + $(words $(UTILS)) applet symlinks to $(BIN_DIR)"
+
+test: binary
+	@echo "=== smoke test ==="
+	@bin/true && echo "PASS: true"
+	@if bin/false; then echo "FAIL: false"; exit 1; else echo "PASS: false"; fi
+	@test "$$(echo hi | bin/cat)" = "hi" && echo "PASS: cat"
+	@test "$$(bin/echo hello world)" = "hello world" && echo "PASS: echo"
+	@test "$$(bin/basename /a/b/c.txt)" = "c.txt" && echo "PASS: basename"
+	@test "$$(bin/dirname /a/b/c.txt)" = "/a/b" && echo "PASS: dirname"
+	@test "$$(bin/seq 1 3 | tr '\n' ' ')" = "1 2 3 " && echo "PASS: seq"
+	@test "$$(printf 'b\na\n' | bin/sort | tr '\n' ' ')" = "a b " && echo "PASS: sort"
+	@test "$$(printf '' | bin/sha256sum | cut -c1-8)" = "e3b0c442" && echo "PASS: sha256sum"
+	@printf 'apple\nbanana\n' | bin/grep an | grep -q banana && echo "PASS: grep"
+	@echo "=== ok ==="
+
+clean:
+	rm -f $(BIN) $(BIN)-linux-amd64 $(BIN)-linux-arm64 $(BIN)-freebsd-amd64
+	rm -rf bin .build
+	find lib -name '*.so' -o -name '*.wpo' -o -name '*.hash' | xargs rm -f 2>/dev/null || true
diff --git a/README.md b/README.md
index 1a9a237..9fd4447 100644
--- a/README.md
+++ b/README.md
@@ -8,47 +8,65 @@ This is a port of [gerbil-coreutils](https://github.com/jafourni/gerbil-coreutil
 
 ```
 ┌─────────────────────────────────────────┐
-│  110 coreutils (true, cat, ls, ...)     │
-│  .sls R6RS libraries using Jerboa APIs  │
+│  113 coreutils (true, cat, ls, ...)     │
+│  src/jerboa-coreutils/*.ss  (Jerboa)    │
 └─────────────────────────────────────────┘
-            ↓ imports
+   ↓ jerbuild transpile  (src/ .ss → lib/ .sls)
 ┌─────────────────────────────────────────┐
 │  Jerboa: (jerboa core), (std/cli/getopt)│
-│  Gerbil syntax: def, let-hash, match... │
+│  Jerboa syntax: def, let-hash, match... │
 │  Stdlib: format, sugar, sort, crypto... │
 └─────────────────────────────────────────┘
-            ↓ runs on
+   ↓ jerbuild build  (whole-program compile + link)
 ┌─────────────────────────────────────────┐
 │  Stock Chez Scheme 10.x (unmodified)    │
+│  one self-contained multi-call binary   │
 └─────────────────────────────────────────┘
 ```
 
+Code is written in **Jerboa** (`.ss`, using `(jerboa prelude)`, `def`,
+reader extras, `(std …)` modules). The jerboa toolchain (`jerbuild`)
+transpiles `src/` to R6RS `lib/` and whole-program-compiles the multi-call
+entry (`main-binary.ss`) into one binary that dispatches on `argv[0]`.
+
 ## Quick Start
 
 ```bash
-# Prerequisites: Chez Scheme, gcc, jerboa installed at ~/mine/jerboa
-make build
+# Prerequisites: jerbuild + a C compiler + cargo (crypto crate). On FreeBSD
+# use gmake. The vendored crate is auto-fetched by `make vendor-deps`.
+make binary
 
-# Run utilities
+# Run utilities (binary dispatches on argv[0] via bin/ symlinks)
 bin/true
 bin/echo "hello world"
 echo "foo bar" | bin/cat -n
 bin/seq 1 10
 bin/ls -la
+
+# Cross-compile (toolchains required; see Makefile)
+make linux-amd64   # static musl x86-64
+make linux-arm64   # static musl aarch64
+make freebsd-amd64 # FreeBSD 14 amd64
 ```
 
 ## Project Structure
 
 ```
 jerboa-coreutils/
-├── lib/jerboa-coreutils/       # 110 utility libraries (.sls)
-│   ├── common.sls              # Shared error handling, exit codes
-│   ├── common/io.sls           # File/line processing utilities
-│   ├── common/version.sls      # Version information
-│   ├── true.sls ... vdir.sls   # Individual utilities
-├── bin/                         # Generated entry point scripts
+├── src/jerboa-coreutils/       # 113 utilities — Jerboa SOURCE (.ss)
+│   ├── common.ss               # Shared error handling, exit codes
+│   ├── common/io.ss            # File/line processing utilities
+│   ├── common/security.ss      # seccomp/landlock/audit (graceful degrade)
+│   ├── grep/pcre2.ss           # PCRE2 FFI bindings (lazy dlopen)
+│   └── true.ss ... grep.ss     # Individual utilities
+├── lib/jerboa-coreutils/       # GENERATED R6RS libraries (jerbuild transpile)
+├── main-binary.ss              # Multi-call dispatch entry (argv[0] basename)
+├── .jerbuild                   # jerbuild build config (entry, ffi, crate)
 ├── support/
-│   └── libcoreutils.c          # C shim for FFI (stat, ls, cp, etc.)
+│   ├── libcoreutils.c          # C shim for FFI (stat, ls, cp, terminal…)
+│   ├── coreutils-jerbuild-main.c   # custom main() (sets _CU_CMD = argv[0])
+│   └── coreutils-ffi-symbols.list  # registered FFI symbols (shim + crypto)
+├── vendor/jerboa-native-rs/    # vendored crypto crate (checksum applets)
 ├── Makefile
 └── README.md
 ```
@@ -85,11 +103,10 @@ jerboa-coreutils/
 |--------|-----------------|------------------|
 | Backend | Gerbil compiler + Gambit VM | Chez Scheme 10.x |
 | FFI | `begin-ffi` with inline C | C shim library + `foreign-procedure` |
-| Build | `gerbil build` | `make build` (gcc + Chez) |
-| Format | `.ss` Gerbil modules | `.sls` R6RS libraries |
+| Build | `gerbil build` | `make binary` (jerbuild: transpile + whole-program) |
+| Format | `.ss` Gerbil modules | `.ss` Jerboa source → `.sls` (generated) |
 | Getopt | 1-arg callback | 2-arg callback (cmd, hash) |
 
 ## License
 
 Same as gerbil-coreutils.
-# jerboa-coreutils
diff --git a/bin/.generated b/bin/.generated
deleted file mode 100644
index e69de29..0000000
--- a/bin/.generated
+++ /dev/null
diff --git a/bin/arch b/bin/arch
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/arch
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/arch.sps b/bin/arch.sps
deleted file mode 100644
index f882321..0000000
--- a/bin/arch.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils arch))
-(apply main (cdr (command-line)))
diff --git a/bin/b2sum b/bin/b2sum
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/b2sum
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/b2sum.sps b/bin/b2sum.sps
deleted file mode 100644
index 1226351..0000000
--- a/bin/b2sum.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils b2sum))
-(apply main (cdr (command-line)))
diff --git a/bin/base32 b/bin/base32
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/base32
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/base32.sps b/bin/base32.sps
deleted file mode 100644
index 988f131..0000000
--- a/bin/base32.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils base32))
-(apply main (cdr (command-line)))
diff --git a/bin/base64 b/bin/base64
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/base64
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/base64.sps b/bin/base64.sps
deleted file mode 100644
index 7550b7e..0000000
--- a/bin/base64.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils base64))
-(apply main (cdr (command-line)))
diff --git a/bin/basename b/bin/basename
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/basename
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/basename.sps b/bin/basename.sps
deleted file mode 100644
index bd2162c..0000000
--- a/bin/basename.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils basename))
-(apply main (cdr (command-line)))
diff --git a/bin/basenc b/bin/basenc
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/basenc
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/basenc.sps b/bin/basenc.sps
deleted file mode 100644
index ee3f159..0000000
--- a/bin/basenc.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils basenc))
-(apply main (cdr (command-line)))
diff --git a/bin/cat b/bin/cat
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/cat
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/cat.sps b/bin/cat.sps
deleted file mode 100644
index 82b027b..0000000
--- a/bin/cat.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils cat))
-(apply main (cdr (command-line)))
diff --git a/bin/chcon b/bin/chcon
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/chcon
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/chcon.sps b/bin/chcon.sps
deleted file mode 100644
index 12c86ff..0000000
--- a/bin/chcon.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils chcon))
-(apply main (cdr (command-line)))
diff --git a/bin/chgrp b/bin/chgrp
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/chgrp
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/chgrp.sps b/bin/chgrp.sps
deleted file mode 100644
index 2e93161..0000000
--- a/bin/chgrp.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils chgrp))
-(apply main (cdr (command-line)))
diff --git a/bin/chmod b/bin/chmod
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/chmod
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/chmod.sps b/bin/chmod.sps
deleted file mode 100644
index 1ec9f7e..0000000
--- a/bin/chmod.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils chmod))
-(apply main (cdr (command-line)))
diff --git a/bin/chown b/bin/chown
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/chown
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/chown.sps b/bin/chown.sps
deleted file mode 100644
index 4467e01..0000000
--- a/bin/chown.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils chown))
-(apply main (cdr (command-line)))
diff --git a/bin/chroot b/bin/chroot
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/chroot
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/chroot.sps b/bin/chroot.sps
deleted file mode 100644
index 2f7fb27..0000000
--- a/bin/chroot.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils chroot))
-(apply main (cdr (command-line)))
diff --git a/bin/cksum b/bin/cksum
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/cksum
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/cksum.sps b/bin/cksum.sps
deleted file mode 100644
index 635c85d..0000000
--- a/bin/cksum.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils cksum))
-(apply main (cdr (command-line)))
diff --git a/bin/comm b/bin/comm
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/comm
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/comm.sps b/bin/comm.sps
deleted file mode 100644
index 9712a21..0000000
--- a/bin/comm.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils comm))
-(apply main (cdr (command-line)))
diff --git a/bin/cp b/bin/cp
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/cp
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/cp.sps b/bin/cp.sps
deleted file mode 100644
index 871b2d4..0000000
--- a/bin/cp.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils cp))
-(apply main (cdr (command-line)))
diff --git a/bin/csplit b/bin/csplit
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/csplit
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/csplit.sps b/bin/csplit.sps
deleted file mode 100644
index 271b892..0000000
--- a/bin/csplit.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils csplit))
-(apply main (cdr (command-line)))
diff --git a/bin/cut b/bin/cut
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/cut
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/cut.sps b/bin/cut.sps
deleted file mode 100644
index d6eeae4..0000000
--- a/bin/cut.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils cut))
-(apply main (cdr (command-line)))
diff --git a/bin/date b/bin/date
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/date
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/date.sps b/bin/date.sps
deleted file mode 100644
index 29a5761..0000000
--- a/bin/date.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils date))
-(apply main (cdr (command-line)))
diff --git a/bin/dd b/bin/dd
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/dd
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/dd.sps b/bin/dd.sps
deleted file mode 100644
index 89c1a94..0000000
--- a/bin/dd.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils dd))
-(apply main (cdr (command-line)))
diff --git a/bin/df b/bin/df
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/df
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/df.sps b/bin/df.sps
deleted file mode 100644
index f7dcb01..0000000
--- a/bin/df.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils df))
-(apply main (cdr (command-line)))
diff --git a/bin/dir b/bin/dir
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/dir
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/dir.sps b/bin/dir.sps
deleted file mode 100644
index c47cf03..0000000
--- a/bin/dir.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils dir))
-(apply main (cdr (command-line)))
diff --git a/bin/dircolors b/bin/dircolors
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/dircolors
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/dircolors.sps b/bin/dircolors.sps
deleted file mode 100644
index 57bdd4c..0000000
--- a/bin/dircolors.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils dircolors))
-(apply main (cdr (command-line)))
diff --git a/bin/dirname b/bin/dirname
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/dirname
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/dirname.sps b/bin/dirname.sps
deleted file mode 100644
index 3b8cb84..0000000
--- a/bin/dirname.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils dirname))
-(apply main (cdr (command-line)))
diff --git a/bin/du b/bin/du
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/du
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/du.sps b/bin/du.sps
deleted file mode 100644
index 74486e1..0000000
--- a/bin/du.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils du))
-(apply main (cdr (command-line)))
diff --git a/bin/echo b/bin/echo
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/echo
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/echo.sps b/bin/echo.sps
deleted file mode 100644
index 2565050..0000000
--- a/bin/echo.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils echo))
-(apply main (cdr (command-line)))
diff --git a/bin/env b/bin/env
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/env
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/env.sps b/bin/env.sps
deleted file mode 100644
index fd5c312..0000000
--- a/bin/env.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils env))
-(apply main (cdr (command-line)))
diff --git a/bin/expand b/bin/expand
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/expand
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/expand.sps b/bin/expand.sps
deleted file mode 100644
index db3983b..0000000
--- a/bin/expand.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils expand))
-(apply main (cdr (command-line)))
diff --git a/bin/expr b/bin/expr
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/expr
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/expr.sps b/bin/expr.sps
deleted file mode 100644
index f9fe401..0000000
--- a/bin/expr.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils expr))
-(apply main (cdr (command-line)))
diff --git a/bin/factor b/bin/factor
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/factor
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/factor.sps b/bin/factor.sps
deleted file mode 100644
index 3e42970..0000000
--- a/bin/factor.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils factor))
-(apply main (cdr (command-line)))
diff --git a/bin/false b/bin/false
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/false
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/false.sps b/bin/false.sps
deleted file mode 100644
index 54e36d1..0000000
--- a/bin/false.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils false))
-(apply main (cdr (command-line)))
diff --git a/bin/fmt b/bin/fmt
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/fmt
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/fmt.sps b/bin/fmt.sps
deleted file mode 100644
index dbf15aa..0000000
--- a/bin/fmt.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils fmt))
-(apply main (cdr (command-line)))
diff --git a/bin/fold b/bin/fold
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/fold
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/fold.sps b/bin/fold.sps
deleted file mode 100644
index 3a0ee89..0000000
--- a/bin/fold.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils fold))
-(apply main (cdr (command-line)))
diff --git a/bin/groups b/bin/groups
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/groups
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/groups.sps b/bin/groups.sps
deleted file mode 100644
index 488b6e2..0000000
--- a/bin/groups.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils groups))
-(apply main (cdr (command-line)))
diff --git a/bin/head b/bin/head
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/head
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/head.sps b/bin/head.sps
deleted file mode 100644
index dda7b78..0000000
--- a/bin/head.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils head))
-(apply main (cdr (command-line)))
diff --git a/bin/hostid b/bin/hostid
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/hostid
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/hostid.sps b/bin/hostid.sps
deleted file mode 100644
index dcdfc96..0000000
--- a/bin/hostid.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils hostid))
-(apply main (cdr (command-line)))
diff --git a/bin/hostname b/bin/hostname
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/hostname
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/hostname.sps b/bin/hostname.sps
deleted file mode 100644
index 78f9e67..0000000
--- a/bin/hostname.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils hostname))
-(apply main (cdr (command-line)))
diff --git a/bin/id b/bin/id
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/id
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/id.sps b/bin/id.sps
deleted file mode 100644
index aa02bdb..0000000
--- a/bin/id.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils id))
-(apply main (cdr (command-line)))
diff --git a/bin/install b/bin/install
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/install
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/install.sps b/bin/install.sps
deleted file mode 100644
index 7b24e09..0000000
--- a/bin/install.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils install))
-(apply main (cdr (command-line)))
diff --git a/bin/join b/bin/join
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/join
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/join.sps b/bin/join.sps
deleted file mode 100644
index 1697c58..0000000
--- a/bin/join.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils join))
-(apply main (cdr (command-line)))
diff --git a/bin/kill b/bin/kill
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/kill
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/kill.sps b/bin/kill.sps
deleted file mode 100644
index 253750b..0000000
--- a/bin/kill.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils kill))
-(apply main (cdr (command-line)))
diff --git a/bin/link b/bin/link
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/link
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/link.sps b/bin/link.sps
deleted file mode 100644
index ce9d59a..0000000
--- a/bin/link.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils link))
-(apply main (cdr (command-line)))
diff --git a/bin/ln b/bin/ln
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/ln
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/ln.sps b/bin/ln.sps
deleted file mode 100644
index 0e0ab45..0000000
--- a/bin/ln.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils ln))
-(apply main (cdr (command-line)))
diff --git a/bin/logname b/bin/logname
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/logname
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/logname.sps b/bin/logname.sps
deleted file mode 100644
index edf0c6b..0000000
--- a/bin/logname.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils logname))
-(apply main (cdr (command-line)))
diff --git a/bin/ls b/bin/ls
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/ls
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/ls.sps b/bin/ls.sps
deleted file mode 100644
index 34ab358..0000000
--- a/bin/ls.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils ls))
-(apply main (cdr (command-line)))
diff --git a/bin/md5sum b/bin/md5sum
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/md5sum
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/md5sum.sps b/bin/md5sum.sps
deleted file mode 100644
index c3292de..0000000
--- a/bin/md5sum.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils md5sum))
-(apply main (cdr (command-line)))
diff --git a/bin/mkdir b/bin/mkdir
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/mkdir
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/mkdir.sps b/bin/mkdir.sps
deleted file mode 100644
index 126ff9b..0000000
--- a/bin/mkdir.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils mkdir))
-(apply main (cdr (command-line)))
diff --git a/bin/mkfifo b/bin/mkfifo
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/mkfifo
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/mkfifo.sps b/bin/mkfifo.sps
deleted file mode 100644
index 041501c..0000000
--- a/bin/mkfifo.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils mkfifo))
-(apply main (cdr (command-line)))
diff --git a/bin/mknod b/bin/mknod
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/mknod
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/mknod.sps b/bin/mknod.sps
deleted file mode 100644
index 153edcc..0000000
--- a/bin/mknod.sps
+++ /dev/null
@@ -1,4 +0,0 @@
-#!chezscheme
-(import (except (chezscheme) make-hash-table hash-table? iota 1+ 1- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name))
-(import (jerboa-coreutils mknod))
-(apply main (cdr (command-line)))
diff --git a/bin/mktemp b/bin/mktemp
deleted file mode 120000
index 5dcbc85..0000000
--- a/bin/mktemp
+++ /dev/null
@@ -1 +0,0 @@
-jerboa-coreutils
\ No newline at end of file
diff --git a/bin/mktemp.sps b/bin/mktemp.sps
deleted file mode 100644
index 7778a6c..0000000
--- a/bin/mktemp.sps
+++ /dev/null