systemlink-cli 1.3.1__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.
- slcli/__init__.py +1 -0
- slcli/__main__.py +23 -0
- slcli/_version.py +4 -0
- slcli/asset_click.py +1289 -0
- slcli/cli_formatters.py +218 -0
- slcli/cli_utils.py +504 -0
- slcli/comment_click.py +602 -0
- slcli/completion_click.py +418 -0
- slcli/config.py +81 -0
- slcli/config_click.py +498 -0
- slcli/dff_click.py +979 -0
- slcli/dff_decorators.py +24 -0
- slcli/example_click.py +404 -0
- slcli/example_loader.py +274 -0
- slcli/example_provisioner.py +2777 -0
- slcli/examples/README.md +134 -0
- slcli/examples/_schema/schema-v1.0.json +169 -0
- slcli/examples/demo-complete-workflow/README.md +323 -0
- slcli/examples/demo-complete-workflow/config.yaml +638 -0
- slcli/examples/demo-test-plans/README.md +132 -0
- slcli/examples/demo-test-plans/config.yaml +154 -0
- slcli/examples/exercise-5-1-parametric-insights/README.md +101 -0
- slcli/examples/exercise-5-1-parametric-insights/config.yaml +1589 -0
- slcli/examples/exercise-7-1-test-plans/README.md +93 -0
- slcli/examples/exercise-7-1-test-plans/config.yaml +323 -0
- slcli/examples/spec-compliance-notebooks/README.md +140 -0
- slcli/examples/spec-compliance-notebooks/config.yaml +112 -0
- slcli/examples/spec-compliance-notebooks/notebooks/SpecAnalysis_ComplianceCalculation.ipynb +1553 -0
- slcli/examples/spec-compliance-notebooks/notebooks/SpecComplianceCalculation.ipynb +1577 -0
- slcli/examples/spec-compliance-notebooks/notebooks/SpecfileExtractionAndIngestion.ipynb +912 -0
- slcli/examples/spec-compliance-notebooks/spec_template.xlsx +0 -0
- slcli/feed_click.py +892 -0
- slcli/file_click.py +932 -0
- slcli/function_click.py +1400 -0
- slcli/function_templates.py +85 -0
- slcli/main.py +406 -0
- slcli/mcp_click.py +269 -0
- slcli/mcp_server.py +748 -0
- slcli/notebook_click.py +1770 -0
- slcli/platform.py +345 -0
- slcli/policy_click.py +679 -0
- slcli/policy_utils.py +411 -0
- slcli/profiles.py +411 -0
- slcli/response_handlers.py +359 -0
- slcli/routine_click.py +763 -0
- slcli/skill_click.py +253 -0
- slcli/skills/slcli/SKILL.md +713 -0
- slcli/skills/slcli/references/analysis-recipes.md +474 -0
- slcli/skills/slcli/references/filtering.md +236 -0
- slcli/skills/systemlink-webapp/SKILL.md +744 -0
- slcli/skills/systemlink-webapp/references/deployment.md +123 -0
- slcli/skills/systemlink-webapp/references/nimble-angular.md +380 -0
- slcli/skills/systemlink-webapp/references/systemlink-services.md +192 -0
- slcli/ssl_trust.py +93 -0
- slcli/system_click.py +2216 -0
- slcli/table_utils.py +124 -0
- slcli/tag_click.py +794 -0
- slcli/templates_click.py +599 -0
- slcli/testmonitor_click.py +1667 -0
- slcli/universal_handlers.py +305 -0
- slcli/user_click.py +1218 -0
- slcli/utils.py +832 -0
- slcli/web_editor.py +295 -0
- slcli/webapp_click.py +981 -0
- slcli/workflow_preview.py +287 -0
- slcli/workflows_click.py +988 -0
- slcli/workitem_click.py +2258 -0
- slcli/workspace_click.py +576 -0
- slcli/workspace_utils.py +206 -0
- systemlink_cli-1.3.1.dist-info/METADATA +20 -0
- systemlink_cli-1.3.1.dist-info/RECORD +74 -0
- systemlink_cli-1.3.1.dist-info/WHEEL +4 -0
- systemlink_cli-1.3.1.dist-info/entry_points.txt +7 -0
- systemlink_cli-1.3.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
"""Shell completion functionality for slcli."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any, Dict, List, Optional
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_cli_commands_dynamically() -> Dict[str, List[str]]:
|
|
12
|
+
"""Extract commands and subcommands from the CLI structure dynamically.
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
Dict mapping command names to lists of their subcommands.
|
|
16
|
+
"""
|
|
17
|
+
try:
|
|
18
|
+
# Import the main CLI group to inspect its structure
|
|
19
|
+
from slcli.main import cli
|
|
20
|
+
|
|
21
|
+
commands = {}
|
|
22
|
+
|
|
23
|
+
# Get top-level commands
|
|
24
|
+
commands[""] = list(cli.commands.keys())
|
|
25
|
+
|
|
26
|
+
# Get subcommands for each command
|
|
27
|
+
for cmd_name, cmd_obj in cli.commands.items():
|
|
28
|
+
if isinstance(cmd_obj, click.Group):
|
|
29
|
+
commands[cmd_name] = list(cmd_obj.commands.keys())
|
|
30
|
+
else:
|
|
31
|
+
commands[cmd_name] = []
|
|
32
|
+
|
|
33
|
+
return commands
|
|
34
|
+
except Exception:
|
|
35
|
+
# Fallback to hardcoded commands if introspection fails
|
|
36
|
+
return {
|
|
37
|
+
"": [
|
|
38
|
+
"completion",
|
|
39
|
+
"login",
|
|
40
|
+
"logout",
|
|
41
|
+
"notebook",
|
|
42
|
+
"template",
|
|
43
|
+
"user",
|
|
44
|
+
"workflow",
|
|
45
|
+
"workspace",
|
|
46
|
+
],
|
|
47
|
+
"notebook": ["list", "get", "create", "update", "delete", "run"],
|
|
48
|
+
"template": ["list", "get", "create", "update", "delete"],
|
|
49
|
+
"user": ["list", "get", "create", "update", "delete"],
|
|
50
|
+
"workflow": ["list", "get", "create", "update", "delete", "run"],
|
|
51
|
+
"workspace": ["list", "get", "create", "update", "delete"],
|
|
52
|
+
"completion": [],
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def generate_powershell_completion_dynamic() -> str:
|
|
57
|
+
"""Generate PowerShell completion script using dynamic command discovery."""
|
|
58
|
+
commands_dict = get_cli_commands_dynamically()
|
|
59
|
+
|
|
60
|
+
# Build the command arrays as PowerShell code
|
|
61
|
+
top_level_commands = commands_dict.get("", [])
|
|
62
|
+
|
|
63
|
+
powershell_script = f"""
|
|
64
|
+
# PowerShell completion for slcli (dynamically generated)
|
|
65
|
+
Register-ArgumentCompleter -Native -CommandName slcli -ScriptBlock {{
|
|
66
|
+
param($wordToComplete, $commandAst, $cursorPosition)
|
|
67
|
+
|
|
68
|
+
# Get all arguments passed so far
|
|
69
|
+
$arguments = $commandAst.CommandElements | Select-Object -Skip 1 | ForEach-Object {{ $_.Value }}
|
|
70
|
+
|
|
71
|
+
# Dynamically defined command structure
|
|
72
|
+
$topLevelCommands = @({', '.join(f"'{cmd}'" for cmd in top_level_commands)})
|
|
73
|
+
|
|
74
|
+
# Subcommand mappings
|
|
75
|
+
$subCommands = @{{"""
|
|
76
|
+
|
|
77
|
+
# Add subcommand mappings
|
|
78
|
+
for cmd, subcmds in commands_dict.items():
|
|
79
|
+
if cmd and subcmds: # Skip empty command key and empty subcommand lists
|
|
80
|
+
subcmd_list = ", ".join(f"'{sub}'" for sub in subcmds)
|
|
81
|
+
powershell_script += f"\n '{cmd}' = @({subcmd_list})"
|
|
82
|
+
|
|
83
|
+
powershell_script += """
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# Function to get available options dynamically by calling slcli --help
|
|
87
|
+
function Get-SlcliOptions {
|
|
88
|
+
param($command, $subCommand = $null)
|
|
89
|
+
|
|
90
|
+
$options = @()
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
# Build command to get help for
|
|
94
|
+
$helpCmd = @('slcli')
|
|
95
|
+
if ($command) { $helpCmd += $command }
|
|
96
|
+
if ($subCommand) { $helpCmd += $subCommand }
|
|
97
|
+
$helpCmd += '--help'
|
|
98
|
+
|
|
99
|
+
# Get options from help output
|
|
100
|
+
$helpOutput = & $helpCmd[0] $helpCmd[1..($helpCmd.Length-1)] 2>$null
|
|
101
|
+
if ($LASTEXITCODE -eq 0) {
|
|
102
|
+
$options = $helpOutput |
|
|
103
|
+
Select-String -Pattern "^\\s+(--?\\w+(?:-\\w+)*)" |
|
|
104
|
+
ForEach-Object { $_.Matches[0].Groups[1].Value }
|
|
105
|
+
return $options
|
|
106
|
+
}
|
|
107
|
+
} catch {
|
|
108
|
+
# Fallback to basic options
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# Fallback hardcoded options
|
|
112
|
+
$options += @('--help', '-h')
|
|
113
|
+
|
|
114
|
+
if (-not $command) {
|
|
115
|
+
$options += @('--version')
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return $options
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# Determine what to complete based on current position
|
|
122
|
+
$completions = @()
|
|
123
|
+
|
|
124
|
+
if ($arguments.Count -eq 0) {
|
|
125
|
+
# Complete top-level commands
|
|
126
|
+
$completions = $topLevelCommands | Where-Object { $_ -like "$wordToComplete*" }
|
|
127
|
+
} elseif ($arguments.Count -eq 1) {
|
|
128
|
+
$firstArg = $arguments[0]
|
|
129
|
+
|
|
130
|
+
if ($firstArg.StartsWith('-')) {
|
|
131
|
+
# Complete global options
|
|
132
|
+
$completions = Get-SlcliOptions | Where-Object { $_ -like "$wordToComplete*" }
|
|
133
|
+
} elseif ($subCommands.ContainsKey($firstArg)) {
|
|
134
|
+
# Complete subcommands
|
|
135
|
+
$completions = $subCommands[$firstArg] | Where-Object { $_ -like "$wordToComplete*" }
|
|
136
|
+
}
|
|
137
|
+
} elseif ($arguments.Count -eq 2) {
|
|
138
|
+
$command = $arguments[0]
|
|
139
|
+
$subCommand = $arguments[1]
|
|
140
|
+
|
|
141
|
+
if ($subCommand.StartsWith('-')) {
|
|
142
|
+
# Complete command options
|
|
143
|
+
$completions = Get-SlcliOptions $command | Where-Object { $_ -like "$wordToComplete*" }
|
|
144
|
+
} else {
|
|
145
|
+
# Complete subcommand options
|
|
146
|
+
$completions = Get-SlcliOptions $command $subCommand | Where-Object { $_ -like "$wordToComplete*" }
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
# Complete options for the current command/subcommand context
|
|
150
|
+
$command = $arguments[0]
|
|
151
|
+
$subCommand = if ($arguments.Count -gt 1 -and -not $arguments[1].StartsWith('-')) { $arguments[1] } else { $null }
|
|
152
|
+
$completions = Get-SlcliOptions $command $subCommand | Where-Object { $_ -like "$wordToComplete*" }
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
$completions | ForEach-Object {
|
|
156
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
return powershell_script
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def detect_shell() -> Optional[str]:
|
|
165
|
+
"""Auto-detect the current shell."""
|
|
166
|
+
shell_path = os.environ.get("SHELL", "")
|
|
167
|
+
if "bash" in shell_path:
|
|
168
|
+
return "bash"
|
|
169
|
+
elif "zsh" in shell_path:
|
|
170
|
+
return "zsh"
|
|
171
|
+
elif "fish" in shell_path:
|
|
172
|
+
return "fish"
|
|
173
|
+
elif os.name == "nt" or "pwsh" in os.environ.get("PSModulePath", ""):
|
|
174
|
+
return "powershell"
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def generate_bash_completion_compatible() -> str:
|
|
179
|
+
"""Generate version-compatible bash completion script.
|
|
180
|
+
|
|
181
|
+
This script works with both old bash (3.2+) and new bash (4.4+) versions
|
|
182
|
+
by detecting bash capabilities and using appropriate options.
|
|
183
|
+
"""
|
|
184
|
+
return """_slcli_completion() {
|
|
185
|
+
local IFS=$'\\n'
|
|
186
|
+
local response
|
|
187
|
+
|
|
188
|
+
response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD _SLCLI_COMPLETE=bash_complete $1)
|
|
189
|
+
|
|
190
|
+
for completion in $response; do
|
|
191
|
+
IFS=',' read type value <<< "$completion"
|
|
192
|
+
|
|
193
|
+
if [[ $type == 'dir' ]]; then
|
|
194
|
+
COMPREPLY=()
|
|
195
|
+
if command -v compopt >/dev/null 2>&1; then
|
|
196
|
+
compopt -o dirnames
|
|
197
|
+
fi
|
|
198
|
+
elif [[ $type == 'file' ]]; then
|
|
199
|
+
COMPREPLY=()
|
|
200
|
+
if command -v compopt >/dev/null 2>&1; then
|
|
201
|
+
compopt -o default
|
|
202
|
+
fi
|
|
203
|
+
elif [[ $type == 'plain' ]]; then
|
|
204
|
+
COMPREPLY+=($value)
|
|
205
|
+
fi
|
|
206
|
+
done
|
|
207
|
+
|
|
208
|
+
return 0
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
_slcli_completion_setup() {
|
|
212
|
+
# Check if bash supports nosort option (bash 4.4+)
|
|
213
|
+
if [[ ${BASH_VERSINFO[0]} -gt 4 || (${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -ge 4) ]]; then
|
|
214
|
+
complete -o nosort -F _slcli_completion slcli
|
|
215
|
+
else
|
|
216
|
+
complete -F _slcli_completion slcli
|
|
217
|
+
fi
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
_slcli_completion_setup;"""
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def generate_completion_script(shell: str) -> Optional[str]:
|
|
224
|
+
"""Generate completion script for the specified shell."""
|
|
225
|
+
if shell.lower() == "powershell":
|
|
226
|
+
# Use dynamic completion for PowerShell
|
|
227
|
+
return generate_powershell_completion_dynamic()
|
|
228
|
+
elif shell.lower() == "bash":
|
|
229
|
+
# Use version-compatible bash completion script
|
|
230
|
+
return generate_bash_completion_compatible()
|
|
231
|
+
|
|
232
|
+
# For zsh, fish - use Click's built-in completion
|
|
233
|
+
env_vars = {
|
|
234
|
+
"zsh": ("_SLCLI_COMPLETE", "zsh_source"),
|
|
235
|
+
"fish": ("_SLCLI_COMPLETE", "fish_source"),
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if shell.lower() not in env_vars:
|
|
239
|
+
return None
|
|
240
|
+
|
|
241
|
+
env_var, env_value = env_vars[shell.lower()]
|
|
242
|
+
|
|
243
|
+
try:
|
|
244
|
+
result = subprocess.run(
|
|
245
|
+
["slcli"], capture_output=True, text=True, env={**os.environ, env_var: env_value}
|
|
246
|
+
)
|
|
247
|
+
return result.stdout if result.returncode == 0 else None
|
|
248
|
+
except Exception:
|
|
249
|
+
return None
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def install_bash_completion(completion_script: str) -> bool:
|
|
253
|
+
"""Install bash completion script."""
|
|
254
|
+
home = Path.home()
|
|
255
|
+
|
|
256
|
+
# Try common bash completion directories
|
|
257
|
+
completion_dirs = [
|
|
258
|
+
home / ".bash_completion.d",
|
|
259
|
+
Path("/usr/local/etc/bash_completion.d"),
|
|
260
|
+
Path("/etc/bash_completion.d"),
|
|
261
|
+
]
|
|
262
|
+
|
|
263
|
+
# Find writable completion directory
|
|
264
|
+
completion_dir = None
|
|
265
|
+
for dir_path in completion_dirs:
|
|
266
|
+
if dir_path.exists() and os.access(dir_path, os.W_OK):
|
|
267
|
+
completion_dir = dir_path
|
|
268
|
+
break
|
|
269
|
+
|
|
270
|
+
if not completion_dir:
|
|
271
|
+
# Create user completion directory
|
|
272
|
+
completion_dir = home / ".bash_completion.d"
|
|
273
|
+
completion_dir.mkdir(exist_ok=True)
|
|
274
|
+
|
|
275
|
+
completion_file = completion_dir / "slcli"
|
|
276
|
+
completion_file.write_text(completion_script)
|
|
277
|
+
|
|
278
|
+
click.echo(f"✓ Bash completion installed to {completion_file}")
|
|
279
|
+
click.echo("To activate, reload your shell or run:")
|
|
280
|
+
click.echo(f" source {completion_file}")
|
|
281
|
+
return True
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def install_zsh_completion(completion_script: str) -> bool:
|
|
285
|
+
"""Install zsh completion script."""
|
|
286
|
+
home = Path.home()
|
|
287
|
+
zsh_completion_dir = home / ".zsh_completions"
|
|
288
|
+
zsh_completion_dir.mkdir(exist_ok=True)
|
|
289
|
+
completion_file = zsh_completion_dir / "_slcli"
|
|
290
|
+
|
|
291
|
+
completion_file.write_text(completion_script)
|
|
292
|
+
click.echo(f"✓ Zsh completion installed to {completion_file}")
|
|
293
|
+
click.echo("To activate, add this to your ~/.zshrc:")
|
|
294
|
+
click.echo(" fpath=(~/.zsh_completions $fpath)")
|
|
295
|
+
click.echo(" autoload -U compinit && compinit")
|
|
296
|
+
return True
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def install_fish_completion(completion_script: str) -> bool:
|
|
300
|
+
"""Install fish completion script."""
|
|
301
|
+
home = Path.home()
|
|
302
|
+
fish_completion_dir = home / ".config/fish/completions"
|
|
303
|
+
fish_completion_dir.mkdir(parents=True, exist_ok=True)
|
|
304
|
+
completion_file = fish_completion_dir / "slcli.fish"
|
|
305
|
+
|
|
306
|
+
completion_file.write_text(completion_script)
|
|
307
|
+
click.echo(f"✓ Fish completion installed to {completion_file}")
|
|
308
|
+
click.echo("Completion is now active (restart fish if needed)")
|
|
309
|
+
return True
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def install_powershell_completion(completion_script: str) -> bool:
|
|
313
|
+
"""Install PowerShell completion script."""
|
|
314
|
+
home = Path.home()
|
|
315
|
+
|
|
316
|
+
if os.name == "nt":
|
|
317
|
+
# Windows PowerShell profile
|
|
318
|
+
ps_profile_dirs = [
|
|
319
|
+
home / "Documents" / "PowerShell",
|
|
320
|
+
home / "Documents" / "WindowsPowerShell",
|
|
321
|
+
]
|
|
322
|
+
else:
|
|
323
|
+
# PowerShell Core on Unix
|
|
324
|
+
ps_profile_dirs = [
|
|
325
|
+
home / ".config" / "powershell",
|
|
326
|
+
]
|
|
327
|
+
|
|
328
|
+
# Find or create PowerShell profile directory
|
|
329
|
+
profile_dir = None
|
|
330
|
+
for dir_path in ps_profile_dirs:
|
|
331
|
+
if dir_path.exists():
|
|
332
|
+
profile_dir = dir_path
|
|
333
|
+
break
|
|
334
|
+
|
|
335
|
+
if not profile_dir:
|
|
336
|
+
# Create the first option
|
|
337
|
+
profile_dir = ps_profile_dirs[0]
|
|
338
|
+
profile_dir.mkdir(parents=True, exist_ok=True)
|
|
339
|
+
|
|
340
|
+
completion_file = profile_dir / "slcli_completion.ps1"
|
|
341
|
+
completion_file.write_text(completion_script)
|
|
342
|
+
|
|
343
|
+
click.echo(f"✓ PowerShell completion installed to {completion_file}")
|
|
344
|
+
click.echo("To activate, add this line to your PowerShell profile:")
|
|
345
|
+
click.echo(f" . {completion_file}")
|
|
346
|
+
click.echo("Or run this command to add it automatically:")
|
|
347
|
+
click.echo(f" Add-Content $PROFILE '. {completion_file}'")
|
|
348
|
+
return True
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def install_completion_for_shell(shell: str) -> bool:
|
|
352
|
+
"""Install completion script for the specified shell."""
|
|
353
|
+
completion_script = generate_completion_script(shell)
|
|
354
|
+
|
|
355
|
+
if not completion_script:
|
|
356
|
+
click.echo("✗ Failed to generate completion script", err=True)
|
|
357
|
+
return False
|
|
358
|
+
|
|
359
|
+
installers = {
|
|
360
|
+
"bash": install_bash_completion,
|
|
361
|
+
"zsh": install_zsh_completion,
|
|
362
|
+
"fish": install_fish_completion,
|
|
363
|
+
"powershell": install_powershell_completion,
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
installer = installers.get(shell.lower())
|
|
367
|
+
if not installer:
|
|
368
|
+
click.echo(f"✗ Unsupported shell: {shell}", err=True)
|
|
369
|
+
return False
|
|
370
|
+
|
|
371
|
+
return installer(completion_script)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def register_completion_command(cli: Any) -> None:
|
|
375
|
+
"""Register the completion command with the CLI."""
|
|
376
|
+
|
|
377
|
+
@cli.command()
|
|
378
|
+
@click.option(
|
|
379
|
+
"--shell",
|
|
380
|
+
type=click.Choice(["bash", "zsh", "fish", "powershell"], case_sensitive=False),
|
|
381
|
+
help="Shell type (auto-detected if not specified)",
|
|
382
|
+
)
|
|
383
|
+
@click.option(
|
|
384
|
+
"--install",
|
|
385
|
+
is_flag=True,
|
|
386
|
+
help="Install completion script to shell config file",
|
|
387
|
+
)
|
|
388
|
+
def completion(shell: Optional[str], install: bool) -> None:
|
|
389
|
+
"""Generate and optionally install shell completion scripts.
|
|
390
|
+
|
|
391
|
+
Examples:
|
|
392
|
+
# Generate bash completion script
|
|
393
|
+
slcli completion --shell bash
|
|
394
|
+
|
|
395
|
+
# Install completion for current shell
|
|
396
|
+
slcli completion --install
|
|
397
|
+
|
|
398
|
+
# Generate and save to file
|
|
399
|
+
slcli completion --shell zsh > ~/.zsh_completions/_slcli
|
|
400
|
+
"""
|
|
401
|
+
# Auto-detect shell if not specified
|
|
402
|
+
if not shell:
|
|
403
|
+
shell = detect_shell()
|
|
404
|
+
if not shell:
|
|
405
|
+
click.echo("Could not auto-detect shell. Please specify with --shell", err=True)
|
|
406
|
+
return
|
|
407
|
+
|
|
408
|
+
if install:
|
|
409
|
+
# Install completion script
|
|
410
|
+
install_completion_for_shell(shell)
|
|
411
|
+
else:
|
|
412
|
+
# Just output the completion script
|
|
413
|
+
completion_script = generate_completion_script(shell)
|
|
414
|
+
|
|
415
|
+
if completion_script:
|
|
416
|
+
click.echo(completion_script)
|
|
417
|
+
else:
|
|
418
|
+
click.echo("✗ Failed to generate completion script", err=True)
|
slcli/config.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Configuration management for slcli."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Dict, Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_config_file_path() -> Path:
|
|
10
|
+
"""Get the path to the slcli configuration file."""
|
|
11
|
+
# Use XDG_CONFIG_HOME if set, otherwise use ~/.config
|
|
12
|
+
if "XDG_CONFIG_HOME" in os.environ:
|
|
13
|
+
config_dir = Path(os.environ["XDG_CONFIG_HOME"]) / "slcli"
|
|
14
|
+
else:
|
|
15
|
+
config_dir = Path.home() / ".config" / "slcli"
|
|
16
|
+
|
|
17
|
+
config_dir.mkdir(parents=True, exist_ok=True)
|
|
18
|
+
return config_dir / "config.json"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def load_config() -> Dict[str, str]:
|
|
22
|
+
"""Load configuration from the config file.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
Dictionary containing configuration values
|
|
26
|
+
"""
|
|
27
|
+
config_file = get_config_file_path()
|
|
28
|
+
|
|
29
|
+
if not config_file.exists():
|
|
30
|
+
return {}
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
with open(config_file, "r", encoding="utf-8") as f:
|
|
34
|
+
return json.load(f)
|
|
35
|
+
except (json.JSONDecodeError, OSError):
|
|
36
|
+
# If config file is corrupted or unreadable, return empty config
|
|
37
|
+
return {}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def save_config(config: Dict[str, str]) -> None:
|
|
41
|
+
"""Save configuration to the config file.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
config: Dictionary containing configuration values to save
|
|
45
|
+
"""
|
|
46
|
+
config_file = get_config_file_path()
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
with open(config_file, "w", encoding="utf-8") as f:
|
|
50
|
+
json.dump(config, f, indent=2)
|
|
51
|
+
except OSError as e:
|
|
52
|
+
raise RuntimeError(f"Failed to save configuration: {e}")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def get_function_service_url() -> Optional[str]:
|
|
56
|
+
"""Get the configured URL for the Function Service.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
The configured function service URL, or None if not configured
|
|
60
|
+
"""
|
|
61
|
+
config = load_config()
|
|
62
|
+
return config.get("function_service_url")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def set_function_service_url(url: str) -> None:
|
|
66
|
+
"""Set the URL for the Function Service.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
url: The URL to use for function commands
|
|
70
|
+
"""
|
|
71
|
+
config = load_config()
|
|
72
|
+
config["function_service_url"] = url
|
|
73
|
+
save_config(config)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def remove_function_service_url() -> None:
|
|
77
|
+
"""Remove the configured Function Service URL."""
|
|
78
|
+
config = load_config()
|
|
79
|
+
if "function_service_url" in config:
|
|
80
|
+
del config["function_service_url"]
|
|
81
|
+
save_config(config)
|