@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,549 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Subagent stop hook for Claude Code Agent System
4
+
5
+ Handles session state persistence when specialized agents complete execution.
6
+
7
+ Responsibilities:
8
+ 1. Update .claude/session/active/ with current session state
9
+ 2. Collect and index artifacts generated during agent execution
10
+ 3. Persist metadata for session continuity
11
+ 4. Prepare state for potential bundle creation (via /save-session)
12
+
13
+ Architecture:
14
+ - Part of the hybrid session management system
15
+ - Updates live session context (.claude/session/active/)
16
+ - Does NOT create bundles directly (bundles are on-demand via /save-session)
17
+ - Enables artifact collection for later bundling
18
+
19
+ Integration:
20
+ - Executed automatically after agent tool completes
21
+ - Works with session_startup_check.py for initialization
22
+ - Feeds data to /save-session for explicit bundling
23
+ - Maintains artifact inventory for historical reference
24
+ """
25
+
26
+ import os
27
+ import sys
28
+ import json
29
+ import shutil
30
+ import logging
31
+ from datetime import datetime
32
+ from pathlib import Path
33
+ from typing import Dict, List, Any, Optional
34
+ import hashlib
35
+ from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
36
+
37
+ # Configure structured logging
38
+ logging.basicConfig(
39
+ level=logging.INFO,
40
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
41
+ )
42
+ logger = logging.getLogger(__name__)
43
+
44
+
45
+ class ArtifactCopyError(Exception):
46
+ """Custom exception for artifact copy failures"""
47
+ pass
48
+
49
+
50
+ class BundleCreationError(Exception):
51
+ """Custom exception for bundle creation failures"""
52
+ pass
53
+
54
+ class SessionManager:
55
+ """Manages session state and bundle creation"""
56
+
57
+ def __init__(self, bundle_dir: str = None):
58
+ if bundle_dir is None:
59
+ # Find the .claude directory by looking upward from current location
60
+ claude_dir = self._find_claude_dir()
61
+ bundle_dir = claude_dir / "session" / "bundles"
62
+
63
+ self.bundle_dir = Path(bundle_dir)
64
+ self.bundle_dir.mkdir(parents=True, exist_ok=True)
65
+ self.session_id = self._get_or_create_session_id()
66
+ self.current_bundle_path = self.bundle_dir / f"{datetime.now().strftime('%Y-%m-%d')}-{self.session_id}"
67
+ logger.info(f"SessionManager initialized with bundle_dir={self.bundle_dir}, session_id={self.session_id}")
68
+
69
+ def _find_claude_dir(self) -> Path:
70
+ """Find the .claude directory by searching upward from current location"""
71
+ current = Path.cwd()
72
+
73
+ # If we're already in a .claude directory, go up one level
74
+ if current.name == ".claude":
75
+ logger.debug("Already in .claude directory")
76
+ return current
77
+
78
+ # Look for .claude in current directory
79
+ claude_dir = current / ".claude"
80
+ if claude_dir.exists():
81
+ logger.debug(f"Found .claude at {claude_dir}")
82
+ return claude_dir
83
+
84
+ # Search upward through parent directories
85
+ for parent in current.parents:
86
+ claude_dir = parent / ".claude"
87
+ if claude_dir.exists():
88
+ logger.debug(f"Found .claude at {claude_dir}")
89
+ return claude_dir
90
+
91
+ # Default fallback - create .claude in current directory
92
+ logger.warning("No .claude directory found, creating in current directory")
93
+ claude_dir = current / ".claude"
94
+ claude_dir.mkdir(exist_ok=True)
95
+ return claude_dir
96
+
97
+ def _get_or_create_session_id(self) -> str:
98
+ """Get existing session ID or create new one"""
99
+ session_id = os.environ.get("CLAUDE_SESSION_ID")
100
+ if not session_id:
101
+ # Generate session ID based on timestamp and some randomness
102
+ timestamp = datetime.now().strftime("%H%M%S")
103
+ hash_input = f"{timestamp}-{os.getpid()}"
104
+ session_hash = hashlib.sha256(hash_input.encode()).hexdigest()[:8]
105
+ session_id = f"session-{timestamp}-{session_hash}"
106
+ os.environ["CLAUDE_SESSION_ID"] = session_id
107
+ logger.info(f"Generated new session_id: {session_id}")
108
+ else:
109
+ logger.info(f"Using existing session_id: {session_id}")
110
+ return session_id
111
+
112
+ class BundleManager:
113
+ """Manages artifact bundling and session persistence with enhanced error handling"""
114
+
115
+ def __init__(self, session_manager: SessionManager):
116
+ self.session_manager = session_manager
117
+ self.bundle_path = session_manager.current_bundle_path
118
+ logger.info(f"BundleManager initialized with bundle_path={self.bundle_path}")
119
+
120
+ def create_bundle(self, task_info: Dict[str, Any], artifacts: List[str],
121
+ agent_transcript: str) -> Path:
122
+ """
123
+ Create a bundle with task artifacts and metadata
124
+
125
+ Raises:
126
+ BundleCreationError: If bundle creation fails critically
127
+ """
128
+ try:
129
+ # Create bundle directory
130
+ self.bundle_path.mkdir(parents=True, exist_ok=True)
131
+ logger.info(f"Created bundle directory: {self.bundle_path}")
132
+
133
+ # Bundle metadata
134
+ bundle_metadata = {
135
+ "session_id": self.session_manager.session_id,
136
+ "timestamp": datetime.now().isoformat(),
137
+ "task_info": task_info,
138
+ "artifacts": artifacts,
139
+ "bundle_version": "1.1",
140
+ "errors": [] # Track any non-critical errors
141
+ }
142
+
143
+ # Save metadata
144
+ self._save_metadata(bundle_metadata)
145
+
146
+ # Save transcript
147
+ self._save_transcript(agent_transcript)
148
+
149
+ # Copy artifacts (with error tracking)
150
+ copy_errors = self._copy_artifacts(artifacts)
151
+ if copy_errors:
152
+ bundle_metadata["errors"].extend(copy_errors)
153
+ # Re-save metadata with error info
154
+ self._save_metadata(bundle_metadata)
155
+
156
+ # Generate summary
157
+ self._generate_summary(task_info, artifacts, copy_errors)
158
+
159
+ # Update latest symlink
160
+ self._update_latest_link()
161
+
162
+ logger.info(f"Bundle created successfully: {self.bundle_path}")
163
+ return self.bundle_path
164
+
165
+ except Exception as e:
166
+ logger.error(f"Critical error creating bundle: {e}", exc_info=True)
167
+ raise BundleCreationError(f"Failed to create bundle: {e}") from e
168
+
169
+ def _save_metadata(self, metadata: Dict[str, Any]):
170
+ """Save metadata with error handling"""
171
+ try:
172
+ metadata_file = self.bundle_path / "metadata.json"
173
+ with open(metadata_file, "w") as f:
174
+ json.dump(metadata, f, indent=2)
175
+ logger.debug(f"Saved metadata to {metadata_file}")
176
+ except Exception as e:
177
+ logger.error(f"Failed to save metadata: {e}")
178
+ raise
179
+
180
+ def _save_transcript(self, transcript: str):
181
+ """Save transcript with error handling"""
182
+ try:
183
+ transcript_file = self.bundle_path / "transcript.md"
184
+ with open(transcript_file, "w") as f:
185
+ f.write(transcript)
186
+ logger.debug(f"Saved transcript to {transcript_file}")
187
+ except Exception as e:
188
+ logger.error(f"Failed to save transcript: {e}")
189
+ raise
190
+
191
+ @retry(
192
+ stop=stop_after_attempt(3),
193
+ wait=wait_exponential(multiplier=1, min=2, max=10),
194
+ retry=retry_if_exception_type(ArtifactCopyError)
195
+ )
196
+ def _copy_single_artifact(self, artifact_path: Path, dest_path: Path):
197
+ """
198
+ Copy a single artifact with retry logic
199
+
200
+ Raises:
201
+ ArtifactCopyError: If copy fails after retries
202
+ """
203
+ try:
204
+ shutil.copy2(artifact_path, dest_path)
205
+
206
+ # Verify copy
207
+ if not dest_path.exists():
208
+ raise ArtifactCopyError(f"Destination file not created: {dest_path}")
209
+
210
+ if dest_path.stat().st_size == 0 and artifact_path.stat().st_size > 0:
211
+ raise ArtifactCopyError(f"Destination file empty: {dest_path}")
212
+
213
+ logger.debug(f"Successfully copied artifact: {artifact_path.name}")
214
+
215
+ except Exception as e:
216
+ logger.warning(f"Failed to copy {artifact_path}: {e}")
217
+ raise ArtifactCopyError(f"Copy failed: {e}") from e
218
+
219
+ def _copy_artifacts(self, artifacts: List[str]) -> List[str]:
220
+ """
221
+ Copy artifact files to bundle directory with graceful degradation
222
+
223
+ Returns:
224
+ List of error messages for artifacts that failed to copy
225
+ """
226
+ artifacts_dir = self.bundle_path / "artifacts"
227
+ artifacts_dir.mkdir(exist_ok=True)
228
+
229
+ errors = []
230
+ successful_copies = 0
231
+
232
+ for artifact_path_str in artifacts:
233
+ artifact_file = Path(artifact_path_str)
234
+
235
+ if not artifact_file.exists():
236
+ error_msg = f"Artifact not found: {artifact_path_str}"
237
+ logger.warning(error_msg)
238
+ errors.append(error_msg)
239
+ continue
240
+
241
+ try:
242
+ dest_path = artifacts_dir / artifact_file.name
243
+ self._copy_single_artifact(artifact_file, dest_path)
244
+ successful_copies += 1
245
+
246
+ except Exception as e:
247
+ error_msg = f"Failed to copy {artifact_path_str}: {str(e)}"
248
+ logger.error(error_msg)
249
+ errors.append(error_msg)
250
+ # Continue with other artifacts
251
+
252
+ logger.info(f"Copied {successful_copies}/{len(artifacts)} artifacts successfully")
253
+
254
+ if errors:
255
+ logger.warning(f"Encountered {len(errors)} errors during artifact copy")
256
+
257
+ return errors
258
+
259
+ def _generate_summary(self, task_info: Dict[str, Any], artifacts: List[str],
260
+ copy_errors: List[str]):
261
+ """Generate bundle summary with error information"""
262
+ try:
263
+ summary_content = [
264
+ f"# Bundle Summary - {self.session_manager.session_id}",
265
+ f"",
266
+ f"**Date**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
267
+ f"**Task**: {task_info.get('task_id', 'Unknown')} - {task_info.get('description', 'Unknown')}",
268
+ f"**Agent**: {task_info.get('agent', 'Unknown')}",
269
+ f"**Security Tier**: {task_info.get('tier', 'Unknown')}",
270
+ f"",
271
+ f"## Artifacts Generated",
272
+ f"",
273
+ ]
274
+
275
+ for artifact in artifacts:
276
+ artifact_name = Path(artifact).name
277
+ status = "❌" if any(artifact in err for err in copy_errors) else "✅"
278
+ summary_content.append(f"{status} `{artifact_name}`: {artifact}")
279
+
280
+ if copy_errors:
281
+ summary_content.extend([
282
+ f"",
283
+ f"## ⚠️ Errors Encountered",
284
+ f"",
285
+ ])
286
+ for error in copy_errors:
287
+ summary_content.append(f"- {error}")
288
+
289
+ summary_content.extend([
290
+ f"",
291
+ f"## Task Context",
292
+ f"",
293
+ f"- **Working Directory**: {task_info.get('working_dir', 'Unknown')}",
294
+ f"- **Tags**: {', '.join(task_info.get('tags', []))}",
295
+ f"- **Project Context**: {task_info.get('project_context', 'None')}",
296
+ f"",
297
+ f"## Next Steps",
298
+ f"",
299
+ f"Review artifacts in `artifacts/` directory and check transcript for detailed execution log.",
300
+ f"",
301
+ ])
302
+
303
+ summary_file = self.bundle_path / "summary.md"
304
+ with open(summary_file, "w") as f:
305
+ f.write("\n".join(summary_content))
306
+
307
+ logger.debug(f"Generated summary at {summary_file}")
308
+
309
+ except Exception as e:
310
+ logger.error(f"Failed to generate summary: {e}")
311
+ # Non-critical, don't raise
312
+
313
+ def _update_latest_link(self):
314
+ """Update latest bundle symlink"""
315
+ latest_link = self.bundle_path.parent / "latest"
316
+
317
+ try:
318
+ # Remove existing symlink
319
+ if latest_link.is_symlink():
320
+ latest_link.unlink()
321
+
322
+ # Create new symlink
323
+ latest_link.symlink_to(self.bundle_path.name)
324
+ logger.debug(f"Updated latest symlink to {self.bundle_path.name}")
325
+
326
+ except Exception as e:
327
+ logger.warning(f"Could not create latest symlink: {e}")
328
+ # Non-critical, don't raise
329
+
330
+ class ArtifactCollector:
331
+ """Collects artifacts generated during agent execution"""
332
+
333
+ def __init__(self, working_dir: str = "."):
334
+ self.working_dir = Path(working_dir)
335
+ self.common_artifact_patterns = [
336
+ "*-validation-report.md",
337
+ "*-results-summary.md",
338
+ "*.tfplan",
339
+ "*-analysis.md",
340
+ "*-recommendations.md",
341
+ "security-scan-results.md",
342
+ "terraform-validation-report.md",
343
+ "gitops-validation-report.md",
344
+ "gcp-health-queries.md",
345
+ "gcp-health-results-summary.md",
346
+ ]
347
+ logger.info(f"ArtifactCollector initialized with working_dir={self.working_dir}")
348
+
349
+ def collect_artifacts(self, task_id: str = None) -> List[str]:
350
+ """
351
+ Collect all relevant artifacts from working directory with error handling
352
+
353
+ Returns:
354
+ List of artifact paths
355
+ """
356
+ artifacts = []
357
+
358
+ try:
359
+ # Collect by pattern
360
+ for pattern in self.common_artifact_patterns:
361
+ try:
362
+ matching_files = list(self.working_dir.glob(pattern))
363
+ artifacts.extend([str(f) for f in matching_files])
364
+ except Exception as e:
365
+ logger.warning(f"Pattern {pattern} failed: {e}")
366
+ continue
367
+
368
+ # Collect task-specific artifacts
369
+ if task_id:
370
+ try:
371
+ task_pattern = f"*{task_id.lower()}*"
372
+ task_files = list(self.working_dir.glob(task_pattern))
373
+ artifacts.extend([str(f) for f in task_files if f.suffix in ['.md', '.txt', '.json', '.yaml']])
374
+ except Exception as e:
375
+ logger.warning(f"Task-specific collection failed: {e}")
376
+
377
+ # Collect recent agent-generated files (created in last hour)
378
+ try:
379
+ import time
380
+ current_time = time.time()
381
+ agent_keywords = ['report', 'analysis', 'validation', 'summary', 'results', 'recommendations']
382
+ for file_path in self.working_dir.rglob("*"):
383
+ if file_path.is_file():
384
+ file_age = current_time - file_path.stat().st_mtime
385
+ if file_age < 3600 and file_path.suffix in ['.md', '.txt', '.json', '.yaml']:
386
+ if any(keyword in file_path.name.lower() for keyword in agent_keywords):
387
+ artifacts.append(str(file_path))
388
+ except Exception as e:
389
+ logger.warning(f"Recent file collection failed: {e}")
390
+
391
+ # Remove duplicates
392
+ artifacts = list(set(artifacts))
393
+
394
+ # Filter out system files
395
+ artifacts = [a for a in artifacts if not any(exclude in a for exclude in ['.git', '__pycache__', '.claude'])]
396
+
397
+ logger.info(f"Collected {len(artifacts)} artifacts")
398
+ return artifacts
399
+
400
+ except Exception as e:
401
+ logger.error(f"Error collecting artifacts: {e}")
402
+ return [] # Return empty list on error, don't crash
403
+
404
+ def subagent_stop_hook(task_info: Dict[str, Any], agent_output: str) -> Dict[str, Any]:
405
+ """
406
+ Main subagent stop hook - OPTION A: SILENT MODE
407
+
408
+ Philosophy:
409
+ - Only updates .claude/session/active/ with session state
410
+ - Does NOT create bundles automatically
411
+ - Bundles are created on-demand via /save-session
412
+ - Keeps everything silent and non-intrusive
413
+
414
+ Args:
415
+ task_info: Task information including ID, description, agent, etc.
416
+ agent_output: Complete output from agent execution
417
+
418
+ Returns:
419
+ Success confirmation (no bundle creation)
420
+ """
421
+
422
+ try:
423
+ # Initialize session manager (NOT bundle manager)
424
+ session_manager = SessionManager()
425
+
426
+ # Update .claude/session/active/ with current session state
427
+ active_dir = Path(session_manager._find_claude_dir()) / "session" / "active"
428
+ active_dir.mkdir(parents=True, exist_ok=True)
429
+
430
+ # Save session context for this agent execution
431
+ session_context = {
432
+ "timestamp": datetime.now().isoformat(),
433
+ "session_id": session_manager.session_id,
434
+ "task_id": task_info.get('task_id', 'unknown'),
435
+ "agent": task_info.get('agent', 'unknown'),
436
+ "description": task_info.get('description', ''),
437
+ "tier": task_info.get('tier', 'unknown'),
438
+ "tags": task_info.get('tags', []),
439
+ "working_dir": str(task_info.get('working_dir', '.')),
440
+ "project_context": task_info.get('project_context', ''),
441
+ # Store agent output for later retrieval
442
+ "agent_output_available": len(agent_output) > 0
443
+ }
444
+
445
+ # Save context as JSON
446
+ context_file = active_dir / "context.json"
447
+ with open(context_file, "w") as f:
448
+ json.dump(session_context, f, indent=2)
449
+
450
+ logger.debug(f"✅ Updated session context: {context_file}")
451
+
452
+ # Silently collect artifacts (for later use by /save-session)
453
+ artifact_collector = ArtifactCollector()
454
+ artifacts = artifact_collector.collect_artifacts(task_info.get('task_id'))
455
+ logger.debug(f"📊 Artifacts available for bundling: {len(artifacts)}")
456
+
457
+ # Return success (silent - no bundle created yet)
458
+ return {
459
+ "success": True,
460
+ "session_id": session_manager.session_id,
461
+ "status": "active_updated",
462
+ "artifacts_available": len(artifacts),
463
+ "note": "Bundle creation deferred to /save-session"
464
+ }
465
+
466
+ except Exception as e:
467
+ logger.debug(f"⚠️ Error updating session context: {e}")
468
+ # Don't fail completely - just log and continue
469
+ return {
470
+ "success": False,
471
+ "error": str(e),
472
+ "status": "partial_update",
473
+ "note": "Session context update failed, but agent execution may have succeeded"
474
+ }
475
+
476
+ def main():
477
+ """CLI interface for testing bundle creation"""
478
+
479
+ if len(sys.argv) < 2:
480
+ print("Usage: python subagent_stop.py <task_id>")
481
+ print(" python subagent_stop.py --test")
482
+ sys.exit(1)
483
+
484
+ if sys.argv[1] == "--test":
485
+ # Test bundle creation
486
+ test_task_info = {
487
+ "task_id": "T006",
488
+ "description": "Terraform plan for infrastructure",
489
+ "agent": "terraform-specialist",
490
+ "tier": "T1",
491
+ "tags": ["#terraform", "#infrastructure"],
492
+ "working_dir": os.getcwd(),
493
+ "project_context": "Multi-project repository with TCM application"
494
+ }
495
+
496
+ test_output = """
497
+ # Terraform Specialist Execution Log
498
+
499
+ ## Task: T006 - Terraform plan for infrastructure
500
+
501
+ ### Actions Performed:
502
+ 1. ✅ Format check: terraform fmt -check
503
+ 2. ✅ Initialization: terraform init -backend=false
504
+ 3. ✅ Validation: terraform validate
505
+ 4. ✅ Planning: terraform plan -out=tfplan
506
+
507
+ ### Results:
508
+ - Configuration is properly formatted
509
+ - All modules validated successfully
510
+ - Plan generated with 12 resources to create
511
+ - No security issues detected
512
+
513
+ ### Artifacts Generated:
514
+ - terraform-validation-report.md
515
+ - infrastructure.tfplan
516
+ - security-scan-results.md
517
+
518
+ ### Recommendations:
519
+ - Review tfplan before any apply operations
520
+ - Consider implementing resource tagging
521
+ - Enable state locking for production
522
+ """
523
+
524
+ result = subagent_stop_hook(test_task_info, test_output)
525
+
526
+ if result["success"]:
527
+ print("✅ Test bundle creation successful!")
528
+ print(f"📁 Bundle path: {result['bundle_path']}")
529
+ print(f"📊 Artifacts: {result['artifacts_count']}")
530
+
531
+ # Show bundle contents
532
+ bundle_path = Path(result["bundle_path"])
533
+ if bundle_path.exists():
534
+ print("\n📋 Bundle contents:")
535
+ for item in sorted(bundle_path.rglob("*")):
536
+ if item.is_file():
537
+ relative_path = item.relative_to(bundle_path)
538
+ print(f" {relative_path}")
539
+ else:
540
+ print(f"❌ Test failed: {result['error']}")
541
+
542
+ else:
543
+ task_id = sys.argv[1]
544
+ print(f"Creating bundle for task: {task_id}")
545
+ # In real usage, this would be called by the agent system
546
+ print("Note: This would normally be called automatically by the agent system")
547
+
548
+ if __name__ == "__main__":
549
+ main()
package/index.js ADDED
@@ -0,0 +1,92 @@
1
+ /**
2
+ * @aaxis/claude-agents
3
+ *
4
+ * Shared Claude Code agent system for Aaxis DevOps workflows
5
+ *
6
+ * Usage:
7
+ * import { getAgentPath, getToolPath } from '@aaxis/claude-agents';
8
+ * const agentPath = getAgentPath('gitops-operator');
9
+ * const toolPath = getToolPath('context_provider.py');
10
+ */
11
+
12
+ import { fileURLToPath } from 'url';
13
+ import { dirname, join } from 'path';
14
+
15
+ const __filename = fileURLToPath(import.meta.url);
16
+ const __dirname = dirname(__filename);
17
+
18
+ export const PACKAGE_ROOT = __dirname;
19
+
20
+ /**
21
+ * Get absolute path to an agent definition
22
+ * @param {string} agentName - Name of the agent (e.g., 'gitops-operator')
23
+ * @returns {string} Absolute path to agent file
24
+ */
25
+ export function getAgentPath(agentName) {
26
+ return join(PACKAGE_ROOT, 'agents', `${agentName}.md`);
27
+ }
28
+
29
+ /**
30
+ * Get absolute path to a tool
31
+ * @param {string} toolName - Name of the tool (e.g., 'context_provider.py')
32
+ * @returns {string} Absolute path to tool file
33
+ */
34
+ export function getToolPath(toolName) {
35
+ return join(PACKAGE_ROOT, 'tools', toolName);
36
+ }
37
+
38
+ /**
39
+ * Get absolute path to a hook
40
+ * @param {string} hookName - Name of the hook (e.g., 'pre-commit')
41
+ * @returns {string} Absolute path to hook file
42
+ */
43
+ export function getHookPath(hookName) {
44
+ return join(PACKAGE_ROOT, 'hooks', hookName);
45
+ }
46
+
47
+ /**
48
+ * Get absolute path to a command
49
+ * @param {string} commandName - Name of the command (e.g., 'architect.md')
50
+ * @returns {string} Absolute path to command file
51
+ */
52
+ export function getCommandPath(commandName) {
53
+ return join(PACKAGE_ROOT, 'commands', commandName);
54
+ }
55
+
56
+ /**
57
+ * Get absolute path to documentation
58
+ * @param {string} docName - Name of the doc (e.g., 'orchestration-workflow.md')
59
+ * @returns {string} Absolute path to doc file
60
+ */
61
+ export function getDocPath(docName) {
62
+ return join(PACKAGE_ROOT, 'docs', docName);
63
+ }
64
+
65
+ /**
66
+ * Get absolute path to a template
67
+ * @param {string} templateName - Name of the template (e.g., 'CLAUDE.template.md')
68
+ * @returns {string} Absolute path to template file
69
+ */
70
+ export function getTemplatePath(templateName) {
71
+ return join(PACKAGE_ROOT, 'templates', templateName);
72
+ }
73
+
74
+ /**
75
+ * Get absolute path to config file
76
+ * @param {string} configName - Name of the config (e.g., 'git_standards.json')
77
+ * @returns {string} Absolute path to config file
78
+ */
79
+ export function getConfigPath(configName) {
80
+ return join(PACKAGE_ROOT, 'config', configName);
81
+ }
82
+
83
+ export default {
84
+ PACKAGE_ROOT,
85
+ getAgentPath,
86
+ getToolPath,
87
+ getHookPath,
88
+ getCommandPath,
89
+ getDocPath,
90
+ getTemplatePath,
91
+ getConfigPath
92
+ };
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@jaguilar87/gaia-ops",
3
+ "version": "1.0.0",
4
+ "description": "Multi-agent orchestration system for Claude Code - DevOps automation toolkit",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "gaia-init": "./bin/gaia-init.js"
9
+ },
10
+ "keywords": [
11
+ "claude-code",
12
+ "devops",
13
+ "gitops",
14
+ "terraform",
15
+ "kubernetes",
16
+ "ai-agents",
17
+ "gaia-ops",
18
+ "orchestration",
19
+ "automation"
20
+ ],
21
+ "author": "Jorge Aguilar <jaguilar1897@gmail.com>",
22
+ "license": "MIT",
23
+ "private": false,
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/metraton/gaia-ops.git"
27
+ },
28
+ "files": [
29
+ "bin/",
30
+ "agents/",
31
+ "tools/",
32
+ "hooks/",
33
+ "commands/",
34
+ "templates/",
35
+ "config/",
36
+ "speckit/",
37
+ "CLAUDE.md",
38
+ "CHANGELOG.md",
39
+ "index.js"
40
+ ],
41
+ "scripts": {
42
+ "test": "pytest tests/ -v",
43
+ "validate": "python3 tools/commit_validator.py",
44
+ "lint": "eslint ."
45
+ },
46
+ "dependencies": {
47
+ "prompts": "^2.4.2",
48
+ "chalk": "^5.3.0",
49
+ "ora": "^7.0.1",
50
+ "yargs": "^17.7.2"
51
+ },
52
+ "devDependencies": {
53
+ "eslint": "^8.50.0"
54
+ },
55
+ "engines": {
56
+ "node": ">=18.0.0",
57
+ "python": ">=3.9"
58
+ }
59
+ }