@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,427 @@
1
+ """Core context injection subsystem for project agents.
2
+
3
+ Handles:
4
+ - build_project_context: builds context string for additionalContext injection
5
+ - check_pending_updates_threshold: warns when pending updates accumulate
6
+ - check_recent_critical_anomalies: surfaces critical anomalies from JSONL log
7
+ - consume_anomaly_flag: reads and deletes anomaly signal flags
8
+ """
9
+
10
+ import json
11
+ import logging
12
+ import os
13
+ import subprocess
14
+ from datetime import datetime
15
+ from pathlib import Path
16
+
17
+ from ..core.paths import get_plugin_data_dir
18
+ from ..session.session_manager import get_or_create_session_id
19
+ from .anchor_tracker import extract_anchors, save_anchors
20
+ from .contracts_loader import build_context_update_reminder
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ def _prune_empty_values(data: dict) -> dict:
26
+ """Drop keys with empty telemetry values while preserving False/0."""
27
+ pruned = {}
28
+ for key, value in data.items():
29
+ if value in (None, "", [], {}):
30
+ continue
31
+ pruned[key] = value
32
+ return pruned
33
+
34
+
35
+ def build_context_telemetry_snapshot(context_payload: dict) -> dict:
36
+ """Build a compact telemetry snapshot from injected context payload data."""
37
+ if not isinstance(context_payload, dict) or not context_payload:
38
+ return {}
39
+
40
+ project_knowledge = context_payload.get("project_knowledge") or {}
41
+ metadata = context_payload.get("metadata") or {}
42
+ surface_routing = context_payload.get("surface_routing") or {}
43
+ investigation_brief = context_payload.get("investigation_brief") or {}
44
+ write_permissions = context_payload.get("write_permissions") or {}
45
+
46
+ contract_sections = sorted(project_knowledge.keys())
47
+ readable_sections = sorted(write_permissions.get("readable_sections") or [])
48
+ writable_sections = sorted(write_permissions.get("writable_sections") or [])
49
+
50
+ return _prune_empty_values({
51
+ "contract_sections": contract_sections,
52
+ "contract_sections_count": len(contract_sections),
53
+ "metadata": _prune_empty_values({
54
+ "cloud_provider": metadata.get("cloud_provider"),
55
+ "contract_version": metadata.get("contract_version"),
56
+ "rules_count": metadata.get("rules_count"),
57
+ "historical_episodes_count": metadata.get("historical_episodes_count"),
58
+ "surface_routing_version": metadata.get("surface_routing_version"),
59
+ "active_surfaces_count": metadata.get("active_surfaces_count"),
60
+ "surface_routing_confidence": metadata.get("surface_routing_confidence"),
61
+ }),
62
+ "surface_routing": _prune_empty_values({
63
+ "primary_surface": surface_routing.get("primary_surface"),
64
+ "active_surfaces": sorted(surface_routing.get("active_surfaces") or []),
65
+ "dispatch_mode": surface_routing.get("dispatch_mode"),
66
+ "multi_surface": surface_routing.get("multi_surface"),
67
+ "recommended_agents": sorted(surface_routing.get("recommended_agents") or []),
68
+ }),
69
+ "investigation_brief": _prune_empty_values({
70
+ "agent_role": investigation_brief.get("agent_role"),
71
+ "primary_surface": investigation_brief.get("primary_surface"),
72
+ "adjacent_surfaces": sorted(investigation_brief.get("adjacent_surfaces") or []),
73
+ "cross_check_required": investigation_brief.get("cross_check_required"),
74
+ "consolidation_required": investigation_brief.get("consolidation_required"),
75
+ "required_checks_count": len(investigation_brief.get("required_checks") or []),
76
+ "evidence_required": sorted(investigation_brief.get("evidence_required") or []),
77
+ }),
78
+ "context_update_scope": _prune_empty_values({
79
+ "readable_sections": readable_sections,
80
+ "readable_sections_count": len(readable_sections),
81
+ "writable_sections": writable_sections,
82
+ "writable_sections_count": len(writable_sections),
83
+ }),
84
+ })
85
+
86
+
87
+ def check_pending_updates_threshold() -> str:
88
+ """
89
+ Check if pending updates count exceeds threshold and return warning text.
90
+
91
+ Returns warning string to inject into prompt, or empty string if below threshold.
92
+ Must NEVER block or slow down context injection (target: <50ms).
93
+ """
94
+ try:
95
+ threshold = int(os.environ.get("PENDING_UPDATE_THRESHOLD", "10"))
96
+
97
+ # Fast path: try to read index directly (no module import)
98
+ index_path = Path(".claude/project-context/pending-updates/pending-index.json")
99
+ if not index_path.exists():
100
+ return ""
101
+
102
+ with open(index_path, 'r') as f:
103
+ index_data = json.load(f)
104
+
105
+ pending_count = index_data.get("pending_count", 0)
106
+ if pending_count < threshold:
107
+ return ""
108
+
109
+ logger.info(f"Pending updates threshold reached: {pending_count} >= {threshold}")
110
+ return (
111
+ f"\n# Pending Context Updates Warning\n"
112
+ f"There are {pending_count} pending context update suggestions awaiting review. "
113
+ f"Run `gaia-review` or `python3 tools/review/review_engine.py list` to review them.\n\n"
114
+ )
115
+
116
+ except Exception as e:
117
+ logger.debug(f"Pending updates check failed (non-fatal): {e}")
118
+ return ""
119
+
120
+
121
+ def check_recent_critical_anomalies() -> str:
122
+ """Check anomalies.jsonl for recent critical anomalies and return a summary.
123
+
124
+ Scans the last 20 lines of the anomaly log for critical-severity entries
125
+ from the past hour. Returns a short warning string suitable for context
126
+ injection, or empty string if nothing noteworthy is found.
127
+
128
+ This is intentionally lightweight: reads only the tail of the file and
129
+ returns at most a one-line count + type summary.
130
+ """
131
+ anomaly_log = (
132
+ get_plugin_data_dir() / "project-context" / "workflow-episodic-memory" / "anomalies.jsonl"
133
+ )
134
+ if not anomaly_log.exists():
135
+ return ""
136
+
137
+ try:
138
+ # Read only the tail (last 20 lines) for speed
139
+ lines = anomaly_log.read_text().splitlines()[-20:]
140
+ one_hour_ago = datetime.now().timestamp() - 3600
141
+ critical_types: list[str] = []
142
+
143
+ for line in lines:
144
+ if not line.strip():
145
+ continue
146
+ try:
147
+ entry = json.loads(line)
148
+ except json.JSONDecodeError:
149
+ continue
150
+ ts = entry.get("timestamp", "")
151
+ if ts:
152
+ try:
153
+ entry_time = datetime.fromisoformat(ts).timestamp()
154
+ except (ValueError, TypeError):
155
+ continue
156
+ if entry_time < one_hour_ago:
157
+ continue
158
+ for anomaly in entry.get("anomalies", []):
159
+ if anomaly.get("severity") == "critical":
160
+ critical_types.append(anomaly.get("type", "unknown"))
161
+
162
+ if not critical_types:
163
+ return ""
164
+
165
+ # Deduplicate and summarize
166
+ unique_types = sorted(set(critical_types))
167
+ return (
168
+ f"\n# Recent Critical Anomalies\n"
169
+ f"{len(critical_types)} critical anomaly(ies) in the last hour "
170
+ f"(types: {', '.join(unique_types)}). "
171
+ f"Consider investigating with /gaia.\n"
172
+ )
173
+ except Exception as e:
174
+ logger.debug(f"Critical anomaly check failed (non-fatal): {e}")
175
+ return ""
176
+
177
+
178
+ def consume_anomaly_flag(enriched_prompt: str) -> str:
179
+ """Read and delete the needs_analysis.flag if it exists, appending a warning.
180
+
181
+ The flag is created by subagent_stop.py when workflow anomalies are
182
+ detected. Reading it once and deleting ensures the warning is shown
183
+ exactly once. Must not slow down context injection -- returns
184
+ immediately if the file does not exist.
185
+
186
+ TTL enforcement: flags older than 1 hour (by created_at or file mtime)
187
+ are auto-expired and deleted without injecting a warning.
188
+ """
189
+ flag_path = get_plugin_data_dir() / "project-context" / "workflow-episodic-memory" / "signals" / "needs_analysis.flag"
190
+ if not flag_path.exists():
191
+ return enriched_prompt
192
+ try:
193
+ signal_data = json.loads(flag_path.read_text())
194
+
195
+ # TTL check: auto-expire flags older than ttl_hours (default 1 hour)
196
+ ttl_hours = signal_data.get("ttl_hours", 1)
197
+ ttl_seconds = ttl_hours * 3600
198
+ created_at = signal_data.get("created_at") or signal_data.get("timestamp")
199
+ if created_at:
200
+ created_dt = datetime.fromisoformat(created_at)
201
+ age_seconds = (datetime.now() - created_dt).total_seconds()
202
+ if age_seconds > ttl_seconds:
203
+ flag_path.unlink()
204
+ logger.info(
205
+ "Auto-expired anomaly flag (age: %.0fs, ttl: %ds)",
206
+ age_seconds, ttl_seconds,
207
+ )
208
+ return enriched_prompt
209
+ else:
210
+ # Fallback: check file modification time
211
+ mtime = flag_path.stat().st_mtime
212
+ age_seconds = datetime.now().timestamp() - mtime
213
+ if age_seconds > ttl_seconds:
214
+ flag_path.unlink()
215
+ logger.info(
216
+ "Auto-expired anomaly flag by mtime (age: %.0fs, ttl: %ds)",
217
+ age_seconds, ttl_seconds,
218
+ )
219
+ return enriched_prompt
220
+
221
+ anomalies = signal_data.get("anomalies", [])
222
+ summary = "; ".join(a.get("message", "") for a in anomalies if a.get("message"))
223
+ if summary:
224
+ enriched_prompt += (
225
+ f"\n# Anomaly Alert\n"
226
+ f"Recent anomalies detected: {summary}. "
227
+ f"Consider investigating with /gaia.\n"
228
+ )
229
+ flag_path.unlink()
230
+ logger.info("Consumed anomaly flag and injected warning")
231
+ except Exception as e:
232
+ logger.debug(f"Failed to consume anomaly flag (non-fatal): {e}")
233
+ return enriched_prompt
234
+
235
+
236
+ def build_project_context(
237
+ parameters: dict,
238
+ project_agents: list,
239
+ hooks_dir: Path = None,
240
+ ) -> tuple:
241
+ """
242
+ Build project context string for a project agent without mutating parameters.
243
+
244
+ Returns the context string suitable for additionalContext injection, plus a
245
+ telemetry snapshot. Does NOT modify parameters in any way.
246
+
247
+ Args:
248
+ parameters: Task tool parameters (read-only).
249
+ project_agents: List of valid project agent names.
250
+ hooks_dir: Path to the hooks directory (for fallback paths).
251
+ Defaults to Path(__file__).parent.parent.parent if None.
252
+
253
+ Returns:
254
+ (context_string, telemetry_snapshot) or (None, {}) if no context to inject.
255
+ """
256
+ if hooks_dir is None:
257
+ hooks_dir = Path(__file__).parent.parent.parent
258
+
259
+ subagent_type = parameters.get("subagent_type", "")
260
+
261
+ # Only inject for project agents (not for generic agents like Explore, general-purpose, etc.)
262
+ if subagent_type not in project_agents:
263
+ logger.debug(f"Skipping context injection for non-project agent: {subagent_type}")
264
+ return None, {}
265
+
266
+ prompt = parameters.get("prompt", "")
267
+ if not prompt:
268
+ logger.warning(f"No prompt provided for {subagent_type}, skipping context injection")
269
+ return None, {}
270
+
271
+ try:
272
+ # Find context_provider.py
273
+ context_provider_paths = [
274
+ hooks_dir.parent / "tools" / "context" / "context_provider.py", # plugin root (works in both modes)
275
+ Path(".claude/tools/context/context_provider.py"), # npm symlink fallback
276
+ ]
277
+
278
+ context_provider = None
279
+ for path in context_provider_paths:
280
+ if path.exists():
281
+ context_provider = path
282
+ break
283
+
284
+ if not context_provider:
285
+ logger.warning("context_provider.py not found, skipping context injection")
286
+ return None, {}
287
+
288
+ # Execute context_provider.py to get filtered context
289
+ logger.info(f"Building context for {subagent_type}...")
290
+ result = subprocess.run(
291
+ ["python3", str(context_provider), subagent_type, prompt],
292
+ capture_output=True,
293
+ text=True,
294
+ timeout=15,
295
+ cwd=os.getcwd()
296
+ )
297
+
298
+ if result.returncode != 0:
299
+ logger.error(f"context_provider.py failed: {result.stderr}")
300
+ return None, {}
301
+
302
+ # Parse context JSON
303
+ try:
304
+ context_payload = json.loads(result.stdout)
305
+ except json.JSONDecodeError as e:
306
+ logger.error(f"Failed to parse context JSON: {e}")
307
+ return None, {}
308
+
309
+ # Extract and save context anchors for hit tracking
310
+ try:
311
+ anchors = extract_anchors(context_payload)
312
+ if anchors:
313
+ session_id = get_or_create_session_id()
314
+ save_anchors(session_id, subagent_type, anchors)
315
+ logger.debug(
316
+ "Saved %d context anchors for %s", len(anchors), subagent_type,
317
+ )
318
+ except Exception as exc:
319
+ logger.debug("Anchor extraction failed (non-fatal): %s", exc)
320
+
321
+ # Check pending update count (non-blocking, fast path)
322
+ pending_warning = check_pending_updates_threshold()
323
+
324
+ # Build context update reminder for empty writable sections
325
+ update_reminder = build_context_update_reminder(
326
+ subagent_type, project_agents, hooks_dir
327
+ )
328
+
329
+ # Build context sections from payload
330
+ project_knowledge = context_payload.get("project_knowledge", {})
331
+ write_perms = context_payload.get("write_permissions", {})
332
+ investigation_brief = context_payload.get("investigation_brief", {})
333
+ rules = context_payload.get("rules", {})
334
+ surface_routing_data = context_payload.get("surface_routing", {})
335
+ metadata = context_payload.get("metadata", {})
336
+ historical = context_payload.get("historical_context", {})
337
+
338
+ # Optional sections
339
+ rules_section = f"\n## Rules\n\n{json.dumps(rules, indent=2)}\n" if rules.get("universal") or rules.get("agent_specific") else ""
340
+ routing_section = f"\n## Surface Routing\n\n{json.dumps(surface_routing_data, indent=2)}\n" if surface_routing_data else ""
341
+ metadata_section = f"\n## Metadata\n\n{json.dumps(metadata, indent=2)}\n" if metadata else ""
342
+ historical_section = f"\n## Historical Context\n\n{json.dumps(historical, indent=2)}\n" if historical else ""
343
+
344
+ # Save context_payload to disk for downstream hooks (SubagentStop)
345
+ try:
346
+ payload_dir = Path(os.environ.get("TMPDIR", "/tmp")) / "gaia-context-payloads"
347
+ payload_dir.mkdir(parents=True, exist_ok=True)
348
+ agent_id = parameters.get("_agent_id", "") or subagent_type
349
+ payload_path = payload_dir / f"{agent_id}.json"
350
+ payload_path.write_text(json.dumps(context_payload, separators=(',', ':')))
351
+ logger.debug(f"Context payload saved to {payload_path}")
352
+ except Exception as exc:
353
+ logger.debug(f"Failed to save context payload to disk (non-fatal): {exc}")
354
+
355
+ # Build brief as full JSON (all fields, not just 3)
356
+ brief_json = json.dumps(investigation_brief, indent=2) if investigation_brief else "{}"
357
+
358
+ # Build write permissions as JSON
359
+ write_perms_json = json.dumps({
360
+ "writable": write_perms.get("writable_sections", []),
361
+ "readable": write_perms.get("readable_sections", []),
362
+ "context_update_required": [s for s in write_perms.get("writable_sections", [])
363
+ if not project_knowledge.get(s)]
364
+ }, indent=2)
365
+
366
+ context_string = f"""{rules_section}
367
+ # Project Context
368
+
369
+ {json.dumps(project_knowledge, indent=2)}
370
+
371
+ # Routing
372
+ {routing_section}
373
+ # Brief
374
+
375
+ {brief_json}
376
+
377
+ # Permissions
378
+
379
+ {write_perms_json}
380
+ {pending_warning}{update_reminder}{metadata_section}{historical_section}"""
381
+
382
+ # Append anomaly signal flag (consume once)
383
+ context_string = consume_anomaly_flag(context_string)
384
+
385
+ # Surface recent critical anomalies from the JSONL log
386
+ critical_summary = check_recent_critical_anomalies()
387
+ if critical_summary:
388
+ context_string += critical_summary
389
+
390
+ # Inject recent operational events (non-blocking)
391
+ try:
392
+ from ..events.event_writer import read_events
393
+ recent = read_events(hours=24, limit=20)
394
+ if recent:
395
+ lines = ["\n# Recent Events (last 24h)"]
396
+ for evt in recent:
397
+ ts_short = evt.get("ts", "")[:16]
398
+ etype = evt.get("type", "")
399
+ agent_name = evt.get("agent", "")
400
+ result_str = evt.get("result", "")
401
+ label = f"{agent_name}: " if agent_name else ""
402
+ lines.append(f"- [{ts_short}] {etype}: {label}{result_str}")
403
+ context_string += "\n".join(lines) + "\n"
404
+ except Exception as exc:
405
+ logger.debug("Event context injection failed (non-fatal): %s", exc)
406
+
407
+ # Build telemetry snapshot
408
+ telemetry = build_context_telemetry_snapshot(context_payload)
409
+
410
+ sections_count = len(context_payload.get("project_knowledge", {}))
411
+ rules_count = context_payload.get("metadata", {}).get("rules_count", 0)
412
+
413
+ logger.info(
414
+ f"Context built for {subagent_type} "
415
+ f"(sections={sections_count}, rules={rules_count})"
416
+ )
417
+
418
+ return context_string, telemetry
419
+
420
+ except subprocess.TimeoutExpired:
421
+ logger.error("context_provider.py timed out (15s)")
422
+ return None, {}
423
+ except Exception as e:
424
+ logger.error(f"Error building context: {e}", exc_info=True)
425
+ return None, {}
426
+
427
+