Package Exports
- command-stream
Readme
command-$tream
$treamable commands executor
A modern $ shell utility library with streaming, async iteration, and EventEmitter support, optimized for Bun runtime.
Features
- π Shell-like by Default: Commands behave exactly like running in terminal (stdoutβstdout, stderrβstderr, stdinβstdin)
- ποΈ Fully Controllable: Override default behavior with options (
mirror,capture,stdin) - π Multiple Usage Patterns: Classic await, async iteration, EventEmitter, .pipe() method, and mixed patterns
- π‘ Real-time Streaming: Process command output as it arrives, not after completion
- π Bun Optimized: Designed for Bun runtime with Node.js compatibility
- β‘ Performance: Memory-efficient streaming prevents large buffer accumulation
- π― Backward Compatible: Existing
await $syntax continues to work + Bun.$.text()method - π‘οΈ Type Safe: Full TypeScript support (coming soon)
- π§ Built-in Commands: 18 essential commands work identically across platforms
Comparison with Other Libraries
| Feature | command-stream | Bun.$ | execa | zx |
|---|---|---|---|---|
| Runtime Support | β Bun + Node.js | π‘ Bun only | β Node.js | β Node.js |
| Template Literals | β
$`cmd` |
β
$`cmd` |
β
$`cmd` |
β
$`cmd` |
| Real-time Streaming | β Live output | β Buffer only | π‘ Limited | β Buffer only |
| Synchronous Execution | β
.sync() with events |
β No | β
execaSync |
β No |
| Async Iteration | β
for await (chunk of $.stream()) |
β No | β No | β No |
| EventEmitter Pattern | β
.on('data', ...) |
β No | π‘ Limited events | β No |
| Mixed Patterns | β Events + await/sync | β No | β No | β No |
| Bun.$ Compatibility | β
.text() method support |
β Native API | β No | β No |
| Shell Injection Protection | β Auto-quoting | β Built-in | β Safe by default | β Safe by default |
| Cross-platform | β macOS/Linux/Windows | β Yes | β Yes | β Yes |
| Performance | β‘ Fast (Bun optimized) | β‘ Very fast | π Moderate | π Slow |
| Memory Efficiency | β Streaming prevents buildup | π‘ Buffers in memory | π‘ Buffers in memory | π‘ Buffers in memory |
| Error Handling | β
Configurable (set -e/set +e, non-zero OK by default) |
β Throws on error | β Throws on error | β Throws on error |
| Shell Settings | β
set -e/set +e equivalent |
β No | β No | β No |
| Stdout Support | β Real-time streaming + events | β Shell redirection + buffered | β Node.js streams + interleaved | β
Readable streams + .pipe.stdout |
| Stderr Support | β Real-time streaming + events | β
Redirection + .quiet() access |
β Streams + interleaved output | β
Readable streams + .pipe.stderr |
| Stdin Support | β string/Buffer/inherit/ignore | β Pipe operations | β Input/output streams | β Basic stdin |
| Built-in Commands | β 18 commands: cat, ls, mkdir, rm, mv, cp, touch, basename, dirname, seq, yes + all Bun.$ commands | β echo, cd, etc. | β Uses system | β Uses system |
| Virtual Commands Engine | β Revolutionary: Register JavaScript functions as shell commands with full pipeline support | β No extensibility | β No custom commands | β No custom commands |
| Pipeline/Piping Support | β
Advanced: System + Built-ins + Virtual + Mixed + .pipe() method |
β Standard shell piping | β
Programmatic .pipe() + multi-destination |
β
Shell piping + .pipe() method |
| Bundle Size | π¦ ~15KB | π― 0KB (built-in) | π¦ ~25KB | π¦ ~50KB |
| TypeScript | π Coming soon | β Built-in | β Full support | β Full support |
| License | β Unlicense (Public Domain) | π‘ MIT (+ LGPL dependencies) | π‘ MIT | π‘ Apache 2.0 |
Why Choose command-stream?
- π Truly Free: Unlicense (Public Domain) - No restrictions, no attribution required, use however you want
- π Revolutionary Virtual Commands: World's first fully customizable virtual commands engine - register JavaScript functions as shell commands!
- π Advanced Pipeline System: Only library where virtual commands work seamlessly in pipelines with built-ins and system commands
- π§ Built-in Commands: 18 essential commands work identically across all platforms - no system dependencies!
- π‘ Real-time Processing: Only library with true streaming and async iteration
- π Flexible Patterns: Multiple usage patterns (await, events, iteration, mixed)
- π Shell Replacement: Dynamic error handling with
set -e/set +eequivalents for .sh file replacement - β‘ Bun Optimized: Designed for Bun with Node.js fallback compatibility
- πΎ Memory Efficient: Streaming prevents large buffer accumulation
- π‘οΈ Production Ready: 266+ tests with comprehensive coverage
Built-in Commands (π NEW!)
command-stream now includes 18 built-in commands that work identically to their bash/sh counterparts, providing true cross-platform shell scripting without system dependencies:
π File System Commands
cat- Read and display file contentsls- List directory contents (supports-l,-a,-A)mkdir- Create directories (supports-precursive)rm- Remove files/directories (supports-r,-f)mv- Move/rename files and directoriescp- Copy files/directories (supports-rrecursive)touch- Create files or update timestamps
π§ Utility Commands
basename- Extract filename from pathdirname- Extract directory from pathseq- Generate number sequencesyes- Output string repeatedly (streaming)
β‘ System Commands
cd- Change directorypwd- Print working directoryecho- Print arguments (supports-n)sleep- Wait for specified timetrue/false- Success/failure commandswhich- Locate commandsexit- Exit with codeenv- Print environment variablestest- File condition testing
β¨ Key Advantages
- π Cross-Platform: Works identically on Windows, macOS, and Linux
- π Performance: No system calls - pure JavaScript execution
- π Pipeline Support: All commands work in pipelines and virtual command chains
- βοΈ Option Aware: Commands respect
cwd,env, and other options - π‘οΈ Safe by Default: Proper error handling and safety checks (e.g.,
rmrequires-rfor directories) - π Bash Compatible: Error messages and behavior match bash/sh exactly
import { $ } from 'command-stream';
// All these work without any system dependencies!
await $`mkdir -p project/src`;
await $`touch project/src/index.js`;
await $`echo "console.log('Hello!');" > project/src/index.js`;
await $`ls -la project/src`;
await $`cat project/src/index.js`;
await $`cp -r project project-backup`;
await $`rm -r project-backup`;
// Mix built-ins with pipelines and virtual commands
await $`seq 1 5 | cat > numbers.txt`;
await $`basename /path/to/file.txt .txt`; // β "file"Installation
# Using npm
npm install command-stream
# Using bun
bun add command-streamUsage Patterns
Classic Await (Backward Compatible)
import { $ } from 'command-stream';
const result = await $`ls -la`;
console.log(result.stdout);
console.log(result.code); // exit codeExecution Control (NEW!)
import { $ } from 'command-stream';
// Commands don't auto-start when created
const cmd = $`echo "hello"`;
// Three ways to start execution:
// 1. Explicit start with options
cmd.start(); // Default async mode
cmd.start({ mode: 'async' }); // Explicitly async
cmd.start({ mode: 'sync' }); // Synchronous execution
// 2. Convenience methods
cmd.async(); // Same as start({ mode: 'async' })
cmd.sync(); // Same as start({ mode: 'sync' })
// 3. Auto-start by awaiting (always async)
await cmd; // Auto-starts in async mode
// Event handlers can be attached before starting
const process = $`long-command`
.on('data', chunk => console.log('Received:', chunk))
.on('end', result => console.log('Done!'));
// Start whenever you're ready
process.start();Synchronous Execution
import { $ } from 'command-stream';
// Use .sync() for blocking execution
const result = $`echo "hello"`.sync();
console.log(result.stdout); // "hello\n"
// Events still work but are batched after completion
$`echo "world"`
.on('end', result => console.log('Done:', result))
.sync();Async Iteration (Real-time Streaming)
import { $ } from 'command-stream';
for await (const chunk of $`long-running-command`.stream()) {
if (chunk.type === 'stdout') {
console.log('Real-time output:', chunk.data.toString());
}
}EventEmitter Pattern (Event-driven)
import { $ } from 'command-stream';
// Attach event handlers then start execution
$`command`
.on('data', chunk => {
if (chunk.type === 'stdout') {
console.log('Stdout:', chunk.data.toString());
}
})
.on('stderr', chunk => console.log('Stderr:', chunk))
.on('end', result => console.log('Done:', result))
.on('exit', code => console.log('Exit code:', code))
.start(); // Explicitly start the command
// Or auto-start by awaiting
const cmd = $`another-command`
.on('data', chunk => console.log(chunk));
await cmd; // Auto-starts in async modeMixed Pattern (Best of Both Worlds)
import { $ } from 'command-stream';
// Async mode - events fire in real-time
const process = $`streaming-command`;
process.on('data', chunk => {
processRealTimeData(chunk);
});
const result = await process;
console.log('Final output:', result.stdout);
// Sync mode - events fire after completion (batched)
const syncCmd = $`another-command`;
syncCmd.on('end', result => {
console.log('Completed with:', result.stdout);
});
const syncResult = syncCmd.sync();Shell Replacement (.sh β .mjs)
Replace bash scripts with JavaScript while keeping shell semantics:
import { $, shell, set, unset } from 'command-stream';
// set -e equivalent: exit on any error
shell.errexit(true);
await $`mkdir -p build`;
await $`npm run build`;
// set +e equivalent: allow errors (like bash)
shell.errexit(false);
const cleanup = await $`rm -rf temp`; // Won't throw if fails
// set -e again for critical operations
shell.errexit(true);
await $`cp -r build/* deploy/`;
// Other bash-like settings
shell.verbose(true); // set -v: print commands
shell.xtrace(true); // set -x: trace execution
// Or use the bash-style API
set('e'); // set -e
unset('e'); // set +e
set('x'); // set -x
set('verbose'); // Long form also supportedCross-Platform File Operations (Built-in Commands)
Replace system-dependent operations with built-in commands that work identically everywhere:
import { $ } from 'command-stream';
// File system operations work on Windows, macOS, and Linux identically
await $`mkdir -p project/src project/tests`;
await $`touch project/src/index.js project/tests/test.js`;
// List files with details
const files = await $`ls -la project/src`;
console.log(files.stdout);
// Copy and move operations
await $`cp project/src/index.js project/src/backup.js`;
await $`mv project/src/backup.js project/backup.js`;
// File content operations
await $`echo "export default 'Hello World';" > project/src/index.js`;
const content = await $`cat project/src/index.js`;
console.log(content.stdout);
// Path operations
const filename = await $`basename project/src/index.js .js`; // β "index"
const directory = await $`dirname project/src/index.js`; // β "project/src"
// Generate sequences and process them
await $`seq 1 10 | cat > numbers.txt`;
const numbers = await $`cat numbers.txt`;
// Cleanup
await $`rm -r project numbers.txt`;Virtual Commands (Extensible Shell)
Create custom commands that work seamlessly alongside built-ins:
import { $, register, unregister, listCommands } from 'command-stream';
// Register a custom command
register('greet', async (args, stdin) => {
const name = args[0] || 'World';
return { stdout: `Hello, ${name}!\n`, code: 0 };
});
// Use it like any other command
await $`greet Alice`; // β "Hello, Alice!"
await $`echo "Bob" | greet`; // β "Hello, Bob!"
// Streaming virtual commands with async generators
register('countdown', async function* (args) {
const start = parseInt(args[0] || 5);
for (let i = start; i >= 0; i--) {
yield `${i}\n`;
await new Promise(r => setTimeout(r, 1000));
}
});
// Use in pipelines with built-ins
await $`countdown 3 | cat > countdown.txt`;
// Virtual commands work in all patterns
for await (const chunk of $`countdown 3`.stream()) {
console.log('Countdown:', chunk.data.toString().trim());
}
// Management functions
console.log(listCommands()); // List all registered commands
unregister('greet'); // Remove custom commandsπ₯ Why Virtual Commands Are Revolutionary
No other shell library offers this level of extensibility:
- π« Bun.$: Fixed set of built-in commands, no extensibility API
- π« execa: Transform/pipeline system, but no custom commands
- π« zx: JavaScript functions only, no shell command integration
command-stream breaks the barrier between JavaScript functions and shell commands:
// β Other libraries: Choose JavaScript OR shell
await execa('node', ['script.js']); // execa: separate processes
await $`node script.js`; // zx: shell commands only
// β
command-stream: JavaScript functions AS shell commands
register('deploy', async (args) => {
const env = args[0] || 'staging';
await deployToEnvironment(env);
return { stdout: `Deployed to ${env}!\n`, code: 0 };
});
await $`deploy production`; // JavaScript function as shell command!
await $`deploy staging | tee log.txt`; // Works in pipelines!Unique capabilities:
- Seamless Integration: Virtual commands work exactly like built-ins
- Pipeline Support: Full stdin/stdout passing between virtual and system commands
- Streaming: Async generators for real-time output
- Dynamic Registration: Add/remove commands at runtime
- Option Awareness: Virtual commands respect
cwd,env, etc.
π Advanced Pipeline Support
command-stream offers the most advanced piping system in the JavaScript ecosystem:
Shell-Style Piping (Traditional)
import { $, register } from 'command-stream';
// β
Standard shell piping (like all libraries)
await $`echo "hello world" | wc -w`; // β "2"
// β
Built-in to built-in piping
await $`seq 1 5 | cat > numbers.txt`;
// β
System to built-in piping
await $`git log --oneline | head -n 5`;
// π UNIQUE: Virtual command piping
register('uppercase', async (args, stdin) => {
return { stdout: stdin.toUpperCase(), code: 0 };
});
register('reverse', async (args, stdin) => {
return { stdout: stdin.split('').reverse().join(''), code: 0 };
});
// β
Built-in to virtual piping
await $`echo "hello" | uppercase`; // β "HELLO"
// β
Virtual to virtual piping
await $`echo "hello" | uppercase | reverse`; // β "OLLEH"
// β
Mixed pipelines (system + built-in + virtual)
await $`git log --oneline | head -n 3 | uppercase | cat > LOG.txt`;
// β
Complex multi-stage pipelines
await $`find . -name "*.js" | head -n 10 | basename | sort | uniq`;π Programmatic .pipe() Method (NEW!)
World's first shell library with full .pipe() method support for virtual commands:
import { $, register } from 'command-stream';
// β
Basic programmatic piping
const result = await $`echo "hello"`.pipe($`echo "World: $(cat)"`);
// π Virtual command chaining
register('add-prefix', async (args, stdin) => {
const prefix = args[0] || 'PREFIX:';
return { stdout: `${prefix} ${stdin.trim()}\n`, code: 0 };
});
register('add-suffix', async (args, stdin) => {
const suffix = args[0] || 'SUFFIX';
return { stdout: `${stdin.trim()} ${suffix}\n`, code: 0 };
});
// β
Chain virtual commands with .pipe()
const result = await $`echo "Hello"`
.pipe($`add-prefix "[PROCESSED]"`)
.pipe($`add-suffix "!!!"`);
// β "[PROCESSED] Hello !!!"
// β
Mix with built-in commands
const fileData = await $`cat large-file.txt`
.pipe($`head -n 100`)
.pipe($`add-prefix "Line:"`);
// β
Error handling in pipelines
try {
const result = await $`cat nonexistent.txt`.pipe($`add-prefix "Data:"`);
} catch (error) {
// Source error propagates - destination never executes
console.log('File not found, pipeline stopped');
}
// β
Complex data processing
register('json-parse', async (args, stdin) => {
try {
const data = JSON.parse(stdin);
return { stdout: JSON.stringify(data, null, 2), code: 0 };
} catch (error) {
return { stdout: '', stderr: `JSON Error: ${error.message}`, code: 1 };
}
});
register('extract-field', async (args, stdin) => {
const field = args[0];
try {
const data = JSON.parse(stdin);
const value = data[field] || 'null';
return { stdout: `${value}\n`, code: 0 };
} catch (error) {
return { stdout: '', stderr: `Extract Error: ${error.message}`, code: 1 };
}
});
// Real-world API processing pipeline
const userName = await $`curl -s https://api.github.com/users/octocat`
.pipe($`json-parse`)
.pipe($`extract-field name`);
// β "The Octocat"
// Cleanup
unregister('add-prefix');
unregister('add-suffix');
unregister('json-parse');
unregister('extract-field');π How We Compare
| Library | Pipeline Types | Custom Commands in Pipes | .pipe() Method |
Real-time Streaming |
|---|---|---|---|---|
| command-stream | β System + Built-ins + Virtual + Mixed | β Full support | β Full virtual command support | β Yes |
| Bun.$ | β System + Built-ins | β No custom commands | β No .pipe() method |
β No |
| execa | β
Programmatic .pipe() |
β No shell integration | β Basic process piping | π‘ Limited |
| zx | β
Shell piping + .pipe() |
β No custom commands | β Stream piping only | β No |
π― Unique Advantages:
- Virtual commands work seamlessly in both shell pipes AND
.pipe()method - no other library can do this - Mixed pipeline types - combine system, built-in, and virtual commands freely in both syntaxes
- Real-time streaming through virtual command pipelines
- Full stdin/stdout passing between all command types
- Dual piping syntax - use shell
|OR programmatic.pipe()interchangeably
Default Behavior: Shell-like with Programmatic Control
command-stream behaves exactly like running commands in your shell by default:
import { $ } from 'command-stream';
// This command will:
// 1. Print "Hello" to your terminal (stdoutβstdout)
// 2. Print "Error!" to your terminal (stderrβstderr)
// 3. Capture both outputs for programmatic access
const result = await $`sh -c "echo 'Hello'; echo 'Error!' >&2"`;
console.log('Captured stdout:', result.stdout); // "Hello\n"
console.log('Captured stderr:', result.stderr); // "Error!\n"
console.log('Exit code:', result.code); // 0Key Default Options:
mirror: true- Live output to terminal (like shell)capture: true- Capture output for later use (unlike shell)stdin: 'inherit'- Inherit stdin from parent process
Fully Controllable:
import { $, create, sh } from 'command-stream';
// Disable terminal output but still capture
const result = await sh('echo "silent"', { mirror: false });
// Custom stdin input
const custom = await sh('cat', { stdin: "custom input" });
// Create custom $ with different defaults
const quiet$ = create({ mirror: false });
await quiet$`echo "silent"`; // Won't print to terminal
// Disable both mirroring and capturing for performance
await sh('make build', { mirror: false, capture: false });This gives you the best of both worlds: shell-like behavior by default, but with full programmatic control and real-time streaming capabilities.
Real-world Examples
Log File Streaming with Session ID Extraction
import { $ } from 'command-stream';
import { appendFileSync, writeFileSync } from 'fs';
let sessionId = null;
let logFile = null;
for await (const chunk of $`your-streaming-command`.stream()) {
if (chunk.type === 'stdout') {
const data = chunk.data.toString();
// Extract session ID from output
if (!sessionId && data.includes('session_id')) {
try {
const parsed = JSON.parse(data);
sessionId = parsed.session_id;
logFile = `${sessionId}.log`;
console.log(`Session ID: ${sessionId}`);
} catch (e) {
// Handle JSON parse errors
}
}
// Write to log file in real-time
if (logFile) {
appendFileSync(logFile, data);
}
}
}Progress Monitoring
import { $ } from 'command-stream';
let progress = 0;
$`download-large-file`
.on('stdout', (chunk) => {
const output = chunk.toString();
if (output.includes('Progress:')) {
progress = parseProgress(output);
updateProgressBar(progress);
}
})
.on('end', (result) => {
console.log('Download completed!');
});API Reference
ProcessRunner Class
The enhanced $ function returns a ProcessRunner instance that extends EventEmitter.
Events
data: Emitted for each chunk with{type: 'stdout'|'stderr', data: Buffer}stdout: Emitted for stdout chunks (Buffer)stderr: Emitted for stderr chunks (Buffer)end: Emitted when process completes with final result objectexit: Emitted with exit code
Methods
start(options): Explicitly start command executionoptions.mode:'async'(default) or'sync'- execution mode
async(): Shortcut forstart({ mode: 'async' })- start async executionsync(): Shortcut forstart({ mode: 'sync' })- execute synchronously (blocks until completion)stream(): Returns an async iterator for real-time chunk processingpipe(destination): Programmatically pipe output to another command (returns new ProcessRunner)then(),catch(),finally(): Promise interface for await support (auto-starts in async mode)
Properties
stdout: Direct access to child process stdout streamstderr: Direct access to child process stderr streamstdin: Direct access to child process stdin stream
Default Options
By default, command-stream behaves like running commands in the shell:
{
mirror: true, // Live output to terminal (stdoutβstdout, stderrβstderr)
capture: true, // Capture output for programmatic access
stdin: 'inherit' // Inherit stdin from parent process
}Option Details:
mirror: boolean- Whether to pipe output to terminal in real-timecapture: boolean- Whether to capture output in result objectstdin: 'inherit' | 'ignore' | string | Buffer- How to handle stdincwd: string- Working directory for commandenv: object- Environment variables
Override defaults:
- Use
sh(command, options)for one-off overrides - Use
create(defaultOptions)to create custom$with different defaults
Shell Settings API
Control shell behavior like bash set/unset commands:
Functions
shell.errexit(boolean): Enable/disable exit-on-error (likeset Β±e)shell.verbose(boolean): Enable/disable command printing (likeset Β±v)shell.xtrace(boolean): Enable/disable execution tracing (likeset Β±x)set(option): Enable shell option ('e','v','x', or long names)unset(option): Disable shell optionshell.settings(): Get current settings object
Virtual Commands API
Control and extend the command system with custom JavaScript functions:
Functions
register(name, handler): Register a virtual commandname: Command name (string)handler: Function or async generator(args, stdin, options) => result
unregister(name): Remove a virtual commandlistCommands(): Get array of all registered command namesenableVirtualCommands(): Enable virtual command processingdisableVirtualCommands(): Disable virtual commands (use system commands only)
Handler Function Signature
// Regular async function
async function handler(args, stdin, options) {
return {
code: 0, // Exit code (number)
stdout: "output", // Standard output (string)
stderr: "", // Standard error (string)
};
}
// Async generator for streaming
async function* streamingHandler(args, stdin, options) {
yield "chunk1\n";
yield "chunk2\n";
// Each yield sends a chunk in real-time
}Built-in Commands
18 cross-platform commands that work identically everywhere:
File System: cat, ls, mkdir, rm, mv, cp, touch
Utilities: basename, dirname, seq, yes
System: cd, pwd, echo, sleep, true, false, which, exit, env, test
All built-in commands support:
- Standard flags (e.g.,
ls -la,mkdir -p,rm -rf) - Pipeline operations
- Option awareness (
cwd,env, etc.) - Bash-compatible error messages and exit codes
Supported Options
'e'/'errexit': Exit on command failure'v'/'verbose': Print commands before execution'x'/'xtrace': Trace command execution with+prefix'u'/'nounset': Error on undefined variables (planned)'pipefail': Pipe failure detection (planned)
Result Object
{
code: number, // Exit code
stdout: string, // Complete stdout output
stderr: string, // Complete stderr output
stdin: string, // Input sent to process
child: ChildProcess, // Original child process object
async text() // Bun.$ compatibility method - returns stdout as string
}.text() Method (Bun.$ Compatibility)
For compatibility with Bun.$, all result objects include an async .text() method:
import { $ } from 'command-stream';
// Both sync and async execution support .text()
const result1 = await $`echo "hello world"`;
const text1 = await result1.text(); // "hello world\n"
const result2 = $`echo "sync example"`.sync();
const text2 = await result2.text(); // "sync example\n"
// .text() is equivalent to accessing .stdout
expect(await result.text()).toBe(result.stdout);
// Works with built-in commands
const result3 = await $`seq 1 3`;
const text3 = await result3.text(); // "1\n2\n3\n"
// Works with .pipe() method
const result4 = await $`echo "pipe test"`.pipe($`cat`);
const text4 = await result4.text(); // "pipe test\n"Testing
# Run comprehensive test suite (266 tests)
bun test
# Run tests with coverage report
bun test --coverage
# Run specific test categories
npm run test:features # Feature comparison tests
npm run test:builtin # Built-in commands tests
npm run test:pipe # .pipe() method tests
npm run test:sync # Synchronous execution testsRequirements
- Bun: >= 1.0.0 (primary runtime)
- Node.js: >= 20.0.0 (compatibility support)
Roadmap
π Coming Soon
- TypeScript Support: Full .d.ts definitions and type safety
- Enhanced Shell Options:
set -u(nounset) andset -o pipefailsupport - More Built-in Commands: Additional cross-platform utilities
π‘ Planned Features
- Performance Optimizations: Further Bun runtime optimizations
- Advanced Error Handling: Enhanced error context and debugging
- Plugin System: Extensible architecture for custom integrations
Contributing
We welcome contributions! Since command-stream is public domain software, your contributions will also be released into the public domain.
π Getting Started
git clone https://github.com/link-foundation/command-stream.git
cd command-stream
bun install
bun test # Run the full test suiteπ Development Guidelines
- All features must have comprehensive tests
- Built-in commands should match bash/sh behavior exactly
- Maintain cross-platform compatibility (Windows, macOS, Linux)
- Follow the existing code style and patterns
π§ͺ Running Tests
bun test # All 266 tests
bun test tests/pipe.test.mjs # Specific test file
npm run test:builtin # Built-in commands onlyLicense - Our Biggest Advantage
The Unlicense (Public Domain)
Unlike other shell utilities that require attribution (MIT, Apache 2.0), command-stream is released into the public domain. This means:
- β No attribution required - Use it without crediting anyone
- β No license files to include - Simplify your distribution
- β No restrictions - Modify, sell, embed, whatever you want
- β No legal concerns - It's as free as code can be
- β Corporate friendly - No license compliance overhead
This makes command-stream ideal for:
- Commercial products where license attribution is inconvenient
- Embedded systems where every byte counts
- Educational materials that can be freely shared
- Internal tools without legal review requirements
"This is free and unencumbered software released into the public domain."