oasr 0.5.0__py3-none-any.whl
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.
- __init__.py +3 -0
- __main__.py +6 -0
- adapter.py +396 -0
- adapters/__init__.py +17 -0
- adapters/base.py +254 -0
- adapters/claude.py +82 -0
- adapters/codex.py +84 -0
- adapters/copilot.py +210 -0
- adapters/cursor.py +78 -0
- adapters/windsurf.py +83 -0
- agents/__init__.py +25 -0
- agents/base.py +96 -0
- agents/claude.py +25 -0
- agents/codex.py +25 -0
- agents/copilot.py +25 -0
- agents/opencode.py +25 -0
- agents/registry.py +57 -0
- cli.py +97 -0
- commands/__init__.py +6 -0
- commands/adapter.py +102 -0
- commands/add.py +435 -0
- commands/clean.py +30 -0
- commands/clone.py +178 -0
- commands/config.py +163 -0
- commands/diff.py +180 -0
- commands/exec.py +245 -0
- commands/find.py +56 -0
- commands/help.py +51 -0
- commands/info.py +152 -0
- commands/list.py +110 -0
- commands/registry.py +447 -0
- commands/rm.py +128 -0
- commands/status.py +119 -0
- commands/sync.py +143 -0
- commands/update.py +417 -0
- commands/use.py +45 -0
- commands/validate.py +74 -0
- config/__init__.py +119 -0
- config/defaults.py +40 -0
- config/schema.py +73 -0
- discovery.py +145 -0
- manifest.py +437 -0
- oasr-0.5.0.dist-info/METADATA +358 -0
- oasr-0.5.0.dist-info/RECORD +59 -0
- oasr-0.5.0.dist-info/WHEEL +4 -0
- oasr-0.5.0.dist-info/entry_points.txt +3 -0
- oasr-0.5.0.dist-info/licenses/LICENSE +187 -0
- oasr-0.5.0.dist-info/licenses/NOTICE +8 -0
- policy/__init__.py +50 -0
- policy/defaults.py +27 -0
- policy/enforcement.py +98 -0
- policy/profile.py +185 -0
- registry.py +173 -0
- remote.py +482 -0
- skillcopy/__init__.py +71 -0
- skillcopy/local.py +40 -0
- skillcopy/remote.py +98 -0
- tracking.py +181 -0
- validate.py +362 -0
agents/codex.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Codex agent driver."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from agents.base import AgentDriver
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CodexDriver(AgentDriver):
|
|
9
|
+
"""Driver for Codex CLI agent."""
|
|
10
|
+
|
|
11
|
+
def get_name(self) -> str:
|
|
12
|
+
"""Get the agent name."""
|
|
13
|
+
return "codex"
|
|
14
|
+
|
|
15
|
+
def get_binary_name(self) -> str:
|
|
16
|
+
"""Get the CLI binary name."""
|
|
17
|
+
return "codex"
|
|
18
|
+
|
|
19
|
+
def build_command(self, skill_content: str, user_prompt: str, cwd: Path) -> list[str]:
|
|
20
|
+
"""Build codex exec command.
|
|
21
|
+
|
|
22
|
+
Codex syntax: codex exec "<prompt>"
|
|
23
|
+
"""
|
|
24
|
+
injected_prompt = self.format_injected_prompt(skill_content, user_prompt, cwd)
|
|
25
|
+
return ["codex", "exec", injected_prompt]
|
agents/copilot.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""GitHub Copilot agent driver."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from agents.base import AgentDriver
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CopilotDriver(AgentDriver):
|
|
9
|
+
"""Driver for GitHub Copilot CLI agent."""
|
|
10
|
+
|
|
11
|
+
def get_name(self) -> str:
|
|
12
|
+
"""Get the agent name."""
|
|
13
|
+
return "copilot"
|
|
14
|
+
|
|
15
|
+
def get_binary_name(self) -> str:
|
|
16
|
+
"""Get the CLI binary name."""
|
|
17
|
+
return "copilot"
|
|
18
|
+
|
|
19
|
+
def build_command(self, skill_content: str, user_prompt: str, cwd: Path) -> list[str]:
|
|
20
|
+
"""Build copilot command.
|
|
21
|
+
|
|
22
|
+
Copilot syntax: copilot -p "<prompt>"
|
|
23
|
+
"""
|
|
24
|
+
injected_prompt = self.format_injected_prompt(skill_content, user_prompt, cwd)
|
|
25
|
+
return ["copilot", "-p", injected_prompt]
|
agents/opencode.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""OpenCode CLI agent driver."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from agents.base import AgentDriver
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class OpenCodeDriver(AgentDriver):
|
|
9
|
+
"""Driver for OpenCode CLI agent."""
|
|
10
|
+
|
|
11
|
+
def get_name(self) -> str:
|
|
12
|
+
"""Get the agent name."""
|
|
13
|
+
return "opencode"
|
|
14
|
+
|
|
15
|
+
def get_binary_name(self) -> str:
|
|
16
|
+
"""Get the CLI binary name."""
|
|
17
|
+
return "opencode"
|
|
18
|
+
|
|
19
|
+
def build_command(self, skill_content: str, user_prompt: str, cwd: Path) -> list[str]:
|
|
20
|
+
"""Build opencode run command.
|
|
21
|
+
|
|
22
|
+
OpenCode syntax: opencode run "<prompt>"
|
|
23
|
+
"""
|
|
24
|
+
injected_prompt = self.format_injected_prompt(skill_content, user_prompt, cwd)
|
|
25
|
+
return ["opencode", "run", injected_prompt]
|
agents/registry.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Agent driver registry and factory."""
|
|
2
|
+
|
|
3
|
+
from agents.base import AgentDriver
|
|
4
|
+
from agents.claude import ClaudeDriver
|
|
5
|
+
from agents.codex import CodexDriver
|
|
6
|
+
from agents.copilot import CopilotDriver
|
|
7
|
+
from agents.opencode import OpenCodeDriver
|
|
8
|
+
|
|
9
|
+
# Registry of all available drivers
|
|
10
|
+
DRIVERS: dict[str, type[AgentDriver]] = {
|
|
11
|
+
"codex": CodexDriver,
|
|
12
|
+
"copilot": CopilotDriver,
|
|
13
|
+
"claude": ClaudeDriver,
|
|
14
|
+
"opencode": OpenCodeDriver,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_driver(agent_name: str) -> AgentDriver:
|
|
19
|
+
"""Get driver instance by agent name.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
agent_name: Name of agent (codex, copilot, claude).
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
AgentDriver instance.
|
|
26
|
+
|
|
27
|
+
Raises:
|
|
28
|
+
ValueError: If agent name is invalid.
|
|
29
|
+
"""
|
|
30
|
+
if agent_name not in DRIVERS:
|
|
31
|
+
valid = ", ".join(sorted(DRIVERS.keys()))
|
|
32
|
+
raise ValueError(f"Invalid agent '{agent_name}'. Must be one of: {valid}")
|
|
33
|
+
|
|
34
|
+
return DRIVERS[agent_name]()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def detect_available_agents() -> list[str]:
|
|
38
|
+
"""Detect which agent binaries are available in PATH.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
List of available agent names.
|
|
42
|
+
"""
|
|
43
|
+
available = []
|
|
44
|
+
for name, driver_class in DRIVERS.items():
|
|
45
|
+
driver = driver_class()
|
|
46
|
+
if driver.detect():
|
|
47
|
+
available.append(name)
|
|
48
|
+
return sorted(available)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def get_all_agent_names() -> list[str]:
|
|
52
|
+
"""Get all supported agent names.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
List of all agent names.
|
|
56
|
+
"""
|
|
57
|
+
return sorted(DRIVERS.keys())
|
cli.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""CLI entry point (argparse wiring + dispatch).
|
|
2
|
+
|
|
3
|
+
Command implementations live under `src/commands/`.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import argparse
|
|
9
|
+
import json
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from commands import adapter, clean, clone, config, diff, exec, find, registry, sync, update, use, validate
|
|
14
|
+
from commands import help as help_cmd
|
|
15
|
+
|
|
16
|
+
__version__ = "0.5.0"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def main(argv: list[str] | None = None) -> int:
|
|
20
|
+
"""Main CLI entry point."""
|
|
21
|
+
parser = create_parser()
|
|
22
|
+
args = parser.parse_args(argv)
|
|
23
|
+
|
|
24
|
+
if not hasattr(args, "func"):
|
|
25
|
+
parser.print_help()
|
|
26
|
+
return 1
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
return args.func(args)
|
|
30
|
+
except KeyboardInterrupt:
|
|
31
|
+
print("\nInterrupted.", file=sys.stderr)
|
|
32
|
+
return 130
|
|
33
|
+
except Exception as e:
|
|
34
|
+
if args.json if hasattr(args, "json") else False:
|
|
35
|
+
print(json.dumps({"error": str(e)}), file=sys.stderr)
|
|
36
|
+
else:
|
|
37
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
38
|
+
return 3
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def create_parser() -> argparse.ArgumentParser:
|
|
42
|
+
"""Create the argument parser."""
|
|
43
|
+
parser = argparse.ArgumentParser(
|
|
44
|
+
prog="oasr",
|
|
45
|
+
description="Open Agent Skills Registry - Manage agent skills across IDE integrations.",
|
|
46
|
+
)
|
|
47
|
+
parser.add_argument(
|
|
48
|
+
"--version",
|
|
49
|
+
action="version",
|
|
50
|
+
version=f"%(prog)s {__version__}",
|
|
51
|
+
)
|
|
52
|
+
parser.add_argument(
|
|
53
|
+
"--config",
|
|
54
|
+
type=Path,
|
|
55
|
+
help="Override config file path",
|
|
56
|
+
)
|
|
57
|
+
parser.add_argument(
|
|
58
|
+
"--json",
|
|
59
|
+
action="store_true",
|
|
60
|
+
help="Output in JSON format",
|
|
61
|
+
)
|
|
62
|
+
parser.add_argument(
|
|
63
|
+
"--quiet",
|
|
64
|
+
action="store_true",
|
|
65
|
+
help="Suppress info and warnings",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
subparsers = parser.add_subparsers(dest="command", help="Available commands")
|
|
69
|
+
|
|
70
|
+
# New taxonomy (v0.3.0+)
|
|
71
|
+
registry.register(subparsers) # Registry operations (add, rm, sync, list)
|
|
72
|
+
diff.register(subparsers) # Show tracked skill status
|
|
73
|
+
sync.register(subparsers) # Refresh tracked skills
|
|
74
|
+
config.register(subparsers) # Configuration management
|
|
75
|
+
clone.register(subparsers) # Clone skills to directory
|
|
76
|
+
exec.register(subparsers) # Execute skills with agent CLI
|
|
77
|
+
|
|
78
|
+
# Deprecated commands
|
|
79
|
+
use.register(subparsers) # DEPRECATED - use clone instead
|
|
80
|
+
find.register(subparsers)
|
|
81
|
+
validate.register(subparsers)
|
|
82
|
+
clean.register(subparsers)
|
|
83
|
+
adapter.register(subparsers)
|
|
84
|
+
update.register(subparsers)
|
|
85
|
+
|
|
86
|
+
# Import and register info command
|
|
87
|
+
from commands import info as info_cmd
|
|
88
|
+
|
|
89
|
+
info_cmd.register(subparsers)
|
|
90
|
+
|
|
91
|
+
help_cmd.register(subparsers, parser)
|
|
92
|
+
|
|
93
|
+
return parser
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
if __name__ == "__main__":
|
|
97
|
+
raise SystemExit(main())
|
commands/__init__.py
ADDED
commands/adapter.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""`asr adapter` command."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from adapters import ClaudeAdapter, CodexAdapter, CopilotAdapter, CursorAdapter, WindsurfAdapter
|
|
11
|
+
from config import load_config
|
|
12
|
+
from registry import load_registry
|
|
13
|
+
|
|
14
|
+
ADAPTERS = {
|
|
15
|
+
"cursor": CursorAdapter(),
|
|
16
|
+
"windsurf": WindsurfAdapter(),
|
|
17
|
+
"codex": CodexAdapter(),
|
|
18
|
+
"copilot": CopilotAdapter(),
|
|
19
|
+
"claude": ClaudeAdapter(),
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def register(subparsers) -> None:
|
|
24
|
+
p = subparsers.add_parser("adapter", help="Generate IDE-specific files")
|
|
25
|
+
p.add_argument("--exclude", help="Comma-separated skill names to exclude")
|
|
26
|
+
p.add_argument("--output-dir", type=Path, default=Path("."), help="Output directory")
|
|
27
|
+
p.add_argument("--copy", action="store_true", help="(Deprecated) Skills are always copied now")
|
|
28
|
+
p.add_argument("--json", action="store_true", help="Output in JSON format")
|
|
29
|
+
p.add_argument("--quiet", action="store_true", help="Suppress info/warnings")
|
|
30
|
+
p.add_argument("--config", type=Path, help="Override config file path")
|
|
31
|
+
|
|
32
|
+
adapter_subs = p.add_subparsers(dest="target", help="Target IDE")
|
|
33
|
+
|
|
34
|
+
for name in ["cursor", "windsurf", "codex", "copilot", "claude"]:
|
|
35
|
+
adapter_subs.add_parser(name, help=f"Generate {name} files")
|
|
36
|
+
|
|
37
|
+
p.set_defaults(func=run)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def run(args: argparse.Namespace) -> int:
|
|
41
|
+
config = load_config(args.config)
|
|
42
|
+
entries = load_registry()
|
|
43
|
+
|
|
44
|
+
if not entries:
|
|
45
|
+
if args.json:
|
|
46
|
+
print(json.dumps({"generated": 0, "error": "no skills registered"}))
|
|
47
|
+
else:
|
|
48
|
+
print("No skills registered. Use 'asr add <path>' first.")
|
|
49
|
+
return 1
|
|
50
|
+
|
|
51
|
+
exclude = set()
|
|
52
|
+
if args.exclude:
|
|
53
|
+
exclude = set(args.exclude.split(","))
|
|
54
|
+
|
|
55
|
+
output_dir = args.output_dir
|
|
56
|
+
|
|
57
|
+
if args.target:
|
|
58
|
+
targets = [args.target]
|
|
59
|
+
else:
|
|
60
|
+
targets = config["adapter"]["default_targets"]
|
|
61
|
+
|
|
62
|
+
total_generated = 0
|
|
63
|
+
total_removed = 0
|
|
64
|
+
results = {}
|
|
65
|
+
|
|
66
|
+
for target in targets:
|
|
67
|
+
if target not in ADAPTERS:
|
|
68
|
+
if not args.quiet:
|
|
69
|
+
print(f"Warning: Unknown adapter target: {target}", file=sys.stderr)
|
|
70
|
+
continue
|
|
71
|
+
|
|
72
|
+
adapter = ADAPTERS[target]
|
|
73
|
+
# Always copy skills now (--copy flag is deprecated but kept for backward compat)
|
|
74
|
+
generated, removed = adapter.generate_all(entries, output_dir, exclude, copy=True)
|
|
75
|
+
|
|
76
|
+
total_generated += len(generated)
|
|
77
|
+
total_removed += len(removed)
|
|
78
|
+
|
|
79
|
+
results[target] = {
|
|
80
|
+
"generated": len(generated),
|
|
81
|
+
"removed": len(removed),
|
|
82
|
+
"output_dir": str(adapter.resolve_output_dir(output_dir)),
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if args.json:
|
|
86
|
+
print(
|
|
87
|
+
json.dumps(
|
|
88
|
+
{
|
|
89
|
+
"total_generated": total_generated,
|
|
90
|
+
"total_removed": total_removed,
|
|
91
|
+
"targets": results,
|
|
92
|
+
},
|
|
93
|
+
indent=2,
|
|
94
|
+
)
|
|
95
|
+
)
|
|
96
|
+
else:
|
|
97
|
+
for target, info in results.items():
|
|
98
|
+
print(f"{target}: Generated {info['generated']} file(s) in {info['output_dir']}")
|
|
99
|
+
if info["removed"]:
|
|
100
|
+
print(f" Removed {info['removed']} stale file(s)")
|
|
101
|
+
|
|
102
|
+
return 0
|