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.
Files changed (74) hide show
  1. slcli/__init__.py +1 -0
  2. slcli/__main__.py +23 -0
  3. slcli/_version.py +4 -0
  4. slcli/asset_click.py +1289 -0
  5. slcli/cli_formatters.py +218 -0
  6. slcli/cli_utils.py +504 -0
  7. slcli/comment_click.py +602 -0
  8. slcli/completion_click.py +418 -0
  9. slcli/config.py +81 -0
  10. slcli/config_click.py +498 -0
  11. slcli/dff_click.py +979 -0
  12. slcli/dff_decorators.py +24 -0
  13. slcli/example_click.py +404 -0
  14. slcli/example_loader.py +274 -0
  15. slcli/example_provisioner.py +2777 -0
  16. slcli/examples/README.md +134 -0
  17. slcli/examples/_schema/schema-v1.0.json +169 -0
  18. slcli/examples/demo-complete-workflow/README.md +323 -0
  19. slcli/examples/demo-complete-workflow/config.yaml +638 -0
  20. slcli/examples/demo-test-plans/README.md +132 -0
  21. slcli/examples/demo-test-plans/config.yaml +154 -0
  22. slcli/examples/exercise-5-1-parametric-insights/README.md +101 -0
  23. slcli/examples/exercise-5-1-parametric-insights/config.yaml +1589 -0
  24. slcli/examples/exercise-7-1-test-plans/README.md +93 -0
  25. slcli/examples/exercise-7-1-test-plans/config.yaml +323 -0
  26. slcli/examples/spec-compliance-notebooks/README.md +140 -0
  27. slcli/examples/spec-compliance-notebooks/config.yaml +112 -0
  28. slcli/examples/spec-compliance-notebooks/notebooks/SpecAnalysis_ComplianceCalculation.ipynb +1553 -0
  29. slcli/examples/spec-compliance-notebooks/notebooks/SpecComplianceCalculation.ipynb +1577 -0
  30. slcli/examples/spec-compliance-notebooks/notebooks/SpecfileExtractionAndIngestion.ipynb +912 -0
  31. slcli/examples/spec-compliance-notebooks/spec_template.xlsx +0 -0
  32. slcli/feed_click.py +892 -0
  33. slcli/file_click.py +932 -0
  34. slcli/function_click.py +1400 -0
  35. slcli/function_templates.py +85 -0
  36. slcli/main.py +406 -0
  37. slcli/mcp_click.py +269 -0
  38. slcli/mcp_server.py +748 -0
  39. slcli/notebook_click.py +1770 -0
  40. slcli/platform.py +345 -0
  41. slcli/policy_click.py +679 -0
  42. slcli/policy_utils.py +411 -0
  43. slcli/profiles.py +411 -0
  44. slcli/response_handlers.py +359 -0
  45. slcli/routine_click.py +763 -0
  46. slcli/skill_click.py +253 -0
  47. slcli/skills/slcli/SKILL.md +713 -0
  48. slcli/skills/slcli/references/analysis-recipes.md +474 -0
  49. slcli/skills/slcli/references/filtering.md +236 -0
  50. slcli/skills/systemlink-webapp/SKILL.md +744 -0
  51. slcli/skills/systemlink-webapp/references/deployment.md +123 -0
  52. slcli/skills/systemlink-webapp/references/nimble-angular.md +380 -0
  53. slcli/skills/systemlink-webapp/references/systemlink-services.md +192 -0
  54. slcli/ssl_trust.py +93 -0
  55. slcli/system_click.py +2216 -0
  56. slcli/table_utils.py +124 -0
  57. slcli/tag_click.py +794 -0
  58. slcli/templates_click.py +599 -0
  59. slcli/testmonitor_click.py +1667 -0
  60. slcli/universal_handlers.py +305 -0
  61. slcli/user_click.py +1218 -0
  62. slcli/utils.py +832 -0
  63. slcli/web_editor.py +295 -0
  64. slcli/webapp_click.py +981 -0
  65. slcli/workflow_preview.py +287 -0
  66. slcli/workflows_click.py +988 -0
  67. slcli/workitem_click.py +2258 -0
  68. slcli/workspace_click.py +576 -0
  69. slcli/workspace_utils.py +206 -0
  70. systemlink_cli-1.3.1.dist-info/METADATA +20 -0
  71. systemlink_cli-1.3.1.dist-info/RECORD +74 -0
  72. systemlink_cli-1.3.1.dist-info/WHEEL +4 -0
  73. systemlink_cli-1.3.1.dist-info/entry_points.txt +7 -0
  74. 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)