@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
@@ -1,213 +1,134 @@
1
1
  """
2
- Metrics collection and aggregation.
2
+ Metrics aggregation from audit logs.
3
3
 
4
- Collects execution metrics and provides FUNCTIONAL generate_summary.
4
+ Reads audit-*.jsonl files (the single source of truth for execution data)
5
+ and produces aggregated summaries. No write path — audit/logger.py owns writes.
5
6
  """
6
7
 
7
8
  import json
8
9
  import logging
9
- import os
10
10
  from pathlib import Path
11
11
  from datetime import datetime, timedelta
12
12
  from typing import Dict, Any, List, Optional
13
13
  from collections import defaultdict
14
14
 
15
- from ..core.paths import get_metrics_dir
15
+ from ..core.paths import get_logs_dir
16
16
 
17
17
  logger = logging.getLogger(__name__)
18
18
 
19
19
 
20
- class MetricsCollector:
21
- """Collect and aggregate execution metrics."""
22
-
23
- def __init__(self, metrics_dir: Optional[Path] = None):
24
- """
25
- Initialize metrics collector.
26
-
27
- Args:
28
- metrics_dir: Override metrics directory (for testing)
29
- """
30
- if metrics_dir is not None:
31
- self.metrics_dir = Path(metrics_dir) if isinstance(metrics_dir, str) else metrics_dir
32
- else:
33
- self.metrics_dir = get_metrics_dir()
34
- self.metrics_dir.mkdir(parents=True, exist_ok=True)
35
-
36
- def record_execution(
37
- self,
38
- tool_name: str,
39
- command: str,
40
- duration: float,
41
- success: bool,
42
- tier: str = "unknown"
43
- ) -> None:
44
- """
45
- Record execution metrics.
46
-
47
- Args:
48
- tool_name: Name of the tool
49
- command: Command executed
50
- duration: Duration in seconds
51
- success: Whether execution succeeded
52
- tier: Security tier
53
- """
54
- timestamp = datetime.now().isoformat()
55
-
56
- metrics_record = {
57
- "timestamp": timestamp,
58
- "tool_name": tool_name,
59
- "command_type": self._classify_command(command),
60
- "duration_ms": round(duration * 1000, 2),
61
- # NOTE: 'success' field removed -- Claude Code's PostToolUse hook
62
- # always reports success=True due to API limitation, making the
63
- # field unreliable. Use exit_code from audit logs instead.
64
- "tier": tier,
65
- }
66
-
67
- # Metrics files are a strict subset of audit logs (audit-*.jsonl).
68
- # Writing is disabled by default; audit logs are the SSOT for command metrics.
69
- # Set GAIA_WRITE_METRICS=1 to re-enable for backward compatibility.
70
- if os.environ.get("GAIA_WRITE_METRICS") == "1":
71
- metrics_file = self.metrics_dir / f"metrics-{datetime.now().strftime('%Y-%m')}.jsonl"
72
- try:
73
- with open(metrics_file, "a") as f:
74
- f.write(json.dumps(metrics_record) + "\n")
75
- except Exception as e:
76
- logger.error(f"Error writing metrics: {e}")
77
-
78
- def _classify_command(self, command: str) -> str:
79
- """Classify command type for metrics."""
80
- command_lower = command.lower()
81
- classifiers = [
82
- ("terraform", "terraform"),
83
- ("kubectl", "kubernetes"),
84
- ("helm", "helm"),
85
- ("gcloud", "gcp"),
86
- ("aws", "aws"),
87
- ("flux", "flux"),
88
- ("docker", "docker"),
89
- ("git", "git"),
90
- ]
91
- for keyword, classification in classifiers:
92
- if keyword in command_lower:
93
- return classification
94
- return "general"
95
-
96
- def generate_summary(self, days: int = 7) -> Dict[str, Any]:
97
- """
98
- Generate FUNCTIONAL metrics summary for the last N days.
99
-
100
- Args:
101
- days: Number of days to include
102
-
103
- Returns:
104
- Dictionary with aggregated metrics
105
- """
106
- cutoff_date = datetime.now() - timedelta(days=days)
107
- records = self._load_records_since(cutoff_date)
108
-
109
- if not records:
110
- return {
111
- "period_days": days,
112
- "total_executions": 0,
113
- "success_rate": 0.0,
114
- "avg_duration_ms": 0.0,
115
- "top_commands": [],
116
- "tier_distribution": {},
117
- "command_type_distribution": {},
118
- }
119
-
120
- # Calculate metrics
121
- total = len(records)
122
- successes = sum(1 for r in records if r.get("success", True))
123
- total_duration = sum(r.get("duration_ms", 0) for r in records)
124
-
125
- # Count by command type
126
- command_types = defaultdict(int)
127
- for r in records:
128
- command_types[r.get("command_type", "unknown")] += 1
129
-
130
- # Count by tier
131
- tiers = defaultdict(int)
132
- for r in records:
133
- tiers[r.get("tier", "unknown")] += 1
134
-
135
- # Top command types
136
- top_commands = sorted(
137
- command_types.items(),
138
- key=lambda x: x[1],
139
- reverse=True
140
- )[:10]
141
-
142
- return {
143
- "period_days": days,
144
- "total_executions": total,
145
- "success_rate": round(successes / total, 4) if total > 0 else 0.0,
146
- "avg_duration_ms": round(total_duration / total, 2) if total > 0 else 0.0,
147
- "top_commands": [{"type": t, "count": c} for t, c in top_commands],
148
- "tier_distribution": dict(tiers),
149
- "command_type_distribution": dict(command_types),
150
- "generated_at": datetime.now().isoformat(),
151
- }
152
-
153
- def _load_records_since(self, cutoff_date: datetime) -> List[Dict]:
154
- """Load all records since cutoff date."""
155
- records = []
20
+ def _classify_command(command: str) -> str:
21
+ """Classify command type for metrics aggregation."""
22
+ command_lower = command.lower()
23
+ classifiers = [
24
+ ("terraform", "terraform"),
25
+ ("kubectl", "kubernetes"),
26
+ ("helm", "helm"),
27
+ ("gcloud", "gcp"),
28
+ ("aws", "aws"),
29
+ ("flux", "flux"),
30
+ ("docker", "docker"),
31
+ ("git", "git"),
32
+ ]
33
+ for keyword, classification in classifiers:
34
+ if keyword in command_lower:
35
+ return classification
36
+ return "general"
37
+
38
+
39
+ def _load_audit_records_since(
40
+ logs_dir: Path, cutoff_date: datetime
41
+ ) -> List[Dict]:
42
+ """Load audit records from audit-*.jsonl files since cutoff date."""
43
+ records = []
44
+
45
+ try:
46
+ audit_files = list(logs_dir.glob("audit-*.jsonl"))
47
+ except Exception as e:
48
+ logger.error(f"Error listing audit files: {e}")
49
+ return records
156
50
 
