@jaguilar87/gaia-ops 4.4.0 → 4.7.2

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 (371) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +12 -3
  3. package/ARCHITECTURE.md +9 -8
  4. package/CHANGELOG.md +34 -0
  5. package/README.md +43 -11
  6. package/agents/terraform-architect.md +1 -1
  7. package/bin/README.md +2 -2
  8. package/bin/gaia-doctor.js +18 -5
  9. package/bin/gaia-history.js +0 -1
  10. package/bin/gaia-metrics.js +2 -2
  11. package/bin/gaia-scan.py +23 -1
  12. package/bin/gaia-update.js +346 -54
  13. package/bin/pre-publish-validate.js +33 -10
  14. package/commands/gaia.md +37 -0
  15. package/config/README.md +3 -9
  16. package/config/context-contracts.json +47 -15
  17. package/config/surface-routing.json +9 -1
  18. package/dist/gaia-ops/.claude-plugin/plugin.json +22 -0
  19. package/dist/gaia-ops/agents/cloud-troubleshooter.md +73 -0
  20. package/dist/gaia-ops/agents/devops-developer.md +57 -0
  21. package/dist/gaia-ops/agents/gaia-system.md +58 -0
  22. package/dist/gaia-ops/agents/gitops-operator.md +60 -0
  23. package/dist/gaia-ops/agents/speckit-planner.md +71 -0
  24. package/dist/gaia-ops/agents/terraform-architect.md +60 -0
  25. package/dist/gaia-ops/commands/gaia.md +37 -0
  26. package/dist/gaia-ops/config/README.md +58 -0
  27. package/dist/gaia-ops/config/cloud/aws.json +140 -0
  28. package/dist/gaia-ops/config/cloud/gcp.json +145 -0
  29. package/dist/gaia-ops/config/context-contracts.json +131 -0
  30. package/dist/gaia-ops/config/git_standards.json +72 -0
  31. package/dist/gaia-ops/config/surface-routing.json +197 -0
  32. package/dist/gaia-ops/config/universal-rules.json +10 -0
  33. package/dist/gaia-ops/hooks/adapters/__init__.py +52 -0
  34. package/dist/gaia-ops/hooks/adapters/base.py +219 -0
  35. package/dist/gaia-ops/hooks/adapters/channel.py +17 -0
  36. package/dist/gaia-ops/hooks/adapters/claude_code.py +1477 -0
  37. package/dist/gaia-ops/hooks/adapters/types.py +194 -0
  38. package/dist/gaia-ops/hooks/adapters/utils.py +25 -0
  39. package/dist/gaia-ops/hooks/hooks.json +126 -0
  40. package/dist/gaia-ops/hooks/modules/__init__.py +15 -0
  41. package/dist/gaia-ops/hooks/modules/agents/__init__.py +29 -0
  42. package/dist/gaia-ops/hooks/modules/agents/contract_validator.py +647 -0
  43. package/dist/gaia-ops/hooks/modules/agents/response_contract.py +496 -0
  44. package/dist/gaia-ops/hooks/modules/agents/skill_injection_verifier.py +124 -0
  45. package/dist/gaia-ops/hooks/modules/agents/task_info_builder.py +74 -0
  46. package/dist/gaia-ops/hooks/modules/agents/transcript_analyzer.py +458 -0
  47. package/dist/gaia-ops/hooks/modules/agents/transcript_reader.py +152 -0
  48. package/dist/gaia-ops/hooks/modules/audit/__init__.py +28 -0
  49. package/dist/gaia-ops/hooks/modules/audit/event_detector.py +168 -0
  50. package/dist/gaia-ops/hooks/modules/audit/logger.py +131 -0
  51. package/dist/gaia-ops/hooks/modules/audit/metrics.py +134 -0
  52. package/dist/gaia-ops/hooks/modules/audit/workflow_auditor.py +576 -0
  53. package/dist/gaia-ops/hooks/modules/audit/workflow_recorder.py +296 -0
  54. package/dist/gaia-ops/hooks/modules/context/__init__.py +11 -0
  55. package/dist/gaia-ops/hooks/modules/context/anchor_tracker.py +317 -0
  56. package/dist/gaia-ops/hooks/modules/context/compact_context_builder.py +215 -0
  57. package/dist/gaia-ops/hooks/modules/context/context_cache.py +129 -0
  58. package/dist/gaia-ops/hooks/modules/context/context_freshness.py +145 -0
  59. package/dist/gaia-ops/hooks/modules/context/context_injector.py +427 -0
  60. package/dist/gaia-ops/hooks/modules/context/context_writer.py +518 -0
  61. package/dist/gaia-ops/hooks/modules/context/contracts_loader.py +161 -0
  62. package/dist/gaia-ops/hooks/modules/core/__init__.py +40 -0
  63. package/dist/gaia-ops/hooks/modules/core/hook_entry.py +78 -0
  64. package/dist/gaia-ops/hooks/modules/core/paths.py +160 -0
  65. package/dist/gaia-ops/hooks/modules/core/plugin_mode.py +149 -0
  66. package/dist/gaia-ops/hooks/modules/core/plugin_setup.py +558 -0
  67. package/dist/gaia-ops/hooks/modules/core/state.py +179 -0
  68. package/dist/gaia-ops/hooks/modules/core/stdin.py +24 -0
  69. package/dist/gaia-ops/hooks/modules/events/__init__.py +1 -0
  70. package/dist/gaia-ops/hooks/modules/events/event_writer.py +210 -0
  71. package/dist/gaia-ops/hooks/modules/identity/__init__.py +0 -0
  72. package/dist/gaia-ops/hooks/modules/identity/identity_provider.py +21 -0
  73. package/dist/gaia-ops/hooks/modules/identity/ops_identity.py +34 -0
  74. package/dist/gaia-ops/hooks/modules/identity/security_identity.py +10 -0
  75. package/dist/gaia-ops/hooks/modules/memory/__init__.py +8 -0
  76. package/dist/gaia-ops/hooks/modules/memory/episode_writer.py +227 -0
  77. package/dist/gaia-ops/hooks/modules/orchestrator/__init__.py +1 -0
  78. package/dist/gaia-ops/hooks/modules/orchestrator/delegate_mode.py +128 -0
  79. package/dist/gaia-ops/hooks/modules/scanning/__init__.py +8 -0
  80. package/dist/gaia-ops/hooks/modules/scanning/scan_trigger.py +84 -0
  81. package/dist/gaia-ops/hooks/modules/security/__init__.py +89 -0
  82. package/dist/gaia-ops/hooks/modules/security/approval_cleanup.py +87 -0
  83. package/dist/gaia-ops/hooks/modules/security/approval_constants.py +23 -0
  84. package/dist/gaia-ops/hooks/modules/security/approval_grants.py +912 -0
  85. package/dist/gaia-ops/hooks/modules/security/approval_messages.py +71 -0
  86. package/dist/gaia-ops/hooks/modules/security/approval_scopes.py +153 -0
  87. package/dist/gaia-ops/hooks/modules/security/blocked_commands.py +584 -0
  88. package/dist/gaia-ops/hooks/modules/security/blocked_message_formatter.py +86 -0
  89. package/dist/gaia-ops/hooks/modules/security/command_semantics.py +130 -0
  90. package/dist/gaia-ops/hooks/modules/security/gitops_validator.py +179 -0
  91. package/dist/gaia-ops/hooks/modules/security/mutative_verbs.py +850 -0
  92. package/dist/gaia-ops/hooks/modules/security/prompt_validator.py +40 -0
  93. package/dist/gaia-ops/hooks/modules/security/tiers.py +196 -0
  94. package/dist/gaia-ops/hooks/modules/session/__init__.py +10 -0
  95. package/dist/gaia-ops/hooks/modules/session/session_context_writer.py +100 -0
  96. package/dist/gaia-ops/hooks/modules/session/session_event_injector.py +158 -0
  97. package/dist/gaia-ops/hooks/modules/session/session_manager.py +31 -0
  98. package/dist/gaia-ops/hooks/modules/tools/__init__.py +25 -0
  99. package/dist/gaia-ops/hooks/modules/tools/bash_validator.py +708 -0
  100. package/dist/gaia-ops/hooks/modules/tools/cloud_pipe_validator.py +181 -0
  101. package/dist/gaia-ops/hooks/modules/tools/hook_response.py +55 -0
  102. package/dist/gaia-ops/hooks/modules/tools/shell_parser.py +227 -0
  103. package/dist/gaia-ops/hooks/modules/tools/task_validator.py +283 -0
  104. package/dist/gaia-ops/hooks/modules/validation/__init__.py +23 -0
  105. package/dist/gaia-ops/hooks/modules/validation/commit_validator.py +380 -0
  106. package/dist/gaia-ops/hooks/post_compact.py +43 -0
  107. package/dist/gaia-ops/hooks/post_tool_use.py +54 -0
  108. package/dist/gaia-ops/hooks/pre_tool_use.py +383 -0
  109. package/dist/gaia-ops/hooks/session_start.py +69 -0
  110. package/dist/gaia-ops/hooks/stop_hook.py +69 -0
  111. package/dist/gaia-ops/hooks/subagent_start.py +71 -0
  112. package/dist/gaia-ops/hooks/subagent_stop.py +288 -0
  113. package/dist/gaia-ops/hooks/task_completed.py +70 -0
  114. package/dist/gaia-ops/hooks/user_prompt_submit.py +177 -0
  115. package/dist/gaia-ops/settings.json +72 -0
  116. package/dist/gaia-ops/skills/README.md +109 -0
  117. package/dist/gaia-ops/skills/agent-protocol/SKILL.md +105 -0
  118. package/dist/gaia-ops/skills/agent-protocol/examples.md +170 -0
  119. package/dist/gaia-ops/skills/agent-response/SKILL.md +53 -0
  120. package/dist/gaia-ops/skills/approval/SKILL.md +85 -0
  121. package/dist/gaia-ops/skills/approval/examples.md +140 -0
  122. package/dist/gaia-ops/skills/approval/reference.md +57 -0
  123. package/dist/gaia-ops/skills/command-execution/SKILL.md +64 -0
  124. package/dist/gaia-ops/skills/command-execution/reference.md +83 -0
  125. package/dist/gaia-ops/skills/context-updater/SKILL.md +76 -0
  126. package/dist/gaia-ops/skills/context-updater/examples.md +71 -0
  127. package/dist/gaia-ops/skills/developer-patterns/SKILL.md +93 -0
  128. package/dist/gaia-ops/skills/developer-patterns/reference.md +112 -0
  129. package/dist/gaia-ops/skills/execution/SKILL.md +66 -0
  130. package/dist/gaia-ops/skills/fast-queries/SKILL.md +47 -0
  131. package/dist/gaia-ops/skills/gaia-patterns/SKILL.md +92 -0
  132. package/dist/gaia-ops/skills/gaia-patterns/reference.md +22 -0
  133. package/dist/gaia-ops/skills/git-conventions/SKILL.md +48 -0
  134. package/dist/gaia-ops/skills/gitops-patterns/SKILL.md +73 -0
  135. package/dist/gaia-ops/skills/gitops-patterns/reference.md +183 -0
  136. package/dist/gaia-ops/skills/investigation/SKILL.md +77 -0
  137. package/dist/gaia-ops/skills/orchestrator-approval/SKILL.md +64 -0
  138. package/dist/gaia-ops/skills/reference.md +134 -0
  139. package/dist/gaia-ops/skills/security-tiers/SKILL.md +61 -0
  140. package/dist/gaia-ops/skills/security-tiers/destructive-commands-reference.md +623 -0
  141. package/dist/gaia-ops/skills/security-tiers/reference.md +39 -0
  142. package/dist/gaia-ops/skills/skill-creation/SKILL.md +119 -0
  143. package/dist/gaia-ops/skills/specification/SKILL.md +186 -0
  144. package/dist/gaia-ops/skills/speckit-workflow/SKILL.md +165 -0
  145. package/dist/gaia-ops/skills/speckit-workflow/reference.md +117 -0
  146. package/dist/gaia-ops/skills/terraform-patterns/SKILL.md +63 -0
  147. package/dist/gaia-ops/skills/terraform-patterns/reference.md +93 -0
  148. package/dist/gaia-ops/speckit/README.md +516 -0
  149. package/dist/gaia-ops/speckit/scripts/.gitkeep +0 -0
  150. package/dist/gaia-ops/speckit/templates/adr-template.md +118 -0
  151. package/dist/gaia-ops/speckit/templates/agent-file-template.md +23 -0
  152. package/dist/gaia-ops/speckit/templates/plan-template.md +227 -0
  153. package/dist/gaia-ops/speckit/templates/spec-template.md +140 -0
  154. package/dist/gaia-ops/speckit/templates/tasks-template.md +257 -0
  155. package/dist/gaia-ops/tools/context/README.md +132 -0
  156. package/dist/gaia-ops/tools/context/__init__.py +42 -0
  157. package/dist/gaia-ops/tools/context/_paths.py +20 -0
  158. package/dist/gaia-ops/tools/context/context_provider.py +476 -0
  159. package/dist/gaia-ops/tools/context/context_section_reader.py +330 -0
  160. package/dist/gaia-ops/tools/context/deep_merge.py +159 -0
  161. package/dist/gaia-ops/tools/context/pending_updates.py +760 -0
  162. package/dist/gaia-ops/tools/context/surface_router.py +278 -0
  163. package/dist/gaia-ops/tools/fast-queries/README.md +65 -0
  164. package/dist/gaia-ops/tools/fast-queries/__init__.py +30 -0
  165. package/dist/gaia-ops/tools/fast-queries/appservices/quicktriage_devops_developer.sh +75 -0
  166. package/dist/gaia-ops/tools/fast-queries/cloud/aws/quicktriage_aws_troubleshooter.sh +32 -0
  167. package/dist/gaia-ops/tools/fast-queries/cloud/gcp/quicktriage_gcp_troubleshooter.sh +88 -0
  168. package/dist/gaia-ops/tools/fast-queries/gitops/quicktriage_gitops_operator.sh +48 -0
  169. package/dist/gaia-ops/tools/fast-queries/run_triage.sh +59 -0
  170. package/dist/gaia-ops/tools/fast-queries/terraform/quicktriage_terraform_architect.sh +80 -0
  171. package/dist/gaia-ops/tools/gaia_simulator/__init__.py +33 -0
  172. package/dist/gaia-ops/tools/gaia_simulator/cli.py +354 -0
  173. package/dist/gaia-ops/tools/gaia_simulator/extractor.py +457 -0
  174. package/dist/gaia-ops/tools/gaia_simulator/reporter.py +258 -0
  175. package/dist/gaia-ops/tools/gaia_simulator/routing_simulator.py +334 -0
  176. package/dist/gaia-ops/tools/gaia_simulator/runner.py +539 -0
  177. package/dist/gaia-ops/tools/gaia_simulator/skills_mapper.py +262 -0
  178. package/dist/gaia-ops/tools/memory/README.md +0 -0
  179. package/dist/gaia-ops/tools/memory/__init__.py +20 -0
  180. package/dist/gaia-ops/tools/memory/episodic.py +1196 -0
  181. package/dist/gaia-ops/tools/persist_transcript_analysis.py +85 -0
  182. package/dist/gaia-ops/tools/review/__init__.py +1 -0
  183. package/dist/gaia-ops/tools/review/review_engine.py +157 -0
  184. package/dist/gaia-ops/tools/scan/__init__.py +35 -0
  185. package/dist/gaia-ops/tools/scan/config.py +247 -0
  186. package/dist/gaia-ops/tools/scan/merge.py +212 -0
  187. package/dist/gaia-ops/tools/scan/orchestrator.py +549 -0
  188. package/dist/gaia-ops/tools/scan/registry.py +127 -0
  189. package/dist/gaia-ops/tools/scan/scanners/__init__.py +18 -0
  190. package/dist/gaia-ops/tools/scan/scanners/base.py +137 -0
  191. package/dist/gaia-ops/tools/scan/scanners/environment.py +324 -0
  192. package/dist/gaia-ops/tools/scan/scanners/git.py +570 -0
  193. package/dist/gaia-ops/tools/scan/scanners/infrastructure.py +875 -0
  194. package/dist/gaia-ops/tools/scan/scanners/orchestration.py +600 -0
  195. package/dist/gaia-ops/tools/scan/scanners/stack.py +1085 -0
  196. package/dist/gaia-ops/tools/scan/scanners/tools.py +260 -0
  197. package/dist/gaia-ops/tools/scan/setup.py +753 -0
  198. package/dist/gaia-ops/tools/scan/tests/__init__.py +1 -0
  199. package/dist/gaia-ops/tools/scan/tests/conftest.py +796 -0
  200. package/dist/gaia-ops/tools/scan/tests/test_environment.py +323 -0
  201. package/dist/gaia-ops/tools/scan/tests/test_git.py +419 -0
  202. package/dist/gaia-ops/tools/scan/tests/test_infrastructure.py +382 -0
  203. package/dist/gaia-ops/tools/scan/tests/test_integration.py +920 -0
  204. package/dist/gaia-ops/tools/scan/tests/test_merge.py +269 -0
  205. package/dist/gaia-ops/tools/scan/tests/test_orchestration.py +304 -0
  206. package/dist/gaia-ops/tools/scan/tests/test_stack.py +604 -0
  207. package/dist/gaia-ops/tools/scan/tests/test_tools.py +349 -0
  208. package/dist/gaia-ops/tools/scan/ui.py +624 -0
  209. package/dist/gaia-ops/tools/scan/verify.py +266 -0
  210. package/dist/gaia-ops/tools/scan/walk.py +118 -0
  211. package/dist/gaia-ops/tools/scan/workspace.py +85 -0
  212. package/dist/gaia-ops/tools/validation/README.md +244 -0
  213. package/dist/gaia-ops/tools/validation/__init__.py +17 -0
  214. package/dist/gaia-ops/tools/validation/approval_gate.py +321 -0
  215. package/dist/gaia-ops/tools/validation/validate_skills.py +189 -0
  216. package/dist/gaia-security/.claude-plugin/plugin.json +22 -0
  217. package/dist/gaia-security/config/universal-rules.json +10 -0
  218. package/dist/gaia-security/hooks/adapters/__init__.py +52 -0
  219. package/dist/gaia-security/hooks/adapters/base.py +219 -0
  220. package/dist/gaia-security/hooks/adapters/channel.py +17 -0
  221. package/dist/gaia-security/hooks/adapters/claude_code.py +1477 -0
  222. package/dist/gaia-security/hooks/adapters/types.py +194 -0
  223. package/dist/gaia-security/hooks/adapters/utils.py +25 -0
  224. package/dist/gaia-security/hooks/hooks.json +57 -0
  225. package/dist/gaia-security/hooks/modules/__init__.py +15 -0
  226. package/dist/gaia-security/hooks/modules/agents/__init__.py +29 -0
  227. package/dist/gaia-security/hooks/modules/agents/contract_validator.py +647 -0
  228. package/dist/gaia-security/hooks/modules/agents/response_contract.py +496 -0
  229. package/dist/gaia-security/hooks/modules/agents/skill_injection_verifier.py +124 -0
  230. package/dist/gaia-security/hooks/modules/agents/task_info_builder.py +74 -0
  231. package/dist/gaia-security/hooks/modules/agents/transcript_analyzer.py +458 -0
  232. package/dist/gaia-security/hooks/modules/agents/transcript_reader.py +152 -0
  233. package/dist/gaia-security/hooks/modules/audit/__init__.py +28 -0
  234. package/dist/gaia-security/hooks/modules/audit/event_detector.py +168 -0
  235. package/dist/gaia-security/hooks/modules/audit/logger.py +131 -0
  236. package/dist/gaia-security/hooks/modules/audit/metrics.py +134 -0
  237. package/dist/gaia-security/hooks/modules/audit/workflow_auditor.py +576 -0
  238. package/dist/gaia-security/hooks/modules/audit/workflow_recorder.py +296 -0
  239. package/dist/gaia-security/hooks/modules/context/__init__.py +11 -0
  240. package/dist/gaia-security/hooks/modules/context/anchor_tracker.py +317 -0
  241. package/dist/gaia-security/hooks/modules/context/compact_context_builder.py +215 -0
  242. package/dist/gaia-security/hooks/modules/context/context_cache.py +129 -0
  243. package/dist/gaia-security/hooks/modules/context/context_freshness.py +145 -0
  244. package/dist/gaia-security/hooks/modules/context/context_injector.py +427 -0
  245. package/dist/gaia-security/hooks/modules/context/context_writer.py +518 -0
  246. package/dist/gaia-security/hooks/modules/context/contracts_loader.py +161 -0
  247. package/dist/gaia-security/hooks/modules/core/__init__.py +40 -0
  248. package/dist/gaia-security/hooks/modules/core/hook_entry.py +78 -0
  249. package/dist/gaia-security/hooks/modules/core/paths.py +160 -0
  250. package/dist/gaia-security/hooks/modules/core/plugin_mode.py +149 -0
  251. package/dist/gaia-security/hooks/modules/core/plugin_setup.py +558 -0
  252. package/dist/gaia-security/hooks/modules/core/state.py +179 -0
  253. package/dist/gaia-security/hooks/modules/core/stdin.py +24 -0
  254. package/dist/gaia-security/hooks/modules/events/__init__.py +1 -0
  255. package/dist/gaia-security/hooks/modules/events/event_writer.py +210 -0
  256. package/dist/gaia-security/hooks/modules/identity/__init__.py +0 -0
  257. package/dist/gaia-security/hooks/modules/identity/identity_provider.py +21 -0
  258. package/dist/gaia-security/hooks/modules/identity/ops_identity.py +34 -0
  259. package/dist/gaia-security/hooks/modules/identity/security_identity.py +10 -0
  260. package/dist/gaia-security/hooks/modules/memory/__init__.py +8 -0
  261. package/dist/gaia-security/hooks/modules/memory/episode_writer.py +227 -0
  262. package/dist/gaia-security/hooks/modules/orchestrator/__init__.py +1 -0
  263. package/dist/gaia-security/hooks/modules/orchestrator/delegate_mode.py +128 -0
  264. package/dist/gaia-security/hooks/modules/scanning/__init__.py +8 -0
  265. package/dist/gaia-security/hooks/modules/scanning/scan_trigger.py +84 -0
  266. package/dist/gaia-security/hooks/modules/security/__init__.py +89 -0
  267. package/dist/gaia-security/hooks/modules/security/approval_cleanup.py +87 -0
  268. package/dist/gaia-security/hooks/modules/security/approval_constants.py +23 -0
  269. package/dist/gaia-security/hooks/modules/security/approval_grants.py +912 -0
  270. package/dist/gaia-security/hooks/modules/security/approval_messages.py +71 -0
  271. package/dist/gaia-security/hooks/modules/security/approval_scopes.py +153 -0
  272. package/dist/gaia-security/hooks/modules/security/blocked_commands.py +584 -0
  273. package/dist/gaia-security/hooks/modules/security/blocked_message_formatter.py +86 -0
  274. package/dist/gaia-security/hooks/modules/security/command_semantics.py +130 -0
  275. package/dist/gaia-security/hooks/modules/security/gitops_validator.py +179 -0
  276. package/dist/gaia-security/hooks/modules/security/mutative_verbs.py +850 -0
  277. package/dist/gaia-security/hooks/modules/security/prompt_validator.py +40 -0
  278. package/dist/gaia-security/hooks/modules/security/tiers.py +196 -0
  279. package/dist/gaia-security/hooks/modules/session/__init__.py +10 -0
  280. package/dist/gaia-security/hooks/modules/session/session_context_writer.py +100 -0
  281. package/dist/gaia-security/hooks/modules/session/session_event_injector.py +158 -0
  282. package/dist/gaia-security/hooks/modules/session/session_manager.py +31 -0
  283. package/dist/gaia-security/hooks/modules/tools/__init__.py +25 -0
  284. package/dist/gaia-security/hooks/modules/tools/bash_validator.py +708 -0
  285. package/dist/gaia-security/hooks/modules/tools/cloud_pipe_validator.py +181 -0
  286. package/dist/gaia-security/hooks/modules/tools/hook_response.py +55 -0
  287. package/dist/gaia-security/hooks/modules/tools/shell_parser.py +227 -0
  288. package/dist/gaia-security/hooks/modules/tools/task_validator.py +283 -0
  289. package/dist/gaia-security/hooks/modules/validation/__init__.py +23 -0
  290. package/dist/gaia-security/hooks/modules/validation/commit_validator.py +380 -0
  291. package/dist/gaia-security/hooks/post_tool_use.py +54 -0
  292. package/dist/gaia-security/hooks/pre_tool_use.py +383 -0
  293. package/dist/gaia-security/hooks/session_start.py +69 -0
  294. package/dist/gaia-security/hooks/stop_hook.py +69 -0
  295. package/dist/gaia-security/hooks/user_prompt_submit.py +177 -0
  296. package/dist/gaia-security/settings.json +58 -0
  297. package/git-hooks/commit-msg +41 -0
  298. package/hooks/README.md +8 -6
  299. package/hooks/adapters/channel.py +0 -25
  300. package/hooks/adapters/claude_code.py +364 -125
  301. package/hooks/elicitation_result.py +132 -0
  302. package/hooks/hooks.json +10 -1
  303. package/hooks/modules/README.md +3 -2
  304. package/hooks/modules/agents/contract_validator.py +3 -51
  305. package/hooks/modules/agents/response_contract.py +4 -8
  306. package/hooks/modules/agents/transcript_reader.py +4 -5
  307. package/hooks/modules/audit/__init__.py +4 -6
  308. package/hooks/modules/audit/event_detector.py +0 -2
  309. package/hooks/modules/audit/metrics.py +108 -187
  310. package/hooks/modules/audit/workflow_auditor.py +0 -4
  311. package/hooks/modules/audit/workflow_recorder.py +0 -5
  312. package/hooks/modules/context/compact_context_builder.py +1 -0
  313. package/hooks/modules/context/context_cache.py +129 -0
  314. package/hooks/modules/context/context_injector.py +18 -40
  315. package/hooks/modules/context/context_writer.py +1 -25
  316. package/hooks/modules/context/contracts_loader.py +7 -10
  317. package/hooks/modules/core/hook_entry.py +1 -0
  318. package/hooks/modules/core/paths.py +12 -13
  319. package/hooks/modules/core/plugin_mode.py +74 -4
  320. package/hooks/modules/core/plugin_setup.py +395 -23
  321. package/hooks/modules/events/__init__.py +1 -0
  322. package/hooks/modules/events/event_writer.py +210 -0
  323. package/hooks/modules/identity/ops_identity.py +18 -27
  324. package/hooks/modules/memory/episode_writer.py +1 -6
  325. package/hooks/modules/orchestrator/__init__.py +1 -0
  326. package/hooks/modules/orchestrator/delegate_mode.py +128 -0
  327. package/hooks/modules/security/__init__.py +2 -4
  328. package/hooks/modules/security/approval_constants.py +5 -1
  329. package/hooks/modules/security/approval_grants.py +189 -6
  330. package/hooks/modules/security/approval_messages.py +9 -21
  331. package/hooks/modules/security/blocked_commands.py +98 -34
  332. package/hooks/modules/security/command_semantics.py +0 -4
  333. package/hooks/modules/security/gitops_validator.py +1 -11
  334. package/hooks/modules/security/mutative_verbs.py +179 -38
  335. package/hooks/modules/security/tiers.py +1 -19
  336. package/hooks/modules/session/session_event_injector.py +1 -25
  337. package/hooks/modules/tools/bash_validator.py +310 -94
  338. package/hooks/modules/tools/shell_parser.py +0 -1
  339. package/hooks/modules/tools/task_validator.py +9 -29
  340. package/hooks/post_tool_use.py +0 -72
  341. package/hooks/pre_tool_use.py +42 -102
  342. package/hooks/session_start.py +4 -2
  343. package/hooks/subagent_start.py +6 -2
  344. package/hooks/subagent_stop.py +1 -13
  345. package/hooks/user_prompt_submit.py +119 -37
  346. package/index.js +1 -1
  347. package/package.json +5 -3
  348. package/skills/README.md +3 -5
  349. package/skills/agent-protocol/SKILL.md +17 -16
  350. package/skills/agent-protocol/examples.md +6 -6
  351. package/skills/agent-response/SKILL.md +11 -14
  352. package/skills/approval/SKILL.md +28 -13
  353. package/skills/approval/reference.md +2 -2
  354. package/skills/execution/SKILL.md +1 -1
  355. package/skills/gaia-patterns/SKILL.md +2 -3
  356. package/skills/orchestrator-approval/SKILL.md +22 -50
  357. package/skills/security-tiers/SKILL.md +1 -1
  358. package/templates/README.md +9 -9
  359. package/templates/managed-settings.template.json +43 -0
  360. package/tools/gaia_simulator/runner.py +34 -1
  361. package/tools/scan/orchestrator.py +13 -0
  362. package/tools/scan/scanners/base.py +8 -0
  363. package/tools/scan/scanners/git.py +78 -0
  364. package/tools/scan/scanners/infrastructure.py +65 -0
  365. package/tools/scan/scanners/stack.py +110 -0
  366. package/tools/scan/setup.py +120 -13
  367. package/tools/scan/workspace.py +85 -0
  368. package/config/context-contracts.aws.json +0 -42
  369. package/config/context-contracts.gcp.json +0 -39
  370. package/skills/project-dispatch/SKILL.md +0 -34
  371. package/templates/settings.template.json +0 -226
