claude-all-hands 1.0.1 → 1.0.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/.claude/agents/code-simplifier.md +52 -0
- package/.claude/agents/curator.md +189 -245
- package/.claude/agents/documentor.md +147 -0
- package/.claude/agents/planner.md +123 -166
- package/.claude/agents/researcher.md +58 -41
- package/.claude/agents/surveyor.md +81 -0
- package/.claude/agents/worker.md +74 -0
- package/.claude/commands/audit-docs.md +94 -0
- package/.claude/commands/continue.md +120 -0
- package/.claude/commands/create-docs.md +100 -0
- package/.claude/commands/create-skill.md +107 -0
- package/.claude/commands/create-specialist.md +111 -0
- package/.claude/commands/curator-audit.md +4 -0
- package/.claude/commands/debug.md +183 -0
- package/.claude/commands/plan.md +199 -102
- package/.claude/commands/validate.md +11 -0
- package/.claude/commands/whats-next.md +106 -134
- package/.claude/envoy/envoy +11 -14
- package/.claude/envoy/package-lock.json +1388 -0
- package/.claude/envoy/package.json +29 -0
- package/.claude/envoy/src/cli.ts +126 -0
- package/.claude/envoy/src/commands/base.ts +216 -0
- package/.claude/envoy/src/commands/gemini.ts +999 -0
- package/.claude/envoy/src/commands/git.ts +639 -0
- package/.claude/envoy/src/commands/index.ts +73 -0
- package/.claude/envoy/src/commands/knowledge.ts +187 -0
- package/.claude/envoy/src/commands/perplexity.ts +129 -0
- package/.claude/envoy/src/commands/plan/core.ts +134 -0
- package/.claude/envoy/src/commands/plan/findings.ts +446 -0
- package/.claude/envoy/src/commands/plan/gates.ts +672 -0
- package/.claude/envoy/src/commands/plan/index.ts +135 -0
- package/.claude/envoy/src/commands/plan/lifecycle.ts +648 -0
- package/.claude/envoy/src/commands/plan/plan-file.ts +138 -0
- package/.claude/envoy/src/commands/plan/prompts.ts +285 -0
- package/.claude/envoy/src/commands/plan/protocols.ts +166 -0
- package/.claude/envoy/src/commands/repomix.ts +99 -0
- package/.claude/envoy/src/commands/tavily.ts +220 -0
- package/.claude/envoy/src/commands/xai.ts +168 -0
- package/.claude/envoy/src/lib/design.ts +41 -0
- package/.claude/envoy/src/lib/feedback-schemas.ts +154 -0
- package/.claude/envoy/src/lib/findings.ts +215 -0
- package/.claude/envoy/src/lib/gates.ts +572 -0
- package/.claude/envoy/src/lib/git.ts +132 -0
- package/.claude/envoy/src/lib/index.ts +188 -0
- package/.claude/envoy/src/lib/knowledge.ts +594 -0
- package/.claude/envoy/src/lib/markdown.ts +75 -0
- package/.claude/envoy/src/lib/observability.ts +262 -0
- package/.claude/envoy/src/lib/paths.ts +130 -0
- package/.claude/envoy/src/lib/plan-io.ts +117 -0
- package/.claude/envoy/src/lib/prompts.ts +231 -0
- package/.claude/envoy/src/lib/protocols.ts +314 -0
- package/.claude/envoy/src/lib/repomix.ts +133 -0
- package/.claude/envoy/src/lib/retry.ts +138 -0
- package/.claude/envoy/src/lib/watcher.ts +167 -0
- package/.claude/envoy/tsconfig.json +21 -0
- package/.claude/hooks/scripts/scan_agents.py +62 -0
- package/.claude/hooks/scripts/scan_commands.py +50 -0
- package/.claude/hooks/scripts/scan_skills.py +46 -70
- package/.claude/hooks/scripts/validate_artifacts.py +128 -0
- package/.claude/hooks/startup.sh +26 -24
- package/.claude/protocols/bug-discovery.yaml +55 -0
- package/.claude/protocols/debugging.yaml +51 -0
- package/.claude/protocols/discovery.yaml +53 -0
- package/.claude/protocols/implementation.yaml +84 -0
- package/.claude/settings.json +37 -97
- package/.claude/skills/brainstorming/SKILL.md +54 -0
- package/.claude/skills/commands-development/SKILL.md +630 -0
- package/.claude/skills/commands-development/references/arguments.md +252 -0
- package/.claude/skills/commands-development/references/patterns.md +796 -0
- package/.claude/skills/commands-development/references/tool-restrictions.md +376 -0
- package/.claude/skills/discovery-mode/SKILL.md +108 -0
- package/.claude/skills/hooks-development/SKILL.md +332 -0
- package/.claude/skills/hooks-development/references/command-vs-prompt.md +269 -0
- package/.claude/skills/hooks-development/references/examples.md +658 -0
- package/.claude/skills/hooks-development/references/hook-types.md +463 -0
- package/.claude/skills/hooks-development/references/input-output-schemas.md +469 -0
- package/.claude/skills/hooks-development/references/matchers.md +470 -0
- package/.claude/skills/hooks-development/references/troubleshooting.md +587 -0
- package/.claude/skills/implementation-mode/SKILL.md +171 -0
- package/.claude/skills/research-tools/SKILL.md +35 -33
- package/.claude/skills/skills-development/SKILL.md +192 -0
- package/.claude/skills/skills-development/references/api-security.md +226 -0
- package/.claude/skills/skills-development/references/be-clear-and-direct.md +531 -0
- package/.claude/skills/skills-development/references/common-patterns.md +595 -0
- package/.claude/skills/skills-development/references/core-principles.md +437 -0
- package/.claude/skills/skills-development/references/executable-code.md +175 -0
- package/.claude/skills/skills-development/references/iteration-and-testing.md +474 -0
- package/.claude/skills/skills-development/references/recommended-structure.md +168 -0
- package/.claude/skills/skills-development/references/skill-structure.md +372 -0
- package/.claude/skills/skills-development/references/use-xml-tags.md +466 -0
- package/.claude/skills/skills-development/references/using-scripts.md +113 -0
- package/.claude/skills/skills-development/references/using-templates.md +112 -0
- package/.claude/skills/skills-development/references/workflows-and-validation.md +510 -0
- package/.claude/skills/skills-development/templates/router-skill.md +73 -0
- package/.claude/skills/skills-development/templates/simple-skill.md +33 -0
- package/.claude/skills/skills-development/workflows/add-reference.md +96 -0
- package/.claude/skills/skills-development/workflows/add-script.md +93 -0
- package/.claude/skills/skills-development/workflows/add-template.md +74 -0
- package/.claude/skills/skills-development/workflows/add-workflow.md +120 -0
- package/.claude/skills/skills-development/workflows/audit-skill.md +138 -0
- package/.claude/skills/skills-development/workflows/create-domain-expertise-skill.md +605 -0
- package/.claude/skills/skills-development/workflows/create-new-skill.md +191 -0
- package/.claude/skills/skills-development/workflows/get-guidance.md +121 -0
- package/.claude/skills/skills-development/workflows/upgrade-to-router.md +161 -0
- package/.claude/skills/skills-development/workflows/verify-skill.md +204 -0
- package/.claude/skills/subagents-development/SKILL.md +325 -0
- package/.claude/skills/subagents-development/references/context-management.md +567 -0
- package/.claude/skills/subagents-development/references/debugging-agents.md +714 -0
- package/.claude/skills/subagents-development/references/error-handling-and-recovery.md +502 -0
- package/.claude/skills/subagents-development/references/evaluation-and-testing.md +374 -0
- package/.claude/skills/subagents-development/references/orchestration-patterns.md +591 -0
- package/.claude/skills/subagents-development/references/subagents.md +508 -0
- package/.claude/skills/subagents-development/references/writing-subagent-prompts.md +517 -0
- package/.claude/statusline.sh +24 -0
- package/bin/cli.js +110 -72
- package/package.json +1 -1
- package/.claude/agents/explorer.md +0 -62
- package/.claude/agents/parallel-worker.md +0 -121
- package/.claude/commands/curation-fix.md +0 -92
- package/.claude/commands/new-branch.md +0 -36
- package/.claude/commands/parallel-discovery.md +0 -69
- package/.claude/commands/parallel-orchestration.md +0 -99
- package/.claude/commands/plan-checkpoint.md +0 -37
- package/.claude/envoy/commands/__init__.py +0 -1
- package/.claude/envoy/commands/base.py +0 -95
- package/.claude/envoy/commands/parallel.py +0 -439
- package/.claude/envoy/commands/perplexity.py +0 -86
- package/.claude/envoy/commands/plans.py +0 -451
- package/.claude/envoy/commands/tavily.py +0 -156
- package/.claude/envoy/commands/vertex.py +0 -358
- package/.claude/envoy/commands/xai.py +0 -124
- package/.claude/envoy/envoy.py +0 -122
- package/.claude/envoy/pyrightconfig.json +0 -4
- package/.claude/envoy/requirements.txt +0 -2
- package/.claude/hooks/capture-queries.sh +0 -3
- package/.claude/hooks/scripts/enforce_planning.py +0 -118
- package/.claude/hooks/scripts/enforce_rg.py +0 -34
- package/.claude/hooks/scripts/validate_skill.py +0 -81
- package/.claude/skills/claude-envoy-curation/SKILL.md +0 -162
- package/.claude/skills/claude-envoy-usage/SKILL.md +0 -46
- package/.claude/skills/command-development/SKILL.md +0 -206
- package/.claude/skills/command-development/examples/simple-commands.md +0 -212
- package/.claude/skills/command-development/references/frontmatter-reference.md +0 -221
- package/.claude/skills/hook-development/SKILL.md +0 -127
- package/.claude/skills/hook-development/examples/command-hooks.md +0 -301
- package/.claude/skills/hook-development/examples/prompt-hooks.md +0 -114
- package/.claude/skills/hook-development/references/event-reference.md +0 -226
- package/.claude/skills/repomix-extraction/SKILL.md +0 -91
- package/.claude/skills/skill-development/SKILL.md +0 -168
- package/.claude/skills/skill-development/examples/complete-skill-examples.md +0 -281
- package/.claude/skills/skill-development/references/progressive-disclosure.md +0 -141
- package/.claude/skills/skill-development/references/writing-style.md +0 -180
- package/.claude/skills/skill-development/scripts/validate-skill.sh +0 -144
- package/.claude/skills/specialist-builder/SKILL.md +0 -327
- package/.claude/skills/specialist-builder/docs/agent-catalog.md +0 -28
- package/.claude/skills/specialist-builder/examples/complete-agent-examples.md +0 -206
- package/.claude/skills/specialist-builder/references/system-prompt-patterns.md +0 -281
- package/.claude/skills/specialist-builder/references/triggering-examples.md +0 -162
- package/.claude/skills/specialist-builder/scripts/validate-agent.sh +0 -137
- /package/.claude/{envoy/claude-envoy.py → skills/claude-envoy-patterns/SKILL.md} +0 -0
|
@@ -1,358 +0,0 @@
|
|
|
1
|
-
"""Vertex AI (Gemini) commands."""
|
|
2
|
-
# pyright: reportAttributeAccessIssue=false
|
|
3
|
-
|
|
4
|
-
from __future__ import annotations
|
|
5
|
-
|
|
6
|
-
import json
|
|
7
|
-
import os
|
|
8
|
-
import re
|
|
9
|
-
import subprocess
|
|
10
|
-
from pathlib import Path
|
|
11
|
-
from typing import Optional
|
|
12
|
-
|
|
13
|
-
from .base import BaseCommand
|
|
14
|
-
from .plans import get_plan_dir, get_branch, is_direct_mode_branch
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def get_base_branch() -> str:
|
|
18
|
-
"""Auto-detect base branch using merge-base."""
|
|
19
|
-
# Try common base branches in order
|
|
20
|
-
for base in ["main", "master", "develop", "staging", "production"]:
|
|
21
|
-
result = subprocess.run(
|
|
22
|
-
["git", "merge-base", base, "HEAD"],
|
|
23
|
-
capture_output=True,
|
|
24
|
-
text=True,
|
|
25
|
-
)
|
|
26
|
-
if result.returncode == 0:
|
|
27
|
-
return base
|
|
28
|
-
return "main"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class VertexAskCommand(BaseCommand):
|
|
32
|
-
"""Raw Gemini inference - thin wrapper, no system prompt."""
|
|
33
|
-
|
|
34
|
-
name = "ask"
|
|
35
|
-
description = "Raw Gemini inference"
|
|
36
|
-
|
|
37
|
-
def add_arguments(self, parser) -> None:
|
|
38
|
-
parser.add_argument("query", help="Query for Gemini")
|
|
39
|
-
parser.add_argument("--files", nargs="+", help="Files to include as context")
|
|
40
|
-
parser.add_argument("--context", help="Additional context")
|
|
41
|
-
parser.add_argument("--model", default="gemini-2.0-flash", help="Model to use")
|
|
42
|
-
|
|
43
|
-
def execute(
|
|
44
|
-
self,
|
|
45
|
-
*,
|
|
46
|
-
query: str,
|
|
47
|
-
model: str = "gemini-2.0-flash",
|
|
48
|
-
files: Optional[list[str]] = None,
|
|
49
|
-
context: Optional[str] = None,
|
|
50
|
-
**kwargs,
|
|
51
|
-
) -> dict:
|
|
52
|
-
api_key = os.environ.get("VERTEX_API_KEY")
|
|
53
|
-
if not api_key:
|
|
54
|
-
return self.error("auth_error", "VERTEX_API_KEY not set")
|
|
55
|
-
|
|
56
|
-
parts = []
|
|
57
|
-
if context:
|
|
58
|
-
parts.append(context)
|
|
59
|
-
if files:
|
|
60
|
-
file_contents = self.read_files(files)
|
|
61
|
-
if file_contents:
|
|
62
|
-
file_context = "\n\n".join(f"### {path}\n```\n{content}\n```" for path, content in file_contents.items())
|
|
63
|
-
parts.append(file_context)
|
|
64
|
-
parts.append(query)
|
|
65
|
-
prompt = "\n\n".join(parts)
|
|
66
|
-
|
|
67
|
-
try:
|
|
68
|
-
response, duration_ms = self.timed_execute(self._call_api, api_key, model, prompt)
|
|
69
|
-
return self.success(
|
|
70
|
-
{"content": response},
|
|
71
|
-
{"model": model, "command": "vertex ask", "duration_ms": duration_ms},
|
|
72
|
-
)
|
|
73
|
-
except Exception as e:
|
|
74
|
-
return self.error("api_error", str(e))
|
|
75
|
-
|
|
76
|
-
def _call_api(self, api_key: str, model: str, prompt: str) -> str:
|
|
77
|
-
from google import genai
|
|
78
|
-
|
|
79
|
-
client = genai.Client(vertexai=True, api_key=api_key)
|
|
80
|
-
response = client.models.generate_content(model=model, contents=prompt)
|
|
81
|
-
return response.text
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
class VertexValidateCommand(BaseCommand):
|
|
85
|
-
"""Validate plan against user requirements."""
|
|
86
|
-
|
|
87
|
-
name = "validate"
|
|
88
|
-
description = "Validate plan against requirements (anti-overengineering)"
|
|
89
|
-
|
|
90
|
-
SYSTEM_PROMPT = """You are a plan validator ensuring implementations are NOT over-engineered.
|
|
91
|
-
|
|
92
|
-
Given user requirements and a proposed plan, evaluate:
|
|
93
|
-
- Does plan exceed what user actually asked for?
|
|
94
|
-
- Are there unnecessary abstractions/features?
|
|
95
|
-
- Is complexity justified by requirements?
|
|
96
|
-
- Are there gaps, risks, or architectural concerns?
|
|
97
|
-
|
|
98
|
-
Be thorough and direct. Only ask questions for user discretion you can't answer yourself.
|
|
99
|
-
|
|
100
|
-
IMPORTANT: This is SYSTEM VALIDATION only. User approval is a separate step after validation passes.
|
|
101
|
-
|
|
102
|
-
Validation result: "valid" or "invalid" (binary).
|
|
103
|
-
- "valid": Plan is acceptable and ready for user approval.
|
|
104
|
-
- "invalid": Plan has issues (over-engineered, unclear requirements, missing details, etc.)
|
|
105
|
-
|
|
106
|
-
The verdict_context MUST explain the reasoning - whether over-engineered, what's missing, lack of user clarification, or why it passes.
|
|
107
|
-
|
|
108
|
-
CRITICAL: If user_questions is non-empty, validation_result MUST be "invalid". A "valid" result with pending questions is not allowed.
|
|
109
|
-
|
|
110
|
-
Output JSON:
|
|
111
|
-
{
|
|
112
|
-
"validation_result": "valid" | "invalid",
|
|
113
|
-
"verdict_context": "Specific reasoning - what's wrong, over-engineering issues, missing clarification, or why it passes",
|
|
114
|
-
"recommended_edits": ["Edits to fix issues (required if invalid), or minor improvements (optional if valid)"],
|
|
115
|
-
"user_questions": ["Questions needing user input - MUST be empty if validation_result is valid"]
|
|
116
|
-
}"""
|
|
117
|
-
|
|
118
|
-
def add_arguments(self, parser) -> None:
|
|
119
|
-
parser.add_argument("--queries", help="Queries file path (optional)")
|
|
120
|
-
parser.add_argument("--context", help="Additional context")
|
|
121
|
-
|
|
122
|
-
def execute(self, *, queries: Optional[str] = None, context: Optional[str] = None, **kwargs) -> dict:
|
|
123
|
-
api_key = os.environ.get("VERTEX_API_KEY")
|
|
124
|
-
if not api_key:
|
|
125
|
-
return self.error("auth_error", "VERTEX_API_KEY not set")
|
|
126
|
-
|
|
127
|
-
if is_direct_mode_branch(get_branch()):
|
|
128
|
-
return self.error("direct_mode", "No plan in direct mode")
|
|
129
|
-
|
|
130
|
-
plan_path = get_plan_dir()
|
|
131
|
-
plan_file = plan_path / "plan.md"
|
|
132
|
-
queries_file = queries or plan_path / "queries.jsonl"
|
|
133
|
-
|
|
134
|
-
plan_content = self.read_file(str(plan_file))
|
|
135
|
-
if not plan_content:
|
|
136
|
-
return self.error("file_not_found", f"Plan file not found: {plan_file}")
|
|
137
|
-
|
|
138
|
-
queries_content = ""
|
|
139
|
-
if queries_file and Path(queries_file).exists():
|
|
140
|
-
with open(queries_file) as f:
|
|
141
|
-
queries_list = [json.loads(line).get("prompt", "") for line in f if line.strip()]
|
|
142
|
-
queries_content = "\n".join(f"- {q}" for q in queries_list)
|
|
143
|
-
|
|
144
|
-
additional = f"\n\n## Additional Context\n{context}" if context else ""
|
|
145
|
-
full_prompt = f"""{self.SYSTEM_PROMPT}
|
|
146
|
-
|
|
147
|
-
## User Requirements/Queries
|
|
148
|
-
{queries_content or "(No queries captured)"}
|
|
149
|
-
|
|
150
|
-
## Plan
|
|
151
|
-
{plan_content}
|
|
152
|
-
{additional}
|
|
153
|
-
|
|
154
|
-
Respond with JSON only."""
|
|
155
|
-
|
|
156
|
-
try:
|
|
157
|
-
response, duration_ms = self.timed_execute(self._call_api, api_key, full_prompt)
|
|
158
|
-
parsed = self._parse_json_response(response)
|
|
159
|
-
return self.success(parsed, {"command": "vertex validate", "duration_ms": duration_ms})
|
|
160
|
-
except Exception as e:
|
|
161
|
-
return self.error("api_error", str(e))
|
|
162
|
-
|
|
163
|
-
def _call_api(self, api_key: str, prompt: str) -> str:
|
|
164
|
-
from google import genai
|
|
165
|
-
|
|
166
|
-
client = genai.Client(vertexai=True, api_key=api_key)
|
|
167
|
-
response = client.models.generate_content(model="gemini-2.0-flash", contents=prompt)
|
|
168
|
-
return response.text
|
|
169
|
-
|
|
170
|
-
def _parse_json_response(self, response: str) -> dict:
|
|
171
|
-
json_match = re.search(r"\{[\s\S]*\}", response)
|
|
172
|
-
if json_match:
|
|
173
|
-
return json.loads(json_match.group())
|
|
174
|
-
return {"raw_response": response}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
class VertexReviewCommand(BaseCommand):
|
|
178
|
-
"""Review implementation against plan."""
|
|
179
|
-
|
|
180
|
-
name = "review"
|
|
181
|
-
description = "Review implementation against plan (uses git diff)"
|
|
182
|
-
|
|
183
|
-
SYSTEM_PROMPT = """You are an implementation reviewer for a lean development team.
|
|
184
|
-
|
|
185
|
-
Core philosophy: Keep it simple. Favor pragmatic shortcuts over elaborate abstractions. This is likely an early-stage or small-team product where shipping fast matters more than architectural perfection.
|
|
186
|
-
|
|
187
|
-
Given a plan and git diff of changes, evaluate:
|
|
188
|
-
- Does implementation match plan intent?
|
|
189
|
-
- Is there over-engineering? (unnecessary abstractions, premature optimization, excessive indirection)
|
|
190
|
-
- Are there simpler alternatives that would achieve the same goal?
|
|
191
|
-
- Code quality issues that actually matter?
|
|
192
|
-
|
|
193
|
-
Flag anything that adds complexity without clear immediate value.
|
|
194
|
-
|
|
195
|
-
Output JSON:
|
|
196
|
-
{
|
|
197
|
-
"verdict": "approved" | "needs_work" | "off_track",
|
|
198
|
-
"verdict_context": "Explanation with specific reasoning",
|
|
199
|
-
"over_engineering": ["Any unnecessary complexity spotted"],
|
|
200
|
-
"simplification_opportunities": ["Ways to reduce complexity"],
|
|
201
|
-
"issues": ["Actionable issues to address"],
|
|
202
|
-
"user_questions": ["Questions if clarification needed"]
|
|
203
|
-
}"""
|
|
204
|
-
|
|
205
|
-
def add_arguments(self, parser) -> None:
|
|
206
|
-
parser.add_argument("--last-commit", action="store_true", help="Diff against last commit (for step reviews)")
|
|
207
|
-
parser.add_argument("--context", help="Additional context")
|
|
208
|
-
|
|
209
|
-
def execute(self, *, last_commit: bool = False, context: Optional[str] = None, **kwargs) -> dict:
|
|
210
|
-
api_key = os.environ.get("VERTEX_API_KEY")
|
|
211
|
-
if not api_key:
|
|
212
|
-
return self.error("auth_error", "VERTEX_API_KEY not set")
|
|
213
|
-
|
|
214
|
-
if is_direct_mode_branch(get_branch()):
|
|
215
|
-
return self.error("direct_mode", "No plan in direct mode")
|
|
216
|
-
|
|
217
|
-
plan_path = get_plan_dir()
|
|
218
|
-
plan_file = plan_path / "plan.md"
|
|
219
|
-
plan_content = self.read_file(str(plan_file))
|
|
220
|
-
if not plan_content:
|
|
221
|
-
return self.error("file_not_found", f"Plan file not found: {plan_file}")
|
|
222
|
-
|
|
223
|
-
diff_ref = "HEAD~1" if last_commit else get_base_branch()
|
|
224
|
-
diff_result = subprocess.run(["git", "diff", diff_ref], capture_output=True, text=True)
|
|
225
|
-
|
|
226
|
-
# Fallback to empty tree if ref doesn't exist (fresh repo)
|
|
227
|
-
if diff_result.returncode != 0:
|
|
228
|
-
empty_tree = "4b825dc642cb6eb9a060e54bf8d69288fbee4904" # git's empty tree hash
|
|
229
|
-
diff_ref = "empty tree"
|
|
230
|
-
diff_result = subprocess.run(["git", "diff", empty_tree], capture_output=True, text=True)
|
|
231
|
-
if diff_result.returncode != 0:
|
|
232
|
-
return self.error("git_error", f"Failed to get diff: {diff_result.stderr}")
|
|
233
|
-
|
|
234
|
-
diff_content = diff_result.stdout or "(No changes)"
|
|
235
|
-
|
|
236
|
-
additional = f"\n\n## Additional Context\n{context}" if context else ""
|
|
237
|
-
full_prompt = f"""{self.SYSTEM_PROMPT}
|
|
238
|
-
|
|
239
|
-
## Plan
|
|
240
|
-
{plan_content}
|
|
241
|
-
|
|
242
|
-
## Git Diff (against {diff_ref})
|
|
243
|
-
```diff
|
|
244
|
-
{diff_content}
|
|
245
|
-
```
|
|
246
|
-
{additional}
|
|
247
|
-
|
|
248
|
-
Respond with JSON only."""
|
|
249
|
-
|
|
250
|
-
try:
|
|
251
|
-
response, duration_ms = self.timed_execute(self._call_api, api_key, full_prompt)
|
|
252
|
-
parsed = self._parse_json_response(response)
|
|
253
|
-
return self.success(parsed, {"command": "vertex review", "duration_ms": duration_ms})
|
|
254
|
-
except Exception as e:
|
|
255
|
-
return self.error("api_error", str(e))
|
|
256
|
-
|
|
257
|
-
def _call_api(self, api_key: str, prompt: str) -> str:
|
|
258
|
-
from google import genai
|
|
259
|
-
|
|
260
|
-
client = genai.Client(vertexai=True, api_key=api_key)
|
|
261
|
-
response = client.models.generate_content(model="gemini-2.0-flash", contents=prompt)
|
|
262
|
-
return response.text
|
|
263
|
-
|
|
264
|
-
def _parse_json_response(self, response: str) -> dict:
|
|
265
|
-
json_match = re.search(r"\{[\s\S]*\}", response)
|
|
266
|
-
if json_match:
|
|
267
|
-
return json.loads(json_match.group())
|
|
268
|
-
return {"raw_response": response}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
class VertexArchitectCommand(BaseCommand):
|
|
272
|
-
"""Solutions architecture guidance."""
|
|
273
|
-
|
|
274
|
-
name = "architect"
|
|
275
|
-
description = "Solutions architecture for complex features"
|
|
276
|
-
|
|
277
|
-
SYSTEM_PROMPT = """You are a solutions architect for complex software systems.
|
|
278
|
-
|
|
279
|
-
Given a feature request and optional codebase context:
|
|
280
|
-
1. Identify architectural decisions needed
|
|
281
|
-
2. Propose approaches with trade-offs
|
|
282
|
-
3. Recommend implementation strategy
|
|
283
|
-
4. Flag risks and unknowns
|
|
284
|
-
|
|
285
|
-
Output JSON:
|
|
286
|
-
{
|
|
287
|
-
"complexity_assessment": "simple" | "moderate" | "complex" | "system_integration",
|
|
288
|
-
"architectural_decisions": [{
|
|
289
|
-
"decision": "string",
|
|
290
|
-
"options": [{"option": "string", "pros": ["string"], "cons": ["string"]}],
|
|
291
|
-
"recommendation": "string",
|
|
292
|
-
"rationale": "string"
|
|
293
|
-
}],
|
|
294
|
-
"implementation_strategy": {
|
|
295
|
-
"approach": "string",
|
|
296
|
-
"phases": [{"phase": "string", "deliverables": ["string"]}],
|
|
297
|
-
"dependencies": ["string"]
|
|
298
|
-
},
|
|
299
|
-
"risks": [{"risk": "string", "mitigation": "string", "severity": "low"|"medium"|"high"}],
|
|
300
|
-
"questions_for_user": ["string"]
|
|
301
|
-
}"""
|
|
302
|
-
|
|
303
|
-
def add_arguments(self, parser) -> None:
|
|
304
|
-
parser.add_argument("query", help="Feature/system description")
|
|
305
|
-
parser.add_argument("--files", nargs="+", help="Relevant code files")
|
|
306
|
-
parser.add_argument("--context", help="Additional context or constraints")
|
|
307
|
-
|
|
308
|
-
def execute(self, *, query: str, files: Optional[list[str]] = None, context: Optional[str] = None, **kwargs) -> dict:
|
|
309
|
-
api_key = os.environ.get("VERTEX_API_KEY")
|
|
310
|
-
if not api_key:
|
|
311
|
-
return self.error("auth_error", "VERTEX_API_KEY not set")
|
|
312
|
-
|
|
313
|
-
file_context = ""
|
|
314
|
-
if files:
|
|
315
|
-
file_contents = self.read_files(files)
|
|
316
|
-
if file_contents:
|
|
317
|
-
file_context = "\n\n## Existing Code\n" + "\n\n".join(
|
|
318
|
-
f"### {path}\n```\n{content}\n```" for path, content in file_contents.items()
|
|
319
|
-
)
|
|
320
|
-
|
|
321
|
-
additional = f"\n\n## Additional Context\n{context}" if context else ""
|
|
322
|
-
|
|
323
|
-
full_prompt = f"""{self.SYSTEM_PROMPT}
|
|
324
|
-
|
|
325
|
-
## Feature Request
|
|
326
|
-
{query}
|
|
327
|
-
{file_context}
|
|
328
|
-
{additional}
|
|
329
|
-
|
|
330
|
-
Respond with JSON only."""
|
|
331
|
-
|
|
332
|
-
try:
|
|
333
|
-
response, duration_ms = self.timed_execute(self._call_api, api_key, full_prompt)
|
|
334
|
-
parsed = self._parse_json_response(response)
|
|
335
|
-
return self.success(parsed, {"command": "vertex architect", "duration_ms": duration_ms})
|
|
336
|
-
except Exception as e:
|
|
337
|
-
return self.error("api_error", str(e))
|
|
338
|
-
|
|
339
|
-
def _call_api(self, api_key: str, prompt: str) -> str:
|
|
340
|
-
from google import genai
|
|
341
|
-
|
|
342
|
-
client = genai.Client(vertexai=True, api_key=api_key)
|
|
343
|
-
response = client.models.generate_content(model="gemini-2.0-flash", contents=prompt)
|
|
344
|
-
return response.text
|
|
345
|
-
|
|
346
|
-
def _parse_json_response(self, response: str) -> dict:
|
|
347
|
-
json_match = re.search(r"\{[\s\S]*\}", response)
|
|
348
|
-
if json_match:
|
|
349
|
-
return json.loads(json_match.group())
|
|
350
|
-
return {"raw_response": response}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
COMMANDS = {
|
|
354
|
-
"ask": VertexAskCommand,
|
|
355
|
-
"validate": VertexValidateCommand,
|
|
356
|
-
"review": VertexReviewCommand,
|
|
357
|
-
"architect": VertexArchitectCommand,
|
|
358
|
-
}
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
"""xAI Grok API commands - X search for technology research."""
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
from typing import Optional
|
|
5
|
-
|
|
6
|
-
import requests
|
|
7
|
-
from .base import BaseCommand
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
SYSTEM_PROMPT = """You are a technology research assistant. Search X (Twitter) for posts about the given technology, tool, or concept.
|
|
11
|
-
|
|
12
|
-
Find and synthesize:
|
|
13
|
-
- Developer opinions and experiences
|
|
14
|
-
- Comparisons with alternatives
|
|
15
|
-
- Common issues or gotchas
|
|
16
|
-
- Recent developments or announcements
|
|
17
|
-
- Community sentiment
|
|
18
|
-
|
|
19
|
-
Return a structured summary with key findings and notable posts."""
|
|
20
|
-
|
|
21
|
-
CHALLENGER_PROMPT = """You are a critical research challenger. Given research findings, search X to:
|
|
22
|
-
|
|
23
|
-
1. CHALLENGE: Find contradicting opinions, failed implementations, known issues
|
|
24
|
-
2. ALTERNATIVES: Surface newer/better tools the research may have missed
|
|
25
|
-
3. TRENDS: Identify emerging patterns that could affect the recommendations
|
|
26
|
-
4. SENTIMENT: Gauge real developer satisfaction vs marketing claims
|
|
27
|
-
5. DISCUSSIONS: Find where the best practitioners are discussing this topic
|
|
28
|
-
|
|
29
|
-
Be skeptical. Surface what the research missed or got wrong. Focus on recent posts (last 6 months)."""
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
class XaiSearchCommand(BaseCommand):
|
|
33
|
-
"""Search X for technology insights using Grok with X Search tool."""
|
|
34
|
-
|
|
35
|
-
name = "search"
|
|
36
|
-
description = "Search X for technology opinions, alternatives, and community insights"
|
|
37
|
-
|
|
38
|
-
def add_arguments(self, parser) -> None:
|
|
39
|
-
parser.add_argument("query", help="Technology/topic to research on X")
|
|
40
|
-
parser.add_argument("--context", help="Previous research findings to build upon")
|
|
41
|
-
parser.add_argument("--results-to-challenge", help="Research results to challenge (enables challenger mode)")
|
|
42
|
-
|
|
43
|
-
def execute(self, query: str, context: Optional[str] = None, results_to_challenge: Optional[str] = None, **kwargs) -> dict:
|
|
44
|
-
api_key = os.environ.get("X_AI_API_KEY")
|
|
45
|
-
if not api_key:
|
|
46
|
-
return self.error("auth_error", "X_AI_API_KEY not set")
|
|
47
|
-
|
|
48
|
-
# Determine mode and build prompt
|
|
49
|
-
if results_to_challenge:
|
|
50
|
-
system_prompt = CHALLENGER_PROMPT
|
|
51
|
-
user_prompt = f"""Original query: {query}
|
|
52
|
-
|
|
53
|
-
Research findings to challenge:
|
|
54
|
-
{results_to_challenge}
|
|
55
|
-
|
|
56
|
-
Search X to challenge these findings."""
|
|
57
|
-
elif context:
|
|
58
|
-
system_prompt = SYSTEM_PROMPT
|
|
59
|
-
user_prompt = f"""Previous research findings:
|
|
60
|
-
{context}
|
|
61
|
-
|
|
62
|
-
Now search X for additional insights about: {query}
|
|
63
|
-
|
|
64
|
-
Focus on opinions, alternatives, and community discussions that complement the existing findings."""
|
|
65
|
-
else:
|
|
66
|
-
system_prompt = SYSTEM_PROMPT
|
|
67
|
-
user_prompt = f"Search X for developer opinions, experiences, and alternatives regarding: {query}"
|
|
68
|
-
|
|
69
|
-
try:
|
|
70
|
-
response, duration_ms = self.timed_execute(
|
|
71
|
-
self._call_api, api_key, user_prompt, system_prompt
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
content = response["choices"][0]["message"]["content"]
|
|
75
|
-
citations = response.get("citations", [])
|
|
76
|
-
usage = response.get("usage", {})
|
|
77
|
-
tool_usage = response.get("server_side_tool_usage", {})
|
|
78
|
-
|
|
79
|
-
return self.success(
|
|
80
|
-
{
|
|
81
|
-
"content": content,
|
|
82
|
-
"citations": citations,
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
"model": "grok-4-1-fast",
|
|
86
|
-
"command": "xai search",
|
|
87
|
-
"duration_ms": duration_ms,
|
|
88
|
-
"input_tokens": usage.get("prompt_tokens"),
|
|
89
|
-
"output_tokens": usage.get("completion_tokens"),
|
|
90
|
-
"reasoning_tokens": usage.get("reasoning_tokens"),
|
|
91
|
-
"x_search_calls": tool_usage.get("SERVER_SIDE_TOOL_X_SEARCH", 0),
|
|
92
|
-
},
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
except requests.exceptions.Timeout:
|
|
96
|
-
return self.error("timeout", f"Request timed out after {self.timeout_ms}ms")
|
|
97
|
-
except requests.exceptions.RequestException as e:
|
|
98
|
-
return self.error("api_error", str(e))
|
|
99
|
-
except (KeyError, IndexError) as e:
|
|
100
|
-
return self.error("parse_error", f"Unexpected response format: {e}")
|
|
101
|
-
|
|
102
|
-
def _call_api(self, api_key: str, user_prompt: str, system_prompt: str) -> dict:
|
|
103
|
-
response = requests.post(
|
|
104
|
-
"https://api.x.ai/v1/chat/completions",
|
|
105
|
-
headers={
|
|
106
|
-
"Authorization": f"Bearer {api_key}",
|
|
107
|
-
"Content-Type": "application/json",
|
|
108
|
-
},
|
|
109
|
-
json={
|
|
110
|
-
"model": "grok-4-1-fast",
|
|
111
|
-
"messages": [
|
|
112
|
-
{"role": "system", "content": system_prompt},
|
|
113
|
-
{"role": "user", "content": user_prompt},
|
|
114
|
-
],
|
|
115
|
-
},
|
|
116
|
-
timeout=self.timeout_ms / 1000,
|
|
117
|
-
)
|
|
118
|
-
response.raise_for_status()
|
|
119
|
-
return response.json()
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
COMMANDS = {
|
|
123
|
-
"search": XaiSearchCommand,
|
|
124
|
-
}
|
package/.claude/envoy/envoy.py
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""claude-envoy: CLI for agent-scoped external tool access.
|
|
3
|
-
|
|
4
|
-
Commands are auto-discovered from the commands/ directory.
|
|
5
|
-
Each command module registers itself via COMMANDS dict.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
import argparse
|
|
9
|
-
import importlib
|
|
10
|
-
import json
|
|
11
|
-
import os
|
|
12
|
-
import sys
|
|
13
|
-
from pathlib import Path
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def discover_commands() -> dict:
|
|
17
|
-
"""Auto-discover command modules from commands/ directory.
|
|
18
|
-
|
|
19
|
-
Each module should have a COMMANDS dict mapping subcommand names to classes.
|
|
20
|
-
Returns: {group: {subcommand: CommandClass}}
|
|
21
|
-
"""
|
|
22
|
-
commands_dir = Path(__file__).parent / "commands"
|
|
23
|
-
all_commands = {}
|
|
24
|
-
|
|
25
|
-
for module_file in commands_dir.glob("*.py"):
|
|
26
|
-
if module_file.name.startswith("_"):
|
|
27
|
-
continue
|
|
28
|
-
|
|
29
|
-
module_name = module_file.stem
|
|
30
|
-
try:
|
|
31
|
-
module = importlib.import_module(f"commands.{module_name}")
|
|
32
|
-
if hasattr(module, "COMMANDS"):
|
|
33
|
-
all_commands[module_name] = module.COMMANDS
|
|
34
|
-
except ImportError as e:
|
|
35
|
-
# Skip modules with missing dependencies
|
|
36
|
-
print(f"Warning: Could not load {module_name}: {e}", file=sys.stderr)
|
|
37
|
-
|
|
38
|
-
return all_commands
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def build_parser(commands: dict) -> argparse.ArgumentParser:
|
|
42
|
-
"""Build argparse parser from discovered commands."""
|
|
43
|
-
parser = argparse.ArgumentParser(
|
|
44
|
-
prog="envoy",
|
|
45
|
-
description="CLI for agent-scoped external tool access",
|
|
46
|
-
)
|
|
47
|
-
subparsers = parser.add_subparsers(dest="command", help="Available commands")
|
|
48
|
-
|
|
49
|
-
for group_name, subcommands in commands.items():
|
|
50
|
-
group = subparsers.add_parser(group_name, help=f"{group_name.title()} tools")
|
|
51
|
-
group_sub = group.add_subparsers(dest="subcommand")
|
|
52
|
-
|
|
53
|
-
for subcmd_name, cmd_class in subcommands.items():
|
|
54
|
-
cmd = cmd_class()
|
|
55
|
-
subcmd = group_sub.add_parser(subcmd_name, help=cmd.description)
|
|
56
|
-
cmd.add_arguments(subcmd)
|
|
57
|
-
|
|
58
|
-
# Built-in info command
|
|
59
|
-
subparsers.add_parser("info", help="Show available commands and API status")
|
|
60
|
-
|
|
61
|
-
return parser
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def run_command(args: argparse.Namespace, commands: dict) -> dict:
|
|
65
|
-
"""Route to appropriate command handler."""
|
|
66
|
-
if args.command == "info":
|
|
67
|
-
return get_info(commands)
|
|
68
|
-
|
|
69
|
-
if args.command in commands:
|
|
70
|
-
subcommands = commands[args.command]
|
|
71
|
-
if args.subcommand in subcommands:
|
|
72
|
-
cmd = subcommands[args.subcommand]()
|
|
73
|
-
# Convert args namespace to dict, excluding command/subcommand
|
|
74
|
-
kwargs = {k: v for k, v in vars(args).items() if k not in ("command", "subcommand")}
|
|
75
|
-
return cmd.execute(**kwargs)
|
|
76
|
-
|
|
77
|
-
return {"status": "error", "error": {"type": "invalid_command", "message": "Unknown command"}}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def get_info(commands: dict) -> dict:
|
|
81
|
-
"""Return info about available commands and API status."""
|
|
82
|
-
cmd_list = ["info"]
|
|
83
|
-
for group, subcommands in commands.items():
|
|
84
|
-
for subcmd in subcommands:
|
|
85
|
-
cmd_list.append(f"{group} {subcmd}")
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
"status": "success",
|
|
89
|
-
"data": {
|
|
90
|
-
"version": "0.1.0",
|
|
91
|
-
"commands": sorted(cmd_list),
|
|
92
|
-
"api_keys": {
|
|
93
|
-
"PERPLEXITY_API_KEY": "set" if os.environ.get("PERPLEXITY_API_KEY") else "missing",
|
|
94
|
-
"TAVILY_API_KEY": "set" if os.environ.get("TAVILY_API_KEY") else "missing",
|
|
95
|
-
"VERTEX_API_KEY": "set" if os.environ.get("VERTEX_API_KEY") else "missing",
|
|
96
|
-
},
|
|
97
|
-
"timeout_ms": os.environ.get("ENVOY_TIMEOUT_MS", "120000"),
|
|
98
|
-
},
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
def main():
|
|
103
|
-
# Add commands directory to path
|
|
104
|
-
sys.path.insert(0, str(Path(__file__).parent))
|
|
105
|
-
|
|
106
|
-
commands = discover_commands()
|
|
107
|
-
parser = build_parser(commands)
|
|
108
|
-
args = parser.parse_args()
|
|
109
|
-
|
|
110
|
-
if not args.command:
|
|
111
|
-
parser.print_help()
|
|
112
|
-
sys.exit(1)
|
|
113
|
-
|
|
114
|
-
if args.command in commands and not getattr(args, "subcommand", None):
|
|
115
|
-
parser.parse_args([args.command, "-h"])
|
|
116
|
-
|
|
117
|
-
result = run_command(args, commands)
|
|
118
|
-
print(json.dumps(result, indent=2))
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if __name__ == "__main__":
|
|
122
|
-
main()
|