@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,215 @@
1
+ """Compact context builder for post-compaction re-injection.
2
+
3
+ Builds a lightweight context summary from session data sources.
4
+ Each source is independent and fail-safe.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import json
9
+ import logging
10
+ from datetime import datetime
11
+ from pathlib import Path
12
+
13
+ from ..core.paths import get_plugin_data_dir
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ # Defaults
18
+ DEFAULT_MAX_SNAPSHOTS = 5
19
+ DEFAULT_ANOMALY_WINDOW_HOURS = 1
20
+ DEFAULT_MAX_EVENTS = 5
21
+
22
+
23
+ def build_compact_context(
24
+ *,
25
+ max_snapshots: int = DEFAULT_MAX_SNAPSHOTS,
26
+ anomaly_window_hours: int = DEFAULT_ANOMALY_WINDOW_HOURS,
27
+ max_events: int = DEFAULT_MAX_EVENTS,
28
+ ) -> str:
29
+ """Build compact context for post-compaction re-injection.
30
+
31
+ Returns a markdown string with 4 blocks:
32
+ 1. Orchestrator identity reminder
33
+ 2. Session activity summary (from run-snapshots.jsonl)
34
+ 3. Active anomalies (from anomalies.jsonl)
35
+ 4. Recent session events (from context.json)
36
+
37
+ Each block is independent — if a source fails, the others still produce output.
38
+ """
39
+ blocks = []
40
+
41
+ # Block 1: Orchestrator identity (always present, static)
42
+ blocks.append(_build_identity_block())
43
+
44
+ # Block 2: Session activity from run-snapshots.jsonl
45
+ activity = _build_activity_block(max_snapshots)
46
+ if activity:
47
+ blocks.append(activity)
48
+
49
+ # Block 3: Active anomalies from anomalies.jsonl
50
+ anomalies = _build_anomalies_block(anomaly_window_hours)
51
+ if anomalies:
52
+ blocks.append(anomalies)
53
+
54
+ # Block 4: Recent events from context.json
55
+ events = _build_events_block(max_events)
56
+ if events:
57
+ blocks.append(events)
58
+
59
+ return "\n\n".join(blocks)
60
+
61
+
62
+ def _build_identity_block() -> str:
63
+ """Static orchestrator identity reminder."""
64
+ return (
65
+ "# Post-Compaction Context Refresh\n\n"
66
+ "You are the orchestrator. Dispatch work via Agent, resume agents via "
67
+ "SendMessage(to: agentId), get user approval via AskUserQuestion. "
68
+ "Never execute infrastructure commands directly.\n"
69
+ "Agents: cloud-troubleshooter, gitops-operator, terraform-architect, "
70
+ "devops-developer, speckit-planner, gaia-system"
71
+ )
72
+
73
+
74
+ def _build_activity_block(max_snapshots: int) -> str | None:
75
+ """Build session activity summary from run-snapshots.jsonl."""
76
+ snapshots_path = (
77
+ get_plugin_data_dir() / "project-context" / "workflow-episodic-memory" / "run-snapshots.jsonl"
78
+ )
79
+ if not snapshots_path.exists():
80
+ return None
81
+
82
+ try:
83
+ lines = snapshots_path.read_text().splitlines()
84
+ # Take last N lines
85
+ recent = lines[-max_snapshots:] if len(lines) > max_snapshots else lines
86
+
87
+ entries = []
88
+ for line in recent:
89
+ if not line.strip():
90
+ continue
91
+ try:
92
+ snap = json.loads(line)
93
+ agent = snap.get("agent", "unknown")
94
+ status = snap.get("plan_status", "unknown")
95
+ prompt = snap.get("prompt", "")[:80]
96
+ cmd_count = snap.get("commands_executed_count", 0)
97
+ entries.append(f"- {agent} → {status} ({prompt}, {cmd_count} commands)")
98
+ except json.JSONDecodeError:
99
+ continue
100
+
101
+ if not entries:
102
+ return None
103
+
104
+ return "## Session Activity\n" + "\n".join(entries)
105
+
106
+ except Exception as e:
107
+ logger.debug("Failed to build activity block (non-fatal): %s", e)
108
+ return None
109
+
110
+
111
+ def _build_anomalies_block(window_hours: int) -> str | None:
112
+ """Build active anomalies summary from anomalies.jsonl."""
113
+ anomaly_path = (
114
+ get_plugin_data_dir() / "project-context" / "workflow-episodic-memory" / "anomalies.jsonl"
115
+ )
116
+ if not anomaly_path.exists():
117
+ return None
118
+
119
+ try:
120
+ lines = anomaly_path.read_text().splitlines()[-20:]
121
+ cutoff = datetime.now().timestamp() - (window_hours * 3600)
122
+
123
+ critical_types: list[str] = []
124
+ warning_types: list[str] = []
125
+
126
+ for line in lines:
127
+ if not line.strip():
128
+ continue
129
+ try:
130
+ entry = json.loads(line)
131
+ ts = entry.get("timestamp", "")
132
+ if ts:
133
+ try:
134
+ entry_time = datetime.fromisoformat(ts).timestamp()
135
+ if entry_time < cutoff:
136
+ continue
137
+ except (ValueError, TypeError):
138
+ continue
139
+
140
+ for anomaly in entry.get("anomalies", []):
141
+ severity = anomaly.get("severity", "")
142
+ atype = anomaly.get("type", "unknown")
143
+ if severity == "critical":
144
+ critical_types.append(atype)
145
+ elif severity == "warning":
146
+ warning_types.append(atype)
147
+ except json.JSONDecodeError:
148
+ continue
149
+
150
+ if not critical_types and not warning_types:
151
+ return None
152
+
153
+ parts = []
154
+ if critical_types:
155
+ unique = sorted(set(critical_types))
156
+ parts.append(f"- {len(critical_types)} critical: {', '.join(unique)}")
157
+ if warning_types:
158
+ unique = sorted(set(warning_types))
159
+ parts.append(f"- {len(warning_types)} warning: {', '.join(unique)}")
160
+
161
+ return "## Active Anomalies\n" + "\n".join(parts)
162
+
163
+ except Exception as e:
164
+ logger.debug("Failed to build anomalies block (non-fatal): %s", e)
165
+ return None
166
+
167
+
168
+ def _build_events_block(max_events: int) -> str | None:
169
+ """Build recent events summary from session context.json."""
170
+ context_path = Path(".claude/session/active/context.json")
171
+ if not context_path.exists():
172
+ return None
173
+
174
+ try:
175
+ with open(context_path) as f:
176
+ context = json.load(f)
177
+
178
+ events = context.get("critical_events", [])
179
+ if not events:
180
+ return None
181
+
182
+ # Take last N events
183
+ recent = events[-max_events:]
184
+
185
+ lines = []
186
+ for event in recent:
187
+ etype = event.get("event_type", "")
188
+ ts = event.get("timestamp", "")[:16]
189
+
190
+ if etype == "git_commit":
191
+ msg = event.get("commit_message", "")
192
+ hash_val = event.get("commit_hash", "")[:7]
193
+ if hash_val and msg:
194
+ lines.append(f"- [{ts}] Commit {hash_val}: {msg}")
195
+ elif etype == "git_push":
196
+ branch = event.get("branch", "")
197
+ if branch:
198
+ lines.append(f"- [{ts}] Pushed to {branch}")
199
+ elif etype == "file_modifications":
200
+ count = event.get("modification_count", 0)
201
+ if count:
202
+ lines.append(f"- [{ts}] Modified {count} files")
203
+ elif etype == "infrastructure_change":
204
+ cmd = event.get("command", "")
205
+ if cmd:
206
+ lines.append(f"- [{ts}] Infrastructure: {cmd}")
207
+
208
+ if not lines:
209
+ return None
210
+
211
+ return "## Recent Events\n" + "\n".join(lines)
212
+
213
+ except Exception as e:
214
+ logger.debug("Failed to build events block (non-fatal): %s", e)
215
+ return None
@@ -0,0 +1,129 @@
1
+ """
2
+ Context cache for PreToolUse -> SubagentStart handoff.
3
+
4
+ PreToolUse:Agent builds context (needs the prompt for surface routing) but the
5
+ context must reach the subagent, not the orchestrator. SubagentStart is where
6
+ context should be injected, but SubagentStart does not receive the prompt.
7
+
8
+ Solution: PreToolUse caches the built context to a temp file keyed by
9
+ session_id. SubagentStart reads and consumes the cache (one-shot).
10
+
11
+ Cache location: /tmp/gaia-context-cache/{session_id}-{timestamp}.json
12
+ TTL: 60 seconds (stale files cleaned on write).
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import json
18
+ import logging
19
+ import os
20
+ import time
21
+ from pathlib import Path
22
+ from typing import Optional
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+ CACHE_DIR = Path("/tmp/gaia-context-cache")
27
+ CACHE_TTL_SECONDS = 60
28
+
29
+
30
+ def write_context_cache(
31
+ session_id: str,
32
+ context: str,
33
+ agent_type: str = "",
34
+ ) -> Path:
35
+ """Write context to a cache file for later consumption by SubagentStart.
36
+
37
+ Args:
38
+ session_id: Hook session identifier (shared between PreToolUse and SubagentStart).
39
+ context: The full additionalContext string to inject into the subagent.
40
+ agent_type: The agent type (for logging/diagnostics).
41
+
42
+ Returns:
43
+ Path to the written cache file.
44
+ """
45
+ CACHE_DIR.mkdir(parents=True, exist_ok=True)
46
+
47
+ # Clean stale files first
48
+ _cleanup_stale_caches()
49
+
50
+ timestamp = int(time.time() * 1000)
51
+ cache_file = CACHE_DIR / f"{session_id}-{timestamp}.json"
52
+ payload = {
53
+ "context": context,
54
+ "agent_type": agent_type,
55
+ "timestamp": timestamp,
56
+ }
57
+
58
+ cache_file.write_text(json.dumps(payload))
59
+ logger.info(
60
+ "Cached context for session=%s agent=%s (%d bytes) -> %s",
61
+ session_id, agent_type, len(context), cache_file.name,
62
+ )
63
+ return cache_file
64
+
65
+
66
+ def read_context_cache(session_id: str) -> Optional[dict]:
67
+ """Read and consume the most recent context cache for a session.
68
+
69
+ Returns the cache payload dict if found, or None if no cache exists.
70
+ The cache file is deleted after reading (one-shot consumption).
71
+
72
+ Args:
73
+ session_id: Hook session identifier.
74
+
75
+ Returns:
76
+ Dict with keys: context, agent_type, timestamp. Or None.
77
+ """
78
+ if not CACHE_DIR.exists():
79
+ logger.debug("No cache directory found")
80
+ return None
81
+
82
+ # Find all cache files for this session, sorted by timestamp (newest first)
83
+ prefix = f"{session_id}-"
84
+ candidates = sorted(
85
+ [f for f in CACHE_DIR.iterdir() if f.name.startswith(prefix) and f.suffix == ".json"],
86
+ key=lambda p: p.stat().st_mtime,
87
+ reverse=True,
88
+ )
89
+
90
+ if not candidates:
91
+ logger.debug("No cache files found for session=%s", session_id)
92
+ return None
93
+
94
+ cache_file = candidates[0]
95
+ try:
96
+ payload = json.loads(cache_file.read_text())
97
+ logger.info(
98
+ "Read context cache for session=%s agent=%s from %s",
99
+ session_id, payload.get("agent_type", "unknown"), cache_file.name,
100
+ )
101
+ # One-shot: delete after reading
102
+ cache_file.unlink(missing_ok=True)
103
+
104
+ # Clean up any older duplicates for this session
105
+ for stale in candidates[1:]:
106
+ stale.unlink(missing_ok=True)
107
+
108
+ return payload
109
+
110
+ except (json.JSONDecodeError, OSError) as exc:
111
+ logger.warning("Failed to read cache file %s: %s", cache_file, exc)
112
+ cache_file.unlink(missing_ok=True)
113
+ return None
114
+
115
+
116
+ def _cleanup_stale_caches() -> None:
117
+ """Remove cache files older than CACHE_TTL_SECONDS."""
118
+ if not CACHE_DIR.exists():
119
+ return
120
+
121
+ cutoff = time.time() - CACHE_TTL_SECONDS
122
+ for f in CACHE_DIR.iterdir():
123
+ if f.suffix == ".json":
124
+ try:
125
+ if f.stat().st_mtime < cutoff:
126
+ f.unlink(missing_ok=True)
127
+ logger.debug("Cleaned stale cache: %s", f.name)
128
+ except OSError:
129
+ pass
@@ -0,0 +1,145 @@
1
+ """
2
+ Context freshness checker for SessionStart hook.
3
+
4
+ Determines whether project-context.json is fresh enough to skip a rescan.
5
+ Uses metadata.scan_config.last_scan (preferred) or file mtime as fallback.
6
+
7
+ Public API:
8
+ - check_freshness(project_root: Path) -> FreshnessResult
9
+ """
10
+
11
+ import json
12
+ import logging
13
+ import os
14
+ from dataclasses import dataclass
15
+ from datetime import datetime, timedelta, timezone
16
+ from pathlib import Path
17
+ from typing import Optional
18
+
19
+ from ..core.paths import find_claude_dir
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+ # Context freshness threshold (hours) -- env var overrides default
24
+ DEFAULT_FRESHNESS_HOURS = 24
25
+
26
+
27
+ @dataclass(frozen=True)
28
+ class FreshnessResult:
29
+ """Result of a context freshness check."""
30
+
31
+ is_fresh: bool
32
+ reason: str
33
+ age_hours: float = 0.0
34
+
35
+
36
+ def _get_context_path() -> Path:
37
+ """Return path to project-context.json."""
38
+ claude_dir = find_claude_dir()
39
+ return claude_dir / "project-context" / "project-context.json"
40
+
41
+
42
+ def _read_staleness_from_context(context_path: Path) -> Optional[int]:
43
+ """Read staleness_hours from metadata.scan_config in the context file.
44
+
45
+ Returns None if the file cannot be read or the field is absent.
46
+ """
47
+ if not context_path.is_file():
48
+ return None
49
+ try:
50
+ with open(context_path, "r") as f:
51
+ data = json.load(f)
52
+ return (
53
+ int(
54
+ data.get("metadata", {})
55
+ .get("scan_config", {})
56
+ .get("staleness_hours", 0)
57
+ )
58
+ or None
59
+ )
60
+ except (json.JSONDecodeError, OSError, ValueError, TypeError):
61
+ return None
62
+
63
+
64
+ def _get_effective_threshold() -> int:
65
+ """Determine the effective freshness threshold in hours."""
66
+ return int(
67
+ os.environ.get(
68
+ "GAIA_SCAN_STALENESS_HOURS",
69
+ os.environ.get("CONTEXT_FRESHNESS_HOURS", str(DEFAULT_FRESHNESS_HOURS)),
70
+ )
71
+ )
72
+
73
+
74
+ def check_freshness(project_root: Path = None) -> FreshnessResult:
75
+ """Check if project-context.json exists and is fresh (< threshold).
76
+
77
+ Args:
78
+ project_root: Unused, kept for API compatibility. Context path
79
+ is resolved via find_claude_dir().
80
+
81
+ Returns:
82
+ FreshnessResult with is_fresh, reason, and age_hours.
83
+ """
84
+ context_path = _get_context_path()
85
+
86
+ if not context_path.exists():
87
+ logger.info("project-context.json not found at %s", context_path)
88
+ return FreshnessResult(is_fresh=False, reason="missing", age_hours=0.0)
89
+
90
+ # Determine effective threshold: env var > context file > default
91
+ effective_hours = _get_effective_threshold()
92
+ ctx_hours = _read_staleness_from_context(context_path)
93
+ if ctx_hours and not os.environ.get("GAIA_SCAN_STALENESS_HOURS"):
94
+ effective_hours = ctx_hours
95
+
96
+ try:
97
+ # Try metadata.scan_config.last_scan first (more accurate)
98
+ with open(context_path, "r") as f:
99
+ data = json.load(f)
100
+ last_scan = data.get("metadata", {}).get("scan_config", {}).get("last_scan")
101
+
102
+ if last_scan:
103
+ scan_dt = datetime.fromisoformat(last_scan)
104
+ now = datetime.now(timezone.utc)
105
+ age = now - scan_dt
106
+ age_hours = age.total_seconds() / 3600.0
107
+ threshold = timedelta(hours=effective_hours)
108
+
109
+ if age > threshold:
110
+ logger.info(
111
+ "project-context.json is stale (last_scan age: %s, threshold: %sh)",
112
+ age,
113
+ effective_hours,
114
+ )
115
+ return FreshnessResult(
116
+ is_fresh=False, reason="stale", age_hours=age_hours
117
+ )
118
+
119
+ logger.debug("project-context.json is fresh (last_scan age: %s)", age)
120
+ return FreshnessResult(
121
+ is_fresh=True, reason="fresh", age_hours=age_hours
122
+ )
123
+
124
+ # Fallback: use file mtime
125
+ mtime = datetime.fromtimestamp(context_path.stat().st_mtime)
126
+ age = datetime.now() - mtime
127
+ age_hours = age.total_seconds() / 3600.0
128
+ threshold = timedelta(hours=effective_hours)
129
+
130
+ if age > threshold:
131
+ logger.info(
132
+ "project-context.json is stale (mtime age: %s, threshold: %sh)",
133
+ age,
134
+ effective_hours,
135
+ )
136
+ return FreshnessResult(
137
+ is_fresh=False, reason="stale", age_hours=age_hours
138
+ )
139
+
140
+ logger.debug("project-context.json is fresh (mtime age: %s)", age)
141
+ return FreshnessResult(is_fresh=True, reason="fresh", age_hours=age_hours)
142
+
143
+ except Exception as e:
144
+ logger.warning("Error checking context freshness: %s", e)
145
+ return FreshnessResult(is_fresh=False, reason="error", age_hours=0.0)