plugin-scanner 2.0.108__tar.gz → 2.0.109__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 (350) hide show
  1. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/PKG-INFO +20 -9
  2. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/README.md +19 -8
  3. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/docs/guard/get-started.md +24 -0
  4. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/docs/guard/testing-matrix.md +10 -0
  5. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/pyproject.toml +1 -1
  6. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/pyproject.toml.bak +1 -1
  7. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/cli/commands.py +9 -1
  8. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/cli/render.py +117 -51
  9. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/version.py +1 -1
  10. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_cli.py +36 -0
  11. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_cli.py +157 -0
  12. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_product_flow.py +11 -0
  13. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_runtime.py +40 -1
  14. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/.clusterfuzzlite/Dockerfile +0 -0
  15. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/.clusterfuzzlite/build.sh +0 -0
  16. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/.clusterfuzzlite/project.yaml +0 -0
  17. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/.clusterfuzzlite/requirements-atheris.txt +0 -0
  18. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/.dockerignore +0 -0
  19. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/.github/CODEOWNERS +0 -0
  20. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  21. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  22. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
  23. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/.github/dependabot.yml +0 -0
  24. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/.github/workflows/ci.yml +0 -0
  25. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/.github/workflows/codeql.yml +0 -0
  26. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/.github/workflows/dependabot-uv-lock.yml +0 -0
  27. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/.github/workflows/fuzz.yml +0 -0
  28. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/.github/workflows/harness-smoke.yml +0 -0
  29. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/.github/workflows/publish.yml +0 -0
  30. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/.github/workflows/scorecard.yml +0 -0
  31. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/.gitignore +0 -0
  32. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/.pre-commit-hooks.yaml +0 -0
  33. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/CONTRIBUTING.md +0 -0
  34. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/Dockerfile +0 -0
  35. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/LICENSE +0 -0
  36. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/SECURITY.md +0 -0
  37. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/index.html +0 -0
  38. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/package.json +0 -0
  39. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/pnpm-lock.yaml +0 -0
  40. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/public/apple-touch-icon.png +0 -0
  41. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
  42. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/public/brand/Logo_Whole.png +0 -0
  43. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/public/favicon-16x16.png +0 -0
  44. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/public/favicon-32x32.png +0 -0
  45. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/public/favicon.ico +0 -0
  46. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/src/app.tsx +0 -0
  47. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/src/approval-center-layout.tsx +0 -0
  48. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/src/approval-center-primitives.tsx +0 -0
  49. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/src/approval-center-utils.ts +0 -0
  50. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/src/fleet-workspace.tsx +0 -0
  51. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/src/guard-api.test.ts +0 -0
  52. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/src/guard-api.ts +0 -0
  53. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/src/guard-demo.ts +0 -0
  54. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/src/guard-types.ts +0 -0
  55. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/src/main.tsx +0 -0
  56. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/src/receipts-workspace.tsx +0 -0
  57. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/src/runtime-overview.tsx +0 -0
  58. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/src/settings-workspace.tsx +0 -0
  59. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/src/styles.css +0 -0
  60. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/src/vite-env.d.ts +0 -0
  61. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/tsconfig.json +0 -0
  62. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/dashboard/vite.config.ts +0 -0
  63. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/docker-requirements.txt +0 -0
  64. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/docs/guard/approval-audit.md +0 -0
  65. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/docs/guard/architecture.md +0 -0
  66. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/docs/guard/harness-support.md +0 -0
  67. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/docs/guard/local-vs-cloud.md +0 -0
  68. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/docs/trust/mcp-trust-draft.md +0 -0
  69. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/docs/trust/plugin-trust-draft.md +0 -0
  70. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/docs/trust/skill-trust-local.md +0 -0
  71. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/fuzzers/manifest_fuzzer.py +0 -0
  72. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/requirements.txt +0 -0
  73. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/schemas/plugin-quality.v1.json +0 -0
  74. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/schemas/scan-result.v1.json +0 -0
  75. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/schemas/verify-result.v1.json +0 -0
  76. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/__init__.py +0 -0
  77. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/action_runner.py +0 -0
  78. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/argparse_utils.py +0 -0
  79. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/checks/__init__.py +0 -0
  80. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
  81. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/checks/claude.py +0 -0
  82. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
  83. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
  84. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/checks/gemini.py +0 -0
  85. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/checks/manifest.py +0 -0
  86. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
  87. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
  88. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
  89. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/checks/opencode.py +0 -0
  90. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
  91. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/checks/security.py +0 -0
  92. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
  93. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/cli.py +0 -0
  94. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/cli_ui.py +0 -0
  95. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/config.py +0 -0
  96. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
  97. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
  98. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
  99. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
  100. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
  101. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
  102. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
  103. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
  104. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
  105. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/github_reporting.py +0 -0
  106. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/__init__.py +0 -0
  107. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
  108. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
  109. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
  110. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
  111. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/adapters/cloud_identity.py +0 -0
  112. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
  113. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
  114. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
  115. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
  116. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
  117. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
  118. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/adapters/openclaw.py +0 -0
  119. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/adapters/openclaw_config.py +0 -0
  120. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/adapters/openclaw_support.py +0 -0
  121. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
  122. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
  123. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
  124. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/approvals.py +0 -0
  125. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
  126. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
  127. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
  128. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
  129. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
  130. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
  131. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
  132. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
  133. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
  134. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
  135. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
  136. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/config.py +0 -0
  137. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
  138. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
  139. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
  140. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
  141. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
  142. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
  143. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
  144. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
  145. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
  146. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
  147. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
  148. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
  149. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
  150. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
  151. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
  152. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
  153. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/incident.py +0 -0
  154. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/launcher.py +0 -0
  155. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
  156. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/models.py +0 -0
  157. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
  158. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
  159. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/protect.py +0 -0
  160. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
  161. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
  162. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
  163. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
  164. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
  165. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
  166. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/redaction.py +0 -0
  167. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/risk.py +0 -0
  168. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
  169. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/runtime/actions.py +0 -0
  170. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/runtime/decisions.py +0 -0
  171. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/runtime/detectors.py +0 -0
  172. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/runtime/runner.py +0 -0
  173. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
  174. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/runtime/secret_sensitivity.py +0 -0
  175. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/runtime/signals.py +0 -0
  176. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
  177. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
  178. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
  179. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
  180. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
  181. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/shims.py +0 -0
  182. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/store.py +0 -0
  183. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
  184. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
  185. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/guard/types.py +0 -0
  186. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
  187. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
  188. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
  189. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/lint_fixes.py +0 -0
  190. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/marketplace_support.py +0 -0
  191. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/models.py +0 -0
  192. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/path_support.py +0 -0
  193. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/policy.py +0 -0
  194. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/quality_artifact.py +0 -0
  195. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/repo_detect.py +0 -0
  196. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/reporting.py +0 -0
  197. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/rules/__init__.py +0 -0
  198. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/rules/registry.py +0 -0
  199. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/rules/specs.py +0 -0
  200. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/scanner.py +0 -0
  201. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/submission.py +0 -0
  202. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/suppressions.py +0 -0
  203. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
  204. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/trust_helpers.py +0 -0
  205. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
  206. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/trust_models.py +0 -0
  207. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
  208. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/trust_scoring.py +0 -0
  209. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
  210. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/trust_specs.py +0 -0
  211. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/src/codex_plugin_scanner/verification.py +0 -0
  212. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/__init__.py +0 -0
  213. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/conftest.py +0 -0
  214. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/__init__.py +0 -0
  215. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
  216. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/bad-plugin/.mcp.json +0 -0
  217. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/bad-plugin/secrets.js +0 -0
  218. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
  219. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
  220. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/claude-plugin-good/README.md +0 -0
  221. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
  222. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
  223. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
  224. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/code-quality-bad/evil.js +0 -0
  225. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/code-quality-bad/inject.js +0 -0
  226. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
  227. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
  228. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/gemini-extension-good/README.md +0 -0
  229. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
  230. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
  231. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
  232. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
  233. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/good-plugin/.codexignore +0 -0
  234. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/good-plugin/LICENSE +0 -0
  235. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/good-plugin/README.md +0 -0
  236. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/good-plugin/SECURITY.md +0 -0
  237. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
  238. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
  239. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
  240. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
  241. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
  242. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
  243. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
  244. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
  245. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
  246. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
  247. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
  248. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
  249. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
  250. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
  251. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
  252. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
  253. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
  254. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
  255. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
  256. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/mcp-canary-server.py +0 -0
  257. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
  258. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
  259. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/mit-license/LICENSE +0 -0
  260. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
  261. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
  262. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
  263. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
  264. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
  265. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
  266. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
  267. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
  268. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
  269. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
  270. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
  271. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
  272. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
  273. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
  274. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
  275. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
  276. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
  277. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
  278. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/opencode-good/LICENSE +0 -0
  279. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/opencode-good/README.md +0 -0
  280. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/opencode-good/SECURITY.md +0 -0
  281. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
  282. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
  283. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
  284. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
  285. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
  286. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
  287. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/fixtures/with-marketplace/marketplace.json +0 -0
  288. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test-trust-scoring.py +0 -0
  289. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test-trust-specs.py +0 -0
  290. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_action_runner.py +0 -0
  291. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_best_practices.py +0 -0
  292. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_cisco_install_surfaces.py +0 -0
  293. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_code_quality.py +0 -0
  294. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_config.py +0 -0
  295. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_coverage_remaining.py +0 -0
  296. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_ecosystems.py +0 -0
  297. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_edge_cases.py +0 -0
  298. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_final_coverage.py +0 -0
  299. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_access_graph.py +0 -0
  300. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_approvals.py +0 -0
  301. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_bootstrap.py +0 -0
  302. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_capabilities.py +0 -0
  303. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_claude_adapter.py +0 -0
  304. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_codex_e2e.py +0 -0
  305. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_codex_install.py +0 -0
  306. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_codex_proxy.py +0 -0
  307. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_config_paths.py +0 -0
  308. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_connect_flow.py +0 -0
  309. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_consumer_mode.py +0 -0
  310. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_copilot_adapter.py +0 -0
  311. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_copilot_proxy.py +0 -0
  312. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_daemon_manager.py +0 -0
  313. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_event_schema_v1.py +0 -0
  314. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_events.py +0 -0
  315. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_launch_env.py +0 -0
  316. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_opencode_proxy.py +0 -0
  317. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_protect.py +0 -0
  318. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_render.py +0 -0
  319. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_risk.py +0 -0
  320. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_runtime_action_harnesses.py +0 -0
  321. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_runtime_actions.py +0 -0
  322. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_runtime_decisions.py +0 -0
  323. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_runtime_detectors.py +0 -0
  324. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_runtime_signals.py +0 -0
  325. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_store_migrations.py +0 -0
  326. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_surface_server.py +0 -0
  327. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_guard_verdicts.py +0 -0
  328. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_hermes_adapter.py +0 -0
  329. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_integration.py +0 -0
  330. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_lint_fixes.py +0 -0
  331. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_live_cisco_smoke.py +0 -0
  332. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_manifest.py +0 -0
  333. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_marketplace.py +0 -0
  334. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_mcp_security.py +0 -0
  335. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_openclaw_adapter.py +0 -0
  336. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_operational_security.py +0 -0
  337. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_policy.py +0 -0
  338. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_quality_artifact.py +0 -0
  339. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_rule_registry.py +0 -0
  340. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_scanner.py +0 -0
  341. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_schema_contracts.py +0 -0
  342. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_security.py +0 -0
  343. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_security_ops.py +0 -0
  344. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_skill_security.py +0 -0
  345. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_submission.py +0 -0
  346. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_trust_scoring.py +0 -0
  347. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_trust_specs.py +0 -0
  348. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_verification.py +0 -0
  349. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/tests/test_versioning.py +0 -0
  350. {plugin_scanner-2.0.108 → plugin_scanner-2.0.109}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plugin-scanner
