systemlink-cli 1.9.4__tar.gz → 1.11.0__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 (88) hide show
  1. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/PKG-INFO +1 -1
  2. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/pyproject.toml +1 -1
  3. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/_version.py +1 -1
  4. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/main.py +7 -0
  5. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/mcp_reachability.py +11 -0
  6. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/mcp_server.py +44 -5
  7. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/platform.py +104 -5
  8. systemlink_cli-1.11.0/slcli/skills/slcli/SKILL.md +180 -0
  9. systemlink_cli-1.9.4/slcli/skills/slcli/SKILL.md → systemlink_cli-1.11.0/slcli/skills/slcli/references/commands.md +54 -125
  10. systemlink_cli-1.11.0/slcli/skills/slcli/references/datasheet-workflow.md +510 -0
  11. systemlink_cli-1.11.0/slcli/skills/slcli/references/troubleshooting.md +45 -0
  12. systemlink_cli-1.11.0/slcli/spec_click.py +2141 -0
  13. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/system_click.py +424 -65
  14. systemlink_cli-1.11.0/slcli/system_query_utils.py +161 -0
  15. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/LICENSE +0 -0
  16. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/dff-editor/editor.js +0 -0
  17. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/dff-editor/index.html +0 -0
  18. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/__init__.py +0 -0
  19. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/__main__.py +0 -0
  20. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/asset_click.py +0 -0
  21. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/cli_formatters.py +0 -0
  22. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/cli_utils.py +0 -0
  23. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/comment_click.py +0 -0
  24. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/completion_click.py +0 -0
  25. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/config.py +0 -0
  26. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/config_click.py +0 -0
  27. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/dff_click.py +0 -0
  28. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/dff_decorators.py +0 -0
  29. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/example_click.py +0 -0
  30. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/example_loader.py +0 -0
  31. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/example_provisioner.py +0 -0
  32. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/README.md +0 -0
  33. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/_schema/schema-v1.0.json +0 -0
  34. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/demo-complete-workflow/README.md +0 -0
  35. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/demo-complete-workflow/config.yaml +0 -0
  36. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/demo-test-plans/README.md +0 -0
  37. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/demo-test-plans/config.yaml +0 -0
  38. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/exercise-5-1-parametric-insights/README.md +0 -0
  39. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/exercise-5-1-parametric-insights/config.yaml +0 -0
  40. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/exercise-7-1-test-plans/README.md +0 -0
  41. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/exercise-7-1-test-plans/config.yaml +0 -0
  42. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/spec-compliance-notebooks/README.md +0 -0
  43. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/spec-compliance-notebooks/config.yaml +0 -0
  44. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/spec-compliance-notebooks/notebooks/SpecAnalysis_ComplianceCalculation.ipynb +0 -0
  45. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/spec-compliance-notebooks/notebooks/SpecComplianceCalculation.ipynb +0 -0
  46. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/spec-compliance-notebooks/notebooks/SpecfileExtractionAndIngestion.ipynb +0 -0
  47. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/spec-compliance-notebooks/spec_template.xlsx +0 -0
  48. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/feed_click.py +0 -0
  49. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/file_click.py +0 -0
  50. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/function_click.py +0 -0
  51. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/function_templates.py +0 -0
  52. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/mcp_click.py +0 -0
  53. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/notebook_click.py +0 -0
  54. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/policy_click.py +0 -0
  55. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/policy_utils.py +0 -0
  56. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/profiles.py +0 -0
  57. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/response_handlers.py +0 -0
  58. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/rich_output.py +0 -0
  59. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/routine_click.py +0 -0
  60. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skill_click.py +0 -0
  61. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/nipkg-file-package/SKILL.md +0 -0
  62. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/slcli/references/analysis-recipes.md +0 -0
  63. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/slcli/references/filtering.md +0 -0
  64. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/systemlink-job-debugging/SKILL.md +0 -0
  65. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/systemlink-notebook/SKILL.md +0 -0
  66. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/systemlink-notebook/references/interfaces.md +0 -0
  67. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/systemlink-notebook/references/notebook-patterns.md +0 -0
  68. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/systemlink-python-test/SKILL.md +0 -0
  69. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/systemlink-webapp/SKILL.md +0 -0
  70. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/systemlink-webapp/references/deployment.md +0 -0
  71. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/systemlink-webapp/references/layout-patterns.md +0 -0
  72. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/systemlink-webapp/references/nimble-angular.md +0 -0
  73. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/systemlink-webapp/references/systemlink-services.md +0 -0
  74. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/ssl_trust.py +0 -0
  75. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/table_utils.py +0 -0
  76. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/tag_click.py +0 -0
  77. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/templates_click.py +0 -0
  78. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/testmonitor_click.py +0 -0
  79. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/universal_handlers.py +0 -0
  80. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/user_click.py +0 -0
  81. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/utils.py +0 -0
  82. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/web_editor.py +0 -0
  83. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/webapp_click.py +0 -0
  84. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/workflow_preview.py +0 -0
  85. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/workflows_click.py +0 -0
  86. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/workitem_click.py +0 -0
  87. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/workspace_click.py +0 -0
  88. {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/workspace_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: systemlink-cli
3
- Version: 1.9.4
3
+ Version: 1.11.0
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.9.4"
3
+ version = "1.11.0"
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.9.4"
4
+ __version__ = "1.11.0"
@@ -30,6 +30,7 @@ from .rich_output import install_rich_output
30
30
  from .rich_output import render_table
31
31
  from .routine_click import register_routine_commands
32
32
  from .skill_click import register_skill_commands
33
+ from .spec_click import register_spec_commands
33
34
  from .ssl_trust import OS_TRUST_INJECTED, OS_TRUST_REASON
34
35
  from .system_click import register_system_commands
35
36
  from .tag_click import register_tag_commands
@@ -365,6 +366,11 @@ def info(format: str, skip_health: bool) -> None:
365
366
  workspace_display = truncate(workspace)
366
367
  info_rows.append(["Workspace", workspace_display])
367
368
 
369
+ system_query_endpoint = platform_info.get("system_query_endpoint")
370
+ if system_query_endpoint:
371
+ system_query_display = truncate(str(system_query_endpoint))
372
+ info_rows.append(["System Query", system_query_display])
373
+
368
374
  file_query_endpoint = platform_info.get("file_query_endpoint")
369
375
  if file_query_endpoint:
370
376
  if (
@@ -427,6 +433,7 @@ register_notebook_commands(cli)
427
433
  register_policy_commands(cli)
428
434
  register_routine_commands(cli)
429
435
  register_system_commands(cli)
436
+ register_spec_commands(cli)
430
437
  register_tag_commands(cli)
431
438
  register_testmonitor_commands(cli)
432
439
  register_webapp_commands(cli)
@@ -35,6 +35,17 @@ def is_reachability_failure(exc: BaseException) -> bool:
35
35
  return True
36
36
 
37
37
  message = str(candidate).lower()
38
+ if "/mcp" in message and any(
39
+ token in message
40
+ for token in (
41
+ "server error",
42
+ "not found",
43
+ "method not allowed",
44
+ "bad request",
45
+ )
46
+ ):
47
+ return True
48
+
38
49
  if any(
39
50
  token in message
40
51
  for token in (
@@ -271,7 +271,41 @@ def query_systems(
271
271
  take: int = 100,
272
272
  ) -> str:
273
273
  """Query systems by alias, connection state, workspace, or raw filter."""
274
- from .utils import get_base_url
274
+ import requests as requests_lib
275
+
276
+ from .system_query_utils import (
277
+ MATERIALIZED_SYSTEM_MCP_PROJECTION,
278
+ build_materialized_system_search_filter,
279
+ get_system_query_url,
280
+ get_system_search_url,
281
+ )
282
+ from .utils import make_api_request
283
+
284
+ materialized_filter = None
285
+ if filter is None:
286
+ materialized_filter = build_materialized_system_search_filter(
287
+ alias=alias,
288
+ state=state,
289
+ workspace_id=workspace,
290
+ )
291
+
292
+ if filter is None:
293
+ search_payload: Dict[str, Any] = {
294
+ "take": take,
295
+ "projection": MATERIALIZED_SYSTEM_MCP_PROJECTION,
296
+ }
297
+ if materialized_filter:
298
+ search_payload["filter"] = materialized_filter
299
+ try:
300
+ data = make_api_request(
301
+ "POST",
302
+ get_system_search_url(),
303
+ payload=search_payload,
304
+ handle_errors=False,
305
+ ).json()
306
+ return _dump(_normalize_systems(data))
307
+ except requests_lib.RequestException:
308
+ pass
275
309
 
276
310
  filter_parts: List[str] = []
277
311
  if alias:
@@ -283,11 +317,16 @@ def query_systems(
283
317
  if filter:
284
318
  filter_parts.append(filter)
285
319
 
286
- payload: Dict[str, Any] = {"take": take}
320
+ query_payload: Dict[str, Any] = {"take": take}
287
321
  if filter_parts:
288
- payload["filter"] = " and ".join(filter_parts)
289
-
290
- data = _post_json(f"{get_base_url()}/nisysmgmt/v1/query-systems", payload)
322
+ query_payload["filter"] = " and ".join(filter_parts)
323
+
324
+ data = make_api_request(
325
+ "POST",
326
+ get_system_query_url(),
327
+ payload=query_payload,
328
+ handle_errors=False,
329
+ ).json()
291
330
  return _dump(_normalize_systems(data))
292
331
 
293
332
 
@@ -59,6 +59,8 @@ FEATURE_DISPLAY_NAMES: Dict[str, str] = {
59
59
  FILE_SEARCH_PATH = "/nifile/v1/service-groups/Default/search-files"
60
60
  FILE_QUERY_PATH = "/nifile/v1/service-groups/Default/query-files"
61
61
  FILE_QUERY_LINQ_PATH = "/nifile/v1/service-groups/Default/query-files-linq"
62
+ SYSTEM_SEARCH_PATH = "/nisysmgmt/v1/materialized/search-systems"
63
+ SYSTEM_QUERY_PATH = "/nisysmgmt/v1/query-systems"
62
64
 
63
65
 
64
66
  def _get_keyring_config() -> Dict[str, Any]:
@@ -195,7 +197,7 @@ SERVICE_CHECKS: List[List[str]] = [
195
197
  ["Auth", "GET", "/niauth/v1/policies"],
196
198
  ["Test Monitor", "GET", "/nitestmonitor/v2/results?take=0"],
197
199
  ["Asset Management", "POST", "/niapm/v1/query-assets"],
198
- ["Systems", "POST", "/nisysmgmt/v1/query-systems"],
200
+ ["Systems", "POST", SYSTEM_QUERY_PATH],
199
201
  ["Tag", "GET", "/nitag/v2/tags?take=0"],
200
202
  ["File", "POST", FILE_SEARCH_PATH],
201
203
  ["Notebook", "POST", "/ninotebook/v1/notebook/query"],
@@ -319,6 +321,87 @@ def get_file_query_capability(api_url: str, api_key: str) -> Dict[str, Any]:
319
321
  }
320
322
 
321
323
 
324
+ def get_system_query_capability(api_url: str, api_key: str) -> Dict[str, Any]:
325
+ """Determine which systems query endpoint is available for this server."""
326
+ headers = {
327
+ "x-ni-api-key": api_key,
328
+ "Content-Type": "application/json",
329
+ "User-Agent": "SystemLink-CLI/1.0 (cross-platform)",
330
+ }
331
+ ssl_verify = get_ssl_verify()
332
+
333
+ try:
334
+ search_resp = requests.post(
335
+ f"{api_url}{SYSTEM_SEARCH_PATH}",
336
+ headers=headers,
337
+ json={"take": 1, "projection": ["id"]},
338
+ verify=ssl_verify,
339
+ timeout=10,
340
+ )
341
+ if search_resp.status_code in (200, 400):
342
+ return {
343
+ "status": "ok",
344
+ "system_query_endpoint": "search-systems",
345
+ "materialized_search_available": True,
346
+ }
347
+ if search_resp.status_code in (401, 403):
348
+ return {
349
+ "status": "unauthorized",
350
+ "system_query_endpoint": "search-systems",
351
+ "materialized_search_available": True,
352
+ }
353
+ if search_resp.status_code not in (404, 501):
354
+ return {
355
+ "status": "error" if search_resp.status_code >= 500 else "not_found",
356
+ "system_query_endpoint": None,
357
+ "materialized_search_available": True,
358
+ }
359
+ except requests.RequestException:
360
+ return {
361
+ "status": "unreachable",
362
+ "system_query_endpoint": None,
363
+ "materialized_search_available": None,
364
+ }
365
+
366
+ try:
367
+ query_resp = requests.post(
368
+ f"{api_url}{SYSTEM_QUERY_PATH}",
369
+ headers=headers,
370
+ json={"take": 1, "projection": "new(id)"},
371
+ verify=ssl_verify,
372
+ timeout=10,
373
+ )
374
+ if query_resp.status_code in (200, 400):
375
+ return {
376
+ "status": "ok",
377
+ "system_query_endpoint": "query-systems",
378
+ "materialized_search_available": False,
379
+ }
380
+ if query_resp.status_code in (401, 403):
381
+ return {
382
+ "status": "unauthorized",
383
+ "system_query_endpoint": "query-systems",
384
+ "materialized_search_available": False,
385
+ }
386
+ if query_resp.status_code in (404, 501):
387
+ return {
388
+ "status": "not_found",
389
+ "system_query_endpoint": None,
390
+ "materialized_search_available": False,
391
+ }
392
+ return {
393
+ "status": "error",
394
+ "system_query_endpoint": None,
395
+ "materialized_search_available": False,
396
+ }
397
+ except requests.RequestException:
398
+ return {
399
+ "status": "unreachable",
400
+ "system_query_endpoint": None,
401
+ "materialized_search_available": False,
402
+ }
403
+
404
+
322
405
  def check_service_status(api_url: str, api_key: str) -> Dict[str, Any]:
323
406
  """Probe key SystemLink services and report their status.
324
407
 
@@ -334,10 +417,12 @@ def check_service_status(api_url: str, api_key: str) -> Dict[str, Any]:
334
417
  - auth_valid: bool | None - whether the API key is authorized (None if unreachable)
335
418
  - services: dict mapping service name to status string
336
419
  ("ok", "unauthorized", "not_found", "error", "unreachable")
337
- - file_query_endpoint: selected file query endpoint, if available
338
- - elasticsearch_available: bool | None - whether search-files is available
339
- - platform: detected platform string (PLATFORM_SLE, PLATFORM_SLS,
340
- PLATFORM_UNREACHABLE, PLATFORM_UNKNOWN)
420
+ - file_query_endpoint: selected file query endpoint, if available
421
+ - elasticsearch_available: bool | None - whether search-files is available
422
+ - system_query_endpoint: selected systems query endpoint, if available
423
+ - materialized_search_available: bool | None - whether search-systems is available
424
+ - platform: detected platform string (PLATFORM_SLE, PLATFORM_SLS,
425
+ PLATFORM_UNREACHABLE, PLATFORM_UNKNOWN)
341
426
  """
342
427
  headers = {
343
428
  "x-ni-api-key": api_key,
@@ -396,6 +481,8 @@ def check_service_status(api_url: str, api_key: str) -> Dict[str, Any]:
396
481
  "services": services,
397
482
  "file_query_endpoint": None,
398
483
  "elasticsearch_available": None,
484
+ "system_query_endpoint": None,
485
+ "materialized_search_available": None,
399
486
  "platform": PLATFORM_UNREACHABLE,
400
487
  }
401
488
 
@@ -416,6 +503,8 @@ def check_service_status(api_url: str, api_key: str) -> Dict[str, Any]:
416
503
 
417
504
  file_capability = get_file_query_capability(api_url, api_key)
418
505
  services["File"] = file_capability["status"]
506
+ system_capability = get_system_query_capability(api_url, api_key)
507
+ services["Systems"] = system_capability["status"]
419
508
 
420
509
  return {
421
510
  "server_reachable": True,
@@ -423,6 +512,8 @@ def check_service_status(api_url: str, api_key: str) -> Dict[str, Any]:
423
512
  "services": services,
424
513
  "file_query_endpoint": file_capability["file_query_endpoint"],
425
514
  "elasticsearch_available": file_capability["elasticsearch_available"],
515
+ "system_query_endpoint": system_capability["system_query_endpoint"],
516
+ "materialized_search_available": system_capability["materialized_search_available"],
426
517
  "platform": platform,
427
518
  }
428
519
 
@@ -473,6 +564,8 @@ def get_platform_info(skip_health: bool = False) -> Dict[str, Any]:
473
564
  platform = stored_platform
474
565
  file_query_endpoint: Optional[str] = None
475
566
  elasticsearch_available: Optional[bool] = None
567
+ system_query_endpoint: Optional[str] = None
568
+ materialized_search_available: Optional[bool] = None
476
569
 
477
570
  if not skip_health and logged_in and isinstance(api_url, str) and api_url != "Not configured":
478
571
  status = check_service_status(api_url, api_key)
@@ -482,6 +575,8 @@ def get_platform_info(skip_health: bool = False) -> Dict[str, Any]:
482
575
  platform = status["platform"]
483
576
  file_query_endpoint = status.get("file_query_endpoint")
484
577
  elasticsearch_available = status.get("elasticsearch_available")
578
+ system_query_endpoint = status.get("system_query_endpoint")
579
+ materialized_search_available = status.get("materialized_search_available")
485
580
 
486
581
  info: Dict[str, Any] = {
487
582
  "api_url": api_url,
@@ -497,6 +592,10 @@ def get_platform_info(skip_health: bool = False) -> Dict[str, Any]:
497
592
  info["file_query_endpoint"] = file_query_endpoint
498
593
  if elasticsearch_available is not None:
499
594
  info["elasticsearch_available"] = elasticsearch_available
595
+ if system_query_endpoint is not None:
596
+ info["system_query_endpoint"] = system_query_endpoint
597
+ if materialized_search_available is not None:
598
+ info["materialized_search_available"] = materialized_search_available
500
599
 
501
600
  if services is not None:
502
601
  info["services"] = services
@@ -0,0 +1,180 @@
1
+ ---
2
+ name: slcli
3
+ description: >-
4
+ Query and manage NI SystemLink resources using the slcli command-line interface.
5
+ Covers test results, assets, systems, tags, feeds, files, notebooks,
6
+ routines, work items, work item templates, workflows, test plan templates,
7
+ specifications, products, datasheet-to-specification ingestion (PDF and CSV),
8
+ custom fields, web applications, authorization policies, users, workspaces, and more.
9
+ Use when the user asks about test data analysis, asset management, calibration status,
10
+ system fleet health, operator performance, failure analysis, production metrics,
11
+ equipment utilization, work order tracking, specification management,
12
+ importing datasheets, creating products from spec sheets,
13
+ or any SystemLink resource operations.
14
+ Supports filtering, aggregation, summary statistics, and JSON output for programmatic processing.
15
+ argument-hint: >-
16
+ Describe what you want to do: query test results, import a datasheet,
17
+ list assets, manage work items, etc.
18
+ compatibility: >-
19
+ Requires slcli installed and authenticated (slcli login). Python 3.10+.
20
+ Requires network access to a SystemLink server instance.
21
+ metadata:
22
+ author: ni-kismet
23
+ version: "2.0"
24
+ # --system SYSTEM_ID Assign a system (by minion/system ID). Repeatable.
25
+ # --fixture ASSET_ID Assign a fixture/slot (by asset ID, asset type FIXTURE). Repeatable.
26
+ # --dut ASSET_ID Assign a DUT (by asset ID, asset type DEVICE_UNDER_TEST). Repeatable.
27
+ # Use `slcli asset list --asset-type FIXTURE` to find fixture IDs.
28
+ # Use `slcli system list` to find system IDs.
29
+ # At least one option must be provided; time and resource options can be combined freely.
30
+ slcli workitem schedule <WORK_ITEM_ID> \
31
+ [--start ISO8601] [--end ISO8601] [--duration SECONDS] \
32
+ [--assigned-to USER_ID] \
33
+ [--system SYSTEM_ID]... [--fixture ASSET_ID]... [--dut ASSET_ID]...
34
+
35
+ # Work item template subgroup
36
+ slcli workitem template list [-w WORKSPACE] [--filter TEXT] [-t INT] [-f json]
37
+ slcli workitem template get <TEMPLATE_ID> [-f json]
38
+ slcli workitem template create --name TEXT --type TEXT --template-group TEXT [-w WORKSPACE] [OPTIONS]
39
+ slcli workitem template update <TEMPLATE_ID> [--name TEXT] [--description TEXT] [--summary TEXT]
40
+ slcli workitem template delete <TEMPLATE_ID>... [--yes]
41
+
42
+ # Workflow subgroup
43
+ slcli workitem workflow list [-w WORKSPACE] [-t INT] [-f json]
44
+ slcli workitem workflow get [--id WORKFLOW_ID] [--name NAME] [-f json]
45
+ slcli workitem workflow init [--name TEXT] [--directory DIR] # Scaffold a local workflow file
46
+ slcli workitem workflow create --file PATH [-w WORKSPACE] # Create from JSON file
47
+ slcli workitem workflow import --file PATH [-w WORKSPACE] # Import workflow from JSON
48
+ slcli workitem workflow export [--id WORKFLOW_ID] [--name NAME] [-o FILE] # Export to JSON
49
+ slcli workitem workflow update --id WORKFLOW_ID --file PATH # Update from JSON file
50
+ slcli workitem workflow delete --id WORKFLOW_ID [--yes]
51
+ slcli workitem workflow preview [--file PATH] [--id WORKFLOW_ID] [--html] [--no-open] [-o FILE]
52
+ ```
53
+
54
+ **Create work item options:**
55
+
56
+ ```bash
57
+ slcli workitem create \
58
+ --name "Battery Cycle Test" \
59
+ --type testplan \
60
+ --state NEW \
61
+ --part-number "P-BAT-001" \
62
+ --description "Battery capacity test" \
63
+ --assigned-to <user-id> \
64
+ --workflow-id <workflow-id> \
65
+ --workspace Default \
66
+ --format json
67
+ ```
68
+
69
+ ### workflow — Workflow management
70
+
71
+ > **Note:** The standalone `slcli workflow` command group has been replaced by
72
+ > `slcli workitem workflow`. Use `slcli workitem workflow` for all workflow operations.
73
+ > See the **workitem** section above.
74
+
75
+ ### webapp — Web application management
76
+
77
+ Scaffold, package, and publish custom web applications to SystemLink.
78
+
79
+ ```bash
80
+ slcli webapp init <DIRECTORY> # Scaffold the Angular starter
81
+ slcli webapp manifest init <DIRECTORY> [OPTIONS] # Create nipkg.config.json for packaging
82
+ slcli webapp pack [FOLDER] [--config FILE] [-o OUTPUT_FILE] # Package a webapp into a .nipkg
83
+ slcli webapp list [-w WORKSPACE] [-t INT] [-f json]
84
+ slcli webapp get <WEBAPP_ID> [-f json]
85
+ slcli webapp publish PATH [--workspace NAME] # Upload and publish a webapp
86
+ slcli webapp delete <WEBAPP_ID>
87
+ slcli webapp open <WEBAPP_ID> # Open webapp URL in browser
88
+ ```
89
+
90
+ `webapp init` creates the SystemLink Angular starter, not a generic HTML app. The starter installs
91
+ project-scoped skills into `.agents/skills/` and creates `PROMPTS.md` plus `START_HERE.md` so an
92
+ AI assistant can bootstrap the Angular workspace in place with the same Nimble/SystemLink
93
+ conventions described by the `systemlink-webapp` skill.
94
+
95
+ `webapp manifest init` writes `nipkg.config.json` using the Plugin Manager field names
96
+ (`section`, `maintainer`, `homepage`, `xbPlugin`, `slPluginManagerTags`,
97
+ `slPluginManagerMinServerVersion`, `iconFile`). `webapp pack --config ...` consumes that
98
+ metadata, carries the icon into the package, writes the matching control-file fields into the
99
+ generated `.nipkg`, and emits a thin `manifest.json` with `schemaVersion`, `nipkgFile`,
100
+ `sha256`, and any configured provenance fields.
101
+
102
+ ### skill — AI skill installation
103
+
104
+ Install bundled skills for supported AI clients.
105
+
106
+ ```bash
107
+ slcli skill install --skill [slcli|systemlink-webapp|systemlink-notebook|all] --client [agents|claude|all] --scope [personal|project|both]
108
+ ```
109
+
110
+ Client paths:
111
+
112
+ - `agents` — personal: `~/.agents/skills/`, project: `.agents/skills/` (most agents)
113
+ - `claude` — personal: `~/.claude/skills/`, project: `.claude/skills/`
114
+ - `all` — install to both the `agents` and `claude` locations for the selected scope
115
+
116
+ Notes:
117
+
118
+ - `agents` is the default client in interactive mode.
119
+ - `webapp init` installs project-scoped skills into `.agents/skills/` by default.
120
+
121
+ ### example — Built-in example resource provisioning
122
+
123
+ Install pre-built demo configurations (systems, assets, DUTs, templates, etc.)
124
+ for training, testing, or evaluation.
125
+
126
+ ```bash
127
+ slcli example list [-f json] # List available examples
128
+ slcli example info <EXAMPLE_ID> # Show example details
129
+ slcli example install <EXAMPLE_ID> [--workspace NAME] # Provision example resources
130
+ slcli example delete <EXAMPLE_ID> [--workspace NAME] # Remove provisioned resources
131
+ ```
132
+
133
+ ## Reference docs
134
+
135
+ Consult these for detailed guidance. Load only what you need for the current task.
136
+
137
+ | Topic | File | When to load |
138
+ | --------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------- |
139
+ | CLI command reference | [commands.md](./references/commands.md) | Looking up command syntax, options, or examples |
140
+ | Datasheet-to-specs workflow | [datasheet-workflow.md](./references/datasheet-workflow.md) | Importing specs from PDF, CSV, or structured text |
141
+ | Filtering guide | [filtering.md](./references/filtering.md) | Advanced LINQ expressions, parameterized queries |
142
+ | Analysis recipes | [analysis-recipes.md](./references/analysis-recipes.md) | Multi-step analysis: yield, calibration, operator performance |
143
+ | Troubleshooting | [troubleshooting.md](./references/troubleshooting.md) | Workspace ID issues, SSL errors, encoding, PowerShell quoting |
144
+
145
+ ## Command groups at a glance
146
+
147
+ | Group | Purpose | Key subcommands |
148
+ | ------------- | ----------------------- | ---------------------------------------------------- |
149
+ | `testmonitor` | Test results & products | `result list/get`, `product list/create/update` |
150
+ | `spec` | Specifications | `list`, `query`, `get`, `create`, `import`, `export` |
151
+ | `asset` | Assets & calibration | `list`, `get`, `summary`, `calibration` |
152
+ | `system` | System fleet | `list`, `get`, `compare`, `summary`, `job` |
153
+ | `tag` | Tag read/write | `list`, `get-value`, `set-value`, `create` |
154
+ | `routine` | Event-action routines | `list`, `create`, `enable/disable` (v1 + v2) |
155
+ | `comment` | Resource comments | `list`, `add`, `update`, `delete` |
156
+ | `workitem` | Work items & workflows | `list`, `create`, `schedule`, `template`, `workflow` |
157
+ | `file` | File management | `list`, `upload`, `download`, `query`, `watch` |
158
+ | `notebook` | Jupyter notebooks | `manage list/create`, `execute start/sync` |
159
+ | `feed` | Package feeds | `list`, `create`, `package upload` |
160
+ | `customfield` | Dynamic form fields | `list`, `create`, `export`, `edit` |
161
+ | `template` | Test plan templates | `list`, `import`, `export` |
162
+ | `webapp` | Web applications | `init`, `pack`, `publish`, `list` |
163
+ | `config` | Connection profiles | `list`, `use`, `add`, `delete` |
164
+ | `user` | User management | `list`, `get`, `create`, `update` |
165
+ | `auth` | Authorization policies | `policy list/create`, `template list` |
166
+ | `workspace` | Workspaces | `list`, `get` |
167
+ | `skill` | AI skill installation | `install` |
168
+ | `example` | Demo provisioning | `list`, `install`, `delete` |
169
+ ## Key rules
170
+
171
+ 1. **Always use `-f json`** when piping output to `jq` or doing programmatic analysis.
172
+ 2. **Use `--summary --group-by`** for aggregation instead of fetching all records and counting.
173
+ 3. **Use convenience filters first** (e.g., `--status FAILED`), fall back to `--filter` for complex queries.
174
+ 4. **Parameterize `--filter` queries** — use `--substitution` instead of string interpolation.
175
+ 5. **Combine filters** — convenience filters are ANDed together automatically.
176
+ 6. **Use `--take`** to control result volume; JSON returns all matching up to `--take`.
177
+ 7. **Status enum values**: `PASSED`, `FAILED`, `RUNNING`, `ERRORED`, `TERMINATED`, `TIMEDOUT`, `WAITING`, `SKIPPED`, `CUSTOM`.
178
+ 8. **Exit codes**: 0 = success, 1 = general error, 2 = invalid input, 3 = not found, 4 = permission denied, 5 = network error.
179
+ 9. **Prefer workspace IDs** (UUIDs) over names in scripted workflows — some endpoints reject names.
180
+ 10. **Use `make_api_request`** from `slcli.utils` for helper scripts — handles auth, SSL, and errors.