@jaguilar87/gaia-ops 2.5.8 → 2.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.
@@ -25,7 +25,7 @@
25
25
 
26
26
  import { join, dirname, resolve } from 'path';
27
27
  import fs from 'fs/promises';
28
- import { existsSync } from 'fs';
28
+ import { existsSync, lstatSync } from 'fs';
29
29
  import chalk from 'chalk';
30
30
  import ora from 'ora';
31
31
 
@@ -136,13 +136,15 @@ async function removeSymlinks() {
136
136
 
137
137
  let removed = 0;
138
138
  for (const symlinkPath of symlinks) {
139
- if (existsSync(symlinkPath)) {
140
- try {
139
+ try {
140
+ // Use lstat to check if path exists as a symlink (works for broken symlinks too)
141
+ const stats = lstatSync(symlinkPath);
142
+ if (stats.isSymbolicLink() || stats.isFile()) {
141
143
  await fs.unlink(symlinkPath);
142
144
  removed++;
143
- } catch (error) {
144
- // Ignore errors
145
145
  }
146
+ } catch (error) {
147
+ // Path doesn't exist or other error, skip
146
148
  }
147
149
  }
148
150
 
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * @jaguilar87/gaia-ops - Uninstall wrapper
5
+ *
6
+ * Safely uninstalls gaia-ops by:
7
+ * 1. Running gaia-cleanup to remove all generated files
8
+ * 2. Running npm uninstall to remove the package
9
+ *
10
+ * Usage:
11
+ * npx gaia-uninstall
12
+ * OR
13
+ * npm exec gaia-uninstall
14
+ *
15
+ * This ensures a clean uninstallation with no leftover files.
16
+ */
17
+
18
+ import { execSync } from 'child_process';
19
+ import { fileURLToPath } from 'url';
20
+ import { dirname, join } from 'path';
21
+ import { existsSync } from 'fs';
22
+ import chalk from 'chalk';
23
+ import ora from 'ora';
24
+
25
+ const __filename = fileURLToPath(import.meta.url);
26
+ const __dirname = dirname(__filename);
27
+ const CWD = process.env.INIT_CWD || process.cwd();
28
+
29
+ /**
30
+ * Run gaia-cleanup to remove generated files
31
+ */
32
+ async function runCleanup() {
33
+ const spinner = ora('Running cleanup...').start();
34
+
35
+ try {
36
+ // Import and execute gaia-cleanup
37
+ const cleanupScript = join(__dirname, 'gaia-cleanup.js');
38
+
39
+ if (!existsSync(cleanupScript)) {
40
+ spinner.fail('Cleanup script not found');
41
+ return false;
42
+ }
43
+
44
+ // Execute cleanup by importing it
45
+ await import(`file://${cleanupScript}`);
46
+
47
+ spinner.succeed('Cleanup completed');
48
+ return true;
49
+ } catch (error) {
50
+ spinner.fail(`Cleanup failed: ${error.message}`);
51
+ return false;
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Run npm uninstall
57
+ */
58
+ function runUninstall() {
59
+ const spinner = ora('Uninstalling @jaguilar87/gaia-ops...').start();
60
+
61
+ try {
62
+ execSync('npm uninstall @jaguilar87/gaia-ops', {
63
+ cwd: CWD,
64
+ stdio: 'inherit'
65
+ });
66
+
67
+ spinner.succeed('Package uninstalled');
68
+ return true;
69
+ } catch (error) {
70
+ spinner.fail(`Uninstall failed: ${error.message}`);
71
+ return false;
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Main function
77
+ */
78
+ async function main() {
79
+ console.log(chalk.cyan('\n🗑️ @jaguilar87/gaia-ops uninstaller\n'));
80
+
81
+ try {
82
+ // Step 1: Run cleanup
83
+ const cleanupSuccess = await runCleanup();
84
+
85
+ if (!cleanupSuccess) {
86
+ console.log(chalk.yellow('\n⚠️ Cleanup had issues, but continuing with uninstall...\n'));
87
+ }
88
+
89
+ console.log('');
90
+
91
+ // Step 2: Run uninstall
92
+ const uninstallSuccess = runUninstall();
93
+
94
+ if (uninstallSuccess) {
95
+ console.log(chalk.green('\n✅ Uninstall complete!\n'));
96
+ console.log(chalk.gray('All gaia-ops files have been removed.'));
97
+ console.log(chalk.gray('Your project data (logs, tests, project-context) was preserved.\n'));
98
+ } else {
99
+ console.log(chalk.red('\n❌ Uninstall failed\n'));
100
+ console.log(chalk.yellow('You can try manually:'));
101
+ console.log(chalk.gray(' 1. npx gaia-cleanup'));
102
+ console.log(chalk.gray(' 2. npm uninstall @jaguilar87/gaia-ops\n'));
103
+ process.exit(1);
104
+ }
105
+ } catch (error) {
106
+ console.error(chalk.red(`\n❌ Uninstall error: ${error.message}\n`));
107
+ process.exit(1);
108
+ }
109
+ }
110
+
111
+ main();
@@ -0,0 +1,122 @@
1
+ # Binary Delegation Matrix
2
+
3
+ **Version:** 1.0.0
4
+ **Purpose:** Deterministic decision of when to delegate vs execute locally
5
+
6
+ ## Decision Rules (Priority Order)
7
+
8
+ | # | Condition | Decision | Confidence | Reason |
9
+ |---|-----------|----------|------------|---------|
10
+ | 1 | `has_task_id AND task_agent != None` | DELEGATE | 1.0 | Task metadata routing |
11
+ | 2 | `security_tier == "T3"` | DELEGATE | 1.0 | T3 requires agent + approval |
12
+ | 3 | `file_count >= 3` | DELEGATE | 0.9 | Multi-file threshold |
13
+ | 4 | `file_span_multiple_dirs == True` | DELEGATE | 0.9 | Multiple directories |
14
+ | 5 | `has_infrastructure_keywords AND requires_context` | DELEGATE | 0.85 | Infrastructure + context |
15
+ | 6 | `has_chained_commands == True` | DELEGATE | 0.8 | Chained commands safety |
16
+ | 7 | `tier == "T0" AND file_count <= 1 AND !has_approval_keywords` | LOCAL | 0.9 | Atomic T0 operation |
17
+ | 8 | `tier == "T1" AND file_count <= 1 AND !requires_credentials` | LOCAL | 0.85 | Simple T1 validation |
18
+ | 9 | DEFAULT (fallback) | DELEGATE | 0.5 | Safety default |
19
+
20
+ ## Binary Conditions Extracted
21
+
22
+ ```python
23
+ @dataclass
24
+ class DelegationConditions:
25
+ file_count: int # Count of files to modify
26
+ file_span_multiple_dirs: bool # Files in multiple directories
27
+ has_chained_commands: bool # Uses && or pipes
28
+ has_infrastructure_keywords: bool # terraform/kubectl/etc
29
+ has_approval_keywords: bool # apply/deploy/push/delete
30
+ security_tier: str # T0, T1, T2, T3
31
+ requires_context: bool # Needs project-context.json
32
+ requires_credentials: bool # Needs GCP/AWS/K8s creds
33
+ has_task_id: bool # Mentions task ID
34
+ task_agent: str # Agent from task metadata
35
+ ```
36
+
37
+ ## Examples
38
+
39
+ ```python
40
+ # Example 1: Simple git status (LOCAL)
41
+ conditions = DelegationConditions(
42
+ file_count=0,
43
+ file_span_multiple_dirs=False,
44
+ has_infrastructure_keywords=False,
45
+ security_tier="T0"
46
+ )
47
+ → Decision: LOCAL (Rule 7: Atomic T0 operation)
48
+
49
+ # Example 2: Terraform apply (DELEGATE)
50
+ conditions = DelegationConditions(
51
+ has_infrastructure_keywords=True,
52
+ has_approval_keywords=True,
53
+ security_tier="T3"
54
+ )
55
+ → Decision: DELEGATE (Rule 2: T3 requires agent)
56
+
57
+ # Example 3: Multi-file edit (DELEGATE)
58
+ conditions = DelegationConditions(
59
+ file_count=5,
60
+ file_span_multiple_dirs=True,
61
+ security_tier="T1"
62
+ )
63
+ → Decision: DELEGATE (Rule 3: Multi-file threshold)
64
+ ```
65
+
66
+ ## Integration with Orchestrator
67
+
68
+ ```python
69
+ from agent_router import should_delegate
70
+
71
+ # At orchestrator entry point
72
+ result = should_delegate(user_request, context={
73
+ "file_count": 3,
74
+ "multiple_directories": True
75
+ })
76
+
77
+ if result["delegate"]:
78
+ agent = result["suggested_agent"]
79
+ # Proceed with agent invocation
80
+ else:
81
+ # Execute locally (orchestrator)
82
+ pass
83
+ ```
84
+
85
+ ## Confidence Levels
86
+
87
+ - **1.0**: Absolute confidence (deterministic rules)
88
+ - **0.9**: High confidence (clear patterns)
89
+ - **0.85**: Medium-high confidence (strong indicators)
90
+ - **0.8**: Medium confidence (good heuristics)
91
+ - **0.5**: Low confidence (fallback/safety)
92
+
93
+ ## Security Considerations
94
+
95
+ 1. **T3 operations ALWAYS delegate** - No exceptions
96
+ 2. **Chained commands prefer delegation** - Safety over convenience
97
+ 3. **Default to delegation when uncertain** - Better safe than sorry
98
+ 4. **Infrastructure operations require context** - Never execute without proper context
99
+
100
+ ## Testing the Matrix
101
+
102
+ ```bash
103
+ # Run standalone test
104
+ python3 .claude/tools/0-guards/delegation_matrix.py
105
+
106
+ # Test integration with router
107
+ python3 -c "from tools.1-routing.agent_router import should_delegate; \
108
+ print(should_delegate('terraform apply', {'file_count': 1}))"
109
+ ```
110
+
111
+ ## Monitoring
112
+
113
+ Delegation decisions are logged to:
114
+ - `.claude/logs/delegation.jsonl` (if logging enabled)
115
+ - Included in metrics collection for KPI tracking
116
+
117
+ ## Future Improvements
118
+
119
+ 1. **Machine Learning Enhancement**: Train on actual delegation outcomes
120
+ 2. **Custom Rules**: Allow project-specific delegation rules
121
+ 3. **Context Awareness**: Consider recent operations for better decisions
122
+ 4. **Performance Metrics**: Track decision accuracy and adjust thresholds
@@ -0,0 +1,37 @@
1
+ {
2
+ "kpi_targets": {
3
+ "routing_accuracy": {
4
+ "avg_confidence_min": 0.7,
5
+ "semantic_routing_rate_min": 0.6
6
+ },
7
+ "delegation_effectiveness": {
8
+ "avg_confidence_min": 0.8
9
+ },
10
+ "guard_effectiveness": {
11
+ "pass_rate_min": 0.9,
12
+ "phase_4_pass_rate_min": 1.0
13
+ },
14
+ "phase_completion": {
15
+ "phase_4_skip_rate_t3_max": 0.0
16
+ },
17
+ "approval_gate": {
18
+ "approval_rate_min": 0.5,
19
+ "avg_response_time_max_seconds": 300
20
+ },
21
+ "agent_performance": {
22
+ "success_rate_min": 0.8,
23
+ "avg_duration_max_ms": 30000
24
+ },
25
+ "overall_health": {
26
+ "score_min": 0.8
27
+ }
28
+ },
29
+ "alerting": {
30
+ "enabled": true,
31
+ "critical_thresholds": {
32
+ "phase_4_skip_rate_t3": 0.0,
33
+ "guard_pass_rate": 0.85,
34
+ "agent_success_rate": 0.7
35
+ }
36
+ }
37
+ }
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Post-Phase Hook - Validar resultados DESPUÉS de cada fase.
4
+ """
5
+
6
+ import sys
7
+ import logging
8
+ from pathlib import Path
9
+ from typing import Dict, Any
10
+
11
+ sys.path.insert(0, str(Path(__file__).parent.parent / "tools" / "0-guards"))
12
+
13
+ from workflow_enforcer import get_enforcer, GuardViolation
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ def post_phase_4_approval(
19
+ tier: str,
20
+ user_response: str,
21
+ validation_result: Dict[str, Any]
22
+ ) -> Dict[str, Any]:
23
+ """
24
+ Validar que T3 operations recibieron approval.
25
+ """
26
+ enforcer = get_enforcer()
27
+
28
+ try:
29
+ if tier == "T3":
30
+ enforcer.enforce(
31
+ "guard_phase_4_approval_validation",
32
+ validation_result=validation_result
33
+ )
34
+
35
+ return {"valid": True, "reason": "Approval validation passed"}
36
+
37
+ except GuardViolation as e:
38
+ return {"valid": False, "reason": str(e)}
39
+
40
+
41
+ def post_phase_6_ssot_update(
42
+ tier: str,
43
+ ssot_updated: bool
44
+ ) -> Dict[str, Any]:
45
+ """
46
+ Validar que T3 operations actualizaron SSOT.
47
+ """
48
+ enforcer = get_enforcer()
49
+
50
+ try:
51
+ enforcer.enforce(
52
+ "guard_phase_6_ssot_update_after_t3",
53
+ tier=tier,
54
+ ssot_updated=ssot_updated
55
+ )
56
+
57
+ return {"valid": True, "reason": "SSOT update validation passed"}
58
+
59
+ except GuardViolation as e:
60
+ return {"valid": False, "reason": str(e)}
61
+
62
+
63
+ # CLI for testing
64
+ if __name__ == "__main__":
65
+ logging.basicConfig(level=logging.INFO)
66
+
67
+ print("🧪 Testing Post-Phase Hooks...\n")
68
+
69
+ # Test post-phase 4 validation (T3 without approval)
70
+ result = post_phase_4_approval(
71
+ tier="T3",
72
+ user_response="reject",
73
+ validation_result={"approved": False, "action": "abort"}
74
+ )
75
+ print(f"Post-Phase 4 (T3 rejected): {result}")
76
+
77
+ # Test post-phase 4 validation (T3 with approval)
78
+ result = post_phase_4_approval(
79
+ tier="T3",
80
+ user_response="approve",
81
+ validation_result={"approved": True, "action": "proceed"}
82
+ )
83
+ print(f"Post-Phase 4 (T3 approved): {result}")
84
+
85
+ # Test post-phase 6 validation (T3 without SSOT update)
86
+ result = post_phase_6_ssot_update(
87
+ tier="T3",
88
+ ssot_updated=False
89
+ )
90
+ print(f"Post-Phase 6 (T3 no SSOT update): {result}")
91
+
92
+ # Test post-phase 6 validation (T3 with SSOT update)
93
+ result = post_phase_6_ssot_update(
94
+ tier="T3",
95
+ ssot_updated=True
96
+ )
97
+ print(f"Post-Phase 6 (T3 SSOT updated): {result}")
@@ -0,0 +1,222 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Pre-Phase Hook - Ejecutar guards ANTES de cada fase.
4
+ Se invoca desde el orchestrator antes de comenzar una fase.
5
+ """
6
+
7
+ import sys
8
+ import logging
9
+ from pathlib import Path
10
+ from typing import Dict, Any, Optional
11
+
12
+ # Add tools to path
13
+ sys.path.insert(0, str(Path(__file__).parent.parent / "tools" / "0-guards"))
14
+
15
+ from workflow_enforcer import get_enforcer, GuardViolation
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ def pre_phase_0_clarification(
21
+ ambiguity_score: float,
22
+ user_prompt: str
23
+ ) -> Dict[str, Any]:
24
+ """
25
+ Ejecutar guards antes de Phase 0 (Clarification).
26
+
27
+ Returns:
28
+ {"allowed": bool, "reason": str}
29
+ """
30
+ enforcer = get_enforcer()
31
+
32
+ try:
33
+ # Guard: Ambiguity threshold
34
+ enforcer.enforce(
35
+ "guard_phase_0_ambiguity_threshold",
36
+ ambiguity_score=ambiguity_score,
37
+ threshold=0.3
38
+ )
39
+
40
+ return {
41
+ "allowed": True,
42
+ "reason": "Phase 0 guards passed"
43
+ }
44
+
45
+ except GuardViolation as e:
46
+ return {
47
+ "allowed": False,
48
+ "reason": str(e)
49
+ }
50
+
51
+
52
+ def pre_phase_1_routing(
53
+ agent_name: str,
54
+ routing_confidence: float,
55
+ available_agents: list
56
+ ) -> Dict[str, Any]:
57
+ """
58
+ Ejecutar guards antes de Phase 1 (Routing).
59
+ """
60
+ enforcer = get_enforcer()
61
+
62
+ try:
63
+ # Guard: Routing confidence
64
+ enforcer.enforce(
65
+ "guard_phase_1_routing_confidence",
66
+ routing_confidence=routing_confidence,
67
+ min_confidence=0.5
68
+ )
69
+
70
+ # Guard: Agent exists
71
+ enforcer.enforce(
72
+ "guard_phase_1_agent_exists",
73
+ agent_name=agent_name,
74
+ available_agents=available_agents
75
+ )
76
+
77
+ return {"allowed": True, "reason": "Phase 1 guards passed"}
78
+
79
+ except GuardViolation as e:
80
+ return {"allowed": False, "reason": str(e)}
81
+
82
+
83
+ def pre_phase_2_context(
84
+ context_payload: Dict[str, Any],
85
+ agent_name: str
86
+ ) -> Dict[str, Any]:
87
+ """
88
+ Ejecutar guards antes de Phase 2 (Context Provisioning).
89
+ """
90
+ enforcer = get_enforcer()
91
+
92
+ # Determinar required sections según el agente
93
+ agent_requirements = {
94
+ "terraform-architect": ["project_details", "terraform_infrastructure", "operational_guidelines"],
95
+ "gitops-operator": ["project_details", "gitops_configuration", "cluster_details"],
96
+ "gcp-troubleshooter": ["project_details", "cluster_details"],
97
+ "devops-developer": ["project_details", "operational_guidelines"]
98
+ }
99
+
100
+ required_sections = agent_requirements.get(agent_name, ["project_details"])
101
+
102
+ try:
103
+ # Guard: Context completeness
104
+ enforcer.enforce(
105
+ "guard_phase_2_context_completeness",
106
+ context_payload=context_payload,
107
+ required_sections=required_sections
108
+ )
109
+
110
+ return {"allowed": True, "reason": "Phase 2 guards passed"}
111
+
112
+ except GuardViolation as e:
113
+ return {"allowed": False, "reason": str(e)}
114
+
115
+
116
+ def pre_phase_4_approval(
117
+ tier: str,
118
+ realization_package: Dict[str, Any]
119
+ ) -> Dict[str, Any]:
120
+ """
121
+ Ejecutar guards antes de Phase 4 (Approval Gate).
122
+
123
+ CRITICAL: Este guard NO PUEDE fallar para T3 operations.
124
+ """
125
+ enforcer = get_enforcer()
126
+
127
+ try:
128
+ # Guard: Planning complete
129
+ enforcer.enforce(
130
+ "guard_phase_5_planning_complete",
131
+ realization_package=realization_package
132
+ )
133
+
134
+ # Note: El guard de approval_received se ejecuta DESPUÉS
135
+ # de recibir la respuesta del usuario en post_phase_4
136
+
137
+ return {"allowed": True, "reason": "Phase 4 pre-guards passed"}
138
+
139
+ except GuardViolation as e:
140
+ return {"allowed": False, "reason": str(e)}
141
+
142
+
143
+ def pre_phase_5_realization(
144
+ tier: str,
145
+ approval_validation: Dict[str, Any],
146
+ realization_package: Dict[str, Any]
147
+ ) -> Dict[str, Any]:
148
+ """
149
+ Ejecutar guards antes de Phase 5 (Realization).
150
+
151
+ CRITICAL: Valida que T3 operations tengan aprobación.
152
+ """
153
+ enforcer = get_enforcer()
154
+
155
+ try:
156
+ # Guard: Approval mandatory for T3
157
+ enforcer.enforce(
158
+ "guard_phase_4_approval_mandatory",
159
+ tier=tier,
160
+ approval_received=approval_validation.get("approved", False)
161
+ )
162
+
163
+ # Guard: Approval validation
164
+ if tier == "T3":
165
+ enforcer.enforce(
166
+ "guard_phase_4_approval_validation",
167
+ validation_result=approval_validation
168
+ )
169
+
170
+ return {"allowed": True, "reason": "Phase 5 guards passed"}
171
+
172
+ except GuardViolation as e:
173
+ return {"allowed": False, "reason": str(e)}
174
+
175
+
176
+ def pre_phase_6_ssot_update(
177
+ tier: str,
178
+ realization_success: bool
179
+ ) -> Dict[str, Any]:
180
+ """
181
+ Ejecutar guards antes de Phase 6 (SSOT Update).
182
+ """
183
+ if not realization_success:
184
+ return {
185
+ "allowed": False,
186
+ "reason": "Cannot update SSOT: Realization failed"
187
+ }
188
+
189
+ # Phase 6 no tiene guards bloqueantes adicionales
190
+ # (el guard de ssot_updated se ejecuta DESPUÉS del update)
191
+
192
+ return {"allowed": True, "reason": "Phase 6 pre-guards passed"}
193
+
194
+
195
+ # CLI for testing
196
+ if __name__ == "__main__":
197
+ logging.basicConfig(level=logging.INFO)
198
+
199
+ print("🧪 Testing Pre-Phase Hooks...\n")
200
+
201
+ # Test Phase 4 pre-guard
202
+ result = pre_phase_4_approval(
203
+ tier="T3",
204
+ realization_package={"files": [], "git_operations": {}}
205
+ )
206
+ print(f"Phase 4 pre-guard: {result}")
207
+
208
+ # Test Phase 5 pre-guard (sin approval - debe fallar)
209
+ result = pre_phase_5_realization(
210
+ tier="T3",
211
+ approval_validation={"approved": False},
212
+ realization_package={}
213
+ )
214
+ print(f"Phase 5 pre-guard (no approval): {result}")
215
+
216
+ # Test Phase 5 pre-guard (con approval - debe pasar)
217
+ result = pre_phase_5_realization(
218
+ tier="T3",
219
+ approval_validation={"approved": True, "action": "proceed_to_realization"},
220
+ realization_package={}
221
+ )
222
+ print(f"Phase 5 pre-guard (with approval): {result}")
@@ -16,6 +16,29 @@ from tenacity import retry, stop_after_attempt, wait_exponential
16
16
 
17
17
  from pre_kubectl_security import validate_gitops_workflow
18
18
 
19
+ # ============================================================================
20
+ # CLAUDE CODE ATTRIBUTION FOOTER DETECTION
21
+ # ============================================================================
22
+
23
+ def detect_claude_footers(command: str) -> bool:
24
+ """
25
+ Detect Claude Code attribution footers in any command.
26
+
27
+ Looks for patterns like:
28
+ - "Generated with Claude Code"
29
+ - "Co-Authored-By: Claude"
30
+ """
31
+ forbidden_patterns = [
32
+ r"Generated with\s+Claude Code",
33
+ r"Co-Authored-By:\s+Claude",
34
+ ]
35
+
36
+ for pattern in forbidden_patterns:
37
+ if re.search(pattern, command, re.IGNORECASE):
38
+ return True
39
+
40
+ return False
41
+
19
42
  # Configure logging
20
43
  logging.basicConfig(
21
44
  level=logging.INFO,
@@ -423,6 +446,16 @@ class PolicyEngine:
423
446
  if not is_allowed:
424
447
  return is_allowed, tier, reason
425
448
 
449
+ # INTERCEPT: Detect Claude Code attribution footers in ANY command
450
+ if detect_claude_footers(command):
451
+ logger.warning(f"Command contains Claude Code attribution footers: {command[:100]}")
452
+ return False, SecurityTier.T3_BLOCKED, (
453
+ "❌ Command contains Claude Code attribution footers\n\n"
454
+ "Remove these patterns and retry:\n"
455
+ " • 'Generated with Claude Code'\n"
456
+ " • 'Co-Authored-By: Claude'"
457
+ )
458
+
426
459
  # Enforce GitOps security rules for cluster-related commands
427
460
  if any(keyword in command for keyword in ("kubectl", "helm", "flux")):
428
461
  try:
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@jaguilar87/gaia-ops",
3
- "version": "2.5.8",
3
+ "version": "2.6.0",
4
4
  "description": "Multi-agent orchestration system for Claude Code - DevOps automation toolkit",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
7
  "bin": {
8
8
  "gaia-init": "bin/gaia-init.js",
9
- "gaia-cleanup": "bin/gaia-cleanup.js"
9
+ "gaia-cleanup": "bin/gaia-cleanup.js",
10
+ "gaia-uninstall": "bin/gaia-uninstall.js"
10
11
  },
11
12
  "keywords": [
12
13
  "claude-code",