3
- Version: 2.0.108
3
+ Version: 2.0.109
4
4
  Summary: Lint, verify, and gate plugin ecosystems for maintainers, CI, and publish workflows.
5
5
  Project-URL: Homepage, https://github.com/hashgraph-online/ai-plugin-scanner
6
6
  Project-URL: Repository, https://github.com/hashgraph-online/ai-plugin-scanner
@@ -302,33 +302,44 @@ The scanner evaluates only the surfaces a plugin actually exposes, then normaliz
302
302
 
303
303
  ```bash
304
304
  # Scan a plugin directory
305
- plugin-scanner ./my-plugin
305
+ plugin-scanner scan ./my-plugin
306
306
 
307
307
  # Auto-detect all supported ecosystems inside a repo (default)
308
- plugin-scanner ./plugins-repo --ecosystem auto
308
+ plugin-scanner scan ./plugins-repo --ecosystem auto
309
309
 
310
310
  # Scan only Claude package surfaces
311
- plugin-scanner ./plugins-repo --ecosystem claude
311
+ plugin-scanner scan ./plugins-repo --ecosystem claude
312
312
 
313
313
  # List supported ecosystems
314
314
  plugin-scanner --list-ecosystems
315
315
 
316
316
  # Output JSON
317
- plugin-scanner ./my-plugin --json
317
+ plugin-scanner scan ./my-plugin --format json
318
318
 
319
319
  # Write a SARIF report for GitHub code scanning
320
- plugin-scanner ./my-plugin --format sarif --output plugin-scanner.sarif
320
+ plugin-scanner scan ./my-plugin --format sarif --output plugin-scanner.sarif
321
321
 
322
322
  # Fail CI on findings at or above high severity
323
- plugin-scanner ./my-plugin --fail-on-severity high
323
+ plugin-scanner scan ./my-plugin --fail-on-severity high
324
324
 
325
325
  # Require Cisco skill scanning with a strict policy
326
- plugin-scanner ./my-plugin --cisco-skill-scan on --cisco-policy strict
326
+ plugin-scanner scan ./my-plugin --cisco-skill-scan on --cisco-policy strict
327
327
 
328
328
  # Require optional Cisco MCP static analysis
329
- plugin-scanner ./my-plugin --cisco-mcp-scan on
329
+ plugin-scanner scan ./my-plugin --cisco-mcp-scan on
330
330
  ```
