modestbench 0.0.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 (275) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/LICENSE.md +55 -0
  3. package/README.md +699 -0
  4. package/dist/bootstrap.cjs +37 -0
  5. package/dist/bootstrap.cjs.map +1 -0
  6. package/dist/bootstrap.d.cts +17 -0
  7. package/dist/bootstrap.d.cts.map +1 -0
  8. package/dist/bootstrap.d.ts +17 -0
  9. package/dist/bootstrap.d.ts.map +1 -0
  10. package/dist/bootstrap.js +33 -0
  11. package/dist/bootstrap.js.map +1 -0
  12. package/dist/cli/commands/history.cjs +459 -0
  13. package/dist/cli/commands/history.cjs.map +1 -0
  14. package/dist/cli/commands/history.d.cts +34 -0
  15. package/dist/cli/commands/history.d.cts.map +1 -0
  16. package/dist/cli/commands/history.d.ts +34 -0
  17. package/dist/cli/commands/history.d.ts.map +1 -0
  18. package/dist/cli/commands/history.js +422 -0
  19. package/dist/cli/commands/history.js.map +1 -0
  20. package/dist/cli/commands/init.cjs +566 -0
  21. package/dist/cli/commands/init.cjs.map +1 -0
  22. package/dist/cli/commands/init.d.cts +26 -0
  23. package/dist/cli/commands/init.d.cts.map +1 -0
  24. package/dist/cli/commands/init.d.ts +26 -0
  25. package/dist/cli/commands/init.d.ts.map +1 -0
  26. package/dist/cli/commands/init.js +562 -0
  27. package/dist/cli/commands/init.js.map +1 -0
  28. package/dist/cli/commands/run.cjs +285 -0
  29. package/dist/cli/commands/run.cjs.map +1 -0
  30. package/dist/cli/commands/run.d.cts +37 -0
  31. package/dist/cli/commands/run.d.cts.map +1 -0
  32. package/dist/cli/commands/run.d.ts +37 -0
  33. package/dist/cli/commands/run.d.ts.map +1 -0
  34. package/dist/cli/commands/run.js +248 -0
  35. package/dist/cli/commands/run.js.map +1 -0
  36. package/dist/cli/index.cjs +523 -0
  37. package/dist/cli/index.cjs.map +1 -0
  38. package/dist/cli/index.d.cts +58 -0
  39. package/dist/cli/index.d.cts.map +1 -0
  40. package/dist/cli/index.d.ts +58 -0
  41. package/dist/cli/index.d.ts.map +1 -0
  42. package/dist/cli/index.js +515 -0
  43. package/dist/cli/index.js.map +1 -0
  44. package/dist/config/manager.cjs +370 -0
  45. package/dist/config/manager.cjs.map +1 -0
  46. package/dist/config/manager.d.cts +46 -0
  47. package/dist/config/manager.d.cts.map +1 -0
  48. package/dist/config/manager.d.ts +46 -0
  49. package/dist/config/manager.d.ts.map +1 -0
  50. package/dist/config/manager.js +333 -0
  51. package/dist/config/manager.js.map +1 -0
  52. package/dist/config/schema.cjs +182 -0
  53. package/dist/config/schema.cjs.map +1 -0
  54. package/dist/config/schema.d.cts +51 -0
  55. package/dist/config/schema.d.cts.map +1 -0
  56. package/dist/config/schema.d.ts +51 -0
  57. package/dist/config/schema.d.ts.map +1 -0
  58. package/dist/config/schema.js +145 -0
  59. package/dist/config/schema.js.map +1 -0
  60. package/dist/constants.cjs +22 -0
  61. package/dist/constants.cjs.map +1 -0
  62. package/dist/constants.d.cts +10 -0
  63. package/dist/constants.d.cts.map +1 -0
  64. package/dist/constants.d.ts +10 -0
  65. package/dist/constants.d.ts.map +1 -0
  66. package/dist/constants.js +19 -0
  67. package/dist/constants.js.map +1 -0
  68. package/dist/core/benchmark-schema.cjs +135 -0
  69. package/dist/core/benchmark-schema.cjs.map +1 -0
  70. package/dist/core/benchmark-schema.d.cts +139 -0
  71. package/dist/core/benchmark-schema.d.cts.map +1 -0
  72. package/dist/core/benchmark-schema.d.ts +139 -0
  73. package/dist/core/benchmark-schema.d.ts.map +1 -0
  74. package/dist/core/benchmark-schema.js +132 -0
  75. package/dist/core/benchmark-schema.js.map +1 -0
  76. package/dist/core/engine.cjs +669 -0
  77. package/dist/core/engine.cjs.map +1 -0
  78. package/dist/core/engine.d.cts +128 -0
  79. package/dist/core/engine.d.cts.map +1 -0
  80. package/dist/core/engine.d.ts +128 -0
  81. package/dist/core/engine.d.ts.map +1 -0
  82. package/dist/core/engine.js +632 -0
  83. package/dist/core/engine.js.map +1 -0
  84. package/dist/core/engines/accurate-engine.cjs +292 -0
  85. package/dist/core/engines/accurate-engine.cjs.map +1 -0
  86. package/dist/core/engines/accurate-engine.d.cts +63 -0
  87. package/dist/core/engines/accurate-engine.d.cts.map +1 -0
  88. package/dist/core/engines/accurate-engine.d.ts +63 -0
  89. package/dist/core/engines/accurate-engine.d.ts.map +1 -0
  90. package/dist/core/engines/accurate-engine.js +288 -0
  91. package/dist/core/engines/accurate-engine.js.map +1 -0
  92. package/dist/core/engines/index.cjs +21 -0
  93. package/dist/core/engines/index.cjs.map +1 -0
  94. package/dist/core/engines/index.d.cts +16 -0
  95. package/dist/core/engines/index.d.cts.map +1 -0
  96. package/dist/core/engines/index.d.ts +16 -0
  97. package/dist/core/engines/index.d.ts.map +1 -0
  98. package/dist/core/engines/index.js +16 -0
  99. package/dist/core/engines/index.js.map +1 -0
  100. package/dist/core/engines/tinybench-engine.cjs +286 -0
  101. package/dist/core/engines/tinybench-engine.cjs.map +1 -0
  102. package/dist/core/engines/tinybench-engine.d.cts +18 -0
  103. package/dist/core/engines/tinybench-engine.d.cts.map +1 -0
  104. package/dist/core/engines/tinybench-engine.d.ts +18 -0
  105. package/dist/core/engines/tinybench-engine.d.ts.map +1 -0
  106. package/dist/core/engines/tinybench-engine.js +282 -0
  107. package/dist/core/engines/tinybench-engine.js.map +1 -0
  108. package/dist/core/error-manager.cjs +303 -0
  109. package/dist/core/error-manager.cjs.map +1 -0
  110. package/dist/core/error-manager.d.cts +77 -0
  111. package/dist/core/error-manager.d.cts.map +1 -0
  112. package/dist/core/error-manager.d.ts +77 -0
  113. package/dist/core/error-manager.d.ts.map +1 -0
  114. package/dist/core/error-manager.js +299 -0
  115. package/dist/core/error-manager.js.map +1 -0
  116. package/dist/core/loader.cjs +287 -0
  117. package/dist/core/loader.cjs.map +1 -0
  118. package/dist/core/loader.d.cts +55 -0
  119. package/dist/core/loader.d.cts.map +1 -0
  120. package/dist/core/loader.d.ts +55 -0
  121. package/dist/core/loader.d.ts.map +1 -0
  122. package/dist/core/loader.js +250 -0
  123. package/dist/core/loader.js.map +1 -0
  124. package/dist/core/stats-utils.cjs +99 -0
  125. package/dist/core/stats-utils.cjs.map +1 -0
  126. package/dist/core/stats-utils.d.cts +50 -0
  127. package/dist/core/stats-utils.d.cts.map +1 -0
  128. package/dist/core/stats-utils.d.ts +50 -0
  129. package/dist/core/stats-utils.d.ts.map +1 -0
  130. package/dist/core/stats-utils.js +94 -0
  131. package/dist/core/stats-utils.js.map +1 -0
  132. package/dist/index.cjs +64 -0
  133. package/dist/index.cjs.map +1 -0
  134. package/dist/index.d.cts +22 -0
  135. package/dist/index.d.cts.map +1 -0
  136. package/dist/index.d.ts +22 -0
  137. package/dist/index.d.ts.map +1 -0
  138. package/dist/index.js +30 -0
  139. package/dist/index.js.map +1 -0
  140. package/dist/progress/manager.cjs +325 -0
  141. package/dist/progress/manager.cjs.map +1 -0
  142. package/dist/progress/manager.d.cts +125 -0
  143. package/dist/progress/manager.d.cts.map +1 -0
  144. package/dist/progress/manager.d.ts +125 -0
  145. package/dist/progress/manager.d.ts.map +1 -0
  146. package/dist/progress/manager.js +321 -0
  147. package/dist/progress/manager.js.map +1 -0
  148. package/dist/reporters/csv.cjs +250 -0
  149. package/dist/reporters/csv.cjs.map +1 -0
  150. package/dist/reporters/csv.d.cts +92 -0
  151. package/dist/reporters/csv.d.cts.map +1 -0
  152. package/dist/reporters/csv.d.ts +92 -0
  153. package/dist/reporters/csv.d.ts.map +1 -0
  154. package/dist/reporters/csv.js +246 -0
  155. package/dist/reporters/csv.js.map +1 -0
  156. package/dist/reporters/human.cjs +516 -0
  157. package/dist/reporters/human.cjs.map +1 -0
  158. package/dist/reporters/human.d.cts +86 -0
  159. package/dist/reporters/human.d.cts.map +1 -0
  160. package/dist/reporters/human.d.ts +86 -0
  161. package/dist/reporters/human.d.ts.map +1 -0
  162. package/dist/reporters/human.js +509 -0
  163. package/dist/reporters/human.js.map +1 -0
  164. package/dist/reporters/index.cjs +17 -0
  165. package/dist/reporters/index.cjs.map +1 -0
  166. package/dist/reporters/index.d.cts +10 -0
  167. package/dist/reporters/index.d.cts.map +1 -0
  168. package/dist/reporters/index.d.ts +10 -0
  169. package/dist/reporters/index.d.ts.map +1 -0
  170. package/dist/reporters/index.js +10 -0
  171. package/dist/reporters/index.js.map +1 -0
  172. package/dist/reporters/json.cjs +215 -0
  173. package/dist/reporters/json.cjs.map +1 -0
  174. package/dist/reporters/json.d.cts +79 -0
  175. package/dist/reporters/json.d.cts.map +1 -0
  176. package/dist/reporters/json.d.ts +79 -0
  177. package/dist/reporters/json.d.ts.map +1 -0
  178. package/dist/reporters/json.js +211 -0
  179. package/dist/reporters/json.js.map +1 -0
  180. package/dist/reporters/registry.cjs +255 -0
  181. package/dist/reporters/registry.cjs.map +1 -0
  182. package/dist/reporters/registry.d.cts +155 -0
  183. package/dist/reporters/registry.d.cts.map +1 -0
  184. package/dist/reporters/registry.d.ts +155 -0
  185. package/dist/reporters/registry.d.ts.map +1 -0
  186. package/dist/reporters/registry.js +249 -0
  187. package/dist/reporters/registry.js.map +1 -0
  188. package/dist/reporters/simple.cjs +328 -0
  189. package/dist/reporters/simple.cjs.map +1 -0
  190. package/dist/reporters/simple.d.cts +51 -0
  191. package/dist/reporters/simple.d.cts.map +1 -0
  192. package/dist/reporters/simple.d.ts +51 -0
  193. package/dist/reporters/simple.d.ts.map +1 -0
  194. package/dist/reporters/simple.js +321 -0
  195. package/dist/reporters/simple.js.map +1 -0
  196. package/dist/schema/modestbench-config.schema.json +162 -0
  197. package/dist/storage/history.cjs +456 -0
  198. package/dist/storage/history.cjs.map +1 -0
  199. package/dist/storage/history.d.cts +99 -0
  200. package/dist/storage/history.d.cts.map +1 -0
  201. package/dist/storage/history.d.ts +99 -0
  202. package/dist/storage/history.d.ts.map +1 -0
  203. package/dist/storage/history.js +452 -0
  204. package/dist/storage/history.js.map +1 -0
  205. package/dist/types/cli.cjs +21 -0
  206. package/dist/types/cli.cjs.map +1 -0
  207. package/dist/types/cli.d.cts +296 -0
  208. package/dist/types/cli.d.cts.map +1 -0
  209. package/dist/types/cli.d.ts +296 -0
  210. package/dist/types/cli.d.ts.map +1 -0
  211. package/dist/types/cli.js +18 -0
  212. package/dist/types/cli.js.map +1 -0
  213. package/dist/types/core.cjs +14 -0
  214. package/dist/types/core.cjs.map +1 -0
  215. package/dist/types/core.d.cts +380 -0
  216. package/dist/types/core.d.cts.map +1 -0
  217. package/dist/types/core.d.ts +380 -0
  218. package/dist/types/core.d.ts.map +1 -0
  219. package/dist/types/core.js +13 -0
  220. package/dist/types/core.js.map +1 -0
  221. package/dist/types/index.cjs +27 -0
  222. package/dist/types/index.cjs.map +1 -0
  223. package/dist/types/index.d.cts +11 -0
  224. package/dist/types/index.d.cts.map +1 -0
  225. package/dist/types/index.d.ts +11 -0
  226. package/dist/types/index.d.ts.map +1 -0
  227. package/dist/types/index.js +11 -0
  228. package/dist/types/index.js.map +1 -0
  229. package/dist/types/interfaces.cjs +10 -0
  230. package/dist/types/interfaces.cjs.map +1 -0
  231. package/dist/types/interfaces.d.cts +381 -0
  232. package/dist/types/interfaces.d.cts.map +1 -0
  233. package/dist/types/interfaces.d.ts +381 -0
  234. package/dist/types/interfaces.d.ts.map +1 -0
  235. package/dist/types/interfaces.js +9 -0
  236. package/dist/types/interfaces.js.map +1 -0
  237. package/dist/types/utility.cjs +92 -0
  238. package/dist/types/utility.cjs.map +1 -0
  239. package/dist/types/utility.d.cts +330 -0
  240. package/dist/types/utility.d.cts.map +1 -0
  241. package/dist/types/utility.d.ts +330 -0
  242. package/dist/types/utility.d.ts.map +1 -0
  243. package/dist/types/utility.js +78 -0
  244. package/dist/types/utility.js.map +1 -0
  245. package/package.json +211 -0
  246. package/src/bootstrap.ts +35 -0
  247. package/src/cli/commands/history.ts +569 -0
  248. package/src/cli/commands/init.ts +658 -0
  249. package/src/cli/commands/run.ts +346 -0
  250. package/src/cli/index.ts +642 -0
  251. package/src/config/manager.ts +387 -0
  252. package/src/config/schema.ts +188 -0
  253. package/src/constants.ts +21 -0
  254. package/src/core/benchmark-schema.ts +185 -0
  255. package/src/core/engine.ts +888 -0
  256. package/src/core/engines/accurate-engine.ts +408 -0
  257. package/src/core/engines/index.ts +16 -0
  258. package/src/core/engines/tinybench-engine.ts +335 -0
  259. package/src/core/error-manager.ts +372 -0
  260. package/src/core/loader.ts +324 -0
  261. package/src/core/stats-utils.ts +135 -0
  262. package/src/index.ts +46 -0
  263. package/src/progress/manager.ts +415 -0
  264. package/src/reporters/csv.ts +368 -0
  265. package/src/reporters/human.ts +707 -0
  266. package/src/reporters/index.ts +10 -0
  267. package/src/reporters/json.ts +302 -0
  268. package/src/reporters/registry.ts +349 -0
  269. package/src/reporters/simple.ts +459 -0
  270. package/src/storage/history.ts +600 -0
  271. package/src/types/cli.ts +312 -0
  272. package/src/types/core.ts +414 -0
  273. package/src/types/index.ts +18 -0
  274. package/src/types/interfaces.ts +451 -0
  275. package/src/types/utility.ts +446 -0
