Merge remote-tracking branch 'origin/master'

ober

64487e698232a3bbd9235f2e55f88c3bf9228081

diff --git a/build-jcode-cross.ss b/build-jcode-cross.ss
index c4b0cbc..3f45ab0 100644
--- a/build-jcode-cross.ss
+++ b/build-jcode-cross.ss
@@ -62,6 +62,7 @@
 
 (define cross-prefix (format "~a/.chez-cross-~a" jerboa-home chez-machine))
 (define xpatch       (format "~a/build/chez/xc-~a/s/xpatch" jerboa-home chez-machine))
+(define use-xpatch?  (not (string=? (symbol->string (machine-type)) chez-machine)))
 (define cross-cc     (or (getenv "CROSS_CC") (list-ref arch-row 2)))
 
 (define output       (list-ref arch-row 4))
@@ -104,7 +105,7 @@
   (unless (file-exists? p)
     (error 'build-jcode-cross "missing file" p)))
 
-(require-file xpatch)
+(when use-xpatch? (require-file xpatch))
 (require-file (format "~a/libkernel.a"  cross-csv-dir))
 (require-file (format "~a/scheme.h"     cross-csv-dir))
 (require-file (format "~a/petite.boot"  cross-csv-dir))
@@ -114,7 +115,8 @@
 (printf "==> build-jcode-cross~n")
 (printf "    JERBOA_HOME:   ~a~n" jerboa-home)
 (printf "    cross csv-dir: ~a~n" cross-csv-dir)
-(printf "    xpatch:        ~a~n" xpatch)
+(printf "    xpatch:        ~a~n"
+        (if use-xpatch? xpatch "not needed (host and target machine types match)"))
 (printf "    cross-cc:      ~a~n" cross-cc)
 (printf "    output:        ~a~n" output)
 (printf "    features:      ~a~n~n" cargo-features)
@@ -263,8 +265,11 @@
 
 ;; ── Stage 1: load xpatch (target=ta6le emit mode) ──────────────────────────
 (define orig-libdirs (library-directories))
-(printf "==> [1/6] loading xpatch (compiler -> ~a emit mode)~n" chez-machine)
-(load xpatch)
+(if use-xpatch?
+    (begin
+      (printf "==> [1/6] loading xpatch (compiler -> ~a emit mode)~n" chez-machine)
+      (load xpatch))
+    (printf "==> [1/6] host already emits ~a; xpatch not needed~n" chez-machine))
 (library-directories
   (map (lambda (pair)
          (cons (if (pair? pair) (car pair) pair) obj-dir))
@@ -275,7 +280,7 @@
 
 ;; ── Stage 2: compile-program main-binary.ss ────────────────────────────────
 (printf "==> [2/6] compile-program ~a -> ~a~n" entry-script entry-so)
-(guard (e [#t (cleanup!) (raise e)])
+(guard (e [#t (raise e)])
   (compile-program entry-script entry-so))
 
 ;; ── Stage 3: compile-whole-program → wpo .so ───────────────────────────────
@@ -366,8 +371,11 @@
     "jerboa_wasm_fuel_remaining" "jerboa_wasm_get_log"
     "jerboa_wasm_allow_cdb_dir" "jerboa_wasm_set_socket"
     ;; jerboa-native (regex) — visit-time even if jcode never matches
-    "jerboa_regex_compile" "jerboa_regex_is_match"
-    "jerboa_regex_find" "jerboa_regex_replace_all" "jerboa_regex_free"
+    "jerboa_regex_compile" "jerboa_regex_compile_ex" "jerboa_regex_is_match"
+    "jerboa_regex_find" "jerboa_regex_find_at"
+    "jerboa_regex_captures" "jerboa_regex_group_count"
+    "jerboa_regex_replace_all" "jerboa_regex_replace_all_alloc"
+    "jerboa_regex_free"
     ;; jerboa-native (aproc) — (std os aproc) for bash tool
     "jerboa_aproc_spawn" "jerboa_aproc_spawn_pty"
     "jerboa_aproc_set_nonblock" "jerboa_aproc_killpg" "jerboa_aproc_wait4"
@@ -398,8 +406,9 @@
     "sysconf" "getpagesize" "getrlimit"
     "system" "getenv" "putenv" "_exit" "exit" "execvp" "execve"
     ;; files/directories/FIFOs — std/os/temp, std/os/posix
-    "access" "chdir" "stat" "fstat" "lstat" "readlink" "realpath"
-    "mkdir" "fdopen"
+    "access" "chdir" "chmod" "fchmod" "fsync"
+    "stat" "fstat" "lstat" "readlink" "realpath"
+    "mkdir" "mkdirat" "openat" "unlinkat" "renameat" "linkat" "fdopen"
     "mkstemp" "mkdtemp" "mkfifo" "umask" "unlink" "rmdir"
     ;; errno location + Linux sandboxing
     "__errno_location" "fcntl" "prctl" "syscall"))
diff --git a/src/jcode/ui/cli.ss b/src/jcode/ui/cli.ss
index 10117a3..ac87e40 100644
--- a/src/jcode/ui/cli.ss
+++ b/src/jcode/ui/cli.ss
@@ -1422,15 +1422,12 @@ EXAMPLES:
 
 (def (save-terminal-state)
   (guard (e [else #f])
-    ;; P3.1: Use argv-style spawn instead of shell string to avoid injection
-    (let-values (((to-stdin from-stdout from-stderr pid)
-                  (open-process-ports "stty" '("-g")
-                    (buffer-mode block) (native-transcoder))))
-      (let ((state (get-line from-stdout)))
-        (close-port to-stdin)
-        (close-port from-stdout)
-        (close-port from-stderr)
-        (if (eof-object? state) #f state)))))
+    ;; Use argv-style spawn so the command is never interpreted by a shell.
+    (let-values (((stdout stderr status)
+                  (aproc-run/status* '("stty" "-g"))))
+      (and (= status 0)
+           (string? stdout)
+           (string-trim stdout)))))
 
 (def (restore-terminal-state! state)
   (when (and state (string? state) (> (string-length state) 0))
diff --git a/support/ffi-symbols.list b/support/ffi-symbols.list
index be118c6..0fb2089 100644
--- a/support/ffi-symbols.list
+++ b/support/ffi-symbols.list
@@ -39,7 +39,6 @@ jerboa_tls_server_new
 jerboa_tls_server_new_pem
 jerboa_tls_server_new_mtls
 jerboa_tls_server_free
-jerboa_x509_generate_self_signed_mem
 jerboa_tls_accept
 jerboa_tls_connect
 jerboa_tls_connect_timeout
@@ -87,12 +86,9 @@ jerboa_wasm_memory_size
 jerboa_wasm_add_fuel
 jerboa_wasm_fuel_remaining
 jerboa_wasm_get_log
-jerboa_wasm_allow_cdb_dir
-jerboa_wasm_set_socket
 
 # ── jerboa-native always-on runtime helpers ─────────────────────
 jerboa_regex_compile
-jerboa_regex_compile_ex
 jerboa_regex_is_match
 jerboa_regex_find
 jerboa_regex_find_at
diff --git a/support/jcode-main.c b/support/jcode-main.c
index 7f6e18f..e7558e4 100644
--- a/support/jcode-main.c
+++ b/support/jcode-main.c
@@ -250,6 +250,7 @@ int main(int argc, const char *argv[]) {
     Sregister_boot_file_bytes("scheme",
         (void *)scheme_boot_data, scheme_boot_size);
     Sbuild_heap(NULL, NULL);
+    register_ffi_symbols();
 
     char *prog_path = write_program_tmpfile();
     int status = Sscheme_program(prog_path, argc, argv);
diff --git a/test/binary-startup.sh b/test/binary-startup.sh
index 641e0f9..1d35cc8 100644
--- a/test/binary-startup.sh
+++ b/test/binary-startup.sh
@@ -56,6 +56,27 @@ case "$help" in
     ;;
 esac
 
+# A published native package contains only the executable. Verify the static
+# FFI registration works without repository libraries or runtime overrides.
+mkdir -p "$stage/standalone"
+cp "$binary" "$stage/standalone/jcode"
+standalone_version=$(cd "$stage/standalone" && run_clean ./jcode --version)
+case "$standalone_version" in
+  "jcode "*) ;;
+  *)
+    echo "FAIL: standalone jcode did not start: $standalone_version" >&2
+    exit 1
+    ;;
+esac
+standalone_help=$(cd "$stage/standalone" && run_clean ./jcode --help)
+case "$standalone_help" in
+  *"Portable AI coding agent"* ) ;;
+  *)
+    echo "FAIL: standalone jcode --help did not print CLI help" >&2
+    exit 1
+    ;;
+esac
+
 clean_home=$stage/home
 mkdir -p "$clean_home"
 set +e