Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
9a8a5cd
docs(project): fix create URL flag example (#42)
JerryNee Jul 2, 2026
7928b5c
fix(help): include request-timeout in global flag guidance (#41)
JerryNee Jul 2, 2026
5f7dba1
fix(auth): honor request timeout during configure (#52)
JerryNee Jul 2, 2026
547b5be
fix(history): reject fractional page sizes (#55)
JerryNee Jul 2, 2026
9102430
fix(target-url): treat trailing-dot hostnames as loopback in SSRF gua…
lxcario Jul 2, 2026
33b7848
fix(auth): preserve typed API error envelope when key verification fa…
lxcario Jul 2, 2026
2fd8f4c
fix(skill-nudge): require complete Codex managed section (#90)
ahndohun Jul 2, 2026
a524702
fix: align documented Node engine floor (#84)
naufalfx805-source Jul 2, 2026
497bf62
fix(pagination): reject fractional pagination flags (#40)
JerryNee Jul 2, 2026
9aa7ba9
fix(project): reject empty/whitespace-only --name in create and updat…
lxcario Jul 2, 2026
21dc760
fix(cli): validate --output uniformly across all command groups (#14)
Davidson3556 Jul 2, 2026
000c196
ci: add Node 20 to test and build matrix (#133)
lxcario Jul 2, 2026
5e0f7c4
fix(test): reject empty code get --out and strip code-file BOM (#34)
SahilRakhaiya05 Jul 2, 2026
dfdee5a
fix(prompt): preserve buffered input between prompts (#118)
cmdr-chara Jul 2, 2026
be9784a
fix(test): emit auto-minted idempotency-key under --output json in re…
ahndohun Jul 2, 2026
03c4f11
fix(agent): apply symlink fail-close guard to own-file --dry-run inst…
ahndohun Jul 2, 2026
03251d7
fix(test): stop run --all --wait from polling queued runs past the sh…
ahndohun Jul 2, 2026
7b7d80d
fix(credentials): strip CR/LF from values to prevent INI injection (#…
lxcario Jul 2, 2026
76fff29
fix(target-url): treat trailing-dot hostnames as loopback in SSRF gua…
lxcario Jul 2, 2026
8da07fb
fix(project): avoid password file reads in dry-run update (#54)
JerryNee Jul 2, 2026
e53257d
fix(cli): validate --request-timeout flag and de-duplicate its parser…
Davidson3556 Jul 2, 2026
cff19ae
fix(rerun): emit partial stdout on TimeoutError in single-FE rerun --…
Jul 3, 2026
6e7f2b3
fix(test): reject whitespace-only --name in test update (parity with …
lxcario Jul 5, 2026
dc4d337
fix(test): guard parseDuration --since against overflow with VALIDATI…
crypticsaiyan Jul 5, 2026
6b90ff4
feat(cli): respect NO_COLOR environment variable per no-color.org (#12)
lxcario Jul 5, 2026
205b1fb
fix(paginate): bound cursor loops without dropping empty pages (#48)
aldorizona10-glitch Jul 5, 2026
06d2c4d
fix(project): validate list flags before client setup (#150)
JerryNee Jul 5, 2026
495db2f
fix(test): validate list status before client setup (#152)
JerryNee Jul 5, 2026
25ed285
fix: reject blank project passwords (#139)
naufalfx805-source Jul 5, 2026
87a6dff
fix: reject directory code output paths (#140)
naufalfx805-source Jul 5, 2026
b5879de
fix: reject malformed api keys (#141)
naufalfx805-source Jul 5, 2026
c0b52f8
fix(setup): validate endpoint before key check (#149)
JerryNee Jul 5, 2026
0de372c
fix(http): map non-JSON 200 responses to a typed error envelope (#166)
Davidson3556 Jul 5, 2026
b675630
feat(test): JUnit XML report export for batch --wait runs (#96)
SahilRakhaiya05 Jul 5, 2026
38e0f19
fix(rerun): preserve exit code and escalate auth errors in batch reru…
crypticsaiyan Jul 5, 2026
e1d00f1
feat(agent): add kiro as an install target (#10)
lxcario Jul 5, 2026
3fd703f
fix(poll): emit partial run to stdout on TimeoutError in run --wait a…
Awad-de Jul 5, 2026
dcbcbee
fix(steps): surface run-scoped per-step error text and stepType (#167)
Andy00L Jul 5, 2026
9457583
feat(test): add "test diff <run-a> <run-b>" to isolate run-to-run reg…
Andy00L Jul 5, 2026
011208c
feat(agent): stamp installed skills with a version/hash marker and ad…
Andy00L Jul 5, 2026
f732fdb
fix(bundle): only sweep bundle-owned files in commit, preserving user…
kshitij-heizen Jul 5, 2026
93c621c
fix(rerun): reject explicit IDs with --all and --status/--skip-termin…
kshitij-heizen Jul 5, 2026
759e85c
docs(usage): add --debug example + exit codes to help text (#171)
mo01115285816-cyber Jul 5, 2026
5ff6399
fix(cli): route interactive prompts and prelude to stderr (keep stdou…
Davidson3556 Jul 5, 2026
17650be
fix: harden failure bundle artifact downloads (#60)
merlinsantiago982-cmd Jul 5, 2026
d72290d
fix(test): guard default artifact run id path (#172)
Lexiie Jul 5, 2026
2ddb03d
feat(cli): add runtime Node.js version check with clear error message…
lxcario Jul 5, 2026
8eb4acf
feat(agent): add windsurf as an install target (#29)
Davidson3556 Jul 5, 2026
768da46
feat(cli): honor HTTPS_PROXY/HTTP_PROXY/NO_PROXY behind corporate and…
Andy00L Jul 5, 2026
c6f946e
feat(cli): non-blocking "new version available" notice (24h-cached np…
Andy00L Jul 5, 2026
4b72461
feat(cli): add 'test flaky' repeat-run flaky-test detector (#132)
lxcario Jul 5, 2026
b9e9601
feat(test): add "test lint" offline plan/steps validator (#176)
Andy00L Jul 5, 2026
f7b10b7
fix(config): treat empty env vars as unset in loadConfig (#8)
SahilRakhaiya05 Jul 6, 2026
c6d37b7
fix(http): clear per-attempt timeout timers (#137)
JerryNee Jul 6, 2026
78ad224
chore(ci): bump marocchino/sticky-pull-request-comment (#187)
dependabot[bot] Jul 6, 2026
108a913
fix(batch): classify RequestTimeoutError as timeout in --all --wait f…
Awad-de Jul 6, 2026
8ac2ff0
feat(test): make "test wait" variadic — attach to several runs in one…
Andy00L Jul 6, 2026
e819e45
feat(agent): add GitHub Copilot as an install target (#194)
Davidson3556 Jul 6, 2026
edc31c8
feat(cli): add "testsprite doctor" environment diagnostic (#183)
Andy00L Jul 6, 2026
946f5b3
feat(cli): graceful termination signals + broken-pipe guard (#185)
Andy00L Jul 6, 2026
3305dfa
feat(test): add "test scaffold" to emit a schema-correct starter plan…
Andy00L Jul 6, 2026
95e3978
Merge branch 'main' into fix/rerun-wait-timeout-stdout-pr
Awad-de Jul 9, 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
15 changes: 11 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,32 +48,39 @@ jobs:
- run: npm run typecheck

test:
name: Unit Tests
name: Unit Tests (Node ${{ matrix.node-version }})
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20, 22]
steps:
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1

- name: Setup Node.js
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
node-version: 22
node-version: ${{ matrix.node-version }}
cache: 'npm'

- run: npm ci
- run: npm run build
- run: npm test
env:
CI: true

build:
name: Build
name: Build (Node ${{ matrix.node-version }})
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20, 22]
steps:
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1

- name: Setup Node.js
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
node-version: 22
node-version: ${{ matrix.node-version }}
cache: 'npm'

- run: npm ci
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ jobs:
fi

- name: Add Coverage PR Comment
uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2.9.4
uses: marocchino/sticky-pull-request-comment@5770ad5eb8f42dd2c4f34da00c94c5381e49af88 # v3.0.5
if: github.event_name == 'pull_request' && steps.coverage-summary.outputs.coverage_generated == 'true'
with:
recreate: true
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ All notable changes to `@testsprite/testsprite-cli` are documented here. The for

## [Unreleased]

### Added

- **JUnit XML report export for batch `--wait` runs.** `test run --all` and batch `test rerun` (`--all` or multiple test ids) accept `--report junit --report-file <path>` to write a CI-friendly XML sidecar after polling completes. `--output json` is unchanged; the report is written even when the batch exits non-zero. `--dry-run` writes a canned sample without network calls.
- **`testsprite test flaky <test-id>`** — repeat-run flaky-test detector. Replays a test N times (`--runs <n>`, default 5), aggregates the outcomes, and reports a stability verdict (`stable` / `flaky` / `failing`) plus the `runId` and `failureKind` of every attempt that did not pass. Replays run with auto-heal OFF (strict verbatim) so a healed drift can't mask a nondeterministic pass/fail. Exit code is 0 only when every attempt passed, so CI can gate a merge on flakiness (`testsprite test flaky <id> --runs 5 || exit 1`). Flags: `--runs <n>` (1–10), `--until-fail` (stop at the first non-passing attempt), `--timeout <s>` (per-attempt), and `--output json` for a machine-readable stability report. Frontend replays are free verbatim script replays; a one-line advisory is printed for backend tests, whose closure reruns may cost credits.

## [0.2.0] - 2026-06-29

### Added
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ the thread or in Discord is welcome.

## Prerequisites

- Node 20 or newer (development happens on Node 22).
- Node 20.19+, Node 22.13+, or Node 24+ (development happens on Node 22).

## Build from source

Expand Down
88 changes: 74 additions & 14 deletions DOCUMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Or run it without installing:
npx @testsprite/testsprite-cli --version
```

Requires **Node.js ≥ 20**.
Requires **Node.js 20.19+**, **22.13+**, or **24+**.

Confirm the binary works **without** configuring an API key:

Expand Down Expand Up @@ -112,15 +112,18 @@ testsprite agent install claude # install the skill for Claude Code
testsprite agent install codex # install into AGENTS.md for Codex (managed-section)
testsprite agent install cursor # .cursor/rules/testsprite-verify.mdc
testsprite agent install cline # .clinerules/testsprite-verify.md
testsprite agent install windsurf # .windsurf/rules/testsprite-verify.md
testsprite agent install antigravity # .agents/skills/testsprite-verify/SKILL.md
testsprite agent list # list all 5 targets with status + mode + path
testsprite agent install kiro # .kiro/skills/testsprite-verify/SKILL.md
testsprite agent install copilot # .github/instructions/testsprite-verify.instructions.md
testsprite agent list # list all 8 targets with status + mode + path
```

Supported targets: `claude` (GA), `codex` (experimental), `cursor` (experimental), `cline` (experimental), `antigravity` (experimental).
Supported targets: `claude` (GA), `codex` (experimental), `cursor` (experimental), `cline` (experimental), `antigravity` (experimental), `kiro` (experimental), `windsurf` (experimental), `copilot` (experimental).

The `codex` target uses **managed-section mode** — it writes only a sentinel-delimited section inside your existing `AGENTS.md`, so your project instructions are never clobbered. Re-running without `--force` replaces the section in-place; user content outside the sentinels is always preserved.

Re-running with `--force` on **own-file targets** (claude, cursor, cline, antigravity) backs up the existing file to `<path>.bak` first.
Re-running with `--force` on **own-file targets** (claude, cursor, cline, antigravity, kiro, windsurf, copilot) backs up the existing file to `<path>.bak` first.

## Command reference

Expand Down Expand Up @@ -319,10 +322,10 @@ testsprite test plan put test_xxxxxxxx --steps ./refined.plan.json --dry-run --o

#### `testsprite project create` / `project update`

Manage projects from the CLI. Both pre-flight `--target-url` against local addresses for fast feedback.
Manage projects from the CLI. Both pre-flight `--url` against local addresses for fast feedback.

```bash
testsprite project create --name "Checkout" --target-url https://staging.example.com
testsprite project create --type frontend --name "Checkout" --url https://staging.example.com
testsprite project update proj_xxxxxxxx --name "Checkout v2"
```

Expand All @@ -344,9 +347,19 @@ testsprite test run test_xxxxxxxx --target-url https://staging.example.com \

# Dry-run prints a canned queued response (no network, no credentials)
testsprite test run test_xxxxxxxx --dry-run --output json

# Batch BE run with JUnit XML for CI (sidecar; --output json unchanged)
testsprite test run --all --project proj_xxxxxxxx --wait \
--report junit --report-file ./results.xml --output json

# Optional custom suite name (default: testsprite:<projectId>)
testsprite test run --all --project proj_xxxxxxxx --wait \
--report junit --report-file ./results.xml --report-suite-name my-ci-suite --output json
```

`--target-url` must be a publicly reachable URL — the CLI pre-flights it against local addresses (`localhost`, `127.x`, `::1`, `0.0.0.0`, `169.254.x`, RFC1918) and the backend resolves it via DNS. For testing against localhost, use the [TestSprite MCP plugin](https://www.testsprite.com/docs), which handles the local tunnel. The CLI auto-mints an idempotency key (printed to stderr at `--verbose`); pass `--idempotency-key <uuid>` to control it explicitly.
Batch `--report` flags apply only to `test run --all --wait` (and batch `test rerun --wait`). `--report junit --report-file <path>` writes a JUnit XML sidecar after polling completes (atomic write); `--output json` is unchanged. Optional `--report-suite-name <name>` overrides the default `testsprite:<projectId>` suite name.

`--target-url` must be a publicly reachable URL — the CLI pre-flights it against local addresses (`localhost`, `127.x`, `::1`, `0.0.0.0`, `169.254.x`, RFC1918) and the backend resolves it via DNS. For testing against localhost, use the [TestSprite MCP plugin](https://www.testsprite.com/docs), which handles the local tunnel. The CLI auto-mints an idempotency key (printed to stderr under `--output json`, `--verbose`, or `--debug`); pass `--idempotency-key <uuid>` to control it explicitly.

#### `testsprite test rerun [test-id...]`

Expand All @@ -365,21 +378,55 @@ testsprite test rerun test_be_xxxx --skip-dependencies --output json
# Rerun every test in a project (batch)
testsprite test rerun --all --project proj_xxxxxxxx --wait --max-concurrency 4 --output json

# Batch rerun with JUnit XML for CI
testsprite test rerun --all --project proj_xxxxxxxx --wait \
--report junit --report-file ./results.xml --output json

# Optional custom suite name (default: testsprite:<projectId>)
testsprite test rerun --all --project proj_xxxxxxxx --wait \
--report junit --report-file ./results.xml --report-suite-name my-ci-suite --output json

# Several specific tests
testsprite test rerun test_aaaa test_bbbb --wait --output json
```

Batch `--report` flags apply only to batch `--wait` reruns (`--all` or multiple test ids). `--report junit --report-file <path>` writes a JUnit XML sidecar after polling completes (atomic write); `--output json` is unchanged. When `--project` is omitted, the CLI infers `projectId` from polled run rows for classname / default suite naming; if inference fails, pass `--project <id>` explicitly (required under `--dry-run`).

Flags:

- `--all` — rerun every test in the resolved project; requires `--project <id>`.
- `--wait`, `--timeout <s>` — block until terminal; same exit matrix as `test run --wait`.
- `--auto-heal` / `--no-auto-heal` — frontend AI heal-on-drift, **on by default** for FE reruns; opt out with `--no-auto-heal`. Verbatim-replay passes are free; a heal engage costs a small amount of credit. Ignored for backend tests.
- `--skip-dependencies` — backend only: rerun just the named test without expanding the producer/teardown closure.
- `--max-concurrency <n>` — with `--wait`, cap on in-flight polls during a batch rerun.
- `--idempotency-key <key>` — auto-minted when omitted.
- `--idempotency-key <key>` — auto-minted when omitted (the minted key is printed to stderr under `--output json`, `--verbose`, or `--debug`).
- `--report junit --report-file <path>` — with batch `--wait`, write a JUnit XML sidecar after polling (atomic write). Optional `--report-suite-name <name>` overrides the default `testsprite:<projectId>` suite name. Requires `--wait`; not available on single-test reruns.

A batch rerun returns `accepted[]` (one `runId` per dispatched test) plus `deferred[]` for any test shed by the per-key run-rate limit; under `--wait`, a non-empty `deferred[]` exits 7 with a `nextAction` you can retry with a fresh idempotency key.

#### `testsprite test flaky <test-id>`

Detect a **flaky** test by replaying it several times and reporting how often it passes. Each attempt is a rerun with auto-heal **off** (a strict verbatim replay), so healed drift can't disguise a nondeterministic pass/fail — this measures the replay stability of the saved script against the configured URL. Frontend replays are free verbatim script replays; backend tests re-run their dependency closure and may cost credits (a one-line stderr advisory is printed before the run).

```bash
# Replay 10 times and print a stability score
testsprite test flaky test_xxxxxxxx --runs 10

# Fast "is it flaky at all?" — stop at the first non-passing attempt
testsprite test flaky test_xxxxxxxx --runs 10 --until-fail

# Machine-readable stability report for CI
testsprite test flaky test_xxxxxxxx --runs 10 --output json
```

Flags:

- `--runs <n>` — number of replays (1–10, default 5).
- `--until-fail` — stop at the first attempt that does not pass.
- `--timeout <s>` — per-attempt polling deadline (same semantics as `test wait`).

`--output json` emits `{ testId, runs, passed, failed, stableRatio, verdict, failures: [{ attempt, runId, outcome, failureKind }] }`. Exit codes: **0** when every observed attempt passed (`stable`); **1** when any attempt did not pass (`flaky` or `failing`); **4** when the test has no replayable run (trigger `testsprite test run <id>` first); **5** on a validation error.

#### `testsprite test wait <run-id>`

Block until a run reaches a terminal status. Same exit-code matrix as `test run --wait`. Used to resume polling after a timed-out `test run --wait`, or when an agent already has a `runId` from a previous invocation.
Expand Down Expand Up @@ -426,12 +473,25 @@ These apply to every command:

### Environment variables

| Variable | Purpose |
| ------------------------------- | --------------------------------------------------------------------------------- |
| `TESTSPRITE_API_KEY` | API key — overrides the credentials file |
| `TESTSPRITE_API_URL` | API endpoint — overrides the credentials file |
| `TESTSPRITE_PROFILE` | Active profile (below `--profile`, above `default`) |
| `TESTSPRITE_REQUEST_TIMEOUT_MS` | Per-request timeout in **milliseconds** (default `120000`, range `1000`–`600000`) |
| Variable | Purpose |
| ------------------------------- | --------------------------------------------------------------------------------------- |
| `TESTSPRITE_API_KEY` | API key — overrides the credentials file |
| `TESTSPRITE_API_URL` | API endpoint — overrides the credentials file |
| `TESTSPRITE_PROFILE` | Active profile (below `--profile`, above `default`) |
| `TESTSPRITE_REQUEST_TIMEOUT_MS` | Per-request timeout in **milliseconds** (default `120000`, range `1000`–`600000`) |
| `TESTSPRITE_NO_UPDATE_NOTIFIER` | Any non-empty value disables the once-per-24h "new version available" notice |
| `NO_COLOR` | Suppress ANSI escape sequences in ticker output ([no-color.org](https://no-color.org/)) |

### Update notice

Interactive runs print a one-line "new version available" notice on stderr when
a newer release exists. To learn this, the CLI contacts the public npm registry
(`registry.npmjs.org`) at most once per 24 hours; the request carries the
package name only — never your API key, project data, or command line. The
check is skipped in CI, when stderr is not a TTY, under `--output json` /
`--dry-run`, and entirely when `TESTSPRITE_NO_UPDATE_NOTIFIER` is set. Any
failure is silent: the notice can never break or delay a command. This is the
only outbound call the CLI makes besides your configured API endpoint.

### Scopes

Expand Down
Loading