oasr 0.5.2__py3-none-any.whl → 0.6.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.
- agents/base.py +17 -3
- agents/claude.py +12 -2
- agents/codex.py +12 -2
- agents/copilot.py +12 -2
- agents/opencode.py +12 -2
- cli.py +3 -2
- commands/config.py +284 -37
- commands/exec.py +21 -1
- commands/profile.py +84 -0
- commands/update.py +89 -7
- completions/bash.sh +19 -6
- completions/fish.fish +21 -7
- completions/powershell.ps1 +77 -8
- completions/zsh.sh +31 -5
- config/__init__.py +11 -0
- config/defaults.py +4 -21
- config/schema.py +3 -29
- {oasr-0.5.2.dist-info → oasr-0.6.0.dist-info}/METADATA +31 -21
- {oasr-0.5.2.dist-info → oasr-0.6.0.dist-info}/RECORD +32 -24
- policy/defaults.py +3 -19
- policy/profile.py +5 -7
- profiles/__init__.py +23 -0
- profiles/builtins.py +63 -0
- profiles/loader.py +74 -0
- profiles/paths.py +22 -0
- profiles/registry.py +19 -0
- profiles/summary.py +23 -0
- profiles/validation.py +34 -0
- {oasr-0.5.2.dist-info → oasr-0.6.0.dist-info}/WHEEL +0 -0
- {oasr-0.5.2.dist-info → oasr-0.6.0.dist-info}/entry_points.txt +0 -0
- {oasr-0.5.2.dist-info → oasr-0.6.0.dist-info}/licenses/LICENSE +0 -0
- {oasr-0.5.2.dist-info → oasr-0.6.0.dist-info}/licenses/NOTICE +0 -0
commands/update.py
CHANGED
|
@@ -6,6 +6,8 @@ import argparse
|
|
|
6
6
|
import json
|
|
7
7
|
import subprocess
|
|
8
8
|
import sys
|
|
9
|
+
import urllib.request
|
|
10
|
+
from importlib import metadata
|
|
9
11
|
from pathlib import Path
|
|
10
12
|
|
|
11
13
|
|
|
@@ -215,6 +217,51 @@ def get_stats(repo_path: Path, old_commit: str, new_commit: str) -> dict:
|
|
|
215
217
|
return stats
|
|
216
218
|
|
|
217
219
|
|
|
220
|
+
def get_installed_version(package: str = "oasr") -> str | None:
|
|
221
|
+
"""Get installed package version."""
|
|
222
|
+
try:
|
|
223
|
+
return metadata.version(package)
|
|
224
|
+
except metadata.PackageNotFoundError:
|
|
225
|
+
return None
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def get_latest_pypi_version(package: str = "oasr") -> str | None:
|
|
229
|
+
"""Fetch the latest version from PyPI."""
|
|
230
|
+
try:
|
|
231
|
+
with urllib.request.urlopen(f"https://pypi.org/pypi/{package}/json", timeout=5) as response:
|
|
232
|
+
data = json.load(response)
|
|
233
|
+
return data.get("info", {}).get("version")
|
|
234
|
+
except Exception:
|
|
235
|
+
return None
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def upgrade_from_pypi(package: str = "oasr") -> tuple[bool, str]:
|
|
239
|
+
"""Upgrade ASR using uv or pip."""
|
|
240
|
+
commands = [
|
|
241
|
+
["uv", "pip", "install", "--upgrade", package],
|
|
242
|
+
[sys.executable, "-m", "pip", "install", "--upgrade", package],
|
|
243
|
+
]
|
|
244
|
+
last_error = ""
|
|
245
|
+
|
|
246
|
+
for cmd in commands:
|
|
247
|
+
try:
|
|
248
|
+
result = subprocess.run(
|
|
249
|
+
cmd,
|
|
250
|
+
capture_output=True,
|
|
251
|
+
text=True,
|
|
252
|
+
timeout=60,
|
|
253
|
+
)
|
|
254
|
+
if result.returncode == 0:
|
|
255
|
+
runner = "uv" if cmd[0] == "uv" else "pip"
|
|
256
|
+
return True, f"Updated with {runner}"
|
|
257
|
+
last_error = result.stderr.strip() or result.stdout.strip()
|
|
258
|
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
259
|
+
last_error = "Update timed out" if isinstance(sys.exc_info()[1], subprocess.TimeoutExpired) else last_error
|
|
260
|
+
continue
|
|
261
|
+
|
|
262
|
+
return False, last_error or "Failed to update with pip"
|
|
263
|
+
|
|
264
|
+
|
|
218
265
|
def reinstall_asr(repo_path: Path) -> tuple[bool, str]:
|
|
219
266
|
"""Reinstall ASR using uv or pip.
|
|
220
267
|
|
|
@@ -293,10 +340,29 @@ def run(args: argparse.Namespace) -> int:
|
|
|
293
340
|
|
|
294
341
|
if not repo_path:
|
|
295
342
|
if args.json:
|
|
296
|
-
print(
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
343
|
+
print(
|
|
344
|
+
json.dumps(
|
|
345
|
+
{
|
|
346
|
+
"success": False,
|
|
347
|
+
"error": "Could not find ASR git repository",
|
|
348
|
+
"hint": "Install from git or use pip to update",
|
|
349
|
+
}
|
|
350
|
+
)
|
|
351
|
+
)
|
|
352
|
+
return 1
|
|
353
|
+
|
|
354
|
+
print("✗ Could not find ASR git repository", file=sys.stderr)
|
|
355
|
+
print(" This command updates git installs only.", file=sys.stderr)
|
|
356
|
+
print(" To update PyPI installs:", file=sys.stderr)
|
|
357
|
+
print(" pip install --upgrade oasr", file=sys.stderr)
|
|
358
|
+
|
|
359
|
+
latest_version = get_latest_pypi_version()
|
|
360
|
+
installed_version = get_installed_version()
|
|
361
|
+
if latest_version and installed_version and latest_version != installed_version:
|
|
362
|
+
print(
|
|
363
|
+
f"\nUpdate available: {installed_version} → {latest_version}",
|
|
364
|
+
file=sys.stderr,
|
|
365
|
+
)
|
|
300
366
|
return 1
|
|
301
367
|
|
|
302
368
|
if not args.quiet and not args.json:
|
|
@@ -306,8 +372,10 @@ def run(args: argparse.Namespace) -> int:
|
|
|
306
372
|
if not (repo_path / ".git").exists():
|
|
307
373
|
if args.json:
|
|
308
374
|
print(json.dumps({"success": False, "error": "Not a git repository"}))
|
|
309
|
-
|
|
310
|
-
|
|
375
|
+
return 1
|
|
376
|
+
print(f"✗ {repo_path} is not a git repository", file=sys.stderr)
|
|
377
|
+
print(" Use pip to update PyPI installs:", file=sys.stderr)
|
|
378
|
+
print(" pip install --upgrade oasr", file=sys.stderr)
|
|
311
379
|
return 1
|
|
312
380
|
|
|
313
381
|
# Get remote URL
|
|
@@ -348,10 +416,24 @@ def run(args: argparse.Namespace) -> int:
|
|
|
348
416
|
|
|
349
417
|
# Check if already up to date
|
|
350
418
|
if message == "already_up_to_date":
|
|
419
|
+
latest_version = get_latest_pypi_version()
|
|
420
|
+
installed_version = get_installed_version()
|
|
421
|
+
|
|
351
422
|
if args.json:
|
|
352
|
-
|
|
423
|
+
payload = {"success": True, "updated": False, "message": "Already up to date"}
|
|
424
|
+
if latest_version and installed_version:
|
|
425
|
+
payload["installed_version"] = installed_version
|
|
426
|
+
payload["latest_version"] = latest_version
|
|
427
|
+
payload["pypi_update_available"] = latest_version != installed_version
|
|
428
|
+
print(json.dumps(payload))
|
|
353
429
|
else:
|
|
354
430
|
print("✓ Already up to date")
|
|
431
|
+
if latest_version and installed_version and latest_version != installed_version:
|
|
432
|
+
print(
|
|
433
|
+
f"\nPyPI update available: {installed_version} → {latest_version}",
|
|
434
|
+
file=sys.stderr,
|
|
435
|
+
)
|
|
436
|
+
print("Run: pip install --upgrade oasr", file=sys.stderr)
|
|
355
437
|
return 0
|
|
356
438
|
|
|
357
439
|
# Get new commit
|
completions/bash.sh
CHANGED
|
@@ -20,13 +20,13 @@ _oasr_agents() {
|
|
|
20
20
|
_oasr_profiles() {
|
|
21
21
|
# Get profile names from config
|
|
22
22
|
local profiles
|
|
23
|
-
profiles=$(oasr config
|
|
23
|
+
profiles=$(oasr config profiles --names 2>/dev/null)
|
|
24
24
|
COMPREPLY=($(compgen -W "$profiles" -- "${COMP_WORDS[COMP_CWORD]}"))
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
_oasr_config_keys() {
|
|
28
28
|
# Common config keys
|
|
29
|
-
COMPREPLY=($(compgen -W "agent profile adapter.
|
|
29
|
+
COMPREPLY=($(compgen -W "agent profile oasr.default_profile adapter.default_targets validation.strict validation.reference_max_lines oasr.completions" -- "${COMP_WORDS[COMP_CWORD]}"))
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
_oasr_completion_shells() {
|
|
@@ -39,7 +39,7 @@ _oasr() {
|
|
|
39
39
|
|
|
40
40
|
# Top-level commands
|
|
41
41
|
if [ $COMP_CWORD -eq 1 ]; then
|
|
42
|
-
COMPREPLY=($(compgen -W "registry diff sync config clone exec use find validate clean adapter update info help completion" -- "$cur"))
|
|
42
|
+
COMPREPLY=($(compgen -W "registry diff sync config profile clone exec use find validate clean adapter update info help completion" -- "$cur"))
|
|
43
43
|
return 0
|
|
44
44
|
fi
|
|
45
45
|
|
|
@@ -69,7 +69,7 @@ _oasr() {
|
|
|
69
69
|
*)
|
|
70
70
|
# Complete skill names and flags
|
|
71
71
|
if [[ "$cur" == -* ]]; then
|
|
72
|
-
COMPREPLY=($(compgen -W "--agent --profile --agent-flags -y --yes --confirm -p --prompt" -- "$cur"))
|
|
72
|
+
COMPREPLY=($(compgen -W "--agent --profile --agent-flags -y --yes --confirm -p --prompt --unsafe" -- "$cur"))
|
|
73
73
|
else
|
|
74
74
|
_oasr_skills
|
|
75
75
|
fi
|
|
@@ -111,7 +111,7 @@ _oasr() {
|
|
|
111
111
|
|
|
112
112
|
config)
|
|
113
113
|
if [ $COMP_CWORD -eq 2 ]; then
|
|
114
|
-
COMPREPLY=($(compgen -W "set get list path" -- "$cur"))
|
|
114
|
+
COMPREPLY=($(compgen -W "set get list agent validation adapter oasr profiles man validate path" -- "$cur"))
|
|
115
115
|
return 0
|
|
116
116
|
fi
|
|
117
117
|
|
|
@@ -127,12 +127,18 @@ _oasr() {
|
|
|
127
127
|
agent)
|
|
128
128
|
_oasr_agents
|
|
129
129
|
;;
|
|
130
|
-
profile)
|
|
130
|
+
profile|oasr.default_profile)
|
|
131
131
|
_oasr_profiles
|
|
132
132
|
;;
|
|
133
133
|
validation.strict|oasr.completions)
|
|
134
134
|
COMPREPLY=($(compgen -W "true false" -- "$cur"))
|
|
135
135
|
;;
|
|
136
|
+
adapter.default_targets)
|
|
137
|
+
return 0
|
|
138
|
+
;;
|
|
139
|
+
validation.reference_max_lines)
|
|
140
|
+
return 0
|
|
141
|
+
;;
|
|
136
142
|
esac
|
|
137
143
|
fi
|
|
138
144
|
return 0
|
|
@@ -186,6 +192,13 @@ _oasr() {
|
|
|
186
192
|
fi
|
|
187
193
|
;;
|
|
188
194
|
|
|
195
|
+
profile)
|
|
196
|
+
if [ $COMP_CWORD -eq 2 ]; then
|
|
197
|
+
_oasr_profiles
|
|
198
|
+
return 0
|
|
199
|
+
fi
|
|
200
|
+
;;
|
|
201
|
+
|
|
189
202
|
find|validate|sync|diff|clean|update|help)
|
|
190
203
|
# These commands have limited or no additional completion
|
|
191
204
|
return 0
|
completions/fish.fish
CHANGED
|
@@ -18,15 +18,16 @@ function __oasr_agents
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
function __oasr_profiles
|
|
21
|
-
oasr config
|
|
21
|
+
oasr config profiles --names 2>/dev/null
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
function __oasr_config_keys
|
|
25
25
|
echo agent
|
|
26
26
|
echo profile
|
|
27
|
-
echo
|
|
27
|
+
echo oasr.default_profile
|
|
28
|
+
echo adapter.default_targets
|
|
28
29
|
echo validation.strict
|
|
29
|
-
echo validation.
|
|
30
|
+
echo validation.reference_max_lines
|
|
30
31
|
echo oasr.completions
|
|
31
32
|
end
|
|
32
33
|
|
|
@@ -45,6 +46,7 @@ complete -c oasr -f -n __fish_use_subcommand -a registry -d "Manage skill regist
|
|
|
45
46
|
complete -c oasr -f -n __fish_use_subcommand -a diff -d "Show tracked skill status"
|
|
46
47
|
complete -c oasr -f -n __fish_use_subcommand -a sync -d "Refresh tracked skills"
|
|
47
48
|
complete -c oasr -f -n __fish_use_subcommand -a config -d "Manage configuration"
|
|
49
|
+
complete -c oasr -f -n __fish_use_subcommand -a profile -d "Select execution profile"
|
|
48
50
|
complete -c oasr -f -n __fish_use_subcommand -a clone -d "Clone skills to directory"
|
|
49
51
|
complete -c oasr -f -n __fish_use_subcommand -a exec -d "Execute a skill"
|
|
50
52
|
complete -c oasr -f -n __fish_use_subcommand -a use -d "DEPRECATED - use clone"
|
|
@@ -58,6 +60,7 @@ complete -c oasr -f -n __fish_use_subcommand -a help -d "Show help"
|
|
|
58
60
|
complete -c oasr -f -n __fish_use_subcommand -a completion -d "Manage shell completions"
|
|
59
61
|
|
|
60
62
|
# exec command
|
|
63
|
+
complete -c oasr -f -n "__fish_seen_subcommand_from exec" -l unsafe -d "Pass unsafe agent flags"
|
|
61
64
|
complete -c oasr -f -n "__fish_seen_subcommand_from exec" -l agent -d "Agent to use" -a "(__oasr_agents)"
|
|
62
65
|
complete -c oasr -f -n "__fish_seen_subcommand_from exec" -l profile -d "Policy profile" -a "(__oasr_profiles)"
|
|
63
66
|
complete -c oasr -f -n "__fish_seen_subcommand_from exec" -l agent-flags -d "Additional agent flags"
|
|
@@ -78,17 +81,28 @@ complete -c oasr -f -n "__fish_seen_subcommand_from info; and not __fish_seen_su
|
|
|
78
81
|
complete -c oasr -f -n "__fish_seen_subcommand_from validate; and not __fish_seen_subcommand_from (__oasr_skills)" -a "(__oasr_skills)" -d "Skill"
|
|
79
82
|
|
|
80
83
|
# config subcommands
|
|
81
|
-
complete -c oasr -f -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from set get list path" -a "set" -d "Set configuration value"
|
|
82
|
-
complete -c oasr -f -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from set get list path" -a "get" -d "Get configuration value"
|
|
83
|
-
complete -c oasr -f -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from set get list path" -a "list" -d "List all configuration"
|
|
84
|
-
complete -c oasr -f -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from set get list path" -a "
|
|
84
|
+
complete -c oasr -f -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from set get list agent validation adapter oasr profiles man validate path" -a "set" -d "Set configuration value"
|
|
85
|
+
complete -c oasr -f -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from set get list agent validation adapter oasr profiles man validate path" -a "get" -d "Get configuration value"
|
|
86
|
+
complete -c oasr -f -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from set get list agent validation adapter oasr profiles man validate path" -a "list" -d "List all configuration"
|
|
87
|
+
complete -c oasr -f -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from set get list agent validation adapter oasr profiles man validate path" -a "agent" -d "Show agent configuration"
|
|
88
|
+
complete -c oasr -f -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from set get list agent validation adapter oasr profiles man validate path" -a "validation" -d "Show validation settings"
|
|
89
|
+
complete -c oasr -f -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from set get list agent validation adapter oasr profiles man validate path" -a "adapter" -d "Show adapter settings"
|
|
90
|
+
complete -c oasr -f -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from set get list agent validation adapter oasr profiles man validate path" -a "oasr" -d "Show core settings"
|
|
91
|
+
complete -c oasr -f -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from set get list agent validation adapter oasr profiles man validate path" -a "profiles" -d "Show profiles"
|
|
92
|
+
complete -c oasr -f -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from set get list agent validation adapter oasr profiles man validate path" -a "man" -d "Show config reference"
|
|
93
|
+
complete -c oasr -f -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from set get list agent validation adapter oasr profiles man validate path" -a "validate" -d "Validate config file"
|
|
94
|
+
complete -c oasr -f -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from set get list agent validation adapter oasr profiles man validate path" -a "path" -d "Show config file path"
|
|
85
95
|
|
|
86
96
|
# config set/get
|
|
87
97
|
complete -c oasr -f -n "__fish_seen_subcommand_from config; and __fish_seen_subcommand_from set get" -a "(__oasr_config_keys)" -d "Config key"
|
|
88
98
|
complete -c oasr -f -n "__fish_seen_subcommand_from config; and __fish_seen_subcommand_from set; and __fish_seen_subcommand_from agent" -a "(__oasr_agents)" -d "Agent"
|
|
89
99
|
complete -c oasr -f -n "__fish_seen_subcommand_from config; and __fish_seen_subcommand_from set; and __fish_seen_subcommand_from profile" -a "(__oasr_profiles)" -d "Profile"
|
|
100
|
+
complete -c oasr -f -n "__fish_seen_subcommand_from config; and __fish_seen_subcommand_from set; and __fish_seen_subcommand_from oasr.default_profile" -a "(__oasr_profiles)" -d "Profile"
|
|
90
101
|
complete -c oasr -f -n "__fish_seen_subcommand_from config; and __fish_seen_subcommand_from set; and __fish_seen_subcommand_from validation.strict oasr.completions" -a "true false" -d "Boolean"
|
|
91
102
|
|
|
103
|
+
# profile command
|
|
104
|
+
complete -c oasr -f -n "__fish_seen_subcommand_from profile" -a "(__oasr_profiles)" -d "Profile"
|
|
105
|
+
|
|
92
106
|
# registry subcommands
|
|
93
107
|
complete -c oasr -f -n "__fish_seen_subcommand_from registry; and not __fish_seen_subcommand_from add rm sync list validate prune" -a "add" -d "Add skill to registry"
|
|
94
108
|
complete -c oasr -f -n "__fish_seen_subcommand_from registry; and not __fish_seen_subcommand_from add rm sync list validate prune" -a "rm" -d "Remove skill from registry"
|
completions/powershell.ps1
CHANGED
|
@@ -18,10 +18,8 @@ function Get-OasrAgents {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
function Get-OasrProfiles {
|
|
21
|
-
$profiles = oasr config
|
|
22
|
-
|
|
23
|
-
$matches[1]
|
|
24
|
-
}
|
|
21
|
+
$profiles = oasr config profiles --names 2>$null | ForEach-Object {
|
|
22
|
+
$_
|
|
25
23
|
} | Sort-Object -Unique
|
|
26
24
|
return $profiles
|
|
27
25
|
}
|
|
@@ -30,9 +28,10 @@ function Get-OasrConfigKeys {
|
|
|
30
28
|
return @(
|
|
31
29
|
'agent',
|
|
32
30
|
'profile',
|
|
33
|
-
'
|
|
31
|
+
'oasr.default_profile',
|
|
32
|
+
'adapter.default_targets',
|
|
34
33
|
'validation.strict',
|
|
35
|
-
'validation.
|
|
34
|
+
'validation.reference_max_lines',
|
|
36
35
|
'oasr.completions'
|
|
37
36
|
)
|
|
38
37
|
}
|
|
@@ -48,7 +47,7 @@ $oasrCompletion = {
|
|
|
48
47
|
# First argument - main commands
|
|
49
48
|
if ($elementCount -eq 2) {
|
|
50
49
|
$commands = @(
|
|
51
|
-
'registry', 'diff', 'sync', 'config', 'clone', 'exec', 'use',
|
|
50
|
+
'registry', 'diff', 'sync', 'config', 'profile', 'clone', 'exec', 'use',
|
|
52
51
|
'find', 'validate', 'clean', 'adapter', 'update', 'info',
|
|
53
52
|
'help', 'completion'
|
|
54
53
|
)
|
|
@@ -79,7 +78,7 @@ $oasrCompletion = {
|
|
|
79
78
|
}
|
|
80
79
|
default {
|
|
81
80
|
if ($wordToComplete -like '-*') {
|
|
82
|
-
@('--agent', '--profile', '--agent-flags', '-y', '--yes', '--confirm', '-p', '--prompt') |
|
|
81
|
+
@('--agent', '--profile', '--agent-flags', '-y', '--yes', '--confirm', '-p', '--prompt', '--unsafe') |
|
|
83
82
|
Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
84
83
|
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
85
84
|
}
|
|
@@ -93,6 +92,67 @@ $oasrCompletion = {
|
|
|
93
92
|
}
|
|
94
93
|
}
|
|
95
94
|
|
|
95
|
+
'config' {
|
|
96
|
+
if ($elementCount -eq 3) {
|
|
97
|
+
@('set', 'get', 'list', 'agent', 'validation', 'adapter', 'oasr', 'profiles', 'man', 'validate', 'path') |
|
|
98
|
+
Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
99
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
100
|
+
}
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
$prevWord = if ($elementCount -gt 2) { $elements[$elementCount - 2].Value } else { '' }
|
|
105
|
+
if ($prevWord -eq 'set' -and $elementCount -eq 4) {
|
|
106
|
+
Get-OasrConfigKeys | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
107
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', "Key: $_")
|
|
108
|
+
}
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if ($prevWord -eq 'set' -and $elementCount -eq 5) {
|
|
113
|
+
$key = $elements[3].Value
|
|
114
|
+
switch ($key) {
|
|
115
|
+
'agent' {
|
|
116
|
+
Get-OasrAgents | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
117
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', "Agent: $_")
|
|
118
|
+
}
|
|
119
|
+
return
|
|
120
|
+
}
|
|
121
|
+
'profile' {
|
|
122
|
+
Get-OasrProfiles | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
123
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', "Profile: $_")
|
|
124
|
+
}
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
'oasr.default_profile' {
|
|
128
|
+
Get-OasrProfiles | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
129
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', "Profile: $_")
|
|
130
|
+
}
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
'validation.strict' {
|
|
134
|
+
@('true', 'false') | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
135
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
136
|
+
}
|
|
137
|
+
return
|
|
138
|
+
}
|
|
139
|
+
'oasr.completions' {
|
|
140
|
+
@('true', 'false') | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
141
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
142
|
+
}
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if ($elements[2].Value -eq 'get' -and $elementCount -eq 4) {
|
|
149
|
+
Get-OasrConfigKeys | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
150
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', "Key: $_")
|
|
151
|
+
}
|
|
152
|
+
return
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
96
156
|
'completion' {
|
|
97
157
|
if ($elementCount -eq 3) {
|
|
98
158
|
@('bash', 'zsh', 'fish', 'powershell', 'install', 'uninstall') | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
@@ -108,6 +168,15 @@ $oasrCompletion = {
|
|
|
108
168
|
}
|
|
109
169
|
return
|
|
110
170
|
}
|
|
171
|
+
|
|
172
|
+
'profile' {
|
|
173
|
+
if ($elementCount -eq 3) {
|
|
174
|
+
Get-OasrProfiles | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
175
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', "Profile: $_")
|
|
176
|
+
}
|
|
177
|
+
return
|
|
178
|
+
}
|
|
179
|
+
}
|
|
111
180
|
}
|
|
112
181
|
}
|
|
113
182
|
|
completions/zsh.sh
CHANGED
|
@@ -25,7 +25,7 @@ _oasr_agents() {
|
|
|
25
25
|
|
|
26
26
|
_oasr_profiles() {
|
|
27
27
|
local profiles
|
|
28
|
-
profiles=(${(f)"$(oasr config
|
|
28
|
+
profiles=(${(f)"$(oasr config profiles --names 2>/dev/null)"})
|
|
29
29
|
_describe 'profile' profiles
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -34,9 +34,9 @@ _oasr_config_keys() {
|
|
|
34
34
|
keys=(
|
|
35
35
|
'agent:Default agent'
|
|
36
36
|
'profile:Default policy profile'
|
|
37
|
-
'adapter.
|
|
37
|
+
'adapter.default_targets:Default adapter targets'
|
|
38
38
|
'validation.strict:Strict validation'
|
|
39
|
-
'validation.
|
|
39
|
+
'validation.reference_max_lines:Reference max lines'
|
|
40
40
|
'oasr.completions:Enable completions'
|
|
41
41
|
)
|
|
42
42
|
_describe 'config key' keys
|
|
@@ -48,7 +48,8 @@ _oasr_exec() {
|
|
|
48
48
|
'--profile[Policy profile]:profile:_oasr_profiles' \
|
|
49
49
|
'--agent-flags[Additional agent flags]:flags:' \
|
|
50
50
|
'(-y --yes)'{-y,--yes}'[Skip confirmation]' \
|
|
51
|
-
'--confirm[Force confirmation]
|
|
51
|
+
'--confirm[Force confirmation] \
|
|
52
|
+
--unsafe[Pass unsafe agent flags]' \
|
|
52
53
|
'(-p --prompt)'{-p,--prompt}'[Prompt from file]:file:_files' \
|
|
53
54
|
'1:skill:_oasr_skills'
|
|
54
55
|
}
|
|
@@ -76,6 +77,13 @@ _oasr_config() {
|
|
|
76
77
|
'set:Set a configuration value'
|
|
77
78
|
'get:Get a configuration value'
|
|
78
79
|
'list:List all configuration'
|
|
80
|
+
'agent:Show agent configuration'
|
|
81
|
+
'validation:Show validation settings'
|
|
82
|
+
'adapter:Show adapter settings'
|
|
83
|
+
'oasr:Show core settings'
|
|
84
|
+
'profiles:Show profiles'
|
|
85
|
+
'man:Show config reference'
|
|
86
|
+
'validate:Validate config file'
|
|
79
87
|
'path:Show config file path'
|
|
80
88
|
)
|
|
81
89
|
|
|
@@ -97,12 +105,18 @@ _oasr_config() {
|
|
|
97
105
|
agent)
|
|
98
106
|
_oasr_agents
|
|
99
107
|
;;
|
|
100
|
-
profile)
|
|
108
|
+
profile|oasr.default_profile)
|
|
101
109
|
_oasr_profiles
|
|
102
110
|
;;
|
|
103
111
|
validation.strict|oasr.completions)
|
|
104
112
|
_values 'boolean' true false
|
|
105
113
|
;;
|
|
114
|
+
validation.reference_max_lines)
|
|
115
|
+
_message 'integer'
|
|
116
|
+
;;
|
|
117
|
+
adapter.default_targets)
|
|
118
|
+
_message 'comma-separated list'
|
|
119
|
+
;;
|
|
106
120
|
esac
|
|
107
121
|
fi
|
|
108
122
|
;;
|
|
@@ -114,6 +128,11 @@ _oasr_config() {
|
|
|
114
128
|
esac
|
|
115
129
|
}
|
|
116
130
|
|
|
131
|
+
_oasr_profile() {
|
|
132
|
+
_arguments \
|
|
133
|
+
'1:profile:_oasr_profiles'
|
|
134
|
+
}
|
|
135
|
+
|
|
117
136
|
_oasr_registry() {
|
|
118
137
|
local -a subcommands
|
|
119
138
|
subcommands=(
|
|
@@ -199,6 +218,7 @@ _oasr() {
|
|
|
199
218
|
'diff:Show tracked skill status'
|
|
200
219
|
'sync:Refresh tracked skills'
|
|
201
220
|
'config:Manage configuration'
|
|
221
|
+
'profile:Select execution profile'
|
|
202
222
|
'clone:Clone skills to directory'
|
|
203
223
|
'exec:Execute a skill'
|
|
204
224
|
'use:DEPRECATED - use clone instead'
|
|
@@ -230,6 +250,9 @@ _oasr() {
|
|
|
230
250
|
exec)
|
|
231
251
|
_oasr_exec
|
|
232
252
|
;;
|
|
253
|
+
profile)
|
|
254
|
+
_oasr_profile
|
|
255
|
+
;;
|
|
233
256
|
clone)
|
|
234
257
|
_oasr_clone
|
|
235
258
|
;;
|
|
@@ -242,6 +265,9 @@ _oasr() {
|
|
|
242
265
|
config)
|
|
243
266
|
_oasr_config
|
|
244
267
|
;;
|
|
268
|
+
profile)
|
|
269
|
+
_oasr_profile
|
|
270
|
+
;;
|
|
245
271
|
registry)
|
|
246
272
|
_oasr_registry
|
|
247
273
|
;;
|
config/__init__.py
CHANGED
|
@@ -14,6 +14,7 @@ import tomli_w
|
|
|
14
14
|
from config.defaults import DEFAULT_CONFIG
|
|
15
15
|
from config.env import load_env_config, merge_configs
|
|
16
16
|
from config.schema import validate_config
|
|
17
|
+
from profiles.loader import load_profiles
|
|
17
18
|
|
|
18
19
|
OASR_DIR = Path.home() / ".oasr"
|
|
19
20
|
CONFIG_FILE = OASR_DIR / "config.toml"
|
|
@@ -75,6 +76,16 @@ def load_config(config_path: Path | None = None, cli_overrides: dict[str, Any] |
|
|
|
75
76
|
with open(path, "rb") as f:
|
|
76
77
|
file_config = tomllib.load(f)
|
|
77
78
|
|
|
79
|
+
if not isinstance(file_config, dict):
|
|
80
|
+
file_config = {}
|
|
81
|
+
|
|
82
|
+
# Merge profile files with inline profiles (inline wins)
|
|
83
|
+
inline_profiles = file_config.get("profiles", {})
|
|
84
|
+
if not isinstance(inline_profiles, dict):
|
|
85
|
+
inline_profiles = {}
|
|
86
|
+
merged_profiles = load_profiles(inline_profiles=inline_profiles)
|
|
87
|
+
file_config["profiles"] = merged_profiles
|
|
88
|
+
|
|
78
89
|
# Load environment variables
|
|
79
90
|
env_config = load_env_config()
|
|
80
91
|
|
config/defaults.py
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
|
+
from profiles.builtins import BUILTIN_PROFILES
|
|
6
|
+
|
|
5
7
|
DEFAULT_CONFIG: dict[str, Any] = {
|
|
6
8
|
"validation": {
|
|
7
9
|
"reference_max_lines": 500,
|
|
@@ -15,26 +17,7 @@ DEFAULT_CONFIG: dict[str, Any] = {
|
|
|
15
17
|
},
|
|
16
18
|
"oasr": {
|
|
17
19
|
"default_profile": "safe",
|
|
20
|
+
"completions": True,
|
|
18
21
|
},
|
|
19
|
-
"profiles": {
|
|
20
|
-
# Built-in safe profile (always available as fallback)
|
|
21
|
-
"safe": {
|
|
22
|
-
"fs_read_roots": ["./"],
|
|
23
|
-
"fs_write_roots": ["./out", "./.oasr"],
|
|
24
|
-
"deny_paths": [
|
|
25
|
-
"~/.ssh",
|
|
26
|
-
"~/.aws",
|
|
27
|
-
"~/.gnupg",
|
|
28
|
-
"~/.config",
|
|
29
|
-
".env",
|
|
30
|
-
"~/.bashrc",
|
|
31
|
-
"~/.zshrc",
|
|
32
|
-
"~/.profile",
|
|
33
|
-
],
|
|
34
|
-
"allowed_commands": ["rg", "fd", "jq", "cat"],
|
|
35
|
-
"deny_shell": True,
|
|
36
|
-
"network": False,
|
|
37
|
-
"allow_env": False,
|
|
38
|
-
},
|
|
39
|
-
},
|
|
22
|
+
"profiles": {name: values.copy() for name, values in BUILTIN_PROFILES.items()},
|
|
40
23
|
}
|
config/schema.py
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
|
+
from profiles.validation import validate_profiles
|
|
6
|
+
|
|
5
7
|
VALID_AGENTS = {"codex", "copilot", "claude", "opencode"}
|
|
6
8
|
|
|
7
9
|
|
|
@@ -80,32 +82,4 @@ def validate_config(config: dict[str, Any]) -> None:
|
|
|
80
82
|
raise ValueError("oasr.default_profile must be a string")
|
|
81
83
|
|
|
82
84
|
if "profiles" in config:
|
|
83
|
-
|
|
84
|
-
raise ValueError("profiles must be a table (dictionary)")
|
|
85
|
-
|
|
86
|
-
# Validate each profile structure
|
|
87
|
-
for profile_name, profile_data in config["profiles"].items():
|
|
88
|
-
if not isinstance(profile_data, dict):
|
|
89
|
-
raise ValueError(f"Profile '{profile_name}' must be a table (dictionary)")
|
|
90
|
-
|
|
91
|
-
# Validate profile fields if present
|
|
92
|
-
if "fs_read_roots" in profile_data and not isinstance(profile_data["fs_read_roots"], list):
|
|
93
|
-
raise ValueError(f"Profile '{profile_name}': fs_read_roots must be a list")
|
|
94
|
-
|
|
95
|
-
if "fs_write_roots" in profile_data and not isinstance(profile_data["fs_write_roots"], list):
|
|
96
|
-
raise ValueError(f"Profile '{profile_name}': fs_write_roots must be a list")
|
|
97
|
-
|
|
98
|
-
if "deny_paths" in profile_data and not isinstance(profile_data["deny_paths"], list):
|
|
99
|
-
raise ValueError(f"Profile '{profile_name}': deny_paths must be a list")
|
|
100
|
-
|
|
101
|
-
if "allowed_commands" in profile_data and not isinstance(profile_data["allowed_commands"], list):
|
|
102
|
-
raise ValueError(f"Profile '{profile_name}': allowed_commands must be a list")
|
|
103
|
-
|
|
104
|
-
if "deny_shell" in profile_data and not isinstance(profile_data["deny_shell"], bool):
|
|
105
|
-
raise ValueError(f"Profile '{profile_name}': deny_shell must be a boolean")
|
|
106
|
-
|
|
107
|
-
if "network" in profile_data and not isinstance(profile_data["network"], bool):
|
|
108
|
-
raise ValueError(f"Profile '{profile_name}': network must be a boolean")
|
|
109
|
-
|
|
110
|
-
if "allow_env" in profile_data and not isinstance(profile_data["allow_env"], bool):
|
|
111
|
-
raise ValueError(f"Profile '{profile_name}': allow_env must be a boolean")
|
|
85
|
+
validate_profiles(config["profiles"])
|