lacuna-cli 0.1.1
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 +451 -0
- package/bin/run.js +5 -0
- package/dist/agent/context.d.ts +25 -0
- package/dist/agent/context.d.ts.map +1 -0
- package/dist/agent/context.js +366 -0
- package/dist/agent/context.js.map +1 -0
- package/dist/agent/fix-loop.d.ts +20 -0
- package/dist/agent/fix-loop.d.ts.map +1 -0
- package/dist/agent/fix-loop.js +466 -0
- package/dist/agent/fix-loop.js.map +1 -0
- package/dist/agent/generator.d.ts +35 -0
- package/dist/agent/generator.d.ts.map +1 -0
- package/dist/agent/generator.js +220 -0
- package/dist/agent/generator.js.map +1 -0
- package/dist/agent/loop.d.ts +23 -0
- package/dist/agent/loop.d.ts.map +1 -0
- package/dist/agent/loop.js +394 -0
- package/dist/agent/loop.js.map +1 -0
- package/dist/agent/project-memory.d.ts +10 -0
- package/dist/agent/project-memory.d.ts.map +1 -0
- package/dist/agent/project-memory.js +57 -0
- package/dist/agent/project-memory.js.map +1 -0
- package/dist/agent/prompts.d.ts +44 -0
- package/dist/agent/prompts.d.ts.map +1 -0
- package/dist/agent/prompts.js +377 -0
- package/dist/agent/prompts.js.map +1 -0
- package/dist/ci/comment.d.ts +2 -0
- package/dist/ci/comment.d.ts.map +1 -0
- package/dist/ci/comment.js +97 -0
- package/dist/ci/comment.js.map +1 -0
- package/dist/ci/parse-outputs.d.ts +2 -0
- package/dist/ci/parse-outputs.d.ts.map +1 -0
- package/dist/ci/parse-outputs.js +30 -0
- package/dist/ci/parse-outputs.js.map +1 -0
- package/dist/commands/analyze.d.ts +13 -0
- package/dist/commands/analyze.d.ts.map +1 -0
- package/dist/commands/analyze.js +151 -0
- package/dist/commands/analyze.js.map +1 -0
- package/dist/commands/fix.d.ts +15 -0
- package/dist/commands/fix.d.ts.map +1 -0
- package/dist/commands/fix.js +106 -0
- package/dist/commands/fix.js.map +1 -0
- package/dist/commands/generate.d.ts +18 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +129 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +131 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/run.d.ts +10 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/run.js +45 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/lib/config.d.ts +58 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +68 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/coverage/gaps.d.ts +12 -0
- package/dist/lib/coverage/gaps.d.ts.map +1 -0
- package/dist/lib/coverage/gaps.js +186 -0
- package/dist/lib/coverage/gaps.js.map +1 -0
- package/dist/lib/coverage/index.d.ts +7 -0
- package/dist/lib/coverage/index.d.ts.map +1 -0
- package/dist/lib/coverage/index.js +24 -0
- package/dist/lib/coverage/index.js.map +1 -0
- package/dist/lib/coverage/json.d.ts +3 -0
- package/dist/lib/coverage/json.d.ts.map +1 -0
- package/dist/lib/coverage/json.js +24 -0
- package/dist/lib/coverage/json.js.map +1 -0
- package/dist/lib/coverage/lcov.d.ts +3 -0
- package/dist/lib/coverage/lcov.d.ts.map +1 -0
- package/dist/lib/coverage/lcov.js +58 -0
- package/dist/lib/coverage/lcov.js.map +1 -0
- package/dist/lib/coverage/types.d.ts +27 -0
- package/dist/lib/coverage/types.d.ts.map +1 -0
- package/dist/lib/coverage/types.js +2 -0
- package/dist/lib/coverage/types.js.map +1 -0
- package/dist/lib/coverage-spinner.d.ts +6 -0
- package/dist/lib/coverage-spinner.d.ts.map +1 -0
- package/dist/lib/coverage-spinner.js +101 -0
- package/dist/lib/coverage-spinner.js.map +1 -0
- package/dist/lib/detector.d.ts +13 -0
- package/dist/lib/detector.d.ts.map +1 -0
- package/dist/lib/detector.js +106 -0
- package/dist/lib/detector.js.map +1 -0
- package/dist/lib/extract-error.d.ts +2 -0
- package/dist/lib/extract-error.d.ts.map +1 -0
- package/dist/lib/extract-error.js +116 -0
- package/dist/lib/extract-error.js.map +1 -0
- package/dist/lib/providers/anthropic.d.ts +8 -0
- package/dist/lib/providers/anthropic.d.ts.map +1 -0
- package/dist/lib/providers/anthropic.js +38 -0
- package/dist/lib/providers/anthropic.js.map +1 -0
- package/dist/lib/providers/index.d.ts +6 -0
- package/dist/lib/providers/index.d.ts.map +1 -0
- package/dist/lib/providers/index.js +27 -0
- package/dist/lib/providers/index.js.map +1 -0
- package/dist/lib/providers/openai-compatible.d.ts +11 -0
- package/dist/lib/providers/openai-compatible.d.ts.map +1 -0
- package/dist/lib/providers/openai-compatible.js +93 -0
- package/dist/lib/providers/openai-compatible.js.map +1 -0
- package/dist/lib/providers/types.d.ts +17 -0
- package/dist/lib/providers/types.d.ts.map +1 -0
- package/dist/lib/providers/types.js +97 -0
- package/dist/lib/providers/types.js.map +1 -0
- package/dist/lib/report-upload.d.ts +3 -0
- package/dist/lib/report-upload.d.ts.map +1 -0
- package/dist/lib/report-upload.js +15 -0
- package/dist/lib/report-upload.js.map +1 -0
- package/dist/lib/reporter.d.ts +51 -0
- package/dist/lib/reporter.d.ts.map +1 -0
- package/dist/lib/reporter.js +172 -0
- package/dist/lib/reporter.js.map +1 -0
- package/dist/lib/runner.d.ts +9 -0
- package/dist/lib/runner.d.ts.map +1 -0
- package/dist/lib/runner.js +50 -0
- package/dist/lib/runner.js.map +1 -0
- package/dist/lib/skeleton.d.ts +8 -0
- package/dist/lib/skeleton.d.ts.map +1 -0
- package/dist/lib/skeleton.js +122 -0
- package/dist/lib/skeleton.js.map +1 -0
- package/dist/lib/streaming-viewer.d.ts +14 -0
- package/dist/lib/streaming-viewer.d.ts.map +1 -0
- package/dist/lib/streaming-viewer.js +80 -0
- package/dist/lib/streaming-viewer.js.map +1 -0
- package/dist/lib/tips.d.ts +16 -0
- package/dist/lib/tips.d.ts.map +1 -0
- package/dist/lib/tips.js +76 -0
- package/dist/lib/tips.js.map +1 -0
- package/dist/lib/typecheck.d.ts +3 -0
- package/dist/lib/typecheck.d.ts.map +1 -0
- package/dist/lib/typecheck.js +28 -0
- package/dist/lib/typecheck.js.map +1 -0
- package/dist/lib/validate.d.ts +7 -0
- package/dist/lib/validate.d.ts.map +1 -0
- package/dist/lib/validate.js +82 -0
- package/dist/lib/validate.js.map +1 -0
- package/dist/lib/worker-display.d.ts +45 -0
- package/dist/lib/worker-display.d.ts.map +1 -0
- package/dist/lib/worker-display.js +168 -0
- package/dist/lib/worker-display.js.map +1 -0
- package/oclif.manifest.json +295 -0
- package/package.json +62 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// Generates a compact structural summary of a TypeScript/JavaScript source file.
|
|
2
|
+
// Large files are skeletonized: only the functions that need to be tested are expanded
|
|
3
|
+
// to their full implementation; everything else is collapsed to its signature.
|
|
4
|
+
// This cuts prompt size by 60–80% on large files without losing signal for the AI.
|
|
5
|
+
const SKELETON_THRESHOLD = 80; // lines; files at or below this are returned as-is
|
|
6
|
+
// ─── Block-end finder ────────────────────────────────────────────────────────
|
|
7
|
+
// Finds the line index of the closing } for a block that opens at startLine.
|
|
8
|
+
// Uses a simple state machine to skip braces inside string literals.
|
|
9
|
+
function findBlockEnd(lines, startLine) {
|
|
10
|
+
let depth = 0;
|
|
11
|
+
let inString = null;
|
|
12
|
+
let escaped = false;
|
|
13
|
+
let opened = false;
|
|
14
|
+
for (let i = startLine; i < lines.length; i++) {
|
|
15
|
+
const line = lines[i];
|
|
16
|
+
for (let j = 0; j < line.length; j++) {
|
|
17
|
+
const ch = line[j];
|
|
18
|
+
if (escaped) {
|
|
19
|
+
escaped = false;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (ch === '\\' && inString) {
|
|
23
|
+
escaped = true;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (inString) {
|
|
27
|
+
if (ch === inString)
|
|
28
|
+
inString = null;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
32
|
+
inString = ch;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (ch === '{') {
|
|
36
|
+
depth++;
|
|
37
|
+
opened = true;
|
|
38
|
+
}
|
|
39
|
+
if (ch === '}') {
|
|
40
|
+
depth--;
|
|
41
|
+
if (opened && depth === 0)
|
|
42
|
+
return i;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return lines.length - 1;
|
|
47
|
+
}
|
|
48
|
+
// ─── Declaration name extractor ──────────────────────────────────────────────
|
|
49
|
+
// Returns the identifier name from a top-level declaration line, or null if
|
|
50
|
+
// the line isn't a recognisable declaration.
|
|
51
|
+
function extractDeclaredName(line) {
|
|
52
|
+
const s = line.trim().replace(/^export\s+(default\s+)?/, '');
|
|
53
|
+
// function name / async function name
|
|
54
|
+
const fn = s.match(/^(?:async\s+)?function\s+(\w+)/);
|
|
55
|
+
if (fn)
|
|
56
|
+
return fn[1];
|
|
57
|
+
// class Name
|
|
58
|
+
const cl = s.match(/^class\s+(\w+)/);
|
|
59
|
+
if (cl)
|
|
60
|
+
return cl[1];
|
|
61
|
+
// const/let/var name = (...) => or = function or = async (
|
|
62
|
+
const cv = s.match(/^(?:const|let|var)\s+(\w+)(?:\s*:\s*[^=]+)?\s*=\s*(?:async\s+)?(?:\(|function\b|\w+\s*=>)/);
|
|
63
|
+
if (cv)
|
|
64
|
+
return cv[1];
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
// ─── Skeleton builder ─────────────────────────────────────────────────────────
|
|
68
|
+
export function shouldUseSkeleton(code) {
|
|
69
|
+
return code.split('\n').length > SKELETON_THRESHOLD;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Returns a skeletonized version of sourceCode.
|
|
73
|
+
* expandFunctions: names of functions whose full body must be included (the uncovered ones).
|
|
74
|
+
* If the file is short enough, returns the original code unchanged.
|
|
75
|
+
*/
|
|
76
|
+
export function buildSourceSkeleton(sourceCode, expandFunctions = []) {
|
|
77
|
+
if (!shouldUseSkeleton(sourceCode))
|
|
78
|
+
return sourceCode;
|
|
79
|
+
const lines = sourceCode.split('\n');
|
|
80
|
+
const expandSet = new Set(expandFunctions);
|
|
81
|
+
const result = [];
|
|
82
|
+
let i = 0;
|
|
83
|
+
while (i < lines.length) {
|
|
84
|
+
const line = lines[i];
|
|
85
|
+
const trimmed = line.trim();
|
|
86
|
+
// ── Always keep verbatim ──────────────────────────────────────────────────
|
|
87
|
+
if (!trimmed ||
|
|
88
|
+
trimmed.startsWith('//') ||
|
|
89
|
+
/^\/?\*/.test(trimmed) || // block comments
|
|
90
|
+
trimmed.startsWith('import ') ||
|
|
91
|
+
trimmed.startsWith('@') || // decorators
|
|
92
|
+
/^export\s+(type|interface|enum)\b/.test(trimmed) ||
|
|
93
|
+
/^(?:type|interface|enum)\s+\w/.test(trimmed)) {
|
|
94
|
+
result.push(line);
|
|
95
|
+
i++;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
// ── Detect a block-opening declaration ────────────────────────────────────
|
|
99
|
+
const name = extractDeclaredName(trimmed);
|
|
100
|
+
const opensBlock = /\{/.test(trimmed) && !/^\s*\/\//.test(trimmed);
|
|
101
|
+
if (name && opensBlock) {
|
|
102
|
+
const blockEnd = findBlockEnd(lines, i);
|
|
103
|
+
const bodyLines = blockEnd - i;
|
|
104
|
+
if (expandSet.has(name)) {
|
|
105
|
+
// Full implementation — the AI needs this to write assertions
|
|
106
|
+
result.push(...lines.slice(i, blockEnd + 1));
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
// Collapse: show the signature line with a stub body
|
|
110
|
+
const sigLine = line.replace(/\{[\s\S]*/, '').trimEnd();
|
|
111
|
+
result.push(`${sigLine}${sigLine.trimEnd().endsWith(')') || sigLine.trimEnd().endsWith('>') ? ' ' : ''}` +
|
|
112
|
+
`{ /* ... (${bodyLines} line${bodyLines === 1 ? '' : 's'}) */ }`);
|
|
113
|
+
}
|
|
114
|
+
i = blockEnd + 1;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
result.push(line);
|
|
118
|
+
i++;
|
|
119
|
+
}
|
|
120
|
+
return result.join('\n');
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=skeleton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skeleton.js","sourceRoot":"","sources":["../../src/lib/skeleton.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare class StreamingFileViewer {
|
|
2
|
+
private readonly filename;
|
|
3
|
+
private content;
|
|
4
|
+
private rendered;
|
|
5
|
+
private tick;
|
|
6
|
+
private timer;
|
|
7
|
+
readonly isTTY: boolean;
|
|
8
|
+
constructor(filename: string);
|
|
9
|
+
start(): void;
|
|
10
|
+
append(token: string): void;
|
|
11
|
+
stop(): void;
|
|
12
|
+
private render;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=streaming-viewer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streaming-viewer.d.ts","sourceRoot":"","sources":["../../src/lib/streaming-viewer.ts"],"names":[],"mappings":"AAUA,qBAAa,mBAAmB;IAOlB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IANrC,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,QAAQ,CAAI;IACpB,OAAO,CAAC,IAAI,CAAI;IAChB,OAAO,CAAC,KAAK,CAA8C;IAC3D,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;gBAEM,QAAQ,EAAE,MAAM;IAI7C,KAAK;IASL,MAAM,CAAC,KAAK,EAAE,MAAM;IAMpB,IAAI;IASJ,OAAO,CAAC,MAAM;CAwCf"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
const PANEL_ROWS = 12; // visible code lines inside the panel
|
|
3
|
+
const BLINK_EVERY = 4; // ticks between cursor flips: 4 × 80ms = 320ms
|
|
4
|
+
// Live panel that shows a test file being written token-by-token.
|
|
5
|
+
// Draws a fixed-height bordered box; as lines accumulate the panel scrolls
|
|
6
|
+
// so the cursor is always visible at the bottom. Redraws at 80ms via setInterval.
|
|
7
|
+
//
|
|
8
|
+
// Non-TTY fallback: streams tokens directly to stdout (no panel, no cursor).
|
|
9
|
+
export class StreamingFileViewer {
|
|
10
|
+
filename;
|
|
11
|
+
content = '';
|
|
12
|
+
rendered = 0;
|
|
13
|
+
tick = 0;
|
|
14
|
+
timer = null;
|
|
15
|
+
isTTY;
|
|
16
|
+
constructor(filename) {
|
|
17
|
+
this.filename = filename;
|
|
18
|
+
this.isTTY = Boolean(process.stdout.isTTY);
|
|
19
|
+
}
|
|
20
|
+
start() {
|
|
21
|
+
if (!this.isTTY) {
|
|
22
|
+
process.stdout.write(`\n ✍ ${this.filename}\n`);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
this.render();
|
|
26
|
+
this.timer = setInterval(() => { this.tick++; this.render(); }, 80);
|
|
27
|
+
}
|
|
28
|
+
append(token) {
|
|
29
|
+
this.content += token;
|
|
30
|
+
if (!this.isTTY)
|
|
31
|
+
process.stdout.write(token);
|
|
32
|
+
// In TTY mode the setInterval render loop picks up the new content
|
|
33
|
+
}
|
|
34
|
+
stop() {
|
|
35
|
+
if (this.timer) {
|
|
36
|
+
clearInterval(this.timer);
|
|
37
|
+
this.timer = null;
|
|
38
|
+
}
|
|
39
|
+
if (this.isTTY && this.rendered > 0) {
|
|
40
|
+
process.stdout.write(`\x1B[${this.rendered}A\x1B[0J`);
|
|
41
|
+
this.rendered = 0;
|
|
42
|
+
}
|
|
43
|
+
this.content = '';
|
|
44
|
+
}
|
|
45
|
+
render() {
|
|
46
|
+
if (!this.isTTY)
|
|
47
|
+
return;
|
|
48
|
+
if (this.rendered > 0)
|
|
49
|
+
process.stdout.write(`\x1B[${this.rendered}A\x1B[0J`);
|
|
50
|
+
const cols = Math.max(60, process.stdout.columns ?? 80);
|
|
51
|
+
const panelWidth = Math.min(cols - 4, 82);
|
|
52
|
+
const innerWidth = panelWidth - 4; // space between "│ " and " │"
|
|
53
|
+
const cursor = Math.floor(this.tick / BLINK_EVERY) % 2 === 0 ? '▌' : ' ';
|
|
54
|
+
const rawLines = (this.content + cursor).split('\n');
|
|
55
|
+
const displayLines = rawLines.slice(-PANEL_ROWS);
|
|
56
|
+
while (displayLines.length < PANEL_ROWS)
|
|
57
|
+
displayLines.unshift('');
|
|
58
|
+
const lines = [''];
|
|
59
|
+
// Header
|
|
60
|
+
const title = ` ✍ ${this.filename} `;
|
|
61
|
+
const headerFill = Math.max(0, panelWidth - title.length - 4); // 4 = '╭──' + '╮'
|
|
62
|
+
lines.push(` ${chalk.dim('╭──')}${chalk.bold.cyan(title)}${chalk.dim('─'.repeat(headerFill) + '╮')}`);
|
|
63
|
+
// Code rows
|
|
64
|
+
for (const line of displayLines) {
|
|
65
|
+
const text = line.length > innerWidth ? line.slice(0, innerWidth - 1) + '…' : line;
|
|
66
|
+
lines.push(` ${chalk.dim('│')} ${chalk.white(text.padEnd(innerWidth))} ${chalk.dim('│')}`);
|
|
67
|
+
}
|
|
68
|
+
// Footer with running line count
|
|
69
|
+
const lineCount = rawLines.length - 1; // -1 for the cursor appended to last line
|
|
70
|
+
const footerText = ` ${lineCount} line${lineCount !== 1 ? 's' : ''} `;
|
|
71
|
+
const footerFill = Math.max(0, panelWidth - footerText.length - 2); // 2 = '╰' + '╯'
|
|
72
|
+
lines.push(` ${chalk.dim('╰' + '─'.repeat(footerFill))}${chalk.dim(footerText)}${chalk.dim('╯')}`);
|
|
73
|
+
lines.push('');
|
|
74
|
+
const out = lines.join('\n');
|
|
75
|
+
process.stdout.write(out);
|
|
76
|
+
// Count \n chars — NOT lines.length (same rule as WorkerDisplay and coverage-spinner)
|
|
77
|
+
this.rendered = (out.match(/\n/g) ?? []).length;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=streaming-viewer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streaming-viewer.js","sourceRoot":"","sources":["../../src/lib/streaming-viewer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface TipContext {
|
|
2
|
+
workers: number;
|
|
3
|
+
targetFile?: string;
|
|
4
|
+
verbose: boolean;
|
|
5
|
+
dryRun: boolean;
|
|
6
|
+
fresh?: boolean;
|
|
7
|
+
model: string;
|
|
8
|
+
threshold: number;
|
|
9
|
+
mocksFile?: string;
|
|
10
|
+
ignore?: string[];
|
|
11
|
+
command?: 'generate' | 'fix';
|
|
12
|
+
}
|
|
13
|
+
export declare function getActiveTips(ctx: TipContext): string[];
|
|
14
|
+
export declare function createTipRotator(tips: string[]): () => string | null;
|
|
15
|
+
export declare function formatTip(text: string): string;
|
|
16
|
+
//# sourceMappingURL=tips.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tips.d.ts","sourceRoot":"","sources":["../../src/lib/tips.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,OAAO,CAAC,EAAE,UAAU,GAAG,KAAK,CAAA;CAC7B;AAkED,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,EAAE,CAEvD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,GAAG,IAAI,CAQpE;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C"}
|
package/dist/lib/tips.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
const TIPS = [
|
|
3
|
+
{
|
|
4
|
+
text: 'use -w 4 (--workers) to process multiple files in parallel',
|
|
5
|
+
hide: (ctx) => ctx.workers > 1,
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
text: 'use -f src/utils/math.ts (--file) to target a single file instead of the whole project',
|
|
9
|
+
hide: (ctx) => Boolean(ctx.targetFile),
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
text: 'use --dry-run to preview what would be written without touching any files',
|
|
13
|
+
hide: (ctx) => ctx.dryRun,
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
text: 'use -v (--verbose) to stream the AI\'s output token by token as it generates',
|
|
17
|
+
hide: (ctx) => ctx.verbose,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
text: 'use -m claude-opus-4-7 (--model) to switch to a more capable model for tough files',
|
|
21
|
+
hide: (ctx) => ctx.model.includes('opus'),
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
text: 'use --fresh to force a new coverage run instead of reusing a cached report',
|
|
25
|
+
hide: (ctx) => Boolean(ctx.fresh) || ctx.command === 'fix',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
text: 'use -t 90 (--threshold) to raise the minimum coverage bar',
|
|
29
|
+
hide: (ctx) => ctx.threshold !== 80 || ctx.command === 'fix',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
text: 'set mocksFile in .lacuna.json to share mocks across all generated tests',
|
|
33
|
+
hide: (ctx) => Boolean(ctx.mocksFile),
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
text: 'add paths to ignore[] in .lacuna.json to skip directories (e.g. "src/graphql/")',
|
|
37
|
+
hide: (ctx) => Boolean(ctx.ignore?.length),
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
text: 'run lacuna fix to automatically repair failing tests without rewriting them from scratch',
|
|
41
|
+
hide: (ctx) => ctx.command === 'fix',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
text: 'run lacuna analyze to inspect coverage gaps without writing any files',
|
|
45
|
+
hide: () => false,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
text: 'use --format json --output report.json to export results for scripts or CI',
|
|
49
|
+
hide: (ctx) => ctx.command === 'fix',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
text: 'increase coverageTimeout in .lacuna.json if your test suite is killed before finishing',
|
|
53
|
+
hide: () => false,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
text: 'set maxTokens in .lacuna.json if tests are cut off mid-generation (lower for Groq/Ollama, raise for large files)',
|
|
57
|
+
hide: () => false,
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
export function getActiveTips(ctx) {
|
|
61
|
+
return TIPS.filter((t) => !t.hide(ctx)).map((t) => t.text);
|
|
62
|
+
}
|
|
63
|
+
export function createTipRotator(tips) {
|
|
64
|
+
if (tips.length === 0)
|
|
65
|
+
return () => null;
|
|
66
|
+
let idx = 0;
|
|
67
|
+
return () => {
|
|
68
|
+
const tip = tips[idx % tips.length];
|
|
69
|
+
idx++;
|
|
70
|
+
return tip ?? null;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
export function formatTip(text) {
|
|
74
|
+
return ` ${chalk.cyan('Tip:')} ${chalk.dim(text)}`;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=tips.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tips.js","sourceRoot":"","sources":["../../src/lib/tips.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typecheck.d.ts","sourceRoot":"","sources":["../../src/lib/typecheck.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AAOxD,wBAAsB,aAAa,CACjC,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,IAAI,CAAC,mBAAmB,EAAE,UAAU,CAAC,GACzC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAoBxB"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { access } from 'fs/promises';
|
|
2
|
+
import { join, basename } from 'path';
|
|
3
|
+
import { runCommand } from './runner.js';
|
|
4
|
+
// Run tsc --noEmit on the project and return type errors that belong to the given
|
|
5
|
+
// test file. Errors in other files are intentionally ignored — we only care about
|
|
6
|
+
// what the AI just wrote. Returns null when there are no errors or when type-checking
|
|
7
|
+
// is not applicable (non-TypeScript project, no tsconfig, tsc not available).
|
|
8
|
+
export async function typeCheckFile(absTestPath, cwd, env) {
|
|
9
|
+
if (env.language !== 'typescript')
|
|
10
|
+
return null;
|
|
11
|
+
try {
|
|
12
|
+
await access(join(cwd, 'tsconfig.json'));
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
const result = await runCommand('npx tsc --noEmit --skipLibCheck', cwd, 60_000);
|
|
18
|
+
if (result.success)
|
|
19
|
+
return null;
|
|
20
|
+
const fileName = basename(absTestPath);
|
|
21
|
+
const errors = (result.stdout + '\n' + result.stderr)
|
|
22
|
+
.split('\n')
|
|
23
|
+
.filter((l) => l.includes(fileName) && /error TS\d+/.test(l))
|
|
24
|
+
.join('\n')
|
|
25
|
+
.trim();
|
|
26
|
+
return errors || null;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=typecheck.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typecheck.js","sourceRoot":"","sources":["../../src/lib/typecheck.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAA;AAErC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,kFAAkF;AAClF,kFAAkF;AAClF,sFAAsF;AACtF,8EAA8E;AAC9E,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,WAAmB,EACnB,GAAW,EACX,GAA0C;IAE1C,IAAI,GAAG,CAAC,QAAQ,KAAK,YAAY;QAAE,OAAO,IAAI,CAAA;IAE9C,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC,CAAA;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,iCAAiC,EAAE,GAAG,EAAE,MAAM,CAAC,CAAA;IAC/E,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,IAAI,CAAA;IAE/B,MAAM,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAA;IACtC,MAAM,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC;SAClD,KAAK,CAAC,IAAI,CAAC;SACX,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SAC5D,IAAI,CAAC,IAAI,CAAC;SACV,IAAI,EAAE,CAAA;IAET,OAAO,MAAM,IAAI,IAAI,CAAA;AACvB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function hasTestFunctions(code: string): boolean;
|
|
2
|
+
export declare function enrichNoTestsError(output: string): string;
|
|
3
|
+
export declare function isZeroTestsOutput(raw: string): boolean;
|
|
4
|
+
export declare function parsePassCount(output: string): number;
|
|
5
|
+
export declare function buildStructureBrokenMessage(initialError: string, currentError: string): string;
|
|
6
|
+
export declare function buildRegressionMessage(initialError: string, currentError: string, baselinePass: number, currentPass: number): string;
|
|
7
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Strip comments and string literals to avoid false positives inside quoted text.
|
|
2
|
+
function stripNonCode(code) {
|
|
3
|
+
return code
|
|
4
|
+
.replace(/\/\/[^\n]*/g, '')
|
|
5
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
6
|
+
.replace(/'[^'\\]*(?:\\.[^'\\]*)*'/g, '""')
|
|
7
|
+
.replace(/"[^"\\]*(?:\\.[^"\\]*)*"/g, '""')
|
|
8
|
+
.replace(/`[^`\\]*(?:\\.[^`\\]*)*`/g, '""');
|
|
9
|
+
}
|
|
10
|
+
// Returns true if the code contains at least one executable test function.
|
|
11
|
+
// A file that only has describe() with no it()/test() inside is considered empty.
|
|
12
|
+
export function hasTestFunctions(code) {
|
|
13
|
+
const stripped = stripNonCode(code);
|
|
14
|
+
return /\b(?:it|test)\s*(?:\.(?:each|concurrent|skip|only))?\s*\(/.test(stripped);
|
|
15
|
+
}
|
|
16
|
+
// If the runner output indicates "no tests found", replace it with a
|
|
17
|
+
// clear instruction so the AI knows exactly what went wrong.
|
|
18
|
+
export function enrichNoTestsError(output) {
|
|
19
|
+
if (!/no tests|0 tests?\b/i.test(output))
|
|
20
|
+
return output;
|
|
21
|
+
return ('ERROR: Vitest found 0 tests in this file. The file ran but had nothing to execute.\n\n' +
|
|
22
|
+
'This means one of:\n' +
|
|
23
|
+
' 1. You wrote only imports, types, or describe() blocks with no it()/test() inside\n' +
|
|
24
|
+
' 2. A module import failed during collection — check the output below for an error\n' +
|
|
25
|
+
' 3. Tests are inside a plain function that is never called\n\n' +
|
|
26
|
+
'REQUIRED: Every test file must have at least one test like this:\n' +
|
|
27
|
+
' it(\'description\', () => {\n' +
|
|
28
|
+
' expect(result).toBe(expected)\n' +
|
|
29
|
+
' })\n\n' +
|
|
30
|
+
'DO NOT wrap tests inside a function. Put them directly inside describe() or at the top level.\n\n' +
|
|
31
|
+
'Original runner output:\n' +
|
|
32
|
+
output);
|
|
33
|
+
}
|
|
34
|
+
// Returns true when the runner output shows that zero tests were collected.
|
|
35
|
+
// Distinct from hasTestFunctions (static check) — this checks actual runtime collection.
|
|
36
|
+
export function isZeroTestsOutput(raw) {
|
|
37
|
+
return /Tests:\s+0\s+total|no tests found|found 0 tests/i.test(raw);
|
|
38
|
+
}
|
|
39
|
+
// Extracts the number of passing tests from the runner summary footer.
|
|
40
|
+
// Targets the "Tests N failed | M passed (total)" line specifically to avoid
|
|
41
|
+
// false matches from file-level headers like "(1 passed)" or test descriptions.
|
|
42
|
+
export function parsePassCount(output) {
|
|
43
|
+
// Prefer the Tests summary line: "Tests 1 failed | 15 passed (16)" or "Tests 15 passed (15)"
|
|
44
|
+
const summaryLine = output.match(/^\s*Tests\b[^\n]*?(\d+)\s+passed/m);
|
|
45
|
+
if (summaryLine)
|
|
46
|
+
return parseInt(summaryLine[1], 10);
|
|
47
|
+
// Fallback: any "N passed" in the output
|
|
48
|
+
const m = output.match(/(\d+)\s+passed/);
|
|
49
|
+
return m ? parseInt(m[1], 10) : 0;
|
|
50
|
+
}
|
|
51
|
+
const RULE_DIVIDER = '─'.repeat(60);
|
|
52
|
+
// Retry message when a fix attempt caused Vitest to collect 0 tests —
|
|
53
|
+
// the model likely broke an import. Anchors the model to the original error.
|
|
54
|
+
export function buildStructureBrokenMessage(initialError, currentError) {
|
|
55
|
+
return (`⚠ CRITICAL — Your fix broke the file structure: Vitest found 0 tests.\n\n` +
|
|
56
|
+
`This means an import is now failing during module collection, or you accidentally removed all test functions.\n` +
|
|
57
|
+
`Look for: Cannot find module, TypeError, SyntaxError in the error output below.\n\n` +
|
|
58
|
+
`RULES:\n` +
|
|
59
|
+
`- Do NOT change any imports unless the import itself caused the original failure\n` +
|
|
60
|
+
`- Do NOT restructure the describe block or rename other tests\n` +
|
|
61
|
+
`- ONLY fix the specific assertion that was originally failing\n\n` +
|
|
62
|
+
`Original failing test error (what you were supposed to fix):\n` +
|
|
63
|
+
`${RULE_DIVIDER}\n` +
|
|
64
|
+
`${initialError}\n` +
|
|
65
|
+
`${RULE_DIVIDER}\n\n` +
|
|
66
|
+
`Error from your attempted fix:\n` +
|
|
67
|
+
`${currentError}`);
|
|
68
|
+
}
|
|
69
|
+
// Retry message when a fix attempt reduced the number of passing tests —
|
|
70
|
+
// the model broke previously-passing tests while trying to fix one.
|
|
71
|
+
export function buildRegressionMessage(initialError, currentError, baselinePass, currentPass) {
|
|
72
|
+
return (`⚠ REGRESSION — Your fix made things worse: ${baselinePass} test(s) were passing before, now only ${currentPass} are.\n\n` +
|
|
73
|
+
`Do NOT modify tests that were already passing.\n` +
|
|
74
|
+
`ONLY fix the test that was originally failing.\n\n` +
|
|
75
|
+
`Original failing test error:\n` +
|
|
76
|
+
`${RULE_DIVIDER}\n` +
|
|
77
|
+
`${initialError}\n` +
|
|
78
|
+
`${RULE_DIVIDER}\n\n` +
|
|
79
|
+
`Current errors:\n` +
|
|
80
|
+
`${currentError}`);
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/lib/validate.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,SAAS,YAAY,CAAC,IAAY;IAChC,OAAO,IAAI;SACR,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC;SAC1B,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC;SAChC,OAAO,CAAC,2BAA2B,EAAE,IAAI,CAAC;SAC1C,OAAO,CAAC,2BAA2B,EAAE,IAAI,CAAC;SAC1C,OAAO,CAAC,2BAA2B,EAAE,IAAI,CAAC,CAAA;AAC/C,CAAC;AAED,2EAA2E;AAC3E,kFAAkF;AAClF,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,CAAA;IACnC,OAAO,2DAA2D,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;AACnF,CAAC;AAED,qEAAqE;AACrE,6DAA6D;AAC7D,MAAM,UAAU,kBAAkB,CAAC,MAAc;IAC/C,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAA;IACvD,OAAO,CACL,wFAAwF;QACxF,sBAAsB;QACtB,uFAAuF;QACvF,uFAAuF;QACvF,iEAAiE;QACjE,oEAAoE;QACpE,iCAAiC;QACjC,qCAAqC;QACrC,UAAU;QACV,mGAAmG;QACnG,2BAA2B;QAC3B,MAAM,CACP,CAAA;AACH,CAAC;AAED,4EAA4E;AAC5E,yFAAyF;AACzF,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,OAAO,kDAAkD,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACrE,CAAC;AAED,uEAAuE;AACvE,8EAA8E;AAC9E,gFAAgF;AAChF,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,+FAA+F;IAC/F,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAA;IACrE,IAAI,WAAW;QAAE,OAAO,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IACpD,yCAAyC;IACzC,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAA;IACxC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AACnC,CAAC;AAED,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAEnC,sEAAsE;AACtE,6EAA6E;AAC7E,MAAM,UAAU,2BAA2B,CAAC,YAAoB,EAAE,YAAoB;IACpF,OAAO,CACL,2EAA2E;QAC3E,iHAAiH;QACjH,qFAAqF;QACrF,UAAU;QACV,oFAAoF;QACpF,iEAAiE;QACjE,mEAAmE;QACnE,gEAAgE;QAChE,GAAG,YAAY,IAAI;QACnB,GAAG,YAAY,IAAI;QACnB,GAAG,YAAY,MAAM;QACrB,kCAAkC;QAClC,GAAG,YAAY,EAAE,CAClB,CAAA;AACH,CAAC;AAED,yEAAyE;AACzE,oEAAoE;AACpE,MAAM,UAAU,sBAAsB,CACpC,YAAoB,EACpB,YAAoB,EACpB,YAAoB,EACpB,WAAmB;IAEnB,OAAO,CACL,8CAA8C,YAAY,0CAA0C,WAAW,WAAW;QAC1H,kDAAkD;QAClD,oDAAoD;QACpD,gCAAgC;QAChC,GAAG,YAAY,IAAI;QACnB,GAAG,YAAY,IAAI;QACnB,GAAG,YAAY,MAAM;QACrB,mBAAmB;QACnB,GAAG,YAAY,EAAE,CAClB,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export type WorkerState = {
|
|
2
|
+
phase: 'idle';
|
|
3
|
+
} | {
|
|
4
|
+
phase: 'generating';
|
|
5
|
+
file: string;
|
|
6
|
+
} | {
|
|
7
|
+
phase: 'writing';
|
|
8
|
+
file: string;
|
|
9
|
+
} | {
|
|
10
|
+
phase: 'running';
|
|
11
|
+
file: string;
|
|
12
|
+
} | {
|
|
13
|
+
phase: 'retrying';
|
|
14
|
+
file: string;
|
|
15
|
+
attempt: number;
|
|
16
|
+
max: number;
|
|
17
|
+
} | {
|
|
18
|
+
phase: 'passed';
|
|
19
|
+
file: string;
|
|
20
|
+
} | {
|
|
21
|
+
phase: 'failed';
|
|
22
|
+
file: string;
|
|
23
|
+
};
|
|
24
|
+
export declare class WorkerDisplay {
|
|
25
|
+
private states;
|
|
26
|
+
private done;
|
|
27
|
+
private passed;
|
|
28
|
+
private failedCount;
|
|
29
|
+
readonly total: number;
|
|
30
|
+
private rendered;
|
|
31
|
+
private tick;
|
|
32
|
+
private timer;
|
|
33
|
+
readonly isTTY: boolean;
|
|
34
|
+
private tips;
|
|
35
|
+
private tipIndex;
|
|
36
|
+
private successLabel;
|
|
37
|
+
constructor(workerCount: number, total: number, tips?: string[], successLabel?: string);
|
|
38
|
+
start(): void;
|
|
39
|
+
update(workerId: number, state: WorkerState): void;
|
|
40
|
+
finish(): void;
|
|
41
|
+
private render;
|
|
42
|
+
private formatRow;
|
|
43
|
+
private plainLabel;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=worker-display.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-display.d.ts","sourceRoot":"","sources":["../../src/lib/worker-display.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,WAAW,GACnB;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GACjB;IAAE,KAAK,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACjE;IAAE,KAAK,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,KAAK,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAOrC,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,IAAI,CAAI;IAChB,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,WAAW,CAAI;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,OAAO,CAAC,QAAQ,CAAI;IACpB,OAAO,CAAC,IAAI,CAAI;IAChB,OAAO,CAAC,KAAK,CAA8C;IAC3D,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,OAAO,CAAC,IAAI,CAAU;IACtB,OAAO,CAAC,QAAQ,CAAI;IACpB,OAAO,CAAC,YAAY,CAAQ;gBAEhB,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,EAAO,EAAE,YAAY,SAAW;IAQ5F,KAAK;IAYL,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW;IAgB3C,MAAM;IAQN,OAAO,CAAC,MAAM;IA8Cd,OAAO,CAAC,SAAS;IAyDjB,OAAO,CAAC,UAAU;CAWnB"}
|