skillctl-secure 0.30.0__tar.gz → 0.30.2__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 (132) hide show
  1. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/PKG-INFO +5 -5
  2. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/README.md +4 -4
  3. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/pyproject.toml +1 -1
  4. skillctl_secure-0.30.2/skillctl/__init__.py +1 -0
  5. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/attack_surface_tui.py +35 -9
  6. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/section_navigation.py +3 -0
  7. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/tui_original.py +10 -7
  8. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/tui_progressive.py +11 -0
  9. skillctl_secure-0.30.0/skillctl/__init__.py +0 -1
  10. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/.gitignore +0 -0
  11. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/LICENSE +0 -0
  12. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/__main__.py +0 -0
  13. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/attack_surface.py +0 -0
  14. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/authcontrols.py +0 -0
  15. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/benchmark.py +0 -0
  16. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/browsercontrols.py +0 -0
  17. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/bulk_cli.py +0 -0
  18. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/bulk_plan.py +0 -0
  19. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/bulk_update.py +0 -0
  20. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/bulk_update_tui.py +0 -0
  21. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/cacheutil.py +0 -0
  22. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/candidates.py +0 -0
  23. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/catalog.py +0 -0
  24. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/catalog_discovery.py +0 -0
  25. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/catalog_index.py +0 -0
  26. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/catalog_schema.py +0 -0
  27. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/catalog_search_cli.py +0 -0
  28. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/catalog_search_tui.py +0 -0
  29. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/cicdflow.py +0 -0
  30. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/cli.py +0 -0
  31. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/cli_contract.py +0 -0
  32. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/compatibility.py +0 -0
  33. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/context_cost.py +0 -0
  34. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/context_tui.py +0 -0
  35. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/copilot_export.py +0 -0
  36. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/coverage.py +0 -0
  37. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/dedupe.py +0 -0
  38. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/destinations.py +0 -0
  39. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/destinations_cli.py +0 -0
  40. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/destinations_tui.py +0 -0
  41. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/doctor.py +0 -0
  42. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/entrypoint.py +0 -0
  43. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/environments.py +0 -0
  44. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/estimate.py +0 -0
  45. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/evidence.py +0 -0
  46. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/findings.py +0 -0
  47. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/findings_tui.py +0 -0
  48. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/flow.py +0 -0
  49. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/governor.py +0 -0
  50. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/hardening.py +0 -0
  51. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/history_tui.py +0 -0
  52. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/iac.py +0 -0
  53. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/install_intent.py +0 -0
  54. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/install_review.py +0 -0
  55. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/intent_diff.py +0 -0
  56. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/inventory.py +0 -0
  57. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/inventory_audit.py +0 -0
  58. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/inventory_audit_tui.py +0 -0
  59. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/journal.py +0 -0
  60. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/journal_hooks.py +0 -0
  61. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/journal_operations.py +0 -0
  62. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/lockfile.py +0 -0
  63. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/manager.py +0 -0
  64. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/models.py +0 -0
  65. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/navigation_tui.py +0 -0
  66. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/operation_lock.py +0 -0
  67. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/packet.py +0 -0
  68. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/paths.py +0 -0
  69. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/permissions.py +0 -0
  70. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/policy.py +0 -0
  71. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/policy_editor.py +0 -0
  72. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/precision.py +0 -0
  73. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/profiles.py +0 -0
  74. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/publishers.py +0 -0
  75. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/ranking.py +0 -0
  76. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/recommendation.py +0 -0
  77. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/recommendation_tui.py +0 -0
  78. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/reconcile_cli.py +0 -0
  79. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/reconcile_recovery.py +0 -0
  80. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/reconcile_transaction.py +0 -0
  81. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/reconcile_tui.py +0 -0
  82. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/reconciliation.py +0 -0
  83. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/remote.py +0 -0
  84. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/benchmarks/vulnlab/answer-key.yaml +0 -0
  85. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/benchmarks/vulnlab/target/.env +0 -0
  86. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/benchmarks/vulnlab/target/.github/workflows/build.yml +0 -0
  87. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/benchmarks/vulnlab/target/README.md +0 -0
  88. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/benchmarks/vulnlab/target/app.py +0 -0
  89. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/benchmarks/vulnlab/target/openapi.yaml +0 -0
  90. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/benchmarks/vulnlab/target/requirements.txt +0 -0
  91. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/benchmarks/vulnlab/target/scripts/build.sh +0 -0
  92. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/benchmarks/vulnlab/target/templates/payment.html +0 -0
  93. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/benchmarks/vulnlab/target/trivy-results.json +0 -0
  94. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/custom/.gitkeep +0 -0
  95. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/custom/api-security-review/SKILL.md +0 -0
  96. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/custom/api-security-review/references/inventory-template.md +0 -0
  97. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/custom/api-security-review/references/output-template.md +0 -0
  98. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/custom/biso-security-review/SKILL.md +0 -0
  99. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/custom/biso-security-review/references/disposition.md +0 -0
  100. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/custom/biso-security-review/references/orchestration.md +0 -0
  101. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/custom/biso-security-review/references/output-template.md +0 -0
  102. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/custom/biso-security-review/references/review-checklist.md +0 -0
  103. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/custom/biso-security-review/references/risk-rating.md +0 -0
  104. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/custom/cicd-security-review/SKILL.md +0 -0
  105. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/custom/cicd-security-review/references/output-template.md +0 -0
  106. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/custom/pci-architect/SKILL.md +0 -0
  107. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/custom/pci-architect/references/control-matrix.md +0 -0
  108. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/custom/pci-architect/references/output-template.md +0 -0
  109. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/custom/threat-model/SKILL.md +0 -0
  110. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/custom/threat-model/references/output-template.md +0 -0
  111. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/custom/threat-model/references/risk-rating.md +0 -0
  112. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/custom/vulnerability-manager/SKILL.md +0 -0
  113. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/custom/vulnerability-manager/references/escalation.md +0 -0
  114. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/custom/vulnerability-manager/references/output-template.md +0 -0
  115. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/custom/vulnerability-manager/references/prioritization.md +0 -0
  116. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/finding-schema.json +0 -0
  117. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/resources/skills.yaml +0 -0
  118. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/review.py +0 -0
  119. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/review_candidates.py +0 -0
  120. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/risk_acknowledgment.py +0 -0
  121. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/runtime_binding.py +0 -0
  122. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/safety_plan.py +0 -0
  123. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/stagecache.py +0 -0
  124. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/stateio.py +0 -0
  125. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/transactions.py +0 -0
  126. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/trust.py +0 -0
  127. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/tui.py +0 -0
  128. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/tui_interaction.py +0 -0
  129. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/tui_refresh.py +0 -0
  130. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/update_intent_review.py +0 -0
  131. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/update_intent_tui.py +0 -0
  132. {skillctl_secure-0.30.0 → skillctl_secure-0.30.2}/skillctl/vulnnormalize.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: skillctl-secure
