plugin-scanner 2.0.65__tar.gz → 2.0.66__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 (320) hide show
  1. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/PKG-INFO +1 -1
  2. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/pyproject.toml +1 -1
  3. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/pyproject.toml.bak +1 -1
  4. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/claude_code.py +1 -3
  5. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/commands.py +99 -3
  6. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/render.py +18 -0
  7. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/store.py +27 -0
  8. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/version.py +1 -1
  9. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_approvals.py +86 -0
  10. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_claude_adapter.py +5 -15
  11. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_cli.py +3 -3
  12. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_runtime.py +72 -21
  13. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_surface_server.py +1 -9
  14. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.clusterfuzzlite/Dockerfile +0 -0
  15. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.clusterfuzzlite/build.sh +0 -0
  16. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.clusterfuzzlite/project.yaml +0 -0
  17. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.clusterfuzzlite/requirements-atheris.txt +0 -0
  18. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.dockerignore +0 -0
  19. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.github/CODEOWNERS +0 -0
  20. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  21. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  22. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
  23. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.github/dependabot.yml +0 -0
  24. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.github/workflows/ci.yml +0 -0
  25. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.github/workflows/codeql.yml +0 -0
  26. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.github/workflows/dependabot-uv-lock.yml +0 -0
  27. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.github/workflows/fuzz.yml +0 -0
  28. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.github/workflows/harness-smoke.yml +0 -0
  29. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.github/workflows/publish.yml +0 -0
  30. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.github/workflows/scorecard.yml +0 -0
  31. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.gitignore +0 -0
  32. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.pre-commit-hooks.yaml +0 -0
  33. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/CONTRIBUTING.md +0 -0
  34. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/Dockerfile +0 -0
  35. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/LICENSE +0 -0
  36. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/README.md +0 -0
  37. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/SECURITY.md +0 -0
  38. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/index.html +0 -0
  39. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/package.json +0 -0
  40. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/pnpm-lock.yaml +0 -0
  41. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/public/brand/Logo_Whole.png +0 -0
  42. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/app.tsx +0 -0
  43. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/approval-center-layout.tsx +0 -0
  44. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/approval-center-primitives.tsx +0 -0
  45. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/approval-center-utils.ts +0 -0
  46. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/fleet-workspace.tsx +0 -0
  47. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/guard-api.ts +0 -0
  48. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/guard-demo.ts +0 -0
  49. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/guard-types.ts +0 -0
  50. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/main.tsx +0 -0
  51. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/receipts-workspace.tsx +0 -0
  52. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/runtime-overview.tsx +0 -0
  53. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/styles.css +0 -0
  54. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/vite-env.d.ts +0 -0
  55. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/tsconfig.json +0 -0
  56. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/vite.config.ts +0 -0
  57. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/docker-requirements.txt +0 -0
  58. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/docs/guard/approval-audit.md +0 -0
  59. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/docs/guard/architecture.md +0 -0
  60. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/docs/guard/get-started.md +0 -0
  61. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/docs/guard/harness-support.md +0 -0
  62. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/docs/guard/local-vs-cloud.md +0 -0
  63. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/docs/guard/testing-matrix.md +0 -0
  64. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/docs/trust/mcp-trust-draft.md +0 -0
  65. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/docs/trust/plugin-trust-draft.md +0 -0
  66. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/docs/trust/skill-trust-local.md +0 -0
  67. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/fuzzers/manifest_fuzzer.py +0 -0
  68. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/requirements.txt +0 -0
  69. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/schemas/plugin-quality.v1.json +0 -0
  70. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/schemas/scan-result.v1.json +0 -0
  71. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/schemas/verify-result.v1.json +0 -0
  72. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/__init__.py +0 -0
  73. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/action_runner.py +0 -0
  74. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/argparse_utils.py +0 -0
  75. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/__init__.py +0 -0
  76. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
  77. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/claude.py +0 -0
  78. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
  79. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
  80. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/gemini.py +0 -0
  81. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/manifest.py +0 -0
  82. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
  83. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
  84. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
  85. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/opencode.py +0 -0
  86. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
  87. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/security.py +0 -0
  88. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
  89. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/cli.py +0 -0
  90. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/cli_ui.py +0 -0
  91. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/config.py +0 -0
  92. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
  93. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
  94. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
  95. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
  96. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
  97. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
  98. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
  99. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
  100. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
  101. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/github_reporting.py +0 -0
  102. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/__init__.py +0 -0
  103. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
  104. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
  105. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
  106. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
  107. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
  108. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
  109. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
  110. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
  111. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
  112. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
  113. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
  114. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/approvals.py +0 -0
  115. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
  116. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
  117. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
  118. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
  119. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
  120. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
  121. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
  122. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
  123. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
  124. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
  125. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
  126. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/config.py +0 -0
  127. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
  128. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
  129. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
  130. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
  131. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
  132. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
  133. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
  134. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
  135. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
  136. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
  137. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
  138. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/incident.py +0 -0
  139. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/launcher.py +0 -0
  140. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
  141. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/models.py +0 -0
  142. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
  143. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
  144. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/protect.py +0 -0
  145. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
  146. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
  147. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
  148. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
  149. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
  150. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
  151. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/risk.py +0 -0
  152. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
  153. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/runtime/runner.py +0 -0
  154. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
  155. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
  156. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
  157. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
  158. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
  159. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
  160. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/shims.py +0 -0
  161. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
  162. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
  163. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/types.py +0 -0
  164. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
  165. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
  166. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
  167. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/lint_fixes.py +0 -0
  168. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/marketplace_support.py +0 -0
  169. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/models.py +0 -0
  170. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/path_support.py +0 -0
  171. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/policy.py +0 -0
  172. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/quality_artifact.py +0 -0
  173. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/repo_detect.py +0 -0
  174. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/reporting.py +0 -0
  175. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/rules/__init__.py +0 -0
  176. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/rules/registry.py +0 -0
  177. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/rules/specs.py +0 -0
  178. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/scanner.py +0 -0
  179. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/submission.py +0 -0
  180. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/suppressions.py +0 -0
  181. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
  182. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/trust_helpers.py +0 -0
  183. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
  184. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/trust_models.py +0 -0
  185. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
  186. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/trust_scoring.py +0 -0
  187. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
  188. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/trust_specs.py +0 -0
  189. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/verification.py +0 -0
  190. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/__init__.py +0 -0
  191. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/conftest.py +0 -0
  192. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/__init__.py +0 -0
  193. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
  194. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/bad-plugin/.mcp.json +0 -0
  195. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/bad-plugin/secrets.js +0 -0
  196. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
  197. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
  198. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/claude-plugin-good/README.md +0 -0
  199. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
  200. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
  201. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
  202. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/code-quality-bad/evil.js +0 -0
  203. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/code-quality-bad/inject.js +0 -0
  204. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
  205. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
  206. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/gemini-extension-good/README.md +0 -0
  207. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
  208. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
  209. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
  210. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
  211. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/.codexignore +0 -0
  212. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/LICENSE +0 -0
  213. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/README.md +0 -0
  214. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/SECURITY.md +0 -0
  215. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
  216. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
  217. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
  218. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
  219. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
  220. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
  221. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
  222. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
  223. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
  224. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
  225. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
  226. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
  227. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
  228. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
  229. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
  230. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
  231. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
  232. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
  233. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
  234. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/mcp-canary-server.py +0 -0
  235. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
  236. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
  237. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/mit-license/LICENSE +0 -0
  238. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
  239. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
  240. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
  241. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
  242. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
  243. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
  244. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
  245. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
  246. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
  247. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
  248. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
  249. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
  250. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
  251. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
  252. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
  253. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
  254. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
  255. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
  256. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/opencode-good/LICENSE +0 -0
  257. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/opencode-good/README.md +0 -0
  258. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/opencode-good/SECURITY.md +0 -0
  259. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
  260. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
  261. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
  262. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
  263. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
  264. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
  265. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/with-marketplace/marketplace.json +0 -0
  266. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test-trust-scoring.py +0 -0
  267. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test-trust-specs.py +0 -0
  268. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_action_runner.py +0 -0
  269. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_best_practices.py +0 -0
  270. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_cisco_install_surfaces.py +0 -0
  271. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_cli.py +0 -0
  272. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_code_quality.py +0 -0
  273. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_config.py +0 -0
  274. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_coverage_remaining.py +0 -0
  275. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_ecosystems.py +0 -0
  276. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_edge_cases.py +0 -0
  277. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_final_coverage.py +0 -0
  278. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_bootstrap.py +0 -0
  279. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_capabilities.py +0 -0
  280. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_codex_e2e.py +0 -0
  281. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_codex_install.py +0 -0
  282. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_codex_proxy.py +0 -0
  283. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_config_paths.py +0 -0
  284. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_connect_flow.py +0 -0
  285. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_consumer_mode.py +0 -0
  286. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_copilot_adapter.py +0 -0
  287. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_copilot_proxy.py +0 -0
  288. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_daemon_manager.py +0 -0
  289. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_event_schema_v1.py +0 -0
  290. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_events.py +0 -0
  291. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_launch_env.py +0 -0
  292. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_opencode_proxy.py +0 -0
  293. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_product_flow.py +0 -0
  294. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_protect.py +0 -0
  295. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_render.py +0 -0
  296. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_risk.py +0 -0
  297. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_store_migrations.py +0 -0
  298. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_verdicts.py +0 -0
  299. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_hermes_adapter.py +0 -0
  300. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_integration.py +0 -0
  301. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_lint_fixes.py +0 -0
  302. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_live_cisco_smoke.py +0 -0
  303. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_manifest.py +0 -0
  304. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_marketplace.py +0 -0
  305. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_mcp_security.py +0 -0
  306. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_operational_security.py +0 -0
  307. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_policy.py +0 -0
  308. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_quality_artifact.py +0 -0
  309. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_rule_registry.py +0 -0
  310. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_scanner.py +0 -0
  311. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_schema_contracts.py +0 -0
  312. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_security.py +0 -0
  313. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_security_ops.py +0 -0
  314. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_skill_security.py +0 -0
  315. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_submission.py +0 -0
  316. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_trust_scoring.py +0 -0
  317. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_trust_specs.py +0 -0
  318. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_verification.py +0 -0
  319. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_versioning.py +0 -0
  320. {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plugin-scanner
3
- Version: 2.0.65
3
+ Version: 2.0.66
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "plugin-scanner"
7
- version = "2.0.65"
7
+ version = "2.0.66"
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.65"
7
+ version = "2.0.66"
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"
@@ -23,7 +23,6 @@ CLAUDE_GUARD_POST_TOOL_MATCHER = f"{CLAUDE_GUARD_TOOL_MATCHER}|AskUserQuestion"
23
23
  CLAUDE_GUARD_NOTIFICATION_MATCHER = "permission_prompt"
24
24
  CLAUDE_GUARD_SESSION_START_MATCHERS = ("startup", "resume", "clear", "compact")
25
25
  CLAUDE_GUARD_TOOL_TIMEOUT_SECONDS = 30
26
- CLAUDE_GUARD_PROMPT_TIMEOUT_SECONDS = 20
27
26
  CLAUDE_GUARD_NOTIFICATION_TIMEOUT_SECONDS = 10
28
27
  CLAUDE_GUARD_SESSION_START_TIMEOUT_SECONDS = 10
29
28
  CLAUDE_GUARD_STOP_TIMEOUT_SECONDS = 10
@@ -47,7 +46,6 @@ def _sync_runtime_hook_groups(hooks: dict[str, object], hook_command: str) -> No
47
46
  ("PreToolUse", CLAUDE_GUARD_TOOL_MATCHER, CLAUDE_GUARD_TOOL_TIMEOUT_SECONDS),
48
47
  ("PermissionRequest", CLAUDE_GUARD_TOOL_MATCHER, CLAUDE_GUARD_NOTIFICATION_TIMEOUT_SECONDS),
49
48
  ("PostToolUse", CLAUDE_GUARD_POST_TOOL_MATCHER, CLAUDE_GUARD_TOOL_TIMEOUT_SECONDS),
50
- ("UserPromptSubmit", None, CLAUDE_GUARD_PROMPT_TIMEOUT_SECONDS),
51
49
  ("Notification", CLAUDE_GUARD_NOTIFICATION_MATCHER, CLAUDE_GUARD_NOTIFICATION_TIMEOUT_SECONDS),
52
50
  ("Stop", None, CLAUDE_GUARD_STOP_TIMEOUT_SECONDS),
53
51
  ):
