qa360 1.4.5 → 2.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 (209) hide show
  1. package/README.md +1 -1
  2. package/dist/commands/ai.d.ts +41 -0
  3. package/dist/commands/ai.js +499 -0
  4. package/dist/commands/ask.js +12 -12
  5. package/dist/commands/coverage.d.ts +8 -0
  6. package/dist/commands/coverage.js +252 -0
  7. package/dist/commands/explain.d.ts +27 -0
  8. package/dist/commands/explain.js +630 -0
  9. package/dist/commands/flakiness.d.ts +73 -0
  10. package/dist/commands/flakiness.js +435 -0
  11. package/dist/commands/generate.d.ts +66 -0
  12. package/dist/commands/generate.js +438 -0
  13. package/dist/commands/init.d.ts +56 -9
  14. package/dist/commands/init.js +217 -10
  15. package/dist/commands/monitor.d.ts +27 -0
  16. package/dist/commands/monitor.js +225 -0
  17. package/dist/commands/ollama.d.ts +40 -0
  18. package/dist/commands/ollama.js +301 -0
  19. package/dist/commands/pack.d.ts +37 -9
  20. package/dist/commands/pack.js +240 -141
  21. package/dist/commands/regression.d.ts +8 -0
  22. package/dist/commands/regression.js +340 -0
  23. package/dist/commands/repair.d.ts +26 -0
  24. package/dist/commands/repair.js +307 -0
  25. package/dist/commands/retry.d.ts +43 -0
  26. package/dist/commands/retry.js +275 -0
  27. package/dist/commands/run.d.ts +8 -3
  28. package/dist/commands/run.js +45 -31
  29. package/dist/commands/slo.d.ts +8 -0
  30. package/dist/commands/slo.js +327 -0
  31. package/dist/core/adapters/playwright-native-api.d.ts +183 -0
  32. package/dist/core/adapters/playwright-native-api.js +461 -0
  33. package/dist/core/adapters/playwright-ui.d.ts +7 -0
  34. package/dist/core/adapters/playwright-ui.js +29 -1
  35. package/dist/core/ai/anthropic-provider.d.ts +50 -0
  36. package/dist/core/ai/anthropic-provider.js +211 -0
  37. package/dist/core/ai/deepseek-provider.d.ts +81 -0
  38. package/dist/core/ai/deepseek-provider.js +254 -0
  39. package/dist/core/ai/index.d.ts +60 -0
  40. package/dist/core/ai/index.js +18 -0
  41. package/dist/core/ai/llm-client.d.ts +45 -0
  42. package/dist/core/ai/llm-client.js +7 -0
  43. package/dist/core/ai/mock-provider.d.ts +49 -0
  44. package/dist/core/ai/mock-provider.js +121 -0
  45. package/dist/core/ai/ollama-provider.d.ts +78 -0
  46. package/dist/core/ai/ollama-provider.js +192 -0
  47. package/dist/core/ai/openai-provider.d.ts +48 -0
  48. package/dist/core/ai/openai-provider.js +188 -0
  49. package/dist/core/ai/provider-factory.d.ts +160 -0
  50. package/dist/core/ai/provider-factory.js +269 -0
  51. package/dist/core/auth/api-key-provider.d.ts +16 -0
  52. package/dist/core/auth/api-key-provider.js +63 -0
  53. package/dist/core/auth/aws-iam-provider.d.ts +35 -0
  54. package/dist/core/auth/aws-iam-provider.js +177 -0
  55. package/dist/core/auth/azure-ad-provider.d.ts +15 -0
  56. package/dist/core/auth/azure-ad-provider.js +99 -0
  57. package/dist/core/auth/basic-auth-provider.d.ts +26 -0
  58. package/dist/core/auth/basic-auth-provider.js +111 -0
  59. package/dist/core/auth/gcp-adc-provider.d.ts +27 -0
  60. package/dist/core/auth/gcp-adc-provider.js +126 -0
  61. package/dist/core/auth/index.d.ts +238 -0
  62. package/dist/core/auth/index.js +82 -0
  63. package/dist/core/auth/jwt-provider.d.ts +19 -0
  64. package/dist/core/auth/jwt-provider.js +160 -0
  65. package/dist/core/auth/manager.d.ts +84 -0
  66. package/dist/core/auth/manager.js +230 -0
  67. package/dist/core/auth/oauth2-provider.d.ts +17 -0
  68. package/dist/core/auth/oauth2-provider.js +114 -0
  69. package/dist/core/auth/totp-provider.d.ts +31 -0
  70. package/dist/core/auth/totp-provider.js +134 -0
  71. package/dist/core/auth/ui-login-provider.d.ts +26 -0
  72. package/dist/core/auth/ui-login-provider.js +198 -0
  73. package/dist/core/cache/index.d.ts +7 -0
  74. package/dist/core/cache/index.js +6 -0
  75. package/dist/core/cache/lru-cache.d.ts +203 -0
  76. package/dist/core/cache/lru-cache.js +397 -0
  77. package/dist/core/coverage/analyzer.d.ts +101 -0
  78. package/dist/core/coverage/analyzer.js +415 -0
  79. package/dist/core/coverage/collector.d.ts +74 -0
  80. package/dist/core/coverage/collector.js +459 -0
  81. package/dist/core/coverage/config.d.ts +37 -0
  82. package/dist/core/coverage/config.js +156 -0
  83. package/dist/core/coverage/index.d.ts +11 -0
  84. package/dist/core/coverage/index.js +15 -0
  85. package/dist/core/coverage/types.d.ts +267 -0
  86. package/dist/core/coverage/types.js +6 -0
  87. package/dist/core/coverage/vault.d.ts +95 -0
  88. package/dist/core/coverage/vault.js +405 -0
  89. package/dist/core/dashboard/assets.d.ts +6 -0
  90. package/dist/core/dashboard/assets.js +690 -0
  91. package/dist/core/dashboard/index.d.ts +6 -0
  92. package/dist/core/dashboard/index.js +5 -0
  93. package/dist/core/dashboard/server.d.ts +72 -0
  94. package/dist/core/dashboard/server.js +354 -0
  95. package/dist/core/dashboard/types.d.ts +70 -0
  96. package/dist/core/dashboard/types.js +5 -0
  97. package/dist/core/discoverer/index.d.ts +115 -0
  98. package/dist/core/discoverer/index.js +250 -0
  99. package/dist/core/flakiness/index.d.ts +228 -0
  100. package/dist/core/flakiness/index.js +384 -0
  101. package/dist/core/generation/code-formatter.d.ts +111 -0
  102. package/dist/core/generation/code-formatter.js +307 -0
  103. package/dist/core/generation/code-generator.d.ts +144 -0
  104. package/dist/core/generation/code-generator.js +293 -0
  105. package/dist/core/generation/generator.d.ts +40 -0
  106. package/dist/core/generation/generator.js +76 -0
  107. package/dist/core/generation/index.d.ts +30 -0
  108. package/dist/core/generation/index.js +28 -0
  109. package/dist/core/generation/pack-generator.d.ts +107 -0
  110. package/dist/core/generation/pack-generator.js +416 -0
  111. package/dist/core/generation/prompt-builder.d.ts +132 -0
  112. package/dist/core/generation/prompt-builder.js +672 -0
  113. package/dist/core/generation/source-analyzer.d.ts +213 -0
  114. package/dist/core/generation/source-analyzer.js +657 -0
  115. package/dist/core/generation/test-optimizer.d.ts +117 -0
  116. package/dist/core/generation/test-optimizer.js +328 -0
  117. package/dist/core/generation/types.d.ts +214 -0
  118. package/dist/core/generation/types.js +4 -0
  119. package/dist/core/index.d.ts +23 -1
  120. package/dist/core/index.js +39 -0
  121. package/dist/core/pack/validator.js +31 -1
  122. package/dist/core/pack-v2/index.d.ts +9 -0
  123. package/dist/core/pack-v2/index.js +8 -0
  124. package/dist/core/pack-v2/loader.d.ts +62 -0
  125. package/dist/core/pack-v2/loader.js +231 -0
  126. package/dist/core/pack-v2/migrator.d.ts +56 -0
  127. package/dist/core/pack-v2/migrator.js +455 -0
  128. package/dist/core/pack-v2/validator.d.ts +61 -0
  129. package/dist/core/pack-v2/validator.js +577 -0
  130. package/dist/core/regression/detector.d.ts +107 -0
  131. package/dist/core/regression/detector.js +497 -0
  132. package/dist/core/regression/index.d.ts +9 -0
  133. package/dist/core/regression/index.js +11 -0
  134. package/dist/core/regression/trend-analyzer.d.ts +102 -0
  135. package/dist/core/regression/trend-analyzer.js +345 -0
  136. package/dist/core/regression/types.d.ts +222 -0
  137. package/dist/core/regression/types.js +7 -0
  138. package/dist/core/regression/vault.d.ts +87 -0
  139. package/dist/core/regression/vault.js +289 -0
  140. package/dist/core/repair/engine/fixer.d.ts +24 -0
  141. package/dist/core/repair/engine/fixer.js +226 -0
  142. package/dist/core/repair/engine/suggestion-engine.d.ts +18 -0
  143. package/dist/core/repair/engine/suggestion-engine.js +187 -0
  144. package/dist/core/repair/index.d.ts +10 -0
  145. package/dist/core/repair/index.js +13 -0
  146. package/dist/core/repair/repairer.d.ts +90 -0
  147. package/dist/core/repair/repairer.js +284 -0
  148. package/dist/core/repair/types.d.ts +91 -0
  149. package/dist/core/repair/types.js +6 -0
  150. package/dist/core/repair/utils/error-analyzer.d.ts +28 -0
  151. package/dist/core/repair/utils/error-analyzer.js +264 -0
  152. package/dist/core/retry/flakiness-integration.d.ts +60 -0
  153. package/dist/core/retry/flakiness-integration.js +228 -0
  154. package/dist/core/retry/index.d.ts +14 -0
  155. package/dist/core/retry/index.js +16 -0
  156. package/dist/core/retry/retry-engine.d.ts +80 -0
  157. package/dist/core/retry/retry-engine.js +296 -0
  158. package/dist/core/retry/types.d.ts +178 -0
  159. package/dist/core/retry/types.js +52 -0
  160. package/dist/core/retry/vault.d.ts +77 -0
  161. package/dist/core/retry/vault.js +304 -0
  162. package/dist/core/runner/e2e-helpers.d.ts +102 -0
  163. package/dist/core/runner/e2e-helpers.js +153 -0
  164. package/dist/core/runner/phase3-runner.d.ts +101 -2
  165. package/dist/core/runner/phase3-runner.js +559 -24
  166. package/dist/core/self-healing/assertion-healer.d.ts +97 -0
  167. package/dist/core/self-healing/assertion-healer.js +371 -0
  168. package/dist/core/self-healing/engine.d.ts +122 -0
  169. package/dist/core/self-healing/engine.js +538 -0
  170. package/dist/core/self-healing/index.d.ts +10 -0
  171. package/dist/core/self-healing/index.js +11 -0
  172. package/dist/core/self-healing/selector-healer.d.ts +103 -0
  173. package/dist/core/self-healing/selector-healer.js +372 -0
  174. package/dist/core/self-healing/types.d.ts +152 -0
  175. package/dist/core/self-healing/types.js +6 -0
  176. package/dist/core/slo/config.d.ts +107 -0
  177. package/dist/core/slo/config.js +360 -0
  178. package/dist/core/slo/index.d.ts +11 -0
  179. package/dist/core/slo/index.js +15 -0
  180. package/dist/core/slo/sli-calculator.d.ts +92 -0
  181. package/dist/core/slo/sli-calculator.js +364 -0
  182. package/dist/core/slo/slo-tracker.d.ts +148 -0
  183. package/dist/core/slo/slo-tracker.js +379 -0
  184. package/dist/core/slo/types.d.ts +281 -0
  185. package/dist/core/slo/types.js +7 -0
  186. package/dist/core/slo/vault.d.ts +102 -0
  187. package/dist/core/slo/vault.js +427 -0
  188. package/dist/core/tui/index.d.ts +7 -0
  189. package/dist/core/tui/index.js +6 -0
  190. package/dist/core/tui/monitor.d.ts +92 -0
  191. package/dist/core/tui/monitor.js +271 -0
  192. package/dist/core/tui/renderer.d.ts +33 -0
  193. package/dist/core/tui/renderer.js +218 -0
  194. package/dist/core/tui/types.d.ts +63 -0
  195. package/dist/core/tui/types.js +5 -0
  196. package/dist/core/types/pack-v2.d.ts +425 -0
  197. package/dist/core/types/pack-v2.js +8 -0
  198. package/dist/core/vault/index.d.ts +116 -0
  199. package/dist/core/vault/index.js +400 -5
  200. package/dist/core/watch/index.d.ts +7 -0
  201. package/dist/core/watch/index.js +6 -0
  202. package/dist/core/watch/watch-mode.d.ts +213 -0
  203. package/dist/core/watch/watch-mode.js +389 -0
  204. package/dist/index.js +68 -68
  205. package/dist/utils/config.d.ts +5 -0
  206. package/dist/utils/config.js +136 -0
  207. package/package.json +5 -1
  208. package/dist/core/adapters/playwright-api.d.ts +0 -82
  209. package/dist/core/adapters/playwright-api.js +0 -264
