| Name | Modified | Size | Downloads / Week |
|---|---|---|---|
| Parent folder | |||
| README.md | 2026-09-03 | 10.4 kB | |
| v0.9.0 source code.tar.gz | 2026-09-03 | 1.2 MB | |
| v0.9.0 source code.zip | 2026-09-03 | 1.3 MB | |
| Totals: 3 Items | 2.5 MB | 0 | |
Added
test getshows the plan steps. It printed onlyplanStepCount, so a test's actual steps were invisible from the CLI.testsprite test get <test-id>now lists them, numbered, as[type] description, and--output jsoncarriesplanStepsas the same{type, description}objectstest createaccepts — V2 and V3 tests alike. The list needs a backend that serves the field; an older backend simply omits it and the count-only line is unchanged. A malformed step from the server renders as[step]/(no description)rather than failing the command.
Changed
testsprite setupinstalls for the agents a project actually uses. It installed forclaudewhenever--agentwas omitted, so a caller using any of the other seven targets got skill files that agent cannot read — and a success message, which made the miss silent.setupnow works out which agents to install for. An explicit--agentstill wins. Otherwise it installs for every agent it finds evidence of — the one calling it, when the environment identifies itself, together with every agent whose configuration is already in the project — and prints what it found and what each match was based on. The two kinds of evidence add up rather than override, so running it from Claude Code in a repo that also holds Cursor and Cline config wires all three: the agent you happen to be using says nothing about the others working in the same repo. In a terminal that set is shown for confirmation first, pre-filled — press enter to accept it, or give a comma-separated list to narrow it.--yes, non-interactive shells and--output jsoninstall it as found. When nothing is detected it installs forclaudeand says so, along with the flag to choose differently. Configuration this CLI wrote itself is never treated as evidence, so a fallback install does not turn into a permanent answer on the next run.--agentno longer has a default value, and under--output jsonthe summary carriesagent.targets,agent.detections(one{target, source, signal}row per detected agent) andagent.detectedBy(flag,detectedorfallback);agent.targetstill holds the first target.agent installwith no--targetresolves the same way — installing for what it detects, prompting with that set pre-filled on a terminal, and naming theclaudefallback when it finds nothing — so no command installs for a fixed agent when it was not told which. At the confirmation prompt an unrecognised name is refused and asked again — up to three tries — before anything is written, instead of being accepted and failing only after credentials were saved; answeringnoneskips the skill install the way--no-agentdoes, and the prompt says so. The[info]line before the prompt now reports only what was detected; the set actually being installed is announced once the answer is in. The summary carriesagentSkippedBy(flagorprompt) saying whyagentis null when the install was skipped; under--output jsononlyflagoccurs, because that mode never prompts. An unknown--agentvalue is likewise refused before anything is written, and the error names--agent— it used to surface as a--targeterror, a flagsetupdoes not have, only after credentials had been saved.
Fixed
test run <id>andtest rerun <id>now print the dashboard link right away. Without--wait, the queued-run output ended attargetUrland gave you nothing to open — the link only appeared after a full--wait, or fromtest open. Both outputs now close with adashboardline whenever the server supplies one (the backend builds it for the store that actually dispatched the run, so it is the same linktest waitwould print — never the V2 template that 404s for a V3 workspace). Under--output jsonthe envelope also carriesexecutionUrl, the run-scoped result page, on V3 runs. Both keys are present-or-absent: an older backend, or a run the server cannot link, simply omits them and nothing is printed.test run --localno longer refuses a run because its own tunnel was not ready yet. Bringing the tunnel up resolved as soon as the WebSocket opened — before the client had even sent its authentication frame, let alone had it acknowledged.--localdoes exactly this sequence (open the tunnel, then trigger the run), so the run could be refused as "tunnel offline" while the tunnel was in fact coming up correctly, and the message told you to start a tunnel you had already started. Being a race, it failed on a cold start and passed on the retry — the worst shape for a feature whose whole job is to be trusted about your local app. Startup now completes only once the server has acknowledged authentication, and it fails with a specific message (rather than hanging) if the server accepts the connection but never acknowledges. Readiness is tracked per connection, so a reconnect re-arms it and a stale acknowledgement from an earlier socket cannot satisfy a fresh start.- Localhost errors no longer tell you to do something you cannot do. Two messages a developer running an app on
localhostmeets early both named a remedy that does not exist. Rejecting a loopback project URL suggestedtestsprite test run <test-id> --local <port>— butproject create,project update,test create,test create --plan-fromandtest create-batchall run that check before a test id exists, andproject createbefore there is even a project. Those five now explain it in the order you can act on: TestSprite runs your tests from the cloud, so the project's URL has to be one the runner can reach — point it at your deployed or staging URL; then, once a test exists, aim a single run at the app on your machine with--local <port>, which is per-run and does not change the project's URL. Separately, when a tunnel run refused to dial a private/LAN/VPN address it told you to setTS_TUNNEL_ALLOW_PRIVATE_NETWORK_TARGET=true— a variable the CLI never reads and cannot be made to honour. That message now states the real policy (a tunnel run reacheslocalhost/127.0.0.1/::1on your machine and the public internet, and refuses private addresses deliberately) and points at making the dependency reachable instead. No guard changed what it accepts or rejects; only the advice was wrong. - A borrowed tunnel that dies ends the run instead of waiting out
--timeout.test run --local --tunnel-client <id>attaches to a tunnel another terminal owns, and deliberately never stops or deletes it. The side effect was that the borrowing run could not see that tunnel disappear: if the owner pressed Ctrl-C or rantunnel stop, the run kept polling a doomed run until--timeoutexpired. The run now checks the borrowed tunnel — at most once every 15 seconds, and never on a run that has already finished — and ends with the sameUNAVAILABLEexit code (10) the owned path uses. Only an explicit offline status or a "no such tunnel" answer counts: a network failure, a timeout, or a server error is treated as unknown and retried later, so a blip in our own read can never fail your run. The message is written for this case specifically — it says the tunnel was minted by another process and points at restarting it or dropping--tunnel-client, rather than repeating the owned path's advice to retry (which mints nothing when you name an existing tunnel). - The missing-verification-skill warning now judges from the calling agent's view. It fell silent as soon as any supported agent had the skill, so a project set up for one agent read as correctly wired to all eight — exactly the case where an agent drives the CLI with no skill of its own and nothing says so. When the environment identifies the caller, only that agent's install counts, and the warning states that the skill is installed but not for it. A caller the environment cannot identify keeps the any-agent answer, which is all it can support.
- Refusing a
localhostURL names the flag you actually typed.project create --url http://localhost:3000saidField `target-url` is invalidand pointed attestsprite test run --help— a flag and a command the caller never used. Every caller now names its own field and help command:url/project createandproject update,endpoint-url/ci init,upload-url/project docs upload,target-url/test create,test create-batchandtest run. Under--output json,details.fieldmatches what was typed. Thetest run --target-urloutput is byte-identical. - Ctrl-C during
test run --localstates what happened, not a guarantee. The CLI cancels the run when its own tunnel is torn down, and used to print that "no further credits are spent on a guaranteed failure". A run seen in exactly that state completed and reachedPASSED36 s later and kept its 0.5-credit charge. The message now names the signal, says the run was cancelled and that a cancelled run's verdict is discarded, notes the run may already have been billed, and leads with the command to start a new run —testsprite test run <test-id> --local <port>.--no-cancel-on-interruptis mentioned only with its consequence: a run left executing has no tunnel, cannot reach your app, and is still billed. - A borrowed tunnel whose owner died leads with the cancel, not a retry. When
test run --local --tunnel-client <id>detects that the owning process is gone, the message used to say "restart the tunnel and run again" while the first run kept executing and billing server-side. It now names the run, says it was left executing, and gives the idempotenttestsprite test cancel <run-id>first, withtestsprite test wait <run-id> --timeout <s>as the alternative for watching it instead. Exit 10 and theowner-gonereason are unchanged. test run --allprints the server's project-level dashboard link. The batch's closing "watch it here" line was templated client-side as/dashboard/tests/{projectId}— the V2 grammar — which the portal bounces onto a/dashboard-v3/...path with no route for a V3 workspace. The line now uses the link the server supplies (the per-testdashboardUrlalready did); when the server says no correct page exists the line is omitted, and an older backend that sends nothing keeps today's fallback byte-for-byte.