@jaguilar87/gaia-ops 1.0.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.
Files changed (91) hide show
  1. package/CHANGELOG.md +315 -0
  2. package/CLAUDE.md +154 -0
  3. package/LICENSE +21 -0
  4. package/README.md +221 -0
  5. package/agents/aws-troubleshooter.md +50 -0
  6. package/agents/claude-architect.md +821 -0
  7. package/agents/devops-developer.md +92 -0
  8. package/agents/gcp-troubleshooter.md +50 -0
  9. package/agents/gitops-operator.md +360 -0
  10. package/agents/terraform-architect.md +289 -0
  11. package/bin/gaia-init.js +620 -0
  12. package/commands/architect.md +97 -0
  13. package/commands/restore-session.md +87 -0
  14. package/commands/save-session.md +88 -0
  15. package/commands/session-status.md +61 -0
  16. package/commands/speckit.add-task.md +144 -0
  17. package/commands/speckit.analyze-task.md +65 -0
  18. package/commands/speckit.implement.md +96 -0
  19. package/commands/speckit.init.md +237 -0
  20. package/commands/speckit.plan.md +88 -0
  21. package/commands/speckit.specify.md +161 -0
  22. package/commands/speckit.tasks.md +188 -0
  23. package/config/AGENTS.md +162 -0
  24. package/config/agent-catalog.md +604 -0
  25. package/config/context-contracts.md +682 -0
  26. package/config/git-standards.md +674 -0
  27. package/config/git_standards.json +69 -0
  28. package/config/orchestration-workflow.md +735 -0
  29. package/hooks/__pycache__/post_tool_use.cpython-312.pyc +0 -0
  30. package/hooks/__pycache__/pre_kubectl_security.cpython-312.pyc +0 -0
  31. package/hooks/__pycache__/pre_tool_use.cpython-312.pyc +0 -0
  32. package/hooks/__pycache__/session_start.cpython-312.pyc +0 -0
  33. package/hooks/__pycache__/subagent_stop.cpython-312.pyc +0 -0
  34. package/hooks/post_tool_use.py +463 -0
  35. package/hooks/pre_kubectl_security.py +205 -0
  36. package/hooks/pre_tool_use.py +530 -0
  37. package/hooks/session_start.py +315 -0
  38. package/hooks/subagent_stop.py +549 -0
  39. package/index.js +92 -0
  40. package/package.json +59 -0
  41. package/speckit/README.en.md +648 -0
  42. package/speckit/README.md +353 -0
  43. package/speckit/governance.md +169 -0
  44. package/speckit/scripts/check-prerequisites.sh +194 -0
  45. package/speckit/scripts/common.sh +126 -0
  46. package/speckit/scripts/create-new-feature.sh +131 -0
  47. package/speckit/scripts/init.sh +42 -0
  48. package/speckit/scripts/setup-plan.sh +95 -0
  49. package/speckit/scripts/update-agent-context.sh +718 -0
  50. package/speckit/templates/adr-template.md +118 -0
  51. package/speckit/templates/agent-file-template.md +23 -0
  52. package/speckit/templates/plan-template.md +233 -0
  53. package/speckit/templates/spec-template.md +116 -0
  54. package/speckit/templates/tasks-template-bkp.md +136 -0
  55. package/speckit/templates/tasks-template.md +345 -0
  56. package/templates/CLAUDE.template.md +170 -0
  57. package/templates/code-examples/approval_gate_workflow.py +141 -0
  58. package/templates/code-examples/clarification_workflow.py +94 -0
  59. package/templates/code-examples/commit_validation.py +86 -0
  60. package/templates/project-context.template.json +126 -0
  61. package/templates/settings.template.json +307 -0
  62. package/tools/__pycache__/agent_router.cpython-312.pyc +0 -0
  63. package/tools/__pycache__/approval_gate.cpython-312.pyc +0 -0
  64. package/tools/__pycache__/clarify_engine.cpython-312.pyc +0 -0
  65. package/tools/__pycache__/clarify_patterns.cpython-312.pyc +0 -0
  66. package/tools/__pycache__/commit_validator.cpython-312.pyc +0 -0
  67. package/tools/__pycache__/context_section_reader.cpython-312.pyc +0 -0
  68. package/tools/__pycache__/routing_dashboard.cpython-312.pyc +0 -0
  69. package/tools/__pycache__/routing_feedback.cpython-312.pyc +0 -0
  70. package/tools/__pycache__/semantic_matcher.cpython-312.pyc +0 -0
  71. package/tools/__pycache__/task_manager.cpython-312.pyc +0 -0
  72. package/tools/agent_capabilities.json +231 -0
  73. package/tools/agent_invoker_helper.py +239 -0
  74. package/tools/agent_router.py +730 -0
  75. package/tools/approval_gate.py +318 -0
  76. package/tools/clarify_engine.py +511 -0
  77. package/tools/clarify_patterns.py +356 -0
  78. package/tools/commit_validator.py +338 -0
  79. package/tools/context_provider.py +181 -0
  80. package/tools/context_section_reader.py +301 -0
  81. package/tools/demo_clarify.py +104 -0
  82. package/tools/generate_embeddings.py +168 -0
  83. package/tools/quicktriage_aws_troubleshooter.sh +45 -0
  84. package/tools/quicktriage_devops_developer.sh +38 -0
  85. package/tools/quicktriage_gcp_troubleshooter.sh +51 -0
  86. package/tools/quicktriage_gitops_operator.sh +47 -0
  87. package/tools/quicktriage_terraform_architect.sh +40 -0
  88. package/tools/semantic_matcher.py +222 -0
  89. package/tools/task_manager.py +547 -0
  90. package/tools/task_manager_README.md +395 -0
  91. package/tools/task_manager_example.py +215 -0
