This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
just-bash is a TypeScript implementation of a bash interpreter with an in-memory virtual filesystem. Designed for AI agents needing a secure, sandboxed bash environment. No WASM dependencies allowed.
# Build & Lint
pnpm build # Build TypeScript (required before using dist/)
pnpm typecheck # Type check
pnpm lint:fix # Fix lint errors (biome)
pnpm knip # Check for unused exports/dependencies
# Testing
pnpm test:run # Run ALL tests (including spec tests)
pnpm test:unit # Run unit tests only (fast, no comparison/spec)
pnpm test:comparison # Run comparison tests only (uses fixtures)
pnpm test:comparison:record # Re-record comparison test fixtures
# Excluding spec tests (spec tests have known failures)
pnpm test:run --exclude src/spec-tests
# Run specific test file
pnpm test:run src/commands/grep/grep.basic.test.ts
# Run specific spec test file by name pattern
pnpm test:run src/spec-tests/spec.test.ts -t "arith.test.sh"
pnpm test:run src/spec-tests/spec.test.ts -t "array-basic.test.sh"
# Interactive shell
pnpm shell # Full network access
pnpm shell --no-network # No network
# Sandboxed CLI (read-only by default)
node ./dist/cli/just-bash.js -c 'ls -la' --root .
node ./dist/cli/just-bash.js -c 'cat package.json' --root .
node ./dist/cli/just-bash.js -c 'grep -r "TODO" src/' --root .The just-bash CLI provides a secure, sandboxed bash environment using OverlayFS:
# Execute inline script (read-only by default)
node ./dist/cli/just-bash.js -c 'ls -la && cat README.md | head -5' --root .
# Execute with JSON output
node ./dist/cli/just-bash.js -c 'echo hello' --root . --json
# Allow writes (writes stay in memory, don't affect real filesystem)
node ./dist/cli/just-bash.js -c 'echo test > /tmp/file.txt && cat /tmp/file.txt' --root . --allow-write
# Execute script file
node ./dist/cli/just-bash.js script.sh --root .
# Exit on first error
node ./dist/cli/just-bash.js -e -c 'false; echo "not reached"' --root .Options:
--root <path>- Root directory (default: current directory)--cwd <path>- Working directory in sandbox (default: /home/user/project)--allow-write- Enable write operations (writes stay in memory)--json- Output as JSON (stdout, stderr, exitCode)-e, --errexit- Exit on first error
Reads script from stdin, executes it, shows output. Prefer this over ad-hoc test files.
# Basic execution
echo 'echo hello' | pnpm dev:exec
# Compare with real bash
echo 'x=5; echo $((x + 3))' | pnpm dev:exec --real-bash
# Show parsed AST
echo 'for i in 1 2 3; do echo $i; done' | pnpm dev:exec --print-ast
# Multi-line script
echo 'arr=(a b c)
for x in "${arr[@]}"; do
echo "item: $x"
done' | pnpm dev:exec --real-bashInput Script → Parser (src/parser/) → AST (src/ast/) → Interpreter (src/interpreter/) → ExecResult
Parser (src/parser/): Recursive descent parser producing AST nodes
lexer.ts- Tokenizer with bash-specific handling (heredocs, quotes, expansions)parser.ts- Main parser orchestrating specialized sub-parsersexpansion-parser.ts- Parameter expansion, command substitution parsingcompound-parser.ts- if/for/while/case/function parsing
Interpreter (src/interpreter/): AST execution engine
interpreter.ts- Main execution loop, command dispatchexpansion.ts- Word expansion (parameter, brace, glob, tilde, command substitution)arithmetic.ts-$((...))and((...))evaluationconditionals.ts-[[ ]]and[ ]test evaluationcontrol-flow.ts- Loops and conditionals executionbuiltins/- Shell builtins (export, local, declare, read, etc.)
Commands (src/commands/): External command implementations
- Each command in its own directory with implementation + tests
- Registry pattern via
registry.ts
Filesystem (src/fs.ts, src/overlay-fs/): In-memory VFS with optional overlay on real filesystem
AWK (src/commands/awk/): AWK text processing implementation
parser.ts- Parses AWK programs (BEGIN/END blocks, rules, user-defined functions)executor.ts- Executes parsed AWK programs line by lineexpressions.ts- Expression evaluation (arithmetic, string functions, comparisons)- Supports: field splitting, pattern matching, printf, gsub/sub/split, user-defined functions
- Limitations: User-defined functions support single return expressions only (no multi-statement bodies or if/else)
SED (src/commands/sed/): Stream editor implementation
parser.ts- Parses sed commands and addressesexecutor.ts- Executes sed commands with pattern/hold space- Supports: s, d, p, q, n, a, i, c, y, =, addresses, ranges, extended regex (-E/-r)
- Has execution limits to prevent runaway compute
Commands go in src/commands/<name>/ with:
- Implementation file with usage statement
- Unit tests (collocated
*.test.ts) - Error on unknown options (unless real bash ignores them)
- Comparison tests in
src/comparison-tests/for behavior validation
- Unit tests: Fast, isolated tests for specific functionality
- Comparison tests: Compare just-bash output against recorded bash fixtures (see
src/comparison-tests/README.md) - Spec tests (
src/spec-tests/): Bash specification conformance (may have known failures)
Prefer comparison tests when uncertain about bash behavior. Keep test files under 300 lines.
Comparison tests use pre-recorded bash outputs stored in src/comparison-tests/fixtures/. This eliminates platform differences (macOS vs Linux). See src/comparison-tests/README.md for details.
# Run comparison tests (uses fixtures, no real bash needed)
pnpm test:comparison
# Re-record fixtures (skips locked fixtures)
RECORD_FIXTURES=1 pnpm test:run src/comparison-tests/mytest.comparison.test.ts
# Force re-record including locked fixtures
RECORD_FIXTURES=force pnpm test:comparisonWhen adding comparison tests:
- Write the test using
setupFiles()andcompareOutputs() - Run with
RECORD_FIXTURES=1to generate fixtures - Commit both the test file and the generated fixture JSON
- If manually adjusting for Linux behavior, add
"locked": trueto the fixture
- Read AGENTS.md
- Use
pnpm dev:execinstead of ad-hoc test scripts (avoids approval prompts) - Always verify with
pnpm typecheck && pnpm lint:fix && pnpm knip && pnpm test:runbefore finishing - Assert full stdout/stderr in tests, not partial matches
- Implementation must match real bash behavior, not convenience
- Dependencies using WASM are not allowed (exception: sql.js for SQLite, approved for security sandboxing)
- We explicitly don't support 64-bit integers
- All parsing/execution must have reasonable limits to prevent runaway compute