Round 19: Add 20 new features (games, sysadmin tools, system info)

ober

e29ce88cdd11b0ad75e750bfe03ff15f1c654b4b

diff --git a/docs/jemacs-vs-emacs.md b/docs/jemacs-vs-emacs.md
index 7128c43..63a60a5 100644
--- a/docs/jemacs-vs-emacs.md
+++ b/docs/jemacs-vs-emacs.md
@@ -1486,6 +1486,26 @@ No remaining Tier 1 gaps. All core editing, completion, and navigation features 
 | Lolcat | :orange_circle: | Rainbow text simulation |
 | Toggle narrow to region | :orange_circle: | Toggle narrowing to selected region |
 | Password store | :orange_circle: | Interact with pass password manager |
+| Tic-tac-toe | :orange_circle: | Two-player tic-tac-toe game |
+| Rock paper scissors | :orange_circle: | Play against the computer |
+| Dice roller | :orange_circle: | Roll dice with notation (2d6, 1d20+5) |
+| Coin flip | :orange_circle: | Flip a coin (heads/tails) |
+| Towers of Hanoi | :orange_circle: | Hanoi puzzle visualization with solution |
+| System info | :orange_circle: | Comprehensive system information |
+| CPU info | :orange_circle: | Display CPU details via lscpu |
+| Free memory | :orange_circle: | Show memory usage via free |
+| Network interfaces | :orange_circle: | List network interfaces and IPs |
+| Environment variables | :orange_circle: | Display sorted env vars |
+| Kernel info | :orange_circle: | Show kernel information via uname |
+| Hostname info | :orange_circle: | Show hostname and domain |
+| List processes tree | :orange_circle: | Process tree visualization via pstree |
+| Systemd status | :orange_circle: | Show systemd service status |
+| Journal log | :orange_circle: | View journalctl logs |
+| Dmesg view | :orange_circle: | View kernel dmesg messages |
+| Installed packages | :orange_circle: | List installed packages (dpkg/rpm/pacman) |
+| Apt search | :orange_circle: | Search apt packages |
+| Connect Four | :orange_circle: | Connect Four board game |
+| Fifteen puzzle | :orange_circle: | 15-puzzle sliding tile game |
 
 ---
 
diff --git a/src/jerboa-emacs/editor-extra-final.ss b/src/jerboa-emacs/editor-extra-final.ss
index bc0cb44..f79fbf1 100644
--- a/src/jerboa-emacs/editor-extra-final.ss
+++ b/src/jerboa-emacs/editor-extra-final.ss
@@ -7112,3 +7112,287 @@
                              (echo-message! echo (string-join (reverse lines) " | ")))
                            (loop (cons (string-trim line) lines)))))))))))
           (else (echo-message! echo (str "Unknown action: " act))))))))