157
- # Get all metrics files
51
+ for audit_file in audit_files:
158
52
  try:
159
- metrics_files = list(self.metrics_dir.glob("metrics-*.jsonl"))
53
+ with open(audit_file, "r") as f:
54
+ for line in f:
55
+ line = line.strip()
56
+ if not line:
57
+ continue
58
+ try:
59
+ record = json.loads(line)
60
+ record_time = datetime.fromisoformat(
61
+ record.get("timestamp", "")
62
+ )
63
+ if record_time >= cutoff_date:
64
+ records.append(record)
65
+ except (json.JSONDecodeError, ValueError):
66
+ continue
160
67
  except Exception as e:
161
- logger.error(f"Error listing metrics files: {e}")
162
- return records
163
-
164
- for metrics_file in metrics_files:
165
- try:
166
- with open(metrics_file, "r") as f:
167
- for line in f:
168
- line = line.strip()
169
- if not line:
170
- continue
171
- try:
172
- record = json.loads(line)
173
- record_time = datetime.fromisoformat(
174
- record.get("timestamp", "")
175
- )
176
- if record_time >= cutoff_date:
177
- records.append(record)
178
- except (json.JSONDecodeError, ValueError):
179
- continue
180
- except Exception as e:
181
- logger.debug(f"Error reading {metrics_file}: {e}")
182
-
183
- return records
68
+ logger.debug(f"Error reading {audit_file}: {e}")
184
69
 
