command-stream 0.8.2 → 0.9.0

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 (500) hide show
  1. package/js/examples/01-basic-streaming.mjs +14 -0
  2. package/js/examples/02-async-iterator.mjs +9 -0
  3. package/js/examples/03-file-and-console.mjs +16 -0
  4. package/js/examples/04-claude-jq-pipe.mjs +16 -0
  5. package/js/examples/CI-DEBUG-README.md +138 -0
  6. package/js/examples/README-examples.mjs +111 -0
  7. package/js/examples/README.md +345 -0
  8. package/js/examples/STREAMING_INTERFACES_SUMMARY.md +116 -0
  9. package/js/examples/add-test-timeouts.js +107 -0
  10. package/js/examples/ansi-default-preserved.mjs +11 -0
  11. package/js/examples/ansi-global-config.mjs +12 -0
  12. package/js/examples/ansi-reset-default.mjs +12 -0
  13. package/js/examples/ansi-strip-utils.mjs +12 -0
  14. package/js/examples/baseline-child-process.mjs +23 -0
  15. package/js/examples/baseline-claude-test.mjs +47 -0
  16. package/js/examples/baseline-working.mjs +34 -0
  17. package/js/examples/capture-mirror-comparison.mjs +23 -0
  18. package/js/examples/capture-mirror-default.mjs +11 -0
  19. package/js/examples/capture-mirror-performance.mjs +16 -0
  20. package/js/examples/capture-mirror-show-only.mjs +16 -0
  21. package/js/examples/capture-mirror-silent-processing.mjs +16 -0
  22. package/js/examples/ci-debug-baseline-vs-library.mjs +265 -0
  23. package/js/examples/ci-debug-es-module-loading.mjs +184 -0
  24. package/js/examples/ci-debug-signal-handling.mjs +225 -0
  25. package/js/examples/ci-debug-stdout-buffering.mjs +94 -0
  26. package/js/examples/ci-debug-test-timeouts.mjs +259 -0
  27. package/js/examples/claude-exact-file-output.mjs +20 -0
  28. package/js/examples/claude-exact-jq.mjs +13 -0
  29. package/js/examples/claude-exact-streaming.mjs +13 -0
  30. package/js/examples/claude-jq-pipeline.mjs +13 -0
  31. package/js/examples/claude-json-stream.mjs +39 -0
  32. package/js/examples/claude-streaming-basic.mjs +99 -0
  33. package/js/examples/claude-streaming-demo.mjs +126 -0
  34. package/js/examples/claude-streaming-final.mjs +20 -0
  35. package/js/examples/cleanup-verification-test.mjs +115 -0
  36. package/js/examples/colors-buffer-processing.mjs +14 -0
  37. package/js/examples/colors-default-preserved.mjs +15 -0
  38. package/js/examples/colors-per-command-config.mjs +15 -0
  39. package/js/examples/colors-strip-ansi.mjs +13 -0
  40. package/js/examples/commandstream-jq.mjs +29 -0
  41. package/js/examples/commandstream-working.mjs +23 -0
  42. package/js/examples/comprehensive-streams-demo.mjs +121 -0
  43. package/js/examples/ctrl-c-concurrent-processes.mjs +20 -0
  44. package/js/examples/ctrl-c-long-running-command.mjs +20 -0
  45. package/js/examples/ctrl-c-real-system-command.mjs +17 -0
  46. package/js/examples/ctrl-c-sleep-command.mjs +17 -0
  47. package/js/examples/ctrl-c-stdin-forwarding.mjs +20 -0
  48. package/js/examples/ctrl-c-virtual-command.mjs +17 -0
  49. package/js/examples/debug-already-started.mjs +20 -0
  50. package/js/examples/debug-ansi-processing.mjs +42 -0
  51. package/js/examples/debug-basic-streaming.mjs +44 -0
  52. package/js/examples/debug-buildshellcommand.mjs +82 -0
  53. package/js/examples/debug-child-process.mjs +43 -0
  54. package/js/examples/debug-child-state.mjs +55 -0
  55. package/js/examples/debug-chunking.mjs +38 -0
  56. package/js/examples/debug-command-parsing.mjs +61 -0
  57. package/js/examples/debug-complete-consolidation.mjs +97 -0
  58. package/js/examples/debug-echo-args.mjs +22 -0
  59. package/js/examples/debug-emit-timing.mjs +28 -0
  60. package/js/examples/debug-end-event.mjs +56 -0
  61. package/js/examples/debug-errexit.mjs +16 -0
  62. package/js/examples/debug-event-emission.mjs +40 -0
  63. package/js/examples/debug-event-timing.mjs +67 -0
  64. package/js/examples/debug-event-vs-result.mjs +30 -0
  65. package/js/examples/debug-exact-command.mjs +28 -0
  66. package/js/examples/debug-exact-test-scenario.mjs +44 -0
  67. package/js/examples/debug-execution-path.mjs +27 -0
  68. package/js/examples/debug-exit-command.mjs +38 -0
  69. package/js/examples/debug-exit-virtual.mjs +54 -0
  70. package/js/examples/debug-finish-consolidation.mjs +44 -0
  71. package/js/examples/debug-force-cleanup.mjs +47 -0
  72. package/js/examples/debug-getter-basic.mjs +13 -0
  73. package/js/examples/debug-getter-direct.mjs +23 -0
  74. package/js/examples/debug-getter-internals.mjs +61 -0
  75. package/js/examples/debug-handler-detection.mjs +65 -0
  76. package/js/examples/debug-idempotent-finish.mjs +54 -0
  77. package/js/examples/debug-idempotent-kill.mjs +58 -0
  78. package/js/examples/debug-interpolation-individual.mjs +88 -0
  79. package/js/examples/debug-interpolation-issue.mjs +101 -0
  80. package/js/examples/debug-jq-streaming.mjs +57 -0
  81. package/js/examples/debug-jq-tty-colors.mjs +168 -0
  82. package/js/examples/debug-kill-cleanup.mjs +56 -0
  83. package/js/examples/debug-kill-method.mjs +33 -0
  84. package/js/examples/debug-listener-interference.mjs +38 -0
  85. package/js/examples/debug-listener-lifecycle.mjs +50 -0
  86. package/js/examples/debug-listener-timing.mjs +62 -0
  87. package/js/examples/debug-listeners-property.mjs +34 -0
  88. package/js/examples/debug-map-methods.mjs +39 -0
  89. package/js/examples/debug-not-awaited-cleanup.mjs +106 -0
  90. package/js/examples/debug-off-method.mjs +28 -0
  91. package/js/examples/debug-option-merging.mjs +17 -0
  92. package/js/examples/debug-options.mjs +36 -0
  93. package/js/examples/debug-output.mjs +25 -0
  94. package/js/examples/debug-pattern-matching.mjs +69 -0
  95. package/js/examples/debug-pipeline-cat.mjs +28 -0
  96. package/js/examples/debug-pipeline-cleanup.mjs +71 -0
  97. package/js/examples/debug-pipeline-error-detailed.mjs +78 -0
  98. package/js/examples/debug-pipeline-error.mjs +65 -0
  99. package/js/examples/debug-pipeline-issue.mjs +26 -0
  100. package/js/examples/debug-pipeline-method.mjs +22 -0
  101. package/js/examples/debug-pipeline-stream.mjs +66 -0
  102. package/js/examples/debug-pipeline.mjs +14 -0
  103. package/js/examples/debug-process-exit-trace.mjs +41 -0
  104. package/js/examples/debug-process-path.mjs +38 -0
  105. package/js/examples/debug-property-check.mjs +29 -0
  106. package/js/examples/debug-resource-cleanup.mjs +83 -0
  107. package/js/examples/debug-shell-args.mjs +103 -0
  108. package/js/examples/debug-sigint-child-handler.mjs +44 -0
  109. package/js/examples/debug-sigint-forwarding.mjs +61 -0
  110. package/js/examples/debug-sigint-handler-install.mjs +79 -0
  111. package/js/examples/debug-sigint-handler-order.mjs +85 -0
  112. package/js/examples/debug-sigint-listeners.mjs +48 -0
  113. package/js/examples/debug-sigint-start-pattern.mjs +62 -0
  114. package/js/examples/debug-sigint-timer.mjs +40 -0
  115. package/js/examples/debug-simple-command.mjs +49 -0
  116. package/js/examples/debug-simple-getter.mjs +30 -0
  117. package/js/examples/debug-simple.mjs +39 -0
  118. package/js/examples/debug-simplified-finished.mjs +102 -0
  119. package/js/examples/debug-stack-overflow.mjs +25 -0
  120. package/js/examples/debug-stdin-simple.mjs +28 -0
  121. package/js/examples/debug-stdin.mjs +28 -0
  122. package/js/examples/debug-stream-emitter-isolated.mjs +45 -0
  123. package/js/examples/debug-stream-emitter.mjs +25 -0
  124. package/js/examples/debug-stream-events.mjs +70 -0
  125. package/js/examples/debug-stream-generator.mjs +23 -0
  126. package/js/examples/debug-stream-getter-issue.mjs +37 -0
  127. package/js/examples/debug-stream-getter.mjs +19 -0
  128. package/js/examples/debug-stream-internals.mjs +64 -0
  129. package/js/examples/debug-stream-method.mjs +46 -0
  130. package/js/examples/debug-stream-object.mjs +58 -0
  131. package/js/examples/debug-stream-properties.mjs +37 -0
  132. package/js/examples/debug-stream-timing.mjs +28 -0
  133. package/js/examples/debug-streaming.mjs +24 -0
  134. package/js/examples/debug-test-isolation.mjs +54 -0
  135. package/js/examples/debug-test-state.mjs +27 -0
  136. package/js/examples/debug-user-sigint.mjs +19 -0
  137. package/js/examples/debug-virtual-disable.mjs +21 -0
  138. package/js/examples/debug-virtual-vs-real.mjs +68 -0
  139. package/js/examples/debug-with-trace.mjs +23 -0
  140. package/js/examples/debug_parent_stream.mjs +22 -0
  141. package/js/examples/emulate-claude-stream.mjs +30 -0
  142. package/js/examples/emulated-streaming-direct.mjs +22 -0
  143. package/js/examples/emulated-streaming-jq-pipe.mjs +22 -0
  144. package/js/examples/emulated-streaming-sh-pipe.mjs +23 -0
  145. package/js/examples/events-build-process.mjs +73 -0
  146. package/js/examples/events-concurrent-streams.mjs +51 -0
  147. package/js/examples/events-error-handling.mjs +59 -0
  148. package/js/examples/events-file-monitoring.mjs +66 -0
  149. package/js/examples/events-interactive-simulation.mjs +69 -0
  150. package/js/examples/events-log-processing.mjs +72 -0
  151. package/js/examples/events-network-monitoring.mjs +68 -0
  152. package/js/examples/events-ping-basic.mjs +37 -0
  153. package/js/examples/events-progress-tracking.mjs +55 -0
  154. package/js/examples/events-stdin-input.mjs +34 -0
  155. package/js/examples/example-ansi-ls.mjs +39 -0
  156. package/js/examples/example-top.mjs +27 -0
  157. package/js/examples/final-ping-stdin-proof.mjs +88 -0
  158. package/js/examples/final-test-shell-operators.mjs +123 -0
  159. package/js/examples/final-working-examples.mjs +162 -0
  160. package/js/examples/gh-auth-test.mjs +103 -0
  161. package/js/examples/gh-delete-hang-test.mjs +79 -0
  162. package/js/examples/gh-gist-creation-test.mjs +276 -0
  163. package/js/examples/gh-gist-minimal-test.mjs +89 -0
  164. package/js/examples/gh-hang-exact-original.mjs +83 -0
  165. package/js/examples/gh-hang-reproduction.mjs +151 -0
  166. package/js/examples/gh-hang-test-with-redirect.mjs +45 -0
  167. package/js/examples/gh-hang-test-without-redirect.mjs +43 -0
  168. package/js/examples/gh-minimal-hang-check.mjs +33 -0
  169. package/js/examples/gh-operations-with-cd.mjs +187 -0
  170. package/js/examples/gh-output-test.mjs +102 -0
  171. package/js/examples/gh-reproduce-hang.mjs +41 -0
  172. package/js/examples/git-operations-with-cd.mjs +186 -0
  173. package/js/examples/interactive-math-calc.mjs +45 -0
  174. package/js/examples/interactive-top-fixed.mjs +25 -0
  175. package/js/examples/interactive-top-improved.mjs +72 -0
  176. package/js/examples/interactive-top-pty-logging.mjs +69 -0
  177. package/js/examples/interactive-top-pty.mjs +27 -0
  178. package/js/examples/interactive-top-with-logging.mjs +56 -0
  179. package/js/examples/interactive-top.mjs +29 -0
  180. package/js/examples/jq-color-demo.mjs +53 -0
  181. package/js/examples/jq-colors-streaming.mjs +42 -0
  182. package/js/examples/manual-ctrl-c-test.mjs +50 -0
  183. package/js/examples/methods-multiple-options.mjs +25 -0
  184. package/js/examples/methods-run-basic.mjs +10 -0
  185. package/js/examples/methods-start-basic.mjs +10 -0
  186. package/js/examples/node-compat-data-events.mjs +36 -0
  187. package/js/examples/node-compat-readable-event.mjs +29 -0
  188. package/js/examples/node-compat-small-buffer.mjs +33 -0
  189. package/js/examples/options-capture-false.mjs +12 -0
  190. package/js/examples/options-combined-settings.mjs +13 -0
  191. package/js/examples/options-custom-input.mjs +16 -0
  192. package/js/examples/options-default-behavior.mjs +10 -0
  193. package/js/examples/options-maximum-performance.mjs +15 -0
  194. package/js/examples/options-mirror-false.mjs +10 -0
  195. package/js/examples/options-performance-mode.mjs +13 -0
  196. package/js/examples/options-performance-optimization.mjs +14 -0
  197. package/js/examples/options-run-alias-demo.mjs +15 -0
  198. package/js/examples/options-run-alias.mjs +10 -0
  199. package/js/examples/options-silent-execution.mjs +14 -0
  200. package/js/examples/options-streaming-capture.mjs +24 -0
  201. package/js/examples/options-streaming-multiple.mjs +35 -0
  202. package/js/examples/options-streaming-silent.mjs +21 -0
  203. package/js/examples/options-streaming-stdin.mjs +21 -0
  204. package/js/examples/ping-streaming-filtered.mjs +22 -0
  205. package/js/examples/ping-streaming-interruptible.mjs +47 -0
  206. package/js/examples/ping-streaming-silent.mjs +24 -0
  207. package/js/examples/ping-streaming-simple.mjs +13 -0
  208. package/js/examples/ping-streaming-statistics.mjs +49 -0
  209. package/js/examples/ping-streaming-timestamps.mjs +22 -0
  210. package/js/examples/ping-streaming.mjs +48 -0
  211. package/js/examples/prove-ping-stdin-limitation.mjs +94 -0
  212. package/js/examples/readme-example.mjs +39 -0
  213. package/js/examples/realtime-json-stream.mjs +143 -0
  214. package/js/examples/reliable-stdin-commands.mjs +135 -0
  215. package/js/examples/reproduce-issue-135-v2.mjs +15 -0
  216. package/js/examples/reproduce-issue-135.mjs +17 -0
  217. package/js/examples/shell-cd-behavior.mjs +88 -0
  218. package/js/examples/sigint-forwarding-test.mjs +60 -0
  219. package/js/examples/sigint-handler-test.mjs +72 -0
  220. package/js/examples/simple-async-test.mjs +49 -0
  221. package/js/examples/simple-claude-test.mjs +17 -0
  222. package/js/examples/simple-event-test.mjs +33 -0
  223. package/js/examples/simple-jq-streaming.mjs +48 -0
  224. package/js/examples/simple-stream-demo.mjs +35 -0
  225. package/js/examples/simple-test-sleep.js +30 -0
  226. package/js/examples/simple-working-stdin.mjs +30 -0
  227. package/js/examples/streaming-behavior-test.mjs +116 -0
  228. package/js/examples/streaming-direct-command.mjs +21 -0
  229. package/js/examples/streaming-filtered-output.mjs +33 -0
  230. package/js/examples/streaming-grep-pipeline.mjs +21 -0
  231. package/js/examples/streaming-interactive-stdin.mjs +24 -0
  232. package/js/examples/streaming-jq-pipeline.mjs +23 -0
  233. package/js/examples/streaming-multistage-pipeline.mjs +23 -0
  234. package/js/examples/streaming-pipes-event-pattern.mjs +27 -0
  235. package/js/examples/streaming-pipes-multistage.mjs +22 -0
  236. package/js/examples/streaming-pipes-realtime-jq.mjs +23 -0
  237. package/js/examples/streaming-progress-tracking.mjs +34 -0
  238. package/js/examples/streaming-reusable-configs.mjs +52 -0
  239. package/js/examples/streaming-silent-capture.mjs +20 -0
  240. package/js/examples/streaming-test-simple.mjs +70 -0
  241. package/js/examples/streaming-virtual-pipeline.mjs +18 -0
  242. package/js/examples/syntax-basic-comparison.mjs +31 -0
  243. package/js/examples/syntax-basic-options.mjs +12 -0
  244. package/js/examples/syntax-combined-options.mjs +19 -0
  245. package/js/examples/syntax-command-chaining.mjs +12 -0
  246. package/js/examples/syntax-custom-directory.mjs +10 -0
  247. package/js/examples/syntax-custom-environment.mjs +13 -0
  248. package/js/examples/syntax-custom-stdin.mjs +10 -0
  249. package/js/examples/syntax-mixed-regular.mjs +11 -0
  250. package/js/examples/syntax-mixed-usage.mjs +15 -0
  251. package/js/examples/syntax-multiple-listeners.mjs +87 -0
  252. package/js/examples/syntax-piping-comparison.mjs +32 -0
  253. package/js/examples/syntax-reusable-config.mjs +16 -0
  254. package/js/examples/syntax-reusable-configs.mjs +21 -0
  255. package/js/examples/syntax-silent-operations.mjs +10 -0
  256. package/js/examples/syntax-stdin-option.mjs +12 -0
  257. package/js/examples/temp-sigint-test.mjs +21 -0
  258. package/js/examples/test-actual-buildshell.mjs +44 -0
  259. package/js/examples/test-async-streams-working.mjs +102 -0
  260. package/js/examples/test-async-streams.mjs +90 -0
  261. package/js/examples/test-auth-parse.mjs +74 -0
  262. package/js/examples/test-auto-quoting.mjs +57 -0
  263. package/js/examples/test-auto-start-fix.mjs +95 -0
  264. package/js/examples/test-baseline-sigint.mjs +38 -0
  265. package/js/examples/test-buffer-behavior.mjs +39 -0
  266. package/js/examples/test-buffers-simple.mjs +35 -0
  267. package/js/examples/test-bun-specific-issue.mjs +106 -0
  268. package/js/examples/test-bun-streaming.mjs +81 -0
  269. package/js/examples/test-cat-direct.mjs +41 -0
  270. package/js/examples/test-cat-pipe.mjs +34 -0
  271. package/js/examples/test-cd-behavior.mjs +42 -0
  272. package/js/examples/test-child-process-timing.mjs +53 -0
  273. package/js/examples/test-child-sigint-handler.mjs +62 -0
  274. package/js/examples/test-cleanup-simple.mjs +21 -0
  275. package/js/examples/test-comprehensive-tracing.mjs +58 -0
  276. package/js/examples/test-correct-space-handling.mjs +46 -0
  277. package/js/examples/test-ctrl-c-debug.mjs +44 -0
  278. package/js/examples/test-ctrl-c-inherit.mjs +30 -0
  279. package/js/examples/test-ctrl-c-sleep.mjs +31 -0
  280. package/js/examples/test-ctrl-c.mjs +17 -0
  281. package/js/examples/test-debug-new-options.mjs +55 -0
  282. package/js/examples/test-debug-pty.mjs +49 -0
  283. package/js/examples/test-debug-tee.mjs +38 -0
  284. package/js/examples/test-debug.mjs +25 -0
  285. package/js/examples/test-direct-jq.mjs +47 -0
  286. package/js/examples/test-direct-pipe-reading.mjs +119 -0
  287. package/js/examples/test-direct-pipe.sh +28 -0
  288. package/js/examples/test-double-quoting-prevention.mjs +138 -0
  289. package/js/examples/test-edge-cases-quoting.mjs +89 -0
  290. package/js/examples/test-events.mjs +37 -0
  291. package/js/examples/test-explicit-stdio.mjs +51 -0
  292. package/js/examples/test-final-streaming.mjs +71 -0
  293. package/js/examples/test-fix.mjs +71 -0
  294. package/js/examples/test-incremental-streaming.mjs +46 -0
  295. package/js/examples/test-individual-spawn.mjs +35 -0
  296. package/js/examples/test-inherit-stdout-not-stdin.mjs +133 -0
  297. package/js/examples/test-injection-protection.mjs +77 -0
  298. package/js/examples/test-interactive-streaming.mjs +140 -0
  299. package/js/examples/test-interactive-top.md +24 -0
  300. package/js/examples/test-interactive.mjs +17 -0
  301. package/js/examples/test-interpolation.mjs +14 -0
  302. package/js/examples/test-interrupt.mjs +40 -0
  303. package/js/examples/test-issue-135-comprehensive.mjs +41 -0
  304. package/js/examples/test-issue12-detailed.mjs +89 -0
  305. package/js/examples/test-issue12-exact.mjs +33 -0
  306. package/js/examples/test-jq-color.mjs +57 -0
  307. package/js/examples/test-jq-colors.mjs +41 -0
  308. package/js/examples/test-jq-compact.mjs +33 -0
  309. package/js/examples/test-jq-native.sh +10 -0
  310. package/js/examples/test-jq-pipeline-behavior.mjs +80 -0
  311. package/js/examples/test-jq-realtime.mjs +40 -0
  312. package/js/examples/test-manual-start.mjs +54 -0
  313. package/js/examples/test-mixed-quoting.mjs +88 -0
  314. package/js/examples/test-multi-stream.mjs +50 -0
  315. package/js/examples/test-multistage-debug.mjs +44 -0
  316. package/js/examples/test-native-spawn-vs-command-stream.mjs +154 -0
  317. package/js/examples/test-no-parse-pipeline.mjs +33 -0
  318. package/js/examples/test-non-virtual.mjs +52 -0
  319. package/js/examples/test-operators.mjs +53 -0
  320. package/js/examples/test-parent-continues.mjs +44 -0
  321. package/js/examples/test-path-interpolation.mjs +86 -0
  322. package/js/examples/test-ping-kill-and-stdin.mjs +98 -0
  323. package/js/examples/test-ping.mjs +12 -0
  324. package/js/examples/test-pty-spawn.mjs +101 -0
  325. package/js/examples/test-pty.mjs +38 -0
  326. package/js/examples/test-quote-behavior-summary.mjs +110 -0
  327. package/js/examples/test-quote-edge-cases.mjs +69 -0
  328. package/js/examples/test-quote-parsing.mjs +23 -0
  329. package/js/examples/test-raw-function.mjs +153 -0
  330. package/js/examples/test-raw-streaming.mjs +47 -0
  331. package/js/examples/test-readme-examples.mjs +142 -0
  332. package/js/examples/test-real-cat.mjs +28 -0
  333. package/js/examples/test-real-commands.mjs +21 -0
  334. package/js/examples/test-real-shell.mjs +31 -0
  335. package/js/examples/test-real-stdin-commands.mjs +160 -0
  336. package/js/examples/test-runner-batched.mjs +98 -0
  337. package/js/examples/test-runner-simple.mjs +80 -0
  338. package/js/examples/test-runner.mjs +67 -0
  339. package/js/examples/test-scope-parse.mjs +31 -0
  340. package/js/examples/test-sh-pipeline.mjs +24 -0
  341. package/js/examples/test-shell-detection.mjs +71 -0
  342. package/js/examples/test-shell-parser.mjs +37 -0
  343. package/js/examples/test-sigint-behavior.mjs +241 -0
  344. package/js/examples/test-sigint-handling.sh +14 -0
  345. package/js/examples/test-simple-pipe.mjs +12 -0
  346. package/js/examples/test-simple-streaming.mjs +32 -0
  347. package/js/examples/test-sleep-stdin.js +27 -0
  348. package/js/examples/test-sleep.mjs +56 -0
  349. package/js/examples/test-smart-quoting.mjs +180 -0
  350. package/js/examples/test-spaces-in-path.mjs +48 -0
  351. package/js/examples/test-special-chars-quoting.mjs +54 -0
  352. package/js/examples/test-stdin-after-start.mjs +39 -0
  353. package/js/examples/test-stdin-simple.mjs +67 -0
  354. package/js/examples/test-stdin-timing.mjs +74 -0
  355. package/js/examples/test-stdio-combinations.mjs +124 -0
  356. package/js/examples/test-stream-access.mjs +84 -0
  357. package/js/examples/test-stream-cleanup.mjs +27 -0
  358. package/js/examples/test-stream-readers.mjs +152 -0
  359. package/js/examples/test-streaming-final.mjs +57 -0
  360. package/js/examples/test-streaming-interfaces.mjs +141 -0
  361. package/js/examples/test-streaming-timing.mjs +27 -0
  362. package/js/examples/test-streaming.mjs +32 -0
  363. package/js/examples/test-streams-stdin-comprehensive.mjs +134 -0
  364. package/js/examples/test-streams-stdin-ctrl-c.mjs +96 -0
  365. package/js/examples/test-template-literal.mjs +26 -0
  366. package/js/examples/test-template-vs-interpolation.mjs +49 -0
  367. package/js/examples/test-timing.mjs +41 -0
  368. package/js/examples/test-top-inherit-stdout-stdin-control.mjs +123 -0
  369. package/js/examples/test-top-quit-stdin.mjs +118 -0
  370. package/js/examples/test-trace-option.mjs +21 -0
  371. package/js/examples/test-user-double-quotes.mjs +36 -0
  372. package/js/examples/test-user-single-quotes.mjs +36 -0
  373. package/js/examples/test-verbose.mjs +18 -0
  374. package/js/examples/test-verbose2.mjs +32 -0
  375. package/js/examples/test-virtual-streaming.mjs +125 -0
  376. package/js/examples/test-waiting-command.mjs +52 -0
  377. package/js/examples/test-waiting-commands.mjs +83 -0
  378. package/js/examples/test-watch-mode.mjs +104 -0
  379. package/js/examples/test-yes-cancellation.mjs +26 -0
  380. package/js/examples/test-yes-detailed.mjs +58 -0
  381. package/js/examples/test-yes-trace.mjs +28 -0
  382. package/js/examples/trace-abort-controller.mjs +30 -0
  383. package/js/examples/trace-error-handling.mjs +22 -0
  384. package/js/examples/trace-pipeline-command.mjs +22 -0
  385. package/js/examples/trace-signal-handling.mjs +35 -0
  386. package/js/examples/trace-simple-command.mjs +18 -0
  387. package/js/examples/trace-stderr-output.mjs +22 -0
  388. package/js/examples/verify-fix-both-runtimes.mjs +73 -0
  389. package/js/examples/verify-issue12-fixed.mjs +78 -0
  390. package/js/examples/which-command-common-commands.mjs +19 -0
  391. package/js/examples/which-command-gh-test.mjs +23 -0
  392. package/js/examples/which-command-nonexistent.mjs +20 -0
  393. package/js/examples/which-command-system-comparison.mjs +28 -0
  394. package/js/examples/working-example.mjs +13 -0
  395. package/js/examples/working-stdin-examples.mjs +138 -0
  396. package/js/examples/working-streaming-demo.mjs +49 -0
  397. package/{src → js/src}/$.mjs +20 -4
  398. package/{src → js/src}/$.utils.mjs +14 -2
  399. package/js/tests/$.features.test.mjs +283 -0
  400. package/js/tests/$.test.mjs +935 -0
  401. package/js/tests/builtin-commands.test.mjs +387 -0
  402. package/js/tests/bun-shell-path-fix.test.mjs +115 -0
  403. package/js/tests/bun.features.test.mjs +189 -0
  404. package/js/tests/cd-virtual-command.test.mjs +622 -0
  405. package/js/tests/cleanup-verification.test.mjs +127 -0
  406. package/js/tests/ctrl-c-baseline.test.mjs +207 -0
  407. package/js/tests/ctrl-c-basic.test.mjs +220 -0
  408. package/js/tests/ctrl-c-library.test.mjs +197 -0
  409. package/js/tests/ctrl-c-signal.test.mjs +915 -0
  410. package/js/tests/examples.test.mjs +252 -0
  411. package/js/tests/execa.features.test.mjs +198 -0
  412. package/js/tests/gh-commands.test.mjs +164 -0
  413. package/js/tests/gh-gist-operations.test.mjs +221 -0
  414. package/js/tests/git-gh-cd.test.mjs +466 -0
  415. package/js/tests/interactive-option.test.mjs +114 -0
  416. package/js/tests/interactive-streaming.test.mjs +307 -0
  417. package/js/tests/issue-135-final.test.mjs +58 -0
  418. package/js/tests/jq-color-behavior.test.mjs +140 -0
  419. package/js/tests/jq.test.mjs +318 -0
  420. package/js/tests/options-examples.test.mjs +106 -0
  421. package/js/tests/options-syntax.test.mjs +112 -0
  422. package/js/tests/path-interpolation.test.mjs +412 -0
  423. package/js/tests/pipe.test.mjs +291 -0
  424. package/js/tests/raw-function.test.mjs +266 -0
  425. package/js/tests/readme-examples.test.mjs +427 -0
  426. package/js/tests/resource-cleanup-internals.test.mjs +669 -0
  427. package/js/tests/shell-settings.test.mjs +279 -0
  428. package/js/tests/sigint-cleanup-isolated.test.mjs +151 -0
  429. package/js/tests/sigint-cleanup.test.mjs +118 -0
  430. package/js/tests/start-run-edge-cases.test.mjs +152 -0
  431. package/js/tests/start-run-options.test.mjs +181 -0
  432. package/js/tests/stderr-output-handling.test.mjs +279 -0
  433. package/js/tests/streaming-interfaces.test.mjs +194 -0
  434. package/js/tests/sync.test.mjs +297 -0
  435. package/js/tests/system-pipe.test.mjs +226 -0
  436. package/js/tests/test-cleanup.mjs +200 -0
  437. package/js/tests/test-helper-fixed.mjs +148 -0
  438. package/js/tests/test-helper-v2.mjs +118 -0
  439. package/js/tests/test-helper.mjs +171 -0
  440. package/js/tests/test-sigint-child.js +15 -0
  441. package/js/tests/text-method.test.mjs +225 -0
  442. package/js/tests/virtual.test.mjs +364 -0
  443. package/js/tests/yes-command-cleanup.test.mjs +208 -0
  444. package/js/tests/zx.features.test.mjs +233 -0
  445. package/package.json +13 -12
  446. package/rust/Cargo.lock +947 -0
  447. package/rust/Cargo.toml +47 -0
  448. package/rust/src/commands/basename.rs +69 -0
  449. package/rust/src/commands/cat.rs +123 -0
  450. package/rust/src/commands/cd.rs +67 -0
  451. package/rust/src/commands/cp.rs +187 -0
  452. package/rust/src/commands/dirname.rs +57 -0
  453. package/rust/src/commands/echo.rs +73 -0
  454. package/rust/src/commands/env.rs +33 -0
  455. package/rust/src/commands/exit.rs +36 -0
  456. package/rust/src/commands/false.rs +24 -0
  457. package/rust/src/commands/ls.rs +182 -0
  458. package/rust/src/commands/mkdir.rs +98 -0
  459. package/rust/src/commands/mod.rs +200 -0
  460. package/rust/src/commands/mv.rs +180 -0
  461. package/rust/src/commands/pwd.rs +28 -0
  462. package/rust/src/commands/rm.rs +150 -0
  463. package/rust/src/commands/seq.rs +179 -0
  464. package/rust/src/commands/sleep.rs +97 -0
  465. package/rust/src/commands/test.rs +204 -0
  466. package/rust/src/commands/touch.rs +99 -0
  467. package/rust/src/commands/true.rs +24 -0
  468. package/rust/src/commands/which.rs +87 -0
  469. package/rust/src/commands/yes.rs +99 -0
  470. package/rust/src/lib.rs +492 -0
  471. package/rust/src/main.rs +37 -0
  472. package/rust/src/shell_parser.rs +565 -0
  473. package/rust/src/utils.rs +335 -0
  474. package/rust/tests/builtin_commands.rs +549 -0
  475. package/rust/tests/process_runner.rs +286 -0
  476. package/rust/tests/shell_parser.rs +296 -0
  477. package/rust/tests/utils.rs +282 -0
  478. package/rust/tests/virtual_commands.rs +199 -0
  479. /package/{src → js/src}/commands/$.basename.mjs +0 -0
  480. /package/{src → js/src}/commands/$.cat.mjs +0 -0
  481. /package/{src → js/src}/commands/$.cd.mjs +0 -0
  482. /package/{src → js/src}/commands/$.cp.mjs +0 -0
  483. /package/{src → js/src}/commands/$.dirname.mjs +0 -0
  484. /package/{src → js/src}/commands/$.echo.mjs +0 -0
  485. /package/{src → js/src}/commands/$.env.mjs +0 -0
  486. /package/{src → js/src}/commands/$.exit.mjs +0 -0
  487. /package/{src → js/src}/commands/$.false.mjs +0 -0
  488. /package/{src → js/src}/commands/$.ls.mjs +0 -0
  489. /package/{src → js/src}/commands/$.mkdir.mjs +0 -0
  490. /package/{src → js/src}/commands/$.mv.mjs +0 -0
  491. /package/{src → js/src}/commands/$.pwd.mjs +0 -0
  492. /package/{src → js/src}/commands/$.rm.mjs +0 -0
  493. /package/{src → js/src}/commands/$.seq.mjs +0 -0
  494. /package/{src → js/src}/commands/$.sleep.mjs +0 -0
  495. /package/{src → js/src}/commands/$.test.mjs +0 -0
  496. /package/{src → js/src}/commands/$.touch.mjs +0 -0
  497. /package/{src → js/src}/commands/$.true.mjs +0 -0
  498. /package/{src → js/src}/commands/$.which.mjs +0 -0
  499. /package/{src → js/src}/commands/$.yes.mjs +0 -0
  500. /package/{src → js/src}/shell-parser.mjs +0 -0
