systemlink-cli 1.9.2__tar.gz → 1.9.4__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.
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/PKG-INFO +1 -1
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/pyproject.toml +38 -31
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/_version.py +1 -1
- systemlink_cli-1.9.4/slcli/mcp_reachability.py +53 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/skill_click.py +51 -21
- systemlink_cli-1.9.4/slcli/skills/nipkg-file-package/SKILL.md +541 -0
- systemlink_cli-1.9.4/slcli/skills/systemlink-job-debugging/SKILL.md +325 -0
- systemlink_cli-1.9.4/slcli/skills/systemlink-python-test/SKILL.md +547 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/utils.py +4 -1
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/webapp_click.py +107 -38
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/LICENSE +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/dff-editor/editor.js +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/dff-editor/index.html +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/__init__.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/__main__.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/asset_click.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/cli_formatters.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/cli_utils.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/comment_click.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/completion_click.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/config.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/config_click.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/dff_click.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/dff_decorators.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/example_click.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/example_loader.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/example_provisioner.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/examples/README.md +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/examples/_schema/schema-v1.0.json +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/examples/demo-complete-workflow/README.md +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/examples/demo-complete-workflow/config.yaml +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/examples/demo-test-plans/README.md +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/examples/demo-test-plans/config.yaml +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/examples/exercise-5-1-parametric-insights/README.md +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/examples/exercise-5-1-parametric-insights/config.yaml +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/examples/exercise-7-1-test-plans/README.md +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/examples/exercise-7-1-test-plans/config.yaml +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/examples/spec-compliance-notebooks/README.md +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/examples/spec-compliance-notebooks/config.yaml +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/examples/spec-compliance-notebooks/notebooks/SpecAnalysis_ComplianceCalculation.ipynb +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/examples/spec-compliance-notebooks/notebooks/SpecComplianceCalculation.ipynb +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/examples/spec-compliance-notebooks/notebooks/SpecfileExtractionAndIngestion.ipynb +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/examples/spec-compliance-notebooks/spec_template.xlsx +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/feed_click.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/file_click.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/function_click.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/function_templates.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/main.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/mcp_click.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/mcp_server.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/notebook_click.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/platform.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/policy_click.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/policy_utils.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/profiles.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/response_handlers.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/rich_output.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/routine_click.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/skills/slcli/SKILL.md +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/skills/slcli/references/analysis-recipes.md +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/skills/slcli/references/filtering.md +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/skills/systemlink-notebook/SKILL.md +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/skills/systemlink-notebook/references/interfaces.md +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/skills/systemlink-notebook/references/notebook-patterns.md +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/skills/systemlink-webapp/SKILL.md +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/skills/systemlink-webapp/references/deployment.md +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/skills/systemlink-webapp/references/layout-patterns.md +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/skills/systemlink-webapp/references/nimble-angular.md +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/skills/systemlink-webapp/references/systemlink-services.md +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/ssl_trust.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/system_click.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/table_utils.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/tag_click.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/templates_click.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/testmonitor_click.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/universal_handlers.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/user_click.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/web_editor.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/workflow_preview.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/workflows_click.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/workitem_click.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/workspace_click.py +0 -0
- {systemlink_cli-1.9.2 → systemlink_cli-1.9.4}/slcli/workspace_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "systemlink-cli"
|
|
3
|
-
version = "1.9.
|
|
3
|
+
version = "1.9.4"
|
|
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" }]
|
|
@@ -16,6 +16,7 @@ slcli = "slcli.__main__:cli"
|
|
|
16
16
|
slcli-mcp = "slcli.mcp_server:main"
|
|
17
17
|
build-pyinstaller = "scripts.build_pyinstaller:main"
|
|
18
18
|
update-version = "scripts.update_version:main"
|
|
19
|
+
release-from-changes = "scripts.towncrier_release:main"
|
|
19
20
|
lint = "scripts.lint:main"
|
|
20
21
|
|
|
21
22
|
[tool.poetry.group.mcp]
|
|
@@ -59,8 +60,8 @@ pyinstaller = "^6.14.2"
|
|
|
59
60
|
types-requests = "*"
|
|
60
61
|
types-tabulate = "*"
|
|
61
62
|
|
|
62
|
-
#
|
|
63
|
-
|
|
63
|
+
# Changelog and release management
|
|
64
|
+
towncrier = "^25.8.0"
|
|
64
65
|
pytest-xdist = "^3.8.0"
|
|
65
66
|
|
|
66
67
|
# Excel file generation
|
|
@@ -114,34 +115,40 @@ check_untyped_defs = true
|
|
|
114
115
|
warn_redundant_casts = true
|
|
115
116
|
warn_unreachable = true
|
|
116
117
|
|
|
117
|
-
[tool.
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
118
|
+
[tool.towncrier]
|
|
119
|
+
package = "slcli"
|
|
120
|
+
package_dir = "."
|
|
121
|
+
directory = "newsfragments"
|
|
122
|
+
filename = "CHANGELOG.md"
|
|
123
|
+
template = "towncrier:default.md"
|
|
124
|
+
start_string = "<!-- towncrier release notes start -->\n"
|
|
125
|
+
title_format = "## v{version} ({project_date})"
|
|
126
|
+
ignore = ["README.md"]
|
|
127
|
+
|
|
128
|
+
[[tool.towncrier.type]]
|
|
129
|
+
directory = "major"
|
|
130
|
+
name = "Breaking Changes"
|
|
131
|
+
showcontent = true
|
|
132
|
+
|
|
133
|
+
[[tool.towncrier.type]]
|
|
134
|
+
directory = "minor"
|
|
135
|
+
name = "Features"
|
|
136
|
+
showcontent = true
|
|
137
|
+
|
|
138
|
+
[[tool.towncrier.type]]
|
|
139
|
+
directory = "patch"
|
|
140
|
+
name = "Bug Fixes"
|
|
141
|
+
showcontent = true
|
|
142
|
+
|
|
143
|
+
[[tool.towncrier.type]]
|
|
144
|
+
directory = "doc"
|
|
145
|
+
name = "Documentation"
|
|
146
|
+
showcontent = true
|
|
147
|
+
|
|
148
|
+
[[tool.towncrier.type]]
|
|
149
|
+
directory = "misc"
|
|
150
|
+
name = "Other Changes"
|
|
151
|
+
showcontent = true
|
|
145
152
|
|
|
146
153
|
[build-system]
|
|
147
154
|
requires = ["poetry-core>=1.0.0"]
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Helpers for classifying local MCP client connectivity failures."""
|
|
2
|
+
|
|
3
|
+
from typing import Iterator, List
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def iter_exceptions(exc: BaseException) -> Iterator[BaseException]:
|
|
7
|
+
"""Yield an exception plus any nested causes, contexts, or exception-group members."""
|
|
8
|
+
stack: List[BaseException] = [exc]
|
|
9
|
+
seen: set[int] = set()
|
|
10
|
+
|
|
11
|
+
while stack:
|
|
12
|
+
current = stack.pop()
|
|
13
|
+
marker = id(current)
|
|
14
|
+
if marker in seen:
|
|
15
|
+
continue
|
|
16
|
+
seen.add(marker)
|
|
17
|
+
yield current
|
|
18
|
+
|
|
19
|
+
cause = getattr(current, "__cause__", None)
|
|
20
|
+
if isinstance(cause, BaseException):
|
|
21
|
+
stack.append(cause)
|
|
22
|
+
|
|
23
|
+
context = getattr(current, "__context__", None)
|
|
24
|
+
if isinstance(context, BaseException):
|
|
25
|
+
stack.append(context)
|
|
26
|
+
|
|
27
|
+
if isinstance(current, BaseExceptionGroup):
|
|
28
|
+
stack.extend(current.exceptions)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def is_reachability_failure(exc: BaseException) -> bool:
|
|
32
|
+
"""Return True when an exception indicates the local MCP server is unreachable."""
|
|
33
|
+
for candidate in iter_exceptions(exc):
|
|
34
|
+
if isinstance(candidate, (OSError, TimeoutError)):
|
|
35
|
+
return True
|
|
36
|
+
|
|
37
|
+
message = str(candidate).lower()
|
|
38
|
+
if any(
|
|
39
|
+
token in message
|
|
40
|
+
for token in (
|
|
41
|
+
"connection refused",
|
|
42
|
+
"connect error",
|
|
43
|
+
"all connection attempts failed",
|
|
44
|
+
"timed out",
|
|
45
|
+
"timeout",
|
|
46
|
+
"name or service not known",
|
|
47
|
+
"nodename nor servname provided",
|
|
48
|
+
"server disconnected",
|
|
49
|
+
)
|
|
50
|
+
):
|
|
51
|
+
return True
|
|
52
|
+
|
|
53
|
+
return False
|
|
@@ -11,7 +11,14 @@ import questionary
|
|
|
11
11
|
from .utils import ExitCodes
|
|
12
12
|
|
|
13
13
|
SKILL_NAME = "slcli"
|
|
14
|
-
|
|
14
|
+
_FALLBACK_SKILL_CHOICES = [
|
|
15
|
+
"nipkg-file-package",
|
|
16
|
+
"slcli",
|
|
17
|
+
"systemlink-job-debugging",
|
|
18
|
+
"systemlink-notebook",
|
|
19
|
+
"systemlink-python-test",
|
|
20
|
+
"systemlink-webapp",
|
|
21
|
+
]
|
|
15
22
|
|
|
16
23
|
# Mapping of client name -> (personal skills dir, project subdir relative to repo root)
|
|
17
24
|
# personal dir uses Path.home() so it's always resolved at call time via _personal_dir().
|
|
@@ -23,6 +30,45 @@ _CLIENT_TABLE: Dict[str, Tuple[str, str]] = {
|
|
|
23
30
|
CLIENT_CHOICES = list(_CLIENT_TABLE.keys())
|
|
24
31
|
|
|
25
32
|
|
|
33
|
+
def _skills_dir_candidates() -> List[Path]:
|
|
34
|
+
"""Return candidate bundled skills directories for source and frozen layouts."""
|
|
35
|
+
candidates: List[Path] = []
|
|
36
|
+
|
|
37
|
+
meipass = getattr(sys, "_MEIPASS", None)
|
|
38
|
+
if meipass:
|
|
39
|
+
candidates.append(Path(meipass) / "skills")
|
|
40
|
+
|
|
41
|
+
if getattr(sys, "frozen", False):
|
|
42
|
+
candidates.append(Path(sys.executable).resolve().parent / "skills")
|
|
43
|
+
|
|
44
|
+
candidates.append(Path(__file__).resolve().parent / "skills")
|
|
45
|
+
return candidates
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _skill_names_in_directory(directory: Path) -> List[str]:
|
|
49
|
+
"""Return sorted bundled skill names for a skills directory."""
|
|
50
|
+
if not directory.exists():
|
|
51
|
+
return []
|
|
52
|
+
|
|
53
|
+
return sorted(
|
|
54
|
+
child.name
|
|
55
|
+
for child in directory.iterdir()
|
|
56
|
+
if child.is_dir() and (child / "SKILL.md").exists()
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _discover_skill_choices() -> List[str]:
|
|
61
|
+
"""Discover bundled skills from the current installation layout."""
|
|
62
|
+
for candidate in _skills_dir_candidates():
|
|
63
|
+
names = _skill_names_in_directory(candidate)
|
|
64
|
+
if names:
|
|
65
|
+
return names
|
|
66
|
+
return list(_FALLBACK_SKILL_CHOICES)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
SKILL_CHOICES = _discover_skill_choices()
|
|
70
|
+
|
|
71
|
+
|
|
26
72
|
def _personal_dir(client: str) -> Path:
|
|
27
73
|
"""Return the resolved personal skills directory for a client."""
|
|
28
74
|
return Path(_CLIENT_TABLE[client][0]).expanduser()
|
|
@@ -57,24 +103,8 @@ def _find_bundled_skills_dir() -> Path:
|
|
|
57
103
|
Raises:
|
|
58
104
|
FileNotFoundError: If the skills directory cannot be found.
|
|
59
105
|
"""
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
# PyInstaller onefile mode
|
|
63
|
-
meipass = getattr(sys, "_MEIPASS", None)
|
|
64
|
-
if meipass:
|
|
65
|
-
candidates.append(Path(meipass) / "skills")
|
|
66
|
-
|
|
67
|
-
# PyInstaller onedir / frozen executable
|
|
68
|
-
if getattr(sys, "frozen", False):
|
|
69
|
-
candidates.append(Path(sys.executable).resolve().parent / "skills")
|
|
70
|
-
|
|
71
|
-
# pip install / development: skills/ bundled inside the slcli package
|
|
72
|
-
candidates.append(Path(__file__).resolve().parent / "skills")
|
|
73
|
-
|
|
74
|
-
for candidate in candidates:
|
|
75
|
-
if candidate.exists() and any(
|
|
76
|
-
(candidate / name / "SKILL.md").exists() for name in SKILL_CHOICES
|
|
77
|
-
):
|
|
106
|
+
for candidate in _skills_dir_candidates():
|
|
107
|
+
if _skill_names_in_directory(candidate):
|
|
78
108
|
return candidate
|
|
79
109
|
|
|
80
110
|
raise FileNotFoundError(
|
|
@@ -178,7 +208,7 @@ def register_skill_commands(cli: Any) -> None:
|
|
|
178
208
|
"-k",
|
|
179
209
|
type=click.Choice(SKILL_CHOICES + ["all"], case_sensitive=False),
|
|
180
210
|
default=None,
|
|
181
|
-
help="Skill to install (
|
|
211
|
+
help=f"Skill to install ({', '.join(SKILL_CHOICES)}, or all).",
|
|
182
212
|
)
|
|
183
213
|
@click.option(
|
|
184
214
|
"--client",
|
|
@@ -207,7 +237,7 @@ def register_skill_commands(cli: Any) -> None:
|
|
|
207
237
|
"""Install agent skills for AI coding assistants.
|
|
208
238
|
|
|
209
239
|
Copies bundled skills into the skills directory of one or more AI clients.
|
|
210
|
-
Available skills
|
|
240
|
+
Available skills are shown in `--help` and prompted interactively.
|
|
211
241
|
Supported clients and their skill locations:
|
|
212
242
|
|
|
213
243
|
\b
|