@@ -60,7 +58,7 @@ def _sync_runtime_hook_groups(hooks: dict[str, object], hook_command: str) -> No
60
58
 
61
59
 
62
60
  def _remove_unsupported_guard_hook_groups(hooks: dict[str, object]) -> None:
63
- for key in ("PermissionDenied",):
61
+ for key in ("PermissionDenied", "UserPromptSubmit"):
64
62
  entries = hooks.get(key)
65
63
  if not isinstance(entries, list):
66
64
  continue
@@ -311,8 +311,15 @@ def _configure_guard_parser(guard_parser: argparse.ArgumentParser) -> None:
311
311
  policy_parser.add_argument("--json", action="store_true")
312
312
  policy_parser.set_defaults(policy_action=action)
313
313
 
314
- policies_parser = guard_subparsers.add_parser("policies", help="List stored Guard policy decisions")
314
+ policies_parser = guard_subparsers.add_parser("policies", help="List or clear stored Guard policy decisions")
315
+ policies_parser.add_argument("policies_command", nargs="?", choices=("clear",))
315
316
  policies_parser.add_argument("--harness")
317
+ policies_parser.add_argument("--source")
318
+ policies_parser.add_argument(
319
+ "--all",
320
+ action="store_true",
321
+ help="Clear decisions across every harness; cannot be combined with --harness",
322
+ )
316
323
  _add_guard_common_args(policies_parser)
