@democratize-quality/mcp-server 1.2.0 → 1.2.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 (56) hide show
  1. package/cli.js +248 -0
  2. package/package.json +7 -5
  3. package/src/chatmodes//360/237/214/220 api-generator.chatmode.md" +409 -0
  4. package/src/chatmodes//360/237/214/220 api-healer.chatmode.md" +494 -0
  5. package/src/chatmodes//360/237/214/220 api-planner.chatmode.md" +954 -0
  6. package/src/config/environments/api-only.js +72 -0
  7. package/src/config/environments/development.js +73 -0
  8. package/src/config/environments/production.js +88 -0
  9. package/src/config/index.js +360 -0
  10. package/src/config/server.js +60 -0
  11. package/src/config/tools/api.js +86 -0
  12. package/src/config/tools/browser.js +109 -0
  13. package/src/config/tools/default.js +51 -0
  14. package/src/docs/Agent_README.md +310 -0
  15. package/src/docs/QUICK_REFERENCE.md +111 -0
  16. package/src/server.ts +234 -0
  17. package/src/services/browserService.js +344 -0
  18. package/src/skills/api-planning/SKILL.md +224 -0
  19. package/src/skills/test-execution/SKILL.md +777 -0
  20. package/src/skills/test-generation/SKILL.md +309 -0
  21. package/src/skills/test-healing/SKILL.md +405 -0
  22. package/src/tools/api/api-generator.js +1884 -0
  23. package/src/tools/api/api-healer.js +636 -0
  24. package/src/tools/api/api-planner.js +2617 -0
  25. package/src/tools/api/api-project-setup.js +332 -0
  26. package/src/tools/api/api-request.js +660 -0
  27. package/src/tools/api/api-session-report.js +1297 -0
  28. package/src/tools/api/api-session-status.js +414 -0
  29. package/src/tools/api/prompts/README.md +293 -0
  30. package/src/tools/api/prompts/generation-prompts.js +722 -0
  31. package/src/tools/api/prompts/healing-prompts.js +214 -0
  32. package/src/tools/api/prompts/index.js +44 -0
  33. package/src/tools/api/prompts/orchestrator.js +353 -0
  34. package/src/tools/api/prompts/validation-rules.js +358 -0
  35. package/src/tools/base/ToolBase.js +249 -0
  36. package/src/tools/base/ToolRegistry.js +288 -0
  37. package/src/tools/browser/advanced/browser-console.js +403 -0
  38. package/src/tools/browser/advanced/browser-dialog.js +338 -0
  39. package/src/tools/browser/advanced/browser-evaluate.js +356 -0
  40. package/src/tools/browser/advanced/browser-file.js +499 -0
  41. package/src/tools/browser/advanced/browser-keyboard.js +362 -0
  42. package/src/tools/browser/advanced/browser-mouse.js +351 -0
  43. package/src/tools/browser/advanced/browser-network.js +440 -0
  44. package/src/tools/browser/advanced/browser-pdf.js +426 -0
  45. package/src/tools/browser/advanced/browser-tabs.js +516 -0
  46. package/src/tools/browser/advanced/browser-wait.js +397 -0
  47. package/src/tools/browser/click.js +187 -0
  48. package/src/tools/browser/close.js +79 -0
  49. package/src/tools/browser/dom.js +89 -0
  50. package/src/tools/browser/launch.js +86 -0
  51. package/src/tools/browser/navigate.js +289 -0
  52. package/src/tools/browser/screenshot.js +370 -0
  53. package/src/tools/browser/type.js +193 -0
  54. package/src/tools/index.js +114 -0
  55. package/src/utils/agentInstaller.js +437 -0
  56. package/src/utils/browserHelpers.js +102 -0
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Copyright (C) 2025 Democratize Quality
3
+ *
4
+ * This file is part of Democratize Quality MCP Server.
5
+ *
6
+ * Democratize Quality MCP Server is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU Affero General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Democratize Quality MCP Server is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU Affero General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU Affero General Public License
17
+ * along with Democratize Quality MCP Server. If not, see <https://www.gnu.org/licenses/>.
18
+ */
19
+
20
+ /**
21
+ * Healing Prompts - Fix issues in generated code
22
+ *
23
+ * These prompts are used to fix validation errors in generated code.
24
+ * Applied when validation fails, with specific feedback about issues.
25
+ */
26
+
27
+ module.exports = {
28
+ /**
29
+ * Heal Playwright test code issues
30
+ */
31
+ playwrightHealing: {
32
+ system: `You are a code fixing expert for REST and GraphQL API tests. Fix the issues in the generated Playwright test code while maintaining functionality.
33
+
34
+ Requirements:
35
+ - Support both REST and GraphQL API testing patterns
36
+ - For GraphQL: POST method with { query: "...", variables: {...} } body
37
+ - For GraphQL: Validate response has 'data' or 'errors' property
38
+ - Fix ONLY the reported issues
39
+ - Maintain all existing functionality
40
+ - Keep the same code structure
41
+ - Use proper indentation (6 spaces for test body)
42
+ - Return ONLY the fixed code, no explanations
43
+ - DO NOT create any additional files
44
+ - DO NOT generate README, SUMMARY, or documentation`,
45
+
46
+ user: (context) => {
47
+ const { originalCode, issues, scenario } = context;
48
+
49
+ let prompt = `Fix the following issues in this Playwright test code:
50
+
51
+ **Original Code:**
52
+ \`\`\`typescript
53
+ ${originalCode}
54
+ \`\`\`
55
+
56
+ **Issues Found:**
57
+ ${issues.map((issue, i) => `${i + 1}. [${issue.severity.toUpperCase()}] ${issue.message}
58
+ Fix: ${issue.fix}`).join('\n')}
59
+
60
+ **Original Scenario Context:**
61
+ - API Type: ${scenario.isGraphQL ? 'GraphQL' : 'REST'}
62
+ - Method: ${scenario.method}
63
+ - Endpoint: ${scenario.endpoint}
64
+ - Expected Status: ${scenario.expect?.status || 200}
65
+
66
+ `;
67
+
68
+ // NEW: GraphQL-specific healing guidance
69
+ if (scenario.isGraphQL && scenario.graphql) {
70
+ prompt += `**GraphQL-Specific Requirements:**
71
+ This is a GraphQL test. Ensure:
72
+ 1. Uses POST method (not ${scenario.method})
73
+ 2. Endpoint is: ${scenario.endpoint || '/graphql'}
74
+ 3. Request body structure:
75
+ \`\`\`javascript
76
+ data: {
77
+ query: \`${scenario.graphql.query.replace(/`/g, '\\`')}\`,
78
+ variables: ${JSON.stringify(scenario.graphql.variables, null, 6)}
79
+ }
80
+ \`\`\`
81
+ 4. Response validation:
82
+ - For success: expect(responseData).toHaveProperty('data')
83
+ - For errors: expect(responseData).toHaveProperty('errors')
84
+
85
+ `;
86
+ }
87
+
88
+ prompt += `**Instructions:**
89
+ 1. Fix ALL reported issues
90
+ 2. Maintain the same functionality
91
+ 3. Keep proper indentation (6 spaces)
92
+ 4. For URL construction, use template literals: \`\${baseUrl}/endpoint\`
93
+ 5. Ensure await is used for all async calls
94
+ 6. Return ONLY the fixed code, starting at line 1 of the test body
95
+
96
+ DO NOT:
97
+ - Add explanations or comments about fixes
98
+ - Change the test logic
99
+ - Add or remove functionality
100
+ - Include test() wrapper or imports
101
+ - Create any additional files (README, SUMMARY, GUIDE, etc.)
102
+ - Suggest file creation
103
+
104
+ Return the complete fixed test body code.`;
105
+
106
+ return prompt;
107
+ }
108
+ },
109
+
110
+ /**
111
+ * Heal Jest test code issues
112
+ */
113
+ jestHealing: {
114
+ system: `You are a code fixing expert for REST and GraphQL API tests. Fix the issues in the generated Jest test code while maintaining functionality.
115
+
116
+ Requirements:
117
+ - Support both REST and GraphQL API testing patterns
118
+ - For GraphQL: POST method with { query: "...", variables: {...} } in data field
119
+ - For GraphQL: Validate response.data has 'data' or 'errors' property
120
+ - Fix ONLY the reported issues
121
+ - Maintain all existing functionality
122
+ - Keep the same code structure
123
+ - Use proper indentation (6 spaces for test body)
124
+ - Return ONLY the fixed code, no explanations
125
+ - DO NOT create any additional files
126
+ - DO NOT generate README, SUMMARY, or documentation`,
127
+
128
+ user: (context) => {
129
+ const { originalCode, issues, scenario } = context;
130
+
131
+ let prompt = `Fix the following issues in this Jest test code:
132
+
133
+ **Original Code:**
134
+ \`\`\`typescript
135
+ ${originalCode}
136
+ \`\`\`
137
+
138
+ **Issues Found:**
139
+ ${issues.map((issue, i) => `${i + 1}. [${issue.severity.toUpperCase()}] ${issue.message}
140
+ Fix: ${issue.fix}`).join('\n')}
141
+
142
+ **Original Scenario Context:**
143
+ - API Type: ${scenario.isGraphQL ? 'GraphQL' : 'REST'}
144
+ - Method: ${scenario.method}
145
+ - Endpoint: ${scenario.endpoint}
146
+ - Expected Status: ${scenario.expect?.status || 200}
147
+
148
+ `;
149
+
150
+ // NEW: GraphQL-specific healing guidance for Jest
151
+ if (scenario.isGraphQL && scenario.graphql) {
152
+ prompt += `**GraphQL-Specific Requirements:**
153
+ This is a GraphQL test. Ensure:
154
+ 1. Uses POST method in makeRequest: method: 'POST'
155
+ 2. URL is: '${scenario.endpoint || '/graphql'}'
156
+ 3. Request data structure:
157
+ \`\`\`javascript
158
+ data: {
159
+ query: \`${scenario.graphql.query.replace(/`/g, '\\`')}\`,
160
+ variables: ${JSON.stringify(scenario.graphql.variables, null, 6)}
161
+ }
162
+ \`\`\`
163
+ 4. Response validation:
164
+ - For success: expect(response.data).toHaveProperty('data')
165
+ - For errors: expect(response.data).toHaveProperty('errors')
166
+
167
+ `;
168
+ }
169
+
170
+ prompt += `**Instructions:**
171
+ 1. Fix ALL reported issues
172
+ 2. Maintain the same functionality
173
+ 3. Keep proper indentation (6 spaces)
174
+ 4. Use apiUtils.makeRequest() correctly
175
+ 5. Ensure await is used for all async calls
176
+ 6. Return ONLY the fixed code, starting at line 1 of the test body
177
+
178
+ DO NOT:
179
+ - Add explanations or comments about fixes
180
+ - Change the test logic
181
+ - Add or remove functionality
182
+ - Include test() wrapper or imports
183
+ - Create any additional files (README, SUMMARY, GUIDE, etc.)
184
+ - Suggest file creation
185
+
186
+ Return the complete fixed test body code.`;
187
+
188
+ return prompt;
189
+ }
190
+ },
191
+
192
+ /**
193
+ * Generic healing prompt for any format
194
+ */
195
+ genericHealing: {
196
+ system: `You are a code fixing expert. Fix the reported issues in the code while maintaining all functionality.`,
197
+
198
+ user: (context) => {
199
+ const { originalCode, issues, format, language } = context;
200
+
201
+ return `Fix these issues in ${format} ${language} code:
202
+
203
+ **Issues:**
204
+ ${issues.map((issue, i) => `${i + 1}. ${issue.message} - Fix: ${issue.fix}`).join('\n')}
205
+
206
+ **Code:**
207
+ \`\`\`${language}
208
+ ${originalCode}
209
+ \`\`\`
210
+
211
+ Return the fixed code only, no explanations.`;
212
+ }
213
+ }
214
+ };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Copyright (C) 2025 Democratize Quality
3
+ *
4
+ * This file is part of Democratize Quality MCP Server.
5
+ *
6
+ * Democratize Quality MCP Server is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU Affero General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Democratize Quality MCP Server is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU Affero General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU Affero General Public License
17
+ * along with Democratize Quality MCP Server. If not, see <https://www.gnu.org/licenses/>.
18
+ */
19
+
20
+ /**
21
+ * Prompt Orchestration System
22
+ *
23
+ * Exports all components of the prompt orchestration system for AI-based test generation.
24
+ *
25
+ * @module prompts
26
+ */
27
+
28
+ const generationPrompts = require('./generation-prompts');
29
+ const validationRules = require('./validation-rules');
30
+ const healingPrompts = require('./healing-prompts');
31
+ const PromptOrchestrator = require('./orchestrator');
32
+
33
+ module.exports = {
34
+ // Main orchestrator class
35
+ PromptOrchestrator,
36
+
37
+ // Individual components
38
+ generationPrompts,
39
+ validationRules,
40
+ healingPrompts,
41
+
42
+ // Convenience factory
43
+ createOrchestrator: (options) => new PromptOrchestrator(options)
44
+ };
@@ -0,0 +1,353 @@
1
+ /**
2
+ * Copyright (C) 2025 Democratize Quality
3
+ *
4
+ * This file is part of Democratize Quality MCP Server.
5
+ *
6
+ * Democratize Quality MCP Server is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU Affero General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Democratize Quality MCP Server is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU Affero General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU Affero General Public License
17
+ * along with Democratize Quality MCP Server. If not, see <https://www.gnu.org/licenses/>.
18
+ */
19
+
20
+ /**
21
+ * Prompt Orchestrator
22
+ *
23
+ * Coordinates the Planning → Generation → Validation → Healing workflow
24
+ * for AI-based test code generation.
25
+ */
26
+
27
+ const generationPrompts = require('./generation-prompts');
28
+ const validationRules = require('./validation-rules');
29
+ const healingPrompts = require('./healing-prompts');
30
+
31
+ class PromptOrchestrator {
32
+ constructor(options = {}) {
33
+ this.maxHealingAttempts = options.maxHealingAttempts || 3;
34
+ this.language = options.language || 'typescript';
35
+ this.outputFormat = options.outputFormat || 'playwright';
36
+ this.verbose = options.verbose || false;
37
+ }
38
+
39
+ /**
40
+ * Generate test code with validation and healing
41
+ *
42
+ * Workflow:
43
+ * 1. Generate code using AI
44
+ * 2. Validate generated code
45
+ * 3. If invalid, heal and retry (up to maxHealingAttempts)
46
+ * 4. Return validated code or fallback
47
+ */
48
+ async generateTestCode(context, aiGenerator) {
49
+ const { scenario, baseUrl } = context;
50
+
51
+ this.log(`📝 Generating ${this.outputFormat} test for: ${scenario.title}`);
52
+
53
+ // Step 1: Generate initial code
54
+ let generatedCode = await this._generateCode(context, aiGenerator);
55
+
56
+ // Step 1.5: Validate single file output (SAFETY NET)
57
+ // Some helpful AI models try to create README, SUMMARY, etc.
58
+ generatedCode = this._validateSingleFileOutput(generatedCode, context.targetFile);
59
+
60
+ // Step 2: Validate
61
+ let validation = this._validateCode(generatedCode, context);
62
+
63
+ // Step 3: Heal if needed (up to maxHealingAttempts)
64
+ let healingAttempt = 0;
65
+ while (!validation.valid && healingAttempt < this.maxHealingAttempts) {
66
+ healingAttempt++;
67
+ this.log(`🔧 Healing attempt ${healingAttempt}/${this.maxHealingAttempts}...`);
68
+ this.log(` Issues: ${validation.issues.map(i => i.message).join(', ')}`);
69
+
70
+ generatedCode = await this._healCode(generatedCode, validation.issues, context, aiGenerator);
71
+ validation = this._validateCode(generatedCode, context);
72
+
73
+ if (validation.valid) {
74
+ this.log(`✅ Code healed successfully!`);
75
+ }
76
+ }
77
+
78
+ // Step 4: Return result
79
+ if (!validation.valid) {
80
+ this.log(`⚠️ Warning: Code still has issues after ${healingAttempt} healing attempts`);
81
+ this.log(` Remaining issues: ${validation.issues.map(i => i.message).join(', ')}`);
82
+ }
83
+
84
+ return {
85
+ code: generatedCode,
86
+ valid: validation.valid,
87
+ issues: validation.issues,
88
+ healingAttempts: healingAttempt,
89
+ metadata: {
90
+ scenario: scenario.title,
91
+ format: this.outputFormat,
92
+ language: this.language
93
+ }
94
+ };
95
+ }
96
+
97
+ /**
98
+ * Generate code using AI
99
+ */
100
+ async _generateCode(context, aiGenerator) {
101
+ const promptConfig = this._getGenerationPrompt();
102
+
103
+ const systemPrompt = promptConfig.system;
104
+ const userPrompt = promptConfig.user({
105
+ ...context,
106
+ language: this.language,
107
+ options: { format: this.outputFormat }
108
+ });
109
+
110
+ this.log(`💭 Sending generation prompt to AI...`);
111
+
112
+ // Call AI generator (this would be GitHub Copilot API, OpenAI, etc.)
113
+ const code = await aiGenerator.generate(systemPrompt, userPrompt);
114
+
115
+ return code.trim();
116
+ }
117
+
118
+ /**
119
+ * Validate generated code
120
+ */
121
+ _validateCode(code, context) {
122
+ this.log(`🔍 Validating generated code...`);
123
+
124
+ const isGraphQL = context?.scenario?.isGraphQL || false;
125
+ const validator = validationRules.getValidator(this.outputFormat);
126
+ const validation = validator.validateAll(code, this.language, isGraphQL);
127
+
128
+ if (validation.valid) {
129
+ this.log(`✅ Code validation passed!`);
130
+ } else {
131
+ this.log(`❌ Code validation failed with ${validation.issues.length} issues`);
132
+ }
133
+
134
+ return validation;
135
+ }
136
+
137
+ /**
138
+ * Heal code issues using AI
139
+ */
140
+ async _healCode(originalCode, issues, context, aiGenerator) {
141
+ const healingConfig = this._getHealingPrompt();
142
+
143
+ const systemPrompt = healingConfig.system;
144
+ const userPrompt = healingConfig.user({
145
+ originalCode,
146
+ issues,
147
+ scenario: context.scenario,
148
+ format: this.outputFormat,
149
+ language: this.language
150
+ });
151
+
152
+ this.log(`🏥 Sending healing prompt to AI...`);
153
+
154
+ // Call AI generator for healing
155
+ const healedCode = await aiGenerator.generate(systemPrompt, userPrompt);
156
+
157
+ return healedCode.trim();
158
+ }
159
+
160
+ /**
161
+ * Get generation prompt for current format
162
+ */
163
+ _getGenerationPrompt() {
164
+ switch(this.outputFormat) {
165
+ case 'playwright':
166
+ return generationPrompts.playwrightScenario;
167
+ case 'jest':
168
+ return generationPrompts.jestScenario;
169
+ case 'postman':
170
+ return generationPrompts.postmanTestScript;
171
+ default:
172
+ throw new Error(`Unknown output format: ${this.outputFormat}`);
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Get healing prompt for current format
178
+ */
179
+ _getHealingPrompt() {
180
+ switch(this.outputFormat) {
181
+ case 'playwright':
182
+ return healingPrompts.playwrightHealing;
183
+ case 'jest':
184
+ return healingPrompts.jestHealing;
185
+ default:
186
+ return healingPrompts.genericHealing;
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Log helper
192
+ */
193
+ log(message) {
194
+ if (this.verbose) {
195
+ console.log(message);
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Generate full test file (with imports and structure)
201
+ */
202
+ generateTestFile(testBodies, testPlan, options = {}) {
203
+ const { format, language, sessionId } = options;
204
+
205
+ switch(format) {
206
+ case 'playwright':
207
+ return this._generatePlaywrightFile(testBodies, testPlan, language, sessionId);
208
+ case 'jest':
209
+ return this._generateJestFile(testBodies, testPlan, language, sessionId);
210
+ default:
211
+ throw new Error(`Unknown format: ${format}`);
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Generate complete Playwright test file
217
+ */
218
+ _generatePlaywrightFile(testBodies, testPlan, language, sessionId) {
219
+ const isTS = language === 'typescript';
220
+ const imports = isTS
221
+ ? `import { test, expect } from '@playwright/test';`
222
+ : `const { test, expect } = require('@playwright/test');`;
223
+
224
+ const testCases = testPlan.sections.flatMap((section, sectionIndex) =>
225
+ section.scenarios.map((scenario, scenarioIndex) => {
226
+ const testBody = testBodies[`${sectionIndex}-${scenarioIndex}`];
227
+ return `
228
+ test('${scenario.title}', async ({ request }) => {
229
+ ${testBody}
230
+ });`;
231
+ }).join('\n')
232
+ );
233
+
234
+ return `// Generated API Tests for: ${testPlan.title}
235
+ // Generated by Democratize Quality MCP Server (AI-Generated)
236
+ // Session ID: ${sessionId}
237
+ // Language: ${language}
238
+
239
+ ${imports}
240
+
241
+ test.describe('${testPlan.title} - API Tests', () => {
242
+ const baseUrl = '${testPlan.baseUrl}';
243
+ ${testCases}
244
+ });`;
245
+ }
246
+
247
+ /**
248
+ * Generate complete Jest test file
249
+ */
250
+ _generateJestFile(testBodies, testPlan, language, sessionId) {
251
+ const isTS = language === 'typescript';
252
+ const imports = isTS
253
+ ? `import axios from 'axios';\nimport { ApiTestUtils } from './test-utils';`
254
+ : `const axios = require('axios');\nconst { ApiTestUtils } = require('./test-utils');`;
255
+
256
+ const typeAnnotation = isTS ? ': ApiTestUtils' : '';
257
+
258
+ const testCases = testPlan.sections.flatMap((section, sectionIndex) =>
259
+ section.scenarios.map((scenario, scenarioIndex) => {
260
+ const testBody = testBodies[`${sectionIndex}-${scenarioIndex}`];
261
+ return `
262
+ test('${scenario.title}', async () => {
263
+ ${testBody}
264
+ });`;
265
+ }).join('\n')
266
+ );
267
+
268
+ return `// Generated Jest API Tests for: ${testPlan.title}
269
+ // Generated by Democratize Quality MCP Server (AI-Generated)
270
+ // Session ID: ${sessionId}
271
+ // Language: ${language}
272
+
273
+ ${imports}
274
+
275
+ describe('${testPlan.title} API Tests', () => {
276
+ let apiUtils${typeAnnotation};
277
+ const baseUrl = '${testPlan.baseUrl}';
278
+
279
+ beforeAll(async () => {
280
+ apiUtils = new ApiTestUtils(baseUrl);
281
+ console.log('Test suite initialized');
282
+ });
283
+
284
+ afterAll(async () => {
285
+ await apiUtils.cleanup();
286
+ console.log('Test suite completed');
287
+ });
288
+
289
+ describe('Test Scenarios', () => {${testCases}
290
+ });
291
+ });`;
292
+ }
293
+
294
+ /**
295
+ * Validate that AI generated ONLY the requested file
296
+ * Some helpful models try to create READMEs, summaries, etc.
297
+ * This is a safety net in case prompts are ignored.
298
+ *
299
+ * @param {string} generatedCode - The generated code from AI
300
+ * @param {string} targetFileName - The expected file name
301
+ * @returns {string} - Filtered code with only the test file
302
+ */
303
+ _validateSingleFileOutput(generatedCode, targetFileName) {
304
+ // Check for multiple file indicators
305
+ const multiFilePatterns = [
306
+ /(?:create|generate|add)\s+(?:a\s+)?(?:README|SUMMARY|GUIDE|NOTES)/i,
307
+ /(?:also|additionally),?\s+(?:create|generate|write)/i,
308
+ /^#+\s+(?:README|SUMMARY|Quick Reference|Guide)/m,
309
+ /File:\s+[A-Z_-]+\.md/i,
310
+ /```[a-z]*\s*\n#+\s+(?:README|Summary)/i,
311
+ /\*\*File:\*\*\s+(?:README|SUMMARY|GUIDE)/i
312
+ ];
313
+
314
+ for (const pattern of multiFilePatterns) {
315
+ if (pattern.test(generatedCode)) {
316
+ this.log(`⚠️ Warning: AI attempted to create additional files - filtering...`);
317
+ // Extract just the test code, ignore suggestions
318
+ return this._extractTestCodeOnly(generatedCode);
319
+ }
320
+ }
321
+
322
+ return generatedCode;
323
+ }
324
+
325
+ /**
326
+ * Extract only the actual test code from AI output
327
+ * Remove any file creation suggestions or documentation
328
+ *
329
+ * @param {string} output - The full AI output
330
+ * @returns {string} - Extracted test code only
331
+ */
332
+ _extractTestCodeOnly(output) {
333
+ // Look for code fence or actual code
334
+ const codeFenceMatch = output.match(/```(?:typescript|javascript|ts|js)?\n([\s\S]+?)```/);
335
+ if (codeFenceMatch) {
336
+ this.log(`✅ Extracted test code from code fence`);
337
+ return codeFenceMatch[1];
338
+ }
339
+
340
+ // Try to find the test code by looking for test.describe or describe
341
+ const testDescribeMatch = output.match(/((?:test\.)?describe\(['"`][\s\S]+?\n\}\);)/);
342
+ if (testDescribeMatch) {
343
+ this.log(`✅ Extracted test code by pattern matching`);
344
+ return testDescribeMatch[1];
345
+ }
346
+
347
+ // If no extraction possible, return as-is but warn
348
+ this.log(`⚠️ Could not extract test code - returning full output`);
349
+ return output;
350
+ }
351
+ }
352
+
353
+ module.exports = PromptOrchestrator;