buninu 0.2.5 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +100 -2
  3. package/apps/bunmsh/CHANGELOG.md +262 -0
  4. package/apps/bunmsh/LICENSE +21 -0
  5. package/apps/bunmsh/LICENSE-MKSH +295 -0
  6. package/apps/bunmsh/PORTING.md +192 -0
  7. package/apps/bunmsh/README.md +801 -0
  8. package/apps/bunmsh/bunmsh +7 -0
  9. package/apps/bunmsh/bunshell.md +61 -0
  10. package/apps/bunmsh/help/README.md +1945 -0
  11. package/apps/bunmsh/help/alias.md +22 -0
  12. package/apps/bunmsh/help/basename.md +38 -0
  13. package/apps/bunmsh/help/builtin.md +25 -0
  14. package/apps/bunmsh/help/bun.md +27 -0
  15. package/apps/bunmsh/help/bunmsh.md +47 -0
  16. package/apps/bunmsh/help/cat.md +31 -0
  17. package/apps/bunmsh/help/catfancy.md +35 -0
  18. package/apps/bunmsh/help/cd.md +22 -0
  19. package/apps/bunmsh/help/chmod.md +27 -0
  20. package/apps/bunmsh/help/clear.md +21 -0
  21. package/apps/bunmsh/help/colon.md +22 -0
  22. package/apps/bunmsh/help/command.md +27 -0
  23. package/apps/bunmsh/help/cp.md +28 -0
  24. package/apps/bunmsh/help/cut.md +25 -0
  25. package/apps/bunmsh/help/date.md +25 -0
  26. package/apps/bunmsh/help/dirname.md +25 -0
  27. package/apps/bunmsh/help/echo.md +25 -0
  28. package/apps/bunmsh/help/env.md +27 -0
  29. package/apps/bunmsh/help/eval.md +22 -0
  30. package/apps/bunmsh/help/exec.md +22 -0
  31. package/apps/bunmsh/help/exit.md +21 -0
  32. package/apps/bunmsh/help/export.md +22 -0
  33. package/apps/bunmsh/help/false.md +22 -0
  34. package/apps/bunmsh/help/find.md +33 -0
  35. package/apps/bunmsh/help/getopts.md +28 -0
  36. package/apps/bunmsh/help/grep.md +34 -0
  37. package/apps/bunmsh/help/head.md +27 -0
  38. package/apps/bunmsh/help/kill.md +27 -0
  39. package/apps/bunmsh/help/ln.md +28 -0
  40. package/apps/bunmsh/help/ls.md +37 -0
  41. package/apps/bunmsh/help/lsbun.md +29 -0
  42. package/apps/bunmsh/help/lsfancy.md +37 -0
  43. package/apps/bunmsh/help/md5sum.md +21 -0
  44. package/apps/bunmsh/help/mkdir.md +27 -0
  45. package/apps/bunmsh/help/mktemp.md +26 -0
  46. package/apps/bunmsh/help/mv.md +26 -0
  47. package/apps/bunmsh/help/print.md +30 -0
  48. package/apps/bunmsh/help/printf.md +25 -0
  49. package/apps/bunmsh/help/pwd.md +21 -0
  50. package/apps/bunmsh/help/read.md +27 -0
  51. package/apps/bunmsh/help/readonly.md +27 -0
  52. package/apps/bunmsh/help/realpath.md +21 -0
  53. package/apps/bunmsh/help/rm.md +33 -0
  54. package/apps/bunmsh/help/rmdir.md +27 -0
  55. package/apps/bunmsh/help/sed.md +29 -0
  56. package/apps/bunmsh/help/seq.md +29 -0
  57. package/apps/bunmsh/help/serve.md +50 -0
  58. package/apps/bunmsh/help/set.md +26 -0
  59. package/apps/bunmsh/help/sha256sum.md +21 -0
  60. package/apps/bunmsh/help/shift.md +23 -0
  61. package/apps/bunmsh/help/sleep.md +25 -0
  62. package/apps/bunmsh/help/sort.md +29 -0
  63. package/apps/bunmsh/help/source.md +23 -0
  64. package/apps/bunmsh/help/tab.md +30 -0
  65. package/apps/bunmsh/help/tac.md +29 -0
  66. package/apps/bunmsh/help/tail.md +27 -0
  67. package/apps/bunmsh/help/tee.md +25 -0
  68. package/apps/bunmsh/help/test.md +30 -0
  69. package/apps/bunmsh/help/time.md +29 -0
  70. package/apps/bunmsh/help/touch.md +26 -0
  71. package/apps/bunmsh/help/tr.md +26 -0
  72. package/apps/bunmsh/help/true.md +22 -0
  73. package/apps/bunmsh/help/type.md +21 -0
  74. package/apps/bunmsh/help/umask.md +22 -0
  75. package/apps/bunmsh/help/unalias.md +28 -0
  76. package/apps/bunmsh/help/uname.md +31 -0
  77. package/apps/bunmsh/help/unset.md +28 -0
  78. package/apps/bunmsh/help/wc.md +27 -0
  79. package/apps/bunmsh/help/whence.md +26 -0
  80. package/apps/bunmsh/help/which.md +41 -0
  81. package/apps/bunmsh/help/yes.md +23 -0
  82. package/apps/bunmsh/package.json +26 -0
  83. package/apps/bunmsh/serve.js +468 -0
  84. package/apps/bunmsh/single-exe/README.md +402 -0
  85. package/apps/bunmsh/single-exe/assetsHelper.js +371 -0
  86. package/apps/bunmsh/single-exe/assetsLoader.mjs +85 -0
  87. package/apps/bunmsh/single-exe/assetsPacker.js +218 -0
  88. package/apps/bunmsh/single-exe/compiled.js +527 -0
  89. package/apps/bunmsh/single-exe/entry.mjs +17 -0
  90. package/apps/bunmsh/src/cat-fancy.js +117 -0
  91. package/apps/bunmsh/src/cat-operands.js +31 -0
  92. package/apps/bunmsh/src/completion.js +247 -0
  93. package/apps/bunmsh/src/environment.js +61 -0
  94. package/apps/bunmsh/src/fancy-ls.js +186 -0
  95. package/apps/bunmsh/src/find.js +119 -0
  96. package/apps/bunmsh/src/history.js +198 -0
  97. package/apps/bunmsh/src/main.js +776 -0
  98. package/apps/bunmsh/src/mouse.js +103 -0
  99. package/apps/bunmsh/src/shell.js +3557 -0
  100. package/apps/bunmsh/test/catfancy.test.js +102 -0
  101. package/apps/bunmsh/test/reference-source.sh +1 -0
  102. package/apps/bunmsh/test/reference.test.js +677 -0
  103. package/apps/bunmsh/test/serve/README.md +3 -0
  104. package/apps/bunmsh/test/serve/bad.json +1 -0
  105. package/apps/bunmsh/test/serve/data.json +1 -0
  106. package/apps/bunmsh/test/serve/data.json5 +1 -0
  107. package/apps/bunmsh/test/serve/data.jsonc +2 -0
  108. package/apps/bunmsh/test/serve/data.jsonl +2 -0
  109. package/apps/bunmsh/test/serve/data.ndjson +1 -0
  110. package/apps/bunmsh/test/serve/data.toml +3 -0
  111. package/apps/bunmsh/test/serve/data.ts +15 -0
  112. package/apps/bunmsh/test/serve/data.xml +1 -0
  113. package/apps/bunmsh/test/serve/data.yaml +3 -0
  114. package/apps/bunmsh/test/serve/data.yml +1 -0
  115. package/apps/bunmsh/test/serve.test.js +241 -0
  116. package/apps/bunmsh/test/shell.test.js +1778 -0
  117. package/apps/bunmsh/test/t.sh +12 -0
  118. package/apps/native-bridge/native-bridge.js +45 -0
  119. package/apps/xdg-open/xdg-open.js +74 -2
  120. package/bin/init.js +11 -3
  121. package/demo-server.js +64 -0
  122. package/package.json +4 -2
