Migrate 175 .sls -> .ss across lib/{std,jerboa}

ober

e0d98b5ebee7f652889d4ad5550f24dc7f844b15

diff --git a/lib/jerboa/cache.sls b/lib/jerboa/cache.sls
deleted file mode 100644
index 6a3d596..0000000
--- a/lib/jerboa/cache.sls
+++ /dev/null
@@ -1,146 +0,0 @@
-#!chezscheme
-;;; (jerboa cache) — Content-addressed compilation cache
-;;;
-;;; Hashes source + dependencies + Chez version to produce cache keys.
-;;; Avoids recompilation when inputs haven't changed.
-;;;
-;;; Cache layout:
-;;;   ~/.jerboa/cache/<sha256>.so
-;;;
-;;; Cache key = SHA-256(source-content || dep-hash-1 || ... || chez-version || opt-level)
-
-(library (jerboa cache)
-  (export
-    cache-directory
-    cache-lookup
-    cache-store!
-    cache-key
-    with-compilation-cache
-    cache-stats
-    cache-clear!)
-  (import (chezscheme))
-
-  ;; ========== Configuration ==========
-
-  (define cache-directory
-    (make-parameter
-      (let ([home (getenv "HOME")])
-        (if home
-          (string-append home "/.jerboa/cache")
-          "/tmp/jerboa-cache"))))
-
-  ;; ========== Hashing ==========
-
-  ;; Simple string hash using Chez's built-in (not cryptographic, but fast)
-  ;; For production, this should use SHA-256 from chez-crypto
-  ;; FNV-1a hash producing a 128-bit hex string for cache keys
-  (define (string-hash-256 str)
-    (let ([len (string-length str)])
-      (let loop ([i 0] [h1 14695981039346656037] [h2 6364136223846793005])
-        (if (= i len)
-          (string-append
-            (number->string (mod (abs h1) (expt 2 64)) 16)
-            (number->string (mod (abs h2) (expt 2 64)) 16))
-          (let ([byte (char->integer (string-ref str i))])
-            (loop (+ i 1)
-                  (mod (* (bitwise-xor h1 byte) 1099511628211) (expt 2 64))
-                  (mod (* (bitwise-xor h2 (+ byte 37)) 6364136223846793005) (expt 2 64))))))))
-
-  ;; Compute cache key from source file and its dependencies
-  (define (cache-key source-path dep-hashes opt-level)
-    (let* ([source-content (call-with-port (open-input-file source-path)
-                             (lambda (p)
-                               (get-string-all p)))]
-           [chez-ver (scheme-version)]
-           [key-material (apply string-append
-                           source-content
-                           (number->string opt-level)
-                           chez-ver
-                           (map (lambda (h) (or h "")) dep-hashes))])
-      (string-hash-256 key-material)))
-
-  ;; ========== Cache Operations ==========
-
-  (define (ensure-cache-dir!)
-    (let ([dir (cache-directory)])
-      (unless (file-exists? dir)
-        (mkdir-p dir))))
-
-  (define (cache-path key)
-    (string-append (cache-directory) "/" key ".so"))
-
-  ;; Look up a cached .so by its key
-  ;; Returns the path if found, #f if not
-  (define (cache-lookup key)
-    (let ([path (cache-path key)])
-      (if (file-exists? path) path #f)))
-
-  ;; Store a compiled .so file in the cache
-  (define (cache-store! key so-path)
-    (ensure-cache-dir!)
-    (let ([dest (cache-path key)])
-      (unless (file-exists? dest)
-        ;; Copy the file
-        (let ([data (call-with-port (open-file-input-port so-path)
-                      (lambda (p) (get-bytevector-all p)))])
-          (call-with-port (open-file-output-port dest)
-            (lambda (p) (put-bytevector p data)))))))
-
-  ;; ========== High-Level API ==========
-
-  ;; Compile a file with caching
-  ;; Returns the .so path (from cache or freshly compiled)
-  (define (with-compilation-cache source-path output-path dep-hashes opt-level compile-thunk)
-    (let* ([key (cache-key source-path dep-hashes opt-level)]
-           [cached (cache-lookup key)])
-      (if cached
-        ;; Cache hit — copy to output
-        (begin
-          (let ([data (call-with-port (open-file-input-port cached)
-                        (lambda (p) (get-bytevector-all p)))])
-            (call-with-port (open-file-output-port output-path
-                              (file-options no-fail))
-              (lambda (p) (put-bytevector p data))))
-          output-path)
-        ;; Cache miss — compile and store
-        (begin
-          (compile-thunk)
-          (when (file-exists? output-path)
-            (cache-store! key output-path))
-          output-path))))
-
-  ;; ========== Maintenance ==========
-
-  (define (cache-stats)
-    (let ([dir (cache-directory)])
-      (if (file-exists? dir)
-        (let ([files (directory-list dir)])
-          (let ([count (length files)]
-                [size (fold-left
-                        (lambda (acc f)
-                          (let* ([path (string-append dir "/" f)]
-                                 [fsize (call-with-port (open-file-input-port path)
-                                          (lambda (p)
-                                            (set-port-position! p (+ (port-position p) 0))
-                                            (let ([data (get-bytevector-all p)])
-                                              (if (eof-object? data) 0
-                                                  (bytevector-length data)))))])
-                            (+ acc fsize)))
-                        0 files)])
-            (values count size)))
-        (values 0 0))))
-
-  (define (cache-clear!)
-    (let ([dir (cache-directory)])
-      (when (file-exists? dir)
-        (for-each
-          (lambda (f)
-            (delete-file (string-append dir "/" f)))
-          (directory-list dir)))))
-
-  ;; Create ~/.jerboa/cache directory tree
-  (define (mkdir-p path)
-    ;; Use system mkdir -p since Chez doesn't have recursive mkdir
-    (system (format "mkdir -p '~a'" path)))
-
-  ) ;; end library
diff --git a/lib/jerboa/cache.ss b/lib/jerboa/cache.ss
new file mode 100644
index 0000000..359df6a
--- /dev/null
+++ b/lib/jerboa/cache.ss
@@ -0,0 +1,147 @@
+#!chezscheme
+;;; (jerboa cache) — Content-addressed compilation cache
+;;;
+;;; Hashes source + dependencies + Chez version to produce cache keys.
+;;; Avoids recompilation when inputs haven't changed.
+;;;
+;;; Cache layout:
+;;;   ~/.jerboa/cache/<sha256>.so
+;;;
+;;; Cache key = SHA-256(source-content || dep-hash-1 || ... || chez-version || opt-level)
+
+(library (jerboa cache)
+  (export
+    cache-directory
+    cache-lookup
+    cache-store!
+    cache-key
+    with-compilation-cache
+    cache-stats
+    cache-clear!)
+  (import (chezscheme)
+          (only (jerboa core) def))
+
+  ;; ========== Configuration ==========
+
+  (def cache-directory
+    (make-parameter
+      (let ([home (getenv "HOME")])
+        (if home
+          (string-append home "/.jerboa/cache")
+          "/tmp/jerboa-cache"))))
+
+  ;; ========== Hashing ==========
+
+  ;; Simple string hash using Chez's built-in (not cryptographic, but fast)
+  ;; For production, this should use SHA-256 from chez-crypto
+  ;; FNV-1a hash producing a 128-bit hex string for cache keys
+  (def (string-hash-256 str)
+    (let ([len (string-length str)])
+      (let loop ([i 0] [h1 14695981039346656037] [h2 6364136223846793005])
+        (if (= i len)
+          (string-append
+            (number->string (mod (abs h1) (expt 2 64)) 16)
+            (number->string (mod (abs h2) (expt 2 64)) 16))
+          (let ([byte (char->integer (string-ref str i))])
+            (loop (+ i 1)
+                  (mod (* (bitwise-xor h1 byte) 1099511628211) (expt 2 64))
+                  (mod (* (bitwise-xor h2 (+ byte 37)) 6364136223846793005) (expt 2 64))))))))
+
+  ;; Compute cache key from source file and its dependencies
+  (def (cache-key source-path dep-hashes opt-level)
+    (let* ([source-content (call-with-port (open-input-file source-path)
+                             (lambda (p)
+                               (get-string-all p)))]
+           [chez-ver (scheme-version)]
+           [key-material (apply string-append
+                           source-content
+                           (number->string opt-level)
+                           chez-ver
+                           (map (lambda (h) (or h "")) dep-hashes))])
+      (string-hash-256 key-material)))
+
+  ;; ========== Cache Operations ==========
+
+  (def (ensure-cache-dir!)
+    (let ([dir (cache-directory)])
+      (unless (file-exists? dir)
+        (mkdir-p dir))))
+
+  (def (cache-path key)
+    (string-append (cache-directory) "/" key ".so"))
+
+  ;; Look up a cached .so by its key
+  ;; Returns the path if found, #f if not
+  (def (cache-lookup key)
+    (let ([path (cache-path key)])
+      (if (file-exists? path) path #f)))
+
+  ;; Store a compiled .so file in the cache
+  (def (cache-store! key so-path)
+    (ensure-cache-dir!)
+    (let ([dest (cache-path key)])
+      (unless (file-exists? dest)
+        ;; Copy the file
+        (let ([data (call-with-port (open-file-input-port so-path)
+                      (lambda (p) (get-bytevector-all p)))])
+          (call-with-port (open-file-output-port dest)
+            (lambda (p) (put-bytevector p data)))))))
+
+  ;; ========== High-Level API ==========
+
+  ;; Compile a file with caching
+  ;; Returns the .so path (from cache or freshly compiled)
+  (def (with-compilation-cache source-path output-path dep-hashes opt-level compile-thunk)
+    (let* ([key (cache-key source-path dep-hashes opt-level)]
+           [cached (cache-lookup key)])
+      (if cached
+        ;; Cache hit — copy to output
+        (begin
+          (let ([data (call-with-port (open-file-input-port cached)
+                        (lambda (p) (get-bytevector-all p)))])
+            (call-with-port (open-file-output-port output-path
+                              (file-options no-fail))
+              (lambda (p) (put-bytevector p data))))
+          output-path)
+        ;; Cache miss — compile and store
+        (begin
+          (compile-thunk)
+          (when (file-exists? output-path)
+            (cache-store! key output-path))
+          output-path))))
+
+  ;; ========== Maintenance ==========
+
+  (def (cache-stats)
+    (let ([dir (cache-directory)])
+      (if (file-exists? dir)
+        (let ([files (directory-list dir)])
+          (let ([count (length files)]
+                [size (fold-left
+                        (lambda (acc f)
+                          (let* ([path (string-append dir "/" f)]
+                                 [fsize (call-with-port (open-file-input-port path)
+                                          (lambda (p)
+                                            (set-port-position! p (+ (port-position p) 0))
+                                            (let ([data (get-bytevector-all p)])
+                                              (if (eof-object? data) 0
+                                                  (bytevector-length data)))))])
+                            (+ acc fsize)))
+                        0 files)])
+            (values count size)))
+        (values 0 0))))
+
+  (def (cache-clear!)
+    (let ([dir (cache-directory)])
+      (when (file-exists? dir)
+        (for-each
+          (lambda (f)
+            (delete-file (string-append dir "/" f)))
+          (directory-list dir)))))
+
+  ;; Create ~/.jerboa/cache directory tree
+  (def (mkdir-p path)
+    ;; Use system mkdir -p since Chez doesn't have recursive mkdir
+    (system (format "mkdir -p '~a'" path)))
+
+  ) ;; end library
diff --git a/lib/jerboa/cloj.sls b/lib/jerboa/cloj.sls
deleted file mode 100644
index c339c7e..0000000
--- a/lib/jerboa/cloj.sls
+++ /dev/null
@@ -1,97 +0,0 @@
-#!chezscheme
-;;; jerboa/cloj.sls — Clojure reader mode support
-;;;
-;;; Provides:
-;;;   reader-cloj-mode — parameter: activates Clojure syntax in the Jerboa reader
-;;;   fn-literal       — macro: expands  #(...)  anonymous function reader literals
-;;;
-;;; The Jerboa reader expands  #(+ % 1)  to  (fn-literal + % 1)  when cloj mode
-;;; is active.  fn-literal walks the body, detects % / %1 / %2 / %& references,
-;;; and emits the appropriate (lambda ...) form.
-;;;
-;;; Activation: add  #!cloj  at the top of any Jerboa source file, or call
-;;;   (reader-cloj-mode #t)  programmatically.
-
-(library (jerboa cloj)
-  (export reader-cloj-mode fn-literal activate-cloj-reader!)
-
-  (import (chezscheme)
-          (only (jerboa reader) reader-cloj-mode))
-
-  ;;; activate-cloj-reader! — call from library bodies to enable cloj mode
-  ;; Wraps (reader-cloj-mode #t) so that libraries with restricted import
-  ;; environments can activate cloj mode with a single function call.
-  (define (activate-cloj-reader!) (reader-cloj-mode #t))
-
-  ;;; fn-literal — expands #(...) anonymous function literals
-  ;;
-  ;; #(+ % 1)         → (lambda (%1) (+ %1 1))
-  ;; #(str %1 " " %2) → (lambda (%1 %2) (str %1 " " %2))
-  ;; #(apply + %&)    → (lambda %& (apply + %&))
-  ;; #(begin (f %) %) → (lambda (%1) (begin (f %1) %1))
-  ;;
-  ;; % is an alias for %1. %2, %3, etc. for more positional args.
-  ;; %& collects all extra args as a rest list.
-
-  (define-syntax fn-literal
-    (lambda (stx)
-
-      ;; Replace bare % with %1 throughout a datum tree
-      (define (normalize d)
-        (cond
-          ((eq? d '%) '%1)
-          ((pair? d) (cons (normalize (car d)) (normalize (cdr d))))
-          (else d)))
-
-      ;; Walk datum, return (max-positional-n . has-rest?)
-      ;; Recognises: %1 %2 %3 ... (and %) %&
-      (define (find-info d)
-        (let loop ((d d) (n 0) (r? #f))
-          (cond
-            ((null? d)    (cons n r?))
-            ((eq? d '%&)  (cons n #t))
-            ((symbol? d)
-             (let* ((s   (symbol->string d))
-                    (len (string-length s)))
-               (if (and (> len 1) (char=? (string-ref s 0) #\%))
-                   (let ((num (string->number (substring s 1 len))))
-                     (if num (cons (max n num) r?) (cons n r?)))
-                   (cons n r?))))
-            ((pair? d)
-             (let ((r1 (loop (car d) n r?)))
-               (loop (cdr d) (car r1) (cdr r1))))
-            (else (cons n r?)))))
-
-      ;; Build list (1 2 ... n)
-      (define (range-1-to n)
-        (let lp ((i n) (acc '()))
-          (if (= i 0) acc (lp (- i 1) (cons i acc)))))
-
-      (syntax-case stx ()
-        ((kw body-form ...)
-         (let* ((raw   (syntax->datum #'(body-form ...)))
-                (nb    (normalize raw))
-                ;; If there's exactly one form, use it directly; else wrap in begin
-                (body  (if (and (pair? nb) (null? (cdr nb)))
-                           (car nb)
-                           (cons 'begin nb)))
-                (info  (find-info body))
-                (max-n (car info))
-                (rest? (cdr info))
-                (positional (map (lambda (i)
-                                   (string->symbol
-                                     (string-append "%" (number->string i))))
-                                 (range-1-to max-n)))
-                ;; arg-list:
-                ;;   no args + rest   → %&          (variadic bare symbol)
-                ;;   no args, no rest → ()           (nullary)
-                ;;   args + rest      → (%1 %2 . %&) (dotted list via append)
-                ;;   args, no rest    → (%1 %2 ...)
-                (arg-list (cond
-                            ((and (null? positional) rest?) '%&)
-                            ((null? positional)             '())
-                            (rest?  (append positional '%&)) ;; (append list sym) = dotted
-                            (else   positional))))
-           (datum->syntax #'kw `(lambda ,arg-list ,body)))))))
-
-  ) ;; end library
diff --git a/lib/jerboa/cloj.ss b/lib/jerboa/cloj.ss
new file mode 100644
index 0000000..37b9a8c
--- /dev/null
+++ b/lib/jerboa/cloj.ss
@@ -0,0 +1,98 @@
+#!chezscheme
+;;; jerboa/cloj.sls — Clojure reader mode support
+;;;
+;;; Provides:
+;;;   reader-cloj-mode — parameter: activates Clojure syntax in the Jerboa reader
+;;;   fn-literal       — macro: expands  #(...)  anonymous function reader literals
+;;;
+;;; The Jerboa reader expands  #(+ % 1)  to  (fn-literal + % 1)  when cloj mode
+;;; is active.  fn-literal walks the body, detects % / %1 / %2 / %& references,
+;;; and emits the appropriate (lambda ...) form.
+;;;
+;;; Activation: add  #!cloj  at the top of any Jerboa source file, or call
+;;;   (reader-cloj-mode #t)  programmatically.
+
+(library (jerboa cloj)
+  (export reader-cloj-mode fn-literal activate-cloj-reader!)
+
+  (import (chezscheme)
+          (only (jerboa reader) reader-cloj-mode)
+          (only (jerboa core) def))
+
+  ;;; activate-cloj-reader! — call from library bodies to enable cloj mode
+  ;; Wraps (reader-cloj-mode #t) so that libraries with restricted import
+  ;; environments can activate cloj mode with a single function call.
+  (def (activate-cloj-reader!) (reader-cloj-mode #t))
+
+  ;;; fn-literal — expands #(...) anonymous function literals
+  ;;
+  ;; #(+ % 1)         → (lambda (%1) (+ %1 1))
+  ;; #(str %1 " " %2) → (lambda (%1 %2) (str %1 " " %2))
+  ;; #(apply + %&)    → (lambda %& (apply + %&))
+  ;; #(begin (f %) %) → (lambda (%1) (begin (f %1) %1))
+  ;;
+  ;; % is an alias for %1. %2, %3, etc. for more positional args.
+  ;; %& collects all extra args as a rest list.
+
+  (define-syntax fn-literal
+    (lambda (stx)
+
+      ;; Replace bare % with %1 throughout a datum tree
+      (define (normalize d)
+        (cond
+          ((eq? d '%) '%1)
+          ((pair? d) (cons (normalize (car d)) (normalize (cdr d))))
+          (else d)))
+
+      ;; Walk datum, return (max-positional-n . has-rest?)
+      ;; Recognises: %1 %2 %3 ... (and %) %&
+      (define (find-info d)
+        (let loop ((d d) (n 0) (r? #f))
+          (cond
+            ((null? d)    (cons n r?))
+            ((eq? d '%&)  (cons n #t))
+            ((symbol? d)
+             (let* ((s   (symbol->string d))
+                    (len (string-length s)))
+               (if (and (> len 1) (char=? (string-ref s 0) #\%))
+                   (let ((num (string->number (substring s 1 len))))
+                     (if num (cons (max n num) r?) (cons n r?)))
+                   (cons n r?))))
+            ((pair? d)
+             (let ((r1 (loop (car d) n r?)))
+               (loop (cdr d) (car r1) (cdr r1))))
+            (else (cons n r?)))))
+
+      ;; Build list (1 2 ... n)
+      (define (range-1-to n)
+        (let lp ((i n) (acc '()))
+          (if (= i 0) acc (lp (- i 1) (cons i acc)))))
+
+      (syntax-case stx ()
+        ((kw body-form ...)
+         (let* ((raw   (syntax->datum #'(body-form ...)))
+                (nb    (normalize raw))
+                ;; If there's exactly one form, use it directly; else wrap in begin
+                (body  (if (and (pair? nb) (null? (cdr nb)))
+                           (car nb)
+                           (cons 'begin nb)))
+                (info  (find-info body))
+                (max-n (car info))
+                (rest? (cdr info))
+                (positional (map (lambda (i)
+                                   (string->symbol
+                                     (string-append "%" (number->string i))))
+                                 (range-1-to max-n)))
+                ;; arg-list:
+                ;;   no args + rest   → %&          (variadic bare symbol)
+                ;;   no args, no rest → ()           (nullary)
+                ;;   args + rest      → (%1 %2 . %&) (dotted list via append)
+                ;;   args, no rest    → (%1 %2 ...)
+                (arg-list (cond
+                            ((and (null? positional) rest?) '%&)
+                            ((null? positional)             '())
+                            (rest?  (append positional '%&)) ;; (append list sym) = dotted
+                            (else   positional))))
+           (datum->syntax #'kw `(lambda ,arg-list ,body)))))))
+
+  ) ;; end library
diff --git a/lib/jerboa/prelude.sls b/lib/jerboa/prelude.sls
deleted file mode 100644
index 82e4b6c..0000000
--- a/lib/jerboa/prelude.sls
+++ /dev/null
@@ -1,350 +0,0 @@
-#!chezscheme
-;;; jerboa/prelude -- One-import-to-rule-them-all
-;;;
-;;; (import (jerboa prelude)) gives you the full Jerboa API:
-;;; - Core macros: def, defstruct, defmethod, match, try/catch, etc.
-;;; - Runtime: hash tables, method dispatch, keywords
-;;; - Standard library: sort, format, JSON, paths, strings, lists, etc.
-;;; - Advanced: result types, datetime, iterators, CSV, pretty-printer
-;;; - Ergonomic typing: using, :, maybe
-;;; - FFI: c-lambda, define-c-lambda
-
-(library (jerboa prelude)
-  (export
-    ;; ---- Core macros ----
-    def def* defrule defrules
-    defstruct defclass defmethod
-    match match/strict
-    define-match-type define-sealed-hierarchy define-active-pattern
-    try catch finally
-    while until
-
-    ;; hash constructors
-    hash-literal hash-eq-literal
-    let-hash
-
-    ;; ---- Runtime ----
-    ~ bind-method! call-method
-    make-hash-table make-hash-table-eq
-    hash-ref hash-get hash-put! hash-update! hash-remove!
-    hash-key? hash->list hash->plist hash-for-each hash-map hash-fold
-    hash-find hash-keys hash-values hash-copy hash-clear!
-    hash-merge hash-merge! hash-length hash-table?
-    list->hash-table plist->hash-table
-    hash-set
-    keyword? keyword->string string->keyword make-keyword
-    error-message error-irritants error-trace
-    displayln 1+ 1-
-    iota last-pair
-    *method-tables*
-    register-struct-type! *struct-types*
-    struct-predicate struct-field-ref struct-field-set!
-    struct-type-info
-
-    ;; ---- std/sort ----
-    sort sort! stable-sort stable-sort!
-
-    ;; ---- std/format ----
-    format printf fprintf eprintf
-
-    ;; ---- std/error ----
-    Error ContractViolation
-
-    ;; ---- std/sugar ----
-    chain chain-and assert!
-    unwind-protect with-id with-lock with-catch
-    cut cute <> <...>
-    awhen aif when-let if-let
-    -> ->> as-> some-> some->> cond-> cond->>
-    ->? ->>?
-    with-resource str alist defn defrecord
-    let-alist define-enum capture dotimes define-values
-
-    ;; ---- std/text/json ----
-    read-json write-json json-object->string string->json-object
-
-    ;; ---- std/os/platform ----
-    cpu-count
-
-    ;; ---- std/os/path ----
-    path-expand path-normalize path-directory path-strip-directory
-    path-extension path-strip-extension
-    path-join path-absolute?
-
-    ;; ---- std/regex ----
-    re re?
-    re-match? re-search
-    re-find-all re-groups
-    re-replace re-replace-all
-    re-split re-fold
-    re-match-full re-match-group re-match-groups
-    re-match-start re-match-end re-match-named
-
-    ;; ---- std/rx ----
-    rx define-rx
-
-    ;; ---- std/misc/string ----
-    string-split string-join string-trim
-    string-prefix? string-suffix?
-    string-contains string-index
-    string-empty?
-    string-match? string-find string-find-all
-
-    ;; ---- std/misc/list ----
-    flatten unique snoc
-    take drop
-    every any
-    filter-map
-    group-by
-    zip
-    frequencies
-    partition partition-all partition-by
-    interleave interpose
-    mapcat
-    distinct
-    keep
-    some
-    iterate-n
-    reductions
-    take-last drop-last
-    split-at split-with
-    ;; Gerbil v0.19 compat
-    append-map append1 flatten1
-    push! pop!
-    for-each!
-    take-while take-until drop-while drop-until
-    butlast slice split
-    length=? length<? length<=? length>? length>=?
-    length=n? length<n? length<=n? length>n? length>=n?
-    group-consecutive group-n-consecutive group-same
-    rassoc every-consecutive?
-    map/car first-and-only when/list
-    with-list-builder call-with-list-builder
-    duplicates delete-duplicates/hash
-
-    ;; ---- std/misc/alist ----
-    agetq agetv aget
-    asetq! asetv! aset!
-    pgetq pgetv pget
-    alist->hash-table
-    ;; Gerbil v0.19 compat
-    alist? acons
-    asetq asetv aset
-    aremq aremv arem
-    aremq! aremv! arem!
-    psetq psetv pset
-    psetq! psetv! pset!
-    premq premv prem
-    premq! premv! prem!
-    plist->alist* alist->plist*
-
-    ;; ---- std/misc/ports ----
-    read-all-as-string read-all-as-lines
-    read-file-string read-file-lines
-    write-file-string
-    with-input-from-string with-output-to-string
-
-    ;; ---- std/misc/func ----
-    compose compose1 identity constantly flip
-    curry curryn negate conjoin disjoin
-    memo-proc juxt
-    partial complement comp
-    fnil every-pred some-fn
-
-    ;; ---- std/iter ----
-    for for/collect for/fold for/or for/and
-    in-list in-vector in-range in-string
-    in-hash-keys in-hash-values in-hash-pairs
-    in-naturals in-indexed
-    in-port in-lines in-chars in-bytes in-producer
-
-    ;; ---- std/result ----
-    ok err
-    ok? err? result?
-    unwrap unwrap-err unwrap-or unwrap-or-else
-    map-ok map-err
-    and-then or-else
-    flatten-result
-    result->values
-    try-result try-result*
-    result->option
-    results-partition
-    map-results
-    filter-ok filter-err
-    sequence-results
-    ok->list err->list
-
-    ;; ---- std/datetime ----
-    make-datetime datetime?
-    make-date make-time
-    datetime-now datetime-utc-now
-    datetime-year datetime-month datetime-day
-    datetime-hour datetime-minute datetime-second
-    datetime-nanosecond datetime-offset
-    parse-datetime parse-date parse-time
-    datetime->string date->string time->string
-    datetime->iso8601
-    datetime->epoch epoch->datetime
-    datetime->julian julian->datetime
-    datetime-add datetime-subtract
-    datetime-diff
-    duration duration? duration-seconds duration-nanoseconds
-    make-duration
-    datetime<? datetime>? datetime=? datetime<=? datetime>=?
-    datetime-min datetime-max datetime-clamp
-    day-of-week day-of-year days-in-month leap-year?
-    datetime->alist
-    datetime-truncate
-    datetime-floor-hour datetime-floor-day datetime-floor-month
-
-    ;; ---- std/debug/pp ----
-    pp pp-to-string pprint
-    ppd ppd-to-string
-
-    ;; ---- std/csv ----
-    read-csv read-csv-file csv-port->rows
-    write-csv write-csv-file rows->csv-string
-    csv->alists alists->csv
-
-    ;; ---- FFI ----
-    c-lambda define-c-lambda
-    begin-ffi c-declare
-
-    ;; ---- std/ergo ----
-    using : maybe list-of?
-
-    ;; ---- std/misc/atom (Gerbil atom + Clojure aliases) ----
-    ;; Gerbil-style:
-    atom atom? atom-deref atom-reset! atom-swap! atom-update!
-    ;; Clojure-style aliases (familiar to clojure users)
-    deref reset! swap! compare-and-set!
-    ;; Watches (fires on every successful swap/reset/update/CAS)
-    add-watch! remove-watch!
-    ;; Volatiles (single-threaded transient cells for transducers)
-    volatile! volatile? vreset! vswap! vderef
-
-    ;; ---- std/misc/meta (Clojure-style metadata wrappers) ----
-    with-meta meta vary-meta meta-wrapped? strip-meta
-
-    ;; ---- std/misc/shared (atomic cell with CAS) ----
-    make-shared shared? shared-ref shared-set!
-    shared-update! shared-cas! shared-swap!
-
-    ;; ---- std/misc/nested (Clojure-style nested access) ----
-    ;; get-in is polymorphic (imap/chash/hash/vector/alist);
-    ;; assoc-in / update-in are pure (imap only) and return new containers;
-    ;; assoc-in! / update-in! mutate chash/hash/vector in place.
-    get-in assoc-in update-in assoc-in! update-in!
-    nested-get nested-empty-like
-
-    ;; ---- Timing helpers ----
-    sleep-ms
-
-    ;; ---- AI compatibility aliases ----
-    ;; Common names LLMs hallucinate from Racket/Gerbil/Gambit/CL training data.
-    ;; These are thin aliases so AI-generated code works on the first try.
-    hash-has-key? hash-table-set!        ;; Racket
-    directory-exists?                     ;; Gambit
-    eql?                                 ;; Common Lisp
-    random-integer                       ;; Gambit
-    read-line                            ;; Gambit
-    force-output                         ;; Gambit
-    string-map                           ;; Racket/R7RS
-    processor-count                      ;; Racket
-    ;; Regex aliases (common generic names from Python, Ruby, JS training data)
-    regex-match regex-search regex-replace regex-replace-all)
-
-  (import
-    (except (chezscheme)
-            make-hash-table hash-table?
-            sort sort!
-            printf fprintf
-            path-extension path-absolute?
-            with-input-from-string with-output-to-string
-            iota 1+ 1-
-            partition
-            make-date make-time
-            atom?
-            meta)
-    (only (jerboa core)
-      def def* defrule defrules
-      defstruct defclass defmethod
-      try catch finally
-      while until
-      hash-literal hash-eq-literal
-      let-hash)
-    (only (std match2)
-      match match/strict
-      define-match-type define-sealed-hierarchy define-active-pattern)
-    (jerboa runtime)
-    (jerboa ffi)
-    (std sort)
-    (std format)
-    (except (std error) error-message error-irritants error-trace error?
-                        with-exception-handler)
-    (except (std sugar) try catch finally)
-    (std text json)
-    (only (std os platform) platform-cpu-count)
-    (std os path)
-    (std regex)
-    (std rx)
-    (std misc string)
-    (std misc list)
-    (std misc alist)
-    (std misc ports)
-    (std misc func)
-    (std iter)
-    (std result)
-    (std datetime)
-    (std debug pp)
-    (std csv)
-    (std ergo)
-    (std misc atom)
-    (std misc meta)
-    (std misc shared)
-    (std misc nested)
-    ;; Private access to Chez's make-time (shadowed above) so we can
-    ;; build a time-duration record for the sleep-ms wrapper.
-    (rename (only (chezscheme) make-time)
-            (make-time %chez-make-time)))
-
-  ;; ---- System ----
-  (define cpu-count platform-cpu-count)
-  (define processor-count platform-cpu-count)  ;; Racket name
-
-  ;; ---- AI compatibility aliases ----
-  (define hash-has-key? hash-key?)
-  (define hash-table-set! hash-put!)
-  (define directory-exists? file-directory?)
-  (define eql? eqv?)
-  (define random-integer random)
-  (define (read-line . args)
-    (if (null? args)
-        (get-line (current-input-port))
-        (get-line (car args))))
-  (define (force-output . args)
-    (flush-output-port
-      (if (null? args) (current-output-port) (car args))))
-  (define (string-map f s)
-    (list->string (map f (string->list s))))
-
-  ;; ---- Regex AI compatibility aliases ----
-  ;; LLMs trained on Python/Ruby/JavaScript commonly use these generic names.
-  (define (regex-match pat str)       (re-search pat str))
-  (define (regex-search pat str)      (re-search pat str))
-  (define (regex-replace pat str rep) (re-replace pat str rep))
-  (define (regex-replace-all pat str rep) (re-replace-all pat str rep))
-
-  ;; ---- Timing helpers ----
-  ;; (sleep-ms ms) sleeps for MS milliseconds. Wraps Chez's
-  ;; `(sleep (make-time 'time-duration ns sec))` so users never have
-  ;; to reach for `make-time` (which the prelude shadows with a
-  ;; date-style constructor). MS must be a non-negative integer.
-  (define (sleep-ms ms)
-    (unless (and (integer? ms) (>= ms 0))
-      (error 'sleep-ms "ms must be a non-negative integer" ms))
-    (let ([sec (quotient ms 1000)]
-          [ns  (* (remainder ms 1000) 1000000)])
-      (sleep (%chez-make-time 'time-duration ns sec))))
-
-  ) ;; end library
diff --git a/lib/jerboa/prelude.ss b/lib/jerboa/prelude.ss
new file mode 100644
index 0000000..1a24ded
--- /dev/null
+++ b/lib/jerboa/prelude.ss
@@ -0,0 +1,351 @@
+#!chezscheme
+;;; jerboa/prelude -- One-import-to-rule-them-all
+;;;
+;;; (import (jerboa prelude)
+;;;         (only (jerboa core) def)) gives you the full Jerboa API:
+;;; - Core macros: def, defstruct, defmethod, match, try/catch, etc.
+;;; - Runtime: hash tables, method dispatch, keywords
+;;; - Standard library: sort, format, JSON, paths, strings, lists, etc.
+;;; - Advanced: result types, datetime, iterators, CSV, pretty-printer
+;;; - Ergonomic typing: using, :, maybe
+;;; - FFI: c-lambda, define-c-lambda
+
+(library (jerboa prelude)
+  (export
+    ;; ---- Core macros ----
+    def def* defrule defrules
+    defstruct defclass defmethod
+    match match/strict
+    define-match-type define-sealed-hierarchy define-active-pattern
+    try catch finally
+    while until
+
+    ;; hash constructors
+    hash-literal hash-eq-literal
+    let-hash
+
+    ;; ---- Runtime ----
+    ~ bind-method! call-method
+    make-hash-table make-hash-table-eq
+    hash-ref hash-get hash-put! hash-update! hash-remove!
+    hash-key? hash->list hash->plist hash-for-each hash-map hash-fold
+    hash-find hash-keys hash-values hash-copy hash-clear!
+    hash-merge hash-merge! hash-length hash-table?
+    list->hash-table plist->hash-table
+    hash-set
+    keyword? keyword->string string->keyword make-keyword
+    error-message error-irritants error-trace
+    displayln 1+ 1-
+    iota last-pair
+    *method-tables*
+    register-struct-type! *struct-types*
+    struct-predicate struct-field-ref struct-field-set!
+    struct-type-info
+
+    ;; ---- std/sort ----
+    sort sort! stable-sort stable-sort!
+
+    ;; ---- std/format ----
+    format printf fprintf eprintf
+
+    ;; ---- std/error ----
+    Error ContractViolation
+
+    ;; ---- std/sugar ----
+    chain chain-and assert!
+    unwind-protect with-id with-lock with-catch
+    cut cute <> <...>
+    awhen aif when-let if-let
+    -> ->> as-> some-> some->> cond-> cond->>
+    ->? ->>?
+    with-resource str alist defn defrecord
+    let-alist define-enum capture dotimes define-values
+
+    ;; ---- std/text/json ----
+    read-json write-json json-object->string string->json-object
+
+    ;; ---- std/os/platform ----
+    cpu-count
+
+    ;; ---- std/os/path ----
+    path-expand path-normalize path-directory path-strip-directory
+    path-extension path-strip-extension
+    path-join path-absolute?
+
+    ;; ---- std/regex ----
+    re re?
+    re-match? re-search
+    re-find-all re-groups
+    re-replace re-replace-all
+    re-split re-fold
+    re-match-full re-match-group re-match-groups
+    re-match-start re-match-end re-match-named
+
+    ;; ---- std/rx ----
+    rx define-rx
+
+    ;; ---- std/misc/string ----
+    string-split string-join string-trim
+    string-prefix? string-suffix?
+    string-contains string-index
+    string-empty?
+    string-match? string-find string-find-all
+
+    ;; ---- std/misc/list ----
+    flatten unique snoc
+    take drop
+    every any
+    filter-map
+    group-by
+    zip
+    frequencies
+    partition partition-all partition-by
+    interleave interpose
+    mapcat
+    distinct
+    keep
+    some
+    iterate-n
+    reductions
+    take-last drop-last
+    split-at split-with
+    ;; Gerbil v0.19 compat
+    append-map append1 flatten1
+    push! pop!
+    for-each!
+    take-while take-until drop-while drop-until
+    butlast slice split
+    length=? length<? length<=? length>? length>=?
+    length=n? length<n? length<=n? length>n? length>=n?
+    group-consecutive group-n-consecutive group-same
+    rassoc every-consecutive?
+    map/car first-and-only when/list
+    with-list-builder call-with-list-builder
+    duplicates delete-duplicates/hash
+
+    ;; ---- std/misc/alist ----
+    agetq agetv aget
+    asetq! asetv! aset!
+    pgetq pgetv pget
+    alist->hash-table
+    ;; Gerbil v0.19 compat
+    alist? acons
+    asetq asetv aset
+    aremq aremv arem
+    aremq! aremv! arem!
+    psetq psetv pset
+    psetq! psetv! pset!
+    premq premv prem
+    premq! premv! prem!
+    plist->alist* alist->plist*
+
+    ;; ---- std/misc/ports ----
+    read-all-as-string read-all-as-lines
+    read-file-string read-file-lines
+    write-file-string
+    with-input-from-string with-output-to-string
+
+    ;; ---- std/misc/func ----
+    compose compose1 identity constantly flip