3
- Version: 0.30.0
3
+ Version: 0.30.2
4
4
  Summary: Package manager, trust layer, and portability layer for Agent Skills
5
5
  Project-URL: Homepage, https://github.com/brian-scardina/skillctl
6
6
  Project-URL: Repository, https://github.com/brian-scardina/skillctl
@@ -412,7 +412,7 @@ presence is reported in the result, not the exit status.
412
412
 
413
413
  ### Installed-skills attack surface and risk
414
414
 
415
- Open **Skills → Inventory → Attack surface & risk** for an interactive tree of AI
415
+ Open the top-level **Attack Surface & Risk** tab beside **Skills** for an interactive tree of AI
416
416
  app destinations, local skill copies, and observed indicators such as credential
417
417
  references, code execution, network references, and instruction overrides. Select
418
418
  any copy or indicator for its source locations, findings, risks, and suggested
@@ -422,9 +422,9 @@ shown but excluded from the installed-copy rating.
422
422
 
423
423
  Choose **Assess installed skills** to take fresh, read-only audits of up to 100
424
424
  copies. Enable the CVE checkbox to include OSV checks. The map initially shows
425
- **UNKNOWN** because discovery alone is not an audit. Closing the map stops the
426
- assessment between copies; an in-progress bounded audit may finish in the
427
- background. Reopening starts a new assessment, rather than reusing old findings.
425
+ **UNKNOWN** because discovery alone is not an audit. The assessment remains available when switching tabs. Opening the page performs
426
+ metadata discovery; choose **Assess installed skills** to refresh findings.
427
+ Restarting the app starts a new assessment.
428
428
 
