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.
Files changed (144) hide show
  1. package/README.md +451 -0
  2. package/bin/run.js +5 -0
  3. package/dist/agent/context.d.ts +25 -0
  4. package/dist/agent/context.d.ts.map +1 -0
  5. package/dist/agent/context.js +366 -0
  6. package/dist/agent/context.js.map +1 -0
  7. package/dist/agent/fix-loop.d.ts +20 -0
  8. package/dist/agent/fix-loop.d.ts.map +1 -0
  9. package/dist/agent/fix-loop.js +466 -0
  10. package/dist/agent/fix-loop.js.map +1 -0
  11. package/dist/agent/generator.d.ts +35 -0
  12. package/dist/agent/generator.d.ts.map +1 -0
  13. package/dist/agent/generator.js +220 -0
  14. package/dist/agent/generator.js.map +1 -0
  15. package/dist/agent/loop.d.ts +23 -0
  16. package/dist/agent/loop.d.ts.map +1 -0
  17. package/dist/agent/loop.js +394 -0
  18. package/dist/agent/loop.js.map +1 -0
  19. package/dist/agent/project-memory.d.ts +10 -0
  20. package/dist/agent/project-memory.d.ts.map +1 -0
  21. package/dist/agent/project-memory.js +57 -0
  22. package/dist/agent/project-memory.js.map +1 -0
  23. package/dist/agent/prompts.d.ts +44 -0
  24. package/dist/agent/prompts.d.ts.map +1 -0
  25. package/dist/agent/prompts.js +377 -0
  26. package/dist/agent/prompts.js.map +1 -0
  27. package/dist/ci/comment.d.ts +2 -0
  28. package/dist/ci/comment.d.ts.map +1 -0
  29. package/dist/ci/comment.js +97 -0
  30. package/dist/ci/comment.js.map +1 -0
  31. package/dist/ci/parse-outputs.d.ts +2 -0
  32. package/dist/ci/parse-outputs.d.ts.map +1 -0
  33. package/dist/ci/parse-outputs.js +30 -0
  34. package/dist/ci/parse-outputs.js.map +1 -0
  35. package/dist/commands/analyze.d.ts +13 -0
  36. package/dist/commands/analyze.d.ts.map +1 -0
  37. package/dist/commands/analyze.js +151 -0
  38. package/dist/commands/analyze.js.map +1 -0
  39. package/dist/commands/fix.d.ts +15 -0
  40. package/dist/commands/fix.d.ts.map +1 -0
  41. package/dist/commands/fix.js +106 -0
  42. package/dist/commands/fix.js.map +1 -0
  43. package/dist/commands/generate.d.ts +18 -0
  44. package/dist/commands/generate.d.ts.map +1 -0
  45. package/dist/commands/generate.js +129 -0
  46. package/dist/commands/generate.js.map +1 -0
  47. package/dist/commands/init.d.ts +7 -0
  48. package/dist/commands/init.d.ts.map +1 -0
  49. package/dist/commands/init.js +131 -0
  50. package/dist/commands/init.js.map +1 -0
  51. package/dist/commands/run.d.ts +10 -0
  52. package/dist/commands/run.d.ts.map +1 -0
  53. package/dist/commands/run.js +45 -0
  54. package/dist/commands/run.js.map +1 -0
  55. package/dist/lib/config.d.ts +58 -0
  56. package/dist/lib/config.d.ts.map +1 -0
  57. package/dist/lib/config.js +68 -0
  58. package/dist/lib/config.js.map +1 -0
  59. package/dist/lib/coverage/gaps.d.ts +12 -0
  60. package/dist/lib/coverage/gaps.d.ts.map +1 -0
  61. package/dist/lib/coverage/gaps.js +186 -0
  62. package/dist/lib/coverage/gaps.js.map +1 -0
  63. package/dist/lib/coverage/index.d.ts +7 -0
  64. package/dist/lib/coverage/index.d.ts.map +1 -0
  65. package/dist/lib/coverage/index.js +24 -0
  66. package/dist/lib/coverage/index.js.map +1 -0
  67. package/dist/lib/coverage/json.d.ts +3 -0
  68. package/dist/lib/coverage/json.d.ts.map +1 -0
  69. package/dist/lib/coverage/json.js +24 -0
  70. package/dist/lib/coverage/json.js.map +1 -0
  71. package/dist/lib/coverage/lcov.d.ts +3 -0
  72. package/dist/lib/coverage/lcov.d.ts.map +1 -0
  73. package/dist/lib/coverage/lcov.js +58 -0
  74. package/dist/lib/coverage/lcov.js.map +1 -0
  75. package/dist/lib/coverage/types.d.ts +27 -0
  76. package/dist/lib/coverage/types.d.ts.map +1 -0
  77. package/dist/lib/coverage/types.js +2 -0
  78. package/dist/lib/coverage/types.js.map +1 -0
  79. package/dist/lib/coverage-spinner.d.ts +6 -0
  80. package/dist/lib/coverage-spinner.d.ts.map +1 -0
  81. package/dist/lib/coverage-spinner.js +101 -0
  82. package/dist/lib/coverage-spinner.js.map +1 -0
  83. package/dist/lib/detector.d.ts +13 -0
  84. package/dist/lib/detector.d.ts.map +1 -0
  85. package/dist/lib/detector.js +106 -0
  86. package/dist/lib/detector.js.map +1 -0
  87. package/dist/lib/extract-error.d.ts +2 -0
  88. package/dist/lib/extract-error.d.ts.map +1 -0
  89. package/dist/lib/extract-error.js +116 -0
  90. package/dist/lib/extract-error.js.map +1 -0
  91. package/dist/lib/providers/anthropic.d.ts +8 -0
  92. package/dist/lib/providers/anthropic.d.ts.map +1 -0
  93. package/dist/lib/providers/anthropic.js +38 -0
  94. package/dist/lib/providers/anthropic.js.map +1 -0
  95. package/dist/lib/providers/index.d.ts +6 -0
  96. package/dist/lib/providers/index.d.ts.map +1 -0
  97. package/dist/lib/providers/index.js +27 -0
  98. package/dist/lib/providers/index.js.map +1 -0
  99. package/dist/lib/providers/openai-compatible.d.ts +11 -0
  100. package/dist/lib/providers/openai-compatible.d.ts.map +1 -0
  101. package/dist/lib/providers/openai-compatible.js +93 -0
  102. package/dist/lib/providers/openai-compatible.js.map +1 -0
  103. package/dist/lib/providers/types.d.ts +17 -0
  104. package/dist/lib/providers/types.d.ts.map +1 -0
  105. package/dist/lib/providers/types.js +97 -0
  106. package/dist/lib/providers/types.js.map +1 -0
  107. package/dist/lib/report-upload.d.ts +3 -0
  108. package/dist/lib/report-upload.d.ts.map +1 -0
  109. package/dist/lib/report-upload.js +15 -0
  110. package/dist/lib/report-upload.js.map +1 -0
  111. package/dist/lib/reporter.d.ts +51 -0
  112. package/dist/lib/reporter.d.ts.map +1 -0
  113. package/dist/lib/reporter.js +172 -0
  114. package/dist/lib/reporter.js.map +1 -0
  115. package/dist/lib/runner.d.ts +9 -0
  116. package/dist/lib/runner.d.ts.map +1 -0
  117. package/dist/lib/runner.js +50 -0
  118. package/dist/lib/runner.js.map +1 -0
  119. package/dist/lib/skeleton.d.ts +8 -0
  120. package/dist/lib/skeleton.d.ts.map +1 -0
  121. package/dist/lib/skeleton.js +122 -0
  122. package/dist/lib/skeleton.js.map +1 -0
  123. package/dist/lib/streaming-viewer.d.ts +14 -0
  124. package/dist/lib/streaming-viewer.d.ts.map +1 -0
  125. package/dist/lib/streaming-viewer.js +80 -0
  126. package/dist/lib/streaming-viewer.js.map +1 -0
  127. package/dist/lib/tips.d.ts +16 -0
  128. package/dist/lib/tips.d.ts.map +1 -0
  129. package/dist/lib/tips.js +76 -0
  130. package/dist/lib/tips.js.map +1 -0
  131. package/dist/lib/typecheck.d.ts +3 -0
  132. package/dist/lib/typecheck.d.ts.map +1 -0
  133. package/dist/lib/typecheck.js +28 -0
  134. package/dist/lib/typecheck.js.map +1 -0
  135. package/dist/lib/validate.d.ts +7 -0
  136. package/dist/lib/validate.d.ts.map +1 -0
  137. package/dist/lib/validate.js +82 -0
  138. package/dist/lib/validate.js.map +1 -0
  139. package/dist/lib/worker-display.d.ts +45 -0
  140. package/dist/lib/worker-display.d.ts.map +1 -0
  141. package/dist/lib/worker-display.js +168 -0
  142. package/dist/lib/worker-display.js.map +1 -0
  143. package/oclif.manifest.json +295 -0
  144. package/package.json +62 -0
