diff --git a/SPEC.md b/SPEC.md deleted file mode 100644 index de8f7fc..0000000 --- a/SPEC.md +++ /dev/null @@ -1,189 +0,0 @@ -# shout - -A transcript-based shell integration test runner. - -## Format - -A `.shout` file is a plain text transcript of a shell session. Lines starting -with `$ ` are commands. Everything after — until the next `$` or end of file -— is the expected output (stdout and stderr combined). - -``` -$ dev new "add auth" -created draft 1 "add auth" - -$ dev save -saved draft 1 (v1) -``` - -Blank lines within expected output are significant. Trailing newline on the -file is ignored. - -### Comments - -`#` after a command is a comment and is stripped before execution. Comments -in expected output are matched literally. - -``` -$ dev new "add auth" # create a draft from timeline HEAD -created draft 1 "add auth" -``` - -### Wildcards - -A `...` on its own line in expected output matches any number of lines -(including zero). - -``` -$ dev log -... -draft 1 "add auth" -``` - -A `...` inline matches any sequence of characters on that line. - -``` -$ dev status -draft 1 "add auth" (v...) -``` - -### Environment - -Each `.shout` file runs in a fresh temporary directory. The directory is -created before the first command and removed after the last (unless -`--keep` is passed). - -All commands in a file run in a single shell session (`/bin/sh`), so `cd`, -`export`, and other shell state persists between commands. - -The following environment variables are set for every command: - -| Variable | Value | -|---|---| -| `HOME` | the temp directory | -| `PATH` | inherited from host (or prepended via `--path`) | -| `CUE_DIR` | the temp directory | - -All other environment variables are inherited from the host unless explicitly -cleared with `--clean-env`. - -### Exit codes - -By default, a non-zero exit code fails the test regardless of output. To -assert a specific exit code, append `[N]` on the last line of expected output: - -``` -$ dev rm -error: draft 1 has children. use dev rm -f to cascade. -[1] -``` - -`[*]` accepts any non-zero exit code without asserting the value. - ---- - -## CLI - -``` -shout [options] [files|dirs...] -``` - -If no files are given, shout runs all `*.shout` files in the current directory -and subdirectories. Each command in each shout file is run sequentially -(unless `--parallel` is passed). - -### Options - -| Flag | Description | -|---|---| -| `--update` / `-u` | Rewrite expected output in-place with actual output | -| `--keep` / `-k` | Keep temp directories after run (printed to stderr) | -| `--clean-env` | Start with empty environment (only `PATH` and `CUE_DIR` set) | -| `--path ` | Prepend `` to `PATH` (repeatable) | -| `--timeout ` | Per-command timeout, e.g. `500ms`, `10s`, `1m` (default: `10s`) | -| `--verbose` / `-v` | Print each command as it runs | -| `--parallel` | Run files in parallel (implies all files run regardless of failures) | - -### Output - -Passing files print a single `.` per file. Failing files print a unified diff: - -``` -FAIL tests/auth.shout - - $ dev rm - - error: draft 1 has children. use dev rm -f to cascade. - + error: draft 1 has dependents. use dev rm -f to cascade. - [1] -``` - -Summary line at the end: - -``` -12 passed, 1 failed in 340ms -``` - ---- - -## Update mode - -`--update` rewrites the expected output sections of each `.shout` file with the -actual output from the run. Commands, comments, and whitespace are preserved. -Wildcard lines are left in place if the actual output matches them; they are -only replaced if the match fails. - -This makes it safe to run `shout --update` routinely after intentional output -changes — review the diff, commit if correct. - ---- - -## File layout - -``` -tests/ - auth.shout - drafts.shout - stack.shout -``` - -No special directory structure is required. `.shout` files can live anywhere. - ---- - -## Implementation notes - -- Bun + TypeScript -- Each file runs in a single `/bin/sh` session via `Bun.spawn` -- Stdout and stderr merged (same as a terminal) -- Shell state (`cd`, `export`, etc.) persists across commands within a file -- Commands are fed to the shell sequentially; output between commands is - captured by delimiting with sentinel `echo` statements -- shout exits `0` if all tests pass, `1` if any fail - ---- - -## Example - -``` -$ dev new "add auth" -created draft 1 "add auth" - -$ echo 'export function auth() {}' > auth.ts -$ dev save -saved draft 1 (v1) - -$ echo 'export function auth(token: string) {}' > auth.ts -$ dev save -saved draft 1 (v2) - -$ dev status -draft 1 "add auth" (v2) -modified: (none) - -$ dev new "add db" -created draft 2 "add db" - -$ dev rm 1 -error: draft 1 has children. use dev rm -f to cascade. -[1] -```