331
331
 
332
+ Use the bare `plugin-scanner ./my-plugin` form only for compatibility with older automation. New scripts and docs should
333
+ prefer explicit subcommands so `scan`, `lint`, `verify`, `submit`, and `doctor` have predictable help, flags, and output.
334
+
335
+ | Need | Command | Output contract |
336
+ | :--- | :--- | :--- |
337
+ | Human release summary | `plugin-scanner scan ./my-plugin` | terminal summary first, optional JSON/Markdown/SARIF with `--format` |
338
+ | Rule-level authoring feedback | `plugin-scanner lint ./my-plugin` | human findings by default, JSON with `--format json` |
339
+ | Runtime readiness details | `plugin-scanner verify ./my-plugin` | human pass/fail by default, JSON with `--format json` |
340
+ | Publishable quality artifact | `plugin-scanner submit ./my-plugin --attest dist/plugin-quality.json` | writes one artifact for one plugin directory |
341
+ | Troubleshooting bundle | `plugin-scanner doctor ./my-plugin --component mcp --bundle dist/doctor.zip` | diagnostic JSON and bundle artifacts |
342
+
332
343
  ## Quality Suite Commands
333
344
 
334
345
  ```bash
@@ -262,33 +262,44 @@ The scanner evaluates only the surfaces a plugin actually exposes, then normaliz
262
262
 
263
263
  ```bash
