@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,181 @@
1
+ import json
2
+ import argparse
3
+ import sys
4
+ from pathlib import Path
5
+ from typing import Dict, List, Any
6
+
7
+ # This script is expected to be run from the root of the repository.
8
+ # The project context file is located at `.claude/project-context.json`.
9
+ # We construct the path relative to the script's assumed execution location.
10
+ DEFAULT_CONTEXT_PATH = Path(".claude/project-context.json")
11
+
12
+ # Defines the mandatory keys that form the "Context Contract" for each agent.
13
+ AGENT_CONTRACTS: Dict[str, List[str]] = {
14
+ "terraform-architect": [
15
+ "project_details",
16
+ "terraform_infrastructure",
17
+ "operational_guidelines",
18
+ ],
19
+ "gitops-operator": [
20
+ "project_details",
21
+ "gitops_configuration",
22
+ "infrastructure_topology",
23
+ "cluster_details",
24
+ "operational_guidelines",
25
+ ],
26
+ "gcp-troubleshooter": [
27
+ "project_details",
28
+ "infrastructure_topology",
29
+ "terraform_infrastructure",
30
+ "gitops_configuration",
31
+ "application_services",
32
+ "monitoring_observability",
33
+ ],
34
+ "aws-troubleshooter": [
35
+ "project_details",
36
+ "infrastructure_topology",
37
+ "terraform_infrastructure",
38
+ "gitops_configuration",
39
+ "application_services",
40
+ "monitoring_observability",
41
+ ],
42
+ # devops-developer has a more generic contract, often needing the full view.
43
+ "devops-developer": [
44
+ "project_details",
45
+ "application_architecture",
46
+ "application_services",
47
+ "development_standards",
48
+ "operational_guidelines"
49
+ ]
50
+ }
51
+
52
+ def load_project_context(context_path: Path) -> Dict[str, Any]:
53
+ """Loads the project context from the specified JSON file."""
54
+ if not context_path.is_file():
55
+ print(f"Error: Context file not found at {context_path}", file=sys.stderr)
56
+ sys.exit(1)
57
+ with open(context_path, 'r', encoding='utf-8') as f:
58
+ return json.load(f)
59
+
60
+ def get_contract_context(project_context: Dict[str, Any], agent_name: str) -> Dict[str, Any]:
61
+ """Extracts the contract-defined context for a given agent."""
62
+ contract_keys = AGENT_CONTRACTS.get(agent_name)
63
+ if not contract_keys:
64
+ print(
65
+ f"Warning: No contract found for agent '{agent_name}'. Returning empty contract.",
66
+ file=sys.stderr,
67
+ )
68
+ return {}
69
+
70
+ sections = project_context.get("sections", {})
71
+ if not sections:
72
+ raise KeyError("project-context.json must contain a 'sections' object.")
73
+ return {key: sections[key] for key in contract_keys if key in sections}
74
+
75
+
76
+ def get_semantic_enrichment(
77
+ project_context: Dict[str, Any], contract_keys: List[str], user_task: str
78
+ ) -> Dict[str, Any]:
79
+ """
80
+ Performs semantic analysis to find additional relevant context.
81
+
82
+ NOTE: This is a placeholder implementation. A real implementation would use
83
+ vector embeddings (e.g., SentenceTransformers, OpenAI embeddings) to find
84
+ sections of the project_context semantically similar to the user_task.
85
+
86
+ For now, it performs a simple keyword match, excluding keys already in the contract.
87
+ """
88
+ sections = project_context.get("sections", {})
89
+ if not sections:
90
+ raise KeyError("project-context.json must contain a 'sections' object.")
91
+ enrichment: Dict[str, Any] = {}
92
+ contract_key_set = set(contract_keys)
93
+ potential_keys = set(sections.keys()) - contract_key_set
94
+ task_words = {word.strip(".,:;!?") for word in user_task.lower().split()}
95
+
96
+ for key in potential_keys:
97
+ normalized_key = key.lower()
98
+ if normalized_key in task_words:
99
+ enrichment[key] = sections[key]
100
+
101
+ # Light metadata hints when the user asks about freshness/versioning.
102
+ metadata = project_context.get("metadata")
103
+ if metadata and any(word in {"metadata", "version", "updated"} for word in task_words):
104
+ enrichment.setdefault("metadata", metadata)
105
+
106
+ # Heuristic: include matching services based on task wording.
107
+ def normalize_services(raw: Any) -> List[Dict[str, Any]]:
108
+ normalized: List[Dict[str, Any]] = []
109
+ if isinstance(raw, list):
110
+ for svc in raw:
111
+ if isinstance(svc, dict) and svc.get("name"):
112
+ normalized.append(svc)
113
+ elif isinstance(raw, dict):
114
+ for name, payload in raw.items():
115
+ entry = {"name": name}
116
+ if isinstance(payload, dict):
117
+ entry.update(payload)
118
+ normalized.append(entry)
119
+ return normalized
120
+
121
+ candidate_services: List[Dict[str, Any]] = []
122
+ candidate_services.extend(normalize_services(sections.get("application_services")))
123
+
124
+ service_catalog = sections.get("service_catalog")
125
+ if isinstance(service_catalog, dict):
126
+ candidate_services.extend(
127
+ normalize_services(service_catalog.get("applications"))
128
+ )
129
+
130
+ app_arch = sections.get("application_architecture")
131
+ if isinstance(app_arch, dict):
132
+ candidate_services.extend(normalize_services(app_arch.get("services")))
133
+
134
+ if candidate_services and "application_services" not in contract_key_set:
135
+ matched = [
136
+ svc for svc in candidate_services
137
+ if svc.get("name", "").lower() in user_task.lower()
138
+ ]
139
+ if matched:
140
+ enrichment["application_services"] = matched
141
+
142
+ return enrichment
143
+
144
+ def main():
145
+ """Main function to generate and print the context payload."""
146
+ parser = argparse.ArgumentParser(
147
+ description="""
148
+ Generates a structured context payload for a Claude agent based on its contract
149
+ and a semantic analysis of the user's task.
150
+ """
151
+ )
152
+ parser.add_argument("agent_name", choices=AGENT_CONTRACTS.keys(), help="The name of the agent being invoked.")
153
+ parser.add_argument("user_task", help="The user's task or query for the agent.")
154
+ parser.add_argument(
155
+ "--context-file",
156
+ type=Path,
157
+ default=DEFAULT_CONTEXT_PATH,
158
+ help=f"Path to the project-context.json file. Defaults to '{DEFAULT_CONTEXT_PATH}'"
159
+ )
160
+
161
+ args = parser.parse_args()
162
+
163
+ project_context = load_project_context(args.context_file)
164
+
165
+ contract_context = get_contract_context(project_context, args.agent_name)
166
+
167
+ enrichment_context = get_semantic_enrichment(
168
+ project_context,
169
+ list(contract_context.keys()),
170
+ args.user_task
171
+ )
172
+
173
+ final_payload = {
174
+ "contract": contract_context,
175
+ "enrichment": enrichment_context
176
+ }
177
+
178
+ print(json.dumps(final_payload, indent=2))
179
+
180
+ if __name__ == "__main__":
181
+ main()
@@ -0,0 +1,301 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Context Section Reader for Claude Agent System
4
+
5
+ Reads specific sections from project-context.json for selective loading by agents.
6
+ Called by Claude orchestrator BEFORE invoking agents to reduce token usage.
7
+
8
+ Architecture:
9
+ - Claude orchestrator executes this script (NOT agents)
10
+ - Agents receive pre-filtered context in their prompts
11
+ - Reduces token usage by ~70% per agent invocation
12
+
13
+ Usage:
14
+ from .claude.tools.context_section_reader import ContextSectionReader
15
+
16
+ reader = ContextSectionReader()
17
+ context = reader.get_for_agent('gitops-operator')
18
+
19
+ # Pass context to agent in Task tool prompt
20
+ """
21
+
22
+ from pathlib import Path
23
+ from typing import List, Dict, Optional, Any
24
+ import json
25
+
26
+
27
+ def find_claude_dir() -> Path:
28
+ """Find the .claude directory by searching upward from current location"""
29
+ current = Path.cwd()
30
+
31
+ # If we're already in a .claude directory, return it
32
+ if current.name == ".claude":
33
+ return current
34
+
35
+ # Look for .claude in current directory
36
+ claude_dir = current / ".claude"
37
+ if claude_dir.exists():
38
+ return claude_dir
39
+
40
+ # Search upward through parent directories
41
+ for parent in current.parents:
42
+ claude_dir = parent / ".claude"
43
+ if claude_dir.exists():
44
+ return claude_dir
45
+
46
+ # Fallback - raise error if not found
47
+ raise FileNotFoundError(
48
+ "No .claude directory found. Please run from a project directory "
49
+ "or specify context_file explicitly."
50
+ )
51
+
52
+
53
+ class ContextSectionReader:
54
+ """
55
+ Read and filter sections from project-context.json for agent-specific loading.
56
+
57
+ Token Optimization:
58
+ - Without filtering: ~328 lines (1,312 tokens)
59
+ - With filtering: ~80-100 lines (320-400 tokens)
60
+ - Savings: ~70% per agent invocation
61
+ """
62
+
63
+ # Define which sections each agent needs (JSON keys in snake_case)
64
+ AGENT_SECTIONS = {
65
+ 'gitops-operator': [
66
+ 'infrastructure_topology',
67
+ 'gitops_configuration',
68
+ 'operational_guidelines',
69
+ ],
70
+ 'gcp-troubleshooter': [
71
+ 'infrastructure_topology',
72
+ 'operational_guidelines',
73
+ 'monitoring_observability',
74
+ ],
75
+ 'terraform-architect': [
76
+ 'infrastructure_topology',
77
+ 'terraform_infrastructure',
78
+ 'operational_guidelines',
79
+ ],
80
+ 'devops-developer': [
81
+ 'application_architecture',
82
+ 'development_standards',
83
+ 'operational_guidelines',
84
+ ],
85
+ 'aws-troubleshooter': [
86
+ 'infrastructure_topology',
87
+ 'operational_guidelines',
88
+ ]
89
+ }
90
+
91
+ def __init__(self, context_file: Optional[str] = None):
92
+ """
93
+ Initialize reader with project context file.
94
+
95
+ Args:
96
+ context_file: Path to project-context.json (default: searches for .claude/project-context.json)
97
+ """
98
+ if context_file is None:
99
+ # Find the .claude directory by searching upward
100
+ claude_dir = find_claude_dir()
101
+ context_file = claude_dir / "project-context.json"
102
+
103
+ self.path = Path(context_file)
104
+
105
+ if not self.path.exists():
106
+ raise FileNotFoundError(f"Context file not found: {self.path}")
107
+
108
+ with open(self.path, 'r', encoding='utf-8') as f:
109
+ self.data = json.load(f)
110
+
111
+ self._parse_sections()
112
+
113
+ def _parse_sections(self) -> None:
114
+ """Extract sections from JSON data."""
115
+ self.sections: Dict[str, Any] = {}
116
+
117
+ # Extract sections from JSON
118
+ if 'sections' in self.data:
119
+ self.sections = self.data['sections']
120
+ else:
121
+ raise ValueError("Invalid JSON structure: 'sections' key not found")
122
+
123
+ def get_sections(self, section_names: List[str]) -> str:
124
+ """
125
+ Get specific sections as formatted JSON string.
126
+
127
+ Args:
128
+ section_names: List of section names to retrieve
129
+
130
+ Returns:
131
+ Formatted JSON string with requested sections
132
+ """
133
+ result = {}
134
+ missing = []
135
+
136
+ for name in section_names:
137
+ if name in self.sections:
138
+ result[name] = self.sections[name]
139
+ else:
140
+ missing.append(name)
141
+
142
+ if missing:
143
+ print(f"Warning: Sections not found: {missing}")
144
+
145
+ if not result:
146
+ return json.dumps({
147
+ "error": "No sections found",
148
+ "message": "Requested sections were not available."
149
+ }, indent=2)
150
+
151
+ # Format as JSON for agent consumption
152
+ return json.dumps(result, indent=2, ensure_ascii=False)
153
+
154
+ def get_for_agent(self, agent_name: str) -> str:
155
+ """
156
+ Get sections needed by specific agent.
157
+
158
+ Args:
159
+ agent_name: Name of the agent (e.g., 'gitops-operator')
160
+
161
+ Returns:
162
+ Markdown string with agent-specific context
163
+
164
+ Raises:
165
+ ValueError: If agent_name is not recognized
166
+ """
167
+ if agent_name not in self.AGENT_SECTIONS:
168
+ available = ', '.join(self.AGENT_SECTIONS.keys())
169
+ raise ValueError(
170
+ f"Unknown agent: {agent_name}. "
171
+ f"Available agents: {available}"
172
+ )
173
+
174
+ sections = self.AGENT_SECTIONS[agent_name]
175
+ return self.get_sections(sections)
176
+
177
+ def list_sections(self) -> List[str]:
178
+ """Get list of all available sections."""
179
+ return list(self.sections.keys())
180
+
181
+ def get_stats(self) -> Dict[str, Any]:
182
+ """
183
+ Get statistics about the context file.
184
+
185
+ Returns:
186
+ Dictionary with size and token estimates
187
+ """
188
+ # Calculate total JSON size
189
+ total_json = json.dumps(self.data, ensure_ascii=False)
190
+ total_chars = len(total_json)
191
+ total_tokens = total_chars // 4 # Rough estimate: 4 chars per token
192
+
193
+ return {
194
+ 'total_chars': total_chars,
195
+ 'total_tokens_estimated': total_tokens,
196
+ 'total_sections': len(self.sections),
197
+ 'sections': {
198
+ name: {
199
+ 'chars': len(json.dumps(content, ensure_ascii=False)),
200
+ 'tokens_estimated': len(json.dumps(content, ensure_ascii=False)) // 4
201
+ }
202
+ for name, content in self.sections.items()
203
+ }
204
+ }
205
+
206
+ def get_agent_stats(self, agent_name: str) -> Dict[str, Any]:
207
+ """
208
+ Get statistics for a specific agent's context.
209
+
210
+ Args:
211
+ agent_name: Name of the agent
212
+
213
+ Returns:
214
+ Dictionary with character and token counts for agent
215
+ """
216
+ context = self.get_for_agent(agent_name)
217
+ chars = len(context)
218
+ tokens = chars // 4
219
+
220
+ full_stats = self.get_stats()
221
+ savings = {
222
+ 'chars': full_stats['total_chars'] - chars,
223
+ 'tokens': full_stats['total_tokens_estimated'] - tokens,
224
+ 'percentage': round((1 - chars / full_stats['total_chars']) * 100, 1)
225
+ }
226
+
227
+ return {
228
+ 'agent': agent_name,
229
+ 'chars_loaded': chars,
230
+ 'tokens_estimated': tokens,
231
+ 'savings': savings
232
+ }
233
+
234
+
235
+ def main():
236
+ """CLI interface for testing and debugging."""
237
+ import sys
238
+ import json
239
+
240
+ reader = ContextSectionReader()
241
+
242
+ if len(sys.argv) < 2:
243
+ print("Context Section Reader")
244
+ print("\nUsage:")
245
+ print(" python context_section_reader.py <command> [args]")
246
+ print("\nCommands:")
247
+ print(" list - List all available sections")
248
+ print(" stats - Show statistics for context file")
249
+ print(" agent <name> - Get context for specific agent")
250
+ print(" agent-stats <name> - Show stats for agent's context")
251
+ print(" sections <name1> <name2> - Get specific sections")
252
+ print("\nAvailable agents:")
253
+ for agent in reader.AGENT_SECTIONS.keys():
254
+ print(f" - {agent}")
255
+ sys.exit(0)
256
+
257
+ command = sys.argv[1]
258
+
259
+ if command == 'list':
260
+ print("Available sections:")
261
+ for section in reader.list_sections():
262
+ print(f" - {section}")
263
+
264
+ elif command == 'stats':
265
+ stats = reader.get_stats()
266
+ print(json.dumps(stats, indent=2))
267
+
268
+ elif command == 'agent':
269
+ if len(sys.argv) < 3:
270
+ print("Error: Agent name required")
271
+ sys.exit(1)
272
+
273
+ agent_name = sys.argv[2]
274
+ context = reader.get_for_agent(agent_name)
275
+ print(context)
276
+
277
+ elif command == 'agent-stats':
278
+ if len(sys.argv) < 3:
279
+ print("Error: Agent name required")
280
+ sys.exit(1)
281
+
282
+ agent_name = sys.argv[2]
283
+ stats = reader.get_agent_stats(agent_name)
284
+ print(json.dumps(stats, indent=2))
285
+
286
+ elif command == 'sections':
287
+ if len(sys.argv) < 3:
288
+ print("Error: Section names required")
289
+ sys.exit(1)
290
+
291
+ section_names = sys.argv[2:]
292
+ context = reader.get_sections(section_names)
293
+ print(context)
294
+
295
+ else:
296
+ print(f"Unknown command: {command}")
297
+ sys.exit(1)
298
+
299
+
300
+ if __name__ == '__main__':
301
+ main()
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Demo script for the Clarification Engine
4
+
5
+ Shows how the clarification system works with different types of prompts.
6
+ """
7
+
8
+ import sys
9
+ import os
10
+ sys.path.insert(0, os.path.dirname(__file__))
11
+
12
+ from clarify_engine import request_clarification
13
+
14
+
15
+ def demo_prompt(prompt: str, description: str = ""):
16
+ """Test a single prompt and display results."""
17
+ print("=" * 70)
18
+ if description:
19
+ print(f"📝 {description}")
20
+ print(f"Prompt: \"{prompt}\"")
21
+ print("=" * 70)
22
+
23
+ result = request_clarification(prompt)
24
+
25
+ if not result["needs_clarification"]:
26
+ print("✅ No clarification needed - prompt is specific enough\n")
27
+ return
28
+
29
+ print(f"⚠️ Clarification needed (score: {result['clarification_context']['ambiguity_analysis']['ambiguity_score']}/100)\n")
30
+
31
+ # Show summary
32
+ print(result["summary"])
33
+ print()
34
+
35
+ # Show questions
36
+ for i, question in enumerate(result["question_config"]["questions"]):
37
+ print(f"\n{'─' * 70}")
38
+ print(f"{question['header']}")
39
+ print(f"{'─' * 70}")
40
+ print(f"❓ {question['question']}\n")
41
+
42
+ for j, option in enumerate(question["options"], 1):
43
+ print(f" {j}. {option['label']}")
44
+ print(f" → {option['description']}\n")
45
+
46
+ print()
47
+
48
+
49
+ def main():
50
+ """Run demo with various prompt types."""
51
+
52
+ print("\n" + "🎯" * 35)
53
+ print(" CLARIFICATION ENGINE - DEMO")
54
+ print("🎯" * 35 + "\n")
55
+
56
+ # Test 1: Ambiguous service
57
+ demo_prompt(
58
+ "Check the API",
59
+ "Test 1: Ambiguous service reference"
60
+ )
61
+
62
+ # Test 2: Specific service (no clarification)
63
+ demo_prompt(
64
+ "Check tcm-api service status",
65
+ "Test 2: Specific service (should NOT need clarification)"
66
+ )
67
+
68
+ # Test 3: Environment mismatch
69
+ demo_prompt(
70
+ "Deploy to production",
71
+ "Test 3: Environment mismatch warning"
72
+ )
73
+
74
+ # Test 4: Namespace ambiguity
75
+ demo_prompt(
76
+ "Deploy to the cluster",
77
+ "Test 4: Namespace ambiguity"
78
+ )
79
+
80
+ # Test 5: Multiple ambiguities
81
+ demo_prompt(
82
+ "Deploy the API to the cluster",
83
+ "Test 5: Multiple ambiguities (service + namespace)"
84
+ )
85
+
86
+ # Test 6: Spanish prompt
87
+ demo_prompt(
88
+ "Chequea el servicio",
89
+ "Test 6: Spanish keywords"
90
+ )
91
+
92
+ # Test 7: Redis resource
93
+ demo_prompt(
94
+ "Check the Redis instance",
95
+ "Test 7: Resource ambiguity (Redis)"
96
+ )
97
+
98
+ print("\n" + "✅" * 35)
99
+ print(" DEMO COMPLETE")
100
+ print("✅" * 35 + "\n")
101
+
102
+
103
+ if __name__ == "__main__":
104
+ main()