-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathagent
More file actions
executable file
·143 lines (120 loc) · 4.98 KB
/
agent
File metadata and controls
executable file
·143 lines (120 loc) · 4.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#!/usr/bin/env python3
#
# Executable bit is tracked in git (git update-index --chmod=+x) so
# this script is runnable immediately after clone — no chmod needed.
#
# Pure stdlib — no venv, no pip, no dependencies. Anyone with Python 3
# can clone and run this.
#
import argparse
import os
import shutil
import subprocess
import sys
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent
AGENTS_DIR = REPO_ROOT / "agents"
TESTS_DIR = REPO_ROOT / "tests" / "integration"
AVAILABLE = [p.stem for p in AGENTS_DIR.glob("*.md")]
def _resolve_agent(name: str) -> Path:
"""Resolve agent name to source file or exit."""
source = AGENTS_DIR / f"{name}.md"
if not source.is_file():
print(f"Error: no agent '{name}' found at {source}")
sys.exit(1)
return source
def cmd_install(args):
"""Install an agent to user or project scope."""
source = _resolve_agent(args.name)
# Check if the plugin is already installed — agent would be redundant
if not args.force:
cache_dir = Path.home() / ".claude" / "plugins" / "cache"
if cache_dir.exists():
for plugin_dir in cache_dir.iterdir():
candidate = plugin_dir / "agents" / f"{args.name}.md"
if candidate.exists():
print(f"Error: {args.name} is already available via installed plugin "
f"at {candidate.parent.parent.name}")
print("The agent loads automatically in interactive sessions via /agents.")
print("hint: use --force to install anyway (for CLI `claude --agent` usage)")
sys.exit(1)
if args.local:
repo_path = args.local.resolve()
if not repo_path.is_dir():
print(f"Error: {repo_path} is not a directory")
sys.exit(1)
target_dir = repo_path / ".claude" / "agents"
else:
target_dir = Path.home() / ".claude" / "agents"
target_dir.mkdir(parents=True, exist_ok=True)
target = target_dir / f"{args.name}.md"
shutil.copy2(source, target)
print(f"Installed {args.name} → {target}")
print(f"Run: claude --agent {args.name}")
def _resolve_test_dir(name: str) -> Path:
"""Find test directory matching agent name (hyphens or underscores)."""
for variant in [name, name.replace("-", "_"), name.replace("_", "-")]:
candidate = TESTS_DIR / variant
if candidate.is_dir():
return candidate
# Glob fallback — match any subdir whose name starts the same
matches = [d for d in TESTS_DIR.iterdir()
if d.is_dir() and d.name.replace("-", "_") == name.replace("-", "_")]
if matches:
return matches[0]
print(f"Error: no test directory found for '{name}' under {TESTS_DIR}")
sys.exit(1)
def cmd_test(args):
"""Run integration tests for an agent."""
_resolve_agent(args.name)
test_dir = _resolve_test_dir(args.name)
# Auto-create venv with test deps if missing
venv = REPO_ROOT / ".venv-test"
pytest_bin = venv / "bin" / "pytest"
if not pytest_bin.exists():
print("Setting up test venv (one-time)...")
subprocess.run([sys.executable, "-m", "venv", str(venv)], check=True)
subprocess.run([str(venv / "bin" / "pip"), "install", "-q",
"pytest", "pytest-xdist"], check=True)
cmd = [str(pytest_bin), str(test_dir)]
if args.filter:
cmd.extend(["-k", args.filter])
if args.parallel:
cmd.extend(["-n", str(args.parallel)])
env = dict(os.environ)
if args.debug:
env["PRIVACY_GUARD_DEBUG"] = "1"
sys.exit(subprocess.run(cmd, env=env).returncode)
def main():
parser = argparse.ArgumentParser(
description="Manage plugin agents: install for CLI use or run tests.",
epilog=f"Available agents: {', '.join(AVAILABLE)}",
)
sub = parser.add_subparsers(dest="command", required=True)
# --- install ---
p_install = sub.add_parser(
"install", help="Install an agent to user or project scope")
p_install.add_argument("name", help="Agent to install")
p_install.add_argument(
"--local", metavar="PATH", type=Path,
help="Install to a project's .claude/agents/ instead of ~/.claude/agents/")
p_install.add_argument(
"--force", action="store_true",
help="Install even if the plugin is already providing the agent")
# --- test ---
p_test = sub.add_parser(
"test", help="Run integration tests for an agent")
p_test.add_argument("name", help="Agent to test")
p_test.add_argument(
"-k", "--filter",
help="pytest -k filter (test name or pattern)")
p_test.add_argument(
"-n", "--parallel", type=int,
help="Number of parallel workers (requires pytest-xdist)")
p_test.add_argument(
"--debug", action="store_true",
help="Write debug logs to /tmp/privacy-guard-tests/")
args = parser.parse_args()
{"install": cmd_install, "test": cmd_test}[args.command](args)
if __name__ == "__main__":
main()