@@ -46,7 +46,6 @@ from ..core.paths import find_claude_dir, get_plugin_data_dir
46
46
  from ..core.state import get_session_id
47
47
  from .approval_scopes import (
48
48
  ApprovalSignature,
49
- SCOPE_EXACT_COMMAND,
50
49
  SCOPE_SEMANTIC_SIGNATURE,
51
50
  SUPPORTED_SCOPE_TYPES,
52
51
  build_approval_signature,
@@ -56,7 +55,7 @@ from .approval_scopes import (
56
55
  logger = logging.getLogger(__name__)
57
56
 
58
57
  # Default grant TTL in minutes
59
- DEFAULT_GRANT_TTL_MINUTES = 10
58
+ DEFAULT_GRANT_TTL_MINUTES = 5
60
59
 
61
60
  # Cleanup throttle: only run cleanup if 60+ seconds since last run
62
61
  _last_cleanup_time: float = 0.0
@@ -524,7 +523,7 @@ def activate_pending_approval(
524
523
  reason="Unexpected error while activating approval.",
525
524
  )
526
525
 
527
- def check_approval_grant(command: str) -> Optional[ApprovalGrant]:
526
+ def check_approval_grant(command: str, session_id: str = None) -> Optional[ApprovalGrant]:
528
527
  """Check if there is an active approval grant for a command.
529
528
 
530
529
  Called by the bash_validator before blocking a dangerous command.
@@ -533,6 +532,7 @@ def check_approval_grant(command: str) -> Optional[ApprovalGrant]:
533
532
 
534
533
  Args:
535
534
  command: The shell command to check.
535
+ session_id: Session ID for grant scoping (defaults to env var).
536
536
 
537
537
  Returns:
538
538
  The matching ApprovalGrant if found and valid, None otherwise.
@@ -540,7 +540,8 @@ def check_approval_grant(command: str) -> Optional[ApprovalGrant]:
540
540
  global _last_check_found_expired
541
541
  _last_check_found_expired = False
542
542
 
543
- session_id = _get_session_id()
543
+ if not session_id:
544
+ session_id = _get_session_id()
544
545
 
545
546
  try:
546
547
  grants_dir = _get_grants_dir()
@@ -587,7 +588,60 @@ def check_approval_grant(command: str) -> Optional[ApprovalGrant]:
587
588
  return None
588
589
 
589
590
 
590
- def confirm_grant(command: str) -> bool:
591
+ def consume_grant(command: str, session_id: str = None) -> bool:
592
+ """Mark the first matching valid grant as used and persist to disk.
593
+
594
+ Called by bash_validator immediately after check_approval_grant() returns
595
+ a match, so that the grant can only be used once (single-use).
596
+
597
+ Args:
598
+ command: The shell command whose grant should be consumed.
599
+ session_id: Session ID for grant scoping (defaults to env var).
600
+
601
+ Returns:
602
+ True if a grant was found and consumed, False otherwise.
603
+ """
604
+ if not session_id:
605
+ session_id = _get_session_id()
606
+
607
+ try:
608
+ grants_dir = _get_grants_dir()
609
+ if not grants_dir.exists():
610
+ return False
611
+
612
+ for grant_file in sorted(grants_dir.glob(f"grant-{session_id}-*.json")):
613
+ try:
614
+ data = json.loads(grant_file.read_text())
615
+ grant = ApprovalGrant(**data)
616
+
617
+ if not grant.is_valid():
618
+ if grant.is_expired():
619
+ _cleanup_grant(grant_file)
620
+ continue
621
+
622
+ signature = grant.get_signature()
623
+ if signature is None or signature.scope_type not in SUPPORTED_SCOPE_TYPES:
624
+ continue
625
+
626
+ if grant.matches_command(command):
627
+ data["used"] = True
628
+ grant_file.write_text(json.dumps(data, indent=2))
629
+ logger.info(
630
+ "Grant consumed (single-use): command='%s', grant=%s",
631
+ command[:80], grant_file.name,
632
+ )
633
+ return True
634
+
635
+ except (json.JSONDecodeError, TypeError):
636
+ continue
637
+
638
+ except Exception as e:
639
+ logger.error("Error consuming grant: %s", e)
640
+
641
+ return False
642
+
643
+
644
+ def confirm_grant(command: str, session_id: str = None) -> bool:
591
645
  """Mark the first unconfirmed grant matching command as confirmed.
592
646
 
593
647
  Called after the native permission dialog accepts the first T3 execution.
@@ -596,11 +650,13 @@ def confirm_grant(command: str) -> bool:
596
650
 
597
651
  Args:
598
652
  command: The shell command whose grant should be confirmed.
653
+ session_id: Session ID for grant scoping (defaults to env var).
599
654
 
600
655
  Returns:
601
656
  True if a grant was found and confirmed, False otherwise.
602
657
  """
603
- session_id = _get_session_id()
658
+ if not session_id:
659
+ session_id = _get_session_id()
604
660
 
605
661
  try:
606
662
  grants_dir = _get_grants_dir()
@@ -721,6 +777,133 @@ def cleanup_expired_grants() -> int:
721
777
  return cleaned
722
778
 
723
779
 
780
+ def get_pending_approvals_for_session(
781
+ session_id: Optional[str] = None,
782
+ ) -> List[Dict[str, Any]]:
783
+ """Return all non-expired pending approvals for a session.
784
+
785
+ Args:
786
+ session_id: Session ID to filter by (defaults to current session).
787
+
788
+ Returns:
789
+ List of pending approval dicts, newest first.
790
+ """
791
+ if session_id is None:
792
+ session_id = _get_session_id()
793
+
794
+ results: List[Dict[str, Any]] = []
795
+ try:
796
+ grants_dir = _get_grants_dir()
797
+ for pending_file in grants_dir.glob("pending-*.json"):
798
+ if pending_file.name.startswith("pending-index-"):
799
+ continue
800
+ data = _read_json_file(pending_file)
801
+ if not data or data.get("session_id") != session_id:
802
+ continue
803
+ timestamp = data.get("timestamp", 0)
804
+ ttl = data.get("ttl_minutes", DEFAULT_GRANT_TTL_MINUTES)
805
+ if _is_ttl_expired(float(timestamp), int(ttl)):
806
+ continue
807
+ results.append(data)
808
+ except Exception as e:
809
+ logger.error("Error listing pending approvals for session %s: %s", session_id, e)
810
+
811
+ results.sort(key=lambda d: d.get("timestamp", 0), reverse=True)
812
+ return results
813
+
814
+
815
+ def find_pending_for_command(
816
+ session_id: str,
817
+ command: str,
818
+ ) -> Optional[str]:
819
+ """Find an existing pending approval nonce for this command and session.
820
+
821
+ When a subagent retries a blocked T3 command, a pending approval may
822
+ already exist from the first attempt. Reusing the existing nonce
823
+ prevents the infinite-loop of generating a new approval_id on every
824
+ retry while the user is still reviewing the first one.
825
+
826
+ Args:
827
+ session_id: Session to search.
828
+ command: The command to match against pending approvals.
829
+
830
+ Returns:
831
+ The nonce (approval_id) if a matching pending approval exists, else None.
832
+ """
833
+ pending_list = get_pending_approvals_for_session(session_id)
834
+ if not pending_list:
835
+ return None
836
+
837
+ # Build a signature for the incoming command to compare semantically
838
+ target_sig = build_approval_signature(
839
+ command,
840
+ scope_type=SCOPE_SEMANTIC_SIGNATURE,
841
+ )
842
+ if target_sig is None:
843
+ return None
844
+
845
+ for pending_data in pending_list:
846
+ pending_sig_data = pending_data.get("scope_signature")
847
+ if not pending_sig_data:
848
+ continue
849
+ try:
850
+ pending_sig = ApprovalSignature.from_dict(pending_sig_data)
851
+ if matches_approval_signature(pending_sig, command):
852
+ nonce = pending_data.get("nonce")
853
+ if nonce:
854
+ logger.info(
855
+ "Reusing existing pending approval nonce=%s for command: %s",
856
+ nonce, command[:80],
857
+ )
858
+ return nonce
859
+ except Exception:
860
+ continue
861
+
862
+ return None
863
+
864
+
865
+ def activate_grants_for_session(
866
+ session_id: Optional[str] = None,
867
+ ttl_minutes: int = DEFAULT_GRANT_TTL_MINUTES,
868
+ ) -> List[ApprovalActivationResult]:
869
+ """Activate ALL pending approvals for a session.
870
+
871
+ Called by the ElicitationResult hook when the user approves via
872
+ AskUserQuestion. Converts every non-expired pending approval for the
873
+ session into an active grant.
874
+
875
+ Args:
876
+ session_id: Session to activate for (defaults to current session).
877
+ ttl_minutes: TTL for the resulting active grants.
878
+
879
+ Returns:
880
+ List of activation results (one per pending approval).
881
+ """
882
+ if session_id is None:
883
+ session_id = _get_session_id()
884
+
885
+ pending_list = get_pending_approvals_for_session(session_id)
886
+ results: List[ApprovalActivationResult] = []
887
+
888
+ for pending_data in pending_list:
889
+ nonce = pending_data.get("nonce", "")
890
+ if not nonce:
891
+ continue
892
+ result = activate_pending_approval(
893
+ nonce=nonce,
894
+ session_id=session_id,
895
+ ttl_minutes=ttl_minutes,
896
+ )
897
+ results.append(result)
898
+ logger.info(
899
+ "Session-wide activation: nonce=%s status=%s",
900
+ nonce,
901
+ getattr(result.status, "value", str(result.status)),
902
+ )
903
+
904
+ return results
905
+
906
+
724
907
  def _cleanup_grant(grant_file: Path) -> None:
725
908
  """Remove a single grant or pending file."""
726
909
  try:
@@ -1,12 +1,11 @@
1
1
  """Canonical approval/resume text used by hooks, skills, and tests."""
2
+ from __future__ import annotations
2
3
 
3
4
  from .approval_constants import NONCE_APPROVAL_PREFIX
4
5
 
5
6
  CANONICAL_APPROVAL_TOKEN = "APPROVE:<nonce>"
6
7
  CANONICAL_APPROVAL_TOKEN_FORMAT = f"{NONCE_APPROVAL_PREFIX}<32-char-hex>"
7
8
  LATEST_BLOCKED_COMMAND_PHRASE = "latest blocked command"
8
- AWAITING_APPROVAL_STATUS = "AWAITING_APPROVAL"
9
-
10
9
  CANONICAL_APPROVAL_TOKEN_GUIDANCE = (
11
10
  f"Use only {CANONICAL_APPROVAL_TOKEN} from the {LATEST_BLOCKED_COMMAND_PHRASE}."
12
11
  )
@@ -59,25 +58,14 @@ def build_pending_approval_unavailable_message() -> str:
59
58
 
60
59
 
61
60
  def build_t3_approval_instructions(nonce: str | None = None) -> str:
62
- """Return explicit T3 approval workflow steps for blocked commands."""
63
- if nonce:
64
- step_two = f"2. Include the approval code NONCE:{nonce} in your {AWAITING_APPROVAL_STATUS} output.\n"
65
- step_four = (
66
- f"4. Wait for explicit user approval. When resumed, expect APPROVE:{nonce} "
67
- f"and then retry the command. {CANONICAL_APPROVAL_TOKEN_GUIDANCE}\n"
68
- )
69
- else:
70
- step_two = f"2. Retry the blocked command if you need a fresh approval code for {AWAITING_APPROVAL_STATUS}.\n"
71
- step_four = (
72
- f"4. Wait for explicit user approval before executing. {CANONICAL_APPROVAL_TOKEN_GUIDANCE}\n"
73
- )
61
+ """Return T3 approval block data.
74
62
 
63
+ Kept minimal: just the facts (tier, nonce). Workflow instructions
64
+ live in skills (approval, orchestrator-approval, security-tiers) so
65
+ the hook doesn't duplicate or conflict with them.
66
+ """
67
+ nonce_line = f"NONCE:{nonce}" if nonce else "NONCE:unavailable (retry command to generate)"
75
68
  return (
76
- "This is a T3 (state-modifying) operation. Follow the approval workflow:\n"
77
- "1. Present a plan with scope, impact, and rollback steps.\n"
78
- f"{step_two}"
79
- f"3. Set PLAN_STATUS: {AWAITING_APPROVAL_STATUS}.\n"
80
- f"{step_four}"
81
- "5. Include an `approval_request` object in your json:contract with:\n"
82
- " operation, exact_content, scope, risk_level, rollback, verification\n"
69
+ f"[T3_APPROVAL_REQUIRED] {nonce_line}\n"
70
+ "Load the approval skill for next steps."
83
71
  )
@@ -2,14 +2,15 @@
2
2
  Blocked command patterns - PERMANENTLY BLOCKED operations (exit 2, never approvable).
3
3
 
4
4
  This is the single source of truth for DESTRUCTIVE commands. Commands matched here
5
- are blocked with exit 2 and no nonce is generated -- they cannot be approved.
5
+ are blocked with exit 2 and cannot be approved.
6
6
 
7
7
  All other state-modifying commands are detected by the universal verb detector
8
- (mutative_verbs.py) as MUTATIVE and routed through the nonce approval workflow.
8
+ (mutative_verbs.py) as MUTATIVE and routed through the user approval workflow.
9
9
 
10
10
  Categories:
11
11
  - AWS networking/data infrastructure delete operations
12
12
  - AWS KMS/Route53/Organizations operations
13
+ - Azure resource group/networking/data/AKS/Key Vault delete operations
13
14
  - GCP project/cluster/database delete operations
14
15
  - Kubernetes critical delete operations (cluster, namespace, pv, node, CRD, webhooks)
15
16
  - Kubernetes bulk delete operations (--all flag)
@@ -127,6 +128,43 @@ BLOCKED_PATTERNS: Dict[str, List[re.Pattern]] = {
127
128
  re.compile(r"aws\s+route53\s+delete-hosted-zone\b", re.IGNORECASE),
128
129
  ],
129
130
 
131
+ # Azure - Resource group, networking, data infrastructure (irreversible)
132
+ "azure_critical": [
133
+ re.compile(r"az\s+group\s+delete\b", re.IGNORECASE),
134
+ re.compile(r"az\s+network\s+vnet\s+delete\b", re.IGNORECASE),
135
+ re.compile(r"az\s+network\s+vnet\s+subnet\s+delete\b", re.IGNORECASE),
136
+ re.compile(r"az\s+network\s+nsg\s+delete\b", re.IGNORECASE),
137
+ re.compile(r"az\s+network\s+public-ip\s+delete\b", re.IGNORECASE),
138
+ re.compile(r"az\s+network\s+application-gateway\s+delete\b", re.IGNORECASE),
139
+ re.compile(r"az\s+network\s+lb\s+delete\b", re.IGNORECASE),
140
+ re.compile(r"az\s+network\s+dns\s+zone\s+delete\b", re.IGNORECASE),
141
+ re.compile(r"az\s+network\s+private-dns\s+zone\s+delete\b", re.IGNORECASE),
142
+ re.compile(r"az\s+vm\s+delete\b", re.IGNORECASE),
143
+ re.compile(r"az\s+vmss\s+delete\b", re.IGNORECASE),
144
+ re.compile(r"az\s+disk\s+delete\b", re.IGNORECASE),
145
+ re.compile(r"az\s+snapshot\s+delete\b", re.IGNORECASE),
146
+ re.compile(r"az\s+image\s+delete\b", re.IGNORECASE),
147
+ re.compile(r"az\s+sql\s+server\s+delete\b", re.IGNORECASE),
148
+ re.compile(r"az\s+sql\s+db\s+delete\b", re.IGNORECASE),
149
+ re.compile(r"az\s+cosmosdb\s+delete\b", re.IGNORECASE),
150
+ re.compile(r"az\s+redis\s+delete\b", re.IGNORECASE),
151
+ re.compile(r"az\s+storage\s+account\s+delete\b", re.IGNORECASE),
152
+ re.compile(r"az\s+storage\s+container\s+delete\b", re.IGNORECASE),
153
+ re.compile(r"az\s+storage\s+blob\s+delete-batch\b", re.IGNORECASE),
154
+ re.compile(r"az\s+aks\s+delete\b", re.IGNORECASE),
155
+ re.compile(r"az\s+aks\s+nodepool\s+delete\b", re.IGNORECASE),
156
+ re.compile(r"az\s+acr\s+delete\b", re.IGNORECASE),
157
+ re.compile(r"az\s+keyvault\s+delete\b", re.IGNORECASE),
158
+ re.compile(r"az\s+keyvault\s+key\s+delete\b", re.IGNORECASE),
159
+ re.compile(r"az\s+keyvault\s+secret\s+delete\b", re.IGNORECASE),
160
+ re.compile(r"az\s+functionapp\s+delete\b", re.IGNORECASE),
161
+ re.compile(r"az\s+webapp\s+delete\b", re.IGNORECASE),
162
+ re.compile(r"az\s+ad\s+app\s+delete\b", re.IGNORECASE),
163
+ re.compile(r"az\s+ad\s+sp\s+delete\b", re.IGNORECASE),
164
+ re.compile(r"az\s+servicebus\s+namespace\s+delete\b", re.IGNORECASE),
165
+ re.compile(r"az\s+eventhubs\s+namespace\s+delete\b", re.IGNORECASE),
166
+ ],
167
+
130
168
  # GCP - Project, cluster, and database operations (irreversible)
131
169
  "gcp_critical": [
132
170
  re.compile(r"gcloud\s+projects\s+delete\b", re.IGNORECASE),
@@ -240,6 +278,29 @@ BLOCKED_COMMAND_SUGGESTIONS = {
240
278
  "aws organizations delete-organization": "[BLOCKED] Organization deletion is irreversible",
241
279
  "aws route53 delete-hosted-zone": "[BLOCKED] DNS zone deletion causes widespread outage",
242
280
 
281
+ # Azure suggestions
282
+ "az group delete": "[BLOCKED] Resource group deletion destroys all contained resources - use Terraform/Terragrunt",
283
+ "az network vnet delete": "[BLOCKED] VNet deletion is irreversible - use Terraform/Terragrunt",
284
+ "az network vnet subnet delete": "[BLOCKED] Subnet deletion is irreversible - use Terraform/Terragrunt",
285
+ "az network nsg delete": "[BLOCKED] NSG deletion removes all security rules - use Terraform/Terragrunt",
286
+ "az vm delete": "[BLOCKED] VM deletion is irreversible - use Terraform/Terragrunt",
287
+ "az vmss delete": "[BLOCKED] Scale set deletion is irreversible - use Terraform/Terragrunt",
288
+ "az disk delete": "[BLOCKED] Disk deletion loses all data - use Terraform/Terragrunt",
289
+ "az sql server delete": "[BLOCKED] SQL Server deletion destroys all databases - use Terraform/Terragrunt",
290
+ "az sql db delete": "[BLOCKED] Database deletion loses all data - use Terraform/Terragrunt",
291
+ "az cosmosdb delete": "[BLOCKED] CosmosDB deletion is irreversible - use Terraform/Terragrunt",
292
+ "az redis delete": "[BLOCKED] Redis deletion loses all cached data - use Terraform/Terragrunt",
293
+ "az storage account delete": "[BLOCKED] Storage account deletion destroys all data - use Terraform/Terragrunt",
294
+ "az aks delete": "[BLOCKED] AKS cluster deletion is irreversible - use Terraform/Terragrunt",
295
+ "az acr delete": "[BLOCKED] Container registry deletion destroys all images - use Terraform/Terragrunt",
296
+ "az keyvault delete": "[BLOCKED] Key Vault deletion can render encrypted data unrecoverable",
297
+ "az functionapp delete": "[BLOCKED] Function App deletion is irreversible - use Terraform/Terragrunt",
298
+ "az webapp delete": "[BLOCKED] Web App deletion is irreversible - use Terraform/Terragrunt",
299
+ "az ad app delete": "[BLOCKED] App registration deletion breaks all dependent services",
300
+ "az ad sp delete": "[BLOCKED] Service principal deletion breaks authentication for dependent services",
301
+ "az servicebus namespace delete": "[BLOCKED] Service Bus namespace deletion is irreversible - use Terraform/Terragrunt",
302
+ "az eventhubs namespace delete": "[BLOCKED] Event Hubs namespace deletion is irreversible - use Terraform/Terragrunt",
303
+
243
304
  # GCP suggestions
244
305
  "gcloud projects delete": "[BLOCKED] Project deletion is irreversible - must be done via Cloud Console",
245
306
  "gcloud container clusters delete": "[BLOCKED] Use Terraform/Terragrunt for GKE management",
@@ -335,6 +396,41 @@ SEMANTIC_BLOCKED_RULES = (
335
396
  SemanticBlockedRule("aws_critical", ("aws", "organizations", "delete-organization"), "aws organizations delete-organization"),
336
397
  SemanticBlockedRule("aws_critical", ("aws", "route53", "delete-hosted-zone"), "aws route53 delete-hosted-zone"),
337
398
 
399
+ # Azure
400
+ SemanticBlockedRule("azure_critical", ("az", "group", "delete"), "az group delete"),
401
+ SemanticBlockedRule("azure_critical", ("az", "network", "vnet", "delete"), "az network vnet delete"),
402
+ SemanticBlockedRule("azure_critical", ("az", "network", "vnet", "subnet", "delete"), "az network vnet subnet delete"),
403
+ SemanticBlockedRule("azure_critical", ("az", "network", "nsg", "delete"), "az network nsg delete"),
404
+ SemanticBlockedRule("azure_critical", ("az", "network", "public-ip", "delete"), "az network vnet delete"),
405
+ SemanticBlockedRule("azure_critical", ("az", "network", "application-gateway", "delete"), "az network vnet delete"),
406
+ SemanticBlockedRule("azure_critical", ("az", "network", "lb", "delete"), "az network vnet delete"),
407
+ SemanticBlockedRule("azure_critical", ("az", "network", "dns", "zone", "delete"), "az network vnet delete"),
408
+ SemanticBlockedRule("azure_critical", ("az", "network", "private-dns", "zone", "delete"), "az network vnet delete"),
409
+ SemanticBlockedRule("azure_critical", ("az", "vm", "delete"), "az vm delete"),
410
+ SemanticBlockedRule("azure_critical", ("az", "vmss", "delete"), "az vmss delete"),
411
+ SemanticBlockedRule("azure_critical", ("az", "disk", "delete"), "az disk delete"),
412
+ SemanticBlockedRule("azure_critical", ("az", "snapshot", "delete"), "az disk delete"),
413
+ SemanticBlockedRule("azure_critical", ("az", "image", "delete"), "az disk delete"),
414
+ SemanticBlockedRule("azure_critical", ("az", "sql", "server", "delete"), "az sql server delete"),
415
+ SemanticBlockedRule("azure_critical", ("az", "sql", "db", "delete"), "az sql db delete"),
416
+ SemanticBlockedRule("azure_critical", ("az", "cosmosdb", "delete"), "az cosmosdb delete"),
417
+ SemanticBlockedRule("azure_critical", ("az", "redis", "delete"), "az redis delete"),
418
+ SemanticBlockedRule("azure_critical", ("az", "storage", "account", "delete"), "az storage account delete"),
419
+ SemanticBlockedRule("azure_critical", ("az", "storage", "container", "delete"), "az storage account delete"),
420
+ SemanticBlockedRule("azure_critical", ("az", "storage", "blob", "delete-batch"), "az storage account delete"),
421
+ SemanticBlockedRule("azure_critical", ("az", "aks", "delete"), "az aks delete"),
422
+ SemanticBlockedRule("azure_critical", ("az", "aks", "nodepool", "delete"), "az aks delete"),
423
+ SemanticBlockedRule("azure_critical", ("az", "acr", "delete"), "az acr delete"),
424
+ SemanticBlockedRule("azure_critical", ("az", "keyvault", "delete"), "az keyvault delete"),
425
+ SemanticBlockedRule("azure_critical", ("az", "keyvault", "key", "delete"), "az keyvault delete"),
426
+ SemanticBlockedRule("azure_critical", ("az", "keyvault", "secret", "delete"), "az keyvault delete"),
427
+ SemanticBlockedRule("azure_critical", ("az", "functionapp", "delete"), "az functionapp delete"),
428
+ SemanticBlockedRule("azure_critical", ("az", "webapp", "delete"), "az webapp delete"),
429
+ SemanticBlockedRule("azure_critical", ("az", "ad", "app", "delete"), "az ad app delete"),
430
+ SemanticBlockedRule("azure_critical", ("az", "ad", "sp", "delete"), "az ad sp delete"),
431
+ SemanticBlockedRule("azure_critical", ("az", "servicebus", "namespace", "delete"), "az servicebus namespace delete"),
432
+ SemanticBlockedRule("azure_critical", ("az", "eventhubs", "namespace", "delete"), "az eventhubs namespace delete"),
433
+
338
434
  # GCP
339
435
  SemanticBlockedRule("gcp_critical", ("gcloud", "projects", "delete"), "gcloud projects delete"),
340
436
  SemanticBlockedRule(
@@ -432,18 +528,6 @@ def get_blocked_patterns() -> List[re.Pattern]:
432
528
  return patterns
433
529
 
434
530
 
435
- def get_blocked_patterns_by_category(category: str) -> List[re.Pattern]:
436
- """
437
- Get blocked patterns for a specific category.
438
-
439
- Args:
440
- category: Category name (aws_critical, kubernetes_critical, etc.)
441
-
442
- Returns:
443
- List of compiled regex patterns for that category
444
- """
445
- return BLOCKED_PATTERNS.get(category, [])
446
-
447
531
 
448
532
  def is_blocked_command(command: str) -> BlockedCommandResult:
449
533
  """
@@ -490,26 +574,6 @@ def is_blocked_command(command: str) -> BlockedCommandResult:
490
574
  return BlockedCommandResult(is_blocked=False)
491
575
 
492
576
 
493
- def get_suggestion_for_blocked(command: str) -> Optional[str]:
494
- """
495
- Get a safe alternative suggestion for a blocked command.
496
-
497
- Args:
498
- command: The blocked command
499
-
500
- Returns:
501
- Suggestion string or None
502
- """
503
- semantic_rule = _match_semantic_block_rule(command)
504
- if semantic_rule is not None:
505
- return BLOCKED_COMMAND_SUGGESTIONS.get(semantic_rule.suggestion_key)
506
-
507
- command_lower = command.lower()
508
- for cmd_prefix, suggestion in BLOCKED_COMMAND_SUGGESTIONS.items():
509
- if cmd_prefix in command_lower:
510
- return suggestion
511
- return None
512
-
513
577
 
514
578
  def _match_semantic_block_rule(command: str) -> Optional[SemanticBlockedRule]:
515
579
  """Return the first semantic deny rule that matches a command."""
@@ -38,10 +38,6 @@ class CommandSemantics:
38
38
  """Return the canonical analysis form of the command."""
39
39
  return " ".join(self.semantic_tokens)
40
40
 
41
- def has_flag(self, flag: str) -> bool:
42
- """Check whether a normalized flag is present."""
43
- return flag.lower() in self.flag_tokens
44
-
45
41
 
46
42
  def tokenize_command(command: str) -> Tuple[str, ...]:
47
43
  """Tokenize a shell command safely, preserving quoted substrings."""
@@ -9,7 +9,7 @@ Ensures commands follow GitOps principles:
9
9
 
10
10
  import re
11
11
  import logging
12
- from typing import Dict, Any, List, Optional
12
+ from typing import List, Optional
13
13
  from dataclasses import dataclass, field
14
14
 
15
15
  logger = logging.getLogger(__name__)
@@ -177,13 +177,3 @@ def validate_gitops_workflow(
177
177
  severity="warning",
178
178
  suggestions=["Verify command follows GitOps principles"],
179
179
  )
180
-
181
-
182
- def to_dict(result: GitOpsValidationResult) -> Dict[str, Any]:
183
- """Convert GitOpsValidationResult to dictionary for backward compatibility."""
184
- return {
185
- "allowed": result.allowed,
186
- "reason": result.reason,
187
- "severity": result.severity,
188
- "suggestions": result.suggestions,
189
- }