264
264
  # Scan a plugin directory
265
- plugin-scanner ./my-plugin
265
+ plugin-scanner scan ./my-plugin
266
266
 
267
267
  # Auto-detect all supported ecosystems inside a repo (default)
268
- plugin-scanner ./plugins-repo --ecosystem auto
268
+ plugin-scanner scan ./plugins-repo --ecosystem auto
269
269
 
270
270
  # Scan only Claude package surfaces
271
- plugin-scanner ./plugins-repo --ecosystem claude
271
+ plugin-scanner scan ./plugins-repo --ecosystem claude
272
272
 
273
273
  # List supported ecosystems
274
274
  plugin-scanner --list-ecosystems
275
275
 
276
276
  # Output JSON
277
- plugin-scanner ./my-plugin --json
277
+ plugin-scanner scan ./my-plugin --format json
278
278
 
279
279
  # Write a SARIF report for GitHub code scanning
280
- plugin-scanner ./my-plugin --format sarif --output plugin-scanner.sarif
280
+ plugin-scanner scan ./my-plugin --format sarif --output plugin-scanner.sarif
281
281
 
282
282
  # Fail CI on findings at or above high severity
283
- plugin-scanner ./my-plugin --fail-on-severity high
283
+ plugin-scanner scan ./my-plugin --fail-on-severity high
284
284
 
285
285
  # Require Cisco skill scanning with a strict policy
286
- plugin-scanner ./my-plugin --cisco-skill-scan on --cisco-policy strict
286
+ plugin-scanner scan ./my-plugin --cisco-skill-scan on --cisco-policy strict
287
287
 
288
288
  # Require optional Cisco MCP static analysis
289
- plugin-scanner ./my-plugin --cisco-mcp-scan on
289
+ plugin-scanner scan ./my-plugin --cisco-mcp-scan on
290
290
  ```
291
291
 
292
+ Use the bare `plugin-scanner ./my-plugin` form only for compatibility with older automation. New scripts and docs should
293
+ prefer explicit subcommands so `scan`, `lint`, `verify`, `submit`, and `doctor` have predictable help, flags, and output.
294
+
295
+ | Need | Command | Output contract |
296
+ | :--- | :--- | :--- |
297
+ | Human release summary | `plugin-scanner scan ./my-plugin` | terminal summary first, optional JSON/Markdown/SARIF with `--format` |
298
+ | Rule-level authoring feedback | `plugin-scanner lint ./my-plugin` | human findings by default, JSON with `--format json` |
299
+ | Runtime readiness details | `plugin-scanner verify ./my-plugin` | human pass/fail by default, JSON with `--format json` |
300
+ | Publishable quality artifact | `plugin-scanner submit ./my-plugin --attest dist/plugin-quality.json` | writes one artifact for one plugin directory |
301
+ | Troubleshooting bundle | `plugin-scanner doctor ./my-plugin --component mcp --bundle dist/doctor.zip` | diagnostic JSON and bundle artifacts |
302
+
292
303
  ## Quality Suite Commands
293
304
 
294
305
  ```bash
@@ -76,6 +76,30 @@ Use it when you want to protect a harness before local MCP servers, skills, hook
76
76
  hol-guard device rotate