429
429
  The overall **installed-skill risk** uses the highest observed heuristic severity.
430
430
  An affected dependency advisory imposes a **MEDIUM** review floor; this is a
@@ -388,7 +388,7 @@ presence is reported in the result, not the exit status.
388
388
 
389
389
  ### Installed-skills attack surface and risk
390
390
 
391
- Open **Skills → Inventory → Attack surface & risk** for an interactive tree of AI
391
+ Open the top-level **Attack Surface & Risk** tab beside **Skills** for an interactive tree of AI
392
392
  app destinations, local skill copies, and observed indicators such as credential
393
393
  references, code execution, network references, and instruction overrides. Select
394
394
  any copy or indicator for its source locations, findings, risks, and suggested
@@ -398,9 +398,9 @@ shown but excluded from the installed-copy rating.
398
398
 
399
399
  Choose **Assess installed skills** to take fresh, read-only audits of up to 100
400
400
  copies. Enable the CVE checkbox to include OSV checks. The map initially shows
401
- **UNKNOWN** because discovery alone is not an audit. Closing the map stops the
402
- assessment between copies; an in-progress bounded audit may finish in the
403
- background. Reopening starts a new assessment, rather than reusing old findings.
401
+ **UNKNOWN** because discovery alone is not an audit. The assessment remains available when switching tabs. Opening the page performs
402
+ metadata discovery; choose **Assess installed skills** to refresh findings.
403
+ Restarting the app starts a new assessment.
404
404
 
405
405
  The overall **installed-skill risk** uses the highest observed heuristic severity.
406
406
  An affected dependency advisory imposes a **MEDIUM** review floor; this is a
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "skillctl-secure"
3
- version = "0.30.0"
3
+ version = "0.30.2"
4
4
  description = "Package manager, trust layer, and portability layer for Agent Skills"
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -0,0 +1 @@
1
+ __version__ = "0.30.2"
@@ -9,11 +9,10 @@ from .attack_surface import (scan_inventory, build_surface, surface_summary, nod
9
9
  assess_inventory, METHOD)
10
10
 
11
11
 
12
- class AttackSurfaceScreen(ModalScreen):
13
- BINDINGS = [('escape', 'close', 'Close')]
14
- CSS = '''
15
- AttackSurfaceScreen { align: center middle; background: $background 85%; }
16
- #surface-dialog { width: 96%; height: 95%; background: $surface; border: solid $accent; padding: 1; }
12
+ class AttackSurfacePanel(Vertical):
13
+ DEFAULT_CSS = '''
14
+ AttackSurfacePanel { height: 1fr; width: 1fr; }
15
+ #surface-dialog { width: 100%; height: 100%; background: $surface; border: solid $accent; padding: 1; }
17
16
  #surface-summary { height: auto; max-height: 8; overflow-y: auto; }
18
17
  #surface-body { height: 1fr; }
19
18
  #surface-tree { width: 45%; }
@@ -23,9 +22,11 @@ class AttackSurfaceScreen(ModalScreen):
23
22
  #surface-actions Button { width: 1fr; }