317
324
  policies_parser.add_argument("--json", action="store_true")
318
325
 
@@ -775,6 +782,46 @@ def run_guard_command(
775
782
  return 0
776
783
 
777
784
  if args.guard_command == "policies":
785
+ if getattr(args, "policies_command", None) == "clear":
786
+ harness = getattr(args, "harness", None)
787
+ clear_all = bool(getattr(args, "all", False))
788
+ if clear_all and harness is not None:
789
+ _emit(
790
+ "policies",
791
+ {
792
+ "error": "Choose either --all or --harness <name> when clearing Guard policy decisions.",
793
+ "cleared": 0,
794
+ "harness": harness,
795
+ "source": getattr(args, "source", None),
796
+ },
797
+ getattr(args, "json", False),
798
+ )
799
+ return 2
800
+ if not clear_all and harness is None:
801
+ _emit(
802
+ "policies",
803
+ {
804
+ "error": "Choose --harness <name> or --all when clearing Guard policy decisions.",
805
+ "cleared": 0,
806
+ },
807
+ getattr(args, "json", False),
808
+ )
809
+ return 2
810
+ cleared = store.clear_policy_decisions(
811
+ None if clear_all else harness,
812
+ getattr(args, "source", None),
813
+ )
814
+ _emit(
815
+ "policies",
816
+ {
817
+ "generated_at": _now(),
818
+ "cleared": cleared,
819
+ "harness": None if clear_all else harness,
820
+ "source": getattr(args, "source", None),
821
+ },
822
+ getattr(args, "json", False),
823
+ )
824
+ return 0
778
825
  policy_items = store.list_policy_decisions(getattr(args, "harness", None))
779
826
  items = _filter_policy_items(policy_items, active_only=True)
780
827
  _emit("policies", {"generated_at": _now(), "items": items}, getattr(args, "json", False))
@@ -1233,10 +1280,13 @@ def run_guard_command(
1233
1280
  )
1234
1281
  return 0
1235
1282
  if _canonical_harness_name(args.harness) == "claude-code" and _hook_event_name(payload) == "Stop":
1236
- denied = _persist_claude_pending_permission_denials(store, payload)
1283
+ discarded = _discard_claude_pending_permissions(store, payload)
1237
1284
  store.add_event(
1238
1285
  "claude/turn_stop",
1239
- {"session_id": payload.get("session_id"), "saved_denials": denied},
1286
+ {
1287
+ "session_id": payload.get("session_id"),
1288
+ "discarded_pending_permissions": discarded,
1289
+ },
1240
1290
  _now(),
1241
1291
  )
1242
1292
  return 0
@@ -1371,6 +1421,14 @@ def run_guard_command(
1371
1421
  artifact=runtime_artifact,
1372
1422
  artifact_hash=runtime_artifact_hash,
1373
1423
  )
1424
+ if _should_allow_claude_user_prompt_submit_without_output(
1425
+ args,
1426
+ event_name=event_name,
1427
+ policy_action=policy_action,
1428
+ artifact=runtime_artifact,
1429
+ output_stream=output_stream,
1430
+ ):
1431
+ return 0
1374
1432
  if _should_emit_copilot_hook_response(args):
1375
1433
  _emit_copilot_hook_response(
1376
1434
  policy_action=policy_action,
@@ -1724,6 +1782,23 @@ def _should_emit_prequeue_native_hook_response(
1724
1782
  return output_stream is not None
1725
1783
 
1726
1784
 
1785
+ def _should_allow_claude_user_prompt_submit_without_output(
1786
+ args: argparse.Namespace,
1787
+ *,
1788
+ event_name: str,
1789
+ policy_action: str,
1790
+ artifact: GuardArtifact,
1791
+ output_stream: TextIO | None,
1792
+ ) -> bool:
1793
+ return (
1794
+ _canonical_harness_name(args.harness) == "claude-code"
1795
+ and event_name == "UserPromptSubmit"
1796
+ and policy_action == "require-reapproval"
1797
+ and not _prompt_requires_hard_block(artifact)
1798
+ and (not getattr(args, "json", False) or output_stream is not None)
1799
+ )
1800
+
1801
+
1727
1802
  def _emit_claude_permission_request_passthrough(*, output_stream: TextIO | None = None) -> None:
1728
1803
  if output_stream is not None:
1729
1804
  output_stream.write("")
@@ -2044,6 +2119,27 @@ def _persist_claude_native_permission_for_runtime_artifact(
2044
2119
  return True
2045
2120
 
2046
2121
 
2122
+ def _discard_claude_pending_permissions(store: GuardStore, payload: dict[str, object]) -> int:
2123
+ session_id = _optional_string(payload.get("session_id"))
2124
+ if session_id is None:
2125
+ return 0
2126
+ index_key = _claude_pending_permission_index_key(session_id)
2127
+ try:
2128
+ index_payload = store.get_sync_payload(index_key)
2129
+ except (OSError, sqlite3.Error):
2130
+ return 0
2131
+ if not isinstance(index_payload, list):
2132
+ return 0
2133
+ pending_keys = [str(item) for item in index_payload]
2134
+ if not pending_keys:
2135
+ return 0
2136
+ try:
2137
+ store.delete_sync_payloads([*pending_keys, index_key])
2138
+ except (OSError, sqlite3.Error):
2139
+ return 0
2140
+ return len(pending_keys)
2141
+
2142
+
2047
2143
  def _persist_claude_pending_permission_denials(store: GuardStore, payload: dict[str, object]) -> int:
2048
2144
  session_id = _optional_string(payload.get("session_id"))
2049
2145
  if session_id is None:
@@ -295,6 +295,24 @@ def _render_inventory(console: Console, payload: dict[str, object]) -> None:
295
295
 
296
296
 
297
297
  def _render_policies(console: Console, payload: dict[str, object]) -> None:
298
+ if "cleared" in payload or "error" in payload:
299
+ error = payload.get("error")
300
+ cleared = int(payload.get("cleared", 0) or 0)
301
+ scope = str(payload.get("harness") or "all harnesses")
302
+ source = payload.get("source")
303
+ body = Table.grid(padding=(0, 1))
304
+ body.add_row("Outcome", str(error) if error else f"cleared {cleared} decision{'s' if cleared != 1 else ''}")
305
+ body.add_row("Harness", scope)
306
+ if source:
307
+ body.add_row("Source", str(source))
308
+ console.print(
309
+ Panel(
310
+ body,
311
+ title="Guard policy clear",
312
+ border_style="red" if error else "green",
313
+ )
314
+ )
315
+ return
298
316
  items = _coerce_dict_list(payload.get("items"))
299
317
  console.print(
300
318
  Panel.fit(
@@ -1557,6 +1557,22 @@ class GuardStore:
1557
1557
  for row in rows
1558
1558
  ]
1559
1559
 
1560
+ def clear_policy_decisions(self, harness: str | None = None, source: str | None = None) -> int:
1561
+ conditions: list[str] = []
1562
+ params: list[object] = []
1563
+ if harness is not None:
1564
+ conditions.append("harness = ?")
1565
+ params.append(harness)
1566
+ if source is not None:
1567
+ conditions.append("source = ?")
1568
+ params.append(source)
1569
+ query = "delete from policy_decisions"
1570
+ if conditions:
1571
+ query += " where " + " and ".join(conditions)
1572
+ with self._connect() as connection:
1573
+ cursor = connection.execute(query, tuple(params))
1574
+ return int(cursor.rowcount if cursor.rowcount is not None else 0)
1575
+
1560
1576
  def get_latest_diff(self, harness: str, artifact_id: str) -> dict[str, object] | None:
1561
1577
  with self._connect() as connection:
1562
1578
  row = connection.execute(
@@ -1727,6 +1743,17 @@ class GuardStore:
1727
1743
  (state_key,),
1728
1744
  )
1729
1745
 
1746
+ def delete_sync_payloads(self, state_keys: list[str]) -> int:
1747
+ if not state_keys:
1748
+ return 0
1749
+ placeholders = ",".join("?" for _ in state_keys)
1750
+ with self._connect() as connection:
1751
+ cursor = connection.execute(
1752
+ f"delete from sync_state where state_key in ({placeholders})",
1753
+ tuple(state_keys),
1754
+ )
1755
+ return int(cursor.rowcount if cursor.rowcount is not None else 0)
1756
+
1730
1757
  def add_guard_event_v1(self, event: GuardEventV1) -> None:
1731
1758
  with self._connect() as connection:
1732
1759
  self._add_guard_event_v1(connection, event)
@@ -1,3 +1,3 @@
1
1
  """Single source of truth for tool version."""
2
2
 
3
- __version__ = "2.0.65"
3
+ __version__ = "2.0.66"
@@ -1598,6 +1598,92 @@ class TestGuardApprovals:
1598
1598
  assert approve_output["resolved"] is True
1599
1599
  assert store.resolve_policy("codex", "codex:project:workspace_skill", "hash-789") == "allow"
1600
1600
 
1601
+ def test_guard_policies_cli_clears_local_decisions_for_harness(self, tmp_path, capsys):
1602
+ home_dir = tmp_path / "home"
1603
+ store = GuardStore(home_dir)
1604
+ store.upsert_policy(
1605
+ PolicyDecision(
1606
+ harness="claude-code",
1607
+ scope="artifact",
1608
+ action="block",
1609
+ artifact_id="claude-code:runtime:file-read:.npmrc",
1610
+ artifact_hash="hash-npmrc",
1611
+ reason="blocked during local test",
1612
+ source="claude-ask-user-question",
1613
+ ),
1614
+ "2026-04-23T00:00:00+00:00",
1615
+ )
1616
+ store.upsert_policy(
1617
+ PolicyDecision(
1618
+ harness="codex",
1619
+ scope="harness",
1620
+ action="allow",
1621
+ reason="keep codex decisions",
1622
+ source="manual",
1623
+ ),
1624
+ "2026-04-23T00:00:00+00:00",
1625
+ )
1626
+
1627
+ rc = main(["guard", "policies", "clear", "--home", str(home_dir), "--harness", "claude-code", "--json"])
1628
+ output = json.loads(capsys.readouterr().out)
1629
+
1630
+ assert rc == 0
1631
+ assert output["cleared"] == 1
1632
+ assert store.list_policy_decisions("claude-code") == []
1633
+ assert len(store.list_policy_decisions("codex")) == 1
1634
+
1635
+ def test_guard_policies_clear_renders_non_json_result(self, tmp_path, capsys):
1636
+ home_dir = tmp_path / "home"
1637
+ store = GuardStore(home_dir)
1638
+ store.upsert_policy(
1639
+ PolicyDecision(
1640
+ harness="claude-code",
1641
+ scope="artifact",
1642
+ action="block",
1643
+ artifact_id="claude-code:runtime:file-read:.npmrc",
1644
+ artifact_hash="hash-npmrc",
1645
+ reason="blocked during local test",
1646
+ source="claude-ask-user-question",
1647
+ ),
1648
+ "2026-04-23T00:00:00+00:00",
1649
+ )
1650
+
1651
+ rc = main(["guard", "policies", "clear", "--home", str(home_dir), "--harness", "claude-code"])
1652
+ output = capsys.readouterr().out
1653
+
1654
+ assert rc == 0
1655
+ assert "Guard policy clear" in output
1656
+ assert "cleared 1 decision" in output
1657
+ assert store.list_policy_decisions("claude-code") == []
1658
+
1659
+ def test_guard_policies_clear_renders_non_json_validation_error(self, tmp_path, capsys):
1660
+ rc = main(["guard", "policies", "clear", "--home", str(tmp_path / "home")])
1661
+ output = capsys.readouterr().out
1662
+
1663
+ assert rc == 2
1664
+ assert "Guard policy clear" in output
1665
+ assert "Choose --harness <name> or --all" in output
1666
+
1667
+ def test_guard_policies_clear_rejects_all_with_harness(self, tmp_path, capsys):
1668
+ rc = main(
1669
+ [
1670
+ "guard",
1671
+ "policies",
1672
+ "clear",
1673
+ "--home",
1674
+ str(tmp_path / "home"),
1675
+ "--all",
1676
+ "--harness",
1677
+ "claude-code",
1678
+ "--json",
1679
+ ]
1680
+ )
1681
+ output = json.loads(capsys.readouterr().out)
1682
+
1683
+ assert rc == 2
1684
+ assert output["cleared"] == 0
1685
+ assert "Choose either --all or --harness <name>" in output["error"]
1686
+
1601
1687
  def test_guard_bridge_resolves_requests_against_guard_daemon_api(self, tmp_path, monkeypatch):
1602
1688
  store = GuardStore(tmp_path / "guard-home")
1603
1689
  bridge = GuardBridge(
@@ -47,8 +47,8 @@ def _runtime_hook_handlers(payload: dict[str, object]) -> list[dict[str, object]
47
47
  hooks = payload["hooks"]
48
48
  assert isinstance(hooks, dict)
49
49
  handlers: list[dict[str, object]] = []
50
- for key in ("PreToolUse", "PermissionRequest", "PostToolUse", "UserPromptSubmit", "Notification", "Stop"):
51
- entries = hooks[key]
50
+ for key in ("PreToolUse", "PermissionRequest", "PostToolUse", "Notification", "Stop"):
51
+ entries = hooks.get(key, [])
52
52
  assert isinstance(entries, list)
53
53
  for entry in entries:
54
54
  assert isinstance(entry, dict)
@@ -146,7 +146,6 @@ def test_claude_install_writes_session_start_and_command_hook_schema_and_is_idem
146
146
  pre_tool_use = payload["hooks"]["PreToolUse"]
147
147
  permission_request = payload["hooks"]["PermissionRequest"]
148
148
  post_tool_use = payload["hooks"]["PostToolUse"]
149
- prompt_submit = payload["hooks"]["UserPromptSubmit"]
150
149
  notification = payload["hooks"]["Notification"]
151
150
  stop = payload["hooks"]["Stop"]
152
151
  assert len(session_start) == 4
@@ -165,9 +164,7 @@ def test_claude_install_writes_session_start_and_command_hook_schema_and_is_idem
165
164
  assert len(post_tool_use) == 1
166
165
  assert post_tool_use[0]["matcher"] == "Bash|Read|Write|Edit|MultiEdit|WebFetch|WebSearch|mcp__.*|AskUserQuestion"
167
166
  assert post_tool_use[0]["hooks"][0]["type"] == "command"
168
- assert len(prompt_submit) == 1
169
- assert "matcher" not in prompt_submit[0]
170
- assert prompt_submit[0]["hooks"][0]["type"] == "command"
167
+ assert payload["hooks"].get("UserPromptSubmit", []) == []
171
168
  assert len(notification) == 1
172
169
  assert notification[0]["matcher"] == "permission_prompt"
173
170
  assert notification[0]["hooks"][0]["type"] == "command"
@@ -255,8 +252,8 @@ def test_claude_install_replaces_legacy_http_guard_hooks(tmp_path):
255
252
  "command",
256
253
  "command",
257
254
  "command",
258
- "command",
259
255
  ]
256
+ assert payload["hooks"].get("UserPromptSubmit", []) == []
260
257
  assert all(CLAUDE_GUARD_DAEMON_HOOK_MARKER in str(handler.get("command", "")) for handler in installed_handlers)
261
258
  assert all("url" not in handler for handler in installed_handlers)
262
259
 
@@ -397,14 +394,7 @@ def test_claude_daemon_hook_command_falls_back_without_blocking_prompt_on_daemon
397
394
  )
398
395
  assert result.returncode == 0
399
396
  assert result.stderr == ""
400
- payload = json.loads(result.stdout)
401
- assert payload["hookSpecificOutput"]["hookEventName"] == "UserPromptSubmit"
402
- assert "Do not ask for approval at the prompt stage" in payload["hookSpecificOutput"]["additionalContext"]
403
- assert (
404
- "route that concrete action into a HOL Guard approval question"
405
- in payload["hookSpecificOutput"]["additionalContext"]
406
- )
407
- assert "Keep blocked" in payload["hookSpecificOutput"]["additionalContext"]
397
+ assert result.stdout == ""
408
398
 
409
399
 
410
400
  def test_claude_daemon_hook_command_falls_back_to_native_ask_on_daemon_miss(tmp_path):
@@ -2128,7 +2128,7 @@ args = ["workspace-skill.js", "--changed"]
2128
2128
  assert install_settings_payload["hooks"]["PreToolUse"][0]["hooks"][0]["type"] == "command"
2129
2129
  assert install_settings_payload["hooks"]["PreToolUse"][0]["hooks"][0]["command"] == expected_hook_command
2130
2130
  assert "url" not in install_settings_payload["hooks"]["PreToolUse"][0]["hooks"][0]
2131
- assert install_settings_payload["hooks"]["UserPromptSubmit"][0]["hooks"][0]["type"] == "command"
2131
+ assert install_settings_payload["hooks"].get("UserPromptSubmit", []) == []
2132
2132
  assert install_settings_payload["hooks"]["Notification"][0]["matcher"] == "permission_prompt"
2133
2133
  assert install_settings_payload["hooks"]["Notification"][0]["hooks"][0]["type"] == "command"
2134
2134
  assert install_settings_payload["hooks"]["Notification"][0]["hooks"][0]["command"] == expected_hook_command
@@ -2226,7 +2226,7 @@ args = ["workspace-skill.js", "--changed"]
2226
2226
  assert uninstall_rc == 0
2227
2227
  assert output["managed_install"]["active"] is False
2228
2228
  assert payload["hooks"]["PreToolUse"] == []
2229
- assert payload["hooks"]["UserPromptSubmit"] == []
2229
+ assert payload["hooks"].get("UserPromptSubmit", []) == []
2230
2230
  assert payload["hooks"]["Notification"] == []
2231
2231
  assert payload["hooks"]["Stop"] == []
2232
2232
 
@@ -2357,7 +2357,7 @@ args = ["workspace-skill.js", "--changed"]
2357
2357
  assert len(payload["hooks"]["SessionStart"]) == 4
2358
2358
  assert len(payload["hooks"]["PreToolUse"]) == 1
2359
2359
  assert len(payload["hooks"]["PostToolUse"]) == 1
2360
- assert len(payload["hooks"]["UserPromptSubmit"]) == 1
2360
+ assert payload["hooks"].get("UserPromptSubmit", []) == []
2361
2361
  assert len(payload["hooks"]["Notification"]) == 1
2362
2362
  assert len(payload["hooks"]["Stop"]) == 1
2363
2363
  pretool_hook_commands = [
@@ -4492,6 +4492,71 @@ def test_guard_hook_claude_ask_user_question_empty_answers_uses_explicit_fallbac
4492
4492
  assert guard_commands_module._claude_guard_approval_answer(payload) == "allow"
4493
4493
 
4494
4494
 
4495
+ def test_guard_hook_claude_native_cancel_does_not_persist_flat_block(tmp_path, capsys, monkeypatch):
4496
+ home_dir = tmp_path / "home"
4497
+ workspace_dir = tmp_path / "workspace"
4498
+ _build_guard_fixture(home_dir, workspace_dir)
4499
+ first_event = {
4500
+ "session_id": "session-claude-native-cancel",
4501
+ "hook_event_name": "PreToolUse",
4502
+ "tool_name": "Read",
4503
+ "tool_input": {"file_path": str(workspace_dir / ".npmrc")},
4504
+ "source_scope": "project",
4505
+ }
4506
+ monkeypatch.setattr(guard_commands_module, "ensure_guard_daemon", lambda _guard_home: "http://127.0.0.1:4455")
4507
+
4508
+ first_rc, first_output = _run_guard_hook(
4509
+ home_dir=home_dir,
4510
+ workspace_dir=workspace_dir,
4511
+ harness="claude-code",
4512
+ event=first_event,
4513
+ capsys=capsys,
4514
+ monkeypatch=monkeypatch,
4515
+ )
4516
+ notification_rc, notification_output = _run_guard_hook(
4517
+ home_dir=home_dir,
4518
+ workspace_dir=workspace_dir,
4519
+ harness="claude-code",
4520
+ event={
4521
+ **first_event,
4522
+ "hook_event_name": "Notification",
4523
+ "notification_type": "permission_prompt",
4524
+ "message": "Claude needs your permission to use Read",
4525
+ },
4526
+ capsys=capsys,
4527
+ monkeypatch=monkeypatch,
4528
+ )
4529
+ stop_rc, stop_output = _run_guard_hook(
4530
+ home_dir=home_dir,
4531
+ workspace_dir=workspace_dir,
4532
+ harness="claude-code",
4533
+ event={"session_id": "session-claude-native-cancel", "hook_event_name": "Stop"},
4534
+ capsys=capsys,
4535
+ monkeypatch=monkeypatch,
4536
+ )
4537
+ second_rc, second_output = _run_guard_hook(
4538
+ home_dir=home_dir,
4539
+ workspace_dir=workspace_dir,
4540
+ harness="claude-code",
4541
+ event={**first_event, "session_id": "session-claude-native-cancel-retry"},
4542
+ capsys=capsys,
4543
+ monkeypatch=monkeypatch,
4544
+ )
4545
+ first_payload = json.loads(first_output)
4546
+ notification_payload = json.loads(notification_output)
4547
+ second_payload = json.loads(second_output)
4548
+
4549
+ assert first_rc == 0
4550
+ assert first_payload["hookSpecificOutput"]["permissionDecision"] == "ask"
4551
+ assert notification_rc == 0
4552
+ assert "HOL Guard approval question" in notification_payload["systemMessage"]
4553
+ assert stop_rc == 0
4554
+ assert stop_output == ""
4555
+ assert GuardStore(home_dir).list_policy_decisions("claude-code") == []
4556
+ assert second_rc == 0
4557
+ assert second_payload["hookSpecificOutput"]["permissionDecision"] == "ask"
4558
+
4559
+
4495
4560
  def test_guard_hook_claude_alias_reuses_native_approval_policy_with_canonical_harness(tmp_path, capsys, monkeypatch):
4496
4561
  home_dir = tmp_path / "home"
4497
4562
  workspace_dir = tmp_path / "workspace"
@@ -4600,7 +4665,7 @@ def test_guard_hook_claude_alias_reuses_legacy_alias_policy_keys(tmp_path, capsy
4600
4665
  assert payload["hookSpecificOutput"]["permissionDecision"] == "deny"
4601
4666
 
4602
4667
 
4603
- def test_guard_hook_claude_stop_persists_unapproved_native_prompt_as_denied(tmp_path, capsys, monkeypatch):
4668
+ def test_guard_hook_claude_stop_keeps_native_cancel_transient(tmp_path, capsys, monkeypatch):
4604
4669
  home_dir = tmp_path / "home"
4605
4670
  workspace_dir = tmp_path / "workspace"
4606
4671
  _build_guard_fixture(home_dir, workspace_dir)
@@ -4662,8 +4727,8 @@ def test_guard_hook_claude_stop_persists_unapproved_native_prompt_as_denied(tmp_
4662
4727
  assert stop_rc == 0
4663
4728
  assert stop_output == ""
4664
4729
  assert second_rc == 0
4665
- assert second_payload["hookSpecificOutput"]["permissionDecision"] == "deny"
4666
- assert "HOL Guard blocked Claude's attempt to use Read" in second_payload["systemMessage"]
4730
+ assert second_payload["hookSpecificOutput"]["permissionDecision"] == "ask"
4731
+ assert "HOL Guard intercepted Claude's attempt to use Read" in second_payload["systemMessage"]
4667
4732
 
4668
4733
 
4669
4734
  def test_guard_hook_claude_stop_does_not_persist_denial_without_visible_prompt(
@@ -4873,7 +4938,7 @@ def test_runtime_artifact_native_reason_truncates_long_risk_summaries() -> None:
4873
4938
  assert reason.endswith("...")
4874
4939
 
4875
4940
 
4876
- def test_guard_hook_brands_claude_user_prompt_submit_before_native_approval(
4941
+ def test_guard_hook_allows_claude_user_prompt_submit_before_tool_approval(
4877
4942
  tmp_path,
4878
4943
  capsys,
4879
4944
  monkeypatch,
@@ -4895,20 +4960,13 @@ def test_guard_hook_brands_claude_user_prompt_submit_before_native_approval(
4895
4960
  monkeypatch=monkeypatch,
4896
4961
  )
4897
4962
  receipts = GuardStore(home_dir).list_receipts()
4898
- payload = json.loads(output)
4899
4963
 
4900
4964
  assert rc == 0
4901
- assert payload["hookSpecificOutput"]["hookEventName"] == "UserPromptSubmit"
4902
- assert "Do not ask for approval at the prompt stage" in payload["hookSpecificOutput"]["additionalContext"]
4903
- assert (
4904
- "route that concrete action into a HOL Guard approval question"
4905
- in payload["hookSpecificOutput"]["additionalContext"]
4906
- )
4907
- assert "Keep blocked" in payload["hookSpecificOutput"]["additionalContext"]
4965
+ assert output == ""
4908
4966
  assert any(receipt["artifact_id"].startswith("claude-code:session:prompt") for receipt in receipts)
4909
4967
 
4910
4968
 
4911
- def test_guard_hook_brands_generic_claude_user_prompt_submit_before_native_approval(
4969
+ def test_guard_hook_allows_generic_claude_user_prompt_submit_before_tool_approval(
4912
4970
  tmp_path,
4913
4971
  capsys,
4914
4972
  monkeypatch,
@@ -4929,16 +4987,9 @@ def test_guard_hook_brands_generic_claude_user_prompt_submit_before_native_appro
4929
4987
  capsys=capsys,
4930
4988
  monkeypatch=monkeypatch,
4931
4989
  )
4932
- payload = json.loads(output)
4933
4990
 
4934
4991
  assert rc == 0
4935
- assert payload["hookSpecificOutput"]["hookEventName"] == "UserPromptSubmit"
4936
- assert "Do not ask for approval at the prompt stage" in payload["hookSpecificOutput"]["additionalContext"]
4937
- assert (
4938
- "route that concrete action into a HOL Guard approval question"
4939
- in payload["hookSpecificOutput"]["additionalContext"]
4940
- )
4941
- assert "Keep blocked" in payload["hookSpecificOutput"]["additionalContext"]
4992
+ assert output == ""
4942
4993
 
4943
4994
 
4944
4995
  def test_guard_hook_emits_json_for_claude_user_prompt_submit_overridable_prompts(
@@ -253,15 +253,7 @@ class TestGuardSurfaceServer:
253
253
  finally:
254
254
  daemon.stop()
255
255
 
256
- assert "HOL Guard intercepted this prompt" in hook_payload["systemMessage"]
257
- assert hook_payload["hookSpecificOutput"]["hookEventName"] == "UserPromptSubmit"
258
- assert "Do not ask for approval at the prompt stage" in hook_payload["hookSpecificOutput"]["additionalContext"]
259
- assert (
260
- "route that concrete action into a HOL Guard approval question"
261
- in hook_payload["hookSpecificOutput"]["additionalContext"]
262
- )
263
- assert "Allow once" in hook_payload["hookSpecificOutput"]["additionalContext"]
264
- assert "Keep blocked" in hook_payload["hookSpecificOutput"]["additionalContext"]
256
+ assert hook_payload == {}
265
257
 
266
258
  def test_guard_daemon_claude_hook_endpoint_blocks_guard_bypass_user_prompt_submit(self, tmp_path) -> None:
267
259
  home_dir = tmp_path / "home"