Skip to content

Commit c6e29d0

Browse files
committed
feat(sdk): add test-generator cookbook example
Add a new SDK example that generates tests, runs framework-native test commands, and iterates on failures so users can see a practical agent + deterministic verification loop in both CLI and TUI flows. Made-with: Cursor
1 parent 82ca3d3 commit c6e29d0

20 files changed

Lines changed: 4712 additions & 0 deletions

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,8 @@ A kanban board for viewing Cursor Cloud Agents, grouping them by status or repos
2424

2525
A minimal command-line interface that lets you spawn Cursor agents from your terminal.
2626

27+
### [Test generator](sdk/test-generator)
28+
29+
A CLI that points a Cursor agent at your source files, generates unit tests, runs them, and iterates until they pass. Supports TypeScript/JavaScript (Vitest or Jest) and Python (Pytest), with an interactive Ink TUI.
30+
2731
Learn more in the [Cursor SDK TypeScript docs](https://cursor.com/docs/api/sdk/typescript).

sdk/test-generator/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
dist
2+
node_modules
3+
examples/**/*.test.ts
4+
examples/tests/test_*.py
5+
tests/test_*.py
6+
.env

sdk/test-generator/README.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Cursor SDK Test Generator
2+
3+
A local Cursor SDK example that points an agent at source files, generates unit tests, runs them, and feeds failures back into the agent until the tests pass or the iteration limit is reached.
4+
5+
This example demonstrates:
6+
7+
- TypeScript/JavaScript test generation for Vitest or Jest projects,
8+
- Python test generation for Pytest projects,
9+
- deterministic test execution outside the agent loop,
10+
- an Ink TUI with file selection, live progress, model selection, cancellation, and diff review.
11+
12+
## Getting Started
13+
14+
Use Node.js 22 or newer.
15+
16+
Install dependencies:
17+
18+
```bash
19+
pnpm install
20+
```
21+
22+
Set a Cursor API key:
23+
24+
```bash
25+
export CURSOR_API_KEY="crsr_..."
26+
```
27+
28+
Generate tests for a TypeScript file and auto-accept the result:
29+
30+
```bash
31+
pnpm dev -- examples/sample.ts --yes
32+
```
33+
34+
Generate tests for a Python file:
35+
36+
```bash
37+
pnpm dev -- examples/sample.py --lang python --yes
38+
```
39+
40+
Start the interactive TUI by omitting files:
41+
42+
```bash
43+
pnpm dev
44+
```
45+
46+
## CLI Options
47+
48+
```bash
49+
test-gen [files...|globs...] [options]
50+
51+
-C, --cwd <path> Workspace root. Defaults to cwd.
52+
--lang <ts|js|python> Override language detection.
53+
--framework <name> Override framework detection: vitest, jest, pytest.
54+
--max-iters <n> Test repair iterations. Defaults to 3.
55+
--allow-source-edits Let the agent fix source bugs during repair.
56+
--overwrite Overwrite existing test files.
57+
-y, --yes Auto-accept generated test files.
58+
-m, --model <id> Model id. Defaults to CURSOR_MODEL or composer-2.
59+
```
60+
61+
## Notes
62+
63+
The agent writes or repairs tests, but this app runs the tests itself with the detected framework. That keeps the pass/fail signal deterministic and makes it easy to stream test output into the TUI.
64+
65+
If your package manager ignores native dependency build scripts and the SDK reports a missing `sqlite3` binding, approve or rebuild the native dependency before running the agent loop.
66+
67+
Cloud execution, coverage-guided test generation, mutation testing, and languages beyond TypeScript/JavaScript/Python are intentionally out of scope for this example.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
def apply_discount(price: float, percent: float) -> float:
2+
if price < 0:
3+
raise ValueError("price must be non-negative")
4+
5+
if percent < 0 or percent > 100:
6+
raise ValueError("percent must be between 0 and 100")
7+
8+
return round(price * (1 - percent / 100), 2)
9+
10+
11+
def format_invoice_id(invoice_id: int) -> str:
12+
return f"INV-{invoice_id:06d}"
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export function applyDiscount(price: number, percent: number) {
2+
if (price < 0) {
3+
throw new Error("price must be non-negative")
4+
}
5+
6+
if (percent < 0 || percent > 100) {
7+
throw new Error("percent must be between 0 and 100")
8+
}
9+
10+
return Math.round(price * (1 - percent / 100) * 100) / 100
11+
}
12+
13+
export function formatInvoiceId(id: number) {
14+
return `INV-${String(id).padStart(6, "0")}`
15+
}

sdk/test-generator/package.json

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"name": "test-generator",
3+
"version": "0.1.0",
4+
"private": true,
5+
"type": "module",
6+
"packageManager": "pnpm@10.9.0",
7+
"engines": {
8+
"node": ">=22"
9+
},
10+
"bin": {
11+
"test-gen": "./dist/index.js"
12+
},
13+
"scripts": {
14+
"dev": "tsx src/index.ts",
15+
"build": "tsc -p tsconfig.json",
16+
"start": "node dist/index.js",
17+
"typecheck": "tsc -p tsconfig.json --noEmit"
18+
},
19+
"dependencies": {
20+
"@cursor/sdk": "^1.0.7",
21+
"fast-glob": "^3.3.3",
22+
"ink": "^7.0.1",
23+
"ink-select-input": "^6.2.0",
24+
"ink-text-input": "^6.0.0",
25+
"react": "^19.2.5"
26+
},
27+
"devDependencies": {
28+
"@types/node": "^25.6.0",
29+
"@types/react": "^19.2.14",
30+
"tsx": "^4.21.0",
31+
"typescript": "^6.0.3",
32+
"vitest": "^4.0.15"
33+
},
34+
"pnpm": {
35+
"onlyBuiltDependencies": [
36+
"esbuild",
37+
"sqlite3"
38+
]
39+
}
40+
}

0 commit comments

Comments
 (0)