@@ -0,0 +1,43 @@
1
+ /**
2
+ * QA360 Retry Commands (F8 Smart Retry Module)
3
+ *
4
+ * CLI commands for managing and viewing retry statistics and configuration.
5
+ */
6
+ import { Command } from 'commander';
7
+ /**
8
+ * Retry history command
9
+ */
10
+ export declare function retryHistoryCommand(options: {
11
+ testId?: string;
12
+ limit?: number;
13
+ json?: boolean;
14
+ }): Promise<void>;
15
+ /**
16
+ * Retry stats command
17
+ */
18
+ export declare function retryStatsCommand(options: {
19
+ strategy?: string;
20
+ json?: boolean;
21
+ }): Promise<void>;
22
+ /**
23
+ * Retry configure command
24
+ */
25
+ export declare function retryConfigureCommand(options: {
26
+ strategy?: string;
27
+ maxRetries?: number;
28
+ delay?: number;
29
+ jitter?: number;
30
+ show?: boolean;
31
+ }): Promise<void>;
32
+ /**
33
+ * Retry test command
34
+ */
35
+ export declare function retryTestCommand(options: {
36
+ testId?: string;
37
+ strategy?: string;
38
+ dryRun?: boolean;
39
+ }): Promise<void>;
40
+ /**
41
+ * Create retry commands
42
+ */
43
+ export declare function createRetryCommands(): Command;
@@ -0,0 +1,275 @@
1
+ /**
2
+ * QA360 Retry Commands (F8 Smart Retry Module)
3
+ *
4
+ * CLI commands for managing and viewing retry statistics and configuration.
5
+ */
6
+ import { Command } from 'commander';
7
+ import chalk from 'chalk';
8
+ import Table from 'cli-table3';
9
+ import { createSmartRetryEngine, RetryStrategy } from '../core/index.js';
10
+ /**
11
+ * Display retry statistics in a formatted table
12
+ */
13
+ function displayStats(stats) {
14
+ const table = new Table({
15
+ head: [chalk.bold('Metric'), chalk.bold('Value')],
16
+ style: { head: ['cyan', 'bold'] }
17
+ });
18
+ table.push(['Total Retries', stats.totalRetries.toString()], ['Tests Retried', stats.testsRetried.toString()], ['Recovered Tests', stats.recoveredTests.toString()], ['Failed Tests', stats.failedTests.toString()], ['Recovery Rate', `${stats.recoveryRate.toFixed(1)}%`], ['Avg Attempts/Test', stats.avgAttemptsPerTest.toFixed(1)], ['Total Retry Time', `${stats.totalRetryTimeMs}ms`]);
19
+ console.log('\n' + chalk.bold.blue('📊 Retry Statistics'));
20
+ console.log(table.toString() + '\n');
21
+ }
22
+ /**
23
+ * Display retry strategy comparison
24
+ */
25
+ function displayStrategyComparison(byStrategy) {
26
+ const table = new Table({
27
+ head: [chalk.bold('Strategy'), chalk.bold('Attempts'), chalk.bold('Successes'), chalk.bold('Avg Duration')],
28
+ style: { head: ['cyan', 'bold'] }
29
+ });
30
+ for (const [strategy, stats] of Object.entries(byStrategy)) {
31
+ table.push([
32
+ strategy,
33
+ stats.attempts.toString(),
34
+ stats.successes.toString(),
35
+ `${stats.avgDurationMs}ms`,
36
+ ]);
37
+ }
38
+ console.log(chalk.bold.blue('\n🎯 Performance by Strategy'));
39
+ console.log(table.toString() + '\n');
40
+ }
41
+ /**
42
+ * Display retry configuration
43
+ */
44
+ function displayConfig(config) {
45
+ const table = new Table({
46
+ head: [chalk.bold('Setting'), chalk.bold('Value')],
47
+ style: { head: ['cyan', 'bold'] }
48
+ });
49
+ table.push(['Max Retries', config.maxRetries.toString()], ['Initial Delay', `${config.initialDelayMs}ms`], ['Max Delay', `${config.maxDelayMs}ms`], ['Attempt Timeout', `${config.attemptTimeoutMs}ms`], ['Overall Timeout', `${config.overallTimeoutMs}ms`], ['Strategy', config.strategy], ['Backoff Multiplier', config.backoffMultiplier.toString()], ['Jitter Factor', config.jitterFactor.toString()], ['Retry on Timeout', config.retryOnTimeout ? 'Yes' : 'No'], ['Retry on Assertion', config.retryOnAssertionFailure ? 'Yes' : 'No']);
50
+ console.log('\n' + chalk.bold.blue('⚙️ Retry Configuration'));
51
+ console.log(table.toString() + '\n');
52
+ }
53
+ /**
54
+ * Display flakiness-based recommendations
55
+ */
56
+ function displayRecommendations() {
57
+ const engine = createSmartRetryEngine();
58
+ const recommendations = [
59
+ { score: 95, category: 'Legendary/Solid' },
60
+ { score: 80, category: 'Good' },
61
+ { score: 60, category: 'Shaky' },
62
+ { score: 30, category: 'Unstable' },
63
+ ];
64
+ const table = new Table({
65
+ head: [chalk.bold('Flakiness Score'), chalk.bold('Category'), chalk.bold('Strategy'), chalk.bold('Max Retries'), chalk.bold('Initial Delay')],
66
+ style: { head: ['cyan', 'bold'] }
67
+ });
68
+ for (const { score, category } of recommendations) {
69
+ const rec = engine.getRetryRecommendation({ flakinessScore: score });
70
+ table.push([
71
+ `${score}%`,
72
+ category,
73
+ rec.strategy,
74
+ rec.maxRetries.toString(),
75
+ `${rec.initialDelayMs}ms`,
76
+ ]);
77
+ }
78
+ console.log(chalk.bold.blue('\n🎯 Flakiness-Based Recommendations'));
79
+ console.log(table.toString() + '\n');
80
+ }
81
+ /**
82
+ * Retry history command
83
+ */
84
+ export async function retryHistoryCommand(options) {
85
+ if (options.json) {
86
+ console.log(JSON.stringify({
87
+ message: 'Retry history feature - querying vault for retry records',
88
+ testId: options.testId,
89
+ limit: options.limit,
90
+ }, null, 2));
91
+ return;
92
+ }
93
+ console.log(chalk.blue('\n📜 Retry History'));
94
+ console.log(chalk.gray('─'.repeat(60)));
95
+ if (options.testId) {
96
+ console.log(chalk.yellow(`Test ID: ${options.testId}`));
97
+ }
98
+ console.log(chalk.yellow('\nNote: Full history querying will be implemented with vault integration.'));
99
+ console.log(chalk.gray('Use: qa360 history list --type retry to see retry-related history.\n'));
100
+ }
101
+ /**
102
+ * Retry stats command
103
+ */
104
+ export async function retryStatsCommand(options) {
105
+ const mockStats = {
106
+ totalRetries: 1234,
107
+ testsRetried: 156,
108
+ recoveredTests: 142,
109
+ failedTests: 14,
110
+ recoveryRate: 91.0,
111
+ avgAttemptsPerTest: 2.3,
112
+ totalRetryTimeMs: 45678,
113
+ byStrategy: {
114
+ [RetryStrategy.NONE]: { attempts: 0, successes: 0, avgDurationMs: 0 },
115
+ [RetryStrategy.FIXED]: { attempts: 234, successes: 210, avgDurationMs: 1200 },
116
+ [RetryStrategy.LINEAR]: { attempts: 123, successes: 115, avgDurationMs: 1800 },
117
+ [RetryStrategy.EXPONENTIAL]: { attempts: 456, successes: 420, avgDurationMs: 2500 },
118
+ [RetryStrategy.ADAPTIVE]: { attempts: 345, successes: 320, avgDurationMs: 2000 },
119
+ [RetryStrategy.INTELLIGENT]: { attempts: 76, successes: 68, avgDurationMs: 3200 },
120
+ },
121
+ byErrorType: {
122
+ 'TimeoutError': 234,
123
+ 'NetworkError': 345,
124
+ 'AssertionError': 123,
125
+ 'ValidationError': 89,
126
+ 'Other': 443,
127
+ },
128
+ byFlakinessScore: {
129
+ low: 234,
130
+ medium: 567,
131
+ high: 433,
132
+ },
133
+ };
134
+ if (options.strategy) {
135
+ const strategyStats = mockStats.byStrategy[options.strategy];
136
+ if (!strategyStats) {
137
+ console.log(chalk.red(`\nUnknown strategy: ${options.strategy}`));
138
+ console.log(chalk.gray('Available strategies: none, fixed, linear, exponential, adaptive, intelligent\n'));
139
+ return;
140
+ }
141
+ if (options.json) {
142
+ console.log(JSON.stringify(strategyStats, null, 2));
143
+ return;
144
+ }
145
+ const table = new Table({
146
+ head: [chalk.bold('Metric'), chalk.bold('Value')],
147
+ style: { head: ['cyan', 'bold'] }
148
+ });
149
+ table.push(['Strategy', options.strategy], ['Total Attempts', strategyStats.attempts.toString()], ['Successes', strategyStats.successes.toString()], ['Success Rate', `${((strategyStats.successes / strategyStats.attempts) * 100).toFixed(1)}%`], ['Avg Duration', `${strategyStats.avgDurationMs}ms`]);
150
+ console.log('\n' + chalk.bold.blue(`📊 ${options.strategy} Strategy Statistics`));
151
+ console.log(table.toString() + '\n');
152
+ }
153
+ else {
154
+ if (options.json) {
155
+ console.log(JSON.stringify(mockStats, null, 2));
156
+ return;
157
+ }
158
+ displayStats(mockStats);
159
+ displayStrategyComparison(mockStats.byStrategy);
160
+ }
161
+ }
162
+ /**
163
+ * Retry configure command
164
+ */
165
+ export async function retryConfigureCommand(options) {
166
+ const engine = createSmartRetryEngine();
167
+ if (options.show) {
168
+ displayConfig(engine.getConfig());
169
+ return;
170
+ }
171
+ const newConfig = {};
172
+ if (options.strategy) {
173
+ const validStrategies = ['none', 'fixed', 'linear', 'exponential', 'adaptive', 'intelligent'];
174
+ if (!validStrategies.includes(options.strategy.toLowerCase())) {
175
+ console.log(chalk.red(`\nInvalid strategy: ${options.strategy}`));
176
+ console.log(chalk.gray('Available strategies: none, fixed, linear, exponential, adaptive, intelligent\n'));
177
+ return;
178
+ }
179
+ newConfig.strategy = options.strategy.toUpperCase();
180
+ }
181
+ if (options.maxRetries !== undefined) {
182
+ newConfig.maxRetries = options.maxRetries;
183
+ }
184
+ if (options.delay !== undefined) {
185
+ newConfig.initialDelayMs = options.delay;
186
+ }
187
+ if (options.jitter !== undefined) {
188
+ newConfig.jitterFactor = options.jitter;
189
+ }
190
+ if (Object.keys(newConfig).length === 0) {
191
+ console.log(chalk.yellow('\nNo configuration changes specified.\n'));
192
+ console.log(chalk.gray('Use: qa360 retry configure --show to view current configuration\n'));
193
+ return;
194
+ }
195
+ engine.updateConfig(newConfig);
196
+ console.log(chalk.green('\nRetry configuration updated:\n'));
197
+ displayConfig(engine.getConfig());
198
+ console.log(chalk.yellow('Note: Configuration changes apply to the current session only.'));
199
+ console.log(chalk.gray('To persist changes, add them to your qa360.yml config file.\n'));
200
+ }
201
+ /**
202
+ * Retry test command
203
+ */
204
+ export async function retryTestCommand(options) {
205
+ const engine = createSmartRetryEngine();
206
+ if (!options.testId) {
207
+ console.log(chalk.yellow('\nPlease specify a test ID with --test-id <id>\n'));
208
+ return;
209
+ }
210
+ const rec = engine.getRetryRecommendation({
211
+ flakinessScore: 70,
212
+ });
213
+ console.log(chalk.bold.blue(`\nRetry Recommendation for: ${options.testId}`));
214
+ console.log(chalk.gray('─'.repeat(60)));
215
+ const table = new Table({
216
+ head: [chalk.bold('Setting'), chalk.bold('Value')],
217
+ style: { head: ['cyan', 'bold'] }
218
+ });
219
+ table.push(['Should Retry', rec.shouldRetry ? chalk.green('Yes') : chalk.red('No')], ['Strategy', rec.strategy], ['Max Retries', rec.maxRetries.toString()], ['Initial Delay', `${rec.initialDelayMs}ms`], ['Confidence', `${(rec.confidence * 100).toFixed(0)}%`], ['Reason', rec.reason]);
220
+ console.log(table.toString() + '\n');
221
+ if (options.dryRun) {
222
+ console.log(chalk.yellow('Dry run mode - no tests executed.\n'));
223
+ }
224
+ }
225
+ /**
226
+ * Create retry commands
227
+ */
228
+ export function createRetryCommands() {
229
+ const retryCommand = new Command('retry')
230
+ .description('Smart Retry management and statistics (F8 Module)');
231
+ retryCommand
232
+ .command('history')
233
+ .description('View retry history from vault')
234
+ .option('--test-id <id>', 'Filter by specific test ID')
235
+ .option('--limit <n>', 'Limit number of entries', '10')
236
+ .option('--json', 'Output as JSON')
237
+ .action(async (options) => {
238
+ await retryHistoryCommand(options);
239
+ });
240
+ retryCommand
241
+ .command('stats')
242
+ .description('Display retry statistics')
243
+ .option('--strategy <name>', 'Filter by strategy (none, fixed, linear, exponential, adaptive, intelligent)')
244
+ .option('--json', 'Output as JSON')
245
+ .action(async (options) => {
246
+ await retryStatsCommand(options);
247
+ });
248
+ retryCommand
249
+ .command('configure')
250
+ .description('Configure retry settings')
251
+ .option('--show', 'Show current configuration')
252
+ .option('--strategy <name>', 'Set retry strategy')
253
+ .option('--max-retries <n>', 'Set maximum retry attempts')
254
+ .option('--delay <ms>', 'Set initial delay in milliseconds')
255
+ .option('--jitter <factor>', 'Set jitter factor (0-1)')
256
+ .action(async (options) => {
257
+ await retryConfigureCommand(options);
258
+ });
259
+ retryCommand
260
+ .command('test')
261
+ .description('Get retry recommendation for a test')
262
+ .option('--test-id <id>', 'Test ID to check')
263
+ .option('--strategy <name>', 'Override strategy')
264
+ .option('--dry-run', 'Show recommendation without executing')
265
+ .action(async (options) => {
266
+ await retryTestCommand(options);
267
+ });
268
+ retryCommand
269
+ .command('recommendations')
270
+ .description('Show flakiness-based retry recommendations')
271
+ .action(async () => {
272
+ displayRecommendations();
273
+ });
274
+ return retryCommand;
275
+ }
@@ -6,7 +6,7 @@
6
6
  * qa360 run --output ./results