@@ -0,0 +1,622 @@
1
+ import { test, expect, describe, beforeEach, afterEach } from 'bun:test';
2
+ import {
3
+ beforeTestCleanup,
4
+ afterTestCleanup,
5
+ originalCwd,
6
+ } from './test-cleanup.mjs';
7
+ import { $, shell, enableVirtualCommands } from '../src/$.mjs';
8
+ import {
9
+ mkdtempSync,
10
+ rmSync,
11
+ mkdirSync,
12
+ writeFileSync,
13
+ existsSync,
14
+ realpathSync,
15
+ } from 'fs';
16
+ import { tmpdir, homedir } from 'os';
17
+ import { join, resolve } from 'path';
18
+
19
+ // Platform detection - Some tests use Unix-specific commands (cat, ln -s, chmod)
20
+ const isWindows = process.platform === 'win32';
21
+
22
+ // Helper to normalize paths (handles macOS /var -> /private/var symlink)
23
+ const normalizePath = (p) => {
24
+ try {
25
+ return realpathSync(p);
26
+ } catch {
27
+ return p;
28
+ }
29
+ };
30
+
31
+ // Helper function to verify we're in the expected directory
32
+ function verifyCwd(expected, message) {
33
+ const actual = normalizePath(process.cwd());
34
+ const expectedNormalized = normalizePath(expected);
35
+ if (actual !== expectedNormalized) {
36
+ throw new Error(
37
+ `${message}: Expected cwd to be ${expectedNormalized}, but got ${actual}`
38
+ );
39
+ }
40
+ }
41
+
42
+ // Skip on Windows - uses pwd, cat, ln -s, chmod commands
43
+ describe.skipIf(isWindows)('cd Virtual Command - Core Behavior', () => {
44
+ beforeEach(async () => {
45
+ await beforeTestCleanup();
46
+ shell.errexit(false);
47
+ shell.verbose(false);
48
+ shell.xtrace(false);
49
+ shell.pipefail(false);
50
+ shell.nounset(false);
51
+ enableVirtualCommands();
52
+ // Verify we start in the original directory
53
+ verifyCwd(originalCwd, 'Before test start');
54
+ });
55
+
56
+ afterEach(async () => {
57
+ await afterTestCleanup();
58
+ // Verify we restored to original directory
59
+ verifyCwd(originalCwd, 'After test cleanup');
60
+ });
61
+
62
+ test('should change to absolute path', async () => {
63
+ const tempDir = mkdtempSync(join(tmpdir(), 'cd-test-'));
64
+ const testStartCwd = process.cwd();
65
+
66
+ try {
67
+ // Verify we start in the original directory
68
+ verifyCwd(originalCwd, 'Test start');
69
+
70
+ const result = await $`cd ${tempDir}`;
71
+ expect(result.code).toBe(0);
72
+ expect(result.stdout).toBe(''); // cd should not output anything
73
+ expect(result.stderr).toBe('');
74
+
75
+ // Verify cd actually changed the directory
76
+ verifyCwd(tempDir, 'After cd to tempDir');
77
+
78
+ const pwd = await $`pwd`;
79
+ expect(normalizePath(pwd.stdout.trim())).toBe(normalizePath(tempDir));
80
+
81
+ // Go back to original
82
+ await $`cd ${testStartCwd}`;
83
+ verifyCwd(testStartCwd, 'After cd back');
84
+ } finally {
85
+ // Ensure we're back in original before cleanup
86
+ if (process.cwd() !== testStartCwd) {
87
+ process.chdir(testStartCwd);
88
+ }
89
+ rmSync(tempDir, { recursive: true, force: true });
90
+ }
91
+ });
92
+
93
+ test('should change to relative path', async () => {
94
+ const baseDir = mkdtempSync(join(tmpdir(), 'cd-base-'));
95
+ const subDir = join(baseDir, 'subdir');
96
+ mkdirSync(subDir);
97
+ const originalCwd = process.cwd();
98
+
99
+ try {
100
+ await $`cd ${baseDir}`;
101
+
102
+ const result = await $`cd subdir`;
103
+ expect(result.code).toBe(0);
104
+ expect(result.stdout).toBe('');
105
+
106
+ const pwd = await $`pwd`;
107
+ expect(normalizePath(pwd.stdout.trim())).toBe(normalizePath(subDir));
108
+
109
+ await $`cd ${originalCwd}`;
110
+ } finally {
111
+ rmSync(baseDir, { recursive: true, force: true });
112
+ }
113
+ });
114
+
115
+ test('should handle cd with no arguments (go to home)', async () => {
116
+ const originalCwd = process.cwd();
117
+
118
+ try {
119
+ const result = await $`cd`;
120
+ expect(result.code).toBe(0);
121
+ expect(result.stdout).toBe('');
122
+
123
+ const pwd = await $`pwd`;
124
+ const home = homedir();
125
+ expect(pwd.stdout.trim()).toBe(home);
126
+
127
+ await $`cd ${originalCwd}`;
128
+ } catch (error) {
129
+ await $`cd ${originalCwd}`;
130
+ throw error;
131
+ }
132
+ });
133
+
134
+ test('should handle cd - (return to previous directory)', async () => {
135
+ const dir1 = mkdtempSync(join(tmpdir(), 'cd-dir1-'));
136
+ const dir2 = mkdtempSync(join(tmpdir(), 'cd-dir2-'));
137
+ const originalCwd = process.cwd();
138
+
139
+ try {
140
+ await $`cd ${dir1}`;
141
+ const pwd1 = await $`pwd`;
142
+ expect(normalizePath(pwd1.stdout.trim())).toBe(normalizePath(dir1));
143
+
144
+ await $`cd ${dir2}`;
145
+ const pwd2 = await $`pwd`;
146
+ expect(normalizePath(pwd2.stdout.trim())).toBe(normalizePath(dir2));
147
+
148
+ // Note: cd - might not be implemented in virtual command yet
149
+ // This test documents expected behavior
150
+ const result = await $`cd - 2>&1 || echo "not implemented"`;
151
+
152
+ await $`cd ${originalCwd}`;
153
+ } finally {
154
+ rmSync(dir1, { recursive: true, force: true });
155
+ rmSync(dir2, { recursive: true, force: true });
156
+ }
157
+ });
158
+
159
+ test('should handle cd .. (parent directory)', async () => {
160
+ const baseDir = mkdtempSync(join(tmpdir(), 'cd-parent-'));
161
+ const subDir = join(baseDir, 'child');
162
+ mkdirSync(subDir);
163
+ const originalCwd = process.cwd();
164
+
165
+ try {
166
+ await $`cd ${subDir}`;
167
+
168
+ const result = await $`cd ..`;
169
+ expect(result.code).toBe(0);
170
+ expect(result.stdout).toBe('');
171
+
172
+ const pwd = await $`pwd`;
173
+ expect(normalizePath(pwd.stdout.trim())).toBe(normalizePath(baseDir));
174
+
175
+ await $`cd ${originalCwd}`;
176
+ } finally {
177
+ rmSync(baseDir, { recursive: true, force: true });
178
+ }
179
+ });
180
+
181
+ test('should handle cd . (current directory)', async () => {
182
+ const tempDir = mkdtempSync(join(tmpdir(), 'cd-dot-'));
183
+ const originalCwd = process.cwd();
184
+
185
+ try {
186
+ await $`cd ${tempDir}`;
187
+ const pwdBefore = await $`pwd`;
188
+
189
+ const result = await $`cd .`;
190
+ expect(result.code).toBe(0);
191
+ expect(result.stdout).toBe('');
192
+
193
+ const pwdAfter = await $`pwd`;
194
+ expect(pwdAfter.stdout).toBe(pwdBefore.stdout);
195
+
196
+ await $`cd ${originalCwd}`;
197
+ } finally {
198
+ rmSync(tempDir, { recursive: true, force: true });
199
+ }
200
+ });
201
+
202
+ test('should fail with non-existent directory', async () => {
203
+ const nonExistent = '/this/path/should/not/exist/at/all';
204
+ const originalCwd = process.cwd();
205
+
206
+ const result = await $`cd ${nonExistent} 2>&1 || echo "failed"`;
207
+ expect(result.stdout).toContain('failed');
208
+
209
+ // Verify we're still in the same directory
210
+ const pwd = await $`pwd`;
211
+ expect(pwd.stdout.trim()).toBe(originalCwd);
212
+ });
213
+
214
+ test('should handle paths with spaces', async () => {
215
+ const baseDir = mkdtempSync(join(tmpdir(), 'cd-spaces-'));
216
+ const dirWithSpaces = join(baseDir, 'my test directory');
217
+ mkdirSync(dirWithSpaces);
218
+ const originalCwd = process.cwd();
219
+
220
+ try {
221
+ const result = await $`cd ${dirWithSpaces}`;
222
+ expect(result.code).toBe(0);
223
+ expect(result.stdout).toBe('');
224
+
225
+ const pwd = await $`pwd`;
226
+ expect(normalizePath(pwd.stdout.trim())).toBe(
227
+ normalizePath(dirWithSpaces)
228
+ );
229
+
230
+ await $`cd ${originalCwd}`;
231
+ } finally {
232
+ rmSync(baseDir, { recursive: true, force: true });
233
+ }
234
+ });
235
+
236
+ test('should handle special characters in paths', async () => {
237
+ const baseDir = mkdtempSync(join(tmpdir(), 'cd-special-'));
238
+ // Create directory with special characters (but valid for filesystem)
239
+ const specialDir = join(baseDir, 'test-dir_123');
240
+ mkdirSync(specialDir);
241
+ const originalCwd = process.cwd();
242
+
243
+ try {
244
+ const result = await $`cd ${specialDir}`;
245
+ expect(result.code).toBe(0);
246
+ expect(result.stdout).toBe('');
247
+
248
+ const pwd = await $`pwd`;
249
+ expect(normalizePath(pwd.stdout.trim())).toBe(normalizePath(specialDir));
250
+
251
+ await $`cd ${originalCwd}`;
252
+ } finally {
253
+ rmSync(baseDir, { recursive: true, force: true });
254
+ }
255
+ });
256
+ });
257
+
258
+ // Skip on Windows - uses pwd and cat commands
259
+ describe.skipIf(isWindows)('cd Virtual Command - Command Chains', () => {
260
+ beforeEach(async () => {
261
+ await beforeTestCleanup();
262
+ shell.errexit(false);
263
+ shell.verbose(false);
264
+ shell.xtrace(false);
265
+ shell.pipefail(false);
266
+ shell.nounset(false);
267
+ enableVirtualCommands();
268
+ verifyCwd(originalCwd, 'Before test start');
269
+ });
270
+
271
+ afterEach(async () => {
272
+ await afterTestCleanup();
273
+ verifyCwd(originalCwd, 'After test cleanup');
274
+ });
275
+
276
+ test('should persist directory change within command chain', async () => {
277
+ const tempDir = mkdtempSync(join(tmpdir(), 'cd-chain-'));
278
+ const originalCwd = process.cwd();
279
+
280
+ try {
281
+ // Create a test file in temp directory
282
+ writeFileSync(join(tempDir, 'test.txt'), 'test content');
283
+
284
+ // cd and run command in same chain
285
+ const result = await $`cd ${tempDir} && cat test.txt`;
286
+ expect(result.code).toBe(0);
287
+ expect(result.stdout.trim()).toBe('test content');
288
+
289
+ await $`cd ${originalCwd}`;
290
+ } finally {
291
+ rmSync(tempDir, { recursive: true, force: true });
292
+ }
293
+ });
294
+
295
+ test('should handle multiple cd commands in chain', async () => {
296
+ const baseDir = mkdtempSync(join(tmpdir(), 'cd-multi-'));
297
+ const dir1 = join(baseDir, 'dir1');
298
+ const dir2 = join(baseDir, 'dir2');
299
+ mkdirSync(dir1);
300
+ mkdirSync(dir2);
301
+ const originalCwd = process.cwd();
302
+
303
+ try {
304
+ writeFileSync(join(dir1, 'file1.txt'), 'content1');
305
+ writeFileSync(join(dir2, 'file2.txt'), 'content2');
306
+
307
+ // Chain multiple cd commands
308
+ const result =
309
+ await $`cd ${dir1} && cat file1.txt && cd ${dir2} && cat file2.txt`;
310
+ expect(result.code).toBe(0);
311
+ expect(result.stdout).toContain('content1');
312
+ expect(result.stdout).toContain('content2');
313
+
314
+ await $`cd ${originalCwd}`;
315
+ } finally {
316
+ rmSync(baseDir, { recursive: true, force: true });
317
+ }
318
+ });
319
+
320
+ test('should work with git commands in chain', async () => {
321
+ const tempDir = mkdtempSync(join(tmpdir(), 'cd-git-'));
322
+ const originalCwd = process.cwd();
323
+
324
+ try {
325
+ const result = await $`cd ${tempDir} && git init`;
326
+ expect(result.code).toBe(0);
327
+ // Git init outputs to stderr
328
+ const output = (result.stdout + result.stderr).toLowerCase();
329
+ expect(output).toContain('initialized');
330
+
331
+ // Verify git repo was created in the right place
332
+ expect(existsSync(join(tempDir, '.git'))).toBe(true);
333
+
334
+ await $`cd ${originalCwd}`;
335
+ } finally {
336
+ rmSync(tempDir, { recursive: true, force: true });
337
+ }
338
+ });
339
+
340
+ test('should maintain separate directory context per command when not chained', async () => {
341
+ const dir1 = mkdtempSync(join(tmpdir(), 'cd-ctx1-'));
342
+ const dir2 = mkdtempSync(join(tmpdir(), 'cd-ctx2-'));
343
+ const originalCwd = process.cwd();
344
+
345
+ try {
346
+ // First command changes to dir1
347
+ await $`cd ${dir1}`;
348
+ const pwd1 = await $`pwd`;
349
+ expect(normalizePath(pwd1.stdout.trim())).toBe(normalizePath(dir1));
350
+
351
+ // Second separate command should still be in dir1
352
+ const pwd2 = await $`pwd`;
353
+ expect(normalizePath(pwd2.stdout.trim())).toBe(normalizePath(dir1));
354
+
355
+ await $`cd ${originalCwd}`;
356
+ } finally {
357
+ rmSync(dir1, { recursive: true, force: true });
358
+ rmSync(dir2, { recursive: true, force: true });
359
+ }
360
+ });
361
+ });
362
+
363
+ // Skip on Windows - uses subshells (parentheses) and pwd/cat commands
364
+ describe.skipIf(isWindows)('cd Virtual Command - Subshell Behavior', () => {
365
+ beforeEach(async () => {
366
+ await beforeTestCleanup();
367
+ shell.errexit(false);
368
+ shell.verbose(false);
369
+ shell.xtrace(false);
370
+ shell.pipefail(false);
371
+ shell.nounset(false);
372
+ enableVirtualCommands();
373
+ verifyCwd(originalCwd, 'Before test start');
374
+ });
375
+
376
+ afterEach(async () => {
377
+ await afterTestCleanup();
378
+ verifyCwd(originalCwd, 'After test cleanup');
379
+ });
380
+
381
+ test('should not affect parent shell when in subshell', async () => {
382
+ const tempDir = mkdtempSync(join(tmpdir(), 'cd-subshell-'));
383
+ const originalCwd = process.cwd();
384
+
385
+ try {
386
+ // Run cd in subshell (parentheses)
387
+ await $`(cd ${tempDir})`;
388
+
389
+ // Parent shell should still be in original directory
390
+ const pwd = await $`pwd`;
391
+ expect(pwd.stdout.trim()).toBe(originalCwd);
392
+ } finally {
393
+ rmSync(tempDir, { recursive: true, force: true });
394
+ }
395
+ });
396
+
397
+ test('should work in subshell with other commands', async () => {
398
+ const tempDir = mkdtempSync(join(tmpdir(), 'cd-subshell-cmd-'));
399
+ const originalCwd = process.cwd();
400
+
401
+ try {
402
+ writeFileSync(join(tempDir, 'test.txt'), 'subshell test');
403
+
404
+ // Run commands in subshell
405
+ const result = await $`(cd ${tempDir} && cat test.txt)`;
406
+ expect(result.code).toBe(0);
407
+ expect(result.stdout.trim()).toBe('subshell test');
408
+
409
+ // Verify we're still in original directory
410
+ const pwd = await $`pwd`;
411
+ expect(pwd.stdout.trim()).toBe(originalCwd);
412
+ } finally {
413
+ rmSync(tempDir, { recursive: true, force: true });
414
+ }
415
+ });
416
+ });
417
+
418
+ // Skip on Windows - uses ln -s, chmod, and pwd commands
419
+ describe.skipIf(isWindows)('cd Virtual Command - Edge Cases', () => {
420
+ beforeEach(async () => {
421
+ await beforeTestCleanup();
422
+ shell.errexit(false);
423
+ shell.verbose(false);
424
+ shell.xtrace(false);
425
+ shell.pipefail(false);
426
+ shell.nounset(false);
427
+ enableVirtualCommands();
428
+ verifyCwd(originalCwd, 'Before test start');
429
+ });
430
+
431
+ afterEach(async () => {
432
+ await afterTestCleanup();
433
+ verifyCwd(originalCwd, 'After test cleanup');
434
+ });
435
+
436
+ test('should handle symlinks', async () => {
437
+ const baseDir = mkdtempSync(join(tmpdir(), 'cd-symlink-'));
438
+ const realDir = join(baseDir, 'real');
439
+ const linkDir = join(baseDir, 'link');
440
+ mkdirSync(realDir);
441
+ const originalCwd = process.cwd();
442
+
443
+ try {
444
+ // Create symlink
445
+ await $`ln -s ${realDir} ${linkDir}`;
446
+
447
+ // cd through symlink
448
+ const result = await $`cd ${linkDir}`;
449
+ expect(result.code).toBe(0);
450
+
451
+ // pwd should show the symlink path (default behavior)
452
+ const pwd = await $`pwd`;
453
+ // Note: behavior may vary between pwd and pwd -P
454
+ expect(pwd.stdout.trim()).toBeTruthy();
455
+
456
+ await $`cd ${originalCwd}`;
457
+ } finally {
458
+ rmSync(baseDir, { recursive: true, force: true });
459
+ }
460
+ });
461
+
462
+ test('should handle very long paths', async () => {
463
+ const baseDir = mkdtempSync(join(tmpdir(), 'cd-long-'));
464
+ const originalCwd = process.cwd();
465
+
466
+ try {
467
+ // Create deeply nested directory
468
+ let currentPath = baseDir;
469
+ for (let i = 0; i < 10; i++) {
470
+ currentPath = join(currentPath, `level${i}`);
471
+ mkdirSync(currentPath);
472
+ }
473
+
474
+ const result = await $`cd ${currentPath}`;
475
+ expect(result.code).toBe(0);
476
+
477
+ const pwd = await $`pwd`;
478
+ expect(normalizePath(pwd.stdout.trim())).toBe(normalizePath(currentPath));
479
+
480
+ await $`cd ${originalCwd}`;
481
+ } finally {
482
+ rmSync(baseDir, { recursive: true, force: true });
483
+ }
484
+ });
485
+
486
+ test('should handle permission errors gracefully', async () => {
487
+ // Skip on Windows where permissions work differently
488
+ if (process.platform === 'win32') {
489
+ return;
490
+ }
491
+
492
+ const baseDir = mkdtempSync(join(tmpdir(), 'cd-perm-'));
493
+ const restrictedDir = join(baseDir, 'restricted');
494
+ mkdirSync(restrictedDir);
495
+ const originalCwd = process.cwd();
496
+
497
+ try {
498
+ // Remove execute permission
499
+ await $`chmod 000 ${restrictedDir}`;
500
+
501
+ const result =
502
+ await $`cd ${restrictedDir} 2>&1 || echo "permission denied"`;
503
+ expect(result.stdout.toLowerCase()).toContain('denied');
504
+
505
+ // Should still be in original directory
506
+ const pwd = await $`pwd`;
507
+ expect(pwd.stdout.trim()).toBe(originalCwd);
508
+ } finally {
509
+ // Restore permissions for cleanup - need to restore parent dir permission first
510
+ try {
511
+ await $`chmod 755 ${baseDir} 2>/dev/null || true`;
512
+ await $`chmod 755 ${restrictedDir} 2>/dev/null || true`;
513
+ rmSync(baseDir, { recursive: true, force: true });
514
+ } catch (e) {
515
+ // If cleanup fails, try with sudo as last resort
516
+ await $`sudo rm -rf ${baseDir} 2>/dev/null || true`.catch(() => {});
517
+ }
518
+ }
519
+ });
520
+
521
+ test('should handle cd with trailing slash', async () => {
522
+ const tempDir = mkdtempSync(join(tmpdir(), 'cd-slash-'));
523
+ const originalCwd = process.cwd();
524
+
525
+ try {
526
+ // Test with trailing slash
527
+ const result = await $`cd ${tempDir}/`;
528
+ expect(result.code).toBe(0);
529
+ expect(result.stdout).toBe('');
530
+
531
+ const pwd = await $`pwd`;
532
+ expect(normalizePath(pwd.stdout.trim())).toBe(normalizePath(tempDir));
533
+
534
+ await $`cd ${originalCwd}`;
535
+ } finally {
536
+ rmSync(tempDir, { recursive: true, force: true });
537
+ }
538
+ });
539
+
540
+ test('should handle cd with multiple slashes', async () => {
541
+ const baseDir = mkdtempSync(join(tmpdir(), 'cd-slashes-'));
542
+ const subDir = join(baseDir, 'sub');
543
+ mkdirSync(subDir);
544
+ const originalCwd = process.cwd();
545
+
546
+ try {
547
+ // Test with multiple slashes (should normalize)
548
+ const result = await $`cd ${baseDir}//sub///`;
549
+ expect(result.code).toBe(0);
550
+
551
+ const pwd = await $`pwd`;
552
+ expect(normalizePath(pwd.stdout.trim())).toBe(normalizePath(subDir));
553
+
554
+ await $`cd ${originalCwd}`;
555
+ } finally {
556
+ rmSync(baseDir, { recursive: true, force: true });
557
+ }
558
+ });
559
+ });
560
+
561
+ // Skip on Windows - uses pwd command
562
+ describe.skipIf(isWindows)(
563
+ 'cd Virtual Command - Platform Compatibility',
564
+ () => {
565
+ beforeEach(async () => {
566
+ await beforeTestCleanup();
567
+ shell.errexit(false);
568
+ shell.verbose(false);
569
+ shell.xtrace(false);
570
+ shell.pipefail(false);
571
+ shell.nounset(false);
572
+ enableVirtualCommands();
573
+ verifyCwd(originalCwd, 'Before test start');
574
+ });
575
+
576
+ afterEach(async () => {
577
+ await afterTestCleanup();
578
+ verifyCwd(originalCwd, 'After test cleanup');
579
+ });
580
+
581
+ test('should handle platform-specific path separators', async () => {
582
+ const baseDir = mkdtempSync(join(tmpdir(), 'cd-platform-'));
583
+ const subDir = join(baseDir, 'cross', 'platform', 'test');
584
+ mkdirSync(subDir, { recursive: true });
585
+ const originalCwd = process.cwd();
586
+
587
+ try {
588
+ // Use platform-specific path
589
+ const result = await $`cd ${subDir}`;
590
+ expect(result.code).toBe(0);
591
+
592
+ const pwd = await $`pwd`;
593
+ expect(normalizePath(pwd.stdout.trim())).toBe(normalizePath(subDir));
594
+
595
+ await $`cd ${originalCwd}`;
596
+ } finally {
597
+ rmSync(baseDir, { recursive: true, force: true });
598
+ }
599
+ });
600
+
601
+ test('should normalize paths correctly', async () => {
602
+ const baseDir = mkdtempSync(join(tmpdir(), 'cd-normalize-'));
603
+ const sub1 = join(baseDir, 'sub1');
604
+ const sub2 = join(sub1, 'sub2');
605
+ mkdirSync(sub2, { recursive: true });
606
+ const originalCwd = process.cwd();
607
+
608
+ try {
609
+ // Test path with ./ and ../
610
+ await $`cd ${baseDir}`;
611
+ await $`cd ./sub1/../sub1/sub2`;
612
+
613
+ const pwd = await $`pwd`;
614
+ expect(normalizePath(pwd.stdout.trim())).toBe(normalizePath(sub2));
615
+
616
+ await $`cd ${originalCwd}`;
617
+ } finally {
618
+ rmSync(baseDir, { recursive: true, force: true });
619
+ }
620
+ });
621
+ }
622
+ );