Turn the lights on.
Install it.
No account. No licence key.
node ^22.15 || ^24.14.1 · npm ^11.6
Tell it how your app starts.
import { app, defineConfig } from "@gfdelarue/testkit/config";export default defineConfig({ services: { api: app.node({ build: null, start: "", port: , }), },});
Prove one thing.
import { defineFile } from "@gfdelarue/testkit";import { contractScenario, expect,} from "@gfdelarue/testkit/node-vitest/contract";export const testkit = defineFile({ engine: "node-vitest" });contractScenario("the app answers", async ({ http }) => { const response = await http().get(""); expect(response.status).toBe(200);});
$npx testkit run[1/1] PASS api contract src/__testkit__/health.contract.testkit.ts 2sSummaryResult: PASSEDPassed: 1Failed: 0
Send your agent hunting.
npx testkit campaign start --target ""
It prints a brief. Give the brief to your agent.
files in origin, on GitHub or Gitea · reads GITHUB_TOKEN or GITEA_TOKEN
#1400 A refund pays out twice
- Labels
testkitseverity:higharea:checkout- Expected
- 409 on the second request
- Observed
- 201, and a second payout
- Root cause
- A retry with no idempotency key, since
42c7079 - Reproduce
npx testkit run
[1/2] FAIL api contract src/checkout/__testkit__/refund.contract.testkit.ts 1s AssertionError: expected 201 to be 409 // Object.is equality
Your agent files each bug with testkit bug file, and only when its test fails.
Gate every merge.
"scripts": { "testkit": "testkit run"}
permissions: contents: read issues: read pull-requests: write statuses: writesteps: - run: npm ci - run: npm run testkit - run: npx testkit ci report if: always() env: GITHUB_TOKEN: ${{ github.token }}
The run exits zero only when every required file passed. The report posts a commit status, a step summary, and one pull request comment.
Know where they come from.
Turnstone charts every bug's severity, area, commits, and dates. Your reports, tests, and evidence stay in GitHub or Gitea.
Free tier. The CLI never needs it.