@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
@@ -0,0 +1,476 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Context Provider for Claude Agent System
4
+
5
+ Generates structured context payloads for agents based on:
6
+ 1. Agent contracts (context-contracts.json + cloud overlays)
7
+ 2. Universal rules (universal-rules.json)
8
+ 3. Historical episodes (episodic memory)
9
+
10
+ Usage:
11
+ python3 context_provider.py <agent_name> [user_task] [--context-file PATH]
12
+ """
13
+
14
+ import json
15
+ import argparse
16
+ import sys
17
+ from pathlib import Path
18
+ from typing import Dict, List, Any, Optional
19
+
20
+ try:
21
+ from ._paths import resolve_config_dir
22
+ from .surface_router import (
23
+ build_investigation_brief,
24
+ classify_surfaces,
25
+ load_surface_routing_config,
26
+ )
27
+ except ImportError:
28
+ from _paths import resolve_config_dir
29
+ from surface_router import (
30
+ build_investigation_brief,
31
+ classify_surfaces,
32
+ load_surface_routing_config,
33
+ )
34
+
35
+ # Default paths
36
+ DEFAULT_CONTEXT_PATH = Path(".claude/project-context/project-context.json")
37
+
38
+
39
+ # ============================================================================
40
+ # CONTRACTS DIRECTORY RESOLUTION
41
+ # ============================================================================
42
+
43
+ def get_contracts_dir():
44
+ """Determines the correct contracts directory based on execution context."""
45
+ return resolve_config_dir()
46
+
47
+
48
+ DEFAULT_CONTRACTS_DIR = get_contracts_dir()
49
+
50
+
51
+ # ============================================================================
52
+ # UNIVERSAL RULES SYSTEM
53
+ # ============================================================================
54
+
55
+ DEFAULT_RULES_FILE = "universal-rules.json"
56
+
57
+
58
+ def load_universal_rules(agent_name: str, rules_file: Optional[Path] = None) -> Dict[str, Any]:
59
+ """Load universal rules and agent-specific rules from JSON file."""
60
+ if rules_file is None:
61
+ rules_file = get_contracts_dir() / DEFAULT_RULES_FILE
62
+
63
+ if not rules_file.is_file():
64
+ print(f"Warning: Rules file not found: {rules_file}", file=sys.stderr)
65
+ return {"universal": [], "agent_specific": []}
66
+
67
+ try:
68
+ with open(rules_file, 'r', encoding='utf-8') as f:
69
+ rules_data = json.load(f)
70
+
71
+ universal = [r["rule"] for r in rules_data.get("rules", {}).get("universal", [])]
72
+ agent_specific = [
73
+ r["rule"]
74
+ for r in rules_data.get("rules", {}).get("agent_specific", {}).get(agent_name, [])
75
+ ]
76
+
77
+ total_rules = len(universal) + len(agent_specific)
78
+ if total_rules > 0:
79
+ print(f"Loaded {len(universal)} universal rules, {len(agent_specific)} agent-specific", file=sys.stderr)
80
+
81
+ return {
82
+ "universal": universal,
83
+ "agent_specific": agent_specific
84
+ }
85
+ except Exception as e:
86
+ print(f"Warning: Could not load rules: {e}", file=sys.stderr)
87
+ return {"universal": [], "agent_specific": []}
88
+
89
+
90
+ # ============================================================================
91
+ # CLOUD PROVIDER DETECTION
92
+ # ============================================================================
93
+
94
+ def detect_cloud_provider(project_context: Dict[str, Any]) -> str:
95
+ """Detects the cloud provider from project-context.json.
96
+
97
+ Detection priority:
98
+ 1. metadata.cloud_provider (explicit user/scanner setting)
99
+ 2. infrastructure.cloud_providers[0].name (v2 scanner section)
100
+ 3. metadata.project_id presence -> gcp
101
+ 4. Fallback -> gcp
102
+ """
103
+ metadata = project_context.get("metadata", {})
104
+ if "cloud_provider" in metadata:
105
+ provider = metadata["cloud_provider"].lower()
106
+ if provider == "multi-cloud":
107
+ print("Multi-cloud detected, using GCP contracts as primary", file=sys.stderr)
108
+ return "gcp"
109
+ return provider
110
+
111
+ sections = project_context.get("sections", {})
112
+
113
+ # v2: read from infrastructure.cloud_providers
114
+ infra = sections.get("infrastructure", {})
115
+ if isinstance(infra, dict):
116
+ cloud_providers = infra.get("cloud_providers", [])
117
+ if isinstance(cloud_providers, list) and cloud_providers:
118
+ primary = cloud_providers[0]
119
+ if isinstance(primary, dict):
120
+ name = primary.get("name", "")
121
+ if name:
122
+ provider = name.lower()
123
+ if provider == "multi-cloud":
124
+ return "gcp"
125
+ return provider
126
+
127
+ if "project_id" in metadata:
128
+ return "gcp"
129
+
130
+ print("Could not detect cloud provider, defaulting to GCP", file=sys.stderr)
131
+ return "gcp"
132
+
133
+
134
+ def load_provider_contracts(cloud_provider: str, contracts_dir: Path = DEFAULT_CONTRACTS_DIR) -> Dict[str, Any]:
135
+ """
136
+ Loads context contracts using the base+cloud merge strategy.
137
+
138
+ Strategy:
139
+ 1. Load base contracts from context-contracts.json (cloud-agnostic)
140
+ 2. Load cloud overrides from cloud/{provider}.json and merge (extend) read/write lists
141
+ 3. If base contracts missing → error (contracts are the single source of truth)
142
+ """
143
+ base_file = contracts_dir / "context-contracts.json"
144
+ cloud_file = contracts_dir / "cloud" / f"{cloud_provider}.json"
145
+
146
+ # --- Step 1: Load base contracts ---
147
+ if not base_file.is_file():
148
+ print(f"Error: Contract file not found at {base_file}", file=sys.stderr)
149
+ sys.exit(1)
150
+
151
+ try:
152
+ with open(base_file, 'r', encoding='utf-8') as f:
153
+ base_contracts = json.load(f)
154
+ print(f"Loaded base contracts from {base_file}", file=sys.stderr)
155
+ except json.JSONDecodeError as e:
156
+ print(f"Error: Invalid JSON in {base_file}: {e}", file=sys.stderr)
157
+ sys.exit(1)
158
+
159
+ # --- Step 2: Merge cloud-specific overrides ---
160
+ if cloud_file.is_file():
161
+ try:
162
+ with open(cloud_file, 'r', encoding='utf-8') as f:
163
+ cloud_overrides = json.load(f)
164
+ print(f"Loaded {cloud_provider.upper()} cloud overrides from {cloud_file}", file=sys.stderr)
165
+
166
+ for agent_name, agent_overrides in cloud_overrides.get("agents", {}).items():
167
+ if agent_name in base_contracts.get("agents", {}):
168
+ existing_read = base_contracts["agents"][agent_name].get("read", [])
169
+ existing_write = base_contracts["agents"][agent_name].get("write", [])
170
+ extra_read = [s for s in agent_overrides.get("read", []) if s not in existing_read]
171
+ extra_write = [s for s in agent_overrides.get("write", []) if s not in existing_write]
172
+ base_contracts["agents"][agent_name]["read"] = existing_read + extra_read
173
+ base_contracts["agents"][agent_name]["write"] = existing_write + extra_write
174
+ else:
175
+ base_contracts["agents"][agent_name] = agent_overrides
176
+
177
+ except json.JSONDecodeError as e:
178
+ print(f"Warning: Invalid JSON in {cloud_file}: {e} — skipping cloud overrides", file=sys.stderr)
179
+ else:
180
+ print(f"No cloud overrides found at {cloud_file}, using base contracts only", file=sys.stderr)
181
+
182
+ return {
183
+ "version": base_contracts.get("version", "unknown"),
184
+ "provider": cloud_provider,
185
+ "agents": base_contracts.get("agents", {})
186
+ }
187
+
188
+
189
+ def load_project_context(context_path: Path) -> Dict[str, Any]:
190
+ """Loads the project context from the specified JSON file."""
191
+ if not context_path.is_file():
192
+ print(f"Error: Context file not found at {context_path}", file=sys.stderr)
193
+ sys.exit(1)
194
+ with open(context_path, 'r', encoding='utf-8') as f:
195
+ return json.load(f)
196
+
197
+
198
+ # ============================================================================
199
+ # CONTEXT EXTRACTION
200
+ # ============================================================================
201
+
202
+ def get_relevant_sections(
203
+ sections: Dict[str, Any],
204
+ contract_keys: List[str],
205
+ surface_routing: Optional[Dict[str, Any]] = None,
206
+ routing_config: Optional[Dict[str, Any]] = None,
207
+ ) -> Dict[str, Any]:
208
+ """Filter sections by surface relevance, with fallback to all readable sections.
209
+
210
+ Args:
211
+ sections: All available sections from project-context.json.
212
+ contract_keys: The agent's permitted read keys (from context-contracts).
213
+ surface_routing: The routing result from classify_surfaces().
214
+ routing_config: The full surface-routing.json config (has contract_sections per surface).
215
+
216
+ Returns:
217
+ Filtered dict of sections. Falls back to all readable sections when:
218
+ - No surface_routing or routing_config provided
219
+ - No active surfaces detected
220
+ - Surface has no contract_sections defined
221
+ - Intersection of surface sections and agent permissions is empty
222
+ """
223
+ all_readable = {k: sections[k] for k in contract_keys if k in sections}
224
+
225
+ if not surface_routing or not routing_config:
226
+ return all_readable
227
+
228
+ active_surfaces = surface_routing.get("active_surfaces", [])
229
+ if not active_surfaces:
230
+ return all_readable
231
+
232
+ surfaces_cfg = routing_config.get("surfaces", {})
233
+
234
+ # Collect relevant sections from all active surfaces
235
+ relevant: set = set()
236
+ for surface in active_surfaces:
237
+ surface_config = surfaces_cfg.get(surface, {})
238
+ surface_sections = surface_config.get("contract_sections", [])
239
+ relevant.update(surface_sections)
240
+
241
+ if not relevant:
242
+ # Surfaces have no contract_sections defined -- inject all (fallback)
243
+ return all_readable
244
+
245
+ # Filter: agent permissions AND surface relevance
246
+ filtered = {k: sections[k] for k in contract_keys if k in sections and k in relevant}
247
+
248
+ if not filtered:
249
+ # Nothing matched -- inject all (fallback)
250
+ return all_readable
251
+
252
+ omitted = set(all_readable.keys()) - set(filtered.keys())
253
+ if omitted:
254
+ print(
255
+ f"Surface gating: {len(filtered)} sections injected, "
256
+ f"{len(omitted)} omitted ({', '.join(sorted(omitted))})",
257
+ file=sys.stderr,
258
+ )
259
+ else:
260
+ print(
261
+ f"Surface gating: all {len(filtered)} readable sections match active surfaces",
262
+ file=sys.stderr,
263
+ )
264
+
265
+ return filtered
266
+
267
+
268
+ def get_contract_context(
269
+ project_context: Dict[str, Any],
270
+ agent_name: str,
271
+ provider_contracts: Dict[str, Any],
272
+ surface_routing: Optional[Dict[str, Any]] = None,
273
+ routing_config: Optional[Dict[str, Any]] = None,
274
+ ) -> Dict[str, Any]:
275
+ """Extracts the contract-defined context sections for a given agent.
276
+
277
+ When surface_routing and routing_config are provided, sections are filtered
278
+ to only those relevant to the active surface(s). Falls back to returning
279
+ all readable sections when routing is unavailable or yields an empty set.
280
+ """
281
+ agent_contract = provider_contracts.get("agents", {}).get(agent_name)
282
+ if not agent_contract:
283
+ print(f"ERROR: Invalid agent '{agent_name}'. Available: {list(provider_contracts.get('agents', {}).keys())}", file=sys.stderr)
284
+ sys.exit(1)
285
+
286
+ contract_keys = agent_contract.get("read", [])
287
+
288
+ sections = project_context.get("sections", {})
289
+ if not sections:
290
+ raise KeyError("project-context.json must contain a 'sections' object.")
291
+
292
+ return get_relevant_sections(
293
+ sections, contract_keys,
294
+ surface_routing=surface_routing,
295
+ routing_config=routing_config,
296
+ )
297
+
298
+
299
+ def get_context_update_contract(
300
+ agent_name: str,
301
+ provider_contracts: Dict[str, Any]
302
+ ) -> Dict[str, Any]:
303
+ """Return the SSOT contract agents should use for CONTEXT_UPDATE decisions."""
304
+ agent_contract = provider_contracts.get("agents", {}).get(agent_name)
305
+ if not agent_contract:
306
+ print(f"ERROR: Invalid agent '{agent_name}'. Available: {list(provider_contracts.get('agents', {}).keys())}", file=sys.stderr)
307
+ sys.exit(1)
308
+
309
+ return {
310
+ "readable_sections": agent_contract.get("read", []),
311
+ "writable_sections": agent_contract.get("write", []),
312
+ "source": "config/context-contracts.json + config/cloud/{provider}.json",
313
+ }
314
+
315
+
316
+ # ============================================================================
317
+ # EPISODIC MEMORY
318
+ # ============================================================================
319
+
320
+ def load_relevant_episodes(user_task: str, max_episodes: int = 2) -> Dict[str, Any]:
321
+ """Load relevant historical episodes for the user's task."""
322
+ try:
323
+ index_file = Path(".claude/project-context/episodic-memory/index.json")
324
+ if not index_file.exists():
325
+ return {}
326
+
327
+ with open(index_file) as f:
328
+ index = json.load(f)
329
+
330
+ task_lower = user_task.lower()
331
+ task_words = set(task_lower.split())
332
+
333
+ relevant_episodes = []
334
+ for episode in index.get("episodes", []):
335
+ score = 0.0
336
+ for tag in episode.get("tags", []):
337
+ if tag.lower() in task_lower:
338
+ score += 0.4
339
+ title_words = set(episode.get("title", "").lower().split())
340
+ common_words = task_words & title_words
341
+ if common_words:
342
+ score += 0.3 * (len(common_words) / max(len(title_words), 1))
343
+
344
+ final_score = score * episode.get("relevance_score", 0.5)
345
+
346
+ if final_score > 0.1:
347
+ full_episode = load_full_episode(episode["id"], index_file.parent)
348
+ if full_episode:
349
+ relevant_episodes.append({
350
+ "id": full_episode["id"],
351
+ "title": full_episode["title"],
352
+ "type": full_episode["type"],
353
+ "relevance": final_score,
354
+ "lessons_learned": full_episode.get("lessons_learned", [])[:2],
355
+ "resolution": full_episode.get("resolution", "")[:200]
356
+ })
357
+
358
+ relevant_episodes.sort(key=lambda x: x["relevance"], reverse=True)
359
+ relevant_episodes = relevant_episodes[:max_episodes]
360
+
361
+ if relevant_episodes:
362
+ print(f"Added {len(relevant_episodes)} historical episodes to context", file=sys.stderr)
363
+ return {
364
+ "episodes": relevant_episodes,
365
+ "summary": f"Found {len(relevant_episodes)} relevant historical episodes"
366
+ }
367
+
368
+ return {}
369
+
370
+ except Exception as e:
371
+ print(f"Warning: Could not load episodic memory: {e}", file=sys.stderr)
372
+ return {}
373
+
374
+
375
+ def load_full_episode(episode_id: str, memory_dir: Path) -> Optional[Dict[str, Any]]:
376
+ """Load full episode details from JSONL file."""
377
+ try:
378
+ episodes_file = memory_dir / "episodes.jsonl"
379
+ if episodes_file.exists():
380
+ with open(episodes_file) as f:
381
+ for line in f:
382
+ try:
383
+ episode = json.loads(line)
384
+ if episode.get("id") == episode_id:
385
+ return episode
386
+ except Exception:
387
+ continue
388
+ except Exception:
389
+ pass
390
+ return None
391
+
392
+
393
+ # ============================================================================
394
+ # MAIN FUNCTION
395
+ # ============================================================================
396
+
397
+ def main():
398
+ """Main function to generate and print the context payload."""
399
+ parser = argparse.ArgumentParser(
400
+ description="Generates a structured context payload for a Claude agent."
401
+ )
402
+ parser.add_argument("agent_name", help="The name of the agent being invoked.")
403
+ parser.add_argument("user_task", nargs="?", default="General inquiry",
404
+ help="The user's task or query for the agent.")
405
+ parser.add_argument(
406
+ "--context-file",
407
+ type=Path,
408
+ default=DEFAULT_CONTEXT_PATH,
409
+ help=f"Path to the project-context.json file. Defaults to '{DEFAULT_CONTEXT_PATH}'"
410
+ )
411
+
412
+ args = parser.parse_args()
413
+
414
+ # Load project context
415
+ project_context = load_project_context(args.context_file)
416
+
417
+ # Detect cloud provider and load contracts
418
+ cloud_provider = detect_cloud_provider(project_context)
419
+ provider_contracts = load_provider_contracts(cloud_provider)
420
+
421
+ # Compute surface routing BEFORE extracting sections so we can gate by surface
422
+ surface_routing_config = load_surface_routing_config()
423
+ surface_routing = classify_surfaces(
424
+ args.user_task,
425
+ current_agent=args.agent_name,
426
+ routing_config=surface_routing_config,
427
+ )
428
+
429
+ # Extract contracted sections (surface-gated when routing is available)
430
+ contract_context = get_contract_context(
431
+ project_context, args.agent_name, provider_contracts,
432
+ surface_routing=surface_routing,
433
+ routing_config=surface_routing_config,
434
+ )
435
+ context_update_contract = get_context_update_contract(args.agent_name, provider_contracts)
436
+
437
+ # Load historical episodes
438
+ historical_context = load_relevant_episodes(args.user_task)
439
+
440
+ # Load universal rules
441
+ rules_context = load_universal_rules(args.agent_name)
442
+ investigation_brief = build_investigation_brief(
443
+ args.user_task,
444
+ args.agent_name,
445
+ contract_context,
446
+ routing_config=surface_routing_config,
447
+ routing=surface_routing,
448
+ )
449
+
450
+ # Build final payload
451
+ final_payload = {
452
+ "project_knowledge": contract_context,
453
+ "write_permissions": context_update_contract,
454
+ "rules": rules_context,
455
+ "surface_routing": surface_routing,
456
+ "investigation_brief": investigation_brief,
457
+ "metadata": {
458
+ "cloud_provider": cloud_provider,
459
+ "contract_version": provider_contracts.get("version", "unknown"),
460
+ "historical_episodes_count": len(historical_context.get("episodes", [])),
461
+ "rules_count": len(rules_context.get("universal", [])) + len(rules_context.get("agent_specific", [])),
462
+ "surface_routing_version": surface_routing_config.get("version", "unknown"),
463
+ "active_surfaces_count": len(surface_routing.get("active_surfaces", [])),
464
+ "surface_routing_confidence": surface_routing.get("confidence", 0.0),
465
+ }
466
+ }
467
+
468
+ # Add historical context if episodes found
469
+ if historical_context:
470
+ final_payload["historical_context"] = historical_context
471
+
472
+ print(json.dumps(final_payload, indent=2))
473
+
474
+
475
+ if __name__ == "__main__":
476
+ main()