70
+ return records
185
71
 
186
- # Singleton collector
187
- _metrics_collector: Optional[MetricsCollector] = None
188
72
 
73
+ def generate_summary(
74
+ days: int = 7, logs_dir: Optional[Path] = None
75
+ ) -> Dict[str, Any]:
76
+ """
77
+ Generate metrics summary from audit logs for the last N days.
189
78
 
190
- def get_metrics_collector() -> MetricsCollector:
191
- """Get singleton metrics collector."""
192
- global _metrics_collector
193
- if _metrics_collector is None:
194
- _metrics_collector = MetricsCollector()
195
- return _metrics_collector
79
+ Args:
80
+ days: Number of days to include
81
+ logs_dir: Override logs directory (for testing)
196
82
 
83
+ Returns:
84
+ Dictionary with aggregated metrics:
85
+ - period_days, total_executions, avg_duration_ms
86
+ - top_commands (by classified command_type)
87
+ - tier_distribution, command_type_distribution
88
+ """
89
+ if logs_dir is None:
90
+ logs_dir = get_logs_dir()
197
91
 
198
- def record_metric(
199
- tool_name: str,
200
- command: str,
201
- duration: float,
202
- success: bool,
203
- tier: str = "unknown"
204
- ) -> None:
205
- """Record execution metric (convenience function)."""
206
- get_metrics_collector().record_execution(
207
- tool_name, command, duration, success, tier
208
- )
92
+ cutoff_date = datetime.now() - timedelta(days=days)
93
+ records = _load_audit_records_since(logs_dir, cutoff_date)
209
94
 
95
+ if not records:
96
+ return {
97
+ "period_days": days,
98
+ "total_executions": 0,
99
+ "avg_duration_ms": 0.0,
100
+ "top_commands": [],
101
+ "tier_distribution": {},
102
+ "command_type_distribution": {},
103
+ }
210
104
 
211
- def generate_summary(days: int = 7) -> Dict[str, Any]:
212
- """Generate metrics summary (convenience function)."""
213
- return get_metrics_collector().generate_summary(days)
105
+ total = len(records)
106
+ total_duration = sum(r.get("duration_ms", 0) for r in records)
107
+
108
+ # Classify commands from audit log 'command' field
109
+ command_types = defaultdict(int)
110
+ for r in records:
111
+ cmd = r.get("command", "")
112
+ command_types[_classify_command(cmd)] += 1
113
+
114
+ # Count by tier
115
+ tiers = defaultdict(int)
116
+ for r in records:
117
+ tiers[r.get("tier", "unknown")] += 1
118
+
119
+ # Top command types
120
+ top_commands = sorted(
121
+ command_types.items(),
122
+ key=lambda x: x[1],
123
+ reverse=True,
124
+ )[:10]
125
+
126
+ return {
127
+ "period_days": days,
128
+ "total_executions": total,
129
+ "avg_duration_ms": round(total_duration / total, 2) if total > 0 else 0.0,
130
+ "top_commands": [{"type": t, "count": c} for t, c in top_commands],
131
+ "tier_distribution": dict(tiers),
132
+ "command_type_distribution": dict(command_types),
133
+ "generated_at": datetime.now().isoformat(),
134
+ }
@@ -5,7 +5,6 @@ Renamed from anomaly_detector.py and expanded with additional anomaly types.
5
5
 
6
6
  Provides:
7
7
  - audit(): Full anomaly detection suite -> list of anomaly dicts
8
- - detect_anomalies(): Backward-compatible alias for audit()
9
8
  - signal_gaia_analysis(): Create flag file for Gaia analysis