@@ -0,0 +1,346 @@
1
+ /**
2
+ * ModestBench Run Command
3
+ *
4
+ * Execute benchmark files with configuration and reporting. Main entry point
5
+ * for running benchmarks with real-time progress.
6
+ */
7
+
8
+ import { resolve } from 'node:path';
9
+
10
+ import type { BenchmarkRun } from '../../types/index.js';
11
+ import type { CliContext } from '../index.js';
12
+
13
+ import { ExitCodes } from '../../types/cli.js';
14
+
15
+ /**
16
+ * Run command options interface
17
+ */
18
+ interface RunOptions {
19
+ bail?: boolean | undefined;
20
+ config?: string | undefined;
21
+ cwd: string;
22
+ engine?: 'accurate' | 'tinybench' | undefined;
23
+ exclude?: string[] | undefined;
24
+ excludeTags?: string[] | undefined;
25
+ iterations?: number | undefined;
26
+ json?: boolean | undefined;
27
+ noColor?: boolean | undefined;
28
+ outputDir?: string | undefined;
29
+ pattern: string[];
30
+ progress?: boolean | undefined;
31
+ quiet?: boolean | undefined;
32
+ reporters: string[];
33
+ tags?: string[] | undefined;
34
+ time?: number | undefined;
35
+ timeout?: number | undefined;
36
+ verbose?: boolean | undefined;
37
+ warmup?: number | undefined;
38
+ }
39
+
40
+ /**
41
+ * Handle run command
42
+ */
43
+ export const handleRunCommand = async (
44
+ context: CliContext,
45
+ options: RunOptions,
46
+ ): Promise<number> => {
47
+ // Check if JSON reporter is being used (need quiet output for clean JSON)
48
+ // Only force quiet mode if json is used AND no output directory is specified
49
+ // (i.e., outputting to stdout where we need clean JSON)
50
+ const isUsingJsonReporter = options.reporters?.includes('json') ?? false;
51
+ const shouldBeQuiet =
52
+ options.quiet || (isUsingJsonReporter && !options.outputDir);
53
+ const verbose = options.verbose ?? false;
54
+ // CLI messages on stderr should only be suppressed by explicit --quiet, not JSON-forced quiet
55
+ const showCliMessages = verbose && !options.quiet;
56
+
57
+ try {
58
+ // Step 1: Load and merge configuration
59
+ if (showCliMessages) {
60
+ console.error('Loading configuration...');
61
+ }
62
+ const config = await loadConfiguration(context, options);
63
+
64
+ // Step 2: Configure reporters
65
+ if (showCliMessages) {
66
+ console.error('Setting up reporters...');
67
+ }
68
+ const reporters = await setupReporters(
69
+ context,
70
+ config,
71
+ shouldBeQuiet,
72
+ verbose,
73
+ showCliMessages,
74
+ options.quiet ?? false,
75
+ options.outputDir,
76
+ options.progress,
77
+ );
78
+
79
+ // Step 3: Discovery phase
80
+ if (showCliMessages) {
81
+ console.error('Discovering benchmark files...');
82
+ }
83
+ const discoveredFiles = await context.engine.discover(
84
+ config.pattern,
85
+ config.exclude,
86
+ );
87
+
88
+ if (showCliMessages) {
89
+ console.error(`Found ${discoveredFiles.length} benchmark file(s)`);
90
+ }
91
+
92
+ // Step 4: Validation phase
93
+ if (showCliMessages) {
94
+ console.error('Validating benchmark files...');
95
+ }
96
+ const validationResult = await context.engine.validate(discoveredFiles);
97
+
98
+ if (validationResult.warnings.length > 0) {
99
+ if (showCliMessages) {
100
+ console.error('Validation warnings:');
101
+ for (const warning of validationResult.warnings) {
102
+ console.error(` ${warning.code}: ${warning.message}`);
103
+ }
104
+ }
105
+ }
106
+
107
+ if (!validationResult.valid) {
108
+ if (!shouldBeQuiet) {
109
+ console.error('Validation errors:');
110
+ for (const error of validationResult.errors) {
111
+ console.error(` ${error.code}: ${error.message}`);
112
+ }
113
+ }
114
+ return ExitCodes.ValidationError;
115
+ }
116
+
117
+ // Step 5: Execution phase
118
+ if (showCliMessages) {
119
+ console.error('Starting benchmark execution...');
120
+ }
121
+
122
+ const runConfig = {
123
+ ...config,
124
+ cwd: options.cwd,
125
+ files: discoveredFiles,
126
+ };
127
+
128
+ const executionResult = await context.engine.execute(
129
+ runConfig,
130
+ reporters,
131
+ context.abortController.signal,
132
+ );
133
+
134
+ // Step 6: Results handling
135
+ // Check if aborted by signal
136
+ if (context.abortController.signal.aborted) {
137
+ if (!shouldBeQuiet) {
138
+ console.error('\n✋ Benchmark run aborted by user');
139
+ }
140
+ // Exit with SIGINT code (128 + 2 = 130)
141
+ process.exit(130);
142
+ }
143
+
144
+ return handleResults(executionResult, options, shouldBeQuiet);
145
+ } catch (error) {
146
+ if (!shouldBeQuiet) {
147
+ console.error(
148
+ `Error: ${error instanceof Error ? error.message : String(error)}`,
149
+ );
150
+ }
151
+
152
+ // Return appropriate exit code based on error type
153
+ if (error instanceof Error) {
154
+ if (
155
+ error.message.includes('Configuration error') ||
156
+ error.message.includes('Config file not found') ||
157
+ error.message.includes('Failed to load config')
158
+ ) {
159
+ return ExitCodes.ConfigurationError;
160
+ }
161
+ if (
162
+ error.message.includes('No files found') ||
163
+ error.message.includes('No benchmark files found') ||
164
+ error.message.includes('File discovery')
165
+ ) {
166
+ return ExitCodes.FileDiscoveryError;
167
+ }
168
+ }
169
+
170
+ return ExitCodes.GeneralError;
171
+ }
172
+ };
173
+
174
+ /**
175
+ * Handle execution results and determine appropriate exit code
176
+ */
177
+ const handleResults = (
178
+ executionResult: BenchmarkRun,
179
+ _options: RunOptions,
180
+ _shouldBeQuiet: boolean,
181
+ ): number => {
182
+ // The reporters should handle displaying results
183
+ // This function only determines the exit code
184
+
185
+ // Check if any files failed to load/execute
186
+ const hasFileErrors = executionResult.files.some((file) => file.error);
187
+
188
+ // Determine exit code based on results
189
+ if (executionResult && executionResult.summary) {
190
+ // Return error if there are failed tasks OR file-level errors
191
+ return executionResult.summary.failedTasks > 0 || hasFileErrors
192
+ ? ExitCodes.GeneralError
193
+ : ExitCodes.Success;
194
+ }
195
+
196
+ return ExitCodes.Success;
197
+ };
198
+
199
+ /**
200
+ * Load and merge configuration from various sources
201
+ */
202
+ const loadConfiguration = async (context: CliContext, options: RunOptions) => {
203
+ try {
204
+ // Create CLI arguments object for configuration merger
205
+ const cliArgs: Record<string, unknown> = {};
206
+
207
+ // Map CLI arguments to config properties
208
+ // Pass pattern as-is, even if empty (loader will provide defaults)
209
+ if (options.pattern !== undefined) {
210
+ // If pattern is provided, use it; if empty array, pass it (for defaults)
211
+ cliArgs.pattern =
212
+ options.pattern.length === 1 ? options.pattern[0] : options.pattern;
213
+ }
214
+ if (options.reporters) {
215
+ cliArgs.reporters = options.reporters;
216
+ }
217
+ if (options.outputDir) {
218
+ cliArgs.outputDir = resolve(options.cwd, options.outputDir);
219
+ }
220
+ if (options.iterations) {
221
+ cliArgs.iterations = options.iterations;
222
+ }
223
+ if (options.time) {
224
+ cliArgs.time = options.time;
225
+ }
226
+ if (options.warmup !== undefined) {
227
+ cliArgs.warmup = options.warmup;
228
+ }
229
+ if (options.bail !== undefined) {
230
+ cliArgs.bail = options.bail;
231
+ }
232
+ if (options.exclude) {
233
+ cliArgs.exclude = options.exclude;
234
+ }
235
+ if (options.timeout) {
236
+ cliArgs.timeout = options.timeout;
237
+ }
238
+ if (options.quiet !== undefined) {
239
+ cliArgs.quiet = options.quiet;
240
+ }
241
+ if (options.verbose !== undefined) {
242
+ cliArgs.verbose = options.verbose;
243
+ }
244
+ if (options.tags) {
245
+ cliArgs.tags = options.tags;
246
+ }
247
+ if (options.excludeTags) {
248
+ cliArgs.excludeTags = options.excludeTags;
249
+ }
250
+
251
+ // Load configuration with CLI argument precedence
252
+ const config = await context.configManager.load(options.config, cliArgs);
253
+
254
+ return config;
255
+ } catch (error) {
256
+ throw new Error(
257
+ `Configuration error: ${error instanceof Error ? error.message : String(error)}`,
258
+ );
259
+ }
260
+ };
261
+
262
+ /**
263
+ * Setup and configure reporters based on configuration
264
+ */
265
+ const setupReporters = async (
266
+ context: CliContext,
267
+ config: { outputDir?: string; reporters?: string[] },
268
+ shouldBeQuiet: boolean,
269
+ isVerbose: boolean,
270
+ showCliMessages: boolean,
271
+ explicitQuiet: boolean,
272
+ explicitOutputDir?: string,
273
+ progressOption?: boolean,
274
+ ) => {
275
+ try {
276
+ const reporters = [];
277
+ const requestedReporters = config.reporters || ['human'];
278
+
279
+ // Dynamically import reporters for proper configuration
280
+ const { HumanReporter } = await import('../../reporters/human.js');
281
+ const { JsonReporter } = await import('../../reporters/json.js');
282
+ const { CsvReporter } = await import('../../reporters/csv.js');
283
+ const { SimpleReporter } = await import('../../reporters/simple.js');
284
+
285
+ // Only use file output if --output was explicitly provided
286
+ // Use the explicit output dir if provided, otherwise check config
287
+ const outputDir = explicitOutputDir
288
+ ? resolve(explicitOutputDir)
289
+ : undefined;
290
+
291
+ for (const reporterName of requestedReporters) {
292
+ let reporter;
293
+
294
+ // Create reporter instances with output path configuration
295
+ if (reporterName === 'human') {
296
+ reporter = new HumanReporter({
297
+ color: true,
298
+ progress: progressOption ?? true,
299
+ quiet: explicitQuiet, // Only applies explicit --quiet flag; JSON reporter forcing quiet mode does not affect HumanReporter progress output
300
+ verbose: isVerbose,
301
+ });
302
+ } else if (reporterName === 'json') {
303
+ reporter = new JsonReporter({
304
+ ...(outputDir ? { outputPath: `${outputDir}/results.json` } : {}),
305
+ prettyPrint: true,
306
+ quiet: shouldBeQuiet, // JSON uses shouldBeQuiet to avoid polluting stdout
307
+ verbose: isVerbose,
308
+ });
309
+ } else if (reporterName === 'csv') {
310
+ reporter = new CsvReporter({
311
+ includeHeaders: true,
312
+ includeMetadata: true,
313
+ ...(outputDir ? { outputPath: `${outputDir}/results.csv` } : {}),
314
+ quiet: explicitQuiet, // Only applies explicit --quiet flag; CSV output can coexist with progress messages on different streams
315
+ verbose: isVerbose,
316
+ });
317
+ } else if (reporterName === 'simple') {
318
+ reporter = new SimpleReporter({
319
+ quiet: explicitQuiet,
320
+ verbose: isVerbose,
321
+ });
322
+ } else {
323
+ // Fall back to registry for custom reporters
324
+ reporter = context.reporterRegistry.get(reporterName);
325
+ if (!reporter) {
326
+ const availableReporters = ['human', 'json', 'csv', 'simple'];
327
+ throw new Error(
328
+ `Unknown reporter: ${reporterName}. Available: ${availableReporters.join(', ')}`,
329
+ );
330
+ }
331
+ }
332
+
333
+ reporters.push(reporter);
334
+ }
335
+
336
+ if (outputDir && showCliMessages) {
337
+ console.error(`Output directory configured: ${outputDir}`);
338
+ }
339
+
340
+ return reporters;
341
+ } catch (error) {
342
+ throw new Error(
343
+ `Reporter setup error: ${error instanceof Error ? error.message : String(error)}`,
344
+ );
345
+ }
346
+ };