@jaguilar87/gaia-ops 3.10.0 → 3.10.2
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/bin/gaia-doctor.js +0 -0
- package/bin/gaia-review.js +0 -0
- package/bin/gaia-update.js +0 -0
- package/hooks/pre_tool_use.py +94 -3
- package/package.json +1 -1
- package/templates/CLAUDE.template.md +13 -3
package/bin/gaia-doctor.js
CHANGED
|
File without changes
|
package/bin/gaia-review.js
CHANGED
|
File without changes
|
package/bin/gaia-update.js
CHANGED
|
File without changes
|
package/hooks/pre_tool_use.py
CHANGED
|
@@ -66,6 +66,92 @@ PROJECT_AGENTS = [
|
|
|
66
66
|
]
|
|
67
67
|
|
|
68
68
|
|
|
69
|
+
def _load_agent_skills(subagent_type: str) -> str:
|
|
70
|
+
"""
|
|
71
|
+
Load skill content for a project agent by reading its frontmatter.
|
|
72
|
+
|
|
73
|
+
Reads the agent definition to find 'skills:' list, then loads each
|
|
74
|
+
SKILL.md file and returns concatenated content.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
subagent_type: Agent name (e.g. 'cloud-troubleshooter')
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Concatenated skill content or empty string
|
|
81
|
+
"""
|
|
82
|
+
# Find agent definition
|
|
83
|
+
agent_paths = [
|
|
84
|
+
Path(f".claude/agents/{subagent_type}.md"),
|
|
85
|
+
Path(__file__).parent.parent / "agents" / f"{subagent_type}.md",
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
agent_file = None
|
|
89
|
+
for p in agent_paths:
|
|
90
|
+
if p.exists():
|
|
91
|
+
agent_file = p
|
|
92
|
+
break
|
|
93
|
+
|
|
94
|
+
if not agent_file:
|
|
95
|
+
return ""
|
|
96
|
+
|
|
97
|
+
# Parse frontmatter to extract skills list
|
|
98
|
+
try:
|
|
99
|
+
text = agent_file.read_text()
|
|
100
|
+
if not text.startswith("---"):
|
|
101
|
+
return ""
|
|
102
|
+
end = text.index("---", 3)
|
|
103
|
+
frontmatter = text[3:end]
|
|
104
|
+
|
|
105
|
+
skills = []
|
|
106
|
+
in_skills = False
|
|
107
|
+
for line in frontmatter.splitlines():
|
|
108
|
+
stripped = line.strip()
|
|
109
|
+
if stripped.startswith("skills:"):
|
|
110
|
+
in_skills = True
|
|
111
|
+
continue
|
|
112
|
+
if in_skills:
|
|
113
|
+
if stripped.startswith("- "):
|
|
114
|
+
skills.append(stripped[2:].strip())
|
|
115
|
+
else:
|
|
116
|
+
break
|
|
117
|
+
except Exception:
|
|
118
|
+
return ""
|
|
119
|
+
|
|
120
|
+
if not skills:
|
|
121
|
+
return ""
|
|
122
|
+
|
|
123
|
+
# Load each skill file
|
|
124
|
+
skills_dir_paths = [
|
|
125
|
+
Path(".claude/skills"),
|
|
126
|
+
Path(__file__).parent.parent / "skills",
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
skills_dir = None
|
|
130
|
+
for p in skills_dir_paths:
|
|
131
|
+
if p.is_dir():
|
|
132
|
+
skills_dir = p
|
|
133
|
+
break
|
|
134
|
+
|
|
135
|
+
if not skills_dir:
|
|
136
|
+
return ""
|
|
137
|
+
|
|
138
|
+
parts = []
|
|
139
|
+
for skill_name in skills:
|
|
140
|
+
skill_file = skills_dir / skill_name / "SKILL.md"
|
|
141
|
+
if skill_file.exists():
|
|
142
|
+
content = skill_file.read_text().strip()
|
|
143
|
+
# Strip frontmatter from skill content
|
|
144
|
+
if content.startswith("---"):
|
|
145
|
+
try:
|
|
146
|
+
end_idx = content.index("---", 3)
|
|
147
|
+
content = content[end_idx + 3:].strip()
|
|
148
|
+
except ValueError:
|
|
149
|
+
pass
|
|
150
|
+
parts.append(content)
|
|
151
|
+
|
|
152
|
+
return "\n\n---\n\n".join(parts) if parts else ""
|
|
153
|
+
|
|
154
|
+
|
|
69
155
|
|
|
70
156
|
|
|
71
157
|
def _should_inject_on_resume(parameters: dict) -> bool:
|
|
@@ -249,12 +335,16 @@ def _inject_project_context(parameters: dict) -> dict:
|
|
|
249
335
|
# Check pending update count (non-blocking, fast path)
|
|
250
336
|
pending_warning = _check_pending_updates_threshold()
|
|
251
337
|
|
|
252
|
-
#
|
|
338
|
+
# Load skills content for this agent
|
|
339
|
+
skills_content = _load_agent_skills(subagent_type)
|
|
340
|
+
skills_section = f"\n\n---\n\n# Agent Skills (Auto-Injected)\n\n{skills_content}" if skills_content else ""
|
|
341
|
+
|
|
342
|
+
# Inject context and skills into prompt
|
|
253
343
|
enriched_prompt = f"""# Project Context (Auto-Injected)
|
|
254
344
|
|
|
255
345
|
{json.dumps(context_payload, indent=2)}
|
|
256
346
|
|
|
257
|
-
{pending_warning}---
|
|
347
|
+
{pending_warning}---{skills_section}
|
|
258
348
|
|
|
259
349
|
# User Task
|
|
260
350
|
|
|
@@ -271,8 +361,9 @@ def _inject_project_context(parameters: dict) -> dict:
|
|
|
271
361
|
context_level = context_payload.get("metadata", {}).get("context_level", "unknown")
|
|
272
362
|
standards_count = context_payload.get("metadata", {}).get("standards_count", 0)
|
|
273
363
|
|
|
364
|
+
skills_loaded = bool(skills_content)
|
|
274
365
|
logger.info(
|
|
275
|
-
f"✅ Context injected for {subagent_type} "
|
|
366
|
+
f"✅ Context{'+ skills' if skills_loaded else ''} injected for {subagent_type} "
|
|
276
367
|
f"(level={context_level}, standards={standards_count})"
|
|
277
368
|
)
|
|
278
369
|
|
package/package.json
CHANGED
|
@@ -92,9 +92,19 @@ If PLAN_STATUS is:
|
|
|
92
92
|
|
|
93
93
|
## When to Delegate vs. Answer Directly
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
### Answer Directly:
|
|
96
|
+
- Response <200 tokens
|
|
97
|
+
- No code execution needed
|
|
98
|
+
- Simple status query answerable from project-context.json
|
|
99
|
+
|
|
100
|
+
### MUST Delegate:
|
|
101
|
+
- Infrastructure operations (terraform, kubectl, gcloud, aws, helm, flux)
|
|
102
|
+
- Cloud diagnostics (cluster status, namespaces, pods, logs, resources)
|
|
103
|
+
- Multi-file operations (>2 files)
|
|
104
|
+
- T3 operations (apply, deploy, create, delete)
|
|
105
|
+
- Code execution (npm, docker, build, test)
|
|
106
|
+
- Anything requiring credentials (GCP, AWS, K8s)
|
|
107
|
+
- Complex troubleshooting
|
|
98
108
|
|
|
99
109
|
## System Paths
|
|
100
110
|
|