systemlink-cli 1.27.2__tar.gz → 1.27.3__tar.gz

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 (158) hide show
  1. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/PKG-INFO +1 -1
  2. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/pyproject.toml +1 -1
  3. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/_version.py +1 -1
  4. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/main.py +12 -2
  5. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/platform.py +21 -5
  6. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skill_click.py +107 -4
  7. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/SKILL.md +28 -11
  8. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/commands.md +38 -1
  9. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/troubleshooting.md +22 -0
  10. systemlink_cli-1.27.3/slcli/version_click.py +199 -0
  11. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_click.py +8 -2
  12. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/LICENSE +0 -0
  13. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/dff-editor/editor.js +0 -0
  14. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/dff-editor/index.html +0 -0
  15. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/__init__.py +0 -0
  16. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/__main__.py +0 -0
  17. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/alarm_click.py +0 -0
  18. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/asset_click.py +0 -0
  19. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/cli_formatters.py +0 -0
  20. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/cli_utils.py +0 -0
  21. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/comment_click.py +0 -0
  22. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/completion_click.py +0 -0
  23. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/config.py +0 -0
  24. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/config_click.py +0 -0
  25. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/dataframe_click.py +0 -0
  26. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/dff_click.py +0 -0
  27. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/dff_decorators.py +0 -0
  28. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/example_click.py +0 -0
  29. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/example_loader.py +0 -0
  30. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/example_provisioner.py +0 -0
  31. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/README.md +0 -0
  32. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/_schema/schema-v1.0.json +0 -0
  33. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/demo-data-1/README.md +0 -0
  34. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/demo-data-1/config.yaml +0 -0
  35. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/demo-data-2/README.md +0 -0
  36. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/demo-data-2/config.yaml +0 -0
  37. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/demo-data-3/config.yaml +0 -0
  38. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/demo-data-3/leakage-current-results.json +0 -0
  39. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/demo-data-3/overvoltage-results.json +0 -0
  40. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/demo-data-3/product-xyz-compliance-report.json +0 -0
  41. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/demo-data-3/product-xyz-specification.csv +0 -0
  42. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/demo-data-3/thermal-response-results.json +0 -0
  43. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/exercise-5-1-parametric-insights/README.md +0 -0
  44. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/exercise-5-1-parametric-insights/config.yaml +0 -0
  45. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/exercise-7-1-test-plans/README.md +0 -0
  46. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/exercise-7-1-test-plans/config.yaml +0 -0
  47. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/spec-compliance-notebooks/README.md +0 -0
  48. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/spec-compliance-notebooks/config.yaml +0 -0
  49. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/spec-compliance-notebooks/notebooks/SpecAnalysis_ComplianceCalculation.ipynb +0 -0
  50. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/spec-compliance-notebooks/notebooks/SpecComplianceCalculation.ipynb +0 -0
  51. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/spec-compliance-notebooks/notebooks/SpecfileExtractionAndIngestion.ipynb +0 -0
  52. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/spec-compliance-notebooks/spec_template.xlsx +0 -0
  53. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/feed_click.py +0 -0
  54. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/file_click.py +0 -0
  55. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/function_click.py +0 -0
  56. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/function_templates.py +0 -0
  57. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/mcp_click.py +0 -0
  58. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/mcp_reachability.py +0 -0
  59. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/mcp_server.py +0 -0
  60. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/notebook_click.py +0 -0
  61. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/pkce.py +0 -0
  62. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/policy_click.py +0 -0
  63. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/policy_utils.py +0 -0
  64. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/profiles.py +0 -0
  65. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/response_handlers.py +0 -0
  66. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/rich_output.py +0 -0
  67. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/routine_click.py +0 -0
  68. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/analysis-recipes.md +0 -0
  69. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/datasheet-workflow.md +0 -0
  70. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/example-authoring.md +0 -0
  71. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/filtering.md +0 -0
  72. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/import-specs.min.json +0 -0
  73. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/job-debugging/overview.md +0 -0
  74. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/nipkg/overview.md +0 -0
  75. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/notebook/interfaces.md +0 -0
  76. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/notebook/notebook-patterns.md +0 -0
  77. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/notebook/overview.md +0 -0
  78. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/python-test/overview.md +0 -0
  79. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/routine-examples.md +0 -0
  80. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/webapp/angular-ui-packages.md +0 -0
  81. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/webapp/deployment.md +0 -0
  82. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/webapp/layout-patterns.md +0 -0
  83. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/webapp/nimble-angular.md +0 -0
  84. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/webapp/overview.md +0 -0
  85. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/webapp/systemlink-services.md +0 -0
  86. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/webapp/troubleshooting.md +0 -0
  87. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/scripts/spec_import_helper.py +0 -0
  88. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/spec_click.py +0 -0
  89. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/ssl_trust.py +0 -0
  90. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/state_click.py +0 -0
  91. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/system_click.py +0 -0
  92. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/system_query_utils.py +0 -0
  93. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/systemlink-logo.svg +0 -0
  94. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/table_utils.py +0 -0
  95. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/tag_click.py +0 -0
  96. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/templates_click.py +0 -0
  97. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/testmonitor_click.py +0 -0
  98. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/universal_handlers.py +0 -0
  99. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/user_click.py +0 -0
  100. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/utils.py +0 -0
  101. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/web_editor.py +0 -0
  102. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_bootstrap.py +0 -0
  103. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/.gitignore +0 -0
  104. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/README.md +0 -0
  105. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/angular.json +0 -0
  106. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/package.json +0 -0
  107. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/scripts/filter-sbom.js +0 -0
  108. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/app-routing.module.ts +0 -0
  109. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/app.component.html +0 -0
  110. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/app.component.scss +0 -0
  111. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/app.component.ts +0 -0
  112. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/app.module.ts +0 -0
  113. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/core/layout/app-shell.component.html +0 -0
  114. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/core/layout/app-shell.component.scss +0 -0
  115. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/core/layout/app-shell.component.ts +0 -0
  116. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/core/state/app-view-state.service.ts +0 -0
  117. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/core/systemlink/systemlink-context.service.ts +0 -0
  118. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/core/systemlink/theme-sync.service.ts +0 -0
  119. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/core/systemlink/webapp-home-data.service.ts +0 -0
  120. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/assets/assets-page.component.html +0 -0
  121. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/assets/assets-page.component.scss +0 -0
  122. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/assets/assets-page.component.ts +0 -0
  123. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/datasets/datasets-page.component.html +0 -0
  124. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/datasets/datasets-page.component.scss +0 -0
  125. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/datasets/datasets-page.component.ts +0 -0
  126. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/home/home-page.component.html +0 -0
  127. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/home/home-page.component.scss +0 -0
  128. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/home/home-page.component.ts +0 -0
  129. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/master-detail/master-detail-page.component.html +0 -0
  130. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/master-detail/master-detail-page.component.scss +0 -0
  131. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/master-detail/master-detail-page.component.ts +0 -0
  132. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/operations/operations-page.component.html +0 -0
  133. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/operations/operations-page.component.scss +0 -0
  134. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/operations/operations-page.component.ts +0 -0
  135. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/settings/settings-page.component.html +0 -0
  136. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/settings/settings-page.component.scss +0 -0
  137. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/settings/settings-page.component.ts +0 -0
  138. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/shared/components/empty-state.component.html +0 -0
  139. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/shared/components/empty-state.component.scss +0 -0
  140. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/shared/components/empty-state.component.ts +0 -0
  141. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/shared/components/error-banner.component.html +0 -0
  142. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/shared/components/error-banner.component.scss +0 -0
  143. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/shared/components/error-banner.component.ts +0 -0
  144. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/shared/components/loading-state.component.html +0 -0
  145. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/shared/components/loading-state.component.scss +0 -0
  146. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/shared/components/loading-state.component.ts +0 -0
  147. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/shared/states/view-state.model.ts +0 -0
  148. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/assets/README.md +0 -0
  149. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/index.html +0 -0
  150. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/main.ts +0 -0
  151. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/styles.scss +0 -0
  152. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/tsconfig.app.json +0 -0
  153. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/tsconfig.json +0 -0
  154. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/workflow_preview.py +0 -0
  155. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/workflows_click.py +0 -0
  156. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/workitem_click.py +0 -0
  157. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/workspace_click.py +0 -0
  158. {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/workspace_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: systemlink-cli
3
- Version: 1.27.2
3
+ Version: 1.27.3
4
4
  Summary: SystemLink Integrator CLI - cross-platform CLI for SystemLink workflows and templates.
5
5
  License-File: LICENSE
6
6
  Author: Fred Visser
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "systemlink-cli"
3
- version = "1.27.2"
3
+ version = "1.27.3"
4
4
  description = "SystemLink Integrator CLI - cross-platform CLI for SystemLink workflows and templates."
5
5
  authors = ["Fred Visser <fred.visser@emerson.com>"]
6
6
  packages = [{ include = "slcli" }]
@@ -1,4 +1,4 @@
1
1
  """Version information for slcli."""
2
2
 
3
3
  # This file is auto-generated. Do not edit manually.
4
- __version__ = "1.27.2"
4
+ __version__ = "1.27.3"
@@ -43,6 +43,7 @@ from .templates_click import register_templates_commands
43
43
  from .testmonitor_click import register_testmonitor_commands
44
44
  from .user_click import register_user_commands
45
45
  from .utils import describe_config_source
46
+ from .version_click import register_version_commands
46
47
  from .webapp_click import register_webapp_commands
47
48
  from .workitem_click import register_workitem_commands
48
49
  from .workspace_click import register_workspace_commands
@@ -71,7 +72,15 @@ def _configure_rich_click_command_groups() -> None:
71
72
  "slcli": [
72
73
  {
73
74
  "name": "Configure",
74
- "commands": ["config", "login", "logout", "info", "completion", "example"],
75
+ "commands": [
76
+ "config",
77
+ "login",
78
+ "logout",
79
+ "info",
80
+ "version",
81
+ "completion",
82
+ "example",
83
+ ],
75
84
  },
76
85
  {
77
86
  "name": "Administer",
@@ -363,7 +372,7 @@ def cli(ctx: base_click.Context, version: bool, profile: Optional[str]) -> None:
363
372
 
364
373
  # Check for mandatory migration BEFORE any command runs
365
374
  # Skip migration check only for version flag and config migrate command
366
- if ctx.invoked_subcommand not in (None, "config"):
375
+ if ctx.invoked_subcommand not in (None, "config", "version"):
367
376
  from .profiles import ProfileConfig, has_keyring_credentials, migrate_from_keyring
368
377
 
369
378
  config_path = ProfileConfig.get_config_path()
@@ -769,5 +778,6 @@ register_testmonitor_commands(cli)
769
778
  register_webapp_commands(cli)
770
779
  register_skill_commands(cli)
771
780
  register_user_commands(cli)
781
+ register_version_commands(cli)
772
782
  register_workitem_commands(cli)
773
783
  register_workspace_commands(cli)
@@ -168,8 +168,9 @@ def get_platform() -> str:
168
168
 
169
169
  Detection priority:
170
170
  1. SYSTEMLINK_PLATFORM environment variable (explicit, most reliable)
171
- 2. Stored platform from keyring config (set during login via endpoint probing)
172
- 3. Return PLATFORM_UNKNOWN if no explicit or stored platform is available
171
+ 2. Platform stored on the active profile (set during login via endpoint probing)
172
+ 3. Stored platform from keyring config (legacy fallback)
173
+ 4. Return PLATFORM_UNKNOWN if no explicit or stored platform is available
173
174
 
174
175
  Note: Results are cached for performance. Use clear_platform_cache() to reset.
175
176
 
@@ -182,11 +183,26 @@ def get_platform() -> str:
182
183
  if env_platform in (PLATFORM_SLE, PLATFORM_SLS):
183
184
  return env_platform
184
185
 
185
- # Priority 2: Stored platform from keyring config (set during login)
186
- # This was detected via endpoint probing, which is reliable
186
+ if not any(os.environ.get(name) for name in ("SLCLI_API_URL", "SYSTEMLINK_API_URL")):
187
+ try:
188
+ from .profiles import get_active_profile
189
+
190
+ profile = get_active_profile()
191
+ profile_platform = str(profile.platform or "").upper() if profile else ""
192
+ if profile_platform in (PLATFORM_SLE, PLATFORM_SLS):
193
+ return profile_platform
194
+ except (
195
+ FileNotFoundError,
196
+ json.JSONDecodeError,
197
+ KeyError,
198
+ AttributeError,
199
+ click.ClickException,
200
+ ):
201
+ pass
202
+
187
203
  cfg = _get_keyring_config()
188
204
  if cfg:
189
- platform = cfg.get("platform", "")
205
+ platform = str(cfg.get("platform", "")).upper()
190
206
  if platform in (PLATFORM_SLE, PLATFORM_SLS):
191
207
  return platform
192
208
 
@@ -6,11 +6,14 @@ from pathlib import Path
6
6
  from typing import Any, Dict, List, Optional, Tuple
7
7
 
8
8
  import click
9
+ from packaging.version import InvalidVersion, Version
9
10
 
11
+ from ._version import __version__
10
12
  from .utils import ExitCodes, format_success
11
13
 
12
14
  SKILL_NAME = "slcli"
13
15
  _FALLBACK_SKILL_CHOICES = [SKILL_NAME]
16
+ _VERSION_FILENAME = ".slcli-version"
14
17
 
15
18
  # Mapping of client name -> (personal skills dir, project subdir relative to repo root)
16
19
  # personal dir uses Path.home() so it's always resolved at call time via _personal_dir().
@@ -119,6 +122,23 @@ def _expand_skill_selection(skill_names: Optional[List[str]] = None) -> List[str
119
122
  return requested
120
123
 
121
124
 
125
+ def _skill_installation_status(destination_root: Path, skill_name: str) -> str:
126
+ """Return the version status of an installed skill."""
127
+ destination_dir = destination_root / skill_name
128
+ if not destination_dir.exists():
129
+ return "missing"
130
+
131
+ version_file = destination_dir / _VERSION_FILENAME
132
+ if not version_file.exists():
133
+ return "unversioned"
134
+
135
+ try:
136
+ installed_version = version_file.read_text(encoding="utf-8").strip()
137
+ return "current" if Version(installed_version) >= Version(__version__) else "outdated"
138
+ except (InvalidVersion, OSError):
139
+ return "unversioned"
140
+
141
+
122
142
  def install_skills_to_directory(
123
143
  directory: Path,
124
144
  skill_names: Optional[List[str]] = None,
@@ -150,6 +170,7 @@ def install_skills_to_directory(
150
170
  destination_dir.unlink()
151
171
 
152
172
  shutil.copytree(source_dir, destination_dir)
173
+ (destination_dir / _VERSION_FILENAME).write_text(f"{__version__}\n", encoding="utf-8")
153
174
 
154
175
  return len(selected_skills)
155
176
 
@@ -184,6 +205,19 @@ def _resolve_destinations(clients: List[str], scope: str) -> List[Path]:
184
205
  return seen
185
206
 
186
207
 
208
+ def _requested_destinations(
209
+ directory: Optional[Path], client: Optional[str], scope: Optional[str]
210
+ ) -> List[Path]:
211
+ """Resolve either a custom skills directory or client-specific destinations."""
212
+ if directory is not None:
213
+ if client is not None or scope is not None:
214
+ raise ValueError("--directory cannot be combined with --client or --scope")
215
+ return [directory.expanduser()]
216
+
217
+ selected_clients = CLIENT_CHOICES if client == "all" else [client or "agents"]
218
+ return _resolve_destinations(selected_clients, scope or "project")
219
+
220
+
187
221
  def register_skill_commands(cli: Any) -> None:
188
222
  """Register the skill command group with the CLI.
189
223
 
@@ -217,6 +251,13 @@ def register_skill_commands(cli: Any) -> None:
217
251
  default=None,
218
252
  help="personal (~/ home dirs), project (current repo), or both.",
219
253
  )
254
+ @click.option(
255
+ "--directory",
256
+ "-d",
257
+ type=click.Path(file_okay=False, path_type=Path),
258
+ default=None,
259
+ help="Install directly into this skills directory.",
260
+ )
220
261
  @click.option(
221
262
  "--force",
222
263
  "-F",
@@ -225,7 +266,11 @@ def register_skill_commands(cli: Any) -> None:
225
266
  help="Overwrite existing skill installation without prompting.",
226
267
  )
227
268
  def install_skill(
228
- skill: Optional[str], client: Optional[str], scope: Optional[str], force: bool
269
+ skill: Optional[str],
270
+ client: Optional[str],
271
+ scope: Optional[str],
272
+ directory: Optional[Path],
273
+ force: bool,
229
274
  ) -> None:
230
275
  """Install bundled skills for supported AI clients."""
231
276
  requested_skills: Optional[List[str]]
@@ -235,11 +280,9 @@ def register_skill_commands(cli: Any) -> None:
235
280
  assert skill is not None
236
281
  requested_skills = [skill.lower()]
237
282
  selected_skill_names = _expand_skill_selection(requested_skills)
238
- selected_clients = CLIENT_CHOICES if client == "all" else [client or "agents"]
239
- selected_scope = scope or "project"
240
- destinations = _resolve_destinations(selected_clients, selected_scope)
241
283
 
242
284
  try:
285
+ destinations = _requested_destinations(directory, client, scope)
243
286
  installed_count = 0
244
287
  for destination in destinations:
245
288
  installed_count += install_skills_to_directory(
@@ -257,6 +300,9 @@ def register_skill_commands(cli: Any) -> None:
257
300
  except FileNotFoundError as exc:
258
301
  click.echo(f"✗ {exc}", err=True)
259
302
  sys.exit(ExitCodes.GENERAL_ERROR)
303
+ except OSError as exc:
304
+ click.echo(f"✗ Unable to install skills: {exc}", err=True)
305
+ sys.exit(ExitCodes.GENERAL_ERROR)
260
306
 
261
307
  format_success(
262
308
  "Installed bundled AI skills",
@@ -266,3 +312,60 @@ def register_skill_commands(cli: Any) -> None:
266
312
  "Installed": str(installed_count),
267
313
  },
268
314
  )
315
+
316
+ @skill.command(name="check")
317
+ @click.option(
318
+ "--skill",
319
+ "-k",
320
+ type=click.Choice(SKILL_CHOICES + ["all"], case_sensitive=False),
321
+ default=None,
322
+ help=f"Skill to check ({', '.join(SKILL_CHOICES)}, or all).",
323
+ )
324
+ @click.option(
325
+ "--client",
326
+ "-c",
327
+ type=click.Choice(CLIENT_CHOICES + ["all"], case_sensitive=False),
328
+ default=None,
329
+ help="AI client installation to check (agents, claude, or all).",
330
+ )
331
+ @click.option(
332
+ "--scope",
333
+ "-s",
334
+ type=click.Choice(["personal", "project", "both"], case_sensitive=False),
335
+ default=None,
336
+ help="personal (~/ home dirs), project (current repo), or both.",
337
+ )
338
+ @click.option(
339
+ "--directory",
340
+ "-d",
341
+ type=click.Path(file_okay=False, path_type=Path),
342
+ default=None,
343
+ help="Check skills directly in this directory.",
344
+ )
345
+ def check_skills(
346
+ skill: Optional[str],
347
+ client: Optional[str],
348
+ scope: Optional[str],
349
+ directory: Optional[Path],
350
+ ) -> None:
351
+ """Check whether installed skills match the bundled version."""
352
+ requested_skills = None if skill in (None, "all") else [skill.lower()]
353
+
354
+ try:
355
+ selected_skill_names = _expand_skill_selection(requested_skills)
356
+ destinations = _requested_destinations(directory, client, scope)
357
+ except ValueError as exc:
358
+ click.echo(f"✗ {exc}", err=True)
359
+ sys.exit(ExitCodes.INVALID_INPUT)
360
+
361
+ needs_update = False
362
+ click.echo(f"Bundled skill version: {__version__}")
363
+ for destination in destinations:
364
+ for skill_name in selected_skill_names:
365
+ status = _skill_installation_status(destination, skill_name)
366
+ indicator = "✓" if status == "current" else "✗"
367
+ click.echo(f"{indicator} {destination / skill_name}: {status}")
368
+ needs_update = needs_update or status != "current"
369
+
370
+ if needs_update:
371
+ sys.exit(ExitCodes.GENERAL_ERROR)
@@ -47,17 +47,33 @@ Load only what the current task needs.
47
47
 
48
48
  ## Default approach
49
49
 
50
- 1. Prefer long-form flags in generated commands.
51
- 2. Use `--format json` when the result will be filtered, transformed, or piped
50
+ 1. At the start of troubleshooting or a substantial `slcli` workflow, run the
51
+ update preflight once per session:
52
+
53
+ ```bash
54
+ slcli version check
55
+ slcli skill check --client CLIENT --scope SCOPE
56
+ ```
57
+
58
+ Set `CLIENT` and `SCOPE` to the active skill installation, or use
59
+ `--directory PATH` for a custom skills directory. If the CLI is outdated,
60
+ recommend the update command printed by `version check`. If the skill is
61
+ missing, unversioned, or outdated, recommend `slcli skill install` with the
62
+ same target and `--force`. Ask before running either update because they
63
+ modify the user's environment. A failed or unavailable check is advisory:
64
+ report it briefly and continue the requested workflow unless version skew
65
+ is the likely cause.
66
+ 2. Prefer long-form flags in generated commands.
67
+ 3. Use `--format json` when the result will be filtered, transformed, or piped
52
68
  into other tools.
53
- 3. Use `--summary --group-by` for aggregation before fetching large raw result sets.
54
- 4. Use convenience filters first, then fall back to `--filter` with `--substitution` for complex queries.
55
- 5. Stay scoped to the user’s requested resource or workflow.
56
- 6. Load deeper references only when the command surface alone is not enough.
57
- 7. For example authoring or review, load `example-authoring.md` before proposing YAML.
58
- 8. Prefer workspace IDs over names in scripted workflows when an endpoint is strict about identity.
59
- 9. Use `make_api_request` from `slcli.utils` for helper scripts so auth, SSL, and error handling stay consistent.
60
- 10. For datasheet imports, default to autonomy when the product or workspace can be resolved unambiguously.
69
+ 4. Use `--summary --group-by` for aggregation before fetching large raw result sets.
70
+ 5. Use convenience filters first, then fall back to `--filter` with `--substitution` for complex queries.
71
+ 6. Stay scoped to the user’s requested resource or workflow.
72
+ 7. Load deeper references only when the command surface alone is not enough.
73
+ 8. For example authoring or review, load `example-authoring.md` before proposing YAML.
74
+ 9. Prefer workspace IDs over names in scripted workflows when an endpoint is strict about identity.
75
+ 10. Use `make_api_request` from `slcli.utils` for helper scripts so auth, SSL, and error handling stay consistent.
76
+ 11. For datasheet imports, default to autonomy when the product or workspace can be resolved unambiguously.
61
77
 
62
78
  ## Common command groups
63
79
 
@@ -82,7 +98,8 @@ Load only what the current task needs.
82
98
  | `user` | User management | `list`, `get`, `create`, `update` |
83
99
  | `auth` | Authorization policies | `policy list/create`, `template list` |
84
100
  | `workspace` | Workspaces | `list`, `get` |
85
- | `skill` | AI skill installation | `install` |
101
+ | `version` | CLI update hygiene | `check` |
102
+ | `skill` | AI skill installation | `install`, `check` |
86
103
  | `example` | Demo provisioning | `list`, `install`, `delete` |
87
104
 
88
105
  Example package fixtures are declared as `package` resources after a `feed`
@@ -26,6 +26,7 @@ Complete option reference for all `slcli` command groups.
26
26
  - [workitem — Work item, template, and workflow management](#workitem--work-item-template-and-workflow-management)
27
27
  - [workflow — Workflow management](#workflow--workflow-management)
28
28
  - [webapp — Web application management](#webapp--web-application-management)
29
+ - [version — CLI update check](#version--cli-update-check)
29
30
  - [skill — AI skill installation](#skill--ai-skill-installation)
30
31
  - [example — Built-in example resource provisioning](#example--built-in-example-resource-provisioning)
31
32
  - [Scope and capability discovery](#scope-and-capability-discovery)
@@ -946,12 +947,32 @@ metadata, carries the icon into the package, writes the matching control-file fi
946
947
  generated `.nipkg`, and emits a thin `manifest.json` with `schemaVersion`, `nipkgFile`,
947
948
  `sha256`, and any configured provenance fields.
948
949
 
950
+ ## version — CLI update check
951
+
952
+ Compare the installed CLI with the latest PyPI release and identify the update command for
953
+ its installation method.
954
+
955
+ ```bash
956
+ slcli version # Show the installed version without a network request
957
+ slcli version check # Check PyPI and show update guidance
958
+ slcli version check --format json # Structured output for automation
959
+ slcli version check --fail-if-outdated
960
+ ```
961
+
962
+ `version check` detects Homebrew, Scoop, pipx, uv, pip, standalone binaries, and development
963
+ checkouts. By default, finding an update still exits successfully; use `--fail-if-outdated`
964
+ when an outdated installation should fail automation. A lookup failure exits with the network
965
+ error code and must not be interpreted as confirmation that the CLI is current.
966
+
949
967
  ## skill — AI skill installation
950
968
 
951
- Install bundled skills for supported AI clients.
969
+ Install bundled skills for supported AI clients, or directly into a custom skills directory.
952
970
 
953
971
  ```bash
954
972
  slcli skill install --skill [slcli|all] --client [agents|claude|all] --scope [personal|project|both]
973
+ slcli skill install --skill [slcli|all] --directory PATH
974
+ slcli skill check --skill [slcli|all] --client [agents|claude|all] --scope [personal|project|both]
975
+ slcli skill check --skill [slcli|all] --directory PATH
955
976
  ```
956
977
 
957
978
  Client paths:
@@ -963,6 +984,22 @@ Client paths:
963
984
  Notes:
964
985
 
965
986
  - `agents` is the default client in interactive mode.
987
+ - `--directory` is the directory that directly contains skill folders and cannot be combined
988
+ with `--client` or `--scope`.
989
+ - Installed skills carry the `slcli` release version. `skill check` exits nonzero if a skill is
990
+ missing, predates the bundled version, or was installed before version metadata was added.
991
+ - Use `skill install --force` to update an existing installation.
992
+ - For hygiene checks, specify the client and scope that contain the active skill, then use the
993
+ same target to update it:
994
+
995
+ ```bash
996
+ slcli skill check --client agents --scope project
997
+ slcli skill install --client agents --scope project --force
998
+ ```
999
+
1000
+ Substitute `claude`, `personal`, or `--directory PATH` when that is where the active skill is
1001
+ installed. Checking the wrong target can report a current skill as missing.
1002
+
966
1003
  - The bundled `slcli` skill now covers the previous standalone workflow skills.
967
1004
 
968
1005
  ## example — Built-in example resource provisioning
@@ -2,6 +2,28 @@
2
2
 
3
3
  Common issues and workarounds when using `slcli` or scripting against SystemLink APIs.
4
4
 
5
+ ## Update preflight
6
+
7
+ Before deeper troubleshooting, check the CLI and the active installed skill once per session:
8
+
9
+ ```bash
10
+ slcli version check
11
+ slcli skill check --client <CLIENT> --scope <SCOPE>
12
+ ```
13
+
14
+ Use `--directory <PATH>` instead of `--client` and `--scope` for a custom skill location. If
15
+ the CLI is outdated, recommend the manager-specific command printed by `version check`. If the
16
+ skill check reports `missing`, `unversioned`, or `outdated`, recommend updating the same target:
17
+
18
+ ```bash
19
+ slcli skill install --client <CLIENT> --scope <SCOPE> --force
20
+ ```
21
+
22
+ Obtain the user's approval before running an update. If either check fails because the command
23
+ is unavailable or the network cannot be reached, note that version status is unknown and
24
+ continue troubleshooting. Give version skew more weight when the failure involves unknown
25
+ commands, changed options, output shape, packaging, or skill instructions.
26
+
5
27
  ## Effective profile and workspace scope
6
28
 
7
29
  Resolve the workspace and profile independently before querying resources. A
@@ -0,0 +1,199 @@
1
+ """Version checking and update guidance for slcli."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ from dataclasses import asdict, dataclass
7
+ from enum import Enum
8
+ from importlib.metadata import PackageNotFoundError, distribution
9
+ from pathlib import Path
10
+ from typing import Any, Optional
11
+
12
+ import click
13
+ import requests
14
+ from packaging.version import InvalidVersion, Version
15
+
16
+ from ._version import __version__
17
+ from .rich_output import print_json
18
+ from .utils import ExitCodes
19
+
20
+ PYPI_URL = "https://pypi.org/pypi/systemlink-cli/json"
21
+ RELEASES_URL = "https://github.com/ni-kismet/systemlink-cli/releases/latest"
22
+
23
+
24
+ class InstallMethod(str, Enum):
25
+ """Supported slcli installation methods."""
26
+
27
+ HOMEBREW = "homebrew"
28
+ SCOOP = "scoop"
29
+ PIPX = "pipx"
30
+ UV = "uv"
31
+ PIP = "pip"
32
+ STANDALONE = "standalone"
33
+ DEVELOPMENT = "development"
34
+ UNKNOWN = "unknown"
35
+
36
+
37
+ @dataclass(frozen=True)
38
+ class VersionCheckResult:
39
+ """Result returned by a version check.
40
+
41
+ Attributes:
42
+ current_version: Installed slcli version.
43
+ latest_version: Latest version published to PyPI.
44
+ status: One of ``outdated``, ``current``, or ``ahead``.
45
+ install_method: Detected installation method.
46
+ update_command: Suggested update command, when available.
47
+ """
48
+
49
+ current_version: str
50
+ latest_version: str
51
+ status: str
52
+ install_method: str
53
+ update_command: Optional[str]
54
+
55
+
56
+ def fetch_latest_version() -> str:
57
+ """Return the latest published systemlink-cli version from PyPI."""
58
+ response = requests.get(PYPI_URL, timeout=5)
59
+ response.raise_for_status()
60
+ payload: Any = response.json()
61
+ try:
62
+ version = payload["info"]["version"]
63
+ except (KeyError, TypeError) as exc:
64
+ raise ValueError("PyPI returned an unexpected response") from exc
65
+ if not isinstance(version, str) or not version:
66
+ raise ValueError("PyPI returned an invalid version")
67
+ try:
68
+ Version(version)
69
+ except InvalidVersion as exc:
70
+ raise ValueError(f"PyPI returned an invalid version: {version}") from exc
71
+ return version
72
+
73
+
74
+ def _path_contains(path: Path, *parts: str) -> bool:
75
+ """Return whether a path contains the given consecutive components."""
76
+ lowered_parts = tuple(part.lower() for part in path.parts)
77
+ expected = tuple(part.lower() for part in parts)
78
+ width = len(expected)
79
+ return any(lowered_parts[index : index + width] == expected for index in range(len(path.parts)))
80
+
81
+
82
+ def detect_install_method() -> InstallMethod:
83
+ """Detect how the running slcli installation is managed."""
84
+ executable = Path(sys.executable).resolve()
85
+
86
+ if getattr(sys, "frozen", False):
87
+ if _path_contains(executable, "cellar", "slcli"):
88
+ return InstallMethod.HOMEBREW
89
+ if _path_contains(executable, "scoop", "apps", "slcli"):
90
+ return InstallMethod.SCOOP
91
+ return InstallMethod.STANDALONE
92
+
93
+ if _path_contains(executable, "pipx", "venvs", "systemlink-cli"):
94
+ return InstallMethod.PIPX
95
+ if _path_contains(executable, "uv", "tools", "systemlink-cli"):
96
+ return InstallMethod.UV
97
+
98
+ project_root = Path(__file__).resolve().parent.parent
99
+ if (project_root / "pyproject.toml").is_file() and (project_root / ".git").exists():
100
+ return InstallMethod.DEVELOPMENT
101
+
102
+ try:
103
+ distribution("systemlink-cli")
104
+ except PackageNotFoundError:
105
+ return InstallMethod.UNKNOWN
106
+ return InstallMethod.PIP
107
+
108
+
109
+ def get_update_command(install_method: InstallMethod) -> Optional[str]:
110
+ """Return the update command for an installation method, when available."""
111
+ commands = {
112
+ InstallMethod.HOMEBREW: "brew upgrade slcli",
113
+ InstallMethod.SCOOP: "scoop update slcli",
114
+ InstallMethod.PIPX: "pipx upgrade systemlink-cli",
115
+ InstallMethod.UV: "uv tool upgrade systemlink-cli",
116
+ InstallMethod.PIP: f'"{sys.executable}" -m pip install --upgrade systemlink-cli',
117
+ InstallMethod.DEVELOPMENT: "git pull && poetry install",
118
+ }
119
+ return commands.get(install_method)
120
+
121
+
122
+ def check_version(current_version: str = __version__) -> VersionCheckResult:
123
+ """Compare the current slcli version with the latest published version."""
124
+ latest_version = fetch_latest_version()
125
+ current = Version(current_version)
126
+ latest = Version(latest_version)
127
+ status = "outdated" if current < latest else "current" if current == latest else "ahead"
128
+ install_method = detect_install_method()
129
+ return VersionCheckResult(
130
+ current_version=current_version,
131
+ latest_version=latest_version,
132
+ status=status,
133
+ install_method=install_method.value,
134
+ update_command=get_update_command(install_method),
135
+ )
136
+
137
+
138
+ def _render_version_check(result: VersionCheckResult) -> None:
139
+ """Render a human-readable version check result."""
140
+ click.echo(f"Current version: {result.current_version}")
141
+ click.echo(f"Latest version: {result.latest_version}")
142
+ click.echo(f"Installed with: {result.install_method}")
143
+ click.echo()
144
+
145
+ if result.status == "current":
146
+ click.echo("✓ slcli is up to date")
147
+ elif result.status == "ahead":
148
+ click.echo("Current version is newer than the latest published version")
149
+ elif result.update_command:
150
+ click.echo("A newer version is available:")
151
+ click.echo(f" {result.update_command}")
152
+ else:
153
+ click.echo("A newer version is available.")
154
+ click.echo(f"Download it from {RELEASES_URL}")
155
+
156
+
157
+ def register_version_commands(cli: Any) -> None:
158
+ """Register version commands with the CLI."""
159
+
160
+ @cli.group(invoke_without_command=True)
161
+ @click.pass_context
162
+ def version(ctx: click.Context) -> None:
163
+ """Show the installed version and check for updates."""
164
+ if ctx.invoked_subcommand is None:
165
+ click.echo(f"slcli version {__version__}")
166
+
167
+ @version.command(name="check")
168
+ @click.option(
169
+ "--format",
170
+ "-f",
171
+ "format_output",
172
+ type=click.Choice(["text", "json"]),
173
+ default="text",
174
+ show_default=True,
175
+ help="Output format",
176
+ )
177
+ @click.option(
178
+ "--fail-if-outdated",
179
+ is_flag=True,
180
+ help="Exit with code 1 when a newer version is available",
181
+ )
182
+ def check(format_output: str, fail_if_outdated: bool) -> None:
183
+ """Check whether this is the latest slcli version."""
184
+ try:
185
+ result = check_version()
186
+ except InvalidVersion as exc:
187
+ click.echo(f"✗ Invalid version: {exc}", err=True)
188
+ sys.exit(ExitCodes.INVALID_INPUT)
189
+ except (requests.RequestException, ValueError) as exc:
190
+ click.echo(f"✗ Unable to retrieve the latest slcli version: {exc}", err=True)
191
+ sys.exit(ExitCodes.NETWORK_ERROR)
192
+
193
+ if format_output == "json":
194
+ print_json(asdict(result))
195
+ else:
196
+ _render_version_check(result)
197
+
198
+ if fail_if_outdated and result.status == "outdated":
199
+ sys.exit(ExitCodes.GENERAL_ERROR)
@@ -20,6 +20,7 @@ import questionary
20
20
  import requests
21
21
 
22
22
  from .cli_utils import is_interactive_environment, validate_output_format
23
+ from .platform import PLATFORM_SLS, get_platform
23
24
  from .skill_click import install_skills_to_directory
24
25
  from .universal_handlers import UniversalResponseHandler
25
26
  from .utils import (
@@ -96,8 +97,9 @@ def _build_published_webapp_url(
96
97
  ) -> str:
97
98
  """Return the best available published URL for a webapp.
98
99
 
99
- Prefers the friendly SystemLink web UI URL and falls back to any explicit
100
- URL-like property returned by the service, then the raw content endpoint.
100
+ Uses the WebVI host route for SystemLink Server and otherwise prefers the
101
+ friendly SystemLink web UI URL. Falls back to any explicit URL-like property
102
+ returned by the service, then the raw content endpoint.
101
103
  """
102
104
  from urllib.parse import quote
103
105
 
@@ -133,6 +135,10 @@ def _build_published_webapp_url(
133
135
  if mapped_workspace_name:
134
136
  workspace_name = mapped_workspace_name
135
137
 
138
+ if get_platform() == PLATFORM_SLS and webapp_id:
139
+ web_base = get_web_url().rstrip("/")
140
+ return f"{web_base}/#/webvihost/view/webvi/{quote(webapp_id, safe='')}"
141
+
136
142
  if resolved_name and workspace_name:
137
143
  web_base = get_web_url().rstrip("/")
138
144
  return f"{web_base}/webapps/app/{quote(workspace_name)}/{quote(resolved_name)}"
File without changes