Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
21adbd8
Initial commit with task details for issue #21
konard Sep 9, 2025
dabb02a
Remove CLAUDE.md - PR created successfully
konard Sep 9, 2025
112c02d
Add comprehensive Node.js vs Bun.js comparison examples
konard Sep 9, 2025
757537b
Fix cross-runtime behaviour differences found while comparing Node.js…
Sep 16, 2026
bf75dd2
Add probes for the cross-runtime differences found while comparing No…
konard Sep 16, 2026
bcf6423
Make built-ins, pipefail and quoting behave the same in Node.js and Bun
konard Sep 16, 2026
3291e75
Add one runnable example per feature, recording what the feature prod…
konard Sep 16, 2026
12e52fa
🛟 Auto-commit before critical-error recovery (CLAUDE stopped: Subscri…
konard Sep 16, 2026
58e4787
Merge origin/main into issue-21-621d3004
konard Sep 16, 2026
e223318
feat: enforce JavaScript and Rust feature parity
konard Sep 16, 2026
f532b83
docs: generate cross-language feature guide
konard Sep 16, 2026
c2f0558
chore(rust): refresh benchmark lockfile
konard Sep 16, 2026
a8b724a
fix: use POSIX shell operators consistently on Windows
konard Sep 16, 2026
dbef9eb
test(ci): ignore generated benchmark lock metadata
konard Sep 16, 2026
dc35fc7
fix(ci): refresh Rust benchmark baseline lock
konard Sep 16, 2026
12a911f
test(rust): use preferred shell syntax on Windows
konard Sep 16, 2026
641bf1f
fix(docs): render feature data without HTML sinks
konard Sep 16, 2026
308e24c
test(rust): use preferred shell for streaming cwd
konard Sep 16, 2026
0a3abb0
fix(js): preserve virtual pipeline streaming
konard Sep 16, 2026
fa28193
test(rust): verify streaming cwd portably
konard Sep 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .github/DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,25 @@ The Rust workflow maps both names and runs Rust release scripts from
Rust PRs that change crate code must add a changelog fragment in
`rust/changelog.d/`.

## Feature Documentation

The feature catalog in `js/examples/features/catalog.mjs` drives executable
examples for both language packages and the generated documentation in
`docs/`. Pull requests run every catalog entry with Node.js, Bun and Rust, then
verify that the committed guide is current.

Generate and validate the guide locally from the repository root:

```bash
node scripts/generate-docs.mjs
node scripts/check-parity.mjs
node scripts/generate-docs.mjs --check
```

After changes reach `main`, `.github/workflows/docs.yml` publishes
`docs/site/` to GitHub Pages. Configure the repository's Pages source as
**GitHub Actions** before the first deployment.

## Local Release Checks

JavaScript:
Expand Down
1 change: 1 addition & 0 deletions .github/scripts/check-language-parity.sh
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ while IFS= read -r f; do
js/src/*) js_source_changed=true ;;
rust/src/*) rust_source_changed=true ;;
js/benchmarks/* | js/tests/benchmark-*) js_benchmarks_changed=true ;;
rust/benchmarks/Cargo.lock) ;;
rust/benchmarks/*) rust_benchmarks_changed=true ;;
esac
done <<EOF
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/benchmarks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,11 @@ jobs:
git worktree add --detach "$base_directory" "origin/$BASE_REF"
(
cd "$base_directory/rust"
# A release can change the workspace package version while the
# nested benchmark lockfile on the base branch still names the
# previous one. Refresh only that local path package offline; all
# third-party dependencies remain pinned for the locked run.
cargo update --offline --manifest-path benchmarks/Cargo.toml -p command-stream
cargo run --release --locked --manifest-path benchmarks/Cargo.toml -- \
--smoke \
--output "$GITHUB_WORKSPACE/rust/benchmarks/baseline"
Expand Down
73 changes: 73 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: Feature documentation website

on:
push:
branches: [main]
paths:
- 'js/examples/features/**'
- 'js/src/**'
- 'rust/examples/**'
- 'rust/src/**'
- 'scripts/**'
- 'docs/features/**'
- 'docs/site/**'
- 'docs/README.md'
- '.github/workflows/docs.yml'
workflow_dispatch:

permissions:
contents: read

jobs:
deploy:
name: Verify and deploy generated documentation
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
concurrency:
group: main-writer-${{ github.repository }}-main
cancel-in-progress: false
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false

- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24.x'

- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: latest

- name: Setup Rust
uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable branch @ 2026-09-03

- name: Install JavaScript dependencies
working-directory: js
run: bun install --frozen-lockfile

- name: Verify feature parity and generated files
run: |
node scripts/check-parity.mjs
node scripts/generate-docs.mjs --check

- name: Configure GitHub Pages
uses: actions/configure-pages@v6

- name: Upload website artifact
uses: actions/upload-pages-artifact@v5
with:
path: docs/site

- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5
2 changes: 2 additions & 0 deletions .github/workflows/js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ on:
- 'eslint.config.js'
- 'claude-profiles.mjs'
- 'experiments/**'
- 'scripts/**'
- '.github/workflows/js.yml'
- 'README.md'
- 'LICENSE'
Expand All @@ -25,6 +26,7 @@ on:
- 'eslint.config.js'
- 'claude-profiles.mjs'
- 'experiments/**'
- 'scripts/**'
- '.github/workflows/js.yml'
- 'README.md'
- 'LICENSE'
Expand Down
23 changes: 23 additions & 0 deletions .github/workflows/parity.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,26 @@ jobs:
env:
BASE_REF: ${{ github.base_ref }}
run: bash .github/scripts/check-language-parity.sh

- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24.x'

- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: latest

- name: Setup Rust
uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable branch @ 2026-09-03

- name: Install JavaScript dependencies
working-directory: js
run: bun install --frozen-lockfile

- name: Execute every feature in JavaScript and Rust
run: node scripts/check-parity.mjs

- name: Check generated feature documentation
run: node scripts/generate-docs.mjs --check
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,21 @@ handling, pipeline support, and cross-platform behavior. Language-specific API
examples, package-manager instructions, release notes, and best practices live
with each package.

## Feature Guide

The [generated feature guide](./docs/README.md) runs every documented feature
in JavaScript and Rust and captures its real output. JavaScript examples are
also compared across Node.js and Bun. The same catalog powers the searchable
[feature website](https://link-foundation.github.io/command-stream/), which is
verified and deployed by GitHub Actions.

## Repository Layout

| Path | Purpose |
| ---------- | ---------------------------------------------------------- |
| `js/` | JavaScript package source, tests, docs, and CI/CD scripts. |
| `rust/` | Rust crate source, tests, docs, and CI/CD scripts. |
| `docs/` | Repository-level investigations and case studies. |
| `docs/` | Generated feature guide, website, and case studies. |
| `.github/` | GitHub workflow definitions and deployment notes. |

## Releases
Expand Down
130 changes: 130 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Feature documentation

Every feature of command-stream, with executable JavaScript and Rust examples,
captured output, and the same thing written with other shell libraries.

This file is generated by `node scripts/generate-docs.mjs`. Edit the examples in
`js/examples/features/` or the catalog in `js/examples/features/catalog.mjs` instead.

## Language and runtime parity

All 25 examples were executed in JavaScript and Rust. JavaScript was checked in Node.js and Bun.

| Feature | JavaScript (Node.js) | JavaScript (Bun) | Rust |
| -------------------------------------------------------------------- | -------------------- | ---------------- | ---- |
| [Await a command](features/await-result.md) | ✓ | ✓ | ✓ |
| [Read the output with text()](features/result-text.md) | ✓ | ✓ | ✓ |
| [Synchronous execution](features/sync-execution.md) | ✓ | ✓ | ✓ |
| [Exit codes and errors](features/exit-codes.md) | ✓ | ✓ | ✓ |
| [Options: capture, cwd, env, stdin](features/options.md) | ✓ | ✓ | ✓ |
| [Function and builder APIs](features/function-api.md) | ✓ | ✓ | ✓ |
| [Killing and cancelling commands](features/cancellation.md) | ✓ | ✓ | ✓ |
| [Async iteration over output](features/async-iteration.md) | ✓ | ✓ | ✓ |
| [Event-driven output](features/events.md) | ✓ | ✓ | ✓ |
| [Writing to stdin while a command runs](features/stdin-streaming.md) | ✓ | ✓ | ✓ |
| [Buffer and string interfaces](features/buffers-strings.md) | ✓ | ✓ | ✓ |
| [Mirroring and capturing output](features/mirror-capture.md) | ✓ | ✓ | ✓ |
| [The built-in command catalog](features/builtin-catalog.md) | ✓ | ✓ | ✓ |
| [File system built-ins](features/builtin-filesystem.md) | ✓ | ✓ | ✓ |
| [Text and value built-ins](features/builtin-text.md) | ✓ | ✓ | ✓ |
| [Environment built-ins](features/builtin-environment.md) | ✓ | ✓ | ✓ |
| [Registering your own commands](features/virtual-commands.md) | ✓ | ✓ | ✓ |
| [The handler context](features/virtual-context.md) | ✓ | ✓ | ✓ |
| [Streaming commands](features/virtual-streaming.md) | ✓ | ✓ | ✓ |
| [Pipelines](features/pipelines.md) | ✓ | ✓ | ✓ |
| [Redirecting output and input](features/redirection.md) | ✓ | ✓ | ✓ |
| [Command sequences](features/sequences.md) | ✓ | ✓ | ✓ |
| [Safe interpolation](features/interpolation.md) | ✓ | ✓ | ✓ |
| [Shell settings](features/shell-settings.md) | ✓ | ✓ | ✓ |
| [ANSI and control character helpers](features/ansi-utils.md) | ✓ | ✓ | ✓ |

## Library comparison

✓ supported, — not supported. Follow a feature for the code in each library.

| Feature | command-stream | Bun.$ | zx | execa | ShellJS | node:child_process |
| -------------------------------------------------------------------- | -------------- | ----- | --- | ----- | ------- | ------------------ |
| [Await a command](features/await-result.md) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| [Read the output with text()](features/result-text.md) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| [Synchronous execution](features/sync-execution.md) | ✓ | — | ✓ | ✓ | ✓ | ✓ |
| [Exit codes and errors](features/exit-codes.md) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| [Options: capture, cwd, env, stdin](features/options.md) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| [Function and builder APIs](features/function-api.md) | ✓ | — | ✓ | ✓ | ✓ | ✓ |
| [Killing and cancelling commands](features/cancellation.md) | ✓ | — | ✓ | ✓ | ✓ | ✓ |
| [Async iteration over output](features/async-iteration.md) | ✓ | ✓ | ✓ | ✓ | — | ✓ |
| [Event-driven output](features/events.md) | ✓ | — | ✓ | ✓ | ✓ | ✓ |
| [Writing to stdin while a command runs](features/stdin-streaming.md) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| [Buffer and string interfaces](features/buffers-strings.md) | ✓ | ✓ | ✓ | ✓ | — | ✓ |
| [Mirroring and capturing output](features/mirror-capture.md) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| [The built-in command catalog](features/builtin-catalog.md) | ✓ | ✓ | — | — | ✓ | — |
| [File system built-ins](features/builtin-filesystem.md) | ✓ | ✓ | ✓ | — | ✓ | — |
| [Text and value built-ins](features/builtin-text.md) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| [Environment built-ins](features/builtin-environment.md) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| [Registering your own commands](features/virtual-commands.md) | ✓ | — | — | — | ✓ | — |
| [The handler context](features/virtual-context.md) | ✓ | — | — | — | ✓ | — |
| [Streaming commands](features/virtual-streaming.md) | ✓ | — | — | — | — | — |
| [Pipelines](features/pipelines.md) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| [Redirecting output and input](features/redirection.md) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| [Command sequences](features/sequences.md) | ✓ | ✓ | ✓ | — | ✓ | ✓ |
| [Safe interpolation](features/interpolation.md) | ✓ | ✓ | ✓ | ✓ | — | ✓ |
| [Shell settings](features/shell-settings.md) | ✓ | ✓ | ✓ | — | ✓ | ✓ |
| [ANSI and control character helpers](features/ansi-utils.md) | ✓ | — | ✓ | ✓ | — | — |

## Features by category

### Running commands

- [Await a command](features/await-result.md) — Awaiting a command returns an object with stdout, stderr and the exit code.
- [Synchronous execution](features/sync-execution.md) — The same command can be run without awaiting, blocking until it finishes.
- [Exit codes and errors](features/exit-codes.md) — A non-zero exit code is reported on the result instead of thrown, unless errexit is set.
- [Options: capture, cwd, env, stdin](features/options.md) — Execution options control capture, cwd, environment and stdin for a command or reusable runner.
- [Function and builder APIs](features/function-api.md) — Commands can also be built from plain strings instead of template literals.
- [Killing and cancelling commands](features/cancellation.md) — A running command can be killed, and cancelling one leaves the rest of the script running.

### Reading output

- [Read the output with text()](features/result-text.md) — Captured stdout is available as text through each language’s result API.
- [Buffer and string interfaces](features/buffers-strings.md) — Output is available as a string and as raw bytes, without running the command twice.
- [Mirroring and capturing output](features/mirror-capture.md) — Output can be shown, captured, both or neither, chosen independently.

### Streaming

- [Async iteration over output](features/async-iteration.md) — A command is an async iterable of chunks, so output can be handled as it arrives.
- [Event-driven output](features/events.md) — Event APIs report output and lifecycle signals as work progresses.
- [Writing to stdin while a command runs](features/stdin-streaming.md) — Input can be supplied up front or written to a running command.

### Built-in commands

- [The built-in command catalog](features/builtin-catalog.md) — Common commands are implemented in-process in both languages for portable behavior.
- [File system built-ins](features/builtin-filesystem.md) — ls, cat, mkdir, touch, cp, mv, rm and test run in-process.
- [Text and value built-ins](features/builtin-text.md) — echo, seq, yes, basename, dirname, true and false run in-process.
- [Environment built-ins](features/builtin-environment.md) — cd, pwd, env, which and exit affect the command they run in, not the host process.

### Your own commands

- [Registering your own commands](features/virtual-commands.md) — A handler can be registered by name and invoked through a registry or command runner.
- [The handler context](features/virtual-context.md) — A handler receives args, stdin, cwd, env and a cancellation signal.
- [Streaming commands](features/virtual-streaming.md) — A streaming handler publishes output incrementally like a real process.

### Shell syntax

- [Pipelines](features/pipelines.md) — Commands can be composed into pipelines whose output feeds the next stage.
- [Redirecting output and input](features/redirection.md) — >, >> and < redirect command input and output with shell-compatible behavior.
- [Command sequences](features/sequences.md) — &&, ||, ; and parentheses execute with the expected shell semantics.
- [Safe interpolation](features/interpolation.md) — Interpolated values are escaped as arguments; each language also exposes an explicit raw form.
- [Shell settings](features/shell-settings.md) — Shell settings model errexit, pipefail, verbose, xtrace and nounset behavior.

### Utilities

- [ANSI and control character helpers](features/ansi-utils.md) — Helpers can strip colours and control characters from captured output.

## Libraries compared

| Library | Version | Runs in |
| ------------------------------------------------------------------- | --------------- | ------------------ |
| [command-stream](https://github.com/link-foundation/command-stream) | this repository | Node.js, Bun |
| [Bun.$](https://bun.com/docs/runtime/shell) | 1.4 | Bun |
| [zx](https://github.com/google/zx) | 8 | Node.js, Bun, Deno |
| [execa](https://github.com/sindresorhus/execa) | 9.6 | Node.js, Bun, Deno |
| [ShellJS](https://github.com/shelljs/shelljs) | 0.10 | Node.js, Bun |
| [node:child_process](https://nodejs.org/api/child_process.html) | this repository | Node.js, Bun, Deno |
Loading
Loading