lacuna-cli 0.1.1 → 0.1.3
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 +42 -6
- package/dist/agent/fix-loop.d.ts.map +1 -1
- package/dist/agent/fix-loop.js +24 -6
- package/dist/agent/fix-loop.js.map +1 -1
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +34 -7
- package/dist/agent/loop.js.map +1 -1
- package/dist/agent/prompts.d.ts.map +1 -1
- package/dist/agent/prompts.js +153 -11
- package/dist/agent/prompts.js.map +1 -1
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +3 -1
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +315 -26
- package/dist/commands/init.js.map +1 -1
- package/dist/lib/validate.d.ts +9 -0
- package/dist/lib/validate.d.ts.map +1 -1
- package/dist/lib/validate.js +151 -0
- package/dist/lib/validate.js.map +1 -1
- package/oclif.manifest.json +11 -11
- package/package.json +4 -2
package/dist/commands/init.js
CHANGED
|
@@ -1,15 +1,286 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
|
-
import { writeFile, access } from 'fs/promises';
|
|
3
|
-
import { join } from 'path';
|
|
2
|
+
import { writeFile, readFile, access, mkdir } from 'fs/promises';
|
|
3
|
+
import { join, dirname, resolve } from 'path';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
4
5
|
import { select, input, confirm } from '@inquirer/prompts';
|
|
5
6
|
import chalk from 'chalk';
|
|
6
7
|
import { detectEnvironment } from '../lib/detector.js';
|
|
7
8
|
import { PRESETS } from '../lib/providers/index.js';
|
|
9
|
+
async function readProjectMeta(cwd) {
|
|
10
|
+
try {
|
|
11
|
+
const pkg = JSON.parse(await readFile(join(cwd, 'package.json'), 'utf-8'));
|
|
12
|
+
const all = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
13
|
+
return {
|
|
14
|
+
isReact: 'react' in all,
|
|
15
|
+
isNextJs: 'next' in all,
|
|
16
|
+
isTypeScript: 'typescript' in all,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return { isReact: false, isNextJs: false, isTypeScript: false };
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async function isPackageInstalled(pkg, cwd) {
|
|
24
|
+
// Check all dependency fields in package.json (covers workspaces where the
|
|
25
|
+
// package is declared in the root manifest but installed at workspace level)
|
|
26
|
+
try {
|
|
27
|
+
const json = JSON.parse(await readFile(join(cwd, 'package.json'), 'utf-8'));
|
|
28
|
+
const all = {
|
|
29
|
+
...json['dependencies'],
|
|
30
|
+
...json['devDependencies'],
|
|
31
|
+
...json['peerDependencies'],
|
|
32
|
+
...json['optionalDependencies'],
|
|
33
|
+
};
|
|
34
|
+
if (pkg in all)
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
catch { /* fall through to node_modules check */ }
|
|
38
|
+
// Fallback: check if the package is present in node_modules (pnpm workspaces,
|
|
39
|
+
// hoisted installs, or cases where the root manifest differs from what's installed)
|
|
40
|
+
try {
|
|
41
|
+
await access(join(cwd, 'node_modules', pkg));
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
catch { /* not found */ }
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
async function writeFileWithDir(filePath, content) {
|
|
48
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
49
|
+
await writeFile(filePath, content);
|
|
50
|
+
}
|
|
51
|
+
// Reads tsconfig.json and returns the filesystem path that "@/*" maps to.
|
|
52
|
+
// e.g. "@/*": ["./*"] → "." "@/*": ["./src/*"] → "./src"
|
|
53
|
+
// Falls back to "." (project root) when tsconfig is absent or has no @/* mapping.
|
|
54
|
+
async function resolveAtAlias(cwd) {
|
|
55
|
+
try {
|
|
56
|
+
const raw = await readFile(join(cwd, 'tsconfig.json'), 'utf-8');
|
|
57
|
+
// Strip comments before parsing (tsconfig allows them)
|
|
58
|
+
const cleaned = raw.replace(/\/\/[^\n]*/g, '').replace(/\/\*[\s\S]*?\*\//g, '');
|
|
59
|
+
const tsconfig = JSON.parse(cleaned);
|
|
60
|
+
const paths = tsconfig.compilerOptions?.paths ?? {};
|
|
61
|
+
// Look for @/* or @ entry
|
|
62
|
+
const entry = paths['@/*'] ?? paths['@'];
|
|
63
|
+
if (entry?.[0]) {
|
|
64
|
+
// Strip trailing /* to get the base directory
|
|
65
|
+
return entry[0].replace(/\/\*$/, '') || '.';
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch { /* tsconfig missing or unparseable — use default */ }
|
|
69
|
+
return '.';
|
|
70
|
+
}
|
|
71
|
+
// Walk up from startDir until we find a directory containing package.json.
|
|
72
|
+
// This ensures lacuna init works correctly even when run from a subdirectory.
|
|
73
|
+
async function findProjectRoot(startDir) {
|
|
74
|
+
let dir = resolve(startDir);
|
|
75
|
+
while (true) {
|
|
76
|
+
try {
|
|
77
|
+
await access(join(dir, 'package.json'));
|
|
78
|
+
return dir;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
const parent = dirname(dir);
|
|
82
|
+
if (parent === dir)
|
|
83
|
+
return startDir; // reached filesystem root, fall back
|
|
84
|
+
dir = parent;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function buildSetupFileContent(isNextJs) {
|
|
89
|
+
const lines = [`import '@testing-library/jest-dom'`];
|
|
90
|
+
if (isNextJs) {
|
|
91
|
+
lines.push(``, `// ── Next.js global mocks ──────────────────────────────────────────────────`, `// These run before every test so individual test files don't need to mock them.`, ``, `import { vi } from 'vitest'`, ``, `// next/navigation — useRouter, usePathname, etc. are server-side and fail in jsdom`, `vi.mock('next/navigation', () => ({`, ` useRouter: vi.fn(() => ({ push: vi.fn(), replace: vi.fn(), back: vi.fn(), forward: vi.fn(), prefetch: vi.fn() })),`, ` usePathname: vi.fn(() => '/'),`, ` useSearchParams: vi.fn(() => new URLSearchParams()),`, ` useParams: vi.fn(() => ({})),`, ` redirect: vi.fn(),`, ` notFound: vi.fn(),`, `}))`, ``, `// next/headers — server-only, throws in jsdom`, `vi.mock('next/headers', () => ({`, ` cookies: vi.fn(() => ({ get: vi.fn(), set: vi.fn(), delete: vi.fn(), has: vi.fn(), getAll: vi.fn(() => []) })),`, ` headers: vi.fn(() => new Headers()),`, `}))`, ``, `// next/cache — no-ops in tests`, `vi.mock('next/cache', () => ({`, ` revalidatePath: vi.fn(),`, ` revalidateTag: vi.fn(),`, ` unstable_cache: vi.fn((fn: () => unknown) => fn),`, `}))`, ``, `// next/image — uses Next.js image optimization which breaks in jsdom`, `vi.mock('next/image', () => ({`, ` default: vi.fn(({ src, alt, ...props }: Record<string, unknown>) => null),`, `}))`, ``, `// next/font — font loading tries to fetch/read files at import time, fails in tests`, `// Add any fonts your project uses that aren't listed here`, `vi.mock('next/font/google', () => new Proxy({}, {`, ` get: (_: object, fontName: string) =>`, ` () => ({ className: \`font-\${fontName.toLowerCase()}\`, style: { fontFamily: fontName } }),`, `}))`, `vi.mock('next/font/local', () => ({`, ` default: vi.fn(() => ({ className: 'font-local', style: { fontFamily: 'local' } })),`, `}))`);
|
|
92
|
+
}
|
|
93
|
+
return lines.join('\n') + '\n';
|
|
94
|
+
}
|
|
95
|
+
async function ensureTestRunnerSetup(runner, sourceDir, cwd, log) {
|
|
96
|
+
if (runner === 'pytest' || runner === 'go-test')
|
|
97
|
+
return undefined; // outside Node ecosystem
|
|
98
|
+
const meta = await readProjectMeta(cwd);
|
|
99
|
+
const alreadyInstalled = await isPackageInstalled(runner, cwd);
|
|
100
|
+
// ── Determine packages to install ─────────────────────────────────────────
|
|
101
|
+
const basePackages = [];
|
|
102
|
+
const setupFilePackages = [];
|
|
103
|
+
if (runner === 'vitest') {
|
|
104
|
+
basePackages.push('vitest', '@vitest/coverage-v8');
|
|
105
|
+
if (meta.isReact) {
|
|
106
|
+
basePackages.push('jsdom');
|
|
107
|
+
setupFilePackages.push('@testing-library/react', '@testing-library/jest-dom', '@testing-library/user-event');
|
|
108
|
+
}
|
|
109
|
+
// @vitejs/plugin-react is NOT needed for Vitest — esbuild handles JSX/TSX natively.
|
|
110
|
+
}
|
|
111
|
+
else if (runner === 'jest') {
|
|
112
|
+
if (meta.isTypeScript) {
|
|
113
|
+
basePackages.push('jest', '@types/jest', 'ts-jest');
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
basePackages.push('jest');
|
|
117
|
+
}
|
|
118
|
+
if (meta.isReact) {
|
|
119
|
+
basePackages.push('jest-environment-jsdom');
|
|
120
|
+
setupFilePackages.push('@testing-library/react', '@testing-library/jest-dom', '@testing-library/user-event');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else if (runner === 'mocha') {
|
|
124
|
+
basePackages.push('mocha', 'c8');
|
|
125
|
+
if (meta.isTypeScript)
|
|
126
|
+
basePackages.push('@types/mocha', 'ts-node');
|
|
127
|
+
}
|
|
128
|
+
// Next.js: setup file goes at root test/setup.ts — app/ is the App Router,
|
|
129
|
+
// files inside it are treated as routes. For regular React, co-locate with source.
|
|
130
|
+
const setupFilePath = meta.isReact
|
|
131
|
+
? (meta.isNextJs ? `test/setup.ts` : `${sourceDir}/test/setup.ts`)
|
|
132
|
+
: undefined;
|
|
133
|
+
// ── Install missing packages ───────────────────────────────────────────────
|
|
134
|
+
if (!alreadyInstalled) {
|
|
135
|
+
const allPackages = [...basePackages, ...setupFilePackages];
|
|
136
|
+
log(chalk.yellow(`\n ${runner} is not installed.`));
|
|
137
|
+
log(chalk.dim(` Packages: ${allPackages.join(', ')}`));
|
|
138
|
+
const doInstall = await confirm({ message: `Install ${runner} and dependencies?`, default: true });
|
|
139
|
+
if (!doInstall) {
|
|
140
|
+
log(chalk.dim(` Skipped. Install manually: npm install -D ${allPackages.join(' ')}`));
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
log(chalk.dim(`\n Installing packages...`));
|
|
144
|
+
try {
|
|
145
|
+
execSync(`npm install -D ${allPackages.join(' ')}`, { cwd, stdio: 'inherit' });
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
log(chalk.red(` Install failed. Run manually: npm install -D ${allPackages.join(' ')}`));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
else if ((meta.isNextJs || meta.isReact) && setupFilePackages.length > 0) {
|
|
153
|
+
// Runner is installed — check if the extra testing-library packages are present.
|
|
154
|
+
// Must use a for-loop: Array.filter ignores async callbacks (the Promise is always truthy).
|
|
155
|
+
const missing = [];
|
|
156
|
+
for (const p of setupFilePackages) {
|
|
157
|
+
if (!(await isPackageInstalled(p, cwd)))
|
|
158
|
+
missing.push(p);
|
|
159
|
+
}
|
|
160
|
+
if (missing.length > 0) {
|
|
161
|
+
log(chalk.dim(`\n Installing missing test dependencies: ${missing.join(', ')}`));
|
|
162
|
+
try {
|
|
163
|
+
execSync(`npm install -D ${missing.join(' ')}`, { cwd, stdio: 'inherit' });
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
log(chalk.yellow(` Could not install: ${missing.join(', ')} — add them manually if needed`));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// ── Create setup file ──────────────────────────────────────────────────────
|
|
171
|
+
let createdSetupFile;
|
|
172
|
+
if (setupFilePath) {
|
|
173
|
+
const absSetup = join(cwd, setupFilePath);
|
|
174
|
+
try {
|
|
175
|
+
await access(absSetup);
|
|
176
|
+
log(chalk.dim(` ${setupFilePath} already exists — skipping.`));
|
|
177
|
+
createdSetupFile = setupFilePath;
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
const setupContent = buildSetupFileContent(meta.isNextJs);
|
|
181
|
+
await writeFileWithDir(absSetup, setupContent);
|
|
182
|
+
log(chalk.green(` ✓ Created ${setupFilePath}`));
|
|
183
|
+
if (meta.isNextJs)
|
|
184
|
+
log(chalk.dim(` Includes global mocks for next/navigation, next/headers, next/cache`));
|
|
185
|
+
createdSetupFile = setupFilePath;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// ── Create runner config ───────────────────────────────────────────────────
|
|
189
|
+
if (runner === 'vitest') {
|
|
190
|
+
// Always resolve to an absolute path so the config is never created inside
|
|
191
|
+
// a subdirectory regardless of how cwd was derived.
|
|
192
|
+
const configPath = resolve(cwd, 'vitest.config.ts');
|
|
193
|
+
try {
|
|
194
|
+
await access(configPath);
|
|
195
|
+
log(chalk.dim(` vitest.config.ts already exists at ${configPath} — skipping.`));
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
const setupLine = createdSetupFile
|
|
199
|
+
? `\n setupFiles: ['./${createdSetupFile}'],`
|
|
200
|
+
: '';
|
|
201
|
+
const envLine = meta.isReact ? `\n environment: 'jsdom',` : '';
|
|
202
|
+
// Next.js uses @/ as the root alias. Read the actual target from tsconfig.json
|
|
203
|
+
// to stay consistent with whatever the project has configured.
|
|
204
|
+
// No React plugin needed: Vitest uses esbuild which handles JSX/TSX natively.
|
|
205
|
+
const aliasTarget = meta.isNextJs ? await resolveAtAlias(cwd) : null;
|
|
206
|
+
const aliasBlock = aliasTarget
|
|
207
|
+
? `\n resolve: {\n alias: { '@': path.resolve(__dirname, '${aliasTarget}') },\n },`
|
|
208
|
+
: '';
|
|
209
|
+
const pathImport = aliasTarget ? `import path from 'path'\n` : '';
|
|
210
|
+
const content = [
|
|
211
|
+
`${pathImport}import { defineConfig } from 'vitest/config'`,
|
|
212
|
+
``,
|
|
213
|
+
`export default defineConfig({${aliasBlock}`,
|
|
214
|
+
` test: {`,
|
|
215
|
+
` globals: true,${envLine}${setupLine}`,
|
|
216
|
+
` coverage: {`,
|
|
217
|
+
` provider: 'v8',`,
|
|
218
|
+
` reporter: ['lcov', 'text-summary'],`,
|
|
219
|
+
` reportsDirectory: './coverage',`,
|
|
220
|
+
` },`,
|
|
221
|
+
` },`,
|
|
222
|
+
`})`,
|
|
223
|
+
``,
|
|
224
|
+
].join('\n');
|
|
225
|
+
await writeFile(configPath, content);
|
|
226
|
+
log(chalk.green(` ✓ Created vitest.config.ts at project root`));
|
|
227
|
+
}
|
|
228
|
+
log(chalk.dim(`\n Add to package.json scripts: "test": "vitest run --coverage"`));
|
|
229
|
+
}
|
|
230
|
+
else if (runner === 'jest') {
|
|
231
|
+
const configPath = resolve(cwd, 'jest.config.js');
|
|
232
|
+
try {
|
|
233
|
+
await access(configPath);
|
|
234
|
+
log(chalk.dim(` jest.config.js already exists — skipping.`));
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
const setupLine = createdSetupFile
|
|
238
|
+
? `\n setupFilesAfterFramework: ['<rootDir>/${createdSetupFile}'],`
|
|
239
|
+
: '';
|
|
240
|
+
const envLine = meta.isReact ? `\n testEnvironment: 'jsdom',` : '';
|
|
241
|
+
const tsLines = meta.isTypeScript
|
|
242
|
+
? `\n transform: { '^.+\\\\.tsx?$': 'ts-jest' },`
|
|
243
|
+
: '';
|
|
244
|
+
const content = [
|
|
245
|
+
`/** @type {import('jest').Config} */`,
|
|
246
|
+
`module.exports = {${envLine}${tsLines}${setupLine}`,
|
|
247
|
+
` coverageReporters: ['lcov', 'text-summary'],`,
|
|
248
|
+
` coverageDirectory: 'coverage',`,
|
|
249
|
+
`}`,
|
|
250
|
+
``,
|
|
251
|
+
].join('\n');
|
|
252
|
+
await writeFile(configPath, content);
|
|
253
|
+
log(chalk.green(` ✓ Created jest.config.js`));
|
|
254
|
+
}
|
|
255
|
+
log(chalk.dim(`\n Add to package.json scripts: "test": "jest --coverage"`));
|
|
256
|
+
}
|
|
257
|
+
else if (runner === 'mocha') {
|
|
258
|
+
const configPath = resolve(cwd, '.mocharc.json');
|
|
259
|
+
try {
|
|
260
|
+
await access(configPath);
|
|
261
|
+
log(chalk.dim(` .mocharc.json already exists — skipping.`));
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
const content = JSON.stringify({
|
|
265
|
+
spec: `${sourceDir}/**/*.test.{ts,js}`,
|
|
266
|
+
require: meta.isTypeScript ? ['ts-node/register'] : [],
|
|
267
|
+
}, null, 2) + '\n';
|
|
268
|
+
await writeFile(configPath, content);
|
|
269
|
+
log(chalk.green(` ✓ Created .mocharc.json`));
|
|
270
|
+
}
|
|
271
|
+
log(chalk.dim(`\n Add to package.json scripts: "test": "c8 --reporter=lcov mocha"`));
|
|
272
|
+
}
|
|
273
|
+
return createdSetupFile;
|
|
274
|
+
}
|
|
8
275
|
export default class Init extends Command {
|
|
9
276
|
static description = 'Interactive setup wizard — configure lacuna for your project';
|
|
10
277
|
static examples = ['$ lacuna init'];
|
|
11
278
|
async run() {
|
|
12
|
-
const
|
|
279
|
+
const cwd = await findProjectRoot(process.cwd());
|
|
280
|
+
if (cwd !== process.cwd()) {
|
|
281
|
+
this.log(chalk.dim(` (running from ${process.cwd()} — using project root: ${cwd})\n`));
|
|
282
|
+
}
|
|
283
|
+
const configPath = join(cwd, '.lacuna.json');
|
|
13
284
|
try {
|
|
14
285
|
await access(configPath);
|
|
15
286
|
const overwrite = await confirm({
|
|
@@ -24,7 +295,7 @@ export default class Init extends Command {
|
|
|
24
295
|
catch { /* file doesn't exist — proceed */ }
|
|
25
296
|
this.log(chalk.bold('\nlacuna init\n'));
|
|
26
297
|
const env = await detectEnvironment();
|
|
27
|
-
// ── Model / provider
|
|
298
|
+
// ── Model / provider ──────────────────────────────────────────────────────
|
|
28
299
|
const presetKey = await select({
|
|
29
300
|
message: 'Which model do you want to use?',
|
|
30
301
|
choices: [
|
|
@@ -42,33 +313,48 @@ export default class Init extends Command {
|
|
|
42
313
|
};
|
|
43
314
|
}
|
|
44
315
|
else if (presetKey === 'openrouter') {
|
|
45
|
-
const orModel = await input({
|
|
46
|
-
message: 'OpenRouter model (leave blank for default):',
|
|
47
|
-
default: preset.model,
|
|
48
|
-
});
|
|
316
|
+
const orModel = await input({ message: 'OpenRouter model (leave blank for default):', default: preset.model });
|
|
49
317
|
preset = { ...preset, model: orModel };
|
|
50
318
|
}
|
|
51
319
|
else if (presetKey === 'ollama') {
|
|
52
|
-
const ollamaModel = await input({
|
|
53
|
-
message: 'Ollama model name:',
|
|
54
|
-
default: 'llama3.2',
|
|
55
|
-
});
|
|
320
|
+
const ollamaModel = await input({ message: 'Ollama model name:', default: 'llama3.2' });
|
|
56
321
|
preset = { ...preset, model: ollamaModel };
|
|
57
322
|
}
|
|
58
|
-
// ── Test runner
|
|
323
|
+
// ── Test runner ───────────────────────────────────────────────────────────
|
|
59
324
|
const detectedRunner = env.testRunner !== 'unknown' ? env.testRunner : undefined;
|
|
60
325
|
const testRunner = await select({
|
|
61
326
|
message: 'Test runner:',
|
|
62
327
|
choices: [
|
|
63
|
-
{ value: 'jest', name: `jest${detectedRunner === 'jest' ? ' (detected)' : ''}` },
|
|
64
328
|
{ value: 'vitest', name: `vitest${detectedRunner === 'vitest' ? ' (detected)' : ''}` },
|
|
329
|
+
{ value: 'jest', name: `jest${detectedRunner === 'jest' ? ' (detected)' : ''}` },
|
|
65
330
|
{ value: 'mocha', name: `mocha${detectedRunner === 'mocha' ? ' (detected)' : ''}` },
|
|
66
331
|
{ value: 'pytest', name: `pytest${detectedRunner === 'pytest' ? ' (detected)' : ''}` },
|
|
67
332
|
{ value: 'go-test', name: `go test${detectedRunner === 'go-test' ? ' (detected)' : ''}` },
|
|
68
333
|
],
|
|
69
|
-
default: detectedRunner ?? '
|
|
334
|
+
default: detectedRunner ?? 'vitest',
|
|
335
|
+
});
|
|
336
|
+
// ── Source directory ──────────────────────────────────────────────────────
|
|
337
|
+
const sourceDir = await input({
|
|
338
|
+
message: 'Source directory (where your source files live):',
|
|
339
|
+
default: 'src',
|
|
70
340
|
});
|
|
71
|
-
// ──
|
|
341
|
+
// ── Test runner setup (install + config + setup file) ─────────────────────
|
|
342
|
+
const createdSetupFile = await ensureTestRunnerSetup(testRunner, sourceDir, cwd, (msg) => this.log(msg));
|
|
343
|
+
// ── Setup file (if not created above) ────────────────────────────────────
|
|
344
|
+
let setupFile = createdSetupFile;
|
|
345
|
+
if (!setupFile) {
|
|
346
|
+
const hasSetup = await confirm({
|
|
347
|
+
message: 'Do you have a test setup file (e.g. vitest.setup.ts / jest.setup.ts)?',
|
|
348
|
+
default: false,
|
|
349
|
+
});
|
|
350
|
+
if (hasSetup) {
|
|
351
|
+
setupFile = await input({
|
|
352
|
+
message: 'Path to setup file:',
|
|
353
|
+
default: `${sourceDir}/test/setup.ts`,
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
// ── Mocks file ────────────────────────────────────────────────────────────
|
|
72
358
|
const hasMocks = await confirm({
|
|
73
359
|
message: 'Do you have (or want) a shared mock file for all tests?',
|
|
74
360
|
default: true,
|
|
@@ -77,16 +363,13 @@ export default class Init extends Command {
|
|
|
77
363
|
if (hasMocks) {
|
|
78
364
|
mocksFile = await input({
|
|
79
365
|
message: 'Path to shared mock file:',
|
|
80
|
-
default:
|
|
366
|
+
default: `${sourceDir}/test/mocks.ts`,
|
|
81
367
|
});
|
|
82
368
|
}
|
|
83
|
-
// ── Coverage threshold
|
|
84
|
-
const thresholdStr = await input({
|
|
85
|
-
message: 'Coverage threshold (%):',
|
|
86
|
-
default: '80',
|
|
87
|
-
});
|
|
369
|
+
// ── Coverage threshold ────────────────────────────────────────────────────
|
|
370
|
+
const thresholdStr = await input({ message: 'Coverage threshold (%):', default: '80' });
|
|
88
371
|
const threshold = parseInt(thresholdStr, 10);
|
|
89
|
-
// ── Build config
|
|
372
|
+
// ── Build config ──────────────────────────────────────────────────────────
|
|
90
373
|
const config = {
|
|
91
374
|
provider: preset.provider,
|
|
92
375
|
model: preset.model,
|
|
@@ -94,7 +377,7 @@ export default class Init extends Command {
|
|
|
94
377
|
testRunner: testRunner,
|
|
95
378
|
coverageFormat: 'lcov',
|
|
96
379
|
coverageDir: 'coverage',
|
|
97
|
-
sourceDir
|
|
380
|
+
sourceDir,
|
|
98
381
|
threshold,
|
|
99
382
|
maxIterations: 3,
|
|
100
383
|
};
|
|
@@ -102,15 +385,21 @@ export default class Init extends Command {
|
|
|
102
385
|
config.baseURL = preset.baseURL;
|
|
103
386
|
if (mocksFile)
|
|
104
387
|
config.mocksFile = mocksFile;
|
|
105
|
-
|
|
388
|
+
if (setupFile)
|
|
389
|
+
config.setupFile = setupFile;
|
|
106
390
|
const clean = Object.fromEntries(Object.entries(config).filter(([, v]) => v !== undefined));
|
|
107
391
|
await writeFile(configPath, JSON.stringify(clean, null, 2) + '\n');
|
|
108
|
-
// ── Summary
|
|
392
|
+
// ── Summary ───────────────────────────────────────────────────────────────
|
|
109
393
|
this.log(chalk.green('\n✓ Created .lacuna.json\n'));
|
|
110
394
|
this.log(chalk.bold('Setup summary:'));
|
|
111
395
|
this.log(` Model: ${chalk.cyan(preset.model)} via ${preset.provider}`);
|
|
112
396
|
this.log(` Runner: ${chalk.cyan(testRunner)}`);
|
|
397
|
+
this.log(` Source dir: ${chalk.cyan(sourceDir)}`);
|
|
113
398
|
this.log(` Threshold: ${threshold}%`);
|
|
399
|
+
if (setupFile)
|
|
400
|
+
this.log(` Setup file: ${chalk.cyan(setupFile)}`);
|
|
401
|
+
if (mocksFile)
|
|
402
|
+
this.log(` Mocks file: ${chalk.cyan(mocksFile)}`);
|
|
114
403
|
if (preset.apiKeyEnv) {
|
|
115
404
|
const keySet = process.env[preset.apiKeyEnv];
|
|
116
405
|
const keyStatus = keySet ? chalk.green('set ✓') : chalk.red('NOT set ✗');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/validate.d.ts
CHANGED
|
@@ -2,6 +2,15 @@ export declare function hasTestFunctions(code: string): boolean;
|
|
|
2
2
|
export declare function enrichNoTestsError(output: string): string;
|
|
3
3
|
export declare function isZeroTestsOutput(raw: string): boolean;
|
|
4
4
|
export declare function parsePassCount(output: string): number;
|
|
5
|
+
export declare function stripLeadingProse(code: string): {
|
|
6
|
+
code: string;
|
|
7
|
+
stripped: string | null;
|
|
8
|
+
};
|
|
9
|
+
export declare function mergeMocksContent(existing: string, incoming: string): string;
|
|
10
|
+
export declare function sanitizeMocksContent(raw: string): {
|
|
11
|
+
code: string;
|
|
12
|
+
stripped: boolean;
|
|
13
|
+
};
|
|
5
14
|
export declare function buildStructureBrokenMessage(initialError: string, currentError: string): string;
|
|
6
15
|
export declare function buildRegressionMessage(initialError: string, currentError: string, baselinePass: number, currentPass: number): string;
|
|
7
16
|
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/lib/validate.ts"],"names":[],"mappings":"AAYA,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGtD;AAID,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAgBzD;AAID,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEtD;AAKD,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAOrD;AAMD,wBAAgB,2BAA2B,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAgB9F;AAID,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,GAClB,MAAM,CAYR"}
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/lib/validate.ts"],"names":[],"mappings":"AAYA,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGtD;AAID,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAgBzD;AAID,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEtD;AAKD,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAOrD;AAOD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAWzF;AAOD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAqC5E;AA0CD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAiDrF;AAMD,wBAAgB,2BAA2B,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAgB9F;AAID,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,GAClB,MAAM,CAYR"}
|
package/dist/lib/validate.js
CHANGED
|
@@ -48,6 +48,157 @@ export function parsePassCount(output) {
|
|
|
48
48
|
const m = output.match(/(\d+)\s+passed/);
|
|
49
49
|
return m ? parseInt(m[1], 10) : 0;
|
|
50
50
|
}
|
|
51
|
+
// Strips leading prose/thinking lines from generated code output.
|
|
52
|
+
// When a model bleeds reasoning into <code_output>, the file starts with fragments
|
|
53
|
+
// like ", nothing else." or "I'll write the test now." before the real code begins.
|
|
54
|
+
// Scans forward to the first valid TypeScript/code line and strips everything before it.
|
|
55
|
+
// Returns the cleaned code and whether anything was stripped.
|
|
56
|
+
export function stripLeadingProse(code) {
|
|
57
|
+
// Valid first lines for TypeScript/JS, Python, and Go — all languages lacuna supports.
|
|
58
|
+
const VALID_START = /^\s*(import\b|export\b|const\b|let\b|var\b|function\b|class\b|describe\s*\(|it\s*\(|test\s*[(\s]|vi\.|jest\.|before(?:Each|All)\b|after(?:Each|All)\b|\/\/|\/\*|\*\s|type\s+\w|interface\s+\w|enum\s+\w|def\s+\w|async\s+def\s+\w|@\w|pytest\b|package\s+\w|func\s+\w|#)/;
|
|
59
|
+
const lines = code.split('\n');
|
|
60
|
+
const firstCode = lines.findIndex(l => VALID_START.test(l));
|
|
61
|
+
if (firstCode <= 0)
|
|
62
|
+
return { code, stripped: null }; // starts correctly or no code found
|
|
63
|
+
const leakedText = lines.slice(0, firstCode).join('\n').trim().slice(0, 120);
|
|
64
|
+
return { code: lines.slice(firstCode).join('\n'), stripped: leakedText };
|
|
65
|
+
}
|
|
66
|
+
// Merges new mocks content with an existing mocks file without duplicating.
|
|
67
|
+
// Three cases:
|
|
68
|
+
// 1. Existing is empty → use incoming as-is
|
|
69
|
+
// 2. Incoming contains all existing exports (complete replacement) → use incoming
|
|
70
|
+
// 3. Incoming is partial → extract ONLY the new exports and append them
|
|
71
|
+
export function mergeMocksContent(existing, incoming) {
|
|
72
|
+
const existingNames = new Set(extractExportNames(existing));
|
|
73
|
+
if (existingNames.size === 0)
|
|
74
|
+
return incoming;
|
|
75
|
+
const incomingNames = extractExportNames(incoming);
|
|
76
|
+
// Case 2: incoming is a superset — safe to replace entirely
|
|
77
|
+
if (incomingNames.length > 0 && [...existingNames].every(n => incomingNames.includes(n))) {
|
|
78
|
+
return incoming;
|
|
79
|
+
}
|
|
80
|
+
// Case 3: incoming is partial — extract only truly new export declarations
|
|
81
|
+
const newNames = new Set(incomingNames.filter(n => !existingNames.has(n)));
|
|
82
|
+
if (newNames.size === 0)
|
|
83
|
+
return existing; // nothing new, keep existing unchanged
|
|
84
|
+
// Walk lines and capture blocks that belong to new exports.
|
|
85
|
+
// Capturing starts on `export const/function/class X` where X is new,
|
|
86
|
+
// and continues until the next export declaration (handles multi-line exports).
|
|
87
|
+
const lines = incoming.split('\n');
|
|
88
|
+
const toAppend = [];
|
|
89
|
+
let capturing = false;
|
|
90
|
+
for (const line of lines) {
|
|
91
|
+
const exportMatch = line.match(/^\s*export\s+(?:const|let|var|function|async\s+function|class)\s+(\w+)/);
|
|
92
|
+
if (exportMatch) {
|
|
93
|
+
capturing = newNames.has(exportMatch[1]);
|
|
94
|
+
}
|
|
95
|
+
else if (/^\s*import\b/.test(line)) {
|
|
96
|
+
// Include import statements that are not already in the existing file
|
|
97
|
+
if (!existing.includes(line.trim()))
|
|
98
|
+
toAppend.push(line);
|
|
99
|
+
capturing = false;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (capturing)
|
|
103
|
+
toAppend.push(line);
|
|
104
|
+
}
|
|
105
|
+
const appended = toAppend.join('\n').trim();
|
|
106
|
+
return appended ? existing.trimEnd() + '\n\n' + appended : existing;
|
|
107
|
+
}
|
|
108
|
+
function extractExportNames(code) {
|
|
109
|
+
const names = [];
|
|
110
|
+
for (const m of code.matchAll(/^export\s+(?:const|let|var|function|class|async\s+function)\s+(\w+)/gm))
|
|
111
|
+
names.push(m[1]);
|
|
112
|
+
for (const m of code.matchAll(/^export\s*\{([^}]+)\}/gm)) {
|
|
113
|
+
for (const part of m[1].split(',')) {
|
|
114
|
+
const alias = part.trim().split(/\s+as\s+/).pop()?.trim();
|
|
115
|
+
if (alias && /^\w+$/.test(alias))
|
|
116
|
+
names.push(alias);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return [...new Set(names)];
|
|
120
|
+
}
|
|
121
|
+
// Returns true when content is clearly prose/thinking rather than TypeScript.
|
|
122
|
+
// Conservative by design: any real code line (export, const, vi., import) means
|
|
123
|
+
// it's not prose, even if comments look sentence-like.
|
|
124
|
+
function isProseContent(content) {
|
|
125
|
+
const lines = content.split('\n').filter(l => l.trim().length > 0);
|
|
126
|
+
if (lines.length === 0)
|
|
127
|
+
return false;
|
|
128
|
+
// If ANY line looks like real TypeScript/JS code, it's not prose.
|
|
129
|
+
// This prevents false positives on mock files with sentence-like comments.
|
|
130
|
+
const hasCode = lines.some(l => /^\s*(import\s|export\s+(const|let|function|class|default|type|interface)\s)/.test(l) ||
|
|
131
|
+
/^\s*(const|let|var)\s+\w/.test(l) ||
|
|
132
|
+
/^\s*vi\.|^\s*jest\./.test(l) ||
|
|
133
|
+
/^\s*(beforeEach|afterEach|beforeAll|afterAll)\s*\(/.test(l));
|
|
134
|
+
if (hasCode)
|
|
135
|
+
return false;
|
|
136
|
+
// No code found — check for thinking/reasoning patterns that confirm it's prose
|
|
137
|
+
const thinkingPatterns = /\bI think\b|\bLet me\b|\bActually,?\s|\bBut wait\b|\bHmm,?\b/m.test(content);
|
|
138
|
+
const bulletLines = lines.filter(l => /^[-*]\s/.test(l)).length;
|
|
139
|
+
return thinkingPatterns || bulletLines > 5;
|
|
140
|
+
}
|
|
141
|
+
// Removes content that does not belong in a shared mock file.
|
|
142
|
+
// Strips: test blocks (describe/it/test/expect), framework config
|
|
143
|
+
// (defineConfig exports, vitest/jest config objects), whole-file prose,
|
|
144
|
+
// and trailing prose that appears after valid mock definitions.
|
|
145
|
+
export function sanitizeMocksContent(raw) {
|
|
146
|
+
// Prose/thinking detection — reject wholesale if content is not code
|
|
147
|
+
if (isProseContent(raw))
|
|
148
|
+
return { code: '', stripped: true };
|
|
149
|
+
// Reject if content looks like a framework config file
|
|
150
|
+
const CONFIG_FILE_RE = /defineConfig\s*\(|module\.exports\s*=\s*\{[^}]*(?:test|resolve|plugins)\s*:/s;
|
|
151
|
+
if (CONFIG_FILE_RE.test(raw))
|
|
152
|
+
return { code: '', stripped: true };
|
|
153
|
+
const TEST_START = /^\s*(describe|it|test)\s*[.(]|^\s*expect\s*\(/;
|
|
154
|
+
const CONFIG_START = /^\s*export\s+default\s+(defineConfig\s*\(|\{)|^\s*module\.exports\s*=/;
|
|
155
|
+
const lines = raw.split('\n');
|
|
156
|
+
const kept = [];
|
|
157
|
+
let depth = 0;
|
|
158
|
+
let inBlock = false;
|
|
159
|
+
let stripped = false;
|
|
160
|
+
for (const line of lines) {
|
|
161
|
+
if (!inBlock && (TEST_START.test(line) || CONFIG_START.test(line))) {
|
|
162
|
+
inBlock = true;
|
|
163
|
+
stripped = true;
|
|
164
|
+
}
|
|
165
|
+
if (inBlock) {
|
|
166
|
+
for (const ch of line) {
|
|
167
|
+
if (ch === '{' || ch === '(')
|
|
168
|
+
depth++;
|
|
169
|
+
else if (ch === '}' || ch === ')') {
|
|
170
|
+
depth--;
|
|
171
|
+
if (depth < 0)
|
|
172
|
+
depth = 0;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (depth === 0)
|
|
176
|
+
inBlock = false;
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
kept.push(line);
|
|
180
|
+
}
|
|
181
|
+
// Truncate trailing prose that appears after valid mock definitions.
|
|
182
|
+
// Pattern: valid exports → orphaned quote/bracket → bullet-point thinking.
|
|
183
|
+
const CODE_LINE = /^\s*(export\b|import\b|const\b|let\b|var\b|vi\.|jest\.|before(?:Each|All)|after(?:Each|All)|\/\/|\/\*)/;
|
|
184
|
+
const PROSE_LINE = /^\s*["'`]\s*$|^\s*[-*]\s+[A-Za-z]|^\s{1,8}-\s+[A-Z]/;
|
|
185
|
+
let foundCode = false;
|
|
186
|
+
let truncateAt = -1;
|
|
187
|
+
for (let i = 0; i < kept.length; i++) {
|
|
188
|
+
if (CODE_LINE.test(kept[i])) {
|
|
189
|
+
foundCode = true;
|
|
190
|
+
truncateAt = -1;
|
|
191
|
+
}
|
|
192
|
+
else if (foundCode && PROSE_LINE.test(kept[i]) && truncateAt === -1) {
|
|
193
|
+
truncateAt = i;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (truncateAt !== -1) {
|
|
197
|
+
kept.splice(truncateAt);
|
|
198
|
+
stripped = true;
|
|
199
|
+
}
|
|
200
|
+
return { code: kept.join('\n').trim(), stripped };
|
|
201
|
+
}
|
|
51
202
|
const RULE_DIVIDER = '─'.repeat(60);
|
|
52
203
|
// Retry message when a fix attempt caused Vitest to collect 0 tests —
|
|
53
204
|
// the model likely broke an import. Anchors the model to the original error.
|