24
23
  '''
25
24
 
26
- def __init__(self, manager):
25
+ def __init__(self, manager, auto_load=True):
27
26
  super().__init__()
28
27
  self.manager = manager
28
+ self.auto_load = auto_load
29
+ self._loaded = False
29
30
  self.inventory = {'rows': [], 'errors': []}
30
31
 
31
32
  def compose(self):
@@ -38,10 +39,17 @@ class AttackSurfaceScreen(ModalScreen):
38
39
  yield Static(METHOD + '\n\nSelect a skill or indicator to see evidence and fixes. Shared copies appear under each destination but count once. Each run assesses at most 100 copies; remaining copies stay UNKNOWN.', id='surface-detail', markup=False)
39
40
  with Horizontal(id='surface-actions'):
40
41
  yield Button('Assess installed skills', id='surface-assess', variant='primary', disabled=True)
41
- yield Button('Close', id='surface-close')
42
+ if self.auto_load:
43
+ yield Button('Close', id='surface-close')
42
44
 
43
45
  def on_mount(self):
44
- self.load_surface()
46
+ if self.auto_load:
47
+ self.ensure_loaded()
48
+
49
+ def ensure_loaded(self):
50
+ if not self._loaded:
51
+ self._loaded = True
52
+ self.load_surface()
45
53
 
46
54
  @work(thread=True, exclusive=True, group='surface-assessment')
47
55
  def load_surface(self, assess=False, online=False):
@@ -95,7 +103,7 @@ class AttackSurfaceScreen(ModalScreen):
95
103
 
96
104
  def action_close(self):
97
105
  self.workers.cancel_group(self, 'surface-assessment')
98
- self.dismiss()
106
+ self.screen.dismiss()
99
107
 
100
108
  def on_button_pressed(self, event):
101
109
  event.stop()
@@ -105,3 +113,21 @@ class AttackSurfaceScreen(ModalScreen):
105
113
  event.button.disabled = True
106
114
  self.query_one('#surface-detail', Static).update(METHOD + '\n\nAssessment in progress. Select a copy for current findings.')
107
115
  self.load_surface(True, self.query_one('#surface-cves', Checkbox).value)
116
+
117
+
118
+ class AttackSurfaceScreen(ModalScreen):
119
+ BINDINGS = [('escape', 'close', 'Close')]
120
+ CSS = """
121
+ AttackSurfaceScreen { align: center middle; background: $background 85%; }
122
+ AttackSurfaceScreen AttackSurfacePanel { width: 96%; height: 95%; }
123
+ """
124
+
125
+ def __init__(self, manager):
126
+ super().__init__()
127
+ self.manager = manager
128
+
129
+ def compose(self):
130
+ yield AttackSurfacePanel(self.manager)
131
+
132
+ def action_close(self):
133
+ self.query_one(AttackSurfacePanel).action_close()
@@ -4,6 +4,7 @@ from textual.widgets import Tabs, TabbedContent
4
4
  SECTIONS = {
5
5
  'home': ('home',),
6
6
  'skills': ('available', 'inventory', 'installed', 'updates', 'custom'),
7
+ 'risk': ('attack-surface',),
7
8
  'reviews': ('reviews',),
8
9
  'organize': ('collections', 'workbenches', 'profiles', 'environments'),
9
10
  'system': ('doctor', 'policy', 'permissions', 'publishers', 'hosts', 'toolbox'),
@@ -29,6 +30,8 @@ def activate_section(app, event):
29
30
  if event.tabs.id != 'sections':
30
31
  return
31
32
  event.stop()
33
+ if event.tab.id != event.tabs.active:
34
+ return
32
35
  section = event.tab.id.removeprefix('section-')
33
36
  pages = SECTIONS[section]
34
37
  catalog = app.query_one('#catalog', TabbedContent)
@@ -53,8 +53,8 @@ class ConfirmScreen(ModalScreen[bool]):
53
53
  def on_button_pressed(self, event: Button.Pressed) -> None:
54
54
  if event.button.id == "btn-attack-surface":
55
55
  event.stop()
56
- from .attack_surface_tui import AttackSurfaceScreen
57
- self.push_screen(AttackSurfaceScreen(self.manager))
56
+ self.set_focus(None)
57
+ self.query_one("#sections", Tabs).active = "section-risk"
58
58
  return
59
59
  event.stop()
60
60
  self.dismiss(event.button.id == "confirm-ok")
@@ -89,8 +89,8 @@ class AddSkillScreen(ModalScreen[dict | None]):
89
89
  def on_button_pressed(self, event: Button.Pressed) -> None:
90
90
  if event.button.id == "btn-attack-surface":
91
91
  event.stop()
92
- from .attack_surface_tui import AttackSurfaceScreen
93
- self.push_screen(AttackSurfaceScreen(self.manager))
92
+ self.set_focus(None)
93
+ self.query_one("#sections", Tabs).active = "section-risk"
94
94
  return
95
95
  if event.button.id == "cancel":
96
96
  self.dismiss(None)
@@ -270,12 +270,15 @@ class SkillCtlTUI(App):
270
270
  yield Header()
271
271
  yield Static("", id="summary", markup=True)
272
272
  with Horizontal(id="navigation"):
273
- yield Tabs(*(Tab(label, id="section-" + key) for key, label in (("home", "Home"), ("skills", "Skills"), ("reviews", "Reviews"), ("organize", "Organize"), ("system", "System"))), id="sections")
273
+ yield Tabs(*(Tab(label, id="section-" + key) for key, label in (("home", "Home"), ("skills", "Skills"), ("risk", "Attack Surface & Risk"), ("reviews", "Reviews"), ("organize", "Organize"), ("system", "System"))), id="sections")
274
274
  yield Input(placeholder="Search… [/]", id="search", tooltip="Search skills, descriptions, categories, and collections. Press / to focus.")
275
275
  with Horizontal(id="body"):
276
276
  with TabbedContent(id="catalog"):
277
277
  with TabPane("Home", id="home"):
278
278
  yield DataTable(id="table-home", cursor_type="row")
279
+ with TabPane("Attack Surface & Risk", id="attack-surface"):
280
+ from .attack_surface_tui import AttackSurfacePanel
281
+ yield AttackSurfacePanel(self.manager, auto_load=False)
279
282
  with TabPane("Available", id="available"):
280
283
  yield DataTable(id="table-available", cursor_type="row")
281
284
  with TabPane("Inventory", id="inventory"):
@@ -1282,8 +1285,8 @@ class SkillCtlTUI(App):
1282
1285
  def on_button_pressed(self, event: Button.Pressed) -> None:
1283
1286
  if event.button.id == "btn-attack-surface":
1284
1287
  event.stop()
1285
- from .attack_surface_tui import AttackSurfaceScreen
1286
- self.push_screen(AttackSurfaceScreen(self.manager))
1288
+ self.set_focus(None)
1289
+ self.query_one("#sections", Tabs).active = "section-risk"
1287
1290
  return
1288
1291
  if event.button.id == "btn-findings":
1289
1292
  event.stop()
@@ -74,8 +74,19 @@ def install_progressive_startup(SkillCtlTUI, legacy_on_mount) -> None:
74
74
  pane = getattr(event, "pane", None)
75
75
  pane_id = getattr(pane, "id", None)
76
76
  if pane_id:
77
+ from textual.widgets import TabbedContent
78
+ if pane_id != self.query_one("#catalog", TabbedContent).active:
79
+ return
77
80
  from .section_navigation import sync_section
78
81
  sync_section(self, str(pane_id))
82
+ if pane_id == "attack-surface":
83
+ from .attack_surface_tui import AttackSurfacePanel
84
+ self._close_detail()
85
+ self.selected_kind = "attack-surface"
86
+ self.selected_name = None
87
+ self._update_skill_action_buttons(None)
88
+ self.query_one(AttackSurfacePanel).ensure_loaded()
89
+ return
79
90
  self._hydrate_secondary_tab(str(pane_id))
80
91
  if pane_id in {"home", "available", "inventory", "installed", "updates", "custom", "collections", "workbenches", "reviews"}:
81
92
  table = self.query_one(f"#table-{pane_id}", DataTable)
@@ -1 +0,0 @@
1
- __version__ = "0.30.0"