oasr 0.5.0__py3-none-any.whl → 0.5.2__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.
- cli.py +6 -1
- commands/completion.py +345 -0
- commands/config.py +78 -26
- commands/exec.py +22 -10
- completions/__init__.py +1 -0
- completions/bash.sh +197 -0
- completions/fish.fish +120 -0
- completions/powershell.ps1 +115 -0
- completions/zsh.sh +259 -0
- config/__init__.py +27 -18
- config/env.py +248 -0
- config/schema.py +38 -0
- {oasr-0.5.0.dist-info → oasr-0.5.2.dist-info}/METADATA +21 -1
- {oasr-0.5.0.dist-info → oasr-0.5.2.dist-info}/RECORD +18 -11
- {oasr-0.5.0.dist-info → oasr-0.5.2.dist-info}/WHEEL +0 -0
- {oasr-0.5.0.dist-info → oasr-0.5.2.dist-info}/entry_points.txt +0 -0
- {oasr-0.5.0.dist-info → oasr-0.5.2.dist-info}/licenses/LICENSE +0 -0
- {oasr-0.5.0.dist-info → oasr-0.5.2.dist-info}/licenses/NOTICE +0 -0
cli.py
CHANGED
|
@@ -13,7 +13,7 @@ from pathlib import Path
|
|
|
13
13
|
from commands import adapter, clean, clone, config, diff, exec, find, registry, sync, update, use, validate
|
|
14
14
|
from commands import help as help_cmd
|
|
15
15
|
|
|
16
|
-
__version__ = "0.5.
|
|
16
|
+
__version__ = "0.5.2"
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def main(argv: list[str] | None = None) -> int:
|
|
@@ -88,6 +88,11 @@ def create_parser() -> argparse.ArgumentParser:
|
|
|
88
88
|
|
|
89
89
|
info_cmd.register(subparsers)
|
|
90
90
|
|
|
91
|
+
# Import and register completion command
|
|
92
|
+
from commands import completion as completion_cmd
|
|
93
|
+
|
|
94
|
+
completion_cmd.register_parser(subparsers)
|
|
95
|
+
|
|
91
96
|
help_cmd.register(subparsers, parser)
|
|
92
97
|
|
|
93
98
|
return parser
|
commands/completion.py
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
"""Shell completion management for OASR."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import platform
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from config import load_config
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def register_parser(subparsers):
|
|
12
|
+
"""Register the completion subcommand."""
|
|
13
|
+
parser = subparsers.add_parser(
|
|
14
|
+
"completion",
|
|
15
|
+
help="Generate and install shell completions",
|
|
16
|
+
description="Generate shell completion scripts for bash, zsh, fish, and PowerShell.",
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
parser.add_argument(
|
|
20
|
+
"shell",
|
|
21
|
+
nargs="?",
|
|
22
|
+
choices=["bash", "zsh", "fish", "powershell", "install", "uninstall"],
|
|
23
|
+
help="Shell type or action (install/uninstall)",
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
parser.add_argument(
|
|
27
|
+
"--force",
|
|
28
|
+
action="store_true",
|
|
29
|
+
help="Force installation even if already installed",
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
parser.add_argument(
|
|
33
|
+
"--dry-run",
|
|
34
|
+
action="store_true",
|
|
35
|
+
help="Show what would be done without doing it",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
parser.set_defaults(func=run)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def detect_shell():
|
|
42
|
+
"""
|
|
43
|
+
Auto-detect the user's shell based on platform and environment.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
str: Shell name (bash, zsh, fish, powershell)
|
|
47
|
+
"""
|
|
48
|
+
if platform.system() == "Windows":
|
|
49
|
+
shell_env = os.environ.get("SHELL", "").lower()
|
|
50
|
+
if "bash" in shell_env:
|
|
51
|
+
return "bash"
|
|
52
|
+
# Check for PowerShell
|
|
53
|
+
if "PSModulePath" in os.environ or "POWERSHELL" in os.environ.get("SHELL", "").upper():
|
|
54
|
+
return "powershell"
|
|
55
|
+
if "zsh" in shell_env:
|
|
56
|
+
return "zsh"
|
|
57
|
+
if "fish" in shell_env:
|
|
58
|
+
return "fish"
|
|
59
|
+
# Default to PowerShell on Windows
|
|
60
|
+
return "powershell"
|
|
61
|
+
else:
|
|
62
|
+
# Linux/macOS
|
|
63
|
+
shell = os.environ.get("SHELL", "")
|
|
64
|
+
if "zsh" in shell:
|
|
65
|
+
return "zsh"
|
|
66
|
+
elif "fish" in shell:
|
|
67
|
+
return "fish"
|
|
68
|
+
else:
|
|
69
|
+
# Default to bash on Unix
|
|
70
|
+
return "bash"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def get_completion_script(shell):
|
|
74
|
+
"""
|
|
75
|
+
Get the completion script content for the specified shell.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
shell: Shell name (bash, zsh, fish, powershell)
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
str: Completion script content
|
|
82
|
+
"""
|
|
83
|
+
# Map shell names to completion files
|
|
84
|
+
script_files = {
|
|
85
|
+
"bash": "bash.sh",
|
|
86
|
+
"zsh": "zsh.sh",
|
|
87
|
+
"fish": "fish.fish",
|
|
88
|
+
"powershell": "powershell.ps1",
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if shell not in script_files:
|
|
92
|
+
return ""
|
|
93
|
+
|
|
94
|
+
# Get the path to the completion script
|
|
95
|
+
completion_file = Path(__file__).parent.parent / "completions" / script_files[shell]
|
|
96
|
+
|
|
97
|
+
if not completion_file.exists():
|
|
98
|
+
return f"# {shell} completion script not found"
|
|
99
|
+
|
|
100
|
+
return completion_file.read_text()
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def get_completion_path(shell, system=False):
|
|
104
|
+
"""
|
|
105
|
+
Get the installation path for completion script.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
shell: Shell name
|
|
109
|
+
system: If True, use system-wide path (requires root)
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
Path: Installation path
|
|
113
|
+
"""
|
|
114
|
+
paths = {
|
|
115
|
+
"bash": {
|
|
116
|
+
"user": Path.home() / ".bash_completion.d" / "oasr",
|
|
117
|
+
"system": Path("/etc/bash_completion.d/oasr"),
|
|
118
|
+
},
|
|
119
|
+
"zsh": {
|
|
120
|
+
"user": Path.home() / ".zsh" / "completion" / "_oasr",
|
|
121
|
+
"system": Path("/usr/local/share/zsh/site-functions/_oasr"),
|
|
122
|
+
},
|
|
123
|
+
"fish": {
|
|
124
|
+
"user": Path.home() / ".config" / "fish" / "completions" / "oasr.fish",
|
|
125
|
+
"system": Path("/usr/share/fish/vendor_completions.d/oasr.fish"),
|
|
126
|
+
},
|
|
127
|
+
"powershell": {
|
|
128
|
+
"user": Path.home() / ".config" / "powershell" / "oasr_completion.ps1",
|
|
129
|
+
"system": Path.home() / ".config" / "powershell" / "oasr_completion.ps1",
|
|
130
|
+
},
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
level = "system" if system else "user"
|
|
134
|
+
return paths.get(shell, {}).get(level)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def print_activation_instructions(shell, path):
|
|
138
|
+
"""
|
|
139
|
+
Print instructions for activating completions.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
shell: Shell name
|
|
143
|
+
path: Installation path
|
|
144
|
+
"""
|
|
145
|
+
instructions = {
|
|
146
|
+
"bash": f"""
|
|
147
|
+
✓ Completions installed for bash
|
|
148
|
+
|
|
149
|
+
To activate, add this to your ~/.bashrc:
|
|
150
|
+
source {path}
|
|
151
|
+
|
|
152
|
+
Or run:
|
|
153
|
+
echo 'source {path}' >> ~/.bashrc
|
|
154
|
+
|
|
155
|
+
Then restart your shell or run:
|
|
156
|
+
source ~/.bashrc
|
|
157
|
+
""",
|
|
158
|
+
"zsh": f"""
|
|
159
|
+
✓ Completions installed for zsh
|
|
160
|
+
|
|
161
|
+
To activate, add this to your ~/.zshrc:
|
|
162
|
+
fpath=({path.parent} $fpath)
|
|
163
|
+
autoload -Uz compinit && compinit
|
|
164
|
+
|
|
165
|
+
Then restart your shell or run:
|
|
166
|
+
source ~/.zshrc
|
|
167
|
+
""",
|
|
168
|
+
"fish": f"""
|
|
169
|
+
✓ Completions installed for fish
|
|
170
|
+
|
|
171
|
+
Completions will be active in new fish shells.
|
|
172
|
+
To activate now, run:
|
|
173
|
+
source {path}
|
|
174
|
+
""",
|
|
175
|
+
"powershell": f"""
|
|
176
|
+
✓ Completions installed for PowerShell
|
|
177
|
+
|
|
178
|
+
To activate, add this to your PowerShell profile ($PROFILE):
|
|
179
|
+
. {path}
|
|
180
|
+
|
|
181
|
+
Or run:
|
|
182
|
+
echo '. {path}' >> $PROFILE
|
|
183
|
+
|
|
184
|
+
Then restart PowerShell or run:
|
|
185
|
+
. $PROFILE
|
|
186
|
+
""",
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
print(instructions.get(shell, ""))
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def is_already_installed(shell):
|
|
193
|
+
"""
|
|
194
|
+
Check if completions are already installed for the shell.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
shell: Shell name
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
bool: True if already installed
|
|
201
|
+
"""
|
|
202
|
+
path = get_completion_path(shell)
|
|
203
|
+
if not path or not path.exists():
|
|
204
|
+
return False
|
|
205
|
+
|
|
206
|
+
# Check for our signature
|
|
207
|
+
with open(path) as f:
|
|
208
|
+
content = f.read()
|
|
209
|
+
return "# oasr completion" in content.lower()
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def run_output(shell):
|
|
213
|
+
"""
|
|
214
|
+
Output the completion script to stdout.
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
shell: Shell name
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
int: Exit code
|
|
221
|
+
"""
|
|
222
|
+
script = get_completion_script(shell)
|
|
223
|
+
print(script)
|
|
224
|
+
return 0
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def run_install(args):
|
|
228
|
+
"""
|
|
229
|
+
Install completion script.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
args: Parsed arguments
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
int: Exit code
|
|
236
|
+
"""
|
|
237
|
+
shell = args.shell if args.shell != "install" else detect_shell()
|
|
238
|
+
|
|
239
|
+
# Check config
|
|
240
|
+
config = load_config()
|
|
241
|
+
if not config.get("oasr", {}).get("completions", True):
|
|
242
|
+
print("Completions are disabled in config (oasr.completions = false)")
|
|
243
|
+
return 1
|
|
244
|
+
|
|
245
|
+
# Check if already installed
|
|
246
|
+
if is_already_installed(shell) and not args.force:
|
|
247
|
+
print(f"✓ Completions already installed for {shell}")
|
|
248
|
+
print(" Use --force to reinstall")
|
|
249
|
+
return 0
|
|
250
|
+
|
|
251
|
+
# Get paths
|
|
252
|
+
path = get_completion_path(shell)
|
|
253
|
+
if not path:
|
|
254
|
+
print(f"Error: Unsupported shell: {shell}", file=sys.stderr)
|
|
255
|
+
return 1
|
|
256
|
+
|
|
257
|
+
# Dry run
|
|
258
|
+
if args.dry_run:
|
|
259
|
+
print(f"Would install completion to: {path}")
|
|
260
|
+
print_activation_instructions(shell, path)
|
|
261
|
+
return 0
|
|
262
|
+
|
|
263
|
+
# Get script
|
|
264
|
+
script = get_completion_script(shell)
|
|
265
|
+
|
|
266
|
+
# Create directory
|
|
267
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
268
|
+
|
|
269
|
+
# Backup existing
|
|
270
|
+
if path.exists() and not is_already_installed(shell):
|
|
271
|
+
import time
|
|
272
|
+
|
|
273
|
+
backup = path.parent / f"{path.name}.backup.{int(time.time())}"
|
|
274
|
+
path.rename(backup)
|
|
275
|
+
print(f"Backed up existing file to: {backup}")
|
|
276
|
+
|
|
277
|
+
# Write script
|
|
278
|
+
path.write_text(script)
|
|
279
|
+
|
|
280
|
+
# Success message
|
|
281
|
+
print_activation_instructions(shell, path)
|
|
282
|
+
|
|
283
|
+
return 0
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def run_uninstall(args):
|
|
287
|
+
"""
|
|
288
|
+
Uninstall completion script.
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
args: Parsed arguments
|
|
292
|
+
|
|
293
|
+
Returns:
|
|
294
|
+
int: Exit code
|
|
295
|
+
"""
|
|
296
|
+
shell = args.shell if args.shell != "uninstall" else detect_shell()
|
|
297
|
+
|
|
298
|
+
path = get_completion_path(shell)
|
|
299
|
+
if not path or not path.exists():
|
|
300
|
+
print(f"No completions installed for {shell}")
|
|
301
|
+
return 0
|
|
302
|
+
|
|
303
|
+
# Dry run
|
|
304
|
+
if args.dry_run:
|
|
305
|
+
print(f"Would remove: {path}")
|
|
306
|
+
return 0
|
|
307
|
+
|
|
308
|
+
# Remove file
|
|
309
|
+
path.unlink()
|
|
310
|
+
print(f"✓ Removed completions for {shell}")
|
|
311
|
+
|
|
312
|
+
return 0
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def run(args):
|
|
316
|
+
"""
|
|
317
|
+
Execute completion command.
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
args: Parsed arguments
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
int: Exit code
|
|
324
|
+
"""
|
|
325
|
+
# No shell specified - show help
|
|
326
|
+
if not args.shell:
|
|
327
|
+
detected = detect_shell()
|
|
328
|
+
print(f"Detected shell: {detected}")
|
|
329
|
+
print("\nUsage:")
|
|
330
|
+
print(" oasr completion bash # Output bash completion script")
|
|
331
|
+
print(" oasr completion zsh # Output zsh completion script")
|
|
332
|
+
print(" oasr completion fish # Output fish completion script")
|
|
333
|
+
print(" oasr completion powershell # Output PowerShell completion script")
|
|
334
|
+
print(" oasr completion install # Auto-detect and install")
|
|
335
|
+
print(" oasr completion uninstall # Remove installed completions")
|
|
336
|
+
return 0
|
|
337
|
+
|
|
338
|
+
# Handle actions
|
|
339
|
+
if args.shell == "install":
|
|
340
|
+
return run_install(args)
|
|
341
|
+
elif args.shell == "uninstall":
|
|
342
|
+
return run_uninstall(args)
|
|
343
|
+
else:
|
|
344
|
+
# Output script to stdout
|
|
345
|
+
return run_output(args.shell)
|
commands/config.py
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
import argparse
|
|
4
4
|
import sys
|
|
5
5
|
|
|
6
|
-
from agents import detect_available_agents
|
|
6
|
+
from agents import detect_available_agents
|
|
7
7
|
from config import CONFIG_FILE, load_config, save_config
|
|
8
|
+
from config.schema import validate_agent, validate_profile_reference
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
def register(subparsers: argparse._SubParsersAction) -> None:
|
|
@@ -25,6 +26,11 @@ def register(subparsers: argparse._SubParsersAction) -> None:
|
|
|
25
26
|
)
|
|
26
27
|
set_parser.add_argument("key", help="Configuration key (e.g., 'agent')")
|
|
27
28
|
set_parser.add_argument("value", help="Configuration value")
|
|
29
|
+
set_parser.add_argument(
|
|
30
|
+
"--force",
|
|
31
|
+
action="store_true",
|
|
32
|
+
help="Skip validation (use carefully)",
|
|
33
|
+
)
|
|
28
34
|
set_parser.set_defaults(func=run_set)
|
|
29
35
|
|
|
30
36
|
# config get
|
|
@@ -57,40 +63,86 @@ def register(subparsers: argparse._SubParsersAction) -> None:
|
|
|
57
63
|
|
|
58
64
|
|
|
59
65
|
def run_set(args: argparse.Namespace) -> int:
|
|
60
|
-
"""Set a configuration value."""
|
|
66
|
+
"""Set a configuration value with validation."""
|
|
61
67
|
key = args.key.lower()
|
|
62
68
|
value = args.value
|
|
69
|
+
force = getattr(args, "force", False)
|
|
63
70
|
|
|
64
|
-
#
|
|
65
|
-
if
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
print(
|
|
70
|
-
f"Error: Invalid agent '{value}'. Must be one of: {', '.join(valid_agents)}",
|
|
71
|
-
file=sys.stderr,
|
|
72
|
-
)
|
|
71
|
+
# Parse key (support dotted notation like "validation.strict")
|
|
72
|
+
if "." in key:
|
|
73
|
+
parts = key.split(".", 1)
|
|
74
|
+
if len(parts) != 2:
|
|
75
|
+
print(f"Error: Invalid key '{key}'. Use format 'section.field' or 'agent'", file=sys.stderr)
|
|
73
76
|
return 1
|
|
77
|
+
section, field = parts
|
|
78
|
+
elif key == "agent":
|
|
79
|
+
# Special case: bare "agent" means "agent.default"
|
|
80
|
+
section, field = "agent", "default"
|
|
81
|
+
else:
|
|
82
|
+
print(f"Error: Invalid key '{key}'. Use format 'section.field' or 'agent'", file=sys.stderr)
|
|
83
|
+
return 1
|
|
74
84
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
85
|
+
# Type coercion based on field
|
|
86
|
+
original_value = value
|
|
87
|
+
if field == "strict":
|
|
88
|
+
value = value.lower() in ("true", "1", "yes", "on")
|
|
89
|
+
elif field == "reference_max_lines":
|
|
90
|
+
try:
|
|
91
|
+
value = int(value)
|
|
92
|
+
if value < 1:
|
|
93
|
+
print(f"Error: '{field}' must be a positive integer", file=sys.stderr)
|
|
94
|
+
return 1
|
|
95
|
+
except ValueError:
|
|
96
|
+
print(f"Error: '{field}' must be an integer", file=sys.stderr)
|
|
97
|
+
return 1
|
|
79
98
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
99
|
+
# Load config
|
|
100
|
+
config_path = getattr(args, "config", None)
|
|
101
|
+
config = load_config(config_path=config_path)
|
|
102
|
+
|
|
103
|
+
# Validate before setting (unless --force)
|
|
104
|
+
if not force:
|
|
105
|
+
# Validate agent
|
|
106
|
+
if section == "agent" and field == "default":
|
|
107
|
+
is_valid, error_msg = validate_agent(value)
|
|
108
|
+
if not is_valid:
|
|
109
|
+
print(f"Error: {error_msg}", file=sys.stderr)
|
|
110
|
+
print("\nTo set anyway, use: oasr config set --force agent <name>", file=sys.stderr)
|
|
111
|
+
return 1
|
|
112
|
+
|
|
113
|
+
# Validate profile reference
|
|
114
|
+
if section == "oasr" and field == "default_profile":
|
|
115
|
+
is_valid, error_msg = validate_profile_reference(value, config)
|
|
116
|
+
if not is_valid:
|
|
117
|
+
print(f"Error: {error_msg}", file=sys.stderr)
|
|
118
|
+
print("\nCreate the profile in ~/.oasr/config.toml first, or use:", file=sys.stderr)
|
|
119
|
+
print(f" oasr config set --force oasr.default_profile {value}", file=sys.stderr)
|
|
120
|
+
return 1
|
|
121
|
+
|
|
122
|
+
# Set the value
|
|
123
|
+
if section not in config:
|
|
124
|
+
config[section] = {}
|
|
125
|
+
|
|
126
|
+
config[section][field] = value
|
|
127
|
+
|
|
128
|
+
try:
|
|
129
|
+
save_config(config, config_path=config_path)
|
|
130
|
+
|
|
131
|
+
# Show confirmation
|
|
132
|
+
if section == "agent" and field == "default":
|
|
133
|
+
# Special handling for agent - check if available
|
|
134
|
+
available = detect_available_agents()
|
|
135
|
+
if value in available:
|
|
136
|
+
print(f"✓ Default agent set to: {value}")
|
|
137
|
+
else:
|
|
138
|
+
print(f"✓ Default agent set to: {value}")
|
|
139
|
+
print(f" Warning: '{value}' binary not found in PATH. Install it to use this agent.", file=sys.stderr)
|
|
84
140
|
else:
|
|
85
|
-
print(f"✓
|
|
86
|
-
print(
|
|
87
|
-
f" Warning: '{value}' binary not found in PATH. Install it to use this agent.",
|
|
88
|
-
file=sys.stderr,
|
|
89
|
-
)
|
|
141
|
+
print(f"✓ Set {section}.{field} = {original_value}")
|
|
90
142
|
|
|
91
143
|
return 0
|
|
92
|
-
|
|
93
|
-
print(f"Error:
|
|
144
|
+
except ValueError as e:
|
|
145
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
94
146
|
return 1
|
|
95
147
|
|
|
96
148
|
|
commands/exec.py
CHANGED
|
@@ -101,18 +101,30 @@ def run(args: argparse.Namespace) -> int:
|
|
|
101
101
|
# Error already printed by _get_user_prompt
|
|
102
102
|
return 1
|
|
103
103
|
|
|
104
|
-
# Determine which agent to use
|
|
105
|
-
agent_name = _get_agent_name(args)
|
|
106
|
-
if agent_name is None:
|
|
107
|
-
# Error already printed by _get_agent_name
|
|
108
|
-
return 1
|
|
109
|
-
|
|
110
104
|
# === POLICY ENFORCEMENT ===
|
|
111
|
-
#
|
|
112
|
-
|
|
105
|
+
# Build CLI overrides for config loading
|
|
106
|
+
cli_overrides = {}
|
|
107
|
+
if args.agent:
|
|
108
|
+
cli_overrides["agent"] = {"default": args.agent}
|
|
109
|
+
if args.profile:
|
|
110
|
+
cli_overrides["oasr"] = cli_overrides.get("oasr", {})
|
|
111
|
+
cli_overrides["oasr"]["default_profile"] = args.profile
|
|
112
|
+
|
|
113
|
+
# Load configuration with precedence: CLI > env > file > defaults
|
|
114
|
+
config = load_config(cli_overrides=cli_overrides)
|
|
113
115
|
|
|
114
|
-
# Determine
|
|
115
|
-
|
|
116
|
+
# Determine agent and profile from merged config
|
|
117
|
+
agent_name = config.get("agent", {}).get("default")
|
|
118
|
+
profile_name = config.get("oasr", {}).get("default_profile", "safe")
|
|
119
|
+
|
|
120
|
+
# Validate agent is set
|
|
121
|
+
if not agent_name:
|
|
122
|
+
print(
|
|
123
|
+
"Error: No agent configured. Set OASR_AGENT, use --agent flag, or run:",
|
|
124
|
+
file=sys.stderr,
|
|
125
|
+
)
|
|
126
|
+
print(" oasr config set agent <name>", file=sys.stderr)
|
|
127
|
+
return 1
|
|
116
128
|
|
|
117
129
|
# Load the policy profile
|
|
118
130
|
profile = policy.load(config, profile_name)
|
completions/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Shell completion scripts for OASR."""
|