@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.
- package/CHANGELOG.md +315 -0
- package/CLAUDE.md +154 -0
- package/LICENSE +21 -0
- package/README.md +221 -0
- package/agents/aws-troubleshooter.md +50 -0
- package/agents/claude-architect.md +821 -0
- package/agents/devops-developer.md +92 -0
- package/agents/gcp-troubleshooter.md +50 -0
- package/agents/gitops-operator.md +360 -0
- package/agents/terraform-architect.md +289 -0
- package/bin/gaia-init.js +620 -0
- package/commands/architect.md +97 -0
- package/commands/restore-session.md +87 -0
- package/commands/save-session.md +88 -0
- package/commands/session-status.md +61 -0
- package/commands/speckit.add-task.md +144 -0
- package/commands/speckit.analyze-task.md +65 -0
- package/commands/speckit.implement.md +96 -0
- package/commands/speckit.init.md +237 -0
- package/commands/speckit.plan.md +88 -0
- package/commands/speckit.specify.md +161 -0
- package/commands/speckit.tasks.md +188 -0
- package/config/AGENTS.md +162 -0
- package/config/agent-catalog.md +604 -0
- package/config/context-contracts.md +682 -0
- package/config/git-standards.md +674 -0
- package/config/git_standards.json +69 -0
- package/config/orchestration-workflow.md +735 -0
- package/hooks/__pycache__/post_tool_use.cpython-312.pyc +0 -0
- package/hooks/__pycache__/pre_kubectl_security.cpython-312.pyc +0 -0
- package/hooks/__pycache__/pre_tool_use.cpython-312.pyc +0 -0
- package/hooks/__pycache__/session_start.cpython-312.pyc +0 -0
- package/hooks/__pycache__/subagent_stop.cpython-312.pyc +0 -0
- package/hooks/post_tool_use.py +463 -0
- package/hooks/pre_kubectl_security.py +205 -0
- package/hooks/pre_tool_use.py +530 -0
- package/hooks/session_start.py +315 -0
- package/hooks/subagent_stop.py +549 -0
- package/index.js +92 -0
- package/package.json +59 -0
- package/speckit/README.en.md +648 -0
- package/speckit/README.md +353 -0
- package/speckit/governance.md +169 -0
- package/speckit/scripts/check-prerequisites.sh +194 -0
- package/speckit/scripts/common.sh +126 -0
- package/speckit/scripts/create-new-feature.sh +131 -0
- package/speckit/scripts/init.sh +42 -0
- package/speckit/scripts/setup-plan.sh +95 -0
- package/speckit/scripts/update-agent-context.sh +718 -0
- package/speckit/templates/adr-template.md +118 -0
- package/speckit/templates/agent-file-template.md +23 -0
- package/speckit/templates/plan-template.md +233 -0
- package/speckit/templates/spec-template.md +116 -0
- package/speckit/templates/tasks-template-bkp.md +136 -0
- package/speckit/templates/tasks-template.md +345 -0
- package/templates/CLAUDE.template.md +170 -0
- package/templates/code-examples/approval_gate_workflow.py +141 -0
- package/templates/code-examples/clarification_workflow.py +94 -0
- package/templates/code-examples/commit_validation.py +86 -0
- package/templates/project-context.template.json +126 -0
- package/templates/settings.template.json +307 -0
- package/tools/__pycache__/agent_router.cpython-312.pyc +0 -0
- package/tools/__pycache__/approval_gate.cpython-312.pyc +0 -0
- package/tools/__pycache__/clarify_engine.cpython-312.pyc +0 -0
- package/tools/__pycache__/clarify_patterns.cpython-312.pyc +0 -0
- package/tools/__pycache__/commit_validator.cpython-312.pyc +0 -0
- package/tools/__pycache__/context_section_reader.cpython-312.pyc +0 -0
- package/tools/__pycache__/routing_dashboard.cpython-312.pyc +0 -0
- package/tools/__pycache__/routing_feedback.cpython-312.pyc +0 -0
- package/tools/__pycache__/semantic_matcher.cpython-312.pyc +0 -0
- package/tools/__pycache__/task_manager.cpython-312.pyc +0 -0
- package/tools/agent_capabilities.json +231 -0
- package/tools/agent_invoker_helper.py +239 -0
- package/tools/agent_router.py +730 -0
- package/tools/approval_gate.py +318 -0
- package/tools/clarify_engine.py +511 -0
- package/tools/clarify_patterns.py +356 -0
- package/tools/commit_validator.py +338 -0
- package/tools/context_provider.py +181 -0
- package/tools/context_section_reader.py +301 -0
- package/tools/demo_clarify.py +104 -0
- package/tools/generate_embeddings.py +168 -0
- package/tools/quicktriage_aws_troubleshooter.sh +45 -0
- package/tools/quicktriage_devops_developer.sh +38 -0
- package/tools/quicktriage_gcp_troubleshooter.sh +51 -0
- package/tools/quicktriage_gitops_operator.sh +47 -0
- package/tools/quicktriage_terraform_architect.sh +40 -0
- package/tools/semantic_matcher.py +222 -0
- package/tools/task_manager.py +547 -0
- package/tools/task_manager_README.md +395 -0
- package/tools/task_manager_example.py +215 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Approval Gate Enforcement
|
|
3
|
+
|
|
4
|
+
Ensures that no realization occurs without explicit user approval.
|
|
5
|
+
This is a CRITICAL component of the Two-Phase Workflow.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
from typing import Dict, Any, Optional
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ApprovalGate:
|
|
15
|
+
"""
|
|
16
|
+
Enforces approval gate before any realization actions.
|
|
17
|
+
|
|
18
|
+
CRITICAL: This gate is MANDATORY in the workflow. No realization
|
|
19
|
+
can proceed without explicit user approval via AskUserQuestion.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self):
|
|
23
|
+
self.approval_log_path = ".claude/logs/approvals.jsonl"
|
|
24
|
+
self._ensure_log_directory()
|
|
25
|
+
|
|
26
|
+
def _ensure_log_directory(self):
|
|
27
|
+
"""Ensure the logs directory exists."""
|
|
28
|
+
log_dir = os.path.dirname(self.approval_log_path)
|
|
29
|
+
os.makedirs(log_dir, exist_ok=True)
|
|
30
|
+
|
|
31
|
+
def generate_approval_question(
|
|
32
|
+
self,
|
|
33
|
+
realization_package: Dict[str, Any],
|
|
34
|
+
agent_name: str,
|
|
35
|
+
phase: str
|
|
36
|
+
) -> Dict[str, Any]:
|
|
37
|
+
"""
|
|
38
|
+
Generate the approval question configuration for AskUserQuestion tool.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
realization_package: The plan to be realized
|
|
42
|
+
agent_name: Name of the agent that generated the plan
|
|
43
|
+
phase: Current phase (e.g., "Phase 3.3")
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
Dict with AskUserQuestion configuration
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
critical_ops = self._get_critical_operations(realization_package)
|
|
50
|
+
operation_count = self._get_operation_count(realization_package)
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
"questions": [{
|
|
54
|
+
"question": f"¿Apruebas la realización de este plan? Se ejecutará: {critical_ops}",
|
|
55
|
+
"header": "Approval",
|
|
56
|
+
"multiSelect": False,
|
|
57
|
+
"options": [
|
|
58
|
+
{
|
|
59
|
+
"label": "✅ Aprobar y ejecutar",
|
|
60
|
+
"description": f"Proceder con {operation_count} operaciones: {critical_ops}"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"label": "❌ Rechazar",
|
|
64
|
+
"description": "Cancelar la realización. No se harán cambios."
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
}]
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
def generate_summary(self, realization_package: Dict[str, Any]) -> str:
|
|
71
|
+
"""
|
|
72
|
+
Generate human-readable summary of the realization package.
|
|
73
|
+
|
|
74
|
+
This summary should be presented to the user BEFORE calling
|
|
75
|
+
AskUserQuestion so they have full context for their decision.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
summary_parts = []
|
|
79
|
+
summary_parts.append("="*60)
|
|
80
|
+
summary_parts.append("📦 REALIZATION PACKAGE - APPROVAL REQUIRED")
|
|
81
|
+
summary_parts.append("="*60)
|
|
82
|
+
|
|
83
|
+
# Files
|
|
84
|
+
if "files" in realization_package:
|
|
85
|
+
files = realization_package["files"]
|
|
86
|
+
summary_parts.append(f"\n**Archivos a crear/modificar:** {len(files)}")
|
|
87
|
+
for file_info in files[:10]: # Show first 10
|
|
88
|
+
action = file_info.get('action', 'create')
|
|
89
|
+
summary_parts.append(f" [{action}] {file_info['path']}")
|
|
90
|
+
if len(files) > 10:
|
|
91
|
+
summary_parts.append(f" ... y {len(files) - 10} archivos más")
|
|
92
|
+
|
|
93
|
+
# Git operations
|
|
94
|
+
if "git_operations" in realization_package:
|
|
95
|
+
git_ops = realization_package["git_operations"]
|
|
96
|
+
summary_parts.append(f"\n**Git Operations:**")
|
|
97
|
+
summary_parts.append(f" Commit: {git_ops.get('commit_message', 'N/A')}")
|
|
98
|
+
summary_parts.append(f" Branch: {git_ops.get('branch', 'main')}")
|
|
99
|
+
summary_parts.append(f" Remote: {git_ops.get('remote', 'origin')}")
|
|
100
|
+
summary_parts.append(f" Operation: git push")
|
|
101
|
+
|
|
102
|
+
# Resources affected
|
|
103
|
+
if "resources_affected" in realization_package:
|
|
104
|
+
resources = realization_package["resources_affected"]
|
|
105
|
+
summary_parts.append(f"\n**Recursos Afectados en el Cluster:**")
|
|
106
|
+
for resource_type, items in resources.items():
|
|
107
|
+
if isinstance(items, list):
|
|
108
|
+
summary_parts.append(f" {resource_type}: {len(items)}")
|
|
109
|
+
for item in items[:5]:
|
|
110
|
+
summary_parts.append(f" - {item}")
|
|
111
|
+
if len(items) > 5:
|
|
112
|
+
summary_parts.append(f" ... y {len(items) - 5} más")
|
|
113
|
+
else:
|
|
114
|
+
summary_parts.append(f" {resource_type}: {items}")
|
|
115
|
+
|
|
116
|
+
# Terraform operations
|
|
117
|
+
if "terraform_operations" in realization_package:
|
|
118
|
+
tf_ops = realization_package["terraform_operations"]
|
|
119
|
+
summary_parts.append(f"\n**Terraform Operations:**")
|
|
120
|
+
summary_parts.append(f" Command: {tf_ops.get('command', 'N/A')}")
|
|
121
|
+
summary_parts.append(f" Path: {tf_ops.get('path', 'N/A')}")
|
|
122
|
+
if "resources" in tf_ops:
|
|
123
|
+
summary_parts.append(f" Resources to change: {len(tf_ops['resources'])}")
|
|
124
|
+
|
|
125
|
+
# Validation results (if present)
|
|
126
|
+
if "validation_results" in realization_package:
|
|
127
|
+
validation = realization_package["validation_results"]
|
|
128
|
+
summary_parts.append(f"\n**Pre-Deployment Validation:**")
|
|
129
|
+
summary_parts.append(f" Status: {validation.get('status', 'N/A')}")
|
|
130
|
+
if validation.get('warnings'):
|
|
131
|
+
summary_parts.append(f" ⚠️ Warnings: {len(validation['warnings'])}")
|
|
132
|
+
for warning in validation['warnings'][:3]:
|
|
133
|
+
summary_parts.append(f" - {warning}")
|
|
134
|
+
|
|
135
|
+
# Estimated impact
|
|
136
|
+
if "estimated_impact" in realization_package:
|
|
137
|
+
impact = realization_package["estimated_impact"]
|
|
138
|
+
summary_parts.append(f"\n**Estimated Impact:**")
|
|
139
|
+
summary_parts.append(f" Downtime: {impact.get('downtime', 'None expected')}")
|
|
140
|
+
summary_parts.append(f" Risk Level: {impact.get('risk_level', 'Medium')}")
|
|
141
|
+
|
|
142
|
+
summary_parts.append("\n" + "="*60)
|
|
143
|
+
|
|
144
|
+
return "\n".join(summary_parts)
|
|
145
|
+
|
|
146
|
+
def _get_critical_operations(self, realization_package: Dict[str, Any]) -> str:
|
|
147
|
+
"""Extract critical operations for the approval question."""
|
|
148
|
+
operations = []
|
|
149
|
+
|
|
150
|
+
if "git_operations" in realization_package:
|
|
151
|
+
git_ops = realization_package["git_operations"]
|
|
152
|
+
branch = git_ops.get('branch', 'main')
|
|
153
|
+
operations.append(f"git push origin {branch}")
|
|
154
|
+
|
|
155
|
+
if "kubectl_operations" in realization_package:
|
|
156
|
+
operations.append("kubectl apply")
|
|
157
|
+
|
|
158
|
+
if "terraform_operations" in realization_package:
|
|
159
|
+
tf_ops = realization_package["terraform_operations"]
|
|
160
|
+
command = tf_ops.get('command', 'apply')
|
|
161
|
+
operations.append(f"terraform {command}")
|
|
162
|
+
|
|
163
|
+
if "flux_operations" in realization_package:
|
|
164
|
+
operations.append("flux reconcile")
|
|
165
|
+
|
|
166
|
+
return ", ".join(operations) if operations else "cambios al repositorio"
|
|
167
|
+
|
|
168
|
+
def _get_operation_count(self, realization_package: Dict[str, Any]) -> int:
|
|
169
|
+
"""Count total operations in the package."""
|
|
170
|
+
count = 0
|
|
171
|
+
|
|
172
|
+
if "files" in realization_package:
|
|
173
|
+
count += len(realization_package["files"])
|
|
174
|
+
|
|
175
|
+
if "git_operations" in realization_package:
|
|
176
|
+
count += 2 # commit + push
|
|
177
|
+
|
|
178
|
+
if "kubectl_operations" in realization_package:
|
|
179
|
+
kubectl_ops = realization_package["kubectl_operations"]
|
|
180
|
+
count += len(kubectl_ops) if isinstance(kubectl_ops, list) else 1
|
|
181
|
+
|
|
182
|
+
if "terraform_operations" in realization_package:
|
|
183
|
+
count += 1
|
|
184
|
+
|
|
185
|
+
return count
|
|
186
|
+
|
|
187
|
+
def validate_approval_response(self, user_response: str) -> Dict[str, Any]:
|
|
188
|
+
"""
|
|
189
|
+
Validate the user's approval response.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
user_response: The response from AskUserQuestion
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
Dict with validation result and action to take
|
|
196
|
+
"""
|
|
197
|
+
|
|
198
|
+
if user_response == "✅ Aprobar y ejecutar":
|
|
199
|
+
return {
|
|
200
|
+
"approved": True,
|
|
201
|
+
"action": "proceed_to_realization",
|
|
202
|
+
"message": "✅ Aprobación recibida. Procediendo a Fase 5 (Realization)..."
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
elif user_response == "❌ Rechazar":
|
|
206
|
+
return {
|
|
207
|
+
"approved": False,
|
|
208
|
+
"action": "halt_workflow",
|
|
209
|
+
"message": "❌ Realización rechazada por el usuario. Workflow detenido."
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
else:
|
|
213
|
+
# User selected "Other" or provided custom response
|
|
214
|
+
return {
|
|
215
|
+
"approved": False,
|
|
216
|
+
"action": "clarify_with_user",
|
|
217
|
+
"message": f"⚠️ Respuesta no estándar recibida: '{user_response}'. Se requiere clarificación.",
|
|
218
|
+
"user_input": user_response
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
def log_approval(
|
|
222
|
+
self,
|
|
223
|
+
realization_package: Dict[str, Any],
|
|
224
|
+
user_response: str,
|
|
225
|
+
approved: bool,
|
|
226
|
+
agent_name: str,
|
|
227
|
+
phase: str
|
|
228
|
+
):
|
|
229
|
+
"""
|
|
230
|
+
Log the approval decision for audit trail.
|
|
231
|
+
|
|
232
|
+
This creates a permanent record of all approval decisions
|
|
233
|
+
for compliance and troubleshooting purposes.
|
|
234
|
+
"""
|
|
235
|
+
|
|
236
|
+
log_entry = {
|
|
237
|
+
"timestamp": datetime.now().isoformat(),
|
|
238
|
+
"agent": agent_name,
|
|
239
|
+
"phase": phase,
|
|
240
|
+
"approved": approved,
|
|
241
|
+
"user_response": user_response,
|
|
242
|
+
"files_count": len(realization_package.get("files", [])),
|
|
243
|
+
"operations": self._get_critical_operations(realization_package),
|
|
244
|
+
"git_commit": realization_package.get("git_operations", {}).get("commit_message", "N/A")
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
# Append to JSONL log file
|
|
248
|
+
with open(self.approval_log_path, "a") as f:
|
|
249
|
+
f.write(json.dumps(log_entry) + "\n")
|
|
250
|
+
|
|
251
|
+
return log_entry
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
# Convenience function for orchestrator to use
|
|
255
|
+
def request_approval(
|
|
256
|
+
realization_package: Dict[str, Any],
|
|
257
|
+
agent_name: str,
|
|
258
|
+
phase: str
|
|
259
|
+
) -> Dict[str, Any]:
|
|
260
|
+
"""
|
|
261
|
+
Main function to request approval from user.
|
|
262
|
+
|
|
263
|
+
This should be called by the orchestrator in Phase 4 (Approval Gate).
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
realization_package: The complete realization package from the agent
|
|
267
|
+
agent_name: Name of the agent that generated the plan
|
|
268
|
+
phase: Current phase identifier
|
|
269
|
+
|
|
270
|
+
Returns:
|
|
271
|
+
Dict with:
|
|
272
|
+
- summary: String to present to user
|
|
273
|
+
- question_config: Dict to pass to AskUserQuestion tool
|
|
274
|
+
"""
|
|
275
|
+
|
|
276
|
+
gate = ApprovalGate()
|
|
277
|
+
|
|
278
|
+
return {
|
|
279
|
+
"summary": gate.generate_summary(realization_package),
|
|
280
|
+
"question_config": gate.generate_approval_question(realization_package, agent_name, phase),
|
|
281
|
+
"gate_instance": gate
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def process_approval_response(
|
|
286
|
+
gate_instance: ApprovalGate,
|
|
287
|
+
user_response: str,
|
|
288
|
+
realization_package: Dict[str, Any],
|
|
289
|
+
agent_name: str,
|
|
290
|
+
phase: str
|
|
291
|
+
) -> Dict[str, Any]:
|
|
292
|
+
"""
|
|
293
|
+
Process the user's approval response.
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
gate_instance: The ApprovalGate instance from request_approval
|
|
297
|
+
user_response: The user's response from AskUserQuestion
|
|
298
|
+
realization_package: The realization package
|
|
299
|
+
agent_name: Name of the agent
|
|
300
|
+
phase: Current phase
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
Dict with validation result and action to take
|
|
304
|
+
"""
|
|
305
|
+
|
|
306
|
+
# Validate response
|
|
307
|
+
validation = gate_instance.validate_approval_response(user_response)
|
|
308
|
+
|
|
309
|
+
# Log the decision
|
|
310
|
+
gate_instance.log_approval(
|
|
311
|
+
realization_package,
|
|
312
|
+
user_response,
|
|
313
|
+
validation["approved"],
|
|
314
|
+
agent_name,
|
|
315
|
+
phase
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
return validation
|