mirror of
https://github.com/cexll/myclaude.git
synced 2026-02-11 03:23:50 +08:00
Replace external inject-spec.py hook with built-in zero-config skill detection in codeagent-wrapper. The system auto-detects project type from fingerprint files (go.mod, package.json, etc.), maps to installed skills, and injects SKILL.md content directly into sub-agent prompts. Key changes: - Add DetectProjectSkills/ResolveSkillContent in executor/prompt.go - Add Skills field to TaskSpec with parallel config parsing - Add --skills CLI flag for explicit override - Update /do SKILL.md Phase 4 with per-task skill examples - Remove on-stop.py global hook (not needed) - Replace inject-spec.py with no-op (detection now internal) - Add 20 unit tests covering detection, resolution, budget, security Security: path traversal protection via validSkillName regex, 16K char budget with tag overhead accounting, CRLF normalization. Generated with SWE-Agent.ai Co-Authored-By: SWE-Agent.ai <noreply@swe-agent.ai>
150 lines
4.5 KiB
Python
150 lines
4.5 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Get context for current task.
|
|
|
|
Reads the current task's jsonl files and returns context for specified agent.
|
|
Used by inject-context hook to build agent prompts.
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
DIR_TASKS = ".claude/do-tasks"
|
|
FILE_CURRENT_TASK = ".current-task"
|
|
FILE_TASK_JSON = "task.json"
|
|
|
|
|
|
def get_project_root() -> str:
|
|
return os.environ.get("CLAUDE_PROJECT_DIR", os.getcwd())
|
|
|
|
|
|
def get_current_task(project_root: str) -> str | None:
|
|
current_task_file = os.path.join(project_root, DIR_TASKS, FILE_CURRENT_TASK)
|
|
if not os.path.exists(current_task_file):
|
|
return None
|
|
try:
|
|
with open(current_task_file, "r", encoding="utf-8") as f:
|
|
content = f.read().strip()
|
|
return content if content else None
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
def read_file_content(base_path: str, file_path: str) -> str | None:
|
|
full_path = os.path.join(base_path, file_path)
|
|
if os.path.exists(full_path) and os.path.isfile(full_path):
|
|
try:
|
|
with open(full_path, "r", encoding="utf-8") as f:
|
|
return f.read()
|
|
except Exception:
|
|
return None
|
|
return None
|
|
|
|
|
|
def read_jsonl_entries(base_path: str, jsonl_path: str) -> list[tuple[str, str]]:
|
|
full_path = os.path.join(base_path, jsonl_path)
|
|
if not os.path.exists(full_path):
|
|
return []
|
|
|
|
results = []
|
|
try:
|
|
with open(full_path, "r", encoding="utf-8") as f:
|
|
for line in f:
|
|
line = line.strip()
|
|
if not line:
|
|
continue
|
|
try:
|
|
item = json.loads(line)
|
|
file_path = item.get("file") or item.get("path")
|
|
if not file_path:
|
|
continue
|
|
content = read_file_content(base_path, file_path)
|
|
if content:
|
|
results.append((file_path, content))
|
|
except json.JSONDecodeError:
|
|
continue
|
|
except Exception:
|
|
pass
|
|
return results
|
|
|
|
|
|
def get_agent_context(project_root: str, task_dir: str, agent_type: str) -> str:
|
|
"""Get complete context for specified agent."""
|
|
context_parts = []
|
|
|
|
# Read agent-specific jsonl
|
|
agent_jsonl = os.path.join(task_dir, f"{agent_type}.jsonl")
|
|
agent_entries = read_jsonl_entries(project_root, agent_jsonl)
|
|
|
|
for file_path, content in agent_entries:
|
|
context_parts.append(f"=== {file_path} ===\n{content}")
|
|
|
|
# Read prd.md
|
|
prd_content = read_file_content(project_root, os.path.join(task_dir, "prd.md"))
|
|
if prd_content:
|
|
context_parts.append(f"=== {task_dir}/prd.md (Requirements) ===\n{prd_content}")
|
|
|
|
return "\n\n".join(context_parts)
|
|
|
|
|
|
def get_task_info(project_root: str, task_dir: str) -> dict | None:
|
|
"""Get task.json data."""
|
|
task_json_path = os.path.join(project_root, task_dir, FILE_TASK_JSON)
|
|
if not os.path.exists(task_json_path):
|
|
return None
|
|
try:
|
|
with open(task_json_path, "r", encoding="utf-8") as f:
|
|
return json.load(f)
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
def main():
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(description="Get context for current task")
|
|
parser.add_argument("agent", nargs="?", choices=["implement", "check", "debug"],
|
|
help="Agent type (optional, returns task info if not specified)")
|
|
parser.add_argument("--json", action="store_true", help="Output as JSON")
|
|
args = parser.parse_args()
|
|
|
|
project_root = get_project_root()
|
|
task_dir = get_current_task(project_root)
|
|
|
|
if not task_dir:
|
|
if args.json:
|
|
print(json.dumps({"error": "No active task"}))
|
|
else:
|
|
print("No active task.", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
task_info = get_task_info(project_root, task_dir)
|
|
|
|
if not args.agent:
|
|
if args.json:
|
|
print(json.dumps({"task_dir": task_dir, "task_info": task_info}))
|
|
else:
|
|
print(f"Task: {task_dir}")
|
|
if task_info:
|
|
print(f"Title: {task_info.get('title', 'N/A')}")
|
|
print(f"Phase: {task_info.get('current_phase', '?')}/{task_info.get('max_phases', 5)}")
|
|
sys.exit(0)
|
|
|
|
context = get_agent_context(project_root, task_dir, args.agent)
|
|
|
|
if args.json:
|
|
print(json.dumps({
|
|
"task_dir": task_dir,
|
|
"agent": args.agent,
|
|
"context": context,
|
|
"task_info": task_info,
|
|
}))
|
|
else:
|
|
print(context)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|