@@ -0,0 +1,511 @@
1
+ """
2
+ Intelligent Clarification Engine
3
+
4
+ Detects ambiguity in user prompts and asks targeted questions
5
+ before agent routing. Follows the approval_gate.py pattern.
6
+ """
7
+
8
+ import json
9
+ import os
10
+ from typing import Dict, Any, List, Optional
11
+ from datetime import datetime
12
+ from difflib import get_close_matches
13
+
14
+
15
+ class ClarificationEngine:
16
+ """
17
+ Analyzes user prompts for ambiguity and generates intelligent
18
+ clarification questions using project-context.json.
19
+
20
+ Design Pattern: Modeled after approval_gate.py
21
+ """
22
+
23
+ def __init__(self, project_context_path: str = ".claude/project-context.json"):
24
+ self.project_context_path = project_context_path
25
+ self.project_context = self._load_project_context()
26
+ self.clarification_log_path = ".claude/logs/clarifications.jsonl"
27
+ self.config = self._load_config()
28
+ self._ensure_log_directory()
29
+
30
+ # ========================================================================
31
+ # PUBLIC API (Orchestrator Entry Points)
32
+ # ========================================================================
33
+
34
+ def detect_ambiguity(
35
+ self,
36
+ user_prompt: str,
37
+ command_context: Optional[Dict[str, Any]] = None
38
+ ) -> Dict[str, Any]:
39
+ """
40
+ Analyze user prompt for ambiguity.
41
+
42
+ Args:
43
+ user_prompt: Original user request
44
+ command_context: Optional context (e.g., Spec-Kit command type)
45
+
46
+ Returns:
47
+ {
48
+ "needs_clarification": bool,
49
+ "ambiguity_score": int (0-100),
50
+ "ambiguity_points": List[Dict], # What's ambiguous
51
+ "suggested_questions": List[str] # What to ask
52
+ }
53
+ """
54
+ # Import patterns here to avoid circular dependency
55
+ from clarify_patterns import detect_all_ambiguities
56
+
57
+ # Step 1: Detect all ambiguities using patterns
58
+ ambiguities = detect_all_ambiguities(user_prompt, self.project_context)
59
+
60
+ # Step 2: Filter by command context (if specified)
61
+ if command_context and "command" in command_context:
62
+ command_name = command_context["command"]
63
+ if command_name in self.config["command_rules"]:
64
+ command_rules = self.config["command_rules"][command_name]
65
+ if not command_rules.get("enabled", True):
66
+ # Clarification disabled for this command
67
+ return {
68
+ "needs_clarification": False,
69
+ "ambiguity_score": 0,
70
+ "ambiguity_points": [],
71
+ "suggested_questions": []
72
+ }
73
+
74
+ # Filter by allowed patterns
75
+ allowed_patterns = command_rules.get("patterns", [])
76
+ if allowed_patterns:
77
+ ambiguities = [
78
+ amb for amb in ambiguities
79
+ if amb["pattern"] in allowed_patterns
80
+ ]
81
+
82
+ # Step 3: Calculate overall ambiguity score
83
+ ambiguity_score = self._calculate_ambiguity_score(ambiguities)
84
+
85
+ # Step 4: Get threshold from config
86
+ threshold = self.config["global_settings"]["ambiguity_threshold"]
87
+
88
+ # Step 5: Determine if clarification is needed
89
+ needs_clarification = ambiguity_score > threshold
90
+
91
+ return {
92
+ "needs_clarification": needs_clarification,
93
+ "ambiguity_score": ambiguity_score,
94
+ "ambiguity_points": ambiguities,
95
+ "suggested_questions": [a["suggested_question"] for a in ambiguities]
96
+ }
97
+
98
+ def generate_questions(
99
+ self,
100
+ ambiguity_analysis: Dict[str, Any],
101
+ max_questions: int = 5
102
+ ) -> Dict[str, Any]:
103
+ """
104
+ Generate AskUserQuestion configuration.
105
+
106
+ Args:
107
+ ambiguity_analysis: Output from detect_ambiguity()
108
+ max_questions: Maximum number of questions (default 5)
109
+
110
+ Returns:
111
+ {
112
+ "summary": str, # Markdown summary for user
113
+ "question_config": Dict, # For AskUserQuestion tool
114
+ "clarification_context": Dict # For enrich_prompt()
115
+ }
116
+ """
117
+ ambiguities = ambiguity_analysis["ambiguity_points"][:max_questions]
118
+
119
+ # Step 1: Generate summary
120
+ summary = self._generate_summary(ambiguities, ambiguity_analysis["ambiguity_score"])
121
+
122
+ # Step 2: Build questions for AskUserQuestion
123
+ questions = []
124
+ for i, ambiguity in enumerate(ambiguities):
125
+ question = self._build_question(ambiguity, i+1)
126
+ questions.append(question)
127
+
128
+ # Step 3: Build AskUserQuestion config
129
+ question_config = {
130
+ "questions": questions
131
+ }
132
+
133
+ # Step 4: Store context for enrich_prompt()
134
+ clarification_context = {
135
+ "ambiguity_analysis": ambiguity_analysis,
136
+ "questions_asked": len(questions),
137
+ "ambiguities": ambiguities
138
+ }
139
+
140
+ return {
141
+ "summary": summary,
142
+ "question_config": question_config,
143
+ "clarification_context": clarification_context
144
+ }
145
+
146
+ def enrich_prompt(
147
+ self,
148
+ original_prompt: str,
149
+ user_responses: Dict[str, Any],
150
+ clarification_context: Dict[str, Any]
151
+ ) -> str:
152
+ """
153
+ Merge user responses into original prompt.
154
+
155
+ Example:
156
+ Original: "Check the API"
157
+ Response: {"question_1": "📦 tcm-api"}
158
+ Enriched: "Check the API (tcm-api service in tcm-non-prod namespace)"
159
+ """
160
+ enriched = original_prompt
161
+
162
+ # Extract responses
163
+ for question_id, answer in user_responses.items():
164
+ question_index = int(question_id.split("_")[1]) - 1
165
+
166
+ if question_index >= len(clarification_context["ambiguities"]):
167
+ continue
168
+
169
+ ambiguity = clarification_context["ambiguities"][question_index]
170
+
171
+ # Clean answer (remove emoji if present)
172
+ clean_answer = self._clean_answer(answer)
173
+
174
+ # Validate answer against available options
175
+ validated_answer = self._validate_answer(clean_answer, ambiguity)
176
+
177
+ # Append clarification to prompt
178
+ clarification_note = f"\n\n[Clarification - {ambiguity['pattern']}]: {validated_answer}"
179
+ enriched += clarification_note
180
+
181
+ return enriched
182
+
183
+ def log_clarification(
184
+ self,
185
+ original_prompt: str,
186
+ enriched_prompt: str,
187
+ ambiguity_analysis: Dict[str, Any],
188
+ user_responses: Dict[str, Any]
189
+ ):
190
+ """Log clarification decision for audit trail."""
191
+ log_entry = {
192
+ "timestamp": datetime.now().isoformat(),
193
+ "original_prompt": original_prompt,
194
+ "enriched_prompt": enriched_prompt,
195
+ "ambiguity_score": ambiguity_analysis["ambiguity_score"],
196
+ "questions_asked": len(ambiguity_analysis["ambiguity_points"]),
197
+ "patterns_detected": [a["pattern"] for a in ambiguity_analysis["ambiguity_points"]],
198
+ "user_responses": user_responses
199
+ }
200
+
201
+ with open(self.clarification_log_path, "a") as f:
202
+ f.write(json.dumps(log_entry) + "\n")
203
+
204
+ # ========================================================================
205
+ # PRIVATE METHODS (Internal Logic)
206
+ # ========================================================================
207
+
208
+ def _load_project_context(self) -> Dict[str, Any]:
209
+ """Load project-context.json."""
210
+ if not os.path.exists(self.project_context_path):
211
+ return {"sections": {}}
212
+
213
+ with open(self.project_context_path, "r") as f:
214
+ return json.load(f)
215
+
216
+ def _load_config(self) -> Dict[str, Any]:
217
+ """Load clarification_rules.json."""
218
+ config_path = ".claude/config/clarification_rules.json"
219
+ if not os.path.exists(config_path):
220
+ # Return default config
221
+ return {
222
+ "global_settings": {
223
+ "enabled": True,
224
+ "ambiguity_threshold": 30,
225
+ "max_questions_per_cycle": 5
226
+ },
227
+ "command_rules": {},
228
+ "question_templates": {},
229
+ "emoji_map": {}
230
+ }
231
+
232
+ with open(config_path, "r") as f:
233
+ return json.load(f)
234
+
235
+ def _ensure_log_directory(self):
236
+ """Ensure logs directory exists."""
237
+ os.makedirs(os.path.dirname(self.clarification_log_path), exist_ok=True)
238
+
239
+ def _calculate_ambiguity_score(self, ambiguities: List[Dict]) -> int:
240
+ """Calculate overall ambiguity score (0-100)."""
241
+ if not ambiguities:
242
+ return 0
243
+ # Weighted average of top 3 ambiguities
244
+ top_weights = [a["weight"] for a in ambiguities[:3]]
245
+ return int(sum(top_weights) / len(top_weights))
246
+
247
+ def _generate_summary(self, ambiguities: List[Dict], ambiguity_score: int) -> str:
248
+ """Generate human-readable summary of ambiguities."""
249
+ lines = []
250
+ lines.append("=" * 60)
251
+ lines.append("🔍 CLARIFICACIÓN NECESARIA")
252
+ lines.append("=" * 60)
253
+ lines.append("")
254
+ lines.append(f"**Score de ambigüedad:** {ambiguity_score}/100")
255
+ lines.append("")
256
+ lines.append("He detectado las siguientes ambigüedades en tu solicitud:")
257
+ lines.append("")
258
+
259
+ for i, amb in enumerate(ambiguities):
260
+ lines.append(f"**{i+1}. {amb['ambiguity_reason']}**")
261
+ options_preview = amb['available_options'][:5]
262
+ if len(amb['available_options']) > 5:
263
+ options_preview_str = ", ".join(options_preview) + f" (y {len(amb['available_options']) - 5} más)"
264
+ else:
265
+ options_preview_str = ", ".join(options_preview)
266
+ lines.append(f" Opciones disponibles: {options_preview_str}")
267
+ lines.append("")
268
+
269
+ lines.append("Por favor, responde las siguientes preguntas para continuar:")
270
+ lines.append("=" * 60)
271
+
272
+ return "\n".join(lines)
273
+
274
+ def _build_question(self, ambiguity: Dict[str, Any], question_num: int) -> Dict[str, Any]:
275
+ """
276
+ Build question with 3-4 rich options.
277
+
278
+ This implements the optimized UX pattern:
279
+ - Emoji for visual scanning
280
+ - Short label (2-4 words)
281
+ - Rich description with metadata
282
+ - Max 4 options (3 specific + 1 catch-all)
283
+ """
284
+ # Select appropriate emoji based on pattern
285
+ emoji_map = self.config.get("emoji_map", {})
286
+ pattern_name = ambiguity["pattern"].split("_")[0] # "service" from "service_ambiguity"
287
+ emoji = emoji_map.get(pattern_name, "❓")
288
+
289
+ # Build options with rich descriptions
290
+ options = []
291
+ available_options = ambiguity["available_options"][:3] # Max 3 specific options
292
+
293
+ for option_name in available_options:
294
+ # Fetch metadata from project_context
295
+ metadata = self._get_option_metadata(option_name, ambiguity)
296
+
297
+ options.append({
298
+ "label": f"{emoji} {option_name}",
299
+ "description": metadata
300
+ })
301
+
302
+ # Add catch-all 4th option if there are more than 3 options
303
+ if len(ambiguity["available_options"]) > 3:
304
+ total_count = len(ambiguity["available_options"])
305
+ resource_type = pattern_name # "service", "namespace", "resource"
306
+
307
+ options.append({
308
+ "label": f"{emoji_map.get('all', '🌐')} Todos los {resource_type}s",
309
+ "description": f"Aplicar a todos los recursos ({total_count} total)"
310
+ })
311
+
312
+ return {
313
+ "question": ambiguity["suggested_question"],
314
+ "header": f"{emoji} Pregunta {question_num}",
315
+ "multiSelect": ambiguity.get("allow_multiple", False),
316
+ "options": options
317
+ }
318
+
319
+ def _get_option_metadata(self, option_name: str, ambiguity: Dict[str, Any]) -> str:
320
+ """
321
+ Fetch rich metadata for option from project-context.json.
322
+
323
+ This generates the detailed description shown under each option.
324
+ """
325
+ pattern = ambiguity["pattern"]
326
+
327
+ # Service metadata
328
+ if pattern == "service_ambiguity" and "services_metadata" in ambiguity:
329
+ svc = ambiguity["services_metadata"].get(option_name, {})
330
+ tech_stack = svc.get("tech_stack", "N/A")
331
+ namespace = svc.get("namespace", "N/A")
332
+ port = svc.get("port", "N/A")
333
+ status = svc.get("status", "unknown")
334
+ status_emoji = "✅" if status == "running" else "⏸️"
335
+
336
+ return f"{tech_stack} | Namespace: {namespace} | Puerto: {port} | Estado: {status_emoji} {status}"
337
+
338
+ # Namespace metadata
339
+ elif pattern == "namespace_ambiguity" and "namespace_metadata" in ambiguity:
340
+ ns_meta = ambiguity["namespace_metadata"].get(option_name, {})
341
+ services = ns_meta.get("services", [])
342
+ service_count = ns_meta.get("service_count", 0)
343
+ services_str = ", ".join(services[:3])
344
+ if len(services) > 3:
345
+ services_str += f" (y {len(services) - 3} más)"
346
+
347
+ return f"Servicios: {services_str} | Total: {service_count} servicios"
348
+
349
+ # Environment metadata
350
+ elif pattern == "environment_ambiguity":
351
+ current_env = ambiguity.get("current_environment", "unknown")
352
+ if "Continuar" in option_name:
353
+ return f"Proceder con el entorno actual ({current_env}). Operación segura."
354
+ elif "Detener" in option_name:
355
+ return f"Cancelar para configurar el proyecto correctamente primero"
356
+ elif "Forzar" in option_name:
357
+ return f"⚠️ Proceder en producción AUNQUE el contexto diga {current_env}. Solo si estás seguro."
358
+
359
+ # Resource metadata
360
+ elif pattern == "resource_ambiguity" and "resource_metadata" in ambiguity:
361
+ res_meta = ambiguity["resource_metadata"].get(option_name, {})
362
+ res_type = res_meta.get("type", "N/A")
363
+ status = res_meta.get("status", "unknown")
364
+ tier = res_meta.get("tier", "N/A")
365
+ namespace = res_meta.get("namespace", "")
366
+
367
+ parts = [res_type]
368
+ if tier != "N/A":
369
+ parts.append(f"Tier: {tier}")
370
+ if namespace:
371
+ parts.append(f"Namespace: {namespace}")
372
+ parts.append(f"Estado: {status}")
373
+
374
+ return " | ".join(parts)
375
+
376
+ # Default fallback
377
+ return f"Seleccionar: {option_name}"
378
+
379
+ def _clean_answer(self, answer: str) -> str:
380
+ """Remove emoji and extra formatting from user's answer."""
381
+ # Remove common emoji
382
+ emoji_list = ["📦", "🎯", "🔧", "⚠️", "🌐", "✅", "❌", "🗄️", "🔴"]
383
+ cleaned = answer
384
+ for emoji in emoji_list:
385
+ cleaned = cleaned.replace(emoji, "")
386
+ return cleaned.strip()
387
+
388
+ def _validate_answer(self, answer: str, ambiguity: Dict[str, Any]) -> str:
389
+ """
390
+ Validate user's answer against available options.
391
+
392
+ Implements fuzzy matching to handle variations like:
393
+ - "tcm api" → "tcm-api"
394
+ - "todos" → "Todos los servicios"
395
+ """
396
+ available_options = ambiguity["available_options"]
397
+
398
+ # Exact match (after cleaning)
399
+ if answer in available_options:
400
+ return answer
401
+
402
+ # Fuzzy match
403
+ matches = get_close_matches(answer.lower(),
404
+ [opt.lower() for opt in available_options],
405
+ n=1, cutoff=0.6)
406
+
407
+ if matches:
408
+ # Find original option (with proper casing)
409
+ for opt in available_options:
410
+ if opt.lower() == matches[0]:
411
+ return opt
412
+
413
+ # Check for "all" / "todos" keywords
414
+ if any(keyword in answer.lower() for keyword in ["todos", "all", "ambos", "both"]):
415
+ # User wants all options
416
+ return f"Todos ({', '.join(available_options)})"
417
+
418
+ # No match - return original answer
419
+ return answer
420
+
421
+
422
+ # ============================================================================
423
+ # CONVENIENCE FUNCTIONS (Orchestrator API)
424
+ # ============================================================================
425
+
426
+ def request_clarification(
427
+ user_prompt: str,
428
+ command_context: Optional[Dict[str, Any]] = None
429
+ ) -> Dict[str, Any]:
430
+ """
431
+ Main function to request clarification from user.
432
+
433
+ This should be called by the orchestrator in Phase 0.
434
+
435
+ Args:
436
+ user_prompt: Original user request
437
+ command_context: Optional context (e.g., {"command": "speckit.init"})
438
+
439
+ Returns:
440
+ {
441
+ "needs_clarification": bool,
442
+ "summary": str, # Markdown summary (if clarification needed)
443
+ "question_config": Dict, # For AskUserQuestion tool
444
+ "engine_instance": ClarificationEngine, # For process_clarification()
445
+ "clarification_context": Dict # For enrich_prompt()
446
+ }
447
+ """
448
+ engine = ClarificationEngine()
449
+ ambiguity = engine.detect_ambiguity(user_prompt, command_context)
450
+
451
+ if not ambiguity["needs_clarification"]:
452
+ return {
453
+ "needs_clarification": False,
454
+ "enriched_prompt": user_prompt # No changes
455
+ }
456
+
457
+ questions = engine.generate_questions(ambiguity)
458
+
459
+ return {
460
+ "needs_clarification": True,
461
+ "summary": questions["summary"],
462
+ "question_config": questions["question_config"],
463
+ "engine_instance": engine,
464
+ "clarification_context": questions["clarification_context"],
465
+ "original_prompt": user_prompt
466
+ }
467
+
468
+
469
+ def process_clarification(
470
+ engine_instance: ClarificationEngine,
471
+ original_prompt: str,
472
+ user_responses: Dict[str, Any],
473
+ clarification_context: Dict[str, Any]
474
+ ) -> Dict[str, Any]:
475
+ """
476
+ Process user's clarification responses.
477
+
478
+ Args:
479
+ engine_instance: The ClarificationEngine instance from request_clarification
480
+ original_prompt: Original user request
481
+ user_responses: Response from AskUserQuestion
482
+ clarification_context: Context from request_clarification
483
+
484
+ Returns:
485
+ {
486
+ "enriched_prompt": str, # Enriched prompt for agent_router.py
487
+ "clarification_log": Dict # Log entry
488
+ }
489
+ """
490
+ enriched_prompt = engine_instance.enrich_prompt(
491
+ original_prompt,
492
+ user_responses,
493
+ clarification_context
494
+ )
495
+
496
+ # Log the clarification
497
+ engine_instance.log_clarification(
498
+ original_prompt,
499
+ enriched_prompt,
500
+ clarification_context["ambiguity_analysis"],
501
+ user_responses
502
+ )
503
+
504
+ return {
505
+ "enriched_prompt": enriched_prompt,
506
+ "clarification_log": {
507
+ "timestamp": datetime.now().isoformat(),
508
+ "original_prompt": original_prompt,
509
+ "enriched_prompt": enriched_prompt
510
+ }
511
+ }