@@ -0,0 +1,801 @@
1
+ # bunmsh
2
+
3
+ - `bunmsh` is a dependency-free, mksh-inspired cross-platform command shell for Bun JavaScript
4
+ * Bun Modern Shell
5
+ - Early releases focus on a small, usable command interpreter rather than full mksh compatibility.
6
+
7
+ ## Usage
8
+
9
+ ### Install Bun first
10
+
11
+ - bunmsh requires [Bun](https://bun.com)
12
+ - On Android, install it in Termux:
13
+
14
+ ```sh
15
+ npm install -g bun
16
+ ```
17
+
18
+ On other platforms, follow the
19
+ [official Bun installation guide](https://bun.com/docs/installation).
20
+
21
+ No project dependencies need to be installed with `npm install` or `bun install`.
22
+
23
+ #### Prompt note for frequent SSH users
24
+
25
+ - By default bunmsh prompts don't show user@host
26
+ - Show them by a custom `PS1`:
27
+
28
+ ```sh
29
+ PS1=$(printf "$(id -un)@$(hostname):\\w\n%s" '$ ') npx bunmsh
30
+ ```
31
+
32
+ ### Option 1: Run with npx
33
+
34
+ ```sh
35
+ # Interactive shell
36
+ npx bunmsh
37
+ ```
38
+
39
+ ```sh
40
+ npx bunmsh -c 'print $PATH'
41
+
42
+ # Call cmd; this can call bunmsh builtins too
43
+ npx bunmsh -cc echo 'hello world' '*.txt' '$HOME'
44
+
45
+ npx bunmsh script.sh argv1 argv2
46
+ ```
47
+
48
+ `-cc` means **call cmd**. Every argument after it is forwarded as an
49
+ already-quoted word, without another round of bunmsh parsing or expansion. The
50
+ example calls the `echo` builtin and prints `hello world *.txt $HOME` literally.
51
+
52
+ `npx` installs and launches the package, but the `bunmsh` executable itself
53
+ uses Bun. Bun must therefore already be available on `PATH`.
54
+
55
+ ### Option 2: Run with git clone
56
+
57
+ - Interactive shell
58
+
59
+ ```sh
60
+ git clone https://github.com/jjtseng93/bunmsh.git
61
+ cd bunmsh
62
+
63
+ bun ./bunmsh
64
+ ```
65
+
66
+ - Running commands or scripts
67
+
68
+ ```sh
69
+ bun ./bunmsh -c 'print $PATH'
70
+
71
+ # Call cmd; this can call bunmsh builtins too
72
+ bun ./bunmsh -cc echo 'hello world' '*.txt' '$HOME'
73
+
74
+ bun ./bunmsh script.sh argv1 argv2
75
+ ```
76
+
77
+ `-cc` means **call cmd**. Every argument after it is forwarded as an
78
+ already-quoted word, without another round of bunmsh parsing or expansion. The
79
+ example calls the `echo` builtin and prints `hello world *.txt $HOME` literally.
80
+
81
+ ### Useful CLI options
82
+
83
+ | Option | Effect |
84
+ | --- | --- |
85
+ | `-c SCRIPT_TEXT [argv...]` | Treat command text as a shell script, then parse and execute it |
86
+ | `-cc COMMAND [argv...]` | Call a command with argv as-is; no shell parsing or shell expansions |
87
+ | `-i` | Enter interactive mode, including after running a script |
88
+ | `--mouse` | Start with terminal mouse tracking enabled |
89
+ | `--builtin-only` | Skip direct command-name lookup through `PATH` |
90
+
91
+ `-c` treats its command text as a shell script and performs the normal parse
92
+ and execute flow. Arguments after the script text become its argv array:
93
+
94
+ ```sh
95
+ bunmsh -c 'echo "$0" "$1"' Hello world
96
+ # Hello world
97
+ ```
98
+
99
+ `-cc` means **Call Command**. It can call an alias, builtin, or command from
100
+ `PATH`. Every following argument is passed as-is, without shell parsing or
101
+ shell expansions:
102
+
103
+ ```sh
104
+ bunmsh -cc echo '$HOME' '*.js'
105
+ # $HOME *.js
106
+
107
+ bunmsh -cc lsfancy -lh
108
+ # List cwd with emojis
109
+ ```
110
+
111
+ In other words, the argv following `-cc` is already quoted. Metacharacters,
112
+ variables, and glob patterns remain literal instead of being interpreted by
113
+ bunmsh.
114
+
115
+ Mouse tracking can alternatively be enabled with `BUNMSH_MOUSE=1`; `true`,
116
+ `on`, and `yes` are also accepted case-insensitively. It is opt-in because
117
+ application mouse tracking prevents normal scrollback gestures in terminals
118
+ such as Termux and xterm.
119
+
120
+ In `--builtin-only` mode, regular and fallback builtins, aliases, and functions
121
+ remain available. An explicit executable path containing `/` can still run.
122
+ The `which` builtin always searches the current `PATH`, so `"$(which COMMAND)"`
123
+ can explicitly select a PATH executable while this mode is active. Use
124
+ `tab path` interactively to toggle this setting; `tab path on`/`true` enables
125
+ direct PATH lookup and `tab path off`/`false` disables it.
126
+
127
+ ### Highest-priority JavaScript mode
128
+
129
+ ```console
130
+ # Read a Bun value; non-undefined results are printed automatically
131
+ 📁 ~/bunmsh
132
+ $ Bun.version
133
+
134
+ # Promises are awaited automatically
135
+ 📁 ~/bunmsh
136
+ $ Bun.file("package.json").text()
137
+ 📁 ~/bunmsh
138
+ $ Bun.sleep(500).then(() => "finished")
139
+
140
+ # Run arbitrary JavaScript statements or an expression
141
+ 📁 ~/bunmsh
142
+ $ Bun.e; const numbers = [1, 2, 3]; numbers.reduce((a, b) => a + b, 0)
143
+ 6
144
+ 📁 ~/bunmsh
145
+ $ Bun.e, ({ cwd: process.cwd(), pid: process.pid })
146
+
147
+ # Share a value across later commands and cwd tabs in this bunmsh process
148
+ 📁 ~/bunmsh
149
+ $ Bun.sha.var_name = { value: 123 }
150
+ { value: 123 }
151
+ 📁 ~/bunmsh
152
+ $ Bun.sha.var_name.value
153
+ 123
154
+
155
+ # JavaScript inside ordinary shell command substitution
156
+ 📁 ~/bunmsh
157
+ $ echo "Bun version: $(Bun.version)"
158
+
159
+ # Feed a JavaScript result into ordinary shell flow control
160
+ 📁 ~/bunmsh
161
+ $ if [ $(Bun.e, Math.PI<3 ) = true ] ; then echo right ; else echo wrong ; fi
162
+ wrong
163
+ 📁 ~/bunmsh
164
+ $ if [ $(Bun.e, Math.PI>3 ) = true ] ; then echo right ; else echo wrong ; fi
165
+ right
166
+ ```
167
+
168
+ bunmsh can evaluate Bun JavaScript directly. Before tokenising, quoting, or
169
+ performing any shell expansion, it removes leading whitespace and checks the
170
+ first four characters of the original input. If they are exactly `Bun.`, the
171
+ entire input is passed directly to JavaScript `eval` instead of the shell
172
+ parser. Everything else continues through the normal shell parser.
173
+
174
+ A non-`undefined` result is printed automatically. Promises are awaited, so
175
+ asynchronous Bun APIs do not need explicit top-level `await`. Errors are
176
+ printed to standard error with a stack trace and set the shell status to `1`;
177
+ successful evaluation sets it to `0`, and an `undefined` result prints nothing.
178
+
179
+ #### Use `Bun.e;` or `Bun.e,` to run arbitrary JavaScript
180
+
181
+ The `Bun.` prefix is only the switch that selects JavaScript mode; the code
182
+ does not otherwise have to call a Bun API. `Bun.e` is a convenient no-op prefix
183
+ that permits arbitrary JavaScript while keeping the required first four
184
+ characters. The `e` property does not need to exist because reading a missing
185
+ JavaScript property simply produces `undefined`.
186
+
187
+ Technically, any property name works because only the leading `Bun.` is
188
+ checked; for example, `Bun.x;` and `Bun.anything,` also enter JavaScript mode.
189
+ This README uses `e` conventionally because it stands for **eval** (or
190
+ **evaluate**), is short, and makes the intent recognisable. Use `Bun.e;` before
191
+ one or more statements and `Bun.e,` before a single expression.
192
+
193
+ `Bun.e;` discards the `Bun.e` value and starts a new statement. `Bun.e,`
194
+ discards it through JavaScript's comma operator and returns the expression on
195
+ the right. Neither form invokes Bun Shell by itself; their purpose here is
196
+ only to enter the highest-priority evaluator and then execute unrestricted
197
+ JavaScript.
198
+
199
+ #### Use `Bun.sha` as a shared area (hack)
200
+
201
+ `Bun.sha` is normally Bun's SHA hashing function. JavaScript functions are
202
+ objects, and the function is extensible in current Bun versions, so properties
203
+ such as `Bun.sha.var_name` can serve as a lightweight process-wide shared area.
204
+
205
+ These lines already begin with `Bun.`, so they enter the highest-priority
206
+ JavaScript evaluator directly. Values remain available to later commands,
207
+ tabs and command substitutions running in the same bunmsh process. They do
208
+ not survive a bunmsh restart and are not inherited as JavaScript objects by
209
+ external child processes.
210
+
211
+ This is deliberately a hack, not an official Bun storage API. Use fresh
212
+ property names. A function already has reserved or behaviour-sensitive names
213
+ such as `name`, `length`, `call`, `apply`, `bind`, `caller`, `arguments`,
214
+ `constructor` and `toString`. In the tested Bun version, `name` and `length`
215
+ are read-only and assignment throws a `TypeError`; shadowing other Function
216
+ properties can break normal function behaviour. Before choosing a direct
217
+ property name, check it with `Bun.e, Object.hasOwn(Bun.sha, "var_name")`.
218
+
219
+ #### JavaScript inside shell command substitution
220
+
221
+ This mode also composes with shell command substitution. The outer command is
222
+ parsed as shell, while the contents of `$(...)` are evaluated again and can
223
+ trigger JavaScript mode independently.
224
+
225
+ #### Flow control with JavaScript values
226
+
227
+ JavaScript command substitution can feed ordinary shell tests and flow
228
+ control. The outer `if`, `[`, `then`, `else`, and `fi` remain shell syntax;
229
+ only the contents of each `$(...)` enter the JavaScript evaluator.
230
+
231
+ > **Security:** this is unrestricted JavaScript execution with the same file,
232
+ > process, environment and network permissions as bunmsh. Never pass untrusted
233
+ > input to this mode or expose it as a remote command interface.
234
+
235
+ ### History
236
+
237
+ Interactive bunmsh loads its own saved history and imports both Bash and Fish
238
+ history once at startup. Imported commands immediately participate in
239
+ history-based ghost completion, but the Bash and Fish source files are
240
+ read-only and are never modified by bunmsh:
241
+
242
+ ```text
243
+ ~/.bash_history
244
+ ~/.local/share/fish/fish_history
245
+ ```
246
+
247
+ Fish history uses Fish's documented YAML-style records; it is not treated as
248
+ standards-compliant YAML. Empty entries, Bash timestamp records and duplicate
249
+ commands are removed while keeping the most recent occurrence.
250
+ `BUNMSH_IMPORT_HISTORY` controls only the Bash and Fish imports; saved bunmsh
251
+ history is still loaded. Set it to disable external startup imports:
252
+
253
+ ```sh
254
+ BUNMSH_IMPORT_HISTORY=0 bunmsh
255
+ ```
256
+
257
+ `false`, `off` and `no` are also accepted, case-insensitively.
258
+
259
+ bunmsh saves its own history to a file, loaded again by every newly opened
260
+ bunmsh for both Up/Down arrow recall and ghost completion.
261
+
262
+ - **When it saves.** Automatically: every 60 seconds, once more when the
263
+ shell exits, and once more if it receives SIGTERM or SIGHUP (for example
264
+ from `kill`, or a terminal emulator closing its window) — while a
265
+ foreground command owns the terminal, a caught SIGTERM/SIGHUP flushes
266
+ history immediately but does not stop that command, since bunmsh has no
267
+ job-control layer yet that could do so safely; the shell exits once the
268
+ command finishes on its own. `tab s`/`tab save` trigger the same save on
269
+ demand, e.g. right before closing the terminal some other way.
270
+ - **File format.** [JSON Lines](https://jsonlines.org/) — one JSON-encoded
271
+ command per line. A history file saved in bunmsh's older single-JSON-array
272
+ format is still read correctly and is silently upgraded to JSON Lines the
273
+ next time anything is saved.
274
+ - **How concurrent sessions are handled.** Every save above only *appends*
275
+ the lines this session hasn't saved yet — it never rewrites the file. That
276
+ makes it safe to run several bunmsh sessions at once: each one only adds
277
+ its own new lines to the end and never touches (or needs to know about)
278
+ what another session has written, so two sessions saving around the same
279
+ time can't overwrite each other's history the way rewriting the whole file
280
+ each time would risk.
281
+ - **Deduplication.** Because saving only appends, duplicate commands are
282
+ *not* removed automatically. Run `tab s d` (or `tab save dedupe`) to
283
+ rewrite the file keeping only each command's most recent occurrence.
284
+ Unlike the routine append, that rewrites the whole file, so it is an
285
+ explicit, occasional choice that carries a small chance of losing another
286
+ session's write if it happens mid-rewrite — the command's own output says
287
+ so if it looks like that happened, so it's obvious when it's worth
288
+ rerunning.
289
+ - **File location.** `$XDG_DATA_HOME/bunmsh/history`, falling back to
290
+ `~/.local/share/bunmsh/history`. Windows uses
291
+ `%LOCALAPPDATA%/bunmsh/history`.
292
+
293
+ ### Tab system
294
+
295
+ | Command | Effect |
296
+ | --- | --- |
297
+ | `tab` | Create a second tab when only one exists; otherwise cycle right |
298
+ | `tab n` | Create and activate a new tab at the current cwd |
299
+ | `tab NUMBER` | Activate a tab by its 1-based number |
300
+ | `tab l` | Cycle left |
301
+ | `tab r` | Cycle right |
302
+ | `tab x`, `tab c` | Close the active tab |
303
+
304
+ bunmsh includes lightweight cwd tabs and starts with one tab. A tab stores only
305
+ its working directory; variables, environment, aliases, readonly names,
306
+ positional arguments, command status, and other shell state remain shared.
307
+ Switching tabs changes `cwd` and updates `PWD` without changing `OLDPWD`.
308
+
309
+ When more than one tab exists, the prompt displays every remembered path:
310
+
311
+ ```text
312
+ 📁 ~/project 📂 ~/project/src
313
+ [2]$
314
+ ```
315
+
316
+ `📂` marks the active tab and `📁` marks inactive tabs. The complete active
317
+ entry is highlighted in cyan-blue. The default prompt includes the active
318
+ tab's 1-based number only when multiple tabs exist.
319
+
320
+ Left and right movement wrap at the ends. Closing a tab selects the tab that
321
+ moves into the same position, or the previous tab when closing the rightmost
322
+ one. The final remaining tab cannot be closed.
323
+
324
+ A typical workflow is:
325
+
326
+ ```sh
327
+ cd ~/project
328
+ tab n
329
+ cd src
330
+ tab l # back to ~/project
331
+ tab r # back to ~/project/src
332
+ ```
333
+
334
+ ## Special Interactions
335
+
336
+ ### Keyboard shortcuts
337
+
338
+ - `Ctrl-C`: Interrupts the current input or foreground operation and returns to
339
+ a fresh prompt without exiting bunmsh.
340
+
341
+ - `Ctrl-D`: On an empty input line, sends EOF and exits the interactive shell.
342
+
343
+ - `Ctrl-T`: Calls `builtin tab` without adding a command to history. It creates
344
+ a tab when only one exists, or switches to the next tab otherwise. Any
345
+ command text currently being edited is preserved.
346
+
347
+ - `Alt-T`: Calls `builtin tab l` without adding a command to history, switching
348
+ to the tab on the left while preserving the command currently being edited.
349
+
350
+ - `Alt-L`: Calls `builtin lsfancy` without adding a command to history. It
351
+ immediately rereads and lists the active tab's cwd while preserving the
352
+ command currently being edited.
353
+
354
+ - `Alt-U` / `Alt-P`: Calls `builtin lsfancy ..` to list the Parent (upper)
355
+ folder without changing cwd, adding a command to history, or discarding the
356
+ command currently being edited.
357
+
358
+ - `Alt-C`: Calls `builtin tab x` to close the active tab without adding a
359
+ command to history. `tab c` is an equivalent command form.
360
+
361
+ ### Mouse interactions
362
+
363
+ - Mouse tab click: With mouse tracking enabled, left-clicking a tab's icon or
364
+ path activates it. Spaces between tabs are not clickable. Wrapped tab paths
365
+ remain clickable.
366
+
367
+ - Mouse tracking control: `tab mouse` toggles tracking. `tab mouse on`/`true`
368
+ explicitly enables it, while `tab mouse off`/`false` disables it.
369
+
370
+ - Mouse tab double-click: Double-clicking the same tab within 400 ms activates
371
+ it and calls `builtin lsfancy`, immediately rereading the directory without
372
+ using the completion cache.
373
+
374
+ - Mouse prompt-number click: When multiple tabs exist, clicking the number at
375
+ the start of the `$` prompt calls `builtin tab n` to create and activate a
376
+ new tab.
377
+
378
+ - Mouse click-to-position: Clicking anywhere in the line currently being
379
+ typed moves the cursor there instead of leaving it at the end, the same as
380
+ clicking inside a text field. Clicking past the end of the typed text moves
381
+ the cursor to the end of the line rather than doing nothing.
382
+
383
+ - Mouse foreground behavior: Mouse reporting is disabled while a foreground
384
+ command owns the terminal, so full-screen editors and other TUI programs
385
+ receive their own mouse input. bunmsh restores it when the command returns.
386
+
387
+ ### Terminal behavior
388
+
389
+ - `↩️`: When a command finishes without a trailing newline, bunmsh prints this
390
+ marker and then inserts a newline before drawing the next prompt. The marker
391
+ makes it clear where the program's exact output ended; it is not part of the
392
+ command's output. Commands that end their own output with a newline do not
393
+ show the marker.
394
+
395
+ ### Very short aliases
396
+
397
+ - `?`: When the entire command is exactly this single character, prints the
398
+ previous exit status. It is equivalent to `echo $?` and prints `0` after a
399
+ successful command. After a command fails, only the `$` in the next prompt
400
+ is shown in red. For Fish users, the corresponding expression is
401
+ `echo $status` because Fish uses `$status` where POSIX-style shells use `$?`.
402
+
403
+ - `..`: As a standalone command, changes to the parent directory. Equivalent
404
+ to `cd ..`.
405
+
406
+ - `//`: As a standalone command, returns to the most recently visited child
407
+ directory below the current cwd. Equivalent to `cd //`.
408
+
409
+ - `~`: As a standalone command, changes to `$HOME`. Equivalent to `cd`.
410
+
411
+ - `-`: As a standalone command, changes to `$OLDPWD` and prints the selected
412
+ path. Equivalent to `cd -`.
413
+
414
+ ## Built-in commands and supported flags
415
+
416
+ This section documents the options implemented by bunmsh itself. It is not a
417
+ claim of complete POSIX, mksh or GNU compatibility. Shell builtins are resolved
418
+ before `PATH`. Fallback builtins are used only when no executable with the same
419
+ name is found in `PATH`; use `builtin NAME ...` to select either kind explicitly.
420
+
421
+ Run `builtin` with no arguments to print the registered names at runtime.
422
+
423
+ ### Shell builtins (before PATH)
424
+
425
+ | Command | Supported flags/forms |
426
+ | --- | --- |
427
+ | `:`, `true`, `false` | No flags |
428
+ | `command` | `-p`, `-v`, `-V`, `--` |
429
+ | `builtin`, `__builtin` | `--`; no operand lists all builtins (`builtin` only) |
430
+ | `whence` | `-p`, `-v`, `--` |
431
+ | `which` | `--` |
432
+ | `type` | Names only |
433
+ | `alias` | `alias`, `alias NAME`, `alias NAME=VALUE` |
434
+ | `unalias` | `-a`, `--` |
435
+ | `test`, `[` | `!`, `-n`, `-z`, `-e`, `-f`, `-d`, `-b`, `-c`, `-p`, `-S`, `-L`, `-h`, `-s`, `-r`, `-w`, `-x`; string `=`, `==`, `!=`; integer `-eq`, `-ne`, `-gt`, `-ge`, `-lt`, `-le`; file `-nt`, `-ot`, `-ef`; `-a`, `-o` |
436
+ | `echo` | `-n` |
437
+ | `print` | `-r`, `-R`, `-n`, `-l`, `-N`, `-u1`, `-u2`, `--` |
438
+ | `printf` | `%s`, `%d`, `%i`, `%%`, numeric field width and zero padding; basic backslash escapes |
439
+ | `read` | `-r`, `--`; defaults to `REPLY` when no name is given |
440
+ | `pwd` | No flags |
441
+ | `cd`, `chdir` | `cd`, `cd DIR`, `cd -`, `cd //` |
442
+ | `tab` | `n`, `x`, `c`, `l`, `r`, `s`, `save`, `mouse [on\|off\|true\|false]`, `path [on\|off\|true\|false]`, or a 1-based tab number |
443
+ | `-`, `~`, `..`, `//` | Directory-navigation shortcuts |
444
+ | `export` | `NAME`, `NAME=VALUE` |
445
+ | `unset` | `-v`, `--` |
446
+ | `readonly` | `-p`, `--`, `NAME`, `NAME=VALUE` |
447
+ | `env` | `-i`, `--ignore-environment`, `-u NAME`, `--unset NAME`, `--unset=NAME`, `--` |
448
+ | `exec` | Command and arguments; no command is a no-op |
449
+ | `exit` | Optional numeric status |
450
+ | `shift` | Optional non-negative count |
451
+ | `getopts` | `getopts OPTSTRING NAME [ARG ...]` |
452
+ | `eval` | Arguments are joined and evaluated as shell source |
453
+ | `.`, `source` | `FILE [ARG ...]` |
454
+ | `realpath` | One or more paths |
455
+ | `umask` | No operand to display, or an octal mask |
456
+ | `kill` | `-l`, `-SIGNAL`, `-NUMBER` |
457
+ | `set` | No operand to list variables; `-- ARG ...` sets positional arguments |
458
+ | `time` | Command and arguments; reports `real` elapsed time in milliseconds, with each decimal magnitude group shown in a different color |
459
+ | `yes` | Optional output words; no flags |
460
+
461
+ ### The `serve` command
462
+
463
+ `serve [--auto-open] [--minapk-webview] [--random-url] [directory]` starts a minimal HTTP file server; it is a PATH-fallback
464
+ builtin, so a `serve` executable found in `PATH` wins unless it is invoked as
465
+ `builtin serve ...`.
466
+
467
+ - Serves the current working directory, or the directory given as the only
468
+ argument. More than one argument is a usage error.
469
+ - `PORT` selects the port; it defaults to `3000`, and only that default falls
470
+ back to a free port chosen by the OS when it is taken — an explicit `PORT`
471
+ that is already in use is an error. The URL it settled on is printed on
472
+ startup.
473
+ - `--auto-open` opens the printed URL after startup: `xdg-open` on PATH wins
474
+ on any platform, otherwise `open` on macOS or `cmd /c start` on Windows.
475
+ `--auto-open=/path` opens that path relative to the served URL instead of
476
+ the root (resolved after any `--random-url` prefix, so the secret prefix
477
+ stays intact); the `o` stdin control opens the same target.
478
+ - `--minapk-webview` adds `MINAPK_WEBVIEW=1` to the opener's child process,
479
+ matching the npm `buninu` package's WebView-aware `xdg-open` behavior
480
+ (`--minapk-webview=N` passes a different digit string).
481
+ - `--random-url` places the server behind a URL-safe random prefix generated
482
+ from four Bun UUIDv7 values — at least 248 bits of guaranteed-random
483
+ entropy (each UUIDv7's 62-bit `rand_b` field; the 48-bit timestamp isn't
484
+ secret, and Bun's `rand_a` is a monotonic counter within the same
485
+ millisecond rather than independently random, so neither counts toward the
486
+ floor); a request without it gets `404`.
487
+ - All three default to off/not-passed, and `SERVE_AUTO_OPEN`,
488
+ `SERVE_MINAPK_WEBVIEW`, and `SERVE_RANDOM_URL` set their defaults instead
489
+ (`SERVE_AUTO_OPEN` can also be set to a `/`-led path, same as
490
+ `--auto-open=/path`). A CLI flag always overrides its environment default:
491
+ the bare flag forces it on, and `=off`/`=no`/`=false`/`=` (empty) forces it
492
+ off, even if the environment turned it on inline.
493
+ - While it runs it holds the foreground. On a TTY it also takes single-word
494
+ controls on stdin: `q`, `quit` or `exit` stops it, and `o` opens the URL the
495
+ same way `--auto-open` would. `Ctrl-C` and `SIGTERM` stop it too, exiting
496
+ `130` and `143` respectively; every other way out exits `0`.
497
+ - Directory pages list the entries by name, each with an emoji for its kind,
498
+ and hide dotfiles. Previewable files get a `🔍` link next to them: it renders
499
+ Markdown, and pretty-prints JSON, JSON5, JSONC, JSONL, YAML and TOML, plus
500
+ XML when the running Bun provides `Bun.XML.parse`.
501
+ - Files are served through a whole, unsliced `Bun.file()` response, so Bun's
502
+ own `Range` handling applies — `206` and `416` included. Files inside a
503
+ compiled binary are the exception: Bun answers those with the whole body and
504
+ a `200`, whatever `Range` asked for.
505
+ - Paths that would escape the served directory are rejected with `400`, and a
506
+ failing request cannot take the server down; it answers `500` and stays up.
507
+ - It does not have to be started from inside an interactive bunmsh. Any of the
508
+ non-interactive entry points reaches it, which is what makes it usable as a
509
+ one-liner from another shell, a script, or a service unit:
510
+
511
+ ```sh
512
+ ./bmsh -cc builtin serve ./public # argv as-is, no shell parsing
513
+ ./bmsh -c 'builtin serve ./public' # shell text, expansions apply
514
+ bun ./src/main.js -cc builtin serve . # from a source checkout
515
+ npx bunmsh -cc builtin serve . # without installing anything
516
+ ```
517
+
518
+ `-cc` forwards everything after it as argv, so the calling shell does the
519
+ quoting and bunmsh performs no expansion of its own. `builtin` is what pins
520
+ the choice to this implementation; drop it and a `serve` executable found in
521
+ `PATH` would win. The `q`/`o` controls still work whenever stdin is a TTY,
522
+ and `Ctrl-C` stops it either way.
523
+
524
+ #### Serving a folder packed into the executable
525
+
526
+ Want `--auto-open`, `--minapk-webview`, or `--random-url` baked into the
527
+ binary? See the next section.
528
+
529
+ - What this is for is handing a folder of files to someone, not hosting a site.
530
+ - That is why `serve` never answers a directory with its `index.html`:
531
+ - `/` is always the listing, so the folder stays browsable
532
+ - and every file — the index page included — is one click away.
533
+
534
+ - Two other tools do the site case properly
535
+ * `npx serve <dir>` sends `index.html` for any directory that has one and falls
536
+ back to a listing for any directory that does not.
537
+ * Bun 1.4's directory routes do it in-process, streaming with `sendfile` and handling `Content-Type`, `ETag`, `Last-Modified`, `304` and `Range`; a directory holding an `index.html` gets it, one without gets `404`:
538
+
539
+ ```js
540
+ Bun.serve({ routes: { "/static/*": { dir: "./public" } } });
541
+ ```
542
+
543
+ A compiled `bmsh` can serve a directory that lives *inside* the binary, so a
544
+ whole folder ships as one file and is browsed over HTTP wherever it lands.
545
+ Nothing needs to be checked out or configured — two commands do it:
546
+
547
+ ```sh
548
+ npx bunmsh --build-exe --asset /absolute/path/to/mysite
549
+ ./bmsh -cc builtin serve 'B:/~BUN/mysite'
550
+ ```
551
+
552
+ On Windows the build appends `.exe` to the output name, so the same two lines
553
+ read:
554
+
555
+ ```powershell
556
+ npx bunmsh --build-exe --asset C:\path\to\mysite
557
+ ./bmsh.exe -cc builtin serve 'B:/~BUN/mysite'
558
+ ```
559
+
560
+ PowerShell's single quotes are literal, exactly like the POSIX shell's, so the
561
+ argument itself is written the same way on both. `cmd.exe` has no single
562
+ quotes — it would hand them to the program as part of the path — so there the
563
+ line is `.\bmsh.exe -cc builtin serve "B:/~BUN/mysite"`.
564
+
565
+ - Everything after `--build-exe` is forwarded to `bun build`, which is how
566
+ `--asset` reaches the compile step. `--build-for <target>` takes the same
567
+ trailing flags, so a cross-compiled binary can carry the folder too.
568
+ - **The path has to be absolute.** The compile runs from the installed
569
+ package's own directory, wherever npm put it, so a relative path resolves
570
+ somewhere else entirely and the build fails. A trailing slash is harmless.
571
+ - `--asset` keeps only the **basename** of the folder, and that name becomes a
572
+ root inside the binary: `/absolute/path/to/mysite` is served as
573
+ `B:/~BUN/mysite`, or equivalently `/$bunfs/root/mysite`. `serve` accepts
574
+ either spelling on either platform and treats the `/root` part as optional,
575
+ so one argument works on Linux, macOS and Windows alike. Quote it: both the
576
+ POSIX shells and PowerShell would otherwise expand `$bunfs` to nothing.
577
+ - The folder is copied in whole, recursively, and `./bmsh` is written to the
578
+ current directory, along with the `bmsh.meta.json` and `bmsh.meta.md` build
579
+ reports. Only the executable is needed to run it; the two reports can be
580
+ deleted.
581
+ - Serving `'B:/~BUN'` instead browses the binary's whole virtual root, which
582
+ also holds the executable itself and bunmsh's own embedded assets.
583
+ - Only a compiled executable has that virtual filesystem. From a source
584
+ checkout the same folder is just a folder: `serve ./mysite`.
585
+
586
+ `--asset` hands the files to Bun directly and bypasses bunmsh's asset packing
587
+ system. That system is the other way to do this — the folder is declared in
588
+ `package.json` under `assets` and the build runs with `ASSETS_BUNFS=1` — and
589
+ what it buys is source-level access: `readAssetText` and `readAssetBytes`
590
+ answer the same key whether the file is embedded or still on disk, so the same
591
+ JavaScript runs from a checkout and from the binary. `--asset` alone gives no
592
+ such reader; the files exist only as paths inside the binary. See
593
+ [`single-exe/README.md`](single-exe/README.md) for that route and for the
594
+ trade-offs between its two back ends.
595
+
596
+ #### Baking in `--auto-open`, `--minapk-webview`, `--random-url`
597
+
598
+ `serve`'s three env vars (`SERVE_AUTO_OPEN`, `SERVE_MINAPK_WEBVIEW`,
599
+ `SERVE_RANDOM_URL`) are each read as a literal `process.env.NAME` expression,
600
+ so `bun build`'s `--define` can bake a default straight into the compiled
601
+ binary — combine it with `--asset` to ship a self-opening server for a
602
+ folder. `--define` normally needs a bare string quoted as JSON; this
603
+ limitation doesn't apply here, because this project does extra handling to
604
+ quote it for you (see below):
605
+
606
+ ```sh
607
+ bun ./src/main.js --build-exe --asset /absolute/path/to/mysite \
608
+ --define process.env.SERVE_RANDOM_URL=on \
609
+ --define process.env.SERVE_AUTO_OPEN=/index.html \
610
+ --define process.env.SERVE_MINAPK_WEBVIEW=1
611
+ ./bmsh -cc builtin serve 'B:/~BUN/mysite'
612
+ ```
613
+
614
+ ```text
615
+ Serving B:/~BUN/mysite
616
+ http://localhost:3000/AaBIdviNcACxwZF2x3VW0QAaBIdviNcAGsAviNI5B59A.../index.html
617
+ ```
618
+
619
+ That starts `xdg-open` (or the platform fallback) on `/index.html` behind the
620
+ random prefix, with `MINAPK_WEBVIEW=1` on its environment — no flags needed
621
+ on the command line, and a plain `SERVE_AUTO_OPEN=/index.html` set at
622
+ *runtime* no longer does anything, since `--define` replaced the
623
+ `process.env.SERVE_AUTO_OPEN` expression in the compiled code with a literal
624
+ before it ever runs. The CLI flags (`--auto-open`, `--random-url`,
625
+ `--minapk-webview`, with their `=off`/`=no`/`=false` forms) still work
626
+ normally and override whatever was baked in.
627
+
628
+ The resulting `bmsh` is also directly packageable with
629
+ `npx @drxiaozhi/minapk` ([minapk](https://www.npmjs.com/package/@drxiaozhi/minapk))
630
+ as the `libmain.so` the APK runs on launch:
631
+
632
+ ```sh
633
+ npx @drxiaozhi/minapk ./bmsh -c "libmain.so -cc builtin serve 'B:/~BUN/mysite'"
634
+ ```
635
+
636
+ `-c` replaces minapk's default startup command outright — that default is
637
+ what auto-runs a packaged `libmain.so`, so once `-c` is used it has to call
638
+ `libmain.so` itself, exactly as above, or the packaged binary never starts.
639
+ The app itself holds two WebViews from launch: `0` is the console the shell
640
+ above runs in, `1` is a second one that starts blank. `--minapk-webview` (or
641
+ baked-in `SERVE_MINAPK_WEBVIEW`) is what tells the packaged `xdg-open` to
642
+ load the served page into WebView `1` and switch to it — that switch, not
643
+ anything automatic about the APK itself, is what turns "the app opened" into
644
+ "the app is now showing this folder's content." Combine it with
645
+ `--auto-open`/`SERVE_AUTO_OPEN` (baked in as shown above, or passed on the
646
+ `serve` command line) so that switch happens on its own, no browser tab, no
647
+ separate server to start.
648
+
649
+ **`--define` values originally need to be JSON, so a bare string has to
650
+ arrive already quoted** — `--define process.env.SERVE_AUTO_OPEN=/index.html`
651
+ would fail if directly passed to `bun build`, which tries to parse
652
+ `/index.html` as a JS expression. `src/main.js` calls
653
+ `stringifyNonPrimitiveDefineValues` (from
654
+ `single-exe/compiled.js`, the same helper the npm package `jsmdcui` uses for
655
+ its own `--define`-backed settings) once per `SERVE_*` name before
656
+ `buildEarlyExit` runs, so it quotes any string-shaped value for you and
657
+ leaves an already valid bare literal (a number, boolean, `null`, or
658
+ `undefined` — e.g. `=1`) untouched. `off`, `on`, and a `/`-led path are all
659
+ string-shaped, so they get quoted automatically; that's what makes the
660
+ unquoted `=on` / `=/index.html` / `=1` forms above work directly from a
661
+ shell, no manual `--define 'process.env.SERVE_AUTO_OPEN="/index.html"'`
662
+ quoting required.
663
+
664
+ ### PATH-fallback builtins
665
+
666
+ Fallback builtins normally let an executable in `PATH` win. To use the fancy
667
+ implementations by default even when the system provides `cat` and `ls`, make
668
+ aliases that explicitly select the builtin commands:
669
+
670
+ ```sh
671
+ alias cat='builtin catfancy'
672
+ alias ls='builtin lsfancy'
673
+ ```
674
+
675
+ After that, `cat README.md` renders Markdown with ANSI styling and terminal
676
+ hyperlinks, while `ls` uses the emoji and terminal-width-aware listing. Use
677
+ `unalias cat ls` to restore normal PATH-first lookup for the current shell.
678
+
679
+ | Command | Supported flags/forms |
680
+ | --- | --- |
681
+ | `basename` | `--`, optional suffix |
682
+ | `dirname` | `--` |
683
+ | `cat` | `--exclude PATTERN`, `--exclude=PATTERN` using `Bun.Glob`, `--`; files and `-` for stdin |
684
+ | `tac` | `--`; files and `-` for stdin; reverses newline-delimited records in each operand; no other options |
685
+ | `catfancy` | `--exclude PATTERN`, `--exclude=PATTERN` using `Bun.Glob`, `--`; files and `-` for stdin; JSON, JSON5, JSONC, JSONL/NDJSON, YAML, TOML, and XML are parsed, pretty-printed as JSON, and colored with `Bun.color`; Markdown uses `Bun.markdown.ansi` with terminal hyperlinks; `.js`/`.mjs`/`.cjs`/`.jsx` and `.ts`/`.mts`/`.cts`/`.tsx` are wrapped in a fenced ` ```javascript `/` ```typescript ` block and rendered the same way, for the same syntax coloring; other formats are emitted unchanged |
686
+ | `head` | `-n N`, `-N`, `-c N`, `-cN` |
687
+ | `tail` | `-n N`, `-N`; `-n +N` and `-n+N` output starting at line N |
688
+ | `wc` | `-l`, `-w`, `-c`, combinable |
689
+ | `tr` | `-d`; simple ranges such as `a-z` |
690
+ | `tee` | `-a` |
691
+ | `sleep` | Durations with `ms`, `s`, `m`, `h` suffixes; seconds by default |
692
+ | `clear` | No flags |
693
+ | `rmdir` | `-p`, `--parents` |
694
+ | `mktemp` | `-d`; template must end in `XXXXXX` |
695
+ | `sort` | `-r`, `-n`, `-u`, combinable |
696
+ | `date` | `+FORMAT`; `%Y`, `%m`, `%d`, `%H`, `%M`, `%S`, `%s`, `%F`, `%T`, `%%` |
697
+ | `md5sum`, `sha256sum` | Files or stdin; no flags |
698
+ | `grep` | `-E`, `-F`, `-i`, `-q`, `-v`, `-n`, `-o`, `-r`, `-x`, combinable; `--color[=always\|auto\|never]` (`--colour` also accepted) |
699
+ | `sed` | `-n`, `-e SCRIPT`, `-eSCRIPT`, `-E`, `-r`, `-i`; numeric `p`; `s///` with `g` and `p` |
700
+ | `cut` | `-c LIST`, `-cLIST` |
701
+ | `ln` | `-s`, `-f`, `-T`, combinable (including `-sfT`) |
702
+ | `chmod` | Octal modes, `+x`, `a+x` |
703
+ | `uname` | `-a`, `-s`, `-n`, `-r`, `-v`, `-m`, `-p`, combinable (including `-mprs`); uses Node's OS APIs on Windows without `/proc` |
704
+ | `find` | Paths plus `-name`, `-iname`, `-path`, `-ipath`, `-type f/d/l`, `-mindepth`, `-maxdepth`, `-print`, `-print0`, `!`/`-not`, `-exec COMMAND {} \;`, `-exec COMMAND {} +`; regular builtin on Windows, PATH fallback elsewhere |
705
+ | `bunmsh` | Forwards all following arguments to this bunmsh entry point |
706
+ | `bun` | Forwards all following arguments to the active Bun runtime |
707
+ | `serve` | Auto-open, minapk WebView, and high-entropy random-URL flags; see [The `serve` command](#the-serve-command) above |
708
+ | `ls`, `lsfancy` | The `ls` fallback is `lsfancy`: emoji and terminal-width-aware directory listing; `-a`, `-A`, `-d`, `-l`, `-h`, `-t`, `-r`, `-R`, `-S`, `-1`, `-F`, combinable (including `-lh`, `-ltr`, and `-lSF`); `-l` shows a symlink's target (`link -> target`, including a broken one), and a symlink whose target can't be resolved (missing, or a cycle) gets a 🚫 icon instead of 🔗; `-F` appends a classify suffix (`/` directory, `@` symlink, `*` executable, `=` socket, `\|` FIFO); always reads the directory without using the completion cache |
709
+ | `lsbun` | Bun Shell's own `ls`, kept reachable under this name now that the `ls` fallback is `lsfancy`; currently implements `-a`, `-A`, `-d`, `-l`, `-R` |
710
+ | `mv` | Bun Shell currently accepts `-f`, `-h`, `-i`, `-n`, `-v`, but they do not change its behaviour; notably, `-i` and `-n` do not prevent overwriting |
711
+ | `rm` | Bun Shell currently implements `-f`, `-r`, `-R`, `-v`, `-d`, `-i`, `-I`, `--recursive`, `--verbose`, `--dir`, and `--interactive=never|once|always`; `--preserve-root` and `--no-preserve-root` are accepted but currently have no effect |
712
+ | `mkdir` | Bun Shell currently implements `-p`, `-v`, `--parents`, and `--vebose` (Bun's currently accepted spelling) |
713
+ | `seq` | Bun Shell currently accepts `-s`/`--separator`, `-t`/`--terminator`, and `-w`/`--fixed-width`; its formatting differs from GNU `seq` (`-w` does not currently pad, and a custom separator may also be emitted after the final item) |
714
+ | `touch` | Bun Shell fallback currently supports no flags |
715
+ | `cp` | `-r`, `-R`, `-v`; bunmsh converts `-r` to Bun Shell's `-R`. Bun Shell also accepts `-n`, but it currently has no effect |
716
+
717
+ Fallback commands preserve the system-command-first rule. For example, if
718
+ `/bin/grep` exists it runs instead of the fallback; `builtin grep ...` forces
719
+ the implementation described above.
720
+
721
+ The Bun Shell rows above describe the currently tested Bun implementation, not
722
+ a permanent compatibility guarantee. Their supported flags and exact behaviour
723
+ may change with later Bun releases; see [bunshell.md](bunshell.md) for the
724
+ detailed compatibility snapshot.
725
+
726
+ ## What's implemented
727
+
728
+ - Interactive use, stdin and script files, `-c` shell text, and direct `-cc`
729
+ argv forwarding.
730
+ - Highest-priority `Bun.*` JavaScript evaluation, including awaited promises,
731
+ command substitution, and the `Bun.e;`/`Bun.e,` arbitrary-JavaScript forms.
732
+ - Concurrent streaming pipelines and streamed redirects built on `Bun.spawn`,
733
+ including large output and early-closing consumers.
734
+ - Shell lists, pipelines, negation, functions, subshells, and `if`, `case`,
735
+ `while`, `until`, and `for` compound commands.
736
+ - Quotes, parameter/command/arithmetic expansion, IFS field splitting, tilde,
737
+ brace, and pathname expansion.
738
+ - Environment assignments, aliases, readonly names, positional parameters,
739
+ shell functions, common redirections, `2>&1` descriptor duplication,
740
+ here-documents (`<<`/`<<-`, with quoted/escaped delimiters suppressing
741
+ expansion), and mksh-style here-strings (`<<<`).
742
+ - Regular builtins, system-command-first fallback builtins, Bun Shell fallbacks,
743
+ and explicit lookup through `command`, `builtin`, `whence`, `type`, and
744
+ `which`.
745
+ - Interactive history import/save/recall, command and file completion, ghost
746
+ suggestions, cwd tabs, keyboard shortcuts, optional mouse interactions,
747
+ fancy directory listings, and a `PS2` continuation prompt while a
748
+ here-document, an open quote/substitution, or an unfinished compound
749
+ command is still being typed.
750
+ - Linux, Android/Termux, macOS, and Windows-aware paths, plus standalone builds
751
+ and dynamic-linker re-execution support.
752
+
753
+ See [PORTING.md](PORTING.md) for detailed semantics, implementation boundaries,
754
+ platform notes, and the remaining mksh/POSIX compatibility work.
755
+
756
+ ## Test
757
+
758
+ ```sh
759
+ bun test
760
+ ```
761
+
762
+ ## Built-in documentation
763
+
764
+ Show the README or changelog in the terminal with ANSI formatting:
765
+
766
+ ```sh
767
+ bunmsh --readme
768
+ bunmsh --changelog
769
+ ```
770
+
771
+ Both commands read their embedded copy first when running a standalone
772
+ executable, then fall back to `README.md` or `CHANGELOG.md` in the repository
773
+ during development.
774
+
775
+ ## Standalone executable
776
+
777
+ Build a single-file executable for the current platform:
778
+
779
+ ```sh
780
+ bun ./src/main.js --build-exe
781
+ ./bmsh --version
782
+ ```
783
+
784
+ Cross-compile for a Bun-supported target:
785
+
786
+ ```sh
787
+ bun ./src/main.js --build-for bun-linux-x64
788
+ ```
789
+
790
+ The build writes `./bmsh` and leaves the repository's `./bunmsh` launcher
791
+ unchanged. The executable includes the runtime assets declared in
792
+ `package.json`, currently including its README and changelog. See
793
+ [`single-exe/README.md`](single-exe/README.md) for asset-management options.
794
+
795
+ ## License
796
+
797
+ The original bunmsh JavaScript implementation is released under the
798
+ [MIT License](LICENSE), Copyright (c) 2026 Dr. John (醫者小智).
799
+
800
+ mksh is a separate upstream project and is **not** relicensed under MIT. Its
801
+ complete licence terms remain in [LICENSE-MKSH](LICENSE-MKSH).