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,221 @@
1
+ import {
2
+ test,
3
+ expect,
4
+ describe,
5
+ beforeAll,
6
+ afterAll,
7
+ beforeEach,
8
+ afterEach,
9
+ } from 'bun:test';
10
+ import { beforeTestCleanup, afterTestCleanup } from './test-cleanup.mjs';
11
+ import { $ } from '../src/$.mjs';
12
+ import { promises as fs } from 'fs';
13
+ import path from 'path';
14
+ import os from 'os';
15
+
16
+ describe('GitHub Gist Operations with $.mjs', () => {
17
+ let isAuthenticated = false;
18
+ let testGistId = null;
19
+ const TEST_DESC = 'test-command-stream-gist';
20
+ const CI_ENVIRONMENT =
21
+ process.env.CI === 'true' || process.env.GITHUB_ACTIONS === 'true';
22
+
23
+ beforeEach(async () => {
24
+ await beforeTestCleanup();
25
+ });
26
+
27
+ afterEach(async () => {
28
+ await afterTestCleanup();
29
+ });
30
+
31
+ beforeAll(async () => {
32
+ // Skip write operations in CI environment
33
+ if (CI_ENVIRONMENT) {
34
+ console.log('Skipping gist write tests in CI environment');
35
+ return;
36
+ }
37
+
38
+ // Check if authenticated
39
+ const authResult = await $`gh auth status 2>&1`.run({
40
+ capture: true,
41
+ mirror: false,
42
+ });
43
+ isAuthenticated = authResult.code === 0;
44
+
45
+ if (!isAuthenticated) {
46
+ console.log('Skipping gist tests - GitHub CLI not authenticated');
47
+ }
48
+ });
49
+
50
+ afterAll(async () => {
51
+ // Clean up any test gists
52
+ if (isAuthenticated && testGistId) {
53
+ try {
54
+ await $`gh gist delete ${testGistId} --yes`.run({
55
+ capture: true,
56
+ mirror: false,
57
+ });
58
+ } catch (error) {
59
+ // Ignore cleanup errors
60
+ }
61
+ }
62
+ });
63
+
64
+ test('gh gist create should work with $.mjs and not hang', async () => {
65
+ if (CI_ENVIRONMENT || !isAuthenticated) {
66
+ console.log('Skipped: CI environment or not authenticated');
67
+ return;
68
+ }
69
+
70
+ // Create a temporary file
71
+ const tempFile = path.join(os.tmpdir(), 'test-gist-file.txt');
72
+ await fs.writeFile(tempFile, 'Test content for gist\n');
73
+
74
+ try {
75
+ // Create gist with timeout to prevent hanging
76
+ const result =
77
+ await $`gh gist create ${tempFile} --desc "${TEST_DESC}" --public=false 2>&1`.run(
78
+ {
79
+ capture: true,
80
+ mirror: false,
81
+ timeout: 10000, // 10 second timeout
82
+ }
83
+ );
84
+
85
+ // Should complete successfully
86
+ expect(result.code).toBe(0);
87
+ expect(result.stdout).toBeDefined();
88
+ expect(result.stdout).toContain('gist.github.com');
89
+
90
+ // Extract gist ID for cleanup
91
+ const lines = result.stdout.trim().split('\n');
92
+ const gistUrl = lines.find((line) => line.includes('gist.github.com'));
93
+ if (gistUrl) {
94
+ testGistId = gistUrl.split('/').pop();
95
+ expect(testGistId).toBeTruthy();
96
+ expect(testGistId.length).toBeGreaterThan(0);
97
+ }
98
+ } finally {
99
+ // Clean up temp file
100
+ await fs.unlink(tempFile).catch(() => {});
101
+ }
102
+ });
103
+
104
+ test('gh gist view should retrieve gist details', async () => {
105
+ if (CI_ENVIRONMENT || !isAuthenticated || !testGistId) {
106
+ console.log('Skipped: CI environment or no test gist available');
107
+ return;
108
+ }
109
+
110
+ const result = await $`gh gist view ${testGistId} --files`.run({
111
+ capture: true,
112
+ mirror: false,
113
+ });
114
+
115
+ expect(result.code).toBe(0);
116
+ expect(result.stdout).toBeDefined();
117
+ expect(result.stdout).toContain('test-gist-file.txt');
118
+ });
119
+
120
+ test('gh api should work for gist operations', async () => {
121
+ if (CI_ENVIRONMENT || !isAuthenticated || !testGistId) {
122
+ console.log('Skipped: CI environment or no test gist available');
123
+ return;
124
+ }
125
+
126
+ const result = await $`gh api /gists/${testGistId} --jq '.description'`.run(
127
+ {
128
+ capture: true,
129
+ mirror: false,
130
+ }
131
+ );
132
+
133
+ expect(result.code).toBe(0);
134
+ expect(result.stdout.trim()).toBe(TEST_DESC);
135
+ });
136
+
137
+ test.skip('gh gist edit should add files to existing gist', async () => {
138
+ if (CI_ENVIRONMENT || !isAuthenticated || !testGistId) {
139
+ console.log('Skipped: CI environment or no test gist available');
140
+ return;
141
+ }
142
+
143
+ // Create another file to add
144
+ const tempFile2 = path.join(os.tmpdir(), 'additional-file.txt');
145
+ await fs.writeFile(tempFile2, 'Additional content\n');
146
+
147
+ try {
148
+ const result =
149
+ await $`gh gist edit ${testGistId} ${tempFile2} --filename "added.txt" 2>&1`.run(
150
+ {
151
+ capture: true,
152
+ mirror: false,
153
+ timeout: 10000,
154
+ }
155
+ );
156
+
157
+ // gh gist edit may not return 0 but should complete
158
+ expect(result).toBeDefined();
159
+
160
+ // Verify file was added
161
+ const verifyResult = await $`gh gist view ${testGistId} --files`.run({
162
+ capture: true,
163
+ mirror: false,
164
+ });
165
+
166
+ expect(verifyResult.code).toBe(0);
167
+ expect(verifyResult.stdout).toContain('added.txt');
168
+ } finally {
169
+ await fs.unlink(tempFile2).catch(() => {});
170
+ }
171
+ });
172
+
173
+ test('gh gist delete should remove gist', async () => {
174
+ if (CI_ENVIRONMENT || !isAuthenticated || !testGistId) {
175
+ console.log('Skipped: CI environment or no test gist available');
176
+ return;
177
+ }
178
+
179
+ const result = await $`gh gist delete ${testGistId} --yes`.run({
180
+ capture: true,
181
+ mirror: false,
182
+ });
183
+
184
+ expect(result.code).toBe(0);
185
+
186
+ // Verify deletion - should fail to view
187
+ try {
188
+ await $`gh gist view ${testGistId}`.run({
189
+ capture: true,
190
+ mirror: false,
191
+ });
192
+ // Should not reach here
193
+ expect(true).toBe(false);
194
+ } catch (error) {
195
+ // Expected to fail
196
+ expect(error).toBeDefined();
197
+ }
198
+
199
+ // Clear the ID since it's deleted
200
+ testGistId = null;
201
+ });
202
+
203
+ test('complex gh command with pipes should work', async () => {
204
+ if (CI_ENVIRONMENT || !isAuthenticated) {
205
+ console.log('Skipped: CI environment or not authenticated');
206
+ return;
207
+ }
208
+
209
+ // Test piping gh output through other commands
210
+ const result = await $`gh api /gists --jq '.[0].id' | head -1`.run({
211
+ capture: true,
212
+ mirror: false,
213
+ });
214
+
215
+ expect(result.code).toBe(0);
216
+ expect(result.stdout).toBeDefined();
217
+ // Should be a single line (gist ID)
218
+ const lines = result.stdout.trim().split('\n');
219
+ expect(lines.length).toBe(1);
220
+ });
221
+ });
@@ -0,0 +1,466 @@
1
+ import { test, expect, describe, beforeEach, afterEach } from 'bun:test';
2
+ import { beforeTestCleanup, afterTestCleanup } from './test-cleanup.mjs';
3
+ import { $, shell, enableVirtualCommands } from '../src/$.mjs';
4
+ import { mkdtempSync, rmSync, existsSync, realpathSync } from 'fs';
5
+ import { tmpdir } from 'os';
6
+ import { join } from 'path';
7
+
8
+ // Platform detection - Tests use bash and Unix shell commands
9
+ const isWindows = process.platform === 'win32';
10
+
11
+ // Helper to normalize paths (handles macOS /var -> /private/var symlink)
12
+ const normalizePath = (p) => {
13
+ try {
14
+ return realpathSync(p);
15
+ } catch {
16
+ return p;
17
+ }
18
+ };
19
+
20
+ beforeEach(async () => {
21
+ // IMPORTANT: Call cleanup first to restore cwd
22
+ await beforeTestCleanup();
23
+
24
+ shell.errexit(false);
25
+ shell.verbose(false);
26
+ shell.xtrace(false);
27
+ shell.pipefail(false);
28
+ shell.nounset(false);
29
+ enableVirtualCommands();
30
+ });
31
+
32
+ afterEach(async () => {
33
+ // IMPORTANT: Call cleanup to restore cwd
34
+ await afterTestCleanup();
35
+
36
+ shell.errexit(false);
37
+ shell.verbose(false);
38
+ shell.xtrace(false);
39
+ shell.pipefail(false);
40
+ shell.nounset(false);
41
+ });
42
+
43
+ // Skip on Windows - uses bash -c, pwd, subshells, and Unix redirection
44
+ describe.skipIf(isWindows)(
45
+ 'Git and GH commands with cd virtual command',
46
+ () => {
47
+ // The test-cleanup functions handle cwd restoration globally
48
+
49
+ describe('Git operations in temp directories', () => {
50
+ let tempDir;
51
+
52
+ beforeEach(() => {
53
+ tempDir = mkdtempSync(join(tmpdir(), 'git-test-'));
54
+ });
55
+
56
+ afterEach(() => {
57
+ // Clean up the temp directory
58
+ try {
59
+ rmSync(tempDir, { recursive: true, force: true });
60
+ } catch (e) {
61
+ // Ignore cleanup errors
62
+ }
63
+ });
64
+
65
+ test('should initialize git repo after cd to temp directory', async () => {
66
+ const originalCwd = process.cwd();
67
+
68
+ const cdResult = await $`cd ${tempDir}`;
69
+ expect(cdResult.code).toBe(0);
70
+
71
+ const initResult = await $`git init`;
72
+ expect(initResult.code).toBe(0);
73
+ // Git init outputs to stderr
74
+ const output = initResult.stdout + initResult.stderr;
75
+ expect(output).toContain('Initialized empty Git repository');
76
+
77
+ const statusResult = await $`git status`;
78
+ expect(statusResult.code).toBe(0);
79
+ expect(statusResult.stdout).toContain('On branch');
80
+
81
+ await $`cd ${originalCwd}`;
82
+ });
83
+
84
+ test('should handle git commands in temp directory with cd chain', async () => {
85
+ const originalCwd = process.cwd();
86
+
87
+ // Git init and check status
88
+ const result =
89
+ await $`cd ${tempDir} && git init && git status --porcelain`;
90
+ expect(result.code).toBe(0);
91
+ // Git init outputs to stderr, check both
92
+ const output = result.stdout + result.stderr;
93
+ expect(output).toContain('Initialized empty Git repository');
94
+
95
+ await $`cd ${originalCwd}`;
96
+ });
97
+
98
+ test('should create and commit files in temp git repo', async () => {
99
+ const originalCwd = process.cwd();
100
+
101
+ await $`cd ${tempDir}`;
102
+ await $`git init`;
103
+ await $`git config user.email "test@example.com"`;
104
+ await $`git config user.name "Test User"`;
105
+
106
+ // Use bash -c to properly handle redirection
107
+ await $`bash -c 'echo "test content" > test.txt'`;
108
+ await $`git add test.txt`;
109
+
110
+ const commitResult = await $`git commit -m "Initial commit"`;
111
+ expect(commitResult.code).toBe(0);
112
+ expect(commitResult.stdout).toContain('1 file changed');
113
+
114
+ const logResult = await $`git log --oneline`;
115
+ expect(logResult.code).toBe(0);
116
+ expect(logResult.stdout).toContain('Initial commit');
117
+
118
+ await $`cd ${originalCwd}`;
119
+ });
120
+
121
+ test('should handle git branch operations with cd', async () => {
122
+ const originalCwd = process.cwd();
123
+
124
+ await $`cd ${tempDir} && git init`;
125
+ await $`cd ${tempDir} && git config user.email "test@example.com"`;
126
+ await $`cd ${tempDir} && git config user.name "Test User"`;
127
+ await $`cd ${tempDir} && bash -c 'echo "content" > file.txt' && git add . && git commit -m "init"`;
128
+
129
+ const branchResult =
130
+ await $`cd ${tempDir} && git branch --show-current`;
131
+ expect(branchResult.code).toBe(0);
132
+ const defaultBranch = branchResult.stdout.trim();
133
+ expect(['main', 'master']).toContain(defaultBranch);
134
+
135
+ await $`cd ${tempDir} && git checkout -b feature-branch`;
136
+
137
+ const newBranchResult =
138
+ await $`cd ${tempDir} && git branch --show-current`;
139
+ expect(newBranchResult.code).toBe(0);
140
+ expect(newBranchResult.stdout.trim()).toBe('feature-branch');
141
+
142
+ await $`cd ${originalCwd}`;
143
+ });
144
+
145
+ test('should handle multiple temp directories with cd', async () => {
146
+ const tempDir2 = mkdtempSync(join(tmpdir(), 'git-test2-'));
147
+ const originalCwd = process.cwd();
148
+
149
+ try {
150
+ await $`cd ${tempDir} && git init && bash -c 'echo "repo1" > file.txt'`;
151
+ await $`cd ${tempDir2} && git init && bash -c 'echo "repo2" > file.txt'`;
152
+
153
+ const repo1Content = await $`cd ${tempDir} && cat file.txt`;
154
+ expect(repo1Content.stdout.trim()).toBe('repo1');
155
+
156
+ const repo2Content = await $`cd ${tempDir2} && cat file.txt`;
157
+ expect(repo2Content.stdout.trim()).toBe('repo2');
158
+
159
+ const repo1Status = await $`cd ${tempDir} && git status --porcelain`;
160
+ expect(repo1Status.stdout).toContain('file.txt');
161
+
162
+ const repo2Status = await $`cd ${tempDir2} && git status --porcelain`;
163
+ expect(repo2Status.stdout).toContain('file.txt');
164
+ } finally {
165
+ rmSync(tempDir2, { recursive: true, force: true });
166
+ await $`cd ${originalCwd}`;
167
+ }
168
+ });
169
+
170
+ test('should handle git diff operations after cd', async () => {
171
+ const originalCwd = process.cwd();
172
+
173
+ await $`cd ${tempDir} && git init`;
174
+ await $`cd ${tempDir} && git config user.email "test@example.com"`;
175
+ await $`cd ${tempDir} && git config user.name "Test User"`;
176
+ await $`cd ${tempDir} && bash -c 'echo "line1" > file.txt' && git add . && git commit -m "first"`;
177
+ await $`cd ${tempDir} && bash -c 'echo "line2" >> file.txt'`;
178
+
179
+ const diffResult = await $`cd ${tempDir} && git diff`;
180
+ expect(diffResult.code).toBe(0);
181
+ expect(diffResult.stdout).toContain('+line2');
182
+
183
+ const statusResult = await $`cd ${tempDir} && git status --porcelain`;
184
+ expect(statusResult.stdout).toContain(' M file.txt');
185
+
186
+ await $`cd ${originalCwd}`;
187
+ });
188
+
189
+ test('should work with git in subshells', async () => {
190
+ const originalCwd = process.cwd();
191
+
192
+ // Initialize repo in subshell - should not affect parent
193
+ await $`(cd ${tempDir} && git init && git config user.email "test@example.com")`;
194
+
195
+ // Verify we're still in original directory
196
+ const pwd = await $`pwd`;
197
+ expect(pwd.stdout.trim()).toBe(originalCwd);
198
+
199
+ // But the repo should exist
200
+ const checkResult = await $`cd ${tempDir} && git status`;
201
+ expect(checkResult.code).toBe(0);
202
+
203
+ await $`cd ${originalCwd}`;
204
+ });
205
+ });
206
+
207
+ describe('GH CLI operations with cd', () => {
208
+ test('should check gh auth status', async () => {
209
+ const result = await $`gh auth status`;
210
+ // This might fail if not authenticated, but we're testing the command execution
211
+ expect([0, 1]).toContain(result.code);
212
+
213
+ if (result.code === 0) {
214
+ expect(result.stdout.toLowerCase()).toMatch(
215
+ /logged in|authenticated/i
216
+ );
217
+ } else {
218
+ expect(result.stderr.toLowerCase()).toMatch(
219
+ /not.*authenticated|not.*logged/i
220
+ );
221
+ }
222
+ });
223
+
224
+ test('should handle gh api calls with cd to temp directory', async () => {
225
+ const tempDir = mkdtempSync(join(tmpdir(), 'gh-test-'));
226
+ const originalCwd = process.cwd();
227
+
228
+ try {
229
+ await $`cd ${tempDir}`;
230
+
231
+ const result =
232
+ await $`gh api user --jq .login 2>/dev/null || echo "not-authenticated"`;
233
+ expect(result.code).toBe(0);
234
+ // Result will be either a username or "not-authenticated"
235
+ expect(result.stdout.trim().length).toBeGreaterThan(0);
236
+
237
+ await $`cd ${originalCwd}`;
238
+ } finally {
239
+ rmSync(tempDir, { recursive: true, force: true });
240
+ }
241
+ });
242
+
243
+ test('should simulate gh repo clone pattern', async () => {
244
+ const tempDir = mkdtempSync(join(tmpdir(), 'gh-clone-'));
245
+ const originalCwd = process.cwd();
246
+
247
+ try {
248
+ // Simulate the pattern from solve.mjs without actually cloning
249
+ const owner = 'octocat';
250
+ const repo = 'Hello-World';
251
+
252
+ await $`cd ${tempDir}`;
253
+
254
+ // Test the command structure (use -- to separate git flags as per gh documentation)
255
+ const cloneCmd = `gh repo clone ${owner}/${repo} . -- --depth 1 2>&1 || echo "Clone would execute here"`;
256
+ const result = await $`bash -c ${cloneCmd}`;
257
+ expect(result.code).toBe(0);
258
+
259
+ // Test that we're in the right directory
260
+ const pwdResult = await $`pwd`;
261
+ expect(normalizePath(pwdResult.stdout.trim())).toBe(
262
+ normalizePath(tempDir)
263
+ );
264
+
265
+ await $`cd ${originalCwd}`;
266
+ } finally {
267
+ rmSync(tempDir, { recursive: true, force: true });
268
+ }
269
+ });
270
+
271
+ test('should handle gh commands with directory context', async () => {
272
+ const tempDir = mkdtempSync(join(tmpdir(), 'gh-context-'));
273
+ const originalCwd = process.cwd();
274
+
275
+ try {
276
+ // Create a mock git repo structure
277
+ await $`cd ${tempDir} && git init`;
278
+ await $`cd ${tempDir} && git config user.email "test@example.com"`;
279
+ await $`cd ${tempDir} && git config user.name "Test User"`;
280
+ await $`cd ${tempDir} && bash -c 'echo "# Test Repo" > README.md'`;
281
+ await $`cd ${tempDir} && git add . && git commit -m "Initial commit"`;
282
+
283
+ // Test gh command patterns that would work in a repo context
284
+ const statusResult = await $`cd ${tempDir} && git status --porcelain`;
285
+ expect(statusResult.code).toBe(0);
286
+ expect(statusResult.stdout).toBe('');
287
+
288
+ // Simulate checking for existing PRs (would fail without actual remote)
289
+ const prListCmd =
290
+ await $`cd ${tempDir} && gh pr list --limit 1 2>&1 || echo "No remote configured"`;
291
+ expect(prListCmd.code).toBe(0);
292
+
293
+ await $`cd ${originalCwd}`;
294
+ } finally {
295
+ rmSync(tempDir, { recursive: true, force: true });
296
+ }
297
+ });
298
+ });
299
+
300
+ describe('Combined git and gh workflows', () => {
301
+ test('should simulate solve.mjs workflow pattern', async () => {
302
+ const tempDir = mkdtempSync(join(tmpdir(), 'workflow-'));
303
+ const originalCwd = process.cwd();
304
+
305
+ try {
306
+ // Step 1: Navigate to temp directory
307
+ await $`cd ${tempDir}`;
308
+
309
+ // Step 2: Initialize git repo (simulating clone)
310
+ await $`git init`;
311
+ await $`git config user.email "bot@example.com"`;
312
+ await $`git config user.name "Bot User"`;
313
+
314
+ // Step 3: Check current branch
315
+ const branchResult = await $`git branch --show-current`;
316
+ const currentBranch = branchResult.stdout.trim() || 'master';
317
+
318
+ // Step 4: Create new feature branch
319
+ const branchName = `feature-${Date.now()}`;
320
+ await $`git checkout -b ${branchName}`;
321
+
322
+ // Step 5: Verify branch switch
323
+ const newBranchResult = await $`git branch --show-current`;
324
+ expect(newBranchResult.stdout.trim()).toBe(branchName);
325
+
326
+ // Step 6: Make changes
327
+ await $`bash -c 'echo "feature implementation" > feature.js'`;
328
+ await $`git add .`;
329
+
330
+ // Step 7: Check status
331
+ const statusResult = await $`git status --porcelain`;
332
+ expect(statusResult.stdout.trim()).toMatch(/A.*feature\.js/);
333
+
334
+ // Step 8: Commit changes
335
+ await $`git commit -m "Add feature implementation"`;
336
+
337
+ // Step 9: Verify commit
338
+ const logResult = await $`git log --oneline -1`;
339
+ expect(logResult.stdout).toContain('Add feature implementation');
340
+
341
+ await $`cd ${originalCwd}`;
342
+ } finally {
343
+ rmSync(tempDir, { recursive: true, force: true });
344
+ }
345
+ });
346
+
347
+ test('should handle error scenarios with cd and git', async () => {
348
+ const tempDir = mkdtempSync(join(tmpdir(), 'error-test-'));
349
+ const originalCwd = process.cwd();
350
+
351
+ try {
352
+ // Test invalid git command in temp directory
353
+ await $`cd ${tempDir}`;
354
+ // Git status will fail in non-git directory
355
+ const result = await $`git status 2>&1 || echo "not a git repo"`;
356
+ const output = result.stdout + result.stderr;
357
+ expect(output.toLowerCase()).toMatch(
358
+ /not a git repo|fatal.*not a git repository/
359
+ );
360
+
361
+ // Test recovery after error
362
+ await $`git init`;
363
+ const retryResult = await $`git status`;
364
+ expect(retryResult.code).toBe(0);
365
+
366
+ await $`cd ${originalCwd}`;
367
+ } finally {
368
+ rmSync(tempDir, { recursive: true, force: true });
369
+ }
370
+ });
371
+
372
+ test('should preserve cwd after command chains', async () => {
373
+ const tempDir = mkdtempSync(join(tmpdir(), 'cwd-test-'));
374
+ const originalCwd = process.cwd();
375
+
376
+ try {
377
+ // Run commands with cd in subshell
378
+ await $`(cd ${tempDir} && git init && echo 'test' > file.txt)`;
379
+
380
+ // Verify we're still in original directory
381
+ const currentDir = await $`pwd`;
382
+ expect(currentDir.stdout.trim()).toBe(originalCwd);
383
+
384
+ // Verify the commands actually ran in temp directory
385
+ const checkResult =
386
+ await $`cd ${tempDir} && test -f file.txt && echo "exists"`;
387
+ expect(checkResult.stdout.trim()).toBe('exists');
388
+ } finally {
389
+ rmSync(tempDir, { recursive: true, force: true });
390
+ }
391
+ });
392
+
393
+ test('should work with complex git workflows using operators', async () => {
394
+ const tempDir = mkdtempSync(join(tmpdir(), 'complex-'));
395
+ const originalCwd = process.cwd();
396
+
397
+ try {
398
+ // Complex workflow with &&, ||, and ;
399
+ const result =
400
+ await $`cd ${tempDir} && git init && git config user.email "test@test.com" && git config user.name "Test" ; echo "setup done"`;
401
+ expect(result.stdout).toContain('setup done');
402
+
403
+ // Use || for error handling - git remote add returns 0 even for non-existent URLs
404
+ const errorHandling =
405
+ await $`cd ${tempDir} && git remote get-url nonexistent 2>/dev/null || echo "remote failed as expected"`;
406
+ expect(errorHandling.stdout).toContain('remote failed as expected');
407
+
408
+ // Complex chain with file operations
409
+ await $`cd ${tempDir} && echo "test" > file1.txt && git add . && git commit -m "test" && echo "committed"`;
410
+
411
+ const logCheck = await $`cd ${tempDir} && git log --oneline`;
412
+ expect(logCheck.stdout).toContain('test');
413
+
414
+ await $`cd ${originalCwd}`;
415
+ } finally {
416
+ rmSync(tempDir, { recursive: true, force: true });
417
+ }
418
+ });
419
+ });
420
+
421
+ describe('Path resolution and quoting with cd', () => {
422
+ // Global cleanup handles cwd restoration
423
+
424
+ test('should handle paths with spaces in git operations', async () => {
425
+ const baseTempDir = mkdtempSync(join(tmpdir(), 'space-test-'));
426
+ const tempDirWithSpace = join(baseTempDir, 'my test dir');
427
+
428
+ try {
429
+ await $`mkdir -p ${tempDirWithSpace}`;
430
+ await $`cd ${tempDirWithSpace} && git init`;
431
+
432
+ const pwdResult = await $`cd ${tempDirWithSpace} && pwd`;
433
+ expect(normalizePath(pwdResult.stdout.trim())).toBe(
434
+ normalizePath(tempDirWithSpace)
435
+ );
436
+
437
+ // Test git operations in directory with spaces
438
+ await $`cd ${tempDirWithSpace} && git config user.email "test@test.com"`;
439
+ await $`cd ${tempDirWithSpace} && git config user.name "Test User"`;
440
+ await $`cd ${tempDirWithSpace} && bash -c 'echo "test" > file.txt' && git add . && git commit -m "test"`;
441
+
442
+ const logResult =
443
+ await $`cd ${tempDirWithSpace} && git log --oneline`;
444
+ expect(logResult.stdout).toContain('test');
445
+ } finally {
446
+ rmSync(baseTempDir, { recursive: true, force: true });
447
+ }
448
+ });
449
+
450
+ test('should handle special characters in paths', async () => {
451
+ const tempDir = mkdtempSync(join(tmpdir(), 'special-chars-'));
452
+ const specialDir = join(tempDir, "test-'dir'-$1");
453
+
454
+ try {
455
+ await $`mkdir -p ${specialDir}`;
456
+ await $`cd ${specialDir} && git init`;
457
+
458
+ const statusResult = await $`cd ${specialDir} && git status`;
459
+ expect(statusResult.code).toBe(0);
460
+ } finally {
461
+ rmSync(tempDir, { recursive: true, force: true });
462
+ }
463
+ });
464
+ });
465
+ }
466
+ );