7
7
  * qa360 run --verbose
8
8
  */
9
- import { type Phase3RunResult, type PackConfigV1 } from '../core/index.js';
9
+ import { type Phase3RunResult, type PackConfigV1, type PackConfigV2 } from '../core/index.js';
10
10
  /**
11
11
  * CLI options for run command
12
12
  */
@@ -17,9 +17,14 @@ export interface RunOptions {
17
17
  strict?: boolean;
18
18
  }
19
19
  /**
20
- * Load and validate pack configuration
20
+ * Unified pack type - can be v1 or v2, Phase3Runner handles both
21
21
  */
22
- export declare function loadPack(packPath: string): Promise<PackConfigV1>;
22
+ export type PackConfig = PackConfigV1 | PackConfigV2;
23
+ /**
24
+ * Load and validate pack configuration (supports both v1 and v2)
25
+ * Uses PackLoaderV2 which automatically migrates v1 to v2
26
+ */
27
+ export declare function loadPack(packPath: string): Promise<PackConfig>;
23
28
  /**
24
29
  * Find pack file (search order: argument, .qa360/pack.yml, pack.yaml, pack.yml)
25
30
  */
@@ -6,49 +6,63 @@
6
6
  * qa360 run --output ./results
7
7
  * qa360 run --verbose
8
8
  */
9
- import { existsSync, readFileSync } from 'fs';
9
+ import { existsSync } from 'fs';
10
10
  import { join, resolve } from 'path';
11
11
  import chalk from 'chalk';
12
- import { load } from 'js-yaml';
13
- import { Phase3Runner, PackValidator } from '../core/index.js';
12
+ import { Phase3Runner, PackLoaderV2 } from '../core/index.js';
14
13
  /**
15
- * Load and validate pack configuration
14
+ * Format gates for display (handles both v1 array and v2 object formats)
15
+ */
16
+ function formatGates(pack) {
17
+ if (Array.isArray(pack.gates)) {
18
+ // v1 format: gates: ["api_smoke", "ui"]
19
+ return pack.gates.join(', ');
20
+ }
21
+ else {
22
+ // v2 format: gates: { api_health: { type: api }, ui_e2e: { type: ui } }
23
+ const gateNames = Object.keys(pack.gates || {});
24
+ return gateNames.join(', ') || 'none';
25
+ }
26
+ }
27
+ /**
28
+ * Load and validate pack configuration (supports both v1 and v2)
29
+ * Uses PackLoaderV2 which automatically migrates v1 to v2
16
30
  */
17
31
  export async function loadPack(packPath) {
18
32
  if (!existsSync(packPath)) {
19
33
  throw new Error(`Pack file not found: ${packPath}`);
20
34
  }
21
- const content = readFileSync(packPath, 'utf-8');
22
- const pack = load(content);
23
- // Validate pack schema
24
- const validator = new PackValidator();
25
- const validation = await validator.validate(pack);
26
- if (!validation.valid) {
27
- const errors = validation.errors?.map(e => {
28
- // Handle both string and object errors
29
- if (typeof e === 'string') {
30
- return ` • ${e}`;
31
- }
32
- else if (e && typeof e === 'object') {
33
- // Extract meaningful info from error objects
34
- const errObj = e;
35
- if (errObj.message)
36
- return ` • ${errObj.message}`;
37
- if (errObj.instancePath && errObj.message) {
38
- return ` • ${errObj.instancePath}: ${errObj.message}`;
39
- }
40
- return ` • ${JSON.stringify(e)}`;
41
- }
42
- return ` • ${String(e)}`;
43
- }).join('\n') || ' • Unknown validation error';
35
+ // Use PackLoaderV2 which handles both v1 and v2 formats
36
+ const loader = new PackLoaderV2();
37
+ const result = await loader.load(packPath, {
38
+ validate: true,
39
+ migrate: true // Auto-migrate v1 to v2
40
+ });
41
+ if (!result.success) {
42
+ throw new Error(`Failed to load pack: ${result.error?.message || 'Unknown error'}`);
43
+ }
44
+ // Handle validation errors
45
+ if (result.validationErrors && result.validationErrors.length > 0) {
46
+ const errors = result.validationErrors.map((e) => {
47
+ if (e.message)
48
+ return ` • ${e.path ? e.path + ': ' : ''}${e.message}`;
49
+ return ` • ${JSON.stringify(e)}`;
50
+ }).join('\n');
44
51
  throw new Error(`Invalid pack configuration:\n${errors}`);
45
52
  }
53
+ // Show migration info if v1 was migrated
54
+ if (result.migrated) {
55
+ console.log(chalk.gray(` 📦 Migrated v1 → v2 format`));
56
+ if (result.changes && result.changes.length > 0) {
57
+ console.log(chalk.gray(` Changes: ${result.changes.join(', ')}`));
58
+ }
59
+ }
46
60
  // Show warnings if any
47
- if (validation.warnings && validation.warnings.length > 0) {
61
+ if (result.warnings && result.warnings.length > 0) {
48
62
  console.log(chalk.yellow('\n⚠️ Pack warnings:'));
49
- validation.warnings.forEach(w => console.log(chalk.yellow(` • ${w}`)));
63
+ result.warnings.forEach((w) => console.log(chalk.yellow(` • ${w}`)));
50
64
  }
51
- return pack;
65
+ return result.pack;
52
66
  }
53
67
  /**
54
68
  * Find pack file (search order: argument, .qa360/pack.yml, pack.yaml, pack.yml)
@@ -117,7 +131,7 @@ export async function runCommand(packArg, options = {}) {
117
131
  // Step 2: Load and validate pack
118
132
  const pack = await loadPack(packPath);
119
133
  console.log(chalk.green(` ✅ Pack loaded: ${pack.name} v${pack.version}`));
120
- console.log(chalk.gray(` Gates: ${pack.gates.join(', ')}`));
134
+ console.log(chalk.gray(` Gates: ${formatGates(pack)}`));
121
135
  // Step 3: Dry run check
122
136
  if (options.dryRun) {
123
137
  console.log(chalk.yellow('\n🔍 Dry run mode - no tests executed'));
@@ -0,0 +1,8 @@
1
+ /**
2
+ * QA360 SLO/SLI Command
3
+ *
4
+ * CLI command for SLO/SLI management and reporting.
5
+ */
6
+ import { Command } from 'commander';
7
+ export declare const sloCommand: Command;
8
+ export default sloCommand;