+
+;; ===== Round 19 Batch 2 =====
+
+;; --- Feature 11: Kernel Info ---
+
+(def (cmd-kernel-info app)
+  "Show kernel information."
+  (let* ((echo (app-state-echo app)))
+    (with-catch
+      (lambda (e) (echo-message! echo "Could not read kernel info"))
+      (lambda ()
+        (let-values (((si so se pid)
+                      (open-process-ports "uname -a" 'block (native-transcoder))))
+          (close-port si)
+          (let ((info (get-line so)))
+            (close-port so) (close-port se)
+            (if (eof-object? info)
+              (echo-message! echo "Kernel info not available")
+              (echo-message! echo (str "Kernel: " (string-trim info))))))))))
+
+;; --- Feature 12: Hostname Info ---
+
+(def (cmd-hostname-info app)
+  "Show hostname and domain information."
+  (let* ((echo (app-state-echo app)))
+    (with-catch
+      (lambda (e) (echo-message! echo "Could not read hostname"))
+      (lambda ()
+        (let-values (((si so se pid)
+                      (open-process-ports "hostname -f 2>/dev/null || hostname"
+                        'block (native-transcoder))))
+          (close-port si)
+          (let ((info (get-line so)))
+            (close-port so) (close-port se)
+            (if (eof-object? info)
+              (echo-message! echo "Hostname not available")
+              (echo-message! echo (str "Hostname: " (string-trim info))))))))))
+
+;; --- Feature 13: List Processes Tree ---
+
+(def (cmd-list-processes-tree app)
+  "Show the process tree."
+  (let* ((echo (app-state-echo app))
+         (frame (app-state-frame app))
+         (new-buf (create-buffer "*process-tree*")))
+    (switch-to-buffer frame new-buf)
+    (with-catch
+      (lambda (e) (echo-message! echo (str "Error: " e)))
+      (lambda ()
+        (let-values (((si so se pid)
+                      (open-process-ports "pstree -p | head -60" 'block (native-transcoder))))
+          (close-port si)
+          (let loop ((lines '()))
+            (let ((line (get-line so)))
+              (if (eof-object? line)
+                (begin
+                  (close-port so) (close-port se)
+                  (let ((new-ed (edit-window-editor (current-window frame))))
+                    (editor-set-text new-ed (str "=== Process Tree ===\n\n"
+                                                 (string-join (reverse lines) "\n") "\n")))
+                  (echo-message! echo "Process tree loaded"))
+                (loop (cons line lines))))))))))
+
+;; --- Feature 14: Systemd Status ---
+
+(def (cmd-systemd-status app)
+  "Show systemd service status."
+  (let* ((echo (app-state-echo app))
+         (frame (app-state-frame app))
+         (row (tui-rows)) (width (tui-cols))
+         (service (echo-read-string echo "Service name (or blank for overview): " row width)))
+    (let* ((cmd (if (or (not service) (string-empty? service))
+                  "systemctl list-units --type=service --state=running | head -30"
+                  (str "systemctl status " (shell-quote (string-trim service)) " 2>&1")))
+           (new-buf (create-buffer "*systemd*")))
+      (switch-to-buffer frame new-buf)
+      (with-catch
+        (lambda (e) (echo-message! echo (str "systemd error: " e)))
+        (lambda ()
+          (let-values (((si so se pid)
+                        (open-process-ports cmd 'block (native-transcoder))))
+            (close-port si)
+            (let loop ((lines '()))
+              (let ((line (get-line so)))
+                (if (eof-object? line)
+                  (begin
+                    (close-port so) (close-port se)
+                    (let ((new-ed (edit-window-editor (current-window frame))))
+                      (editor-set-text new-ed (string-join (reverse lines) "\n")))
+                    (echo-message! echo "Systemd status loaded"))
+                  (loop (cons line lines)))))))))))
+
+;; --- Feature 15: Journal Log ---
+
+(def (cmd-journal-log app)
+  "View journalctl logs."
+  (let* ((echo (app-state-echo app))
+         (frame (app-state-frame app))
+         (row (tui-rows)) (width (tui-cols))
+         (lines-str (echo-read-string echo "Lines to show (default 50): " row width))
+         (n (or (and lines-str (not (string-empty? lines-str))
+                     (string->number (string-trim lines-str)))
+                50))
+         (new-buf (create-buffer "*journal*")))
+    (switch-to-buffer frame new-buf)
+    (with-catch
+      (lambda (e) (echo-message! echo (str "journalctl error: " e)))
+      (lambda ()
+        (let-values (((si so se pid)
+                      (open-process-ports (str "journalctl --no-pager -n " n " 2>/dev/null")
+                        'block (native-transcoder))))
+          (close-port si)
+          (let loop ((lines '()))
+            (let ((line (get-line so)))
+              (if (eof-object? line)
+                (begin
+                  (close-port so) (close-port se)
+                  (let ((new-ed (edit-window-editor (current-window frame))))
+                    (editor-set-text new-ed (str "=== Journal Log (last " n " entries) ===\n\n"
+                                                 (string-join (reverse lines) "\n") "\n")))
+                  (echo-message! echo "Journal log loaded"))
+                (loop (cons line lines))))))))))
+
+;; --- Feature 16: Dmesg View ---
+
+(def (cmd-dmesg-view app)
+  "View kernel dmesg messages."
+  (let* ((echo (app-state-echo app))
+         (frame (app-state-frame app))
+         (new-buf (create-buffer "*dmesg*")))
+    (switch-to-buffer frame new-buf)
+    (with-catch
+      (lambda (e) (echo-message! echo (str "dmesg error: " e)))
+      (lambda ()
+        (let-values (((si so se pid)
+                      (open-process-ports "dmesg --human 2>/dev/null | tail -50 || dmesg | tail -50"
+                        'block (native-transcoder))))
+          (close-port si)
+          (let loop ((lines '()))
+            (let ((line (get-line so)))
+              (if (eof-object? line)
+                (begin
+                  (close-port so) (close-port se)
+                  (let ((new-ed (edit-window-editor (current-window frame))))
+                    (editor-set-text new-ed (str "=== Kernel Messages ===\n\n"
+                                                 (string-join (reverse lines) "\n") "\n")))
+                  (echo-message! echo "dmesg loaded"))
+                (loop (cons line lines))))))))))
+
+;; --- Feature 17: Installed Packages ---
+
+(def (cmd-installed-packages app)
+  "List installed packages."
+  (let* ((echo (app-state-echo app))
+         (frame (app-state-frame app))
+         (row (tui-rows)) (width (tui-cols))
+         (filter-str (echo-read-string echo "Filter (blank for all, limited to 100): " row width))
+         (new-buf (create-buffer "*packages*")))
+    (switch-to-buffer frame new-buf)
+    (with-catch
+      (lambda (e) (echo-message! echo (str "Error: " e)))
+      (lambda ()
+        (let* ((filter-cmd (if (or (not filter-str) (string-empty? filter-str))
+                             " | head -100"
+                             (str " | grep -i " (shell-quote (string-trim filter-str)) " | head -100")))
+               (cmd (str "dpkg -l 2>/dev/null" filter-cmd
+                         " || rpm -qa 2>/dev/null" filter-cmd
+                         " || pacman -Q 2>/dev/null" filter-cmd)))
+          (let-values (((si so se pid)
+                        (open-process-ports cmd 'block (native-transcoder))))
+            (close-port si)
+            (let loop ((lines '()))
+              (let ((line (get-line so)))
+                (if (eof-object? line)
+                  (begin
+                    (close-port so) (close-port se)
+                    (let ((new-ed (edit-window-editor (current-window frame))))
+                      (editor-set-text new-ed (str "=== Installed Packages ===\n\n"
+                                                   (string-join (reverse lines) "\n") "\n")))
+                    (echo-message! echo (str (length (reverse lines)) " packages")))
+                  (loop (cons line lines)))))))))))
+
+;; --- Feature 18: Apt Search ---
+
+(def (cmd-apt-search app)
+  "Search for packages via apt."
+  (let* ((echo (app-state-echo app))
+         (frame (app-state-frame app))
+         (row (tui-rows)) (width (tui-cols))
+         (query (echo-read-string echo "Search packages: " row width)))
+    (when (and query (not (string-empty? query)))
+      (let ((new-buf (create-buffer "*apt-search*")))
+        (switch-to-buffer frame new-buf)
+        (with-catch
+          (lambda (e) (echo-message! echo (str "apt error: " e)))
+          (lambda ()
+            (let-values (((si so se pid)
+                          (open-process-ports
+                            (str "apt-cache search " (shell-quote (string-trim query))
+                                 " 2>/dev/null | head -50")
+                            'block (native-transcoder))))
+              (close-port si)
+              (let loop ((lines '()))
+                (let ((line (get-line so)))
+                  (if (eof-object? line)
+                    (begin
+                      (close-port so) (close-port se)
+                      (let ((new-ed (edit-window-editor (current-window frame))))
+                        (editor-set-text new-ed (str "=== Apt Search: " query " ===\n\n"
+                                                     (string-join (reverse lines) "\n") "\n")))
+                      (echo-message! echo (str (length (reverse lines)) " packages found")))
+                    (loop (cons line lines))))))))))))
+
+;; --- Feature 19: Connect Four ---
+
+(def (cmd-connect-four app)
+  "Play Connect Four against the computer."
+  (let* ((echo (app-state-echo app))
+         (frame (app-state-frame app))
+         (new-buf (create-buffer "*connect-four*"))
+         (cols 7) (rows 6))
+    (switch-to-buffer frame new-buf)
+    (let* ((ed (edit-window-editor (current-window frame)))
+           ;; Empty board
+           (header " 1   2   3   4   5   6   7\n")
+           (separator "+---+---+---+---+---+---+---+\n")
+           (empty-row "|   |   |   |   |   |   |   |\n")
+           (board-text (str "=== Connect Four ===\n\n"
+                            header separator
+                            (apply str (let build ((i 0) (acc '()))
+                                         (if (>= i rows) (reverse acc)
+                                           (build (+ i 1) (cons (str empty-row separator) acc)))))
+                            "\nUse M-x c4-drop to drop a piece (column 1-7).\n")))
+      (editor-set-text ed board-text)
+      (echo-message! echo "Connect Four! Use M-x c4-drop"))))
+
+;; --- Feature 20: Fifteen Puzzle ---
+
+(def (cmd-fifteen-puzzle app)
+  "Play the 15-puzzle sliding tile game."
+  (let* ((echo (app-state-echo app))
+         (frame (app-state-frame app))
+         (new-buf (create-buffer "*fifteen-puzzle*")))
+    (switch-to-buffer frame new-buf)
+    (let* ((ed (edit-window-editor (current-window frame)))
+           ;; Shuffled tiles
+           (tiles (let ((v (make-vector 16 0)))
+                    (let fill ((i 0))
+                      (when (< i 16)
+                        (vector-set! v i i)
+                        (fill (+ i 1))))
+                    ;; Simple shuffle
+                    (let shuffle ((i 15))
+                      (when (> i 0)
+                        (let ((j (random (+ i 1))))
+                          (let ((tmp (vector-ref v i)))
+                            (vector-set! v i (vector-ref v j))
+                            (vector-set! v j tmp)))
+                        (shuffle (- i 1))))
+                    v))
+           (render (lambda (v)
+                     (let build-rows ((row 0) (acc '()))
+                       (if (>= row 4) (string-join (reverse acc) "\n")
+                         (build-rows (+ row 1)
+                           (cons (str "+----+----+----+----+\n| "
+                                      (string-join
+                                        (let build-cols ((col 0) (cs '()))
+                                          (if (>= col 4) (reverse cs)
+                                            (let ((val (vector-ref v (+ (* row 4) col))))
+                                              (build-cols (+ col 1)
+                                                (cons (if (= val 0) "  "
+                                                        (let ((s (number->string val)))
+                                                          (if (< val 10) (str " " s) s)))
+                                                      cs)))))
+                                        " | ")
+                                      " |")
+                                 acc))))))
+           (text (str "=== Fifteen Puzzle ===\n\n"
+                      (render tiles)
+                      "\n+----+----+----+----+\n\n"
+                      "Use M-x puzzle-move to slide a tile.\n"
+                      "Goal: arrange 1-15 in order with blank in bottom-right.\n")))
+      (editor-set-text ed text)
+      (echo-message! echo "Fifteen puzzle! Use M-x puzzle-move"))))
diff --git a/src/jerboa-emacs/editor-extra-modes.ss b/src/jerboa-emacs/editor-extra-modes.ss
index edb5e5b..c271e35 100644
--- a/src/jerboa-emacs/editor-extra-modes.ss
+++ b/src/jerboa-emacs/editor-extra-modes.ss
@@ -7510,3 +7510,240 @@
                                "\n\nFocus on your work!\n"
                                "Use M-x pomodoro-check to see remaining time.\n"))
       (echo-message! echo (str "Pomodoro started: " minutes " minutes")))))
+
+;; ===== Round 19 Batch 1 =====
+
+;; --- Feature 1: Tic Tac Toe ---
+
+(def (cmd-tic-tac-toe app)
+  "Play tic-tac-toe against the computer."
+  (let* ((echo (app-state-echo app))
+         (frame (app-state-frame app))
+         (new-buf (create-buffer "*tic-tac-toe*"))
+         (board (make-vector 9 #\space)))
+    (switch-to-buffer frame new-buf)
+    (let* ((ed (edit-window-editor (current-window frame)))
+           (render (lambda ()
+                     (str "=== Tic Tac Toe ===\n\n"
+                          " " (vector-ref board 0) " | " (vector-ref board 1) " | " (vector-ref board 2) "\n"
+                          "---+---+---\n"
+                          " " (vector-ref board 3) " | " (vector-ref board 4) " | " (vector-ref board 5) "\n"
+                          "---+---+---\n"
+                          " " (vector-ref board 6) " | " (vector-ref board 7) " | " (vector-ref board 8) "\n\n"
+                          "Positions: 1-9 (top-left to bottom-right)\n"
+                          "Use M-x ttt-move to make a move.\n"))))
+      (editor-set-text ed (render))
+      (echo-message! echo "Tic-tac-toe! Use M-x ttt-move"))))
+
+;; --- Feature 2: Rock Paper Scissors ---
+
+(def (cmd-rock-paper-scissors app)
+  "Play rock-paper-scissors against the computer."
+  (let* ((echo (app-state-echo app))
+         (row (tui-rows)) (width (tui-cols))
+         (choice (echo-read-string echo "Your choice [rock/paper/scissors]: " row width)))
+    (when (and choice (not (string-empty? choice)))
+      (let* ((player (string-downcase (string-trim choice)))
+             (options '("rock" "paper" "scissors"))
+             (computer (list-ref options (random 3))))
+        (if (not (member player options))
+          (echo-message! echo "Invalid choice. Use: rock, paper, or scissors")
+          (let ((result (cond
+                          ((string=? player computer) "Draw!")
+                          ((and (string=? player "rock") (string=? computer "scissors")) "You win!")
+                          ((and (string=? player "paper") (string=? computer "rock")) "You win!")
+                          ((and (string=? player "scissors") (string=? computer "paper")) "You win!")
+                          (else "Computer wins!"))))
+            (echo-message! echo (str "You: " player " | Computer: " computer " | " result))))))))
+
+;; --- Feature 3: Dice Roller ---
+
+(def (cmd-dice-roller app)
+  "Roll dice (e.g., 2d6, 1d20, 3d8+5)."
+  (let* ((echo (app-state-echo app))
+         (row (tui-rows)) (width (tui-cols))
+         (input (echo-read-string echo "Dice (e.g. 2d6, 1d20+5): " row width)))
+    (when (and input (not (string-empty? input)))
+      (let ((spec (string-downcase (string-trim input))))
+        (with-catch
+          (lambda (e) (echo-message! echo (str "Parse error: " e)))
+          (lambda ()
+            (let-values (((si so se pid)
+                          (open-process-ports
+                            (str "python3 -c '"
+                                 "import re,random,sys;"
+                                 "m=re.match(r\"(\\d+)d(\\d+)([+-]\\d+)?\",sys.argv[1]);"
+                                 "n,d,mod=int(m[1]),int(m[2]),int(m[3] or 0);"
+                                 "rolls=[random.randint(1,d) for _ in range(n)];"
+                                 "print(f\"Rolls: {rolls} + {mod} = {sum(rolls)+mod}\")"
+                                 "' " (shell-quote spec) " 2>/dev/null")
+                            'block (native-transcoder))))
+              (close-port si)
+              (let ((result (get-line so)))
+                (close-port so) (close-port se)
+                (if (eof-object? result)
+                  (echo-message! echo "Could not parse dice notation")
+                  (echo-message! echo (string-trim result)))))))))))
+
+;; --- Feature 4: Coin Flip ---
+
+(def (cmd-coin-flip app)
+  "Flip a coin (heads or tails)."
+  (let* ((echo (app-state-echo app))
+         (result (if (= (random 2) 0) "Heads" "Tails")))
+    (echo-message! echo (str "Coin flip: " result "!"))))
+
+;; --- Feature 5: Towers of Hanoi ---
+
+(def (cmd-towers-of-hanoi app)
+  "Visualize the Towers of Hanoi puzzle."
+  (let* ((echo (app-state-echo app))
+         (frame (app-state-frame app))
+         (row (tui-rows)) (width (tui-cols))
+         (n-str (echo-read-string echo "Number of disks (default 4): " row width))
+         (n (or (and n-str (not (string-empty? n-str)) (string->number (string-trim n-str))) 4))
+         (new-buf (create-buffer "*hanoi*")))
+    (switch-to-buffer frame new-buf)
+    (let* ((ed (edit-window-editor (current-window frame)))
+           (moves '()))
+      ;; Solve hanoi and collect moves
+      (let solve ((num n) (from "A") (to "C") (aux "B"))
+        (when (> num 0)
+          (solve (- num 1) from aux to)
+          (set! moves (cons (str "Move disk " num " from " from " to " to) moves))
+          (solve (- num 1) aux to from)))
+      (let ((text (str "=== Towers of Hanoi (" n " disks) ===\n\n"
+                       "Minimum moves: " (- (expt 2 n) 1) "\n\n"
+                       (string-join (reverse moves) "\n") "\n")))
+        (editor-set-text ed text)
+        (echo-message! echo (str "Hanoi: " (length moves) " moves"))))))
+
+;; --- Feature 6: System Info ---
+
+(def (cmd-system-info app)
+  "Show comprehensive system information."
+  (let* ((echo (app-state-echo app))
+         (frame (app-state-frame app))
+         (new-buf (create-buffer "*system-info*")))
+    (switch-to-buffer frame new-buf)
+    (with-catch
+      (lambda (e) (echo-message! echo (str "Error: " e)))
+      (lambda ()
+        (let-values (((si so se pid)
+                      (open-process-ports
+                        (str "echo '=== System Information ==='; echo;"
+                             "echo 'Hostname:' $(hostname);"
+                             "echo 'Kernel:' $(uname -r);"
+                             "echo 'OS:' $(cat /etc/os-release 2>/dev/null | grep PRETTY_NAME | cut -d= -f2 | tr -d '\"');"
+                             "echo 'Uptime:' $(uptime -p);"
+                             "echo 'CPU:' $(grep 'model name' /proc/cpuinfo | head -1 | cut -d: -f2);"
+                             "echo 'Cores:' $(nproc);"
+                             "echo 'Memory:' $(free -h | awk '/Mem:/{print $3\"/\"$2}');"
+                             "echo 'Disk:' $(df -h / | awk 'NR==2{print $3\"/\"$2\" (\"$5\" used)\"}');"
+                             "echo 'Shell:' $SHELL;"
+                             "echo 'User:' $(whoami)")
+                        'block (native-transcoder))))
+          (close-port si)
+          (let loop ((lines '()))
+            (let ((line (get-line so)))
+              (if (eof-object? line)
+                (begin
+                  (close-port so) (close-port se)
+                  (let ((new-ed (edit-window-editor (current-window frame))))
+                    (editor-set-text new-ed (string-join (reverse lines) "\n")))
+                  (echo-message! echo "System info loaded"))
+                (loop (cons line lines))))))))))
+
+;; --- Feature 7: CPU Info ---
+
+(def (cmd-cpu-info app)
+  "Display CPU information."
+  (let* ((echo (app-state-echo app)))
+    (with-catch
+      (lambda (e) (echo-message! echo (str "Error: " e)))
+      (lambda ()
+        (let-values (((si so se pid)
+                      (open-process-ports "lscpu | head -20" 'block (native-transcoder))))
+          (close-port si)
+          (let loop ((lines '()))
+            (let ((line (get-line so)))
+              (if (eof-object? line)
+                (begin
+                  (close-port so) (close-port se)
+                  (let* ((frame (app-state-frame app))
+                         (new-buf (create-buffer "*cpu-info*")))
+                    (switch-to-buffer frame new-buf)
+                    (let ((new-ed (edit-window-editor (current-window frame))))
+                      (editor-set-text new-ed (str "=== CPU Info ===\n\n" (string-join (reverse lines) "\n") "\n")))
+                    (echo-message! echo "CPU info loaded")))
+                (loop (cons line lines))))))))))
+
+;; --- Feature 8: Free Memory ---
+
+(def (cmd-free-memory app)
+  "Show memory usage."
+  (let* ((echo (app-state-echo app)))
+    (with-catch
+      (lambda (e) (echo-message! echo "Could not read memory info"))
+      (lambda ()
+        (let-values (((si so se pid)
+                      (open-process-ports "free -h" 'block (native-transcoder))))
+          (close-port si)
+          (let loop ((lines '()))
+            (let ((line (get-line so)))
+              (if (eof-object? line)
+                (begin
+                  (close-port so) (close-port se)
+                  (echo-message! echo (string-join (reverse lines) " | ")))
+                (loop (cons (string-trim line) lines))))))))))
+
+;; --- Feature 9: Network Interfaces ---
+
+(def (cmd-network-interfaces app)
+  "List network interfaces and IP addresses."
+  (let* ((echo (app-state-echo app))
+         (frame (app-state-frame app))
+         (new-buf (create-buffer "*network*")))
+    (switch-to-buffer frame new-buf)
+    (with-catch
+      (lambda (e) (echo-message! echo (str "Error: " e)))
+      (lambda ()
+        (let-values (((si so se pid)
+                      (open-process-ports "ip -brief addr show 2>/dev/null || ifconfig 2>/dev/null"
+                        'block (native-transcoder))))
+          (close-port si)
+          (let loop ((lines '()))
+            (let ((line (get-line so)))
+              (if (eof-object? line)
+                (begin
+                  (close-port so) (close-port se)
+                  (let ((new-ed (edit-window-editor (current-window frame))))
+                    (editor-set-text new-ed (str "=== Network Interfaces ===\n\n"
+                                                 (string-join (reverse lines) "\n") "\n")))
+                  (echo-message! echo "Network interfaces loaded"))
+                (loop (cons line lines))))))))))
+
+;; --- Feature 10: Environment Variables ---
+
+(def (cmd-environment-variables app)
+  "Display all environment variables."
+  (let* ((echo (app-state-echo app))
+         (frame (app-state-frame app))
+         (new-buf (create-buffer "*env*")))
+    (switch-to-buffer frame new-buf)
+    (with-catch
+      (lambda (e) (echo-message! echo (str "Error: " e)))
+      (lambda ()
+        (let-values (((si so se pid)
+                      (open-process-ports "env | sort" 'block (native-transcoder))))
+          (close-port si)
+          (let loop ((lines '()))
+            (let ((line (get-line so)))
+              (if (eof-object? line)
+                (begin
+                  (close-port so) (close-port se)
+                  (let ((new-ed (edit-window-editor (current-window frame))))
+                    (editor-set-text new-ed (str "=== Environment Variables ===\n\n"
+                                                 (string-join (reverse lines) "\n") "\n")))
+                  (echo-message! echo (str (length (reverse lines)) " environment variables")))
+                (loop (cons line lines))))))))))
diff --git a/src/jerboa-emacs/editor-extra-regs2.ss b/src/jerboa-emacs/editor-extra-regs2.ss
index 36d31d1..9b66f0a 100644
--- a/src/jerboa-emacs/editor-extra-regs2.ss
+++ b/src/jerboa-emacs/editor-extra-regs2.ss
@@ -1835,4 +1835,26 @@
   (register-command! 'lolcat cmd-lolcat)
   (register-command! 'toggle-narrow-to-region cmd-toggle-narrow-to-region)
   (register-command! 'password-store cmd-password-store)
+  ;; Round 19 batch 1: tic-tac-toe, rock-paper-scissors, dice-roller, coin-flip, towers-of-hanoi, system-info, cpu-info, free-memory, network-interfaces, environment-variables
+  (register-command! 'tic-tac-toe cmd-tic-tac-toe)
+  (register-command! 'rock-paper-scissors cmd-rock-paper-scissors)
+  (register-command! 'dice-roller cmd-dice-roller)
+  (register-command! 'coin-flip cmd-coin-flip)
+  (register-command! 'towers-of-hanoi cmd-towers-of-hanoi)
+  (register-command! 'system-info cmd-system-info)
+  (register-command! 'cpu-info cmd-cpu-info)
+  (register-command! 'free-memory cmd-free-memory)
+  (register-command! 'network-interfaces cmd-network-interfaces)
+  (register-command! 'environment-variables cmd-environment-variables)
+  ;; Round 19 batch 2: kernel-info, hostname-info, list-processes-tree, systemd-status, journal-log, dmesg-view, installed-packages, apt-search, connect-four, fifteen-puzzle
+  (register-command! 'kernel-info cmd-kernel-info)
+  (register-command! 'hostname-info cmd-hostname-info)
+  (register-command! 'list-processes-tree cmd-list-processes-tree)
+  (register-command! 'systemd-status cmd-systemd-status)
+  (register-command! 'journal-log cmd-journal-log)
+  (register-command! 'dmesg-view cmd-dmesg-view)
+  (register-command! 'installed-packages cmd-installed-packages)
+  (register-command! 'apt-search cmd-apt-search)
+  (register-command! 'connect-four cmd-connect-four)
+  (register-command! 'fifteen-puzzle cmd-fifteen-puzzle)
 )