10
9
  """
11
10
 
@@ -529,9 +528,6 @@ def audit(
529
528
  return anomalies
530
529
 
531
530
 
532
- # Backward-compatible alias
533
- detect_anomalies = audit
534
-
535
531
 
536
532
  def signal_gaia_analysis(
537
533
  anomalies: List[Dict],
@@ -6,7 +6,6 @@ Renamed from metrics_recorder.py for clarity.
6
6
  Provides:
7
7
  - get_workflow_memory_dir(): Resolve workflow memory directory
8
8
  - record(): Build metrics dict, write to JSONL
9
- - capture_workflow_metrics(): Backward-compatible alias for record()
10
9
  """
11
10
 
12
11
  import json
@@ -295,7 +294,3 @@ def record(
295
294
  )
296
295
 
297
296
  return metrics
298
-
299
-
300
- # Backward-compatible alias
301
- capture_workflow_metrics = record
@@ -3,6 +3,7 @@
3
3
  Builds a lightweight context summary from session data sources.
4
4
  Each source is independent and fail-safe.
5
5
  """
6
+ from __future__ import annotations
6
7
 
7
8
  import json
8
9
  import logging
@@ -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
@@ -1,8 +1,7 @@
1
1
  """Core context injection subsystem for project agents.
2
2
 
3
3
  Handles:
4
- - build_project_context: builds context string without mutating parameters (Phase 2)
5
- - inject_project_context: legacy wrapper that mutates parameters (backward compat)
4
+ - build_project_context: builds context string for additionalContext injection
6
5
  - check_pending_updates_threshold: warns when pending updates accumulate
7
6
  - check_recent_critical_anomalies: surfaces critical anomalies from JSONL log
8
7
  - consume_anomaly_flag: reads and deletes anomaly signal flags