@@ -0,0 +1,168 @@
1
+ import chalk from 'chalk';
2
+ const FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
3
+ const ACTIVE = new Set(['generating', 'writing', 'running', 'retrying']);
4
+ // tip rotates every ~5 seconds at 80ms tick interval
5
+ const TIP_TICKS = 62;
6
+ export class WorkerDisplay {
7
+ states;
8
+ done = 0;
9
+ passed = 0;
10
+ failedCount = 0;
11
+ total;
12
+ rendered = 0;
13
+ tick = 0;
14
+ timer = null;
15
+ isTTY;
16
+ tips;
17
+ tipIndex = 0;
18
+ successLabel;
19
+ constructor(workerCount, total, tips = [], successLabel = 'passed') {
20
+ this.states = Array.from({ length: workerCount }, () => ({ phase: 'idle' }));
21
+ this.total = total;
22
+ this.isTTY = Boolean(process.stdout.isTTY);
23
+ this.tips = tips;
24
+ this.successLabel = successLabel;
25
+ }
26
+ start() {
27
+ if (!this.isTTY)
28
+ return;
29
+ this.render();
30
+ this.timer = setInterval(() => {
31
+ this.tick++;
32
+ if (this.tips.length > 0 && this.tick % TIP_TICKS === 0) {
33
+ this.tipIndex = (this.tipIndex + 1) % this.tips.length;
34
+ }
35
+ this.render();
36
+ }, 80);
37
+ }
38
+ update(workerId, state) {
39
+ const prev = this.states[workerId];
40
+ this.states[workerId] = state;
41
+ if (prev.phase !== 'passed' && prev.phase !== 'failed') {
42
+ if (state.phase === 'passed') {
43
+ this.done++;
44
+ this.passed++;
45
+ }
46
+ else if (state.phase === 'failed') {
47
+ this.done++;
48
+ this.failedCount++;
49
+ }
50
+ }
51
+ if (!this.isTTY) {
52
+ // fallback: plain log line for CI / piped output
53
+ const label = this.plainLabel(state);
54
+ process.stdout.write(` [w${workerId + 1}] ${label}\n`);
55
+ }
56
+ }
57
+ finish() {
58
+ if (this.timer) {
59
+ clearInterval(this.timer);
60
+ this.timer = null;
61
+ }
62
+ if (this.isTTY && this.rendered > 0) {
63
+ process.stdout.write(`\x1B[${this.rendered}A\x1B[0J`);
64
+ this.rendered = 0;
65
+ }
66
+ }
67
+ render() {
68
+ if (!this.isTTY)
69
+ return;
70
+ if (this.rendered > 0) {
71
+ process.stdout.write(`\x1B[${this.rendered}A\x1B[0J`);
72
+ }
73
+ const cols = Math.max(60, process.stdout.columns ?? 80);
74
+ const lines = [''];
75
+ for (let i = 0; i < this.states.length; i++) {
76
+ lines.push(this.formatRow(i, this.states[i], cols));
77
+ }
78
+ const barWidth = Math.min(28, cols - 26);
79
+ const filled = this.total === 0 ? barWidth : Math.round(barWidth * this.done / this.total);
80
+ const bar = chalk.green('█'.repeat(filled)) + chalk.dim('░'.repeat(barWidth - filled));
81
+ const pct = this.total === 0 ? 100 : Math.round((this.done / this.total) * 100);
82
+ const remaining = this.total - this.done;
83
+ lines.push('');
84
+ lines.push(` ${chalk.dim('Progress')} ${bar} ${chalk.bold(String(this.done))}${chalk.dim(`/${this.total}`)} ${chalk.dim(pct + '%')}`);
85
+ lines.push('');
86
+ lines.push(` ` +
87
+ chalk.green(`✓ ${this.passed} ${this.successLabel}`) +
88
+ chalk.dim(' · ') +
89
+ (this.failedCount > 0 ? chalk.red(`✗ ${this.failedCount} failed`) : chalk.dim(`✗ 0 failed`)) +
90
+ chalk.dim(' · ') +
91
+ chalk.dim(`${remaining} remaining`));
92
+ if (this.tips.length > 0) {
93
+ const tip = this.tips[this.tipIndex];
94
+ const maxTipLen = cols - 10;
95
+ const displayTip = tip.length > maxTipLen ? tip.slice(0, maxTipLen - 1) + '…' : tip;
96
+ lines.push('');
97
+ lines.push(` ${chalk.cyan('Tip:')} ${chalk.dim(displayTip)}`);
98
+ }
99
+ lines.push('');
100
+ const out = lines.join('\n');
101
+ process.stdout.write(out);
102
+ this.rendered = (out.match(/\n/g) ?? []).length;
103
+ }
104
+ formatRow(id, state, cols) {
105
+ const wLabel = chalk.dim(`w${id + 1}`.padEnd(2));
106
+ const frame = FRAMES[this.tick % FRAMES.length];
107
+ const isActive = ACTIVE.has(state.phase);
108
+ let icon;
109
+ let label;
110
+ let file = '';
111
+ switch (state.phase) {
112
+ case 'idle':
113
+ icon = chalk.dim('○');
114
+ label = chalk.dim('idle ');
115
+ break;
116
+ case 'generating':
117
+ icon = chalk.cyan(frame);
118
+ label = chalk.cyan('generating');
119
+ file = state.file;
120
+ break;
121
+ case 'writing':
122
+ icon = chalk.blue(frame);
123
+ label = chalk.blue('writing ');
124
+ file = state.file;
125
+ break;
126
+ case 'running':
127
+ icon = chalk.yellow(frame);
128
+ label = chalk.yellow('running ');
129
+ file = state.file;
130
+ break;
131
+ case 'retrying':
132
+ icon = chalk.yellow('↺');
133
+ label = chalk.yellow(`retry ${state.attempt}/${state.max} `.slice(0, 10));
134
+ file = state.file;
135
+ break;
136
+ case 'passed':
137
+ icon = chalk.green('✓');
138
+ label = chalk.green('passed ');
139
+ file = state.file;
140
+ break;
141
+ case 'failed':
142
+ icon = chalk.red('✗');
143
+ label = chalk.red('failed ');
144
+ file = state.file;
145
+ break;
146
+ }
147
+ // fixed prefix width: " w1 ⠙ generating " ≈ 22 chars
148
+ const prefixWidth = 24;
149
+ const maxFileLen = cols - prefixWidth;
150
+ const shortFile = file.length > maxFileLen
151
+ ? '…' + file.slice(-(maxFileLen - 1))
152
+ : file;
153
+ void isActive; // used implicitly via frame reference in the switch
154
+ return ` ${wLabel} ${icon} ${label} ${chalk.dim(shortFile)}`;
155
+ }
156
+ plainLabel(state) {
157
+ switch (state.phase) {
158
+ case 'idle': return 'idle';
159
+ case 'generating': return `generating ${state.file}`;
160
+ case 'writing': return `writing ${state.file}`;
161
+ case 'running': return `running ${state.file}`;
162
+ case 'retrying': return `retry ${state.attempt}/${state.max} ${state.file}`;
163
+ case 'passed': return `✓ passed ${state.file}`;
164
+ case 'failed': return `✗ failed ${state.file}`;
165
+ }
166
+ }
167
+ }
168
+ //# sourceMappingURL=worker-display.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-display.js","sourceRoot":"","sources":["../../src/lib/worker-display.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,295 @@
1
+ {
2
+ "commands": {
3
+ "analyze": {
4
+ "aliases": [],
5
+ "args": {},
6
+ "description": "Analyze test coverage and show gaps — no files are changed",
7
+ "examples": [
8
+ "$ lacuna analyze",
9
+ "$ lacuna analyze --threshold 90",
10
+ "$ lacuna analyze --format json",
11
+ "$ lacuna analyze --format markdown"
12
+ ],
13
+ "flags": {
14
+ "threshold": {
15
+ "char": "t",
16
+ "description": "Minimum coverage percentage",
17
+ "name": "threshold",
18
+ "hasDynamicHelp": false,
19
+ "multiple": false,
20
+ "type": "option"
21
+ },
22
+ "format": {
23
+ "char": "F",
24
+ "description": "Output format",
25
+ "name": "format",
26
+ "default": "terminal",
27
+ "hasDynamicHelp": false,
28
+ "multiple": false,
29
+ "options": [
30
+ "terminal",
31
+ "json",
32
+ "markdown"
33
+ ],
34
+ "type": "option"
35
+ },
36
+ "output": {
37
+ "char": "o",
38
+ "description": "Write report to file instead of stdout",
39
+ "name": "output",
40
+ "hasDynamicHelp": false,
41
+ "multiple": false,
42
+ "type": "option"
43
+ },
44
+ "verbose": {
45
+ "char": "v",
46
+ "description": "Show uncovered line numbers per file",
47
+ "name": "verbose",
48
+ "allowNo": false,
49
+ "type": "boolean"
50
+ }
51
+ },
52
+ "hasDynamicHelp": false,
53
+ "hiddenAliases": [],
54
+ "id": "analyze",
55
+ "pluginAlias": "lacuna",
56
+ "pluginName": "lacuna",
57
+ "pluginType": "core",
58
+ "strict": true,
59
+ "enableJsonFlag": false,
60
+ "isESM": true,
61
+ "relativePath": [
62
+ "dist",
63
+ "commands",
64
+ "analyze.js"
65
+ ]
66
+ },
67
+ "fix": {
68
+ "aliases": [],
69
+ "args": {},
70
+ "description": "Find and fix failing tests using AI — preserves existing tests, only repairs what is broken",
71
+ "examples": [
72
+ "$ lacuna fix",
73
+ "$ lacuna fix --workers 4",
74
+ "$ lacuna fix --file src/utils/math.test.ts",
75
+ "$ lacuna fix --dry-run"
76
+ ],
77
+ "flags": {
78
+ "dry-run": {
79
+ "description": "Show what would be changed without writing files",
80
+ "name": "dry-run",
81
+ "allowNo": false,
82
+ "type": "boolean"
83
+ },
84
+ "file": {
85
+ "char": "f",
86
+ "description": "Target a specific test file instead of all failing tests",
87
+ "name": "file",
88
+ "hasDynamicHelp": false,
89
+ "multiple": false,
90
+ "type": "option"
91
+ },
92
+ "verbose": {
93
+ "char": "v",
94
+ "description": "Show model output and full test runner logs",
95
+ "name": "verbose",
96
+ "allowNo": false,
97
+ "type": "boolean"
98
+ },
99
+ "model": {
100
+ "char": "m",
101
+ "description": "Model to use (overrides .lacuna.json)",
102
+ "name": "model",
103
+ "hasDynamicHelp": false,
104
+ "multiple": false,
105
+ "type": "option"
106
+ },
107
+ "workers": {
108
+ "char": "w",
109
+ "description": "Number of parallel workers (each handles one file at a time)",
110
+ "name": "workers",
111
+ "default": 1,
112
+ "hasDynamicHelp": false,
113
+ "multiple": false,
114
+ "type": "option"
115
+ },
116
+ "fresh": {
117
+ "description": "Re-run the full test suite even if a recent failing-files cache exists",
118
+ "name": "fresh",
119
+ "allowNo": false,
120
+ "type": "boolean"
121
+ }
122
+ },
123
+ "hasDynamicHelp": false,
124
+ "hiddenAliases": [],
125
+ "id": "fix",
126
+ "pluginAlias": "lacuna",
127
+ "pluginName": "lacuna",
128
+ "pluginType": "core",
129
+ "strict": true,
130
+ "enableJsonFlag": false,
131
+ "isESM": true,
132
+ "relativePath": [
133
+ "dist",
134
+ "commands",
135
+ "fix.js"
136
+ ]
137
+ },
138
+ "generate": {
139
+ "aliases": [],
140
+ "args": {},
141
+ "description": "Run the full agent loop: analyze gaps, generate tests, verify they pass",
142
+ "examples": [
143
+ "$ lacuna generate",
144
+ "$ lacuna generate --dry-run",
145
+ "$ lacuna generate --file src/utils/math.ts",
146
+ "$ lacuna generate --format json --output report.json"
147
+ ],
148
+ "flags": {
149
+ "dry-run": {
150
+ "description": "Print what would be written without touching the filesystem",
151
+ "name": "dry-run",
152
+ "allowNo": false,
153
+ "type": "boolean"
154
+ },
155
+ "file": {
156
+ "char": "f",
157
+ "description": "Target a specific source file instead of the whole project",
158
+ "name": "file",
159
+ "hasDynamicHelp": false,
160
+ "multiple": false,
161
+ "type": "option"
162
+ },
163
+ "verbose": {
164
+ "char": "v",
165
+ "description": "Show model output and full test runner logs",
166
+ "name": "verbose",
167
+ "allowNo": false,
168
+ "type": "boolean"
169
+ },
170
+ "model": {
171
+ "char": "m",
172
+ "description": "Model to use (overrides .lacuna.json)",
173
+ "name": "model",
174
+ "hasDynamicHelp": false,
175
+ "multiple": false,
176
+ "type": "option"
177
+ },
178
+ "threshold": {
179
+ "char": "t",
180
+ "description": "Override coverage threshold",
181
+ "name": "threshold",
182
+ "hasDynamicHelp": false,
183
+ "multiple": false,
184
+ "type": "option"
185
+ },
186
+ "format": {
187
+ "char": "F",
188
+ "description": "Output format for the final report",
189
+ "name": "format",
190
+ "default": "terminal",
191
+ "hasDynamicHelp": false,
192
+ "multiple": false,
193
+ "options": [
194
+ "terminal",
195
+ "json",
196
+ "markdown"
197
+ ],
198
+ "type": "option"
199
+ },
200
+ "output": {
201
+ "char": "o",
202
+ "description": "Write report to file instead of stdout",
203
+ "name": "output",
204
+ "hasDynamicHelp": false,
205
+ "multiple": false,
206
+ "type": "option"
207
+ },
208
+ "workers": {
209
+ "char": "w",
210
+ "description": "Number of parallel workers (each handles one file at a time)",
211
+ "name": "workers",
212
+ "default": 1,
213
+ "hasDynamicHelp": false,
214
+ "multiple": false,
215
+ "type": "option"
216
+ },
217
+ "fresh": {
218
+ "description": "Force a fresh coverage run even if a recent report already exists",
219
+ "name": "fresh",
220
+ "allowNo": false,
221
+ "type": "boolean"
222
+ }
223
+ },
224
+ "hasDynamicHelp": false,
225
+ "hiddenAliases": [],
226
+ "id": "generate",
227
+ "pluginAlias": "lacuna",
228
+ "pluginName": "lacuna",
229
+ "pluginType": "core",
230
+ "strict": true,
231
+ "enableJsonFlag": false,
232
+ "isESM": true,
233
+ "relativePath": [
234
+ "dist",
235
+ "commands",
236
+ "generate.js"
237
+ ]
238
+ },
239
+ "init": {
240
+ "aliases": [],
241
+ "args": {},
242
+ "description": "Interactive setup wizard — configure lacuna for your project",
243
+ "examples": [
244
+ "$ lacuna init"
245
+ ],
246
+ "flags": {},
247
+ "hasDynamicHelp": false,
248
+ "hiddenAliases": [],
249
+ "id": "init",
250
+ "pluginAlias": "lacuna",
251
+ "pluginName": "lacuna",
252
+ "pluginType": "core",
253
+ "strict": true,
254
+ "enableJsonFlag": false,
255
+ "isESM": true,
256
+ "relativePath": [
257
+ "dist",
258
+ "commands",
259
+ "init.js"
260
+ ]
261
+ },
262
+ "run": {
263
+ "aliases": [],
264
+ "args": {},
265
+ "description": "Run the test suite and report coverage",
266
+ "examples": [
267
+ "$ lacuna run"
268
+ ],
269
+ "flags": {
270
+ "verbose": {
271
+ "char": "v",
272
+ "description": "Show full test output",
273
+ "name": "verbose",
274
+ "allowNo": false,
275
+ "type": "boolean"
276
+ }
277
+ },
278
+ "hasDynamicHelp": false,
279
+ "hiddenAliases": [],
280
+ "id": "run",
281
+ "pluginAlias": "lacuna",
282
+ "pluginName": "lacuna",
283
+ "pluginType": "core",
284
+ "strict": true,
285
+ "enableJsonFlag": false,
286
+ "isESM": true,
287
+ "relativePath": [
288
+ "dist",
289
+ "commands",
290
+ "run.js"
291
+ ]
292
+ }
293
+ },
294
+ "version": "0.1.0"
295
+ }
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "lacuna-cli",
3
+ "version": "0.1.1",
4
+ "description": "Agentic CLI that finds coverage gaps, generates tests, and verifies them in a loop",
5
+ "keywords": [
6
+ "testing",
7
+ "coverage",
8
+ "ai",
9
+ "agent",
10
+ "cli"
11
+ ],
12
+ "homepage": "https://octagon-simon.github.io/lacuna/",
13
+ "license": "MIT",
14
+ "bin": {
15
+ "lacuna": "./bin/run.js"
16
+ },
17
+ "main": "dist/index.js",
18
+ "type": "module",
19
+ "files": [
20
+ "/bin",
21
+ "/dist",
22
+ "/oclif.manifest.json"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsc -p tsconfig.json",
26
+ "watch": "tsc -p tsconfig.json --watch",
27
+ "lint": "eslint src --ext .ts",
28
+ "format": "prettier --write 'src/**/*.ts'",
29
+ "test": "node --loader ts-node/esm --test src/**/*.test.ts",
30
+ "prepack": "npm run build",
31
+ "dev": "ts-node --esm src/index.ts"
32
+ },
33
+ "dependencies": {
34
+ "@anthropic-ai/sdk": "^0.52.0",
35
+ "@inquirer/prompts": "^8.5.0",
36
+ "@oclif/core": "^4",
37
+ "chalk": "^5",
38
+ "cosmiconfig": "^9",
39
+ "lcov-parse": "^1",
40
+ "openai": "^6.39.0",
41
+ "ora": "^8",
42
+ "zod": "^3"
43
+ },
44
+ "devDependencies": {
45
+ "@oclif/test": "^4",
46
+ "@types/node": "^22",
47
+ "eslint": "^9",
48
+ "prettier": "^3",
49
+ "ts-node": "^10",
50
+ "typescript": "^5"
51
+ },
52
+ "engines": {
53
+ "node": ">=20.0.0"
54
+ },
55
+ "oclif": {
56
+ "bin": "lacuna",
57
+ "dirname": "lacuna",
58
+ "commands": "./dist/commands",
59
+ "plugins": [],
60
+ "topicSeparator": " "
61
+ }
62
+ }