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
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![Version](https://img.shields.io/npm/v/qa360.svg)](https://www.npmjs.com/package/qa360)
6
6
  [![License](https://img.shields.io/npm/l/qa360.svg)](https://github.com/xyqotech/qa360/blob/main/LICENSE)
7
7
  [![Tests](https://img.shields.io/badge/tests-282%20passing-success)](https://github.com/xyqotech/qa360)
8
- [![Coverage](https://img.shields.io/badge/coverage-28.5%25-yellow)](https://github.com/xyqotech/qa360)
8
+ [![Coverage](https://img.shields.io/badge/coverage-55%25-brightgreen)](https://github.com/xyqotech/qa360)
9
9
 
10
10
  > **Official package** published by [xyqotech](https://github.com/xyqotech) with npm provenance signatures
11
11
 
@@ -0,0 +1,41 @@
1
+ /**
2
+ * QA360 AI Command
3
+ *
4
+ * Manage AI provider integration for test generation.
5
+ * Supports DeepSeek, Ollama, OpenAI, Anthropic with automatic selection.
6
+ *
7
+ * DeepSeek is recommended for best value (GPT-4 level at 95% lower cost).
8
+ */
9
+ import { Command } from 'commander';
10
+ /**
11
+ * List all available AI providers
12
+ */
13
+ export declare function aiListCommand(): Promise<void>;
14
+ /**
15
+ * Use a specific AI provider
16
+ */
17
+ export declare function aiUseCommand(provider: string, options?: {
18
+ verify?: boolean;
19
+ }): Promise<void>;
20
+ /**
21
+ * Benchmark all available providers
22
+ */
23
+ export declare function aiBenchmarkCommand(options?: {
24
+ prompt?: string;
25
+ }): Promise<void>;
26
+ /**
27
+ * Generate tests using the best available provider
28
+ */
29
+ export declare function aiGenerateCommand(prompt: string, options?: {
30
+ provider?: string;
31
+ type?: string;
32
+ json?: boolean;
33
+ }): Promise<void>;
34
+ /**
35
+ * Show AI configuration
36
+ */
37
+ export declare function aiConfigCommand(): Promise<void>;
38
+ /**
39
+ * Create AI commands
40
+ */
41
+ export declare function createAICommands(): Command;
@@ -0,0 +1,499 @@
1
+ /**
2
+ * QA360 AI Command
3
+ *
4
+ * Manage AI provider integration for test generation.
5
+ * Supports DeepSeek, Ollama, OpenAI, Anthropic with automatic selection.
6
+ *
7
+ * DeepSeek is recommended for best value (GPT-4 level at 95% lower cost).
8
+ */
9
+ import { Command } from 'commander';
10
+ import chalk from 'chalk';
11
+ import ora from 'ora';
12
+ import Table from 'cli-table3';
13
+ import { createBest, getProviderInfo, getProviderStatus, createLLMProvider, DeepSeekProvider, DeepSeekError, OllamaProvider, OllamaError, OpenAIProvider, OpenAIError, AnthropicProvider, AnthropicError } from '../core/index.js';
14
+ /**
15
+ * List all available AI providers
16
+ */
17
+ export async function aiListCommand() {
18
+ const spinner = ora('Checking AI providers...').start();
19
+ try {
20
+ const providers = await getProviderInfo();
21
+ spinner.succeed('AI Providers Status:\n');
22
+ // Create table for display
23
+ const table = new Table({
24
+ head: [chalk.bold('Provider'), chalk.bold('Status'), chalk.bold('Cost'), chalk.bold('Description')],
25
+ colWidths: [15, 10, 10, 40],
26
+ style: {
27
+ head: ['cyan', 'bold']
28
+ }
29
+ });
30
+ for (const provider of providers) {
31
+ const status = provider.available
32
+ ? chalk.green('✓ Available')
33
+ : chalk.gray('✗ Not set up');
34
+ const costLevel = {
35
+ free: chalk.green('FREE'),
36
+ low: chalk.yellow('LOW'),
37
+ medium: chalk.yellow('MED'),
38
+ high: chalk.red('HIGH')
39
+ }[provider.costLevel];
40
+ table.push([
41
+ chalk.bold(provider.name),
42
+ status,
43
+ costLevel,
44
+ provider.description
45
+ ]);
46
+ }
47
+ console.log(table.toString());
48
+ // Show setup instructions for unavailable providers
49
+ const unavailable = providers.filter(p => !p.available && p.type !== 'mock');
50
+ if (unavailable.length > 0) {
51
+ console.log(chalk.yellow('\n[INFO] Setup available providers:\n'));
52
+ if (!providers.find(p => p.type === 'deepseek')?.available) {
53
+ console.log(chalk.cyan('DeepSeek (Recommended - Best Value):'));
54
+ console.log(chalk.gray(' 1. Get API key: https://platform.deepseek.com/api_keys'));
55
+ console.log(chalk.gray(' 2. Run: qa360 secrets add DEEPSEEK_API_KEY\n'));
56
+ }
57
+ if (!providers.find(p => p.type === 'ollama')?.available) {
58
+ console.log(chalk.cyan('Ollama (Free - Local):'));
59
+ console.log(chalk.gray(' 1. Install: brew install ollama'));
60
+ console.log(chalk.gray(' 2. Run: ollama serve'));
61
+ console.log(chalk.gray(' 3. Pull model: ollama pull deepseek-coder\n'));
62
+ }
63
+ if (!providers.find(p => p.type === 'openai')?.available) {
64
+ console.log(chalk.cyan('OpenAI:'));
65
+ console.log(chalk.gray(' 1. Get API key: https://platform.openai.com/api-keys'));
66
+ console.log(chalk.gray(' 2. Run: qa360 secrets add OPENAI_API_KEY\n'));
67
+ }
68
+ if (!providers.find(p => p.type === 'anthropic')?.available) {
69
+ console.log(chalk.cyan('Anthropic:'));
70
+ console.log(chalk.gray(' 1. Get API key: https://console.anthropic.com/'));
71
+ console.log(chalk.gray(' 2. Run: qa360 secrets add ANTHROPIC_API_KEY\n'));
72
+ }
73
+ }
74
+ // Show best provider
75
+ const bestProvider = providers.find(p => p.available && p.type !== 'mock');
76
+ if (bestProvider) {
77
+ console.log(chalk.green(`\n[STAR] Best available: ${chalk.bold(bestProvider.name)}`));
78
+ console.log(chalk.gray(` ${bestProvider.description}`));
79
+ }
80
+ else {
81
+ console.log(chalk.red('\n[!] No AI providers available. Set up one above to use AI features.\n'));
82
+ }
83
+ }
84
+ catch (error) {
85
+ spinner.fail('Failed to check providers');
86
+ console.error(chalk.red('Error:'), error.message);
87
+ }
88
+ }
89
+ /**
90
+ * Use a specific AI provider
91
+ */
92
+ export async function aiUseCommand(provider, options = {}) {
93
+ const spinner = ora(`Testing ${provider}...`).start();
94
+ try {
95
+ let available = false;
96
+ let error = '';
97
+ switch (provider.toLowerCase()) {
98
+ case 'deepseek': {
99
+ const p = new DeepSeekProvider();
100
+ available = await p.isAvailable();
101
+ if (!available)
102
+ error = 'DEEPSEEK_API_KEY not set or invalid';
103
+ break;
104
+ }
105
+ case 'ollama': {
106
+ const p = new OllamaProvider();
107
+ available = await p.isAvailable();
108
+ if (!available)
109
+ error = 'Ollama not running or not installed';
110
+ break;
111
+ }
112
+ case 'openai': {
113
+ const p = new OpenAIProvider();
114
+ available = await p.isAvailable();
115
+ if (!available)
116
+ error = 'OPENAI_API_KEY not set or invalid';
117
+ break;
118
+ }
119
+ case 'anthropic': {
120
+ const p = new AnthropicProvider();
121
+ available = await p.isAvailable();
122
+ if (!available)
123
+ error = 'ANTHROPIC_API_KEY not set or invalid';
124
+ break;
125
+ }
126
+ default:
127
+ spinner.fail(`Unknown provider: ${provider}`);
128
+ console.log(chalk.yellow('\nAvailable providers: deepseek, ollama, openai, anthropic'));
129
+ return;
130
+ }
131
+ if (available) {
132
+ spinner.succeed(`${provider} is available!`);
133
+ if (options.verify) {
134
+ await runVerificationTest(provider);
135
+ }
136
+ else {
137
+ console.log(chalk.green(`\n[OK] ${provider} is ready to use!\n`));
138
+ console.log(chalk.gray('Use --verify to run a test generation.\n'));
139
+ }
140
+ }
141
+ else {
142
+ spinner.fail(`${provider} is not available`);
143
+ console.log(chalk.red(`\n[X] ${error}\n`));
144
+ showSetupInstructions(provider);
145
+ }
146
+ }
147
+ catch (error) {
148
+ spinner.fail('Provider check failed');
149
+ console.error(chalk.red('Error:'), error.message);
150
+ }
151
+ }
152
+ /**
153
+ * Benchmark all available providers
154
+ */
155
+ export async function aiBenchmarkCommand(options = {}) {
156
+ const spinner = ora('Checking providers...').start();
157
+ const testPrompt = options.prompt || 'Generate a simple API smoke test for https://api.example.com/health';
158
+ try {
159
+ const status = await getProviderStatus();
160
+ spinner.info('Starting benchmark...\n');
161
+ const results = [];
162
+ // Benchmark each provider
163
+ const providers = [
164
+ { key: 'deepseek', name: 'DeepSeek', factory: () => new DeepSeekProvider() },
165
+ { key: 'ollama', name: 'Ollama', factory: () => new OllamaProvider() },
166
+ { key: 'openai', name: 'OpenAI', factory: () => new OpenAIProvider() },
167
+ { key: 'anthropic', name: 'Anthropic', factory: () => new AnthropicProvider() },
168
+ ];
169
+ console.log(chalk.bold('Benchmark Results:\n'));
170
+ console.log(chalk.gray(`Prompt: "${testPrompt}"\n`));
171
+ const table = new Table({
172
+ head: [chalk.bold('Provider'), chalk.bold('Status'), chalk.bold('Time'), chalk.bold('Tokens')],
173
+ colWidths: [15, 15, 12, 15],
174
+ style: { head: ['cyan', 'bold'] }
175
+ });
176
+ for (const { key, name, factory } of providers) {
177
+ if (!status[key]) {
178
+ table.push([name, chalk.gray('Not available'), '-', '-']);
179
+ continue;
180
+ }
181
+ const pSpinner = ora(`Testing ${name}...`).start();
182
+ try {
183
+ const provider = factory();
184
+ const start = Date.now();
185
+ const response = await provider.generate({
186
+ prompt: testPrompt,
187
+ maxTokens: 500
188
+ });
189
+ const time = Date.now() - start;
190
+ pSpinner.succeed();
191
+ table.push([
192
+ name,
193
+ chalk.green('✓ Success'),
194
+ chalk.cyan(`${time}ms`),
195
+ chalk.gray(`${response.usage.totalTokens}`)
196
+ ]);
197
+ }
198
+ catch (error) {
199
+ pSpinner.fail();
200
+ table.push([
201
+ name,
202
+ chalk.red('✗ Failed'),
203
+ '-',
204
+ `-`
205
+ ]);
206
+ }
207
+ }
208
+ console.log(table.toString());
209
+ // Show cost comparison
210
+ console.log(chalk.yellow('\n[$] Cost Comparison (per 1M tokens):\n'));
211
+ console.log(chalk.gray(' DeepSeek: $0.14 input, $0.28 output (~99.5% savings vs GPT-4)'));
212
+ console.log(chalk.gray(' Ollama: FREE (local, privacy)'));
213
+ console.log(chalk.gray(' OpenAI: ~$30 input, ~$60 output'));
214
+ console.log(chalk.gray(' Anthropic: ~$30 input, ~$60 output\n'));
215
+ }
216
+ catch (error) {
217
+ spinner.fail('Benchmark failed');
218
+ console.error(chalk.red('Error:'), error.message);
219
+ }
220
+ }
221
+ /**
222
+ * Generate tests using the best available provider
223
+ */
224
+ export async function aiGenerateCommand(prompt, options = {}) {
225
+ let spinner;
226
+ try {
227
+ const systemPrompt = buildSystemPrompt(options.type || 'api');
228
+ const provider = options.provider
229
+ ? await createLLMProvider({ preferred: options.provider })
230
+ : await createBest();
231
+ const providerType = provider.getProviderType();
232
+ spinner = ora(`Using ${chalk.cyan(providerType)}...`).start();
233
+ const response = await provider.generate({
234
+ prompt,
235
+ systemPrompt,
236
+ maxTokens: 4096,
237
+ temperature: 0.7,
238
+ });
239
+ spinner.succeed(`Generated using ${chalk.cyan(providerType)}!\n`);
240
+ if (options.json) {
241
+ console.log(JSON.stringify({
242
+ provider: providerType,
243
+ content: response.content,
244
+ usage: response.usage,
245
+ model: response.model
246
+ }, null, 2));
247
+ }
248
+ else {
249
+ console.log(chalk.bold('Generated Content:\n'));
250
+ console.log(chalk.gray('─'.repeat(70)));
251
+ console.log(response.content);
252
+ console.log(chalk.gray('─'.repeat(70)));
253
+ console.log(chalk.gray(`\n[INFO] Tokens: ${response.usage.totalTokens} (${response.usage.promptTokens} in + ${response.usage.completionTokens} out)`));
254
+ // Show cost estimate for DeepSeek
255
+ if (providerType === 'deepseek') {
256
+ const deepSeek = new DeepSeekProvider();
257
+ const cost = deepSeek.estimateCost(response.usage.promptTokens, response.usage.completionTokens);
258
+ console.log(chalk.gray(`[$] Cost: $${cost.toFixed(6)} (vs ~$${(cost * 200).toFixed(4)} with GPT-4)`));
259
+ }
260
+ }
261
+ }
262
+ catch (error) {
263
+ spinner?.fail('Generation failed');
264
+ if (error instanceof DeepSeekError) {
265
+ console.error(chalk.red('\nDeepSeek Error:'), error.message);
266
+ if (error.details) {
267
+ console.error(chalk.gray(JSON.stringify(error.details, null, 2)));
268
+ }
269
+ }
270
+ else if (error instanceof OllamaError) {
271
+ console.error(chalk.red('\nOllama Error:'), error.message);
272
+ }
273
+ else if (error instanceof OpenAIError) {
274
+ console.error(chalk.red('\nOpenAI Error:'), error.message);
275
+ }
276
+ else if (error instanceof AnthropicError) {
277
+ console.error(chalk.red('\nAnthropic Error:'), error.message);
278
+ }
279
+ else {
280
+ console.error(chalk.red('Error:'), error.message);
281
+ }
282
+ }
283
+ }
284
+ /**
285
+ * Show AI configuration
286
+ */
287
+ export async function aiConfigCommand() {
288
+ console.log(chalk.cyan('QA360 AI Configuration:\n'));
289
+ const status = await getProviderStatus();
290
+ const configs = [
291
+ { provider: 'DeepSeek', envVar: 'DEEPSEEK_API_KEY', set: !!process.env.DEEPSEEK_API_KEY },
292
+ { provider: 'Ollama', envVar: 'OLLAMA_BASE_URL', set: !!process.env.OLLAMA_BASE_URL },
293
+ { provider: 'OpenAI', envVar: 'OPENAI_API_KEY', set: !!process.env.OPENAI_API_KEY },
294
+ { provider: 'Anthropic', envVar: 'ANTHROPIC_API_KEY', set: !!process.env.ANTHROPIC_API_KEY },
295
+ ];
296
+ const table = new Table({
297
+ head: [chalk.bold('Provider'), chalk.bold('Environment Variable'), chalk.bold('Status')],
298
+ colWidths: [15, 30, 15],
299
+ style: { head: ['cyan', 'bold'] }
300
+ });
301
+ for (const config of configs) {
302
+ const status = config.set
303
+ ? chalk.green('✓ Set')
304
+ : chalk.gray('✗ Not set');
305
+ table.push([config.provider, chalk.gray(config.envVar), status]);
306
+ }
307
+ console.log(table.toString());
308
+ console.log(chalk.yellow('\n[INFO] Set API keys using:'));
309
+ console.log(chalk.gray(' export VARIABLE=value'));
310
+ console.log(chalk.gray(' # OR'));
311
+ console.log(chalk.gray(' qa360 secrets add VARIABLE\n'));
312
+ }
313
+ /**
314
+ * Run verification test for a provider
315
+ */
316
+ async function runVerificationTest(provider) {
317
+ const spinner = ora(`Running verification test with ${provider}...`).start();
318
+ try {
319
+ let testProvider;
320
+ switch (provider.toLowerCase()) {
321
+ case 'deepseek':
322
+ testProvider = new DeepSeekProvider();
323
+ break;
324
+ case 'ollama':
325
+ testProvider = new OllamaProvider();
326
+ break;
327
+ case 'openai':
328
+ testProvider = new OpenAIProvider();
329
+ break;
330
+ case 'anthropic':
331
+ testProvider = new AnthropicProvider();
332
+ break;
333
+ default:
334
+ throw new Error(`Unknown provider: ${provider}`);
335
+ }
336
+ const response = await testProvider.generate({
337
+ prompt: 'Say "OK" if you can read this.',
338
+ maxTokens: 10
339
+ });
340
+ spinner.succeed('Verification test passed!\n');
341
+ console.log(chalk.green('[OK] Provider is working correctly!'));
342
+ console.log(chalk.gray(`Response: ${response.content.trim()}\n`));
343
+ }
344
+ catch (error) {
345
+ spinner.fail('Verification test failed');
346
+ console.error(chalk.red('Error:'), error.message);
347
+ }
348
+ }
349
+ /**
350
+ * Show setup instructions for a provider
351
+ */
352
+ function showSetupInstructions(provider) {
353
+ switch (provider.toLowerCase()) {
354
+ case 'deepseek':
355
+ console.log(chalk.yellow('[NOTE] To set up DeepSeek:\n'));
356
+ console.log(chalk.gray(' 1. Get API key: https://platform.deepseek.com/api_keys'));
357
+ console.log(chalk.gray(' 2. Run: export DEEPSEEK_API_KEY=your_key'));
358
+ console.log(chalk.gray(' 3. Or: qa360 secrets add DEEPSEEK_API_KEY\n'));
359
+ break;
360
+ case 'ollama':
361
+ console.log(chalk.yellow('[NOTE] To set up Ollama:\n'));
362
+ console.log(chalk.gray(' 1. Install: brew install ollama'));
363
+ console.log(chalk.gray(' 2. Start: ollama serve'));
364
+ console.log(chalk.gray(' 3. Pull: ollama pull deepseek-coder\n'));
365
+ break;
366
+ case 'openai':
367
+ console.log(chalk.yellow('[NOTE] To set up OpenAI:\n'));
368
+ console.log(chalk.gray(' 1. Get API key: https://platform.openai.com/api-keys'));
369
+ console.log(chalk.gray(' 2. Run: export OPENAI_API_KEY=your_key'));
370
+ console.log(chalk.gray(' 3. Or: qa360 secrets add OPENAI_API_KEY\n'));
371
+ break;
372
+ case 'anthropic':
373
+ console.log(chalk.yellow('[NOTE] To set up Anthropic:\n'));
374
+ console.log(chalk.gray(' 1. Get API key: https://console.anthropic.com/'));
375
+ console.log(chalk.gray(' 2. Run: export ANTHROPIC_API_KEY=your_key'));
376
+ console.log(chalk.gray(' 3. Or: qa360 secrets add ANTHROPIC_API_KEY\n'));
377
+ break;
378
+ }
379
+ }
380
+ /**
381
+ * Build system prompt for test generation
382
+ */
383
+ function buildSystemPrompt(type) {
384
+ const basePrompt = `You are QA360, an expert QA test generator.
385
+ Generate production-ready, well-structured test code following best practices.
386
+
387
+ Rules:
388
+ - Generate COMPLETE, working code (no "...", no comments saying "to be implemented")
389
+ - Include proper imports and setup
390
+ - Handle errors gracefully
391
+ - Use modern async/await patterns
392
+ - Add helpful comments for complex logic
393
+ - Return ONLY the code, no explanations before or after`;
394
+ const typePrompts = {
395
+ api: `${basePrompt}
396
+
397
+ For API tests using Playwright:
398
+ - Use @playwright/test
399
+ - Use test() and expect() from Playwright
400
+ - Test REST API endpoints (GET, POST, PUT, DELETE)
401
+ - Include proper assertions for status codes, headers, and response body
402
+ - Handle both success and error cases`,
403
+ ui: `${basePrompt}
404
+
405
+ For UI/E2E tests using Playwright:
406
+ - Use @playwright/test
407
+ - Use page.goto(), page.click(), page.fill(), etc.
408
+ - Include explicit waits where needed (waitForLoadState, waitForSelector)
409
+ - Add assertions for page content
410
+ - Use data-testid attributes when possible`,
411
+ perf: `${basePrompt}
412
+
413
+ For performance tests using K6:
414
+ - Use k6/http module
415
+ - Define thresholds for response times and error rates
416
+ - Include realistic user scenarios
417
+ - Use options for VUs and duration`,
418
+ a11y: `${basePrompt}
419
+
420
+ For accessibility tests:
421
+ - Use @axe-core/playwright
422
+ - Test WCAG compliance
423
+ - Check for common a11y issues`,
424
+ };
425
+ return typePrompts[type] || typePrompts.api;
426
+ }
427
+ /**
428
+ * Create AI commands
429
+ */
430
+ export function createAICommands() {
431
+ const aiCmd = new Command('ai')
432
+ .description('Manage AI providers (DeepSeek, Ollama, OpenAI, Anthropic)');
433
+ aiCmd
434
+ .command('list')
435
+ .description('List all available AI providers and their status')
436
+ .action(async () => {
437
+ try {
438
+ await aiListCommand();
439
+ }
440
+ catch (error) {
441
+ console.error(chalk.red('Error listing providers:'), error.message);
442
+ process.exit(1);
443
+ }
444
+ });
445
+ aiCmd
446
+ .command('use <provider>')
447
+ .description('Test and use a specific AI provider')
448
+ .option('--verify', 'Run a verification test')
449
+ .action(async (provider, options) => {
450
+ try {
451
+ await aiUseCommand(provider, options);
452
+ }
453
+ catch (error) {
454
+ console.error(chalk.red('Error testing provider:'), error.message);
455
+ process.exit(1);
456
+ }
457
+ });
458
+ aiCmd
459
+ .command('benchmark')
460
+ .description('Benchmark all available AI providers')
461
+ .option('-p, --prompt <prompt>', 'Custom prompt for benchmark')
462
+ .action(async (options) => {
463
+ try {
464
+ await aiBenchmarkCommand(options);
465
+ }
466
+ catch (error) {
467
+ console.error(chalk.red('Error running benchmark:'), error.message);
468
+ process.exit(1);
469
+ }
470
+ });
471
+ aiCmd
472
+ .command('generate <prompt>')
473
+ .description('Generate tests using the best available AI provider')
474
+ .option('-P, --provider <provider>', 'Use specific provider (deepseek, ollama, openai, anthropic)')
475
+ .option('-t, --type <type>', 'Test type (api, ui, perf, a11y)', 'api')
476
+ .option('--json', 'Output as JSON')
477
+ .action(async (prompt, options) => {
478
+ try {
479
+ await aiGenerateCommand(prompt, options);
480
+ }
481
+ catch (error) {
482
+ console.error(chalk.red('Error generating:'), error.message);
483
+ process.exit(1);
484
+ }
485
+ });
486
+ aiCmd
487
+ .command('config')
488
+ .description('Show AI configuration and environment variables')
489
+ .action(async () => {
490
+ try {
491
+ await aiConfigCommand();
492
+ }
493
+ catch (error) {
494
+ console.error(chalk.red('Error showing config:'), error.message);
495
+ process.exit(1);
496
+ }
497
+ });
498
+ return aiCmd;
499
+ }
@@ -9,7 +9,7 @@ import { dump } from 'js-yaml';
9
9
  export class QA360Ask {
10
10
  qa360Dir = join(process.cwd(), '.qa360');
11
11
  async generatePack(query) {
12
- console.log(chalk.blue('🤖 QA360 Ask - Génération de pack intelligent\n'));
12
+ console.log(chalk.blue('\n[QA360 Ask] Generating test pack from your description...\n'));
13
13
  // Si pas de query, mode interactif
14
14
  if (!query) {
15
15
  return await this.interactiveMode();
@@ -36,12 +36,12 @@ export class QA360Ask {
36
36
  name: 'type',
37
37
  message: 'Type de tests principal:',
38
38
  choices: [
39
- { name: '🌐 API REST/GraphQL', value: 'api' },
40
- { name: '🖥️ Interface Web (UI)', value: 'web' },
41
- { name: ' Performance', value: 'performance' },
42
- { name: '🔒 Sécurité', value: 'security' },
43
- { name: ' Accessibilité', value: 'accessibility' },
44
- { name: '🔄 Complet (Multi-adapters)', value: 'complete' }
39
+ { name: '[WEB] API REST/GraphQL', value: 'api' },
40
+ { name: '[UI] Interface Web (UI)', value: 'web' },
41
+ { name: '[PERF] Performance', value: 'performance' },
42
+ { name: '[SEC] Securite', value: 'security' },
43
+ { name: '[A11Y] Accessibilite', value: 'accessibility' },
44
+ { name: '[ALL] Complet (Multi-adapters)', value: 'complete' }
45
45
  ]
46
46
  },
47
47
  {
@@ -411,11 +411,11 @@ export class QA360Ask {
411
411
  return packPath;
412
412
  }
413
413
  displayPack(pack) {
414
- console.log(chalk.bold('\n📦 Pack généré:\n'));
414
+ console.log(chalk.bold('\n[PACK] Pack genere:\n'));
415
415
  console.log(chalk.blue(`Nom: ${pack.name}`));
416
416
  console.log(chalk.gray(`Description: ${pack.description}`));
417
417
  console.log(chalk.green(`Adapters: ${pack.adapters.join(', ')}`));
418
- console.log(chalk.yellow(`Tests: ${pack.tests.length} test(s) configuré(s)`));
418
+ console.log(chalk.yellow(`Tests: ${pack.tests.length} test(s) configure(s)`));
419
419
  if (pack.environment?.TARGET_URL) {
420
420
  console.log(chalk.cyan(`Cible: ${pack.environment.TARGET_URL}`));
421
421
  }
@@ -427,11 +427,11 @@ export async function askCommand(query) {
427
427
  const pack = await ask.generatePack(query);
428
428
  const packPath = await ask.savePack(pack);
429
429
  ask.displayPack(pack);
430
- console.log(chalk.bold(`\n Pack sauvegardé: ${packPath}`));
431
- console.log(chalk.blue('💡 Prochaine étape: qa360 run'));
430
+ console.log(chalk.bold(`\n[OK] Pack sauvegarde: ${packPath}`));
431
+ console.log(chalk.blue('[INFO] Prochaine etape: qa360 run'));
432
432
  }
433
433
  catch (error) {
434
- console.error(chalk.red(' Erreur lors de la génération du pack:'), error);
434
+ console.error(chalk.red('[X] Erreur lors de la generation du pack:'), error);
435
435
  process.exit(1);
436
436
  }
437
437
  }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * QA360 Coverage Command
3
+ *
4
+ * CLI command for coverage analytics and reporting.
5
+ */
6
+ import { Command } from 'commander';
7
+ export declare const coverageCommand: Command;
8
+ export default coverageCommand;