@hustle-together/api-dev-tools 3.11.1 → 3.12.2
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/.claude/agents/code-reviewer.md +170 -0
- package/.claude/agents/docs-generator.md +80 -0
- package/.claude/agents/implementation-reviewer.md +119 -0
- package/.claude/agents/parallel-researcher.md +52 -0
- package/.claude/agents/research-validator.md +116 -0
- package/.claude/agents/schema-generator.md +70 -0
- package/.claude/agents/test-writer.md +104 -0
- package/.claude/api-dev-state.json +305 -56
- package/.claude/commands/README.md +21 -10
- package/.claude/commands/add-command.md +8 -5
- package/.claude/commands/api-create.md +36 -25
- package/.claude/commands/api-env.md +1 -0
- package/.claude/commands/api-interview.md +32 -19
- package/.claude/commands/api-research.md +47 -21
- package/.claude/commands/api-status.md +21 -1
- package/.claude/commands/api-verify.md +14 -13
- package/.claude/commands/beepboop.md +4 -5
- package/.claude/commands/busycommit.md +2 -3
- package/.claude/commands/commit.md +2 -3
- package/.claude/commands/cycle.md +2 -7
- package/.claude/commands/gap.md +2 -3
- package/.claude/commands/green.md +2 -7
- package/.claude/commands/issue.md +3 -8
- package/.claude/commands/ntfy-setup.md +91 -0
- package/.claude/commands/ntfy-test.md +74 -0
- package/.claude/commands/plan.md +2 -3
- package/.claude/commands/pr.md +2 -3
- package/.claude/commands/publish.md +40 -0
- package/.claude/commands/red.md +2 -7
- package/.claude/commands/refactor.md +2 -7
- package/.claude/commands/spike.md +2 -7
- package/.claude/commands/summarize.md +2 -3
- package/.claude/commands/tdd.md +2 -7
- package/.claude/commands/worktree-add.md +208 -216
- package/.claude/commands/worktree-cleanup.md +172 -178
- package/.claude/settings.json +63 -12
- package/.claude/settings.local.json +2 -1
- package/.claude-plugin/marketplace.json +2 -11
- package/.skills/README.md +55 -53
- package/.skills/_shared/settings.json +1 -1
- package/.skills/add-command/SKILL.md +10 -5
- package/.skills/api-create/SKILL.md +146 -35
- package/.skills/api-env/SKILL.md +1 -0
- package/.skills/api-interview/SKILL.md +32 -19
- package/.skills/api-research/SKILL.md +47 -21
- package/.skills/api-status/SKILL.md +21 -1
- package/.skills/api-verify/SKILL.md +14 -13
- package/.skills/beepboop/SKILL.md +6 -5
- package/.skills/busycommit/SKILL.md +4 -3
- package/.skills/commit/SKILL.md +4 -3
- package/.skills/cycle/SKILL.md +4 -7
- package/.skills/gap/SKILL.md +4 -3
- package/.skills/green/SKILL.md +4 -7
- package/.skills/issue/SKILL.md +5 -8
- package/.skills/plan/SKILL.md +4 -3
- package/.skills/pr/SKILL.md +4 -3
- package/.skills/publish/SKILL.md +160 -0
- package/.skills/red/SKILL.md +4 -7
- package/.skills/refactor/SKILL.md +4 -7
- package/.skills/spike/SKILL.md +4 -7
- package/.skills/summarize/SKILL.md +4 -3
- package/.skills/tdd/SKILL.md +4 -7
- package/.skills/update-todos/SKILL.md +22 -0
- package/.skills/worktree-add/SKILL.md +210 -216
- package/.skills/worktree-cleanup/SKILL.md +183 -187
- package/CHANGELOG.md +97 -79
- package/README.md +161 -7142
- package/bin/cli.js +448 -805
- package/commands/README.md +66 -31
- package/commands/add-command.md +8 -5
- package/commands/beepboop.md +4 -5
- package/commands/busycommit.md +2 -3
- package/commands/commit.md +2 -3
- package/commands/cycle.md +2 -7
- package/commands/gap.md +2 -3
- package/commands/green.md +2 -7
- package/commands/hustle-api-continue.md +8 -5
- package/commands/hustle-api-create.md +70 -29
- package/commands/hustle-api-env.md +1 -0
- package/commands/hustle-api-interview.md +32 -19
- package/commands/hustle-api-research.md +47 -21
- package/commands/hustle-api-sessions.md +8 -7
- package/commands/hustle-api-status.md +21 -1
- package/commands/hustle-api-verify.md +14 -13
- package/commands/hustle-combine.md +488 -241
- package/commands/hustle-ui-create-page.md +113 -50
- package/commands/hustle-ui-create.md +179 -26
- package/commands/issue.md +3 -8
- package/commands/plan.md +2 -3
- package/commands/pr.md +2 -3
- package/commands/red.md +2 -7
- package/commands/refactor.md +2 -7
- package/commands/spike.md +2 -7
- package/commands/summarize.md +2 -3
- package/commands/tdd.md +2 -7
- package/commands/worktree-add.md +208 -216
- package/commands/worktree-cleanup.md +172 -178
- package/hooks/api-workflow-check.py +5 -3
- package/hooks/enforce-component-type-confirm.py +97 -0
- package/hooks/lib/__init__.py +1 -0
- package/hooks/lib/greptile.py +355 -0
- package/hooks/lib/ntfy.py +209 -0
- package/hooks/notify-input-needed.py +73 -0
- package/hooks/notify-phase-complete.py +90 -0
- package/hooks/run-code-review.py +246 -0
- package/hooks/track-token-usage.py +121 -0
- package/package.json +13 -3
- package/scripts/collect-test-results.ts +102 -77
- package/scripts/extract-parameters.ts +112 -70
- package/scripts/generate-test-manifest.ts +118 -77
- package/templates/.env.example +57 -0
- package/templates/BRAND_GUIDE.md +92 -52
- package/templates/CLAUDE-SECTION.md +40 -37
- package/templates/SPEC.json +186 -38
- package/templates/api-dev-state.json +33 -4
- package/templates/api-showcase/_components/APICard.tsx +22 -18
- package/templates/api-showcase/_components/APIModal.tsx +110 -64
- package/templates/api-showcase/_components/APIShowcase.tsx +53 -35
- package/templates/api-showcase/_components/APITester.tsx +128 -67
- package/templates/api-showcase/page.tsx +4 -4
- package/templates/api-test/page.tsx +51 -30
- package/templates/api-test/test-structure/route.ts +43 -34
- package/templates/component/Component.stories.tsx +41 -39
- package/templates/component/Component.test.tsx +96 -78
- package/templates/component/Component.tsx +63 -52
- package/templates/component/Component.types.ts +10 -6
- package/templates/component/Component.visual.spec.ts +170 -0
- package/templates/component/index.ts +2 -2
- package/templates/dev-tools/_components/DevToolsLanding.tsx +8 -8
- package/templates/dev-tools/page.tsx +4 -3
- package/templates/mcp-servers.json +30 -2
- package/templates/page/page.e2e.test.ts +56 -48
- package/templates/page/page.tsx +3 -3
- package/templates/shared/HeroHeader.tsx +16 -15
- package/templates/shared/index.ts +1 -1
- package/templates/ui-showcase/_components/PreviewCard.tsx +20 -20
- package/templates/ui-showcase/_components/PreviewModal.tsx +149 -108
- package/templates/ui-showcase/_components/UIShowcase.tsx +43 -35
- package/templates/ui-showcase/page.tsx +4 -4
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
* @generated by @hustle-together/api-dev-tools v3.0
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { execSync, spawn } from
|
|
15
|
-
import fs from
|
|
16
|
-
import path from
|
|
14
|
+
import { execSync, spawn } from "child_process";
|
|
15
|
+
import fs from "fs";
|
|
16
|
+
import path from "path";
|
|
17
17
|
|
|
18
18
|
// ============================================
|
|
19
19
|
// Types
|
|
@@ -22,7 +22,7 @@ import path from 'path';
|
|
|
22
22
|
interface TestResult {
|
|
23
23
|
name: string;
|
|
24
24
|
file: string;
|
|
25
|
-
status:
|
|
25
|
+
status: "passed" | "failed" | "skipped";
|
|
26
26
|
duration: number;
|
|
27
27
|
error?: string;
|
|
28
28
|
}
|
|
@@ -74,7 +74,7 @@ function parseVitestJson(jsonOutput: string): CollectedResults {
|
|
|
74
74
|
passed: 0,
|
|
75
75
|
failed: 0,
|
|
76
76
|
skipped: 0,
|
|
77
|
-
duration: fileResult.duration || 0
|
|
77
|
+
duration: fileResult.duration || 0,
|
|
78
78
|
};
|
|
79
79
|
|
|
80
80
|
if (fileResult.assertionResults) {
|
|
@@ -82,19 +82,23 @@ function parseVitestJson(jsonOutput: string): CollectedResults {
|
|
|
82
82
|
const result: TestResult = {
|
|
83
83
|
name: test.title || test.fullName,
|
|
84
84
|
file: suite.file,
|
|
85
|
-
status:
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
status:
|
|
86
|
+
test.status === "passed"
|
|
87
|
+
? "passed"
|
|
88
|
+
: test.status === "failed"
|
|
89
|
+
? "failed"
|
|
90
|
+
: "skipped",
|
|
91
|
+
duration: test.duration || 0,
|
|
88
92
|
};
|
|
89
93
|
|
|
90
94
|
if (test.failureMessages && test.failureMessages.length > 0) {
|
|
91
|
-
result.error = test.failureMessages.join(
|
|
95
|
+
result.error = test.failureMessages.join("\n");
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
suite.tests.push(result);
|
|
95
99
|
|
|
96
|
-
if (result.status ===
|
|
97
|
-
else if (result.status ===
|
|
100
|
+
if (result.status === "passed") suite.passed++;
|
|
101
|
+
else if (result.status === "failed") suite.failed++;
|
|
98
102
|
else suite.skipped++;
|
|
99
103
|
}
|
|
100
104
|
}
|
|
@@ -109,7 +113,7 @@ function parseVitestJson(jsonOutput: string): CollectedResults {
|
|
|
109
113
|
}
|
|
110
114
|
|
|
111
115
|
return {
|
|
112
|
-
version:
|
|
116
|
+
version: "3.0.0",
|
|
113
117
|
collectedAt: new Date().toISOString(),
|
|
114
118
|
suites,
|
|
115
119
|
summary: {
|
|
@@ -119,8 +123,8 @@ function parseVitestJson(jsonOutput: string): CollectedResults {
|
|
|
119
123
|
failed: totalFailed,
|
|
120
124
|
skipped: totalSkipped,
|
|
121
125
|
duration: totalDuration,
|
|
122
|
-
success: totalFailed === 0
|
|
123
|
-
}
|
|
126
|
+
success: totalFailed === 0,
|
|
127
|
+
},
|
|
124
128
|
};
|
|
125
129
|
} catch (error) {
|
|
126
130
|
throw new Error(`Failed to parse Vitest JSON output: ${error}`);
|
|
@@ -135,11 +139,13 @@ function parseVitestConsole(output: string): CollectedResults {
|
|
|
135
139
|
const suites: TestSuiteResult[] = [];
|
|
136
140
|
let currentSuite: TestSuiteResult | null = null;
|
|
137
141
|
|
|
138
|
-
const lines = output.split(
|
|
142
|
+
const lines = output.split("\n");
|
|
139
143
|
|
|
140
144
|
for (const line of lines) {
|
|
141
145
|
// Match file header: ✓ src/path/file.test.ts (5 tests) 123ms
|
|
142
|
-
const fileMatch = line.match(
|
|
146
|
+
const fileMatch = line.match(
|
|
147
|
+
/[✓✗◯]\s+([^\s]+\.(?:test|spec)\.tsx?)\s+\((\d+)\s+tests?\)/,
|
|
148
|
+
);
|
|
143
149
|
if (fileMatch) {
|
|
144
150
|
if (currentSuite) {
|
|
145
151
|
suites.push(currentSuite);
|
|
@@ -153,7 +159,7 @@ function parseVitestConsole(output: string): CollectedResults {
|
|
|
153
159
|
passed: 0,
|
|
154
160
|
failed: 0,
|
|
155
161
|
skipped: 0,
|
|
156
|
-
duration: durationMatch ? parseInt(durationMatch[1]) : 0
|
|
162
|
+
duration: durationMatch ? parseInt(durationMatch[1]) : 0,
|
|
157
163
|
};
|
|
158
164
|
continue;
|
|
159
165
|
}
|
|
@@ -163,19 +169,18 @@ function parseVitestConsole(output: string): CollectedResults {
|
|
|
163
169
|
if (testMatch && currentSuite) {
|
|
164
170
|
const [, icon, name, duration] = testMatch;
|
|
165
171
|
|
|
166
|
-
const status:
|
|
167
|
-
icon ===
|
|
168
|
-
icon === '✗' ? 'failed' : 'skipped';
|
|
172
|
+
const status: "passed" | "failed" | "skipped" =
|
|
173
|
+
icon === "✓" ? "passed" : icon === "✗" ? "failed" : "skipped";
|
|
169
174
|
|
|
170
175
|
currentSuite.tests.push({
|
|
171
176
|
name,
|
|
172
177
|
file: currentSuite.file,
|
|
173
178
|
status,
|
|
174
|
-
duration: duration ? parseInt(duration) : 0
|
|
179
|
+
duration: duration ? parseInt(duration) : 0,
|
|
175
180
|
});
|
|
176
181
|
|
|
177
|
-
if (status ===
|
|
178
|
-
else if (status ===
|
|
182
|
+
if (status === "passed") currentSuite.passed++;
|
|
183
|
+
else if (status === "failed") currentSuite.failed++;
|
|
179
184
|
else currentSuite.skipped++;
|
|
180
185
|
}
|
|
181
186
|
}
|
|
@@ -185,29 +190,32 @@ function parseVitestConsole(output: string): CollectedResults {
|
|
|
185
190
|
}
|
|
186
191
|
|
|
187
192
|
// Calculate summary
|
|
188
|
-
const summary = suites.reduce(
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
193
|
+
const summary = suites.reduce(
|
|
194
|
+
(acc, suite) => ({
|
|
195
|
+
totalSuites: acc.totalSuites + 1,
|
|
196
|
+
totalTests: acc.totalTests + suite.tests.length,
|
|
197
|
+
passed: acc.passed + suite.passed,
|
|
198
|
+
failed: acc.failed + suite.failed,
|
|
199
|
+
skipped: acc.skipped + suite.skipped,
|
|
200
|
+
duration: acc.duration + suite.duration,
|
|
201
|
+
success: acc.success && suite.failed === 0,
|
|
202
|
+
}),
|
|
203
|
+
{
|
|
204
|
+
totalSuites: 0,
|
|
205
|
+
totalTests: 0,
|
|
206
|
+
passed: 0,
|
|
207
|
+
failed: 0,
|
|
208
|
+
skipped: 0,
|
|
209
|
+
duration: 0,
|
|
210
|
+
success: true,
|
|
211
|
+
},
|
|
212
|
+
);
|
|
205
213
|
|
|
206
214
|
return {
|
|
207
|
-
version:
|
|
215
|
+
version: "3.0.0",
|
|
208
216
|
collectedAt: new Date().toISOString(),
|
|
209
217
|
suites,
|
|
210
|
-
summary
|
|
218
|
+
summary,
|
|
211
219
|
};
|
|
212
220
|
}
|
|
213
221
|
|
|
@@ -216,20 +224,20 @@ function parseVitestConsole(output: string): CollectedResults {
|
|
|
216
224
|
// ============================================
|
|
217
225
|
|
|
218
226
|
function runVitest(baseDir: string, filter?: string): CollectedResults {
|
|
219
|
-
console.log(
|
|
227
|
+
console.log("🧪 Running Vitest...");
|
|
220
228
|
|
|
221
|
-
const vitestArgs = [
|
|
229
|
+
const vitestArgs = ["vitest", "run", "--reporter=json"];
|
|
222
230
|
if (filter) {
|
|
223
231
|
vitestArgs.push(filter);
|
|
224
232
|
}
|
|
225
233
|
|
|
226
234
|
try {
|
|
227
235
|
// Try running with JSON reporter
|
|
228
|
-
const result = execSync(`npx ${vitestArgs.join(
|
|
236
|
+
const result = execSync(`npx ${vitestArgs.join(" ")}`, {
|
|
229
237
|
cwd: baseDir,
|
|
230
|
-
encoding:
|
|
231
|
-
stdio: [
|
|
232
|
-
maxBuffer: 50 * 1024 * 1024 // 50MB buffer
|
|
238
|
+
encoding: "utf-8",
|
|
239
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
240
|
+
maxBuffer: 50 * 1024 * 1024, // 50MB buffer
|
|
233
241
|
});
|
|
234
242
|
|
|
235
243
|
return parseVitestJson(result);
|
|
@@ -247,13 +255,13 @@ function runVitest(baseDir: string, filter?: string): CollectedResults {
|
|
|
247
255
|
}
|
|
248
256
|
|
|
249
257
|
// Try fallback: run without JSON reporter
|
|
250
|
-
console.log(
|
|
258
|
+
console.log(" ⚠️ JSON reporter failed, trying console output...");
|
|
251
259
|
|
|
252
260
|
try {
|
|
253
|
-
const consoleResult = execSync(`npx vitest run ${filter ||
|
|
261
|
+
const consoleResult = execSync(`npx vitest run ${filter || ""}`, {
|
|
254
262
|
cwd: baseDir,
|
|
255
|
-
encoding:
|
|
256
|
-
stdio: [
|
|
263
|
+
encoding: "utf-8",
|
|
264
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
257
265
|
});
|
|
258
266
|
|
|
259
267
|
return parseVitestConsole(consoleResult);
|
|
@@ -273,11 +281,11 @@ function runVitest(baseDir: string, filter?: string): CollectedResults {
|
|
|
273
281
|
|
|
274
282
|
function updateManifest(manifestPath: string, results: CollectedResults): void {
|
|
275
283
|
if (!fs.existsSync(manifestPath)) {
|
|
276
|
-
console.log(
|
|
284
|
+
console.log(" ⚠️ Manifest not found, skipping update");
|
|
277
285
|
return;
|
|
278
286
|
}
|
|
279
287
|
|
|
280
|
-
const manifest = JSON.parse(fs.readFileSync(manifestPath,
|
|
288
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
281
289
|
|
|
282
290
|
// Create a map of test results by file
|
|
283
291
|
const resultsByFile = new Map<string, TestSuiteResult>();
|
|
@@ -289,7 +297,7 @@ function updateManifest(manifestPath: string, results: CollectedResults): void {
|
|
|
289
297
|
// Update each endpoint's test status
|
|
290
298
|
if (manifest.endpoints) {
|
|
291
299
|
for (const endpoint of manifest.endpoints) {
|
|
292
|
-
const testBasename = path.basename(endpoint.testFile ||
|
|
300
|
+
const testBasename = path.basename(endpoint.testFile || "");
|
|
293
301
|
const suiteResult = resultsByFile.get(testBasename);
|
|
294
302
|
|
|
295
303
|
if (suiteResult) {
|
|
@@ -298,7 +306,7 @@ function updateManifest(manifestPath: string, results: CollectedResults): void {
|
|
|
298
306
|
failed: suiteResult.failed,
|
|
299
307
|
skipped: suiteResult.skipped,
|
|
300
308
|
duration: suiteResult.duration,
|
|
301
|
-
lastRun: results.collectedAt
|
|
309
|
+
lastRun: results.collectedAt,
|
|
302
310
|
};
|
|
303
311
|
}
|
|
304
312
|
}
|
|
@@ -307,7 +315,7 @@ function updateManifest(manifestPath: string, results: CollectedResults): void {
|
|
|
307
315
|
// Update summary
|
|
308
316
|
manifest.lastTestRun = {
|
|
309
317
|
...results.summary,
|
|
310
|
-
timestamp: results.collectedAt
|
|
318
|
+
timestamp: results.collectedAt,
|
|
311
319
|
};
|
|
312
320
|
|
|
313
321
|
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
@@ -322,13 +330,25 @@ function main() {
|
|
|
322
330
|
const args = process.argv.slice(2);
|
|
323
331
|
const baseDir = args[0] || process.cwd();
|
|
324
332
|
const filter = args[1] || undefined;
|
|
325
|
-
const outputPath =
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
333
|
+
const outputPath =
|
|
334
|
+
args[2] ||
|
|
335
|
+
path.join(baseDir, "src", "app", "api-test", "test-results.json");
|
|
336
|
+
const manifestPath = path.join(
|
|
337
|
+
baseDir,
|
|
338
|
+
"src",
|
|
339
|
+
"app",
|
|
340
|
+
"api-test",
|
|
341
|
+
"api-tests-manifest.json",
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
console.log(
|
|
345
|
+
"═══════════════════════════════════════════════════════════════",
|
|
346
|
+
);
|
|
347
|
+
console.log(" 🧪 Test Results Collector");
|
|
348
|
+
console.log(" @hustle-together/api-dev-tools v3.0");
|
|
349
|
+
console.log(
|
|
350
|
+
"═══════════════════════════════════════════════════════════════",
|
|
351
|
+
);
|
|
332
352
|
console.log(`\n📁 Base directory: ${baseDir}`);
|
|
333
353
|
if (filter) {
|
|
334
354
|
console.log(`🔍 Filter: ${filter}`);
|
|
@@ -350,13 +370,17 @@ function main() {
|
|
|
350
370
|
// Update manifest with results
|
|
351
371
|
updateManifest(manifestPath, results);
|
|
352
372
|
|
|
353
|
-
console.log(
|
|
373
|
+
console.log(
|
|
374
|
+
"\n═══════════════════════════════════════════════════════════════",
|
|
375
|
+
);
|
|
354
376
|
if (results.summary.success) {
|
|
355
|
-
console.log(
|
|
377
|
+
console.log(" ✅ All tests passed!");
|
|
356
378
|
} else {
|
|
357
|
-
console.log(
|
|
379
|
+
console.log(" ❌ Some tests failed");
|
|
358
380
|
}
|
|
359
|
-
console.log(
|
|
381
|
+
console.log(
|
|
382
|
+
"═══════════════════════════════════════════════════════════════",
|
|
383
|
+
);
|
|
360
384
|
|
|
361
385
|
console.log(`\n📊 Summary:`);
|
|
362
386
|
console.log(` • Suites: ${results.summary.totalSuites}`);
|
|
@@ -371,12 +395,14 @@ function main() {
|
|
|
371
395
|
console.log(` • Duration: ${results.summary.duration}ms`);
|
|
372
396
|
|
|
373
397
|
// List failed tests
|
|
374
|
-
const failedTests = results.suites.flatMap(suite =>
|
|
375
|
-
suite.tests
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
398
|
+
const failedTests = results.suites.flatMap((suite) =>
|
|
399
|
+
suite.tests
|
|
400
|
+
.filter((t) => t.status === "failed")
|
|
401
|
+
.map((t) => ({
|
|
402
|
+
file: suite.file,
|
|
403
|
+
name: t.name,
|
|
404
|
+
error: t.error,
|
|
405
|
+
})),
|
|
380
406
|
);
|
|
381
407
|
|
|
382
408
|
if (failedTests.length > 0) {
|
|
@@ -384,7 +410,7 @@ function main() {
|
|
|
384
410
|
for (const test of failedTests) {
|
|
385
411
|
console.log(` • ${test.file}: ${test.name}`);
|
|
386
412
|
if (test.error) {
|
|
387
|
-
console.log(` ${test.error.split(
|
|
413
|
+
console.log(` ${test.error.split("\n")[0]}`);
|
|
388
414
|
}
|
|
389
415
|
}
|
|
390
416
|
}
|
|
@@ -394,9 +420,8 @@ function main() {
|
|
|
394
420
|
|
|
395
421
|
// Exit with appropriate code
|
|
396
422
|
process.exit(results.summary.success ? 0 : 1);
|
|
397
|
-
|
|
398
423
|
} catch (error) {
|
|
399
|
-
console.error(
|
|
424
|
+
console.error("\n❌ Failed to collect test results:", error);
|
|
400
425
|
process.exit(1);
|
|
401
426
|
}
|
|
402
427
|
}
|