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,24 @@
1
+ //! Virtual `false` command implementation
2
+
3
+ use crate::commands::CommandContext;
4
+ use crate::utils::CommandResult;
5
+
6
+ /// Execute the false command
7
+ ///
8
+ /// Always returns failure (exit code 1).
9
+ pub async fn r#false(_ctx: CommandContext) -> CommandResult {
10
+ CommandResult::error_with_code("", 1)
11
+ }
12
+
13
+ #[cfg(test)]
14
+ mod tests {
15
+ use super::*;
16
+
17
+ #[tokio::test]
18
+ async fn test_false() {
19
+ let ctx = CommandContext::new(vec![]);
20
+ let result = r#false(ctx).await;
21
+ assert!(!result.is_success());
22
+ assert_eq!(result.code, 1);
23
+ }
24
+ }
@@ -0,0 +1,182 @@
1
+ //! Virtual `ls` command implementation
2
+
3
+ use crate::commands::CommandContext;
4
+ use crate::utils::{trace_lazy, CommandResult};
5
+ use std::fs;
6
+ use std::path::Path;
7
+
8
+ /// Execute the ls command
9
+ ///
10
+ /// Lists directory contents.
11
+ pub async fn ls(ctx: CommandContext) -> CommandResult {
12
+ // Parse flags
13
+ let mut show_all = false;
14
+ let mut long_format = false;
15
+ let mut paths = Vec::new();
16
+
17
+ for arg in &ctx.args {
18
+ if arg == "-a" || arg == "--all" {
19
+ show_all = true;
20
+ } else if arg == "-l" {
21
+ long_format = true;
22
+ } else if arg == "-la" || arg == "-al" {
23
+ show_all = true;
24
+ long_format = true;
25
+ } else if arg.starts_with('-') {
26
+ if arg.contains('a') {
27
+ show_all = true;
28
+ }
29
+ if arg.contains('l') {
30
+ long_format = true;
31
+ }
32
+ } else {
33
+ paths.push(arg.clone());
34
+ }
35
+ }
36
+
37
+ // Default to current directory
38
+ if paths.is_empty() {
39
+ paths.push(".".to_string());
40
+ }
41
+
42
+ let cwd = ctx.get_cwd();
43
+ let mut outputs = Vec::new();
44
+
45
+ for path_str in paths {
46
+ let resolved_path = if Path::new(&path_str).is_absolute() {
47
+ Path::new(&path_str).to_path_buf()
48
+ } else {
49
+ cwd.join(&path_str)
50
+ };
51
+
52
+ trace_lazy("VirtualCommand", || {
53
+ format!("ls: listing {:?}", resolved_path)
54
+ });
55
+
56
+ if !resolved_path.exists() {
57
+ return CommandResult::error(format!(
58
+ "ls: cannot access '{}': No such file or directory\n",
59
+ path_str
60
+ ));
61
+ }
62
+
63
+ if resolved_path.is_file() {
64
+ outputs.push(format_entry(&resolved_path, long_format));
65
+ } else {
66
+ match fs::read_dir(&resolved_path) {
67
+ Ok(entries) => {
68
+ let mut entry_strs = Vec::new();
69
+
70
+ for entry in entries {
71
+ if let Ok(entry) = entry {
72
+ let name = entry.file_name().to_string_lossy().to_string();
73
+
74
+ // Skip hidden files unless -a is specified
75
+ if !show_all && name.starts_with('.') {
76
+ continue;
77
+ }
78
+
79
+ if long_format {
80
+ entry_strs.push(format_entry(&entry.path(), true));
81
+ } else {
82
+ entry_strs.push(name);
83
+ }
84
+ }
85
+ }
86
+
87
+ entry_strs.sort();
88
+ outputs.push(entry_strs.join("\n"));
89
+ }
90
+ Err(e) => {
91
+ return CommandResult::error(format!("ls: cannot open '{}': {}\n", path_str, e));
92
+ }
93
+ }
94
+ }
95
+ }
96
+
97
+ let output = outputs.join("\n");
98
+ if output.is_empty() {
99
+ CommandResult::success_empty()
100
+ } else {
101
+ CommandResult::success(format!("{}\n", output))
102
+ }
103
+ }
104
+
105
+ fn format_entry(path: &Path, long_format: bool) -> String {
106
+ let name = path.file_name()
107
+ .map(|n| n.to_string_lossy().to_string())
108
+ .unwrap_or_else(|| path.display().to_string());
109
+
110
+ if !long_format {
111
+ return name;
112
+ }
113
+
114
+ // Long format: permissions, links, owner, group, size, date, name
115
+ let metadata = match fs::metadata(path) {
116
+ Ok(m) => m,
117
+ Err(_) => return name,
118
+ };
119
+
120
+ let file_type = if metadata.is_dir() { "d" } else { "-" };
121
+ let size = metadata.len();
122
+
123
+ // Simplified permissions
124
+ let perms = if metadata.is_dir() {
125
+ "drwxr-xr-x"
126
+ } else {
127
+ "-rw-r--r--"
128
+ };
129
+
130
+ format!("{} {:>8} {}", perms, size, name)
131
+ }
132
+
133
+ #[cfg(test)]
134
+ mod tests {
135
+ use super::*;
136
+ use tempfile::tempdir;
137
+
138
+ #[tokio::test]
139
+ async fn test_ls_current_dir() {
140
+ let ctx = CommandContext::new(vec![]);
141
+ let result = ls(ctx).await;
142
+ assert!(result.is_success());
143
+ }
144
+
145
+ #[tokio::test]
146
+ async fn test_ls_with_path() {
147
+ let temp = tempdir().unwrap();
148
+ fs::write(temp.path().join("file.txt"), "test").unwrap();
149
+
150
+ let ctx = CommandContext::new(vec![
151
+ temp.path().to_string_lossy().to_string()
152
+ ]);
153
+ let result = ls(ctx).await;
154
+
155
+ assert!(result.is_success());
156
+ assert!(result.stdout.contains("file.txt"));
157
+ }
158
+
159
+ #[tokio::test]
160
+ async fn test_ls_hidden_files() {
161
+ let temp = tempdir().unwrap();
162
+ fs::write(temp.path().join(".hidden"), "test").unwrap();
163
+ fs::write(temp.path().join("visible"), "test").unwrap();
164
+
165
+ // Without -a
166
+ let ctx = CommandContext::new(vec![
167
+ temp.path().to_string_lossy().to_string()
168
+ ]);
169
+ let result = ls(ctx).await;
170
+ assert!(!result.stdout.contains(".hidden"));
171
+ assert!(result.stdout.contains("visible"));
172
+
173
+ // With -a
174
+ let ctx = CommandContext::new(vec![
175
+ "-a".to_string(),
176
+ temp.path().to_string_lossy().to_string(),
177
+ ]);
178
+ let result = ls(ctx).await;
179
+ assert!(result.stdout.contains(".hidden"));
180
+ assert!(result.stdout.contains("visible"));
181
+ }
182
+ }
@@ -0,0 +1,98 @@
1
+ //! Virtual `mkdir` command implementation
2
+
3
+ use crate::commands::CommandContext;
4
+ use crate::utils::{trace_lazy, CommandResult, VirtualUtils};
5
+ use std::fs;
6
+
7
+ /// Execute the mkdir command
8
+ ///
9
+ /// Creates directories.
10
+ pub async fn mkdir(ctx: CommandContext) -> CommandResult {
11
+ if ctx.args.is_empty() {
12
+ return VirtualUtils::missing_operand_error("mkdir");
13
+ }
14
+
15
+ // Check for -p flag
16
+ let mut create_parents = false;
17
+ let mut dirs = Vec::new();
18
+
19
+ for arg in &ctx.args {
20
+ if arg == "-p" {
21
+ create_parents = true;
22
+ } else if arg.starts_with('-') {
23
+ // Skip other flags
24
+ } else {
25
+ dirs.push(arg.clone());
26
+ }
27
+ }
28
+
29
+ if dirs.is_empty() {
30
+ return VirtualUtils::missing_operand_error("mkdir");
31
+ }
32
+
33
+ let cwd = ctx.get_cwd();
34
+
35
+ for dir in dirs {
36
+ let resolved_path = VirtualUtils::resolve_path(&dir, Some(&cwd));
37
+
38
+ trace_lazy("VirtualCommand", || {
39
+ format!("mkdir: creating directory {:?}, parents: {}", resolved_path, create_parents)
40
+ });
41
+
42
+ let result = if create_parents {
43
+ fs::create_dir_all(&resolved_path)
44
+ } else {
45
+ fs::create_dir(&resolved_path)
46
+ };
47
+
48
+ if let Err(e) = result {
49
+ return CommandResult::error(format!("mkdir: cannot create directory '{}': {}\n", dir, e));
50
+ }
51
+ }
52
+
53
+ CommandResult::success_empty()
54
+ }
55
+
56
+ #[cfg(test)]
57
+ mod tests {
58
+ use super::*;
59
+ use tempfile::tempdir;
60
+
61
+ #[tokio::test]
62
+ async fn test_mkdir_simple() {
63
+ let temp = tempdir().unwrap();
64
+ let new_dir = temp.path().join("new_directory");
65
+
66
+ let ctx = CommandContext::new(vec![
67
+ new_dir.to_string_lossy().to_string()
68
+ ]);
69
+ let result = mkdir(ctx).await;
70
+
71
+ assert!(result.is_success());
72
+ assert!(new_dir.exists());
73
+ }
74
+
75
+ #[tokio::test]
76
+ async fn test_mkdir_with_parents() {
77
+ let temp = tempdir().unwrap();
78
+ let nested_dir = temp.path().join("a/b/c/d");
79
+
80
+ let ctx = CommandContext::new(vec![
81
+ "-p".to_string(),
82
+ nested_dir.to_string_lossy().to_string(),
83
+ ]);
84
+ let result = mkdir(ctx).await;
85
+
86
+ assert!(result.is_success());
87
+ assert!(nested_dir.exists());
88
+ }
89
+
90
+ #[tokio::test]
91
+ async fn test_mkdir_missing_operand() {
92
+ let ctx = CommandContext::new(vec![]);
93
+ let result = mkdir(ctx).await;
94
+
95
+ assert!(!result.is_success());
96
+ assert!(result.stderr.contains("missing operand"));
97
+ }
98
+ }
@@ -0,0 +1,200 @@
1
+ //! Virtual command implementations
2
+ //!
3
+ //! This module contains implementations of shell commands that run in-process
4
+ //! without spawning external processes. These provide faster execution and
5
+ //! consistent behavior across platforms.
6
+
7
+ mod cat;
8
+ mod cd;
9
+ mod echo;
10
+ mod pwd;
11
+ mod sleep;
12
+ mod r#true;
13
+ mod r#false;
14
+ mod mkdir;
15
+ mod rm;
16
+ mod touch;
17
+ mod ls;
18
+ mod cp;
19
+ mod mv;
20
+ mod basename;
21
+ mod dirname;
22
+ mod env;
23
+ mod exit;
24
+ mod which;
25
+ mod yes;
26
+ mod seq;
27
+ mod test;
28
+
29
+ pub use cat::cat;
30
+ pub use cd::cd;
31
+ pub use echo::echo;
32
+ pub use pwd::pwd;
33
+ pub use sleep::sleep;
34
+ pub use r#true::r#true;
35
+ pub use r#false::r#false;
36
+ pub use mkdir::mkdir;
37
+ pub use rm::rm;
38
+ pub use touch::touch;
39
+ pub use ls::ls;
40
+ pub use cp::cp;
41
+ pub use mv::mv;
42
+ pub use basename::basename;
43
+ pub use dirname::dirname;
44
+ pub use env::env;
45
+ pub use exit::exit;
46
+ pub use which::which;
47
+ pub use yes::yes;
48
+ pub use seq::seq;
49
+ pub use test::test;
50
+
51
+ use crate::utils::CommandResult;
52
+ use std::collections::HashMap;
53
+ use std::path::Path;
54
+ use tokio::sync::mpsc;
55
+
56
+ /// Context for virtual command execution
57
+ pub struct CommandContext {
58
+ /// Command arguments (excluding the command name)
59
+ pub args: Vec<String>,
60
+ /// Standard input content
61
+ pub stdin: Option<String>,
62
+ /// Current working directory
63
+ pub cwd: Option<std::path::PathBuf>,
64
+ /// Environment variables
65
+ pub env: Option<HashMap<String, String>>,
66
+ /// Channel to send streaming output
67
+ pub output_tx: Option<mpsc::Sender<StreamChunk>>,
68
+ /// Cancellation check function
69
+ pub is_cancelled: Option<Box<dyn Fn() -> bool + Send + Sync>>,
70
+ }
71
+
72
+ impl std::fmt::Debug for CommandContext {
73
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74
+ f.debug_struct("CommandContext")
75
+ .field("args", &self.args)
76
+ .field("stdin", &self.stdin)
77
+ .field("cwd", &self.cwd)
78
+ .field("env", &self.env)
79
+ .field("output_tx", &self.output_tx.is_some())
80
+ .field("is_cancelled", &self.is_cancelled.is_some())
81
+ .finish()
82
+ }
83
+ }
84
+
85
+ /// A chunk of streaming output
86
+ #[derive(Debug, Clone)]
87
+ pub enum StreamChunk {
88
+ Stdout(String),
89
+ Stderr(String),
90
+ }
91
+
92
+ impl CommandContext {
93
+ /// Create a new command context with arguments
94
+ pub fn new(args: Vec<String>) -> Self {
95
+ CommandContext {
96
+ args,
97
+ stdin: None,
98
+ cwd: None,
99
+ env: None,
100
+ output_tx: None,
101
+ is_cancelled: None,
102
+ }
103
+ }
104
+
105
+ /// Check if the command has been cancelled
106
+ pub fn is_cancelled(&self) -> bool {
107
+ self.is_cancelled
108
+ .as_ref()
109
+ .map(|f| f())
110
+ .unwrap_or(false)
111
+ }
112
+
113
+ /// Get the current working directory
114
+ pub fn get_cwd(&self) -> std::path::PathBuf {
115
+ self.cwd.clone().unwrap_or_else(|| {
116
+ std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from("/"))
117
+ })
118
+ }
119
+ }
120
+
121
+ /// Type for virtual command handler functions
122
+ pub type VirtualCommandHandler = fn(CommandContext) -> std::pin::Pin<Box<dyn std::future::Future<Output = CommandResult> + Send>>;
123
+
124
+ /// Registry of virtual commands
125
+ pub struct VirtualCommandRegistry {
126
+ commands: HashMap<String, VirtualCommandHandler>,
127
+ }
128
+
129
+ impl Default for VirtualCommandRegistry {
130
+ fn default() -> Self {
131
+ Self::new()
132
+ }
133
+ }
134
+
135
+ impl VirtualCommandRegistry {
136
+ /// Create a new empty registry
137
+ pub fn new() -> Self {
138
+ VirtualCommandRegistry {
139
+ commands: HashMap::new(),
140
+ }
141
+ }
142
+
143
+ /// Create a registry with all built-in commands registered
144
+ pub fn with_builtins() -> Self {
145
+ let mut registry = Self::new();
146
+ registry.register_builtins();
147
+ registry
148
+ }
149
+
150
+ /// Register a virtual command
151
+ pub fn register(&mut self, name: &str, handler: VirtualCommandHandler) {
152
+ self.commands.insert(name.to_string(), handler);
153
+ }
154
+
155
+ /// Unregister a virtual command
156
+ pub fn unregister(&mut self, name: &str) -> bool {
157
+ self.commands.remove(name).is_some()
158
+ }
159
+
160
+ /// Get a virtual command handler
161
+ pub fn get(&self, name: &str) -> Option<&VirtualCommandHandler> {
162
+ self.commands.get(name)
163
+ }
164
+
165
+ /// Check if a command is registered
166
+ pub fn contains(&self, name: &str) -> bool {
167
+ self.commands.contains_key(name)
168
+ }
169
+
170
+ /// List all registered command names
171
+ pub fn list(&self) -> Vec<&str> {
172
+ self.commands.keys().map(|s| s.as_str()).collect()
173
+ }
174
+
175
+ /// Register all built-in commands
176
+ pub fn register_builtins(&mut self) {
177
+ // Note: These are placeholder registrations - actual async handlers
178
+ // would need proper wrapper functions
179
+ // The actual commands are available as standalone functions
180
+ }
181
+ }
182
+
183
+ /// Global virtual commands enabled flag
184
+ static VIRTUAL_COMMANDS_ENABLED: std::sync::atomic::AtomicBool =
185
+ std::sync::atomic::AtomicBool::new(true);
186
+
187
+ /// Enable virtual commands
188
+ pub fn enable_virtual_commands() {
189
+ VIRTUAL_COMMANDS_ENABLED.store(true, std::sync::atomic::Ordering::SeqCst);
190
+ }
191
+
192
+ /// Disable virtual commands
193
+ pub fn disable_virtual_commands() {
194
+ VIRTUAL_COMMANDS_ENABLED.store(false, std::sync::atomic::Ordering::SeqCst);
195
+ }
196
+
197
+ /// Check if virtual commands are enabled
198
+ pub fn are_virtual_commands_enabled() -> bool {
199
+ VIRTUAL_COMMANDS_ENABLED.load(std::sync::atomic::Ordering::SeqCst)
200
+ }
@@ -0,0 +1,180 @@
1
+ //! Virtual `mv` command implementation
2
+
3
+ use crate::commands::CommandContext;
4
+ use crate::utils::{trace_lazy, CommandResult, VirtualUtils};
5
+ use std::fs;
6
+
7
+ /// Execute the mv command
8
+ ///
9
+ /// Moves (renames) files and directories.
10
+ pub async fn mv(ctx: CommandContext) -> CommandResult {
11
+ if ctx.args.len() < 2 {
12
+ return VirtualUtils::invalid_argument_error("mv", "missing file operand");
13
+ }
14
+
15
+ // Parse flags (currently just skip them)
16
+ let mut paths = Vec::new();
17
+
18
+ for arg in &ctx.args {
19
+ if !arg.starts_with('-') {
20
+ paths.push(arg.clone());
21
+ }
22
+ }
23
+
24
+ if paths.len() < 2 {
25
+ return VirtualUtils::invalid_argument_error("mv", "missing destination file operand");
26
+ }
27
+
28
+ let cwd = ctx.get_cwd();
29
+ let dest = paths.pop().unwrap();
30
+ let dest_path = VirtualUtils::resolve_path(&dest, Some(&cwd));
31
+
32
+ // If multiple sources or dest is a directory, move into the directory
33
+ let dest_is_dir = dest_path.is_dir();
34
+ let multiple_sources = paths.len() > 1;
35
+
36
+ if multiple_sources && !dest_is_dir {
37
+ return CommandResult::error(format!(
38
+ "mv: target '{}' is not a directory\n",
39
+ dest
40
+ ));
41
+ }
42
+
43
+ for source in paths {
44
+ let source_path = VirtualUtils::resolve_path(&source, Some(&cwd));
45
+
46
+ trace_lazy("VirtualCommand", || {
47
+ format!("mv: moving {:?} to {:?}", source_path, dest_path)
48
+ });
49
+
50
+ if !source_path.exists() {
51
+ return CommandResult::error(format!(
52
+ "mv: cannot stat '{}': No such file or directory\n",
53
+ source
54
+ ));
55
+ }
56
+
57
+ let final_dest = if dest_is_dir {
58
+ dest_path.join(source_path.file_name().unwrap_or_default())
59
+ } else {
60
+ dest_path.clone()
61
+ };
62
+
63
+ // Try rename first (fastest if on same filesystem)
64
+ match fs::rename(&source_path, &final_dest) {
65
+ Ok(()) => continue,
66
+ Err(e) => {
67
+ // If rename fails (e.g., cross-filesystem), try copy + delete
68
+ if e.kind() == std::io::ErrorKind::CrossesDevices
69
+ || e.kind() == std::io::ErrorKind::Other
70
+ {
71
+ if source_path.is_dir() {
72
+ if let Err(e) = copy_and_remove_dir(&source_path, &final_dest) {
73
+ return CommandResult::error(format!(
74
+ "mv: cannot move '{}': {}\n",
75
+ source, e
76
+ ));
77
+ }
78
+ } else {
79
+ if let Err(e) = fs::copy(&source_path, &final_dest) {
80
+ return CommandResult::error(format!(
81
+ "mv: cannot move '{}': {}\n",
82
+ source, e
83
+ ));
84
+ }
85
+ if let Err(e) = fs::remove_file(&source_path) {
86
+ return CommandResult::error(format!(
87
+ "mv: cannot remove '{}': {}\n",
88
+ source, e
89
+ ));
90
+ }
91
+ }
92
+ } else {
93
+ return CommandResult::error(format!(
94
+ "mv: cannot move '{}': {}\n",
95
+ source, e
96
+ ));
97
+ }
98
+ }
99
+ }
100
+ }
101
+
102
+ CommandResult::success_empty()
103
+ }
104
+
105
+ fn copy_and_remove_dir(src: &std::path::Path, dst: &std::path::Path) -> std::io::Result<()> {
106
+ fs::create_dir_all(dst)?;
107
+
108
+ for entry in fs::read_dir(src)? {
109
+ let entry = entry?;
110
+ let entry_path = entry.path();
111
+ let dest_path = dst.join(entry.file_name());
112
+
113
+ if entry_path.is_dir() {
114
+ copy_and_remove_dir(&entry_path, &dest_path)?;
115
+ } else {
116
+ fs::copy(&entry_path, &dest_path)?;
117
+ }
118
+ }
119
+
120
+ fs::remove_dir_all(src)?;
121
+ Ok(())
122
+ }
123
+
124
+ #[cfg(test)]
125
+ mod tests {
126
+ use super::*;
127
+ use tempfile::tempdir;
128
+
129
+ #[tokio::test]
130
+ async fn test_mv_file() {
131
+ let temp = tempdir().unwrap();
132
+ let src = temp.path().join("source.txt");
133
+ let dst = temp.path().join("dest.txt");
134
+ fs::write(&src, "test content").unwrap();
135
+
136
+ let ctx = CommandContext::new(vec![
137
+ src.to_string_lossy().to_string(),
138
+ dst.to_string_lossy().to_string(),
139
+ ]);
140
+ let result = mv(ctx).await;
141
+
142
+ assert!(result.is_success());
143
+ assert!(!src.exists());
144
+ assert!(dst.exists());
145
+ assert_eq!(fs::read_to_string(&dst).unwrap(), "test content");
146
+ }
147
+
148
+ #[tokio::test]
149
+ async fn test_mv_directory() {
150
+ let temp = tempdir().unwrap();
151
+ let src_dir = temp.path().join("src_dir");
152
+ let dst_dir = temp.path().join("dst_dir");
153
+
154
+ fs::create_dir(&src_dir).unwrap();
155
+ fs::write(src_dir.join("file.txt"), "test").unwrap();
156
+
157
+ let ctx = CommandContext::new(vec![
158
+ src_dir.to_string_lossy().to_string(),
159
+ dst_dir.to_string_lossy().to_string(),
160
+ ]);
161
+ let result = mv(ctx).await;
162
+
163
+ assert!(result.is_success());
164
+ assert!(!src_dir.exists());
165
+ assert!(dst_dir.join("file.txt").exists());
166
+ }
167
+
168
+ #[tokio::test]
169
+ async fn test_mv_nonexistent() {
170
+ let temp = tempdir().unwrap();
171
+
172
+ let ctx = CommandContext::new(vec![
173
+ "/nonexistent/file".to_string(),
174
+ temp.path().join("dest").to_string_lossy().to_string(),
175
+ ]);
176
+ let result = mv(ctx).await;
177
+
178
+ assert!(!result.is_success());
179
+ }
180
+ }
@@ -0,0 +1,28 @@
1
+ //! Virtual `pwd` command implementation
2
+
3
+ use crate::commands::CommandContext;
4
+ use crate::utils::CommandResult;
5
+ use std::env;
6
+
7
+ /// Execute the pwd command
8
+ ///
9
+ /// Prints the current working directory.
10
+ pub async fn pwd(_ctx: CommandContext) -> CommandResult {
11
+ match env::current_dir() {
12
+ Ok(path) => CommandResult::success(format!("{}\n", path.display())),
13
+ Err(e) => CommandResult::error(format!("pwd: {}\n", e)),
14
+ }
15
+ }
16
+
17
+ #[cfg(test)]
18
+ mod tests {
19
+ use super::*;
20
+
21
+ #[tokio::test]
22
+ async fn test_pwd() {
23
+ let ctx = CommandContext::new(vec![]);
24
+ let result = pwd(ctx).await;
25
+ assert!(result.is_success());
26
+ assert!(!result.stdout.is_empty());
27
+ }
28
+ }