@@ -269,16 +268,6 @@ def build_project_context(
269
268
  logger.warning(f"No prompt provided for {subagent_type}, skipping context injection")
270
269
  return None, {}
271
270
 
272
- # Deduplication guard: if context was already injected (e.g., by a
273
- # previous hook or retry), do not inject again. The "# Project Context"
274
- # header is the canonical marker written by this function.
275
- if "# Project Context" in prompt:
276
- logger.warning(
277
- "Duplicate context injection prevented for %s — prompt already "
278
- "contains '# Project Context' header", subagent_type,
279
- )
280
- return None, {}
281
-
282
271
  try:
283
272
  # Find context_provider.py
284
273
  context_provider_paths = [
@@ -398,6 +387,23 @@ def build_project_context(
398
387
  if critical_summary:
399
388
  context_string += critical_summary
400
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
+
401
407
  # Build telemetry snapshot
402
408
  telemetry = build_context_telemetry_snapshot(context_payload)
403
409
 
@@ -419,31 +425,3 @@ def build_project_context(
419
425
  return None, {}
420
426
 
421
427
 
422
- def inject_project_context(
423
- parameters: dict,
424
- project_agents: list,
425
- hooks_dir: Path = None,
426
- ) -> dict:
427
- """
428
- Legacy wrapper: inject project context by mutating parameters["prompt"].
429
-
430
- Retained for backward compatibility (tests import this function).
431
- New code should use build_project_context() with additionalContext instead.
432
-
433
- Args:
434
- parameters: Original Task tool parameters (will be mutated).
435
- project_agents: List of valid project agent names.
436
- hooks_dir: Path to the hooks directory.
437
-
438
- Returns:
439
- Modified parameters with context injected into prompt.
440
- """
441
- context_string, _telemetry = build_project_context(parameters, project_agents, hooks_dir)
442
- if context_string is None:
443
- return parameters
444
-
445
- prompt = parameters.get("prompt", "")
446
- enriched_prompt = f"# Task\n\n{prompt}\n{context_string}"
447
- parameters["prompt"] = enriched_prompt
448
-
449
- return parameters
@@ -14,7 +14,6 @@ Public API:
14
14
 
15
15
  import json
16
16
  import logging
17
- import re
18
17
  from datetime import datetime, timezone
19
18
  from pathlib import Path
20
19
  from typing import Dict, List, Optional
@@ -107,15 +106,6 @@ LEGACY_AGENT_CONTRACTS: Dict[str, List[str]] = {
107
106
  _contracts_cache: Dict[str, dict] = {}
108
107
 
109
108
 
110
- # ---------------------------------------------------------------------------
111
- # Known markers that terminate CONTEXT_UPDATE extraction
112
- # ---------------------------------------------------------------------------
113
- _KNOWN_MARKERS = re.compile(
114
- r"^(?:AGENT_STATUS|CONTEXT_UPDATE|<!-- AGENT_STATUS):",
115
- re.MULTILINE,
116
- )
117
-
118
-
119
109
  # ============================================================================
120
110
  # 1. parse_context_update
121
111
  # ============================================================================
@@ -334,7 +324,6 @@ def _merge_base_and_cloud(provider: str, config_dir: Path) -> dict:
334
324
  """
335
325
  base_file = config_dir / "context-contracts.json"
336
326
  cloud_file = config_dir / "cloud" / f"{provider}.json"
337
- legacy_file = config_dir / f"context-contracts.{provider}.json"
338
327
 
339
328
  # Step 1: Load base contracts
340
329
  base_contracts = None
@@ -344,16 +333,7 @@ def _merge_base_and_cloud(provider: str, config_dir: Path) -> dict:
344
333
  except (json.JSONDecodeError, OSError) as exc:
345
334
  logger.warning("Failed to load base contracts from %s: %s", base_file, exc)
346
335
 
347
- # Step 2: Fallback to legacy per-provider file
348
- if base_contracts is None:
349
- if legacy_file.exists():
350
- try:
351
- base_contracts = json.loads(legacy_file.read_text())
352
- logger.info("Using legacy contracts from %s", legacy_file)
353
- except (json.JSONDecodeError, OSError) as exc:
354
- logger.warning("Failed to load legacy contracts from %s: %s", legacy_file, exc)
355
-
356
- # Step 3: Final fallback to hardcoded LEGACY_AGENT_CONTRACTS
336
+ # Step 2: Final fallback to hardcoded LEGACY_AGENT_CONTRACTS
357
337
  if base_contracts is None:
358
338
  logger.warning("No contract files found in %s, using hardcoded legacy contracts", config_dir)
359
339
  return {
@@ -536,7 +516,3 @@ def process_context_updates(
536
516
  except Exception as e:
537
517
  logger.debug("Context update processing failed (non-fatal): %s", e)
538
518
  return None
539
-
540
-
541
- # Module-level alias for shorter import name
542
- update = process_context_updates
@@ -93,20 +93,17 @@ def build_context_update_reminder(
93
93
  for config_dir in config_dirs:
94
94
  if not config_dir.is_dir():
95
95
  continue
96
- # Try new base+cloud system first
96
+ # Load base contracts
97
97
  base_file = config_dir / "context-contracts.json"
98
98
  cloud_file = config_dir / "cloud" / f"{cloud_provider}.json"
99
- legacy_file = config_dir / f"context-contracts.{cloud_provider}.json"
100
99
 
101
100
  merged_agents = {}
102
- for contract_file in [base_file, legacy_file]:
103
- if contract_file.exists():
104
- try:
105
- data = json.loads(contract_file.read_text())
106
- merged_agents = data.get("agents", {})
107
- break
108
- except Exception:
109
- continue
101
+ if base_file.exists():
102
+ try:
103
+ data = json.loads(base_file.read_text())
104
+ merged_agents = data.get("agents", {})
105
+ except Exception:
106
+ pass
110
107
 
111
108
  # Merge cloud overrides
112
109
  if merged_agents and cloud_file.exists():
@@ -20,6 +20,7 @@ Usage in a hook entrypoint::
20
20
  if __name__ == "__main__":
21
21
  run_hook(_handle, hook_name="stop_hook")
22
22
  """
23
+ from __future__ import annotations
23
24
 
24
25
  import json
25
26
  import logging