mcp-maestro-mobile-ai 1.4.0 → 1.6.0
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.
- package/CHANGELOG.md +101 -1
- package/package.json +6 -3
- package/src/mcp-server/index.js +339 -4
- package/src/mcp-server/schemas/toolSchemas.js +184 -0
- package/src/mcp-server/tools/contextTools.js +309 -2
- package/src/mcp-server/tools/runTools.js +409 -31
- package/src/mcp-server/utils/knownIssues.js +564 -0
- package/src/mcp-server/utils/promptAnalyzer.js +701 -0
- package/src/mcp-server/utils/yamlCache.js +381 -0
- package/src/mcp-server/utils/yamlGenerator.js +426 -0
- package/src/mcp-server/utils/yamlTemplate.js +303 -0
|
@@ -442,6 +442,9 @@ const validPatternNames = [
|
|
|
442
442
|
"list",
|
|
443
443
|
"settings",
|
|
444
444
|
"logout",
|
|
445
|
+
"dropdown",
|
|
446
|
+
"flutter_login_with_dropdown",
|
|
447
|
+
"flutter_multi_dropdown",
|
|
445
448
|
];
|
|
446
449
|
|
|
447
450
|
/**
|
|
@@ -460,6 +463,156 @@ export const getTestPatternSchema = z.object({
|
|
|
460
463
|
*/
|
|
461
464
|
export const getScreenAnalysisHelpSchema = z.object({});
|
|
462
465
|
|
|
466
|
+
/**
|
|
467
|
+
* Valid pattern categories
|
|
468
|
+
*/
|
|
469
|
+
const validPatternCategories = [
|
|
470
|
+
"keyboard_handling",
|
|
471
|
+
"element_interaction",
|
|
472
|
+
"timing_synchronization",
|
|
473
|
+
"input_handling",
|
|
474
|
+
"dropdown_picker",
|
|
475
|
+
"navigation",
|
|
476
|
+
"scroll_visibility",
|
|
477
|
+
"fallback_strategies",
|
|
478
|
+
];
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Schema for get_interaction_patterns tool
|
|
482
|
+
*/
|
|
483
|
+
export const getKnownIssuesSchema = z.object({
|
|
484
|
+
category: z.enum(validPatternCategories).optional().describe("Filter patterns by category"),
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Schema for get_patterns_by_category tool
|
|
489
|
+
*/
|
|
490
|
+
export const getPlatformRulesSchema = z.object({
|
|
491
|
+
category: z.enum(validPatternCategories, {
|
|
492
|
+
errorMap: () => ({
|
|
493
|
+
message: `Category must be one of: ${validPatternCategories.join(", ")}`,
|
|
494
|
+
}),
|
|
495
|
+
}),
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Schema for get_generation_rules tool (no params)
|
|
500
|
+
*/
|
|
501
|
+
export const getGenerationRulesSchema = z.object({});
|
|
502
|
+
|
|
503
|
+
// ============================================
|
|
504
|
+
// PROMPT ANALYSIS & VALIDATION SCHEMAS
|
|
505
|
+
// ============================================
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Schema for validate_prompt tool
|
|
509
|
+
* Analyzes user prompt using Action-Element-Verification model
|
|
510
|
+
*/
|
|
511
|
+
export const validatePromptSchema = z.object({
|
|
512
|
+
userPrompt: z
|
|
513
|
+
.string()
|
|
514
|
+
.min(5, "Prompt too short - provide a test description")
|
|
515
|
+
.max(5000, "Prompt too long"),
|
|
516
|
+
appId: z
|
|
517
|
+
.string()
|
|
518
|
+
.max(150, "App ID too long")
|
|
519
|
+
.regex(appIdPattern, "Invalid app ID format")
|
|
520
|
+
.optional()
|
|
521
|
+
.describe("Optional app package ID. If not in prompt, must be provided here."),
|
|
522
|
+
forceGenerate: z
|
|
523
|
+
.boolean()
|
|
524
|
+
.optional()
|
|
525
|
+
.default(false)
|
|
526
|
+
.describe("If true, generate YAML with assumptions even if information is incomplete."),
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Schema for analyze_prompt tool
|
|
531
|
+
* Analyzes prompt without generating YAML
|
|
532
|
+
*/
|
|
533
|
+
export const analyzePromptSchema = z.object({
|
|
534
|
+
userPrompt: z
|
|
535
|
+
.string()
|
|
536
|
+
.min(5, "Prompt too short")
|
|
537
|
+
.max(5000, "Prompt too long"),
|
|
538
|
+
appId: z
|
|
539
|
+
.string()
|
|
540
|
+
.max(150)
|
|
541
|
+
.regex(appIdPattern, "Invalid app ID format")
|
|
542
|
+
.optional(),
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
// ============================================
|
|
546
|
+
// YAML CACHE SCHEMAS
|
|
547
|
+
// ============================================
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Schema for save_to_cache tool
|
|
551
|
+
*/
|
|
552
|
+
export const saveToCacheSchema = z.object({
|
|
553
|
+
prompt: z
|
|
554
|
+
.string()
|
|
555
|
+
.min(5, "Prompt too short")
|
|
556
|
+
.max(5000, "Prompt too long")
|
|
557
|
+
.describe("The original test prompt/description"),
|
|
558
|
+
yaml: yamlContentSchema.describe("The YAML content to cache"),
|
|
559
|
+
testName: testNameSchema.describe("Name of the test"),
|
|
560
|
+
appId: optionalAppIdSchema,
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Schema for lookup_cache tool
|
|
565
|
+
*/
|
|
566
|
+
export const lookupCacheSchema = z.object({
|
|
567
|
+
prompt: z
|
|
568
|
+
.string()
|
|
569
|
+
.min(5, "Prompt too short")
|
|
570
|
+
.max(5000, "Prompt too long")
|
|
571
|
+
.describe("The test prompt to look up in cache"),
|
|
572
|
+
appId: optionalAppIdSchema,
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Schema for delete_from_cache tool
|
|
577
|
+
*/
|
|
578
|
+
export const deleteFromCacheSchema = z.object({
|
|
579
|
+
hash: z
|
|
580
|
+
.string()
|
|
581
|
+
.min(8, "Hash too short")
|
|
582
|
+
.max(64, "Hash too long")
|
|
583
|
+
.describe("The cache hash to delete (from list_cache output)"),
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Schema for list_cache tool (no params)
|
|
588
|
+
*/
|
|
589
|
+
export const listCacheSchema = z.object({});
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Schema for clear_cache tool (no params)
|
|
593
|
+
*/
|
|
594
|
+
export const clearCacheSchema = z.object({});
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Schema for get_cache_stats tool (no params)
|
|
598
|
+
*/
|
|
599
|
+
export const getCacheStatsSchema = z.object({});
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Schema for run_test_with_cache tool
|
|
603
|
+
*/
|
|
604
|
+
export const runTestWithCacheSchema = z.object({
|
|
605
|
+
prompt: z
|
|
606
|
+
.string()
|
|
607
|
+
.min(5)
|
|
608
|
+
.max(5000)
|
|
609
|
+
.describe("The original test prompt - used for cache lookup"),
|
|
610
|
+
yaml: yamlContentSchema.describe("The YAML to use if not cached"),
|
|
611
|
+
name: testNameSchema,
|
|
612
|
+
appId: optionalAppIdSchema,
|
|
613
|
+
retries: retriesSchema,
|
|
614
|
+
});
|
|
615
|
+
|
|
463
616
|
// ============================================
|
|
464
617
|
// SCHEMA REGISTRY
|
|
465
618
|
// ============================================
|
|
@@ -513,6 +666,24 @@ export const toolSchemas = {
|
|
|
513
666
|
validate_yaml_structure: validateYamlStructureSchema,
|
|
514
667
|
get_test_pattern: getTestPatternSchema,
|
|
515
668
|
get_screen_analysis_help: getScreenAnalysisHelpSchema,
|
|
669
|
+
|
|
670
|
+
// Known Issues & Platform Rules
|
|
671
|
+
get_known_issues: getKnownIssuesSchema,
|
|
672
|
+
get_platform_rules: getPlatformRulesSchema,
|
|
673
|
+
get_generation_rules: getGenerationRulesSchema,
|
|
674
|
+
|
|
675
|
+
// Prompt Analysis & Validation
|
|
676
|
+
validate_prompt: validatePromptSchema,
|
|
677
|
+
analyze_prompt: analyzePromptSchema,
|
|
678
|
+
|
|
679
|
+
// YAML Cache
|
|
680
|
+
save_to_cache: saveToCacheSchema,
|
|
681
|
+
lookup_cache: lookupCacheSchema,
|
|
682
|
+
delete_from_cache: deleteFromCacheSchema,
|
|
683
|
+
list_cache: listCacheSchema,
|
|
684
|
+
clear_cache: clearCacheSchema,
|
|
685
|
+
get_cache_stats: getCacheStatsSchema,
|
|
686
|
+
run_test_with_cache: runTestWithCacheSchema,
|
|
516
687
|
};
|
|
517
688
|
|
|
518
689
|
// ============================================
|
|
@@ -633,4 +804,17 @@ export default {
|
|
|
633
804
|
validateYamlStructureSchema,
|
|
634
805
|
getTestPatternSchema,
|
|
635
806
|
getScreenAnalysisHelpSchema,
|
|
807
|
+
|
|
808
|
+
// Prompt Analysis & Validation
|
|
809
|
+
validatePromptSchema,
|
|
810
|
+
analyzePromptSchema,
|
|
811
|
+
|
|
812
|
+
// YAML Cache
|
|
813
|
+
saveToCacheSchema,
|
|
814
|
+
lookupCacheSchema,
|
|
815
|
+
deleteFromCacheSchema,
|
|
816
|
+
listCacheSchema,
|
|
817
|
+
clearCacheSchema,
|
|
818
|
+
getCacheStatsSchema,
|
|
819
|
+
runTestWithCacheSchema,
|
|
636
820
|
};
|
|
@@ -20,7 +20,26 @@ import {
|
|
|
20
20
|
TEST_PATTERNS,
|
|
21
21
|
validateYamlStructure,
|
|
22
22
|
getScreenAnalysisInstructions,
|
|
23
|
+
getInteractionPatternHints,
|
|
24
|
+
getYamlGenerationRules,
|
|
25
|
+
getBestPractices,
|
|
26
|
+
PatternCategory,
|
|
23
27
|
} from "../utils/yamlTemplate.js";
|
|
28
|
+
import {
|
|
29
|
+
INTERACTION_PATTERNS,
|
|
30
|
+
getPatternsByCategory,
|
|
31
|
+
getPatternCategories,
|
|
32
|
+
} from "../utils/knownIssues.js";
|
|
33
|
+
import {
|
|
34
|
+
analyzePrompt,
|
|
35
|
+
generateClarificationQuestions,
|
|
36
|
+
generateWarnings,
|
|
37
|
+
CompletenessLevel,
|
|
38
|
+
} from "../utils/promptAnalyzer.js";
|
|
39
|
+
import {
|
|
40
|
+
generateYamlFromAnalysis,
|
|
41
|
+
formatGenerationResponse,
|
|
42
|
+
} from "../utils/yamlGenerator.js";
|
|
24
43
|
|
|
25
44
|
/**
|
|
26
45
|
* Format result as MCP response
|
|
@@ -259,6 +278,9 @@ export async function getTestPattern(patternName) {
|
|
|
259
278
|
list: TEST_PATTERNS.list,
|
|
260
279
|
settings: TEST_PATTERNS.settings,
|
|
261
280
|
logout: TEST_PATTERNS.logout,
|
|
281
|
+
dropdown: TEST_PATTERNS.dropdown,
|
|
282
|
+
flutter_login_with_dropdown: TEST_PATTERNS.flutter_login_with_dropdown,
|
|
283
|
+
flutter_multi_dropdown: TEST_PATTERNS.flutter_multi_dropdown,
|
|
262
284
|
};
|
|
263
285
|
|
|
264
286
|
const pattern = patterns[patternName?.toLowerCase()];
|
|
@@ -268,7 +290,7 @@ export async function getTestPattern(patternName) {
|
|
|
268
290
|
success: false,
|
|
269
291
|
error: `Unknown pattern: ${patternName}`,
|
|
270
292
|
availablePatterns: Object.keys(patterns),
|
|
271
|
-
hint: "Use: login, form, search, navigation, list, settings, or
|
|
293
|
+
hint: "Use: login, form, search, navigation, list, settings, logout, dropdown, flutter_login_with_dropdown, or flutter_multi_dropdown",
|
|
272
294
|
});
|
|
273
295
|
}
|
|
274
296
|
|
|
@@ -276,7 +298,7 @@ export async function getTestPattern(patternName) {
|
|
|
276
298
|
success: true,
|
|
277
299
|
pattern: patternName,
|
|
278
300
|
template: pattern,
|
|
279
|
-
message: "Replace placeholders in {} with actual values. REMEMBER: Always use tapOn before inputText!",
|
|
301
|
+
message: "Replace placeholders in {} with actual values. REMEMBER: Always use tapOn before inputText! For dropdowns: hideKeyboard -> tapCurrentValue -> waitForAnimationToEnd -> tapDesiredValue",
|
|
280
302
|
});
|
|
281
303
|
}
|
|
282
304
|
|
|
@@ -299,6 +321,284 @@ export async function getScreenAnalysis() {
|
|
|
299
321
|
});
|
|
300
322
|
}
|
|
301
323
|
|
|
324
|
+
/**
|
|
325
|
+
* Get interaction patterns for reliable YAML generation
|
|
326
|
+
* These patterns are framework-agnostic and work across all mobile apps.
|
|
327
|
+
*/
|
|
328
|
+
export async function getKnownIssues(category = null) {
|
|
329
|
+
let patterns = INTERACTION_PATTERNS;
|
|
330
|
+
|
|
331
|
+
if (category) {
|
|
332
|
+
patterns = getPatternsByCategory(category);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Format patterns for display
|
|
336
|
+
const formattedPatterns = Object.entries(patterns).map(([key, pattern]) => ({
|
|
337
|
+
name: key,
|
|
338
|
+
category: pattern.category,
|
|
339
|
+
situation: pattern.situation,
|
|
340
|
+
strategy: pattern.strategy,
|
|
341
|
+
rationale: pattern.rationale,
|
|
342
|
+
}));
|
|
343
|
+
|
|
344
|
+
return formatResponse({
|
|
345
|
+
success: true,
|
|
346
|
+
count: formattedPatterns.length,
|
|
347
|
+
patterns: formattedPatterns,
|
|
348
|
+
availableCategories: getPatternCategories(),
|
|
349
|
+
message: "These patterns are automatically included in YAML generation. Apply them to create reliable tests.",
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Get interaction patterns by category
|
|
355
|
+
*/
|
|
356
|
+
export async function getPlatformRules(category) {
|
|
357
|
+
const validCategories = getPatternCategories();
|
|
358
|
+
|
|
359
|
+
if (!category || !validCategories.includes(category)) {
|
|
360
|
+
return formatResponse({
|
|
361
|
+
success: false,
|
|
362
|
+
error: `Invalid category: ${category}`,
|
|
363
|
+
validCategories,
|
|
364
|
+
hint: "Use: keyboard_handling, element_interaction, timing_synchronization, input_handling, dropdown_picker, navigation, scroll_visibility, or fallback_strategies",
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const patterns = getPatternsByCategory(category);
|
|
369
|
+
|
|
370
|
+
return formatResponse({
|
|
371
|
+
success: true,
|
|
372
|
+
category,
|
|
373
|
+
patterns: Object.entries(patterns).map(([key, p]) => ({
|
|
374
|
+
name: key,
|
|
375
|
+
situation: p.situation,
|
|
376
|
+
strategy: p.strategy,
|
|
377
|
+
yamlPattern: p.yamlPattern,
|
|
378
|
+
})),
|
|
379
|
+
message: "Apply these patterns for the specified interaction type.",
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Get all YAML generation rules and best practices
|
|
385
|
+
*/
|
|
386
|
+
export async function getGenerationRules() {
|
|
387
|
+
const rules = getYamlGenerationRules();
|
|
388
|
+
const hints = getInteractionPatternHints();
|
|
389
|
+
const bestPractices = getBestPractices();
|
|
390
|
+
|
|
391
|
+
return formatResponse({
|
|
392
|
+
success: true,
|
|
393
|
+
rules,
|
|
394
|
+
hints,
|
|
395
|
+
bestPractices,
|
|
396
|
+
message: "These rules are automatically applied during YAML generation. They are framework-agnostic and improve test reliability.",
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Validate and analyze user prompt for YAML generation
|
|
402
|
+
* Uses Action-Element-Verification model for generic prompt analysis
|
|
403
|
+
*
|
|
404
|
+
* @param {string} userPrompt - The user's test description
|
|
405
|
+
* @param {object} options - Options: appId, forceGenerate
|
|
406
|
+
* @returns {object} Analysis result with YAML or clarification questions
|
|
407
|
+
*/
|
|
408
|
+
export async function validateAndGenerate(userPrompt, options = {}) {
|
|
409
|
+
const { appId = null, forceGenerate = false } = options;
|
|
410
|
+
|
|
411
|
+
if (!userPrompt || typeof userPrompt !== 'string' || userPrompt.trim().length < 5) {
|
|
412
|
+
return formatResponse({
|
|
413
|
+
success: false,
|
|
414
|
+
error: "Prompt is too short or empty",
|
|
415
|
+
hint: "Provide a description of the test you want to create. Example: 'Test login with username abc and password 1234'",
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Step 1: Analyze the prompt
|
|
420
|
+
const analysis = analyzePrompt(userPrompt, { appId });
|
|
421
|
+
|
|
422
|
+
// Step 2: Check completeness level
|
|
423
|
+
const { assessment, extracted } = analysis;
|
|
424
|
+
|
|
425
|
+
// INSUFFICIENT: Cannot proceed without critical info
|
|
426
|
+
if (assessment.level === CompletenessLevel.INSUFFICIENT) {
|
|
427
|
+
const questions = generateClarificationQuestions(analysis);
|
|
428
|
+
const criticalQuestions = questions.filter(q => q.priority === 'critical');
|
|
429
|
+
|
|
430
|
+
return formatResponse({
|
|
431
|
+
success: false,
|
|
432
|
+
status: 'insufficient',
|
|
433
|
+
message: 'Cannot generate YAML: missing required information',
|
|
434
|
+
understood: {
|
|
435
|
+
appContext: extracted.appContext,
|
|
436
|
+
detectedActions: extracted.actions.map(a => a.type),
|
|
437
|
+
detectedElements: extracted.elements.map(e => e.type),
|
|
438
|
+
valuesFound: Object.keys(extracted.values),
|
|
439
|
+
},
|
|
440
|
+
requiredInfo: criticalQuestions.map(q => ({
|
|
441
|
+
field: q.field,
|
|
442
|
+
question: q.question,
|
|
443
|
+
})),
|
|
444
|
+
hint: 'Please provide at least the app ID (package name) to continue.',
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// NEEDS_CLARIFICATION: Should ask, but can force generate
|
|
449
|
+
if (assessment.level === CompletenessLevel.NEEDS_CLARIFICATION && !forceGenerate) {
|
|
450
|
+
const questions = generateClarificationQuestions(analysis);
|
|
451
|
+
|
|
452
|
+
return formatResponse({
|
|
453
|
+
success: false,
|
|
454
|
+
status: 'needs_clarification',
|
|
455
|
+
message: 'Additional information recommended for reliable test generation',
|
|
456
|
+
completenessScore: assessment.score,
|
|
457
|
+
understood: {
|
|
458
|
+
appId: extracted.appContext.appId,
|
|
459
|
+
appName: extracted.appContext.appName,
|
|
460
|
+
detectedActions: extracted.actions.map(a => ({ type: a.type, context: a.context })),
|
|
461
|
+
detectedElements: extracted.elements.map(e => ({ type: e.type, label: e.label })),
|
|
462
|
+
valuesFound: extracted.values,
|
|
463
|
+
verifications: extracted.verifications.map(v => ({ type: v.type, target: v.target })),
|
|
464
|
+
},
|
|
465
|
+
questions: questions.map(q => ({
|
|
466
|
+
priority: q.priority,
|
|
467
|
+
field: q.field,
|
|
468
|
+
question: q.question,
|
|
469
|
+
required: q.required,
|
|
470
|
+
default: q.default,
|
|
471
|
+
})),
|
|
472
|
+
canForceGenerate: true,
|
|
473
|
+
hint: 'Answer the questions for better accuracy, or use forceGenerate: true to proceed with assumptions.',
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// GENERATABLE: Can generate with assumptions (if forceGenerate is true)
|
|
478
|
+
// or ask for clarification
|
|
479
|
+
if (assessment.level === CompletenessLevel.GENERATABLE && !forceGenerate) {
|
|
480
|
+
const questions = generateClarificationQuestions(analysis);
|
|
481
|
+
const recommendedQuestions = questions.filter(q => q.priority === 'recommended');
|
|
482
|
+
|
|
483
|
+
return formatResponse({
|
|
484
|
+
success: false,
|
|
485
|
+
status: 'can_improve',
|
|
486
|
+
message: 'Prompt understood, but additional details would improve test reliability',
|
|
487
|
+
completenessScore: assessment.score,
|
|
488
|
+
understood: {
|
|
489
|
+
appId: extracted.appContext.appId,
|
|
490
|
+
detectedActions: extracted.actions.map(a => ({ type: a.type, context: a.context })),
|
|
491
|
+
detectedElements: extracted.elements.map(e => ({ type: e.type, label: e.label })),
|
|
492
|
+
valuesFound: extracted.values,
|
|
493
|
+
},
|
|
494
|
+
recommendations: recommendedQuestions.map(q => ({
|
|
495
|
+
field: q.field,
|
|
496
|
+
question: q.question,
|
|
497
|
+
default: q.default,
|
|
498
|
+
})),
|
|
499
|
+
assumptionsIfForced: assessment.assumptions,
|
|
500
|
+
canForceGenerate: true,
|
|
501
|
+
hint: 'Provide additional details or use forceGenerate: true to generate with assumptions.',
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// COMPLETE or forceGenerate: Generate YAML
|
|
506
|
+
const result = generateYamlFromAnalysis(analysis, {
|
|
507
|
+
forceGenerate,
|
|
508
|
+
includeComments: true,
|
|
509
|
+
appId: appId || extracted.appContext.appId,
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
if (!result.success) {
|
|
513
|
+
return formatResponse({
|
|
514
|
+
success: false,
|
|
515
|
+
error: result.error,
|
|
516
|
+
missing: result.missing,
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Generate warnings if force generated
|
|
521
|
+
const warnings = forceGenerate && assessment.level !== CompletenessLevel.COMPLETE
|
|
522
|
+
? generateWarnings(analysis)
|
|
523
|
+
: [];
|
|
524
|
+
|
|
525
|
+
return formatResponse({
|
|
526
|
+
success: true,
|
|
527
|
+
status: result.forceGenerated ? 'generated_with_warnings' : 'generated',
|
|
528
|
+
message: result.forceGenerated
|
|
529
|
+
? 'YAML generated with assumptions. Review and update placeholders before running.'
|
|
530
|
+
: 'YAML generated successfully.',
|
|
531
|
+
yaml: result.yaml,
|
|
532
|
+
completenessScore: result.completenessScore,
|
|
533
|
+
forceGenerated: result.forceGenerated || false,
|
|
534
|
+
warnings: [
|
|
535
|
+
...result.warnings,
|
|
536
|
+
...warnings.map(w => w.message),
|
|
537
|
+
],
|
|
538
|
+
assumptions: result.assumptions,
|
|
539
|
+
analysis: {
|
|
540
|
+
detectedActions: extracted.actions.length,
|
|
541
|
+
detectedElements: extracted.elements.length,
|
|
542
|
+
valuesFound: Object.keys(extracted.values).length,
|
|
543
|
+
verifications: extracted.verifications.length,
|
|
544
|
+
},
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Analyze prompt without generating YAML
|
|
550
|
+
* Useful for understanding what the system detected
|
|
551
|
+
*/
|
|
552
|
+
export async function analyzeTestPrompt(userPrompt, appId = null) {
|
|
553
|
+
if (!userPrompt || typeof userPrompt !== 'string') {
|
|
554
|
+
return formatResponse({
|
|
555
|
+
success: false,
|
|
556
|
+
error: "Invalid prompt",
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
const analysis = analyzePrompt(userPrompt, { appId });
|
|
561
|
+
const { extracted, assessment } = analysis;
|
|
562
|
+
const questions = generateClarificationQuestions(analysis);
|
|
563
|
+
|
|
564
|
+
return formatResponse({
|
|
565
|
+
success: true,
|
|
566
|
+
analysis: {
|
|
567
|
+
appContext: extracted.appContext,
|
|
568
|
+
actions: extracted.actions.map(a => ({
|
|
569
|
+
type: a.type,
|
|
570
|
+
keyword: a.keyword,
|
|
571
|
+
context: a.context,
|
|
572
|
+
})),
|
|
573
|
+
elements: extracted.elements.map(e => ({
|
|
574
|
+
type: e.type,
|
|
575
|
+
label: e.label,
|
|
576
|
+
keyword: e.keyword,
|
|
577
|
+
})),
|
|
578
|
+
values: extracted.values,
|
|
579
|
+
verifications: extracted.verifications.map(v => ({
|
|
580
|
+
type: v.type,
|
|
581
|
+
target: v.target,
|
|
582
|
+
})),
|
|
583
|
+
sequence: extracted.sequence,
|
|
584
|
+
},
|
|
585
|
+
assessment: {
|
|
586
|
+
level: assessment.level,
|
|
587
|
+
score: assessment.score,
|
|
588
|
+
issues: assessment.issues,
|
|
589
|
+
missing: assessment.missing,
|
|
590
|
+
assumptions: assessment.assumptions,
|
|
591
|
+
canForceGenerate: assessment.canForceGenerate,
|
|
592
|
+
},
|
|
593
|
+
clarificationQuestions: questions.map(q => ({
|
|
594
|
+
priority: q.priority,
|
|
595
|
+
field: q.field,
|
|
596
|
+
question: q.question,
|
|
597
|
+
required: q.required,
|
|
598
|
+
})),
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
|
|
302
602
|
export default {
|
|
303
603
|
registerAppElements,
|
|
304
604
|
registerAppScreen,
|
|
@@ -313,5 +613,12 @@ export default {
|
|
|
313
613
|
validateYamlBeforeRun,
|
|
314
614
|
getTestPattern,
|
|
315
615
|
getScreenAnalysis,
|
|
616
|
+
// Known Issues Tools
|
|
617
|
+
getKnownIssues,
|
|
618
|
+
getPlatformRules,
|
|
619
|
+
getGenerationRules,
|
|
620
|
+
// Prompt Analysis & Generation
|
|
621
|
+
validateAndGenerate,
|
|
622
|
+
analyzeTestPrompt,
|
|
316
623
|
};
|
|
317
624
|
|