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
@@ -1,29 +1,32 @@
1
1
  /**
2
2
  * QA360 Pack Commands
3
+ *
3
4
  * Implements pack validate, lint, and migrate commands
5
+ * Supports both v1 and v2 pack formats
4
6
  */
5
7
  import { readFileSync, writeFileSync, existsSync } from 'fs';
6
- import { join } from 'path';
8
+ import { resolve } from 'path';
7
9
  import chalk from 'chalk';
8
10
  import ora from 'ora';
9
11
  import * as yaml from 'js-yaml';
10
- import { PackValidator, PackMigrator } from '../core/index.js';
12
+ import { PackValidator, PackMigrator as PackMigratorV1 } from '../core/index.js';
13
+ import { PackValidatorV2, PackMigrator, PackLoaderV2 } from '../core/index.js';
11
14
  export class QA360Pack {
12
- qa360Dir = join(process.cwd(), '.qa360');
13
- packPath = join(this.qa360Dir, 'pack.yml');
14
- validator = new PackValidator();
15
- migrator = new PackMigrator();
15
+ qa360Dir = resolve(process.cwd(), '.qa360');
16
+ packPath = resolve(this.qa360Dir, 'pack.yml');
17
+ validatorV1 = new PackValidator();
18
+ migratorV1 = new PackMigratorV1();
16
19
  /**
17
- * Validate pack.yml against v1 schema
20
+ * Validate pack.yml (auto-detects v1 or v2)
18
21
  */
19
- async validate(packPath) {
20
- const spinner = ora('🔍 Validation du pack...').start();
22
+ async validate(packPath, options) {
23
+ const spinner = ora('🔍 Validating pack...').start();
21
24
  try {
22
25
  const targetPath = packPath || this.packPath;
23
26
  if (!existsSync(targetPath)) {
24
- spinner.fail(chalk.red('❌ Pack non trouvé'));
25
- console.log(chalk.yellow(`📁 Chemin: ${targetPath}`));
26
- console.log(chalk.blue('💡 Utilisez: qa360 ask "..." pour créer un pack'));
27
+ spinner.fail(chalk.red('❌ Pack not found'));
28
+ console.log(chalk.yellow(`📁 Path: ${targetPath}`));
29
+ console.log(chalk.blue('💡 Use: qa360 init to create a pack'));
27
30
  return { valid: false, exitCode: 1 };
28
31
  }
29
32
  // Load and parse pack
@@ -33,46 +36,94 @@ export class QA360Pack {
33
36
  pack = yaml.load(packContent);
34
37
  }
35
38
  catch (parseError) {
36
- spinner.fail(chalk.red('❌ Erreur de parsing YAML'));
37
- console.log(chalk.red(`\n🔍 Erreur: ${parseError instanceof Error ? parseError.message : 'Parse error'}`));
39
+ spinner.fail(chalk.red('❌ YAML parse error'));
40
+ console.log(chalk.red(`\n🔍 Error: ${parseError instanceof Error ? parseError.message : 'Parse error'}`));
38
41
  return { valid: false, exitCode: 1 };
39
42
  }
40
- // Validate
41
- const result = this.validator.validate(pack);
42
- if (result.valid) {
43
- spinner.succeed(chalk.green('✅ Pack valide'));
44
- // Show warnings if any
45
- if (result.warnings.length > 0) {
46
- console.log(chalk.yellow('\n⚠️ Avertissements:'));
47
- for (const warning of result.warnings) {
48
- console.log(chalk.yellow(` • ${warning.code}: ${warning.message}`));
49
- if (warning.suggestion) {
50
- console.log(chalk.blue(` 💡 ${warning.suggestion}`));
51
- }
43
+ // Detect version
44
+ const loader = new PackLoaderV2();
45
+ const detectedVersion = this.detectPackVersion(pack);
46
+ if (detectedVersion === 2) {
47
+ return await this.validateV2(targetPath, pack, spinner, options?.checkFiles);
48
+ }
49
+ else {
50
+ return await this.validateV1(pack, spinner);
51
+ }
52
+ }
53
+ catch (error) {
54
+ spinner.fail(chalk.red('❌ Validation error'));
55
+ console.log(chalk.red(`\n🔍 Error: ${error instanceof Error ? error.message : 'Unknown error'}`));
56
+ return { valid: false, exitCode: 1 };
57
+ }
58
+ }
59
+ /**
60
+ * Validate v1 pack
61
+ */
62
+ async validateV1(pack, spinner) {
63
+ const result = this.validatorV1.validate(pack);
64
+ if (result.valid) {
65
+ spinner.succeed(chalk.green('✅ Pack v1 is valid'));
66
+ if (result.warnings.length > 0) {
67
+ console.log(chalk.yellow('\n⚠️ Warnings:'));
68
+ for (const warning of result.warnings) {
69
+ console.log(chalk.yellow(` • ${warning.code}: ${warning.message}`));
70
+ if (warning.suggestion) {
71
+ console.log(chalk.blue(` 💡 ${warning.suggestion}`));
52
72
  }
53
73
  }
54
- // Show pack summary
55
- this.showPackSummary(pack);
56
- return { valid: true, exitCode: 0 };
57
74
  }
58
- else {
59
- spinner.fail(chalk.red('❌ Pack invalide'));
60
- console.log(chalk.red('\n🔍 Erreurs détectées:'));
61
- for (const error of result.errors) {
62
- console.log(chalk.red(` • ${error.code}: ${error.message}`));
63
- console.log(chalk.gray(` 📍 Chemin: ${error.path}`));
64
- if (error.suggestion) {
65
- console.log(chalk.blue(` 💡 ${error.suggestion}`));
75
+ this.showPackSummary(pack);
76
+ return { valid: true, exitCode: 0 };
77
+ }
78
+ else {
79
+ spinner.fail(chalk.red('❌ Pack v1 validation failed'));
80
+ console.log(chalk.red('\n🔍 Errors found:'));
81
+ for (const error of result.errors) {
82
+ console.log(chalk.red(` ${error.code}: ${error.message}`));
83
+ console.log(chalk.gray(` 📍 Path: ${error.path}`));
84
+ if (error.suggestion) {
85
+ console.log(chalk.blue(` 💡 ${error.suggestion}`));
86
+ }
87
+ }
88
+ console.log(chalk.blue('\n💡 Tip: Use qa360 pack migrate to upgrade to v2'));
89
+ return { valid: false, exitCode: 1 };
90
+ }
91
+ }
92
+ /**
93
+ * Validate v2 pack
94
+ */
95
+ async validateV2(packPath, pack, spinner, checkFiles) {
96
+ const validator = new PackValidatorV2(packPath);
97
+ const result = await validator.validate(pack, { checkFilesExist: checkFiles });
98
+ if (result.valid) {
99
+ spinner.succeed(chalk.green('✅ Pack v2 is valid'));
100
+ if (result.warnings.length > 0) {
101
+ console.log(chalk.yellow('\n⚠️ Warnings:'));
102
+ for (const warning of result.warnings.slice(0, 10)) {
103
+ console.log(chalk.yellow(` • ${warning.code}: ${warning.message}`));
104
+ if (warning.path) {
105
+ console.log(chalk.gray(` 📍 ${warning.path}`));
66
106
  }
67
107
  }
68
- console.log(chalk.blue('\n📚 Documentation: https://qa360.dev/docs/pack-v1'));
69
- console.log(chalk.blue('🔧 Auto-correction: qa360 pack lint'));
70
- return { valid: false, exitCode: 1 };
108
+ if (result.warnings.length > 10) {
109
+ console.log(chalk.gray(` ... and ${result.warnings.length - 10} more warnings`));
110
+ }
71
111
  }
112
+ this.showPackV2Summary(pack, result);
113
+ return { valid: true, exitCode: 0 };
72
114
  }
73
- catch (error) {
74
- spinner.fail(chalk.red('❌ Erreur de validation'));
75
- console.log(chalk.red(`\n🔍 Erreur: ${error instanceof Error ? error.message : 'Unknown error'}`));
115
+ else {
116
+ spinner.fail(chalk.red('❌ Pack v2 validation failed'));
117
+ console.log(chalk.red('\n🔍 Errors found:'));
118
+ for (const error of result.errors.slice(0, 10)) {
119
+ console.log(chalk.red(` • ${error.code}: ${error.message}`));
120
+ if (error.path) {
121
+ console.log(chalk.gray(` 📍 ${error.path}`));
122
+ }
123
+ }
124
+ if (result.errors.length > 10) {
125
+ console.log(chalk.gray(` ... and ${result.errors.length - 10} more errors`));
126
+ }
76
127
  return { valid: false, exitCode: 1 };
77
128
  }
78
129
  }
@@ -80,12 +131,12 @@ export class QA360Pack {
80
131
  * Lint and auto-fix pack.yml
81
132
  */
82
133
  async lint(packPath) {
83
- const spinner = ora('🔧 Correction automatique du pack...').start();
134
+ const spinner = ora('🔧 Auto-fixing pack...').start();
84
135
  try {
85
136
  const targetPath = packPath || this.packPath;
86
137
  if (!existsSync(targetPath)) {
87
- spinner.fail(chalk.red('❌ Pack non trouvé'));
88
- console.log(chalk.yellow(`📁 Chemin: ${targetPath}`));
138
+ spinner.fail(chalk.red('❌ Pack not found'));
139
+ console.log(chalk.yellow(`📁 Path: ${targetPath}`));
89
140
  return { success: false, exitCode: 1 };
90
141
  }
91
142
  // Load pack
@@ -95,19 +146,29 @@ export class QA360Pack {
95
146
  pack = yaml.load(packContent);
96
147
  }
97
148
  catch (parseError) {
98
- spinner.fail(chalk.red('❌ Erreur de parsing YAML'));
99
- console.log(chalk.red(`\n🔍 Erreur: ${parseError instanceof Error ? parseError.message : 'Parse error'}`));
149
+ spinner.fail(chalk.red('❌ YAML parse error'));
150
+ console.log(chalk.red(`\n🔍 Error: ${parseError instanceof Error ? parseError.message : 'Parse error'}`));
100
151
  return { success: false, exitCode: 1 };
101
152
  }
102
153
  // Apply auto-fixes
103
154
  const fixes = this.applyAutoFixes(pack);
104
155
  if (fixes.length === 0) {
105
- spinner.succeed(chalk.green('✅ Pack déjà conforme'));
156
+ spinner.succeed(chalk.green('✅ Pack already valid'));
106
157
  return { success: true, exitCode: 0 };
107
158
  }
108
159
  // Validate after fixes
109
- const result = this.validator.validate(pack);
110
- if (result.valid || result.errors.length < 5) { // Allow some remaining errors
160
+ const detectedVersion = this.detectPackVersion(pack);
161
+ let valid = false;
162
+ if (detectedVersion === 2) {
163
+ const validator = new PackValidatorV2(targetPath);
164
+ const result = await validator.validate(pack);
165
+ valid = result.valid || result.errors.length < 5;
166
+ }
167
+ else {
168
+ const result = this.validatorV1.validate(pack);
169
+ valid = result.valid || result.errors.length < 5;
170
+ }
171
+ if (valid) {
111
172
  // Save fixed pack
112
173
  const fixedYaml = yaml.dump(pack, {
113
174
  indent: 2,
@@ -115,112 +176,132 @@ export class QA360Pack {
115
176
  noRefs: true
116
177
  });
117
178
  writeFileSync(targetPath, fixedYaml, 'utf8');
118
- spinner.succeed(chalk.green('✅ Pack corrigé et sauvegardé'));
119
- console.log(chalk.blue('\n🔧 Corrections appliquées:'));
179
+ spinner.succeed(chalk.green('✅ Pack fixed and saved'));
180
+ console.log(chalk.blue('\n🔧 Fixes applied:'));
120
181
  for (const fix of fixes) {
121
182
  console.log(chalk.green(` • ${fix}`));
122
183
  }
123
- // Show remaining issues
124
- if (result.errors.length > 0) {
125
- console.log(chalk.yellow('\n⚠️ Corrections manuelles requises:'));
126
- for (const error of result.errors.slice(0, 3)) { // Show max 3
127
- console.log(chalk.yellow(` • ${error.message}`));
128
- if (error.suggestion) {
129
- console.log(chalk.blue(` 💡 ${error.suggestion}`));
130
- }
131
- }
132
- }
133
184
  return { success: true, exitCode: 0 };
134
185
  }
135
186
  else {
136
- spinner.fail(chalk.red('❌ Corrections automatiques insuffisantes'));
137
- console.log(chalk.red('\n🔍 Erreurs restantes nécessitent une correction manuelle'));
187
+ spinner.fail(chalk.red('❌ Auto-fix insufficient'));
188
+ console.log(chalk.red('\n🔍 Manual fixes required'));
138
189
  return { success: false, exitCode: 1 };
139
190
  }
140
191
  }
141
192
  catch (error) {
142
- spinner.fail(chalk.red('❌ Erreur lors de la correction'));
143
- console.log(chalk.red(`\n🔍 Erreur: ${error instanceof Error ? error.message : 'Unknown error'}`));
193
+ spinner.fail(chalk.red('❌ Lint error'));
194
+ console.log(chalk.red(`\n🔍 Error: ${error instanceof Error ? error.message : 'Unknown error'}`));
144
195
  return { success: false, exitCode: 1 };
145
196
  }
146
197
  }
147
198
  /**
148
- * Migrate legacy pack to v1
199
+ * Migrate pack to v2
149
200
  */
150
- async migrate(packPath) {
151
- const spinner = ora('🔄 Migration du pack vers v1...').start();
201
+ async migrate(packPath, options) {
202
+ const spinner = ora('🔄 Migrating pack to v2...').start();
152
203
  try {
153
204
  const targetPath = packPath || this.packPath;
154
205
  if (!existsSync(targetPath)) {
155
- spinner.fail(chalk.red('❌ Pack non trouvé'));
156
- console.log(chalk.yellow(`📁 Chemin: ${targetPath}`));
206
+ spinner.fail(chalk.red('❌ Pack not found'));
207
+ console.log(chalk.yellow(`📁 Path: ${targetPath}`));
157
208
  return { success: false, exitCode: 1 };
158
209
  }
159
- // Load legacy pack
210
+ // Load pack
160
211
  const packContent = readFileSync(targetPath, 'utf8');
161
- let legacyPack;
212
+ let pack;
162
213
  try {
163
- legacyPack = yaml.load(packContent);
214
+ pack = yaml.load(packContent);
164
215
  }
165
216
  catch (parseError) {
166
- spinner.fail(chalk.red('❌ Erreur de parsing YAML'));
167
- console.log(chalk.red(`\n🔍 Erreur: ${parseError instanceof Error ? parseError.message : 'Parse error'}`));
217
+ spinner.fail(chalk.red('❌ YAML parse error'));
218
+ console.log(chalk.red(`\n🔍 Error: ${parseError instanceof Error ? parseError.message : 'Parse error'}`));
168
219
  return { success: false, exitCode: 1 };
169
220
  }
170
- // Check if already v1
171
- if (legacyPack.version === 1) {
172
- spinner.succeed(chalk.green('✅ Pack déjà en version 1'));
221
+ // Check current version
222
+ const detectedVersion = this.detectPackVersion(pack);
223
+ if (detectedVersion === 2) {
224
+ spinner.succeed(chalk.green('✅ Pack already in v2 format'));
173
225
  return { success: true, exitCode: 0 };
174
226
  }
175
- // Migrate
176
- const migrationResult = this.migrator.migrate(legacyPack);
227
+ if (detectedVersion !== 1) {
228
+ spinner.fail(chalk.yellow('⚠️ Unknown pack format'));
229
+ console.log(chalk.yellow(`Version detected: ${pack.version || 'unknown'}`));
230
+ return { success: false, exitCode: 1 };
231
+ }
232
+ // Migrate v1 to v2
233
+ const migrator = new PackMigrator();
234
+ const migrationResult = migrator.migrate(pack);
177
235
  if (!migrationResult.success) {
178
- spinner.fail(chalk.red('❌ Échec de la migration'));
179
- console.log(chalk.red('\n🔍 Erreurs:'));
236
+ spinner.fail(chalk.red('❌ Migration failed'));
237
+ console.log(chalk.red('\n🔍 Errors:'));
180
238
  for (const warning of migrationResult.warnings) {
181
239
  console.log(chalk.red(` • ${warning}`));
182
240
  }
183
241
  return { success: false, exitCode: 1 };
184
242
  }
185
- // Create backup
186
- const backupPath = `${targetPath}.backup`;
187
- writeFileSync(backupPath, packContent, 'utf8');
188
- // Save migrated pack
189
- const migratedYaml = yaml.dump(migrationResult, {
190
- indent: 2,
191
- lineWidth: 120,
192
- noRefs: true
193
- });
194
- writeFileSync(targetPath, migratedYaml, 'utf8');
195
- spinner.succeed(chalk.green('✅ Migration réussie'));
196
- console.log(chalk.blue(`\n🔄 Migration ${migrationResult.fromVersion} → ${migrationResult.toVersion}`));
197
- console.log(chalk.gray(`📁 Sauvegarde: ${backupPath}`));
198
- console.log(chalk.blue('\n🔧 Modifications appliquées:'));
199
- for (const change of migrationResult.changes.slice(0, 5)) { // Show max 5
200
- const icon = change.type === 'added' ? '➕' :
201
- change.type === 'modified' ? '🔧' :
202
- change.type === 'removed' ? '➖' : '🔄';
203
- console.log(chalk.green(` ${icon} ${change.path}: ${change.reason}`));
204
- }
205
- if (migrationResult.changes.length > 5) {
206
- console.log(chalk.gray(` ... et ${migrationResult.changes.length - 5} autres modifications`));
207
- }
208
- // Show warnings
243
+ const outputPath = options?.output || targetPath;
244
+ if (!options?.dryRun) {
245
+ // Create backup
246
+ const backupPath = `${outputPath}.backup`;
247
+ writeFileSync(backupPath, packContent, 'utf8');
248
+ // Save migrated pack
249
+ const migratedYaml = yaml.dump(migrationResult.migrated, {
250
+ indent: 2,
251
+ lineWidth: 120,
252
+ noRefs: true
253
+ });
254
+ writeFileSync(outputPath, migratedYaml, 'utf8');
255
+ }
256
+ spinner.succeed(chalk.green(' Migration successful'));
257
+ console.log(chalk.blue(`\n🔄 Migration v1 v2`));
258
+ if (options?.dryRun) {
259
+ console.log(chalk.yellow(' ⚠️ Dry run mode - no files written'));
260
+ }
261
+ else {
262
+ console.log(chalk.gray(`📁 Output: ${outputPath}`));
263
+ console.log(chalk.gray(`📁 Backup: ${outputPath}.backup`));
264
+ }
265
+ console.log(chalk.blue('\n🔧 Changes applied:'));
266
+ for (const change of migrationResult.changes.slice(0, 10)) {
267
+ console.log(chalk.green(` • ${change}`));
268
+ }
269
+ if (migrationResult.changes.length > 10) {
270
+ console.log(chalk.gray(` ... and ${migrationResult.changes.length - 10} more changes`));
271
+ }
209
272
  if (migrationResult.warnings.length > 0) {
210
- console.log(chalk.yellow('\n⚠️ Avertissements:'));
273
+ console.log(chalk.yellow('\n⚠️ Warnings:'));
211
274
  for (const warning of migrationResult.warnings) {
212
275
  console.log(chalk.yellow(` • ${warning}`));
213
276
  }
214
277
  }
215
- console.log(chalk.blue('\n🔍 Validation recommandée: qa360 pack validate'));
278
+ console.log(chalk.blue('\n💡 Next: qa360 pack validate'));
216
279
  return { success: true, exitCode: 0 };
217
280
  }
218
281
  catch (error) {
219
- spinner.fail(chalk.red('❌ Erreur de migration'));
220
- console.log(chalk.red(`\n🔍 Erreur: ${error instanceof Error ? error.message : 'Unknown error'}`));
282
+ spinner.fail(chalk.red('❌ Migration error'));
283
+ console.log(chalk.red(`\n🔍 Error: ${error instanceof Error ? error.message : 'Unknown error'}`));
221
284
  return { success: false, exitCode: 1 };
222
285
  }
223
286
  }
287
+ /**
288
+ * Detect pack version
289
+ */
290
+ detectPackVersion(pack) {
291
+ if (pack.version === 2)
292
+ return 2;
293
+ if (pack.version === 1)
294
+ return 1;
295
+ // Heuristic detection
296
+ if (pack.gates && Array.isArray(pack.gates))
297
+ return 1;
298
+ if (pack.gates && typeof pack.gates === 'object' && !Array.isArray(pack.gates)) {
299
+ const firstGate = Object.values(pack.gates)[0];
300
+ if (firstGate?.test_files || firstGate?.adapter)
301
+ return 2;
302
+ }
303
+ return 'unknown';
304
+ }
224
305
  /**
225
306
  * Apply automatic fixes to pack
226
307
  */
@@ -228,51 +309,42 @@ export class QA360Pack {
228
309
  const fixes = [];
229
310
  // Add version if missing
230
311
  if (!pack.version) {
231
- pack.version = 1;
232
- fixes.push('Ajout version: 1');
312
+ pack.version = 2; // Default to v2 now
313
+ fixes.push('Added version: 2');
233
314
  }
234
315
  // Add default execution if missing
235
- if (!pack.execution) {
316
+ if (!pack.execution && pack.version === 1) {
236
317
  pack.execution = {
237
318
  retry_on: ['ECONNRESET', '502'],
238
319
  max_retries: 1,
239
320
  fail_on_readiness: true,
240
321
  timeout: 30000
241
322
  };
242
- fixes.push('Ajout configuration execution par défaut');
323
+ fixes.push('Added default execution configuration');
243
324
  }
244
- // Add default observability if missing
245
- if (!pack.observability) {
246
- pack.observability = {
247
- metrics: true,
248
- trace: 'basic'
325
+ // Add default execution for v2
326
+ if (!pack.execution && pack.version === 2) {
327
+ pack.execution = {
328
+ on_failure: 'stop',
329
+ default_timeout: 30000,
330
+ default_retries: 1
249
331
  };
250
- fixes.push('Ajout configuration observability par défaut');
332
+ fixes.push('Added default execution configuration (v2)');
251
333
  }
252
334
  // Fix name pattern if needed
253
335
  if (pack.name && !/^[a-zA-Z0-9_-]+$/.test(pack.name)) {
254
336
  const oldName = pack.name;
255
337
  pack.name = pack.name.replace(/[^a-zA-Z0-9_-]/g, '_');
256
- fixes.push(`Correction nom: "${oldName}" → "${pack.name}"`);
257
- }
258
- // Add default gates if missing
259
- if (!pack.gates || pack.gates.length === 0) {
260
- pack.gates = ['api_smoke'];
261
- fixes.push('Ajout gate par défaut: api_smoke');
262
- }
263
- // Add security.secrets_leak if security section exists
264
- if (pack.security && pack.security.secrets_leak === undefined) {
265
- pack.security.secrets_leak = 0;
266
- fixes.push('Ajout security.secrets_leak: 0');
338
+ fixes.push(`Fixed name: "${oldName}" → "${pack.name}"`);
267
339
  }
268
340
  return fixes;
269
341
  }
270
342
  /**
271
- * Show pack summary
343
+ * Show v1 pack summary
272
344
  */
273
345
  showPackSummary(pack) {
274
- console.log(chalk.blue('\n📋 Résumé du pack:'));
275
- console.log(chalk.gray(` 📦 Nom: ${pack.name}`));
346
+ console.log(chalk.blue('\n📋 Pack summary:'));
347
+ console.log(chalk.gray(` 📦 Name: ${pack.name}`));
276
348
  console.log(chalk.gray(` 🎯 Gates: ${pack.gates.join(', ')}`));
277
349
  if (pack.targets?.api) {
278
350
  console.log(chalk.gray(` 🌐 API: ${pack.targets.api.baseUrl}`));
@@ -292,14 +364,41 @@ export class QA360Pack {
292
364
  }
293
365
  if (pack.environment) {
294
366
  const envCount = Object.keys(pack.environment).length;
295
- console.log(chalk.gray(` 🔧 Variables: ${envCount} définies`));
367
+ console.log(chalk.gray(` 🔧 Variables: ${envCount} defined`));
368
+ }
369
+ }
370
+ /**
371
+ * Show v2 pack summary
372
+ */
373
+ showPackV2Summary(pack, validation) {
374
+ console.log(chalk.blue('\n📋 Pack v2 summary:'));
375
+ console.log(chalk.gray(` 📦 Name: ${pack.name}`));
376
+ console.log(chalk.gray(` 🎯 Gates: ${Object.keys(pack.gates).join(', ')}`));
377
+ if (validation?.info) {
378
+ const { info } = validation;
379
+ if (info.totalTests !== undefined) {
380
+ console.log(chalk.gray(` 🧪 Tests: ${info.totalTests} found`));
381
+ }
382
+ if (info.frameworks && info.frameworks.length > 0) {
383
+ console.log(chalk.gray(` 🔧 Frameworks: ${info.frameworks.join(', ')}`));
384
+ }
385
+ }
386
+ if (pack.auth?.profiles) {
387
+ const profileNames = Object.keys(pack.auth.profiles);
388
+ if (profileNames.length > 0) {
389
+ console.log(chalk.gray(` 🔐 Auth profiles: ${profileNames.join(', ')}`));
390
+ }
391
+ }
392
+ if (pack.variables) {
393
+ const varCount = Object.keys(pack.variables).length;
394
+ console.log(chalk.gray(` 🔧 Variables: ${varCount} defined`));
296
395
  }
297
396
  }
298
397
  }
299
398
  // Command handlers
300
- export async function packValidateCommand(packPath) {
399
+ export async function packValidateCommand(packPath, options) {
301
400
  const pack = new QA360Pack();
302
- const result = await pack.validate(packPath);
401
+ const result = await pack.validate(packPath, options);
303
402
  process.exit(result.exitCode);
304
403
  }
305
404
  export async function packLintCommand(packPath) {
@@ -307,8 +406,8 @@ export async function packLintCommand(packPath) {
307
406
  const result = await pack.lint(packPath);
308
407
  process.exit(result.exitCode);
309
408
  }
310
- export async function packMigrateCommand(packPath) {
409
+ export async function packMigrateCommand(packPath, options) {
311
410
  const pack = new QA360Pack();
312
- const result = await pack.migrate(packPath);
411
+ const result = await pack.migrate(packPath, options);
313
412
  process.exit(result.exitCode);
314
413
  }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * QA360 Regression Command
3
+ *
4
+ * CLI command for regression detection and analysis.
5
+ */
6
+ import { Command } from 'commander';
7
+ export declare const regressionCommand: Command;
8
+ export default regressionCommand;