# shout

Transcript-based shell integration test runner. Bun + TypeScript.

## Commands

- `bun test` — run unit tests
- `bunx tsc --noEmit` — type check
- `bun run src/cli/index.ts test [files...]` — run shout CLI
  - `-u, --update` — rewrite `.shout` files with actual output
  - `-k, --keep` — keep temp directories after run
  - `--clean-env` — start with empty environment
  - `--path <path>` — prepend to `$PATH` (repeatable)
  - `--timeout <dur>` — per-command timeout (default `10s`)
  - `-t, --filter <pattern>` — only run files whose path contains `<pattern>`
  - `-v, --verbose` — print each command as it runs
  - `--port-from <n>` — auto-assign `$PORT` starting from n (default `5400`)
  - `--parallel` — run files in parallel

## Architecture

- `src/parse.ts` — parses `.shout` files into `ShoutFile` (list of `Command`)
- `src/run.ts` — executes commands via `Bun.spawn(["/bin/sh"], { detached: true })`, captures output with sentinels
- `src/match.ts` — wildcard-aware output matching and diff generation
- `src/format.ts` — evaluates pass/fail, formats failures and summary
- `src/update.ts` — rewrites `.shout` files with actual output (`--update` mode)
- `src/utils.ts` — shared utilities (`trimTrailingEmpty`, `escapeRegex`)
- `src/duration.ts` — parses duration strings (`10s`, `500ms`, `1m`)
- `src/cli/index.ts` — CLI entry point via `commander`
- `src/index.ts` — barrel exports
- `web/index.html` — web documentation page

## .shout file format

- `$ ` prefix = command to execute
- Lines between commands = expected output (stdout+stderr merged)
- `...` on its own line = multi-line wildcard (matches zero or more lines)
- `...` inline = matches any characters on that line
- `[N]` on last line of expected output = assert exit code N
- `[*]` = assert any non-zero exit code; default expects 0
- `$#` comment line = not executed, no output expected (e.g. `$# start the server`)
- `#` after a command = comment (stripped); `#` in expected output is literal
- `@env KEY=VALUE` before first command = set environment variable
- `@teardown <command>` before first command = run command after all test commands
  - Runs regardless of pass/fail
  - Teardown failures produce warnings but don't affect test results
  - Can appear in both `.shout` files and setup files
- `@setup path.shout` before first command = prepend commands (and `@env`) from another file
  - Setup files use a plain format: each line is a command (no `$ ` prefix), `#` lines are comments, blank lines ignored
  - Setup files can contain `@env` and `@teardown` directives but not `@setup` (no nesting)
  - User file `@env` overrides setup file `@env`
  - Setup command failures abort the test with an error
- Each file runs in a fresh temp dir with a single `/bin/sh` session
- `$HOME` and `$SHOUT_DIR` are set to the temp dir automatically
- `$SHOUT_SOURCE_DIR` is set to the directory containing the `.shout` file
- `$SHOUT_PROJECT_DIR` is set to `cwd` where `shout` was invoked
- stdout and stderr are merged (`exec 2>&1`)

## New feature checklist

1. `src/parse.ts` — update types (`Directive`, `ShoutFile`, `Command`) and both parsers (`parse` + `parseSetup`)
2. `src/parse.test.ts` — unit tests for parsing the new syntax in both `.shout` and setup file contexts
3. `src/cli/index.ts` — wire up the parsed result in `runOne` (directive resolution, command merging, result handling)
4. `test/*.shout` — integration test file exercising the feature end-to-end
5. `CLAUDE.md` — update `.shout file format` section
6. `README.md` — update Directives section
7. `web/index.html` — add or update a section on the website
8. Run `bun test` and `bun run src/cli/index.ts test test/` to verify

## Style

- Strict TypeScript, Bun runtime
- No classes — plain functions and types
- Tests in `src/*.test.ts`, example `.shout` files in `test/`