77
77
  ```
78
78
 
79
+ ## Which command should I use?
80
+
81
+ | Situation | Command | What it answers |
82
+ | :--- | :--- | :--- |
83
+ | I need the current protection posture | `hol-guard status` | What is Guard watching, is sync connected, and what is the next action? |
84
+ | I need setup or runtime troubleshooting | `hol-guard doctor <harness>` | Why is this harness or Guard runtime not behaving correctly? |
85
+ | A launch was blocked or changed | `hol-guard diff <harness>` | What changed since the last recorded snapshot? |
86
+ | I need to resolve a queued block | `hol-guard approvals` | Which requests are waiting, and how do I approve or deny them? |
87
+ | I need decision history | `hol-guard receipts` | What decisions did Guard record locally? |
88
+ | I need the tracked catalog | `hol-guard inventory` | Which artifacts are currently tracked and present? |
89
+ | I need an exportable evidence artifact | `hol-guard abom` | What local AI-BOM can I attach to an audit or handoff? |
90
+ | I need the chronological log | `hol-guard events` | What happened over time on this machine? |
91
+
92
+ ## Troubleshooting
93
+
94
+ | Symptom | Start here | Then try |
95
+ | :--- | :--- | :--- |
96
+ | Guard did not find my harness | `hol-guard detect --json` | `hol-guard doctor <harness> --json` for adapter-specific warnings |
97
+ | `hol-guard run` paused a launch | `hol-guard diff <harness>` | `hol-guard approvals`, then retry `hol-guard run <harness>` |
98
+ | I approved a prompt and want proof | `hol-guard receipts` | `hol-guard explain <artifact-id>` for the latest receipt and diff context |
99
+ | I need audit or handoff evidence | `hol-guard inventory` | `hol-guard abom --format json` for machine-readable export |
100
+ | I need to understand recent activity | `hol-guard events` | Use `--name <event>` to filter a noisy local timeline |
101
+ | Cloud sync or pairing looks wrong | `hol-guard status` | `hol-guard connect` or `hol-guard sync --json` depending on the status output |
102
+
79
103
  ## Evidence-first decisions
80
104
 
81
105
  Guard now scores local decisions from structured evidence, not only string heuristics. Each changed artifact carries:
@@ -10,6 +10,8 @@ Automated coverage in this phase includes:
10
10
  - consumer-mode JSON contract generation against scanner fixtures
11
11
  - local HTTP sync against a live in-process server instead of mocked transport
12
12
  - scheduled self-hosted harness smoke through `.github/workflows/harness-smoke.yml`
13
+ - CLI DX contract tests for summary-first `run`, `explain`, `doctor`, `scan`, `lint`, and `verify` output
14
+ - scanner command consistency tests for nonexistent target handling across `scan`, `lint`, `verify`, `doctor`, and `submit`
13
15
 
14
16
  Manual verification should include:
15
17
 
@@ -32,6 +34,14 @@ Manual verification should include:
32
34
  - `hol-guard install codex`
33
35
  - `hol-guard run codex --dry-run --default-action allow --json`
34
36
  - `hol-guard receipts`
37
+ - `hol-guard explain codex:project:<artifact-name>`
38
+ - `hol-guard diff codex`
39
+ - `hol-guard events`
40
+ - `hol-guard abom`
41
+ - `plugin-scanner scan tests/fixtures/good-plugin --format json`
42
+ - `plugin-scanner lint tests/fixtures/good-plugin --format json`
43
+ - `plugin-scanner verify tests/fixtures/good-plugin --format json`
44
+ - `plugin-scanner doctor tests/fixtures/good-plugin --component mcp --bundle dist/doctor.zip`
35
45
  - `codex mcp list`
36
46
  - `cursor-agent mcp list`
37
47
  - `antigravity --help`
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "plugin-scanner"
7
- version = "2.0.108"
7
+ version = "2.0.109"
8
8
  description = "Lint, verify, and gate plugin ecosystems for maintainers, CI, and publish workflows."
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "hol-guard"
7
- version = "2.0.108"
7
+ version = "2.0.109"
8
8
  description = "Protect local AI harnesses with HOL Guard and run scanner checks for Codex, Claude, Cursor, Gemini, and OpenCode."
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -150,7 +150,15 @@ _GUARD_HELP_GROUPS = (
150
150
  " bootstrap Detect, install, and launch the approval center\n"
151
151
  " install Enable Guard management for a harness\n"
152
152
  " uninstall Disable Guard management for a harness\n"
153
- " update Update hol-guard in the current environment"
153
+ " update Update hol-guard in the current environment\n"
154
+ "\n"
155
+ "Command selection:\n"
156
+ " Use status for current posture and the next safe step\n"
157
+ " Use doctor for setup and runtime probes\n"
158
+ " Use diff for changed artifacts after a blocked launch\n"
159
+ " Use explain for detailed artifact evidence\n"
160
+ " Use approvals for queued decisions and receipts for audit history\n"
161
+ " Use events for the local timeline"
154
162
  )
155
163
 
156
164
 
@@ -285,37 +285,37 @@ def _render_doctor(console: Console, payload: dict[str, object]) -> None:
285
285
  )
286
286
  adapters = _coerce_dict_list(payload.get("adapters"))
287
287
  console.print(_build_harness_table(adapters))
288
- return
289
-
290
- warnings = _coerce_string_list(payload.get("warnings"))
291
- summary = Table.grid(padding=(0, 1))
292
- summary.add_row("Harness", f"[bold]{payload.get('harness', 'unknown')}[/bold]")
293
- summary.add_row("Installed", _bool_label(bool(payload.get("installed"))))
294
- summary.add_row("Command", _bool_label(bool(payload.get("command_available"))))
295
- summary.add_row("Artifacts", str(len(_coerce_dict_list(payload.get("artifacts")))))
296
- registry = payload.get("runtime_detector_registry")
297
- if isinstance(registry, dict):
298
- registry_state = "enabled" if bool(registry.get("enabled")) else "disabled"
299
- timeout_ms = registry.get("timeout_ms")
300
- summary.add_row("Detector registry", f"{registry_state}, {timeout_ms} ms")
301
- summary.add_row("Warnings", str(len(warnings)))
302
- console.print(Panel(summary, title="Guard doctor", border_style="cyan"))
303
- if warnings:
304
- warning_text = "\n".join(
305
- textwrap.fill(
306
- f"• {warning}",
307
- width=72,
308
- subsequent_indent=" ",
288
+ else:
289
+ warnings = _coerce_string_list(payload.get("warnings"))
290
+ summary = Table.grid(padding=(0, 1))
291
+ summary.add_row("Harness", f"[bold]{payload.get('harness', 'unknown')}[/bold]")
292
+ summary.add_row("Installed", _bool_label(bool(payload.get("installed"))))
293
+ summary.add_row("Command", _bool_label(bool(payload.get("command_available"))))
294
+ summary.add_row("Artifacts", str(len(_coerce_dict_list(payload.get("artifacts")))))
295
+ registry = payload.get("runtime_detector_registry")
296
+ if isinstance(registry, dict):
297
+ registry_state = "enabled" if bool(registry.get("enabled")) else "disabled"
298
+ timeout_ms = registry.get("timeout_ms")
299
+ summary.add_row("Detector registry", f"{registry_state}, {timeout_ms} ms")
300
+ summary.add_row("Warnings", str(len(warnings)))
301
+ console.print(Panel(summary, title="Guard doctor", border_style="cyan"))
302
+ if warnings:
303
+ warning_text = "\n".join(
304
+ textwrap.fill(
305
+ f"• {warning}",
306
+ width=72,
307
+ subsequent_indent=" ",
308
+ )
309
+ for warning in warnings
309
310
  )
310
- for warning in warnings
311
- )
312
- console.print(Panel(Text(warning_text), title="Attention", border_style="yellow"))
313
- runtime_probe = payload.get("runtime_probe")
314
- if isinstance(runtime_probe, dict):
315
- console.print(_build_runtime_probe_panel(runtime_probe))
316
- artifacts = _coerce_dict_list(payload.get("artifacts"))
317
- if artifacts:
318
- console.print(_build_artifact_table(artifacts))
311
+ console.print(Panel(Text(warning_text), title="Attention", border_style="yellow"))
312
+ runtime_probe = payload.get("runtime_probe")
313
+ if isinstance(runtime_probe, dict):
314
+ console.print(_build_runtime_probe_panel(runtime_probe))
315
+ artifacts = _coerce_dict_list(payload.get("artifacts"))
316
+ if artifacts:
317
+ console.print(_build_artifact_table(artifacts))
318
+ console.print(_build_diagnostic_command_panel())
319
319
 
320
320
 
321
321
  def _render_run(console: Console, payload: dict[str, object]) -> None:
@@ -481,7 +481,11 @@ def _render_advisories(console: Console, payload: dict[str, object]) -> None:
481
481
  border_style="cyan",
482
482
  )
483
483
  )
484
- table = Table(box=box.SIMPLE_HEAVY, show_header=True)
484
+ console.print(_build_advisory_table(items))
485
+
486
+
487
+ def _build_advisory_table(items: list[dict[str, object]], *, title: str | None = None) -> Table:
488
+ table = Table(title=title, box=box.SIMPLE_HEAVY, show_header=True)
485
489
  table.add_column("Publisher", style="bold")
486
490
  table.add_column("Severity")
487
491
  table.add_column("Headline")
@@ -493,7 +497,7 @@ def _render_advisories(console: Console, payload: dict[str, object]) -> None:
493
497
  str(item.get("headline") or item.get("cache_key") or "advisory"),
494
498
  str(item.get("updated_at") or "unknown"),
495
499
  )
496
- console.print(table)
500
+ return table
497
501
 
498
502
 
499
503
  def _render_events(console: Console, payload: dict[str, object]) -> None:
@@ -1077,6 +1081,40 @@ def _render_scan(console: Console, payload: dict[str, object]) -> None:
1077
1081
  _render_cisco_evidence(console, payload)
1078
1082
 
1079
1083
 
1084
+ def _render_explain(console: Console, payload: dict[str, object]) -> None:
1085
+ advisories = _coerce_dict_list(payload.get("advisories"))
1086
+ if "artifact_snapshot" in payload:
1087
+ console.print(Panel(_build_consumer_summary_table(payload), title="Path evidence", border_style="cyan"))
1088
+ _render_consumer_evidence_panels(console, payload)
1089
+ _render_cisco_evidence(console, payload)
1090
+ if advisories:
1091
+ console.print(_build_advisory_table(advisories, title="Matching advisories"))
1092
+ return
1093
+ artifact = payload.get("artifact")
1094
+ if not isinstance(artifact, dict):
1095
+ _render_fallback(console, payload)
1096
+ return
1097
+ latest_receipt = payload.get("latest_receipt")
1098
+ latest_diff = payload.get("latest_diff")
1099
+ body = Table.grid(padding=(0, 1))
1100
+ body.add_row("Artifact", str(artifact.get("artifact_name") or artifact.get("artifact_id") or "unknown"))
1101
+ body.add_row("Harness", str(artifact.get("harness") or "unknown"))
1102
+ body.add_row("Type", str(artifact.get("artifact_type") or "artifact"))
1103
+ body.add_row("Scope", str(artifact.get("source_scope") or "unknown"))
1104
+ body.add_row("Present", _bool_label(bool(artifact.get("present"))))
1105
+ if isinstance(latest_receipt, dict):
1106
+ body.add_row("Latest decision", _action_text(str(latest_receipt.get("policy_decision") or "warn")))
1107
+ body.add_row("Receipt time", str(latest_receipt.get("timestamp") or "unknown"))
1108
+ if isinstance(latest_diff, dict):
1109
+ changed_fields = ", ".join(_coerce_string_list(latest_diff.get("changed_fields"))) or "no field changes"
1110
+ body.add_row("Latest diff", changed_fields)
1111
+ body.add_row("Current hash", str(latest_diff.get("current_hash") or "unknown"))
1112
+ body.add_row("Advisories", str(len(advisories)))
1113
+ console.print(Panel(body, title="Guard artifact evidence", border_style="cyan"))
1114
+ if advisories:
1115
+ console.print(_build_advisory_table(advisories, title="Matching advisories"))
1116
+
1117
+
1080
1118
  def _render_preflight(console: Console, payload: dict[str, object]) -> None:
1081
1119
  install_verdict = payload.get("install_verdict")
1082
1120
  install_target = payload.get("install_target")
@@ -1209,6 +1247,21 @@ def _build_steps_panel(steps: list[dict[str, object]]) -> Panel:
1209
1247
  return Panel("\n\n".join(lines), title="Next steps", border_style="green")
1210
1248
 
1211
1249
 
1250
+ def _build_diagnostic_command_panel() -> Panel:
1251
+ return Panel(
1252
+ "\n".join(
1253
+ (
1254
+ "Use status for current posture: hol-guard status",
1255
+ "Use doctor for setup and runtime probes: hol-guard doctor <harness>",
1256
+ "Use diff for changed artifacts: hol-guard diff <harness>",
1257
+ "Use events for the local timeline: hol-guard events",
1258
+ )
1259
+ ),
1260
+ title="Which diagnostic command?",
1261
+ border_style="blue",
1262
+ )
1263
+
1264
+
1212
1265
  def _render_harness_detail(console: Console, detection: dict[str, object]) -> None:
1213
1266
  artifacts = _coerce_dict_list(detection.get("artifacts"))
1214
1267
  warnings = _coerce_string_list(detection.get("warnings"))
@@ -1308,13 +1361,7 @@ def _build_run_steps(payload: dict[str, object], *, blocked: bool, dry_run: bool
1308
1361
  approvals_command = payload.get("approvals_command")
1309
1362
  if blocked and dry_run:
1310
1363
  review_command = (
1311
- str(approvals_command)
1312
- if approval_center_url and isinstance(approvals_command, str) and approvals_command
1313
- else "hol-guard approvals"
1314
- if approval_center_url
1315
- else str(rerun_command)
1316
- if isinstance(rerun_command, str) and rerun_command
1317
- else f"hol-guard run {harness}"
1364
+ str(rerun_command) if isinstance(rerun_command, str) and rerun_command else f"hol-guard run {harness}"
1318
1365
  )
1319
1366
  inspect_command = (
1320
1367
  str(diff_command) if isinstance(diff_command, str) and diff_command else f"hol-guard diff {harness}"
@@ -1324,12 +1371,29 @@ def _build_run_steps(payload: dict[str, object], *, blocked: bool, dry_run: bool
1324
1371
  if isinstance(review_hint, str) and review_hint
1325
1372
  else "Rerun without --dry-run to review the full blocker set and continue into the harness launch."
1326
1373
  )
1327
- return [
1374
+ steps = [
1328
1375
  {
1329
1376
  "title": "Resolve the blocked launch",
1330
1377
  "command": review_command,
1331
1378
  "detail": review_detail,
1332
1379
  },
1380
+ ]
1381
+ if approval_center_url:
1382
+ approval_command = (
1383
+ str(approvals_command)
1384
+ if isinstance(approvals_command, str) and approvals_command
1385
+ else "hol-guard approvals"
1386
+ )
1387
+ steps.append(
1388
+ {
1389
+ "title": "Open the approvals queue",
1390
+ "command": approval_command,
1391
+ "detail": (
1392
+ "Review any queued approval requests after the prompt appears, then retry the guarded command."
1393
+ ),
1394
+ }
1395
+ )
1396
+ steps.append(
1333
1397
  {
1334
1398
  "title": "Inspect only the changed config entries (optional)",
1335
1399
  "command": inspect_command,
@@ -1338,17 +1402,19 @@ def _build_run_steps(payload: dict[str, object], *, blocked: bool, dry_run: bool
1338
1402
  "Guard still needs you to review."
1339
1403
  ),
1340
1404
  },
1341
- ]
1342
- if blocked and isinstance(review_hint, str) and review_hint:
1343
- command = (
1344
- str(approvals_command)
1345
- if approval_center_url and isinstance(approvals_command, str) and approvals_command
1346
- else "hol-guard approvals"
1347
- if approval_center_url
1348
- else str(rerun_command)
1349
- if isinstance(rerun_command, str) and rerun_command
1350
- else f"hol-guard run {harness}"
1351
1405
  )
1406
+ return steps
1407
+ if blocked and isinstance(review_hint, str) and review_hint:
1408
+ if approval_center_url:
1409
+ command = (
1410
+ str(approvals_command)
1411
+ if isinstance(approvals_command, str) and approvals_command
1412
+ else "hol-guard approvals"
1413
+ )
1414
+ elif isinstance(rerun_command, str) and rerun_command:
1415
+ command = str(rerun_command)
1416
+ else:
1417
+ command = f"hol-guard run {harness}"
1352
1418
  return [{"title": "Resolve the blocked launch", "command": command, "detail": review_hint}]
1353
1419
  if dry_run:
1354
1420
  launch_command = (
@@ -1749,5 +1815,5 @@ _RENDERERS: dict[str, Any] = {
1749
1815
  "protect": _render_protect,
1750
1816
  "preflight": _render_preflight,
1751
1817
  "scan": _render_scan,
1752
- "explain": _render_scan,
1818
+ "explain": _render_explain,
1753
1819
  }
@@ -1,3 +1,3 @@
1
1
  """Single source of truth for tool version."""
2
2
 
3
- __version__ = "2.0.108"
3
+ __version__ = "2.0.109"
@@ -162,6 +162,42 @@ class TestMain:
162
162
  rc = main(["/nonexistent/path/that/does/not/exist"])
163
163
  assert rc == 1
164
164
 
165
+ def test_scan_and_lint_reject_nonexistent_directory_consistently(self, capsys):
166
+ scan_rc = main(["scan", str(NONEXISTENT_PLUGIN_DIR), "--format", "json"])
167
+ scan_captured = capsys.readouterr()
168
+
169
+ lint_rc = main(["lint", str(NONEXISTENT_PLUGIN_DIR), "--format", "json"])
170
+ lint_captured = capsys.readouterr()
171
+
172
+ expected = f'Error: "{NONEXISTENT_PLUGIN_DIR}" is not a directory.'
173
+ assert scan_rc == 1
174
+ assert lint_rc == 1
175
+ assert expected in scan_captured.err
176
+ assert expected in lint_captured.err
177
+ assert scan_captured.out == ""
178
+ assert lint_captured.out == ""
179
+
180
+ def test_scanner_human_outputs_stay_summary_first(self, capsys):
181
+ scan_rc = main(["scan", str(FIXTURES / "good-plugin")])
182
+ scan_output = capsys.readouterr().out
183
+
184
+ lint_rc = main(["lint", str(FIXTURES / "good-plugin")])
185
+ lint_output = capsys.readouterr().out
186
+
187
+ verify_rc = main(["verify", str(FIXTURES / "good-plugin")])
188
+ verify_output = capsys.readouterr().out
189
+
190
+ assert scan_rc == 0
191
+ assert lint_rc == 0
192
+ assert verify_rc == 0
193
+ assert "Plugin Scanner" in scan_output
194
+ assert "Final Score" in scan_output
195
+ assert "Lint profile" in lint_output
196
+ assert "Verification: PASS" in verify_output
197
+ assert '"schema_version"' not in scan_output
198
+ assert '"policy_pass"' not in lint_output
199
+ assert '"verify_pass"' not in verify_output
200
+
165
201
  def test_invalid_top_level_command_suggests_closest_match(self, capsys):
166
202
  with pytest.raises(SystemExit) as exc_info:
167
203
  main(["verfy"])