lacuna-cli 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -21
- package/dist/agent/fix-loop.d.ts +5 -0
- package/dist/agent/fix-loop.d.ts.map +1 -1
- package/dist/agent/fix-loop.js +342 -35
- package/dist/agent/fix-loop.js.map +1 -1
- package/dist/agent/generator.d.ts +4 -1
- package/dist/agent/generator.d.ts.map +1 -1
- package/dist/agent/generator.js +42 -3
- package/dist/agent/generator.js.map +1 -1
- package/dist/agent/loop.d.ts +8 -0
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +26 -9
- package/dist/agent/loop.js.map +1 -1
- package/dist/agent/prompts.d.ts +8 -0
- package/dist/agent/prompts.d.ts.map +1 -1
- package/dist/agent/prompts.js +395 -67
- package/dist/agent/prompts.js.map +1 -1
- package/dist/commands/fix.d.ts +2 -0
- package/dist/commands/fix.d.ts.map +1 -1
- package/dist/commands/fix.js +31 -2
- package/dist/commands/fix.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +245 -34
- package/dist/commands/init.js.map +1 -1
- package/dist/lib/config.d.ts +8 -8
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +8 -6
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/coverage/gaps.d.ts +2 -2
- package/dist/lib/coverage/gaps.d.ts.map +1 -1
- package/dist/lib/coverage/gaps.js +4 -4
- package/dist/lib/coverage/gaps.js.map +1 -1
- package/dist/lib/detector.d.ts +3 -2
- package/dist/lib/detector.d.ts.map +1 -1
- package/dist/lib/detector.js +138 -0
- package/dist/lib/detector.js.map +1 -1
- package/dist/lib/providers/anthropic.d.ts.map +1 -1
- package/dist/lib/providers/anthropic.js +26 -3
- package/dist/lib/providers/anthropic.js.map +1 -1
- package/dist/lib/providers/openai-compatible.d.ts +1 -1
- package/dist/lib/providers/openai-compatible.d.ts.map +1 -1
- package/dist/lib/providers/openai-compatible.js +17 -1
- package/dist/lib/providers/openai-compatible.js.map +1 -1
- package/dist/lib/skeleton.d.ts +4 -0
- package/dist/lib/skeleton.d.ts.map +1 -1
- package/dist/lib/skeleton.js +220 -0
- package/dist/lib/skeleton.js.map +1 -1
- package/dist/lib/validate.d.ts +1 -0
- package/dist/lib/validate.d.ts.map +1 -1
- package/dist/lib/validate.js +132 -0
- package/dist/lib/validate.js.map +1 -1
- package/dist/lib/worker-display.d.ts +3 -0
- package/dist/lib/worker-display.d.ts.map +1 -1
- package/dist/lib/worker-display.js +19 -4
- package/dist/lib/worker-display.js.map +1 -1
- package/package.json +1 -1
- package/dist/lib/report-upload.d.ts +0 -3
- package/dist/lib/report-upload.d.ts.map +0 -1
- package/dist/lib/report-upload.js +0 -15
- package/dist/lib/report-upload.js.map +0 -1
- package/oclif.manifest.json +0 -295
package/README.md
CHANGED
|
@@ -67,10 +67,16 @@ Interactive setup wizard. Configures your model, test runner, source directory,
|
|
|
67
67
|
|
|
68
68
|
Works from any subdirectory — lacuna finds the project root automatically.
|
|
69
69
|
|
|
70
|
-
For **React
|
|
70
|
+
For **React** projects, `lacuna init` also:
|
|
71
71
|
- Installs `@testing-library/react`, `@testing-library/jest-dom`, `@testing-library/user-event`, and `jsdom`
|
|
72
|
-
- Creates `vitest.config.ts` with
|
|
73
|
-
- Creates a setup file
|
|
72
|
+
- Creates `vitest.config.ts` with `environment: 'jsdom'` and `restoreMocks: true` + `clearMocks: true`
|
|
73
|
+
- Creates a setup file with `@testing-library/jest-dom` and `beforeEach`/`afterEach` mock cleanup hooks
|
|
74
|
+
|
|
75
|
+
For **Next.js** projects, the setup is different — Next.js and plain React are not compatible:
|
|
76
|
+
- Installs the same testing-library packages but does **not** add `environment: 'jsdom'` to vitest config (Next.js manages its own environment)
|
|
77
|
+
- Adds `@/` alias (from your `tsconfig.json`) and a `server-only` stub alias to vitest config
|
|
78
|
+
- Creates a setup file with global `vi.mock()` calls for `next/navigation`, `next/headers`, `next/cache`, `next/image`, and `next/font` — these are Next.js-specific mocks that would crash a plain React project
|
|
79
|
+
- If test dependencies are found in `node_modules` but not declared in `package.json` (e.g. from a different branch), lacuna will prompt you to add them — undeclared deps break CI on a fresh checkout
|
|
74
80
|
|
|
75
81
|
```bash
|
|
76
82
|
lacuna init
|
|
@@ -108,7 +114,7 @@ lacuna generate --format json --output report.json
|
|
|
108
114
|
```
|
|
109
115
|
|
|
110
116
|
### `lacuna fix`
|
|
111
|
-
Finds all failing tests and repairs them using AI
|
|
117
|
+
Finds all failing tests and repairs them using AI. Sends each failing file along with its error output and source code to the model, which surgically fixes what's broken and retries until it passes. If all fix retries fail, lacuna automatically deletes the broken test and regenerates it from the source file — a clean slate rather than another round of patchwork.
|
|
112
118
|
|
|
113
119
|
```bash
|
|
114
120
|
lacuna fix
|
|
@@ -117,15 +123,19 @@ lacuna fix --file src/utils/math.test.ts # fix a single test file (skips full
|
|
|
117
123
|
lacuna fix --dry-run # preview fixes without writing
|
|
118
124
|
lacuna fix --verbose # live code panel as model writes each fix
|
|
119
125
|
lacuna fix --fresh # re-run the suite even if cache is recent
|
|
126
|
+
lacuna fix --no-regenerate-on-failure # disable the regen fallback (fix only, no delete)
|
|
127
|
+
lacuna fix --fix-polluters # also handle tests that pass alone but fail in suite
|
|
120
128
|
```
|
|
121
129
|
|
|
122
|
-
Unlike `lacuna generate`, which creates new tests, `lacuna fix` operates on existing failing tests
|
|
130
|
+
Unlike `lacuna generate`, which creates new tests, `lacuna fix` operates on existing failing tests and preserves all test logic where possible.
|
|
131
|
+
|
|
132
|
+
**Regeneration fallback (default on):** When fix retries are exhausted, lacuna deletes the test file and regenerates it from scratch using the generate path. This works better than continued repair for structurally broken tests — the AI starts with a clean conversation and full source context instead of carrying forward a chain of failed hypotheses. Use `--no-regenerate-on-failure` to disable this and get fix-only behaviour.
|
|
123
133
|
|
|
124
|
-
|
|
134
|
+
**Passing in isolation, failing in suite (`--fix-polluters`):** Some tests pass when run alone but fail in the full suite. `--fix-polluters` handles these in two phases: (1) bisect the test suite to find if another file is leaking state (e.g. an uncleaned mock or global), and fix the polluter; (2) if no polluter can be isolated (the test has an internal spy lifecycle bug), delete and regenerate the victim file directly.
|
|
125
135
|
|
|
126
|
-
If
|
|
136
|
+
If all retries fail or the model oscillates (identical output detected), the original file is restored automatically. If a fix attempt breaks an import or reduces passing tests, lacuna detects the regression and anchors the next retry to the original error.
|
|
127
137
|
|
|
128
|
-
When `--file` is given, lacuna skips the full suite and runs only the target file
|
|
138
|
+
When `--file` is given, lacuna skips the full suite and runs only the target file. Without `--file`, the failing-files list is cached for 30 minutes. After a fix run, the cache is updated to contain only the still-failing files — so re-running immediately picks up where the last run left off.
|
|
129
139
|
|
|
130
140
|
### `lacuna run`
|
|
131
141
|
Runs your test suite and reports coverage. No AI involved.
|
|
@@ -142,9 +152,10 @@ Created by `lacuna init`. All fields are optional with sensible defaults.
|
|
|
142
152
|
|
|
143
153
|
```json
|
|
144
154
|
{
|
|
145
|
-
"provider": "
|
|
146
|
-
"model": "
|
|
147
|
-
"
|
|
155
|
+
"provider": "openai-compatible",
|
|
156
|
+
"model": "deepseek-chat",
|
|
157
|
+
"baseURL": "https://api.deepseek.com/v1",
|
|
158
|
+
"apiKeyEnv": "DEEPSEEK_API_KEY",
|
|
148
159
|
"testRunner": "jest",
|
|
149
160
|
"coverageFormat": "lcov",
|
|
150
161
|
"coverageDir": "coverage",
|
|
@@ -159,14 +170,14 @@ Created by `lacuna init`. All fields are optional with sensible defaults.
|
|
|
159
170
|
|
|
160
171
|
| Field | Default | Description |
|
|
161
172
|
|---|---|---|
|
|
162
|
-
| `provider` | `
|
|
163
|
-
| `model` | `
|
|
164
|
-
| `apiKeyEnv` | `
|
|
165
|
-
| `baseURL` |
|
|
173
|
+
| `provider` | `openai-compatible` | `anthropic` or `openai-compatible` |
|
|
174
|
+
| `model` | `deepseek-chat` | Model name |
|
|
175
|
+
| `apiKeyEnv` | `DEEPSEEK_API_KEY` | Env var holding your API key |
|
|
176
|
+
| `baseURL` | `https://api.deepseek.com/v1` | API base URL — required for `openai-compatible` provider |
|
|
166
177
|
| `testRunner` | auto-detect | `jest` \| `vitest` \| `pytest` \| `mocha` \| `go-test` |
|
|
167
178
|
| `coverageFormat` | `lcov` | `lcov` \| `json-summary` |
|
|
168
179
|
| `coverageDir` | `coverage` | Where your test runner writes coverage |
|
|
169
|
-
| `sourceDir` | `src` |
|
|
180
|
+
| `sourceDir` | `"src"` | Source directory to scan. Accepts a string or an array — `["src", "lib", "utils"]` scans multiple directories |
|
|
170
181
|
| `threshold` | `80` | Minimum line coverage % to pass |
|
|
171
182
|
| `maxIterations` | `3` | How many times to retry a failing generated test |
|
|
172
183
|
| `coverageTimeout` | `300` | Seconds before the test suite is killed (prevents hanging on open handles) |
|
|
@@ -183,9 +194,10 @@ Lacuna works with any AI model — local or cloud.
|
|
|
183
194
|
|
|
184
195
|
| Preset | Model | API key env | Notes |
|
|
185
196
|
|---|---|---|---|
|
|
186
|
-
|
|
|
197
|
+
| **DeepSeek (default)** | `deepseek-chat` | `DEEPSEEK_API_KEY` | Best value — fast, cheap, no rate-limit issues |
|
|
198
|
+
| DeepSeek R1 | `deepseek-reasoner` | `DEEPSEEK_API_KEY` | Reasoning model |
|
|
199
|
+
| Claude Sonnet | `claude-sonnet-4-6` | `ANTHROPIC_API_KEY` | High quality |
|
|
187
200
|
| Claude Opus | `claude-opus-4-7` | `ANTHROPIC_API_KEY` | Most capable |
|
|
188
|
-
| DeepSeek | `deepseek-chat` | `DEEPSEEK_API_KEY` | Very cost-effective |
|
|
189
201
|
| DeepSeek R1 | `deepseek-reasoner` | `DEEPSEEK_API_KEY` | Reasoning model |
|
|
190
202
|
| GPT-4o | `gpt-4o` | `OPENAI_API_KEY` | |
|
|
191
203
|
| Groq | `llama-3.3-70b-versatile` | `GROQ_API_KEY` | Fast, free tier |
|
|
@@ -255,7 +267,25 @@ beforeEach(() => {
|
|
|
255
267
|
lacuna generate
|
|
256
268
|
```
|
|
257
269
|
|
|
258
|
-
Every generated test will import from `src/test/mocks.ts` instead of creating its own `vi.fn()` calls. If a test needs a mock that doesn't exist yet,
|
|
270
|
+
Every generated test will import from `src/test/mocks.ts` instead of creating its own `vi.fn()` calls. If a test needs a mock that doesn't exist yet, lacuna will add it to the mocks file and import it — keeping everything centralized.
|
|
271
|
+
|
|
272
|
+
### How lacuna reads and updates the shared mock file
|
|
273
|
+
|
|
274
|
+
Lacuna parses your mock file before every prompt and builds a **mock inventory** — a structured table of every `vi.mock()` call, its line number, and all the keys it exports:
|
|
275
|
+
|
|
276
|
+
```
|
|
277
|
+
'react-router-dom' → useNavigate, useParams
|
|
278
|
+
'axios' → get, post, put, delete
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
This inventory is injected into every prompt so the AI knows exactly which modules are already mocked and what they export. When the AI needs to add a new export (e.g. a new API client method), it updates the **existing** `vi.mock()` block with the full list of old + new exports — never appending a second block for the same module.
|
|
282
|
+
|
|
283
|
+
**Structure rules the AI follows:**
|
|
284
|
+
- Module already in inventory → update the existing block (never a duplicate)
|
|
285
|
+
- New mock variable → declare near same-domain exports, reset in existing `beforeEach`
|
|
286
|
+
- New module → append at end before the final `beforeEach`
|
|
287
|
+
|
|
288
|
+
For fix prompts, the mock file is **compressed** before sending (multi-line React component stub bodies are collapsed to `vi.fn()`) to reduce token cost while preserving all `vi.mock()` blocks and factory functions. For generate prompts, only the inventory and exports list are sent — the raw file is skipped entirely since the AI only needs to know what to import, not re-read every implementation.
|
|
259
289
|
|
|
260
290
|
---
|
|
261
291
|
|
|
@@ -298,8 +328,8 @@ jobs:
|
|
|
298
328
|
with:
|
|
299
329
|
threshold: 80
|
|
300
330
|
workers: 2 # parallel workers — increase for larger repos
|
|
301
|
-
model:
|
|
302
|
-
|
|
331
|
+
model: deepseek # default — cost-effective, no rate-limit issues
|
|
332
|
+
deepseek-api-key: ${{ secrets.DEEPSEEK_API_KEY }}
|
|
303
333
|
|
|
304
334
|
# Runs even when lacuna exits with code 1 (below threshold) so generated
|
|
305
335
|
# tests are never lost. Skips the commit if nothing was written.
|
package/dist/agent/fix-loop.d.ts
CHANGED
|
@@ -9,11 +9,16 @@ export interface FixOptions {
|
|
|
9
9
|
targetFile?: string;
|
|
10
10
|
workers?: number;
|
|
11
11
|
fresh?: boolean;
|
|
12
|
+
regenerateOnFailure?: boolean;
|
|
13
|
+
fixPolluters?: boolean;
|
|
12
14
|
log: (msg: string) => void;
|
|
13
15
|
}
|
|
14
16
|
export interface FixResult {
|
|
15
17
|
filesProcessed: number;
|
|
16
18
|
filesFixed: number;
|
|
19
|
+
filesAlreadyPassing: number;
|
|
20
|
+
pollutersFixed: number;
|
|
21
|
+
victimsRegenerated: number;
|
|
17
22
|
errors: string[];
|
|
18
23
|
}
|
|
19
24
|
export declare function runFixLoop(options: FixOptions): Promise<FixResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fix-loop.d.ts","sourceRoot":"","sources":["../../src/agent/fix-loop.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"fix-loop.d.ts","sourceRoot":"","sources":["../../src/agent/fix-loop.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AAiB7D,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,YAAY,CAAA;IACpB,GAAG,EAAE,mBAAmB,CAAA;IACxB,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,OAAO,CAAA;IACf,OAAO,EAAE,OAAO,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CAC3B;AAoCD,MAAM,WAAW,SAAS;IACxB,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,cAAc,EAAE,MAAM,CAAA;IACtB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB;AA6nBD,wBAAsB,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAwJxE"}
|