@jaguilar87/gaia-ops 4.5.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 (360) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/ARCHITECTURE.md +9 -8
  4. package/agents/terraform-architect.md +1 -1
  5. package/bin/gaia-history.js +0 -1
  6. package/bin/gaia-metrics.js +2 -2
  7. package/bin/gaia-scan.py +4 -0
  8. package/bin/gaia-update.js +146 -22
  9. package/bin/pre-publish-validate.js +33 -10
  10. package/commands/gaia.md +37 -0
  11. package/config/README.md +3 -9
  12. package/config/context-contracts.json +47 -15
  13. package/config/surface-routing.json +9 -1
  14. package/dist/gaia-ops/.claude-plugin/plugin.json +22 -0
  15. package/dist/gaia-ops/agents/cloud-troubleshooter.md +73 -0
  16. package/dist/gaia-ops/agents/devops-developer.md +57 -0
  17. package/dist/gaia-ops/agents/gaia-system.md +58 -0
  18. package/dist/gaia-ops/agents/gitops-operator.md +60 -0
  19. package/dist/gaia-ops/agents/speckit-planner.md +71 -0
  20. package/dist/gaia-ops/agents/terraform-architect.md +60 -0
  21. package/dist/gaia-ops/commands/gaia.md +37 -0
  22. package/dist/gaia-ops/config/README.md +58 -0
  23. package/dist/gaia-ops/config/cloud/aws.json +140 -0
  24. package/dist/gaia-ops/config/cloud/gcp.json +145 -0
  25. package/dist/gaia-ops/config/context-contracts.json +131 -0
  26. package/dist/gaia-ops/config/git_standards.json +72 -0
  27. package/dist/gaia-ops/config/surface-routing.json +197 -0
  28. package/dist/gaia-ops/config/universal-rules.json +10 -0
  29. package/dist/gaia-ops/hooks/adapters/__init__.py +52 -0
  30. package/dist/gaia-ops/hooks/adapters/base.py +219 -0
  31. package/dist/gaia-ops/hooks/adapters/channel.py +17 -0
  32. package/dist/gaia-ops/hooks/adapters/claude_code.py +1477 -0
  33. package/dist/gaia-ops/hooks/adapters/types.py +194 -0
  34. package/dist/gaia-ops/hooks/adapters/utils.py +25 -0
  35. package/{templates/settings.template.json → dist/gaia-ops/hooks/hooks.json} +15 -18
  36. package/dist/gaia-ops/hooks/modules/__init__.py +15 -0
  37. package/dist/gaia-ops/hooks/modules/agents/__init__.py +29 -0
  38. package/dist/gaia-ops/hooks/modules/agents/contract_validator.py +647 -0
  39. package/dist/gaia-ops/hooks/modules/agents/response_contract.py +496 -0
  40. package/dist/gaia-ops/hooks/modules/agents/skill_injection_verifier.py +124 -0
  41. package/dist/gaia-ops/hooks/modules/agents/task_info_builder.py +74 -0
  42. package/dist/gaia-ops/hooks/modules/agents/transcript_analyzer.py +458 -0
  43. package/dist/gaia-ops/hooks/modules/agents/transcript_reader.py +152 -0
  44. package/dist/gaia-ops/hooks/modules/audit/__init__.py +28 -0
  45. package/dist/gaia-ops/hooks/modules/audit/event_detector.py +168 -0
  46. package/dist/gaia-ops/hooks/modules/audit/logger.py +131 -0
  47. package/dist/gaia-ops/hooks/modules/audit/metrics.py +134 -0
  48. package/dist/gaia-ops/hooks/modules/audit/workflow_auditor.py +576 -0
  49. package/dist/gaia-ops/hooks/modules/audit/workflow_recorder.py +296 -0
  50. package/dist/gaia-ops/hooks/modules/context/__init__.py +11 -0
  51. package/dist/gaia-ops/hooks/modules/context/anchor_tracker.py +317 -0
  52. package/dist/gaia-ops/hooks/modules/context/compact_context_builder.py +215 -0
  53. package/dist/gaia-ops/hooks/modules/context/context_cache.py +129 -0
  54. package/dist/gaia-ops/hooks/modules/context/context_freshness.py +145 -0
  55. package/dist/gaia-ops/hooks/modules/context/context_injector.py +427 -0
  56. package/dist/gaia-ops/hooks/modules/context/context_writer.py +518 -0
  57. package/dist/gaia-ops/hooks/modules/context/contracts_loader.py +161 -0
  58. package/dist/gaia-ops/hooks/modules/core/__init__.py +40 -0
  59. package/dist/gaia-ops/hooks/modules/core/hook_entry.py +78 -0
  60. package/dist/gaia-ops/hooks/modules/core/paths.py +160 -0
  61. package/dist/gaia-ops/hooks/modules/core/plugin_mode.py +149 -0
  62. package/dist/gaia-ops/hooks/modules/core/plugin_setup.py +558 -0
  63. package/dist/gaia-ops/hooks/modules/core/state.py +179 -0
  64. package/dist/gaia-ops/hooks/modules/core/stdin.py +24 -0
  65. package/dist/gaia-ops/hooks/modules/events/__init__.py +1 -0
  66. package/dist/gaia-ops/hooks/modules/events/event_writer.py +210 -0
  67. package/dist/gaia-ops/hooks/modules/identity/__init__.py +0 -0
  68. package/dist/gaia-ops/hooks/modules/identity/identity_provider.py +21 -0
  69. package/dist/gaia-ops/hooks/modules/identity/ops_identity.py +34 -0
  70. package/dist/gaia-ops/hooks/modules/identity/security_identity.py +10 -0
  71. package/dist/gaia-ops/hooks/modules/memory/__init__.py +8 -0
  72. package/dist/gaia-ops/hooks/modules/memory/episode_writer.py +227 -0
  73. package/dist/gaia-ops/hooks/modules/orchestrator/__init__.py +1 -0
  74. package/dist/gaia-ops/hooks/modules/orchestrator/delegate_mode.py +128 -0
  75. package/dist/gaia-ops/hooks/modules/scanning/__init__.py +8 -0
  76. package/dist/gaia-ops/hooks/modules/scanning/scan_trigger.py +84 -0
  77. package/dist/gaia-ops/hooks/modules/security/__init__.py +89 -0
  78. package/dist/gaia-ops/hooks/modules/security/approval_cleanup.py +87 -0
  79. package/dist/gaia-ops/hooks/modules/security/approval_constants.py +23 -0
  80. package/dist/gaia-ops/hooks/modules/security/approval_grants.py +912 -0
  81. package/dist/gaia-ops/hooks/modules/security/approval_messages.py +71 -0
  82. package/dist/gaia-ops/hooks/modules/security/approval_scopes.py +153 -0
  83. package/dist/gaia-ops/hooks/modules/security/blocked_commands.py +584 -0
  84. package/dist/gaia-ops/hooks/modules/security/blocked_message_formatter.py +86 -0
  85. package/dist/gaia-ops/hooks/modules/security/command_semantics.py +130 -0
  86. package/dist/gaia-ops/hooks/modules/security/gitops_validator.py +179 -0
  87. package/dist/gaia-ops/hooks/modules/security/mutative_verbs.py +850 -0
  88. package/dist/gaia-ops/hooks/modules/security/prompt_validator.py +40 -0
  89. package/dist/gaia-ops/hooks/modules/security/tiers.py +196 -0
  90. package/dist/gaia-ops/hooks/modules/session/__init__.py +10 -0
  91. package/dist/gaia-ops/hooks/modules/session/session_context_writer.py +100 -0
  92. package/dist/gaia-ops/hooks/modules/session/session_event_injector.py +158 -0
  93. package/dist/gaia-ops/hooks/modules/session/session_manager.py +31 -0
  94. package/dist/gaia-ops/hooks/modules/tools/__init__.py +25 -0
  95. package/dist/gaia-ops/hooks/modules/tools/bash_validator.py +708 -0
  96. package/dist/gaia-ops/hooks/modules/tools/cloud_pipe_validator.py +181 -0
  97. package/dist/gaia-ops/hooks/modules/tools/hook_response.py +55 -0
  98. package/dist/gaia-ops/hooks/modules/tools/shell_parser.py +227 -0
  99. package/dist/gaia-ops/hooks/modules/tools/task_validator.py +283 -0
  100. package/dist/gaia-ops/hooks/modules/validation/__init__.py +23 -0
  101. package/dist/gaia-ops/hooks/modules/validation/commit_validator.py +380 -0
  102. package/dist/gaia-ops/hooks/post_compact.py +43 -0
  103. package/dist/gaia-ops/hooks/post_tool_use.py +54 -0
  104. package/dist/gaia-ops/hooks/pre_tool_use.py +383 -0
  105. package/dist/gaia-ops/hooks/session_start.py +69 -0
  106. package/dist/gaia-ops/hooks/stop_hook.py +69 -0
  107. package/dist/gaia-ops/hooks/subagent_start.py +71 -0
  108. package/dist/gaia-ops/hooks/subagent_stop.py +288 -0
  109. package/dist/gaia-ops/hooks/task_completed.py +70 -0
  110. package/dist/gaia-ops/hooks/user_prompt_submit.py +177 -0
  111. package/dist/gaia-ops/settings.json +72 -0
  112. package/dist/gaia-ops/skills/README.md +109 -0
  113. package/dist/gaia-ops/skills/agent-protocol/SKILL.md +105 -0
  114. package/dist/gaia-ops/skills/agent-protocol/examples.md +170 -0
  115. package/dist/gaia-ops/skills/agent-response/SKILL.md +53 -0
  116. package/dist/gaia-ops/skills/approval/SKILL.md +85 -0
  117. package/dist/gaia-ops/skills/approval/examples.md +140 -0
  118. package/dist/gaia-ops/skills/approval/reference.md +57 -0
  119. package/dist/gaia-ops/skills/command-execution/SKILL.md +64 -0
  120. package/dist/gaia-ops/skills/command-execution/reference.md +83 -0
  121. package/dist/gaia-ops/skills/context-updater/SKILL.md +76 -0
  122. package/dist/gaia-ops/skills/context-updater/examples.md +71 -0
  123. package/dist/gaia-ops/skills/developer-patterns/SKILL.md +93 -0
  124. package/dist/gaia-ops/skills/developer-patterns/reference.md +112 -0
  125. package/dist/gaia-ops/skills/execution/SKILL.md +66 -0
  126. package/dist/gaia-ops/skills/fast-queries/SKILL.md +47 -0
  127. package/dist/gaia-ops/skills/gaia-patterns/SKILL.md +92 -0
  128. package/dist/gaia-ops/skills/gaia-patterns/reference.md +22 -0
  129. package/dist/gaia-ops/skills/git-conventions/SKILL.md +48 -0
  130. package/dist/gaia-ops/skills/gitops-patterns/SKILL.md +73 -0
  131. package/dist/gaia-ops/skills/gitops-patterns/reference.md +183 -0
  132. package/dist/gaia-ops/skills/investigation/SKILL.md +77 -0
  133. package/dist/gaia-ops/skills/orchestrator-approval/SKILL.md +64 -0
  134. package/dist/gaia-ops/skills/reference.md +134 -0
  135. package/dist/gaia-ops/skills/security-tiers/SKILL.md +61 -0
  136. package/dist/gaia-ops/skills/security-tiers/destructive-commands-reference.md +623 -0
  137. package/dist/gaia-ops/skills/security-tiers/reference.md +39 -0
  138. package/dist/gaia-ops/skills/skill-creation/SKILL.md +119 -0
  139. package/dist/gaia-ops/skills/specification/SKILL.md +186 -0
  140. package/dist/gaia-ops/skills/speckit-workflow/SKILL.md +165 -0
  141. package/dist/gaia-ops/skills/speckit-workflow/reference.md +117 -0
  142. package/dist/gaia-ops/skills/terraform-patterns/SKILL.md +63 -0
  143. package/dist/gaia-ops/skills/terraform-patterns/reference.md +93 -0
  144. package/dist/gaia-ops/speckit/README.md +516 -0
  145. package/dist/gaia-ops/speckit/scripts/.gitkeep +0 -0
  146. package/dist/gaia-ops/speckit/templates/adr-template.md +118 -0
  147. package/dist/gaia-ops/speckit/templates/agent-file-template.md +23 -0
  148. package/dist/gaia-ops/speckit/templates/plan-template.md +227 -0
  149. package/dist/gaia-ops/speckit/templates/spec-template.md +140 -0
  150. package/dist/gaia-ops/speckit/templates/tasks-template.md +257 -0
  151. package/dist/gaia-ops/tools/context/README.md +132 -0
  152. package/dist/gaia-ops/tools/context/__init__.py +42 -0
  153. package/dist/gaia-ops/tools/context/_paths.py +20 -0
  154. package/dist/gaia-ops/tools/context/context_provider.py +476 -0
  155. package/dist/gaia-ops/tools/context/context_section_reader.py +330 -0
  156. package/dist/gaia-ops/tools/context/deep_merge.py +159 -0
  157. package/dist/gaia-ops/tools/context/pending_updates.py +760 -0
  158. package/dist/gaia-ops/tools/context/surface_router.py +278 -0
  159. package/dist/gaia-ops/tools/fast-queries/README.md +65 -0
  160. package/dist/gaia-ops/tools/fast-queries/__init__.py +30 -0
  161. package/dist/gaia-ops/tools/fast-queries/appservices/quicktriage_devops_developer.sh +75 -0
  162. package/dist/gaia-ops/tools/fast-queries/cloud/aws/quicktriage_aws_troubleshooter.sh +32 -0
  163. package/dist/gaia-ops/tools/fast-queries/cloud/gcp/quicktriage_gcp_troubleshooter.sh +88 -0
  164. package/dist/gaia-ops/tools/fast-queries/gitops/quicktriage_gitops_operator.sh +48 -0
  165. package/dist/gaia-ops/tools/fast-queries/run_triage.sh +59 -0
  166. package/dist/gaia-ops/tools/fast-queries/terraform/quicktriage_terraform_architect.sh +80 -0
  167. package/dist/gaia-ops/tools/gaia_simulator/__init__.py +33 -0
  168. package/dist/gaia-ops/tools/gaia_simulator/cli.py +354 -0
  169. package/dist/gaia-ops/tools/gaia_simulator/extractor.py +457 -0
  170. package/dist/gaia-ops/tools/gaia_simulator/reporter.py +258 -0
  171. package/dist/gaia-ops/tools/gaia_simulator/routing_simulator.py +334 -0
  172. package/dist/gaia-ops/tools/gaia_simulator/runner.py +539 -0
  173. package/dist/gaia-ops/tools/gaia_simulator/skills_mapper.py +262 -0
  174. package/dist/gaia-ops/tools/memory/README.md +0 -0
  175. package/dist/gaia-ops/tools/memory/__init__.py +20 -0
  176. package/dist/gaia-ops/tools/memory/episodic.py +1196 -0
  177. package/dist/gaia-ops/tools/persist_transcript_analysis.py +85 -0
  178. package/dist/gaia-ops/tools/review/__init__.py +1 -0
  179. package/dist/gaia-ops/tools/review/review_engine.py +157 -0
  180. package/dist/gaia-ops/tools/scan/__init__.py +35 -0
  181. package/dist/gaia-ops/tools/scan/config.py +247 -0
  182. package/dist/gaia-ops/tools/scan/merge.py +212 -0
  183. package/dist/gaia-ops/tools/scan/orchestrator.py +549 -0
  184. package/dist/gaia-ops/tools/scan/registry.py +127 -0
  185. package/dist/gaia-ops/tools/scan/scanners/__init__.py +18 -0
  186. package/dist/gaia-ops/tools/scan/scanners/base.py +137 -0
  187. package/dist/gaia-ops/tools/scan/scanners/environment.py +324 -0
  188. package/dist/gaia-ops/tools/scan/scanners/git.py +570 -0
  189. package/dist/gaia-ops/tools/scan/scanners/infrastructure.py +875 -0
  190. package/dist/gaia-ops/tools/scan/scanners/orchestration.py +600 -0
  191. package/dist/gaia-ops/tools/scan/scanners/stack.py +1085 -0
  192. package/dist/gaia-ops/tools/scan/scanners/tools.py +260 -0
  193. package/dist/gaia-ops/tools/scan/setup.py +753 -0
  194. package/dist/gaia-ops/tools/scan/tests/__init__.py +1 -0
  195. package/dist/gaia-ops/tools/scan/tests/conftest.py +796 -0
  196. package/dist/gaia-ops/tools/scan/tests/test_environment.py +323 -0
  197. package/dist/gaia-ops/tools/scan/tests/test_git.py +419 -0
  198. package/dist/gaia-ops/tools/scan/tests/test_infrastructure.py +382 -0
  199. package/dist/gaia-ops/tools/scan/tests/test_integration.py +920 -0
  200. package/dist/gaia-ops/tools/scan/tests/test_merge.py +269 -0
  201. package/dist/gaia-ops/tools/scan/tests/test_orchestration.py +304 -0
  202. package/dist/gaia-ops/tools/scan/tests/test_stack.py +604 -0
  203. package/dist/gaia-ops/tools/scan/tests/test_tools.py +349 -0
  204. package/dist/gaia-ops/tools/scan/ui.py +624 -0
  205. package/dist/gaia-ops/tools/scan/verify.py +266 -0
  206. package/dist/gaia-ops/tools/scan/walk.py +118 -0
  207. package/dist/gaia-ops/tools/scan/workspace.py +85 -0
  208. package/dist/gaia-ops/tools/validation/README.md +244 -0
  209. package/dist/gaia-ops/tools/validation/__init__.py +17 -0
  210. package/dist/gaia-ops/tools/validation/approval_gate.py +321 -0
  211. package/dist/gaia-ops/tools/validation/validate_skills.py +189 -0
  212. package/dist/gaia-security/.claude-plugin/plugin.json +22 -0
  213. package/dist/gaia-security/config/universal-rules.json +10 -0
  214. package/dist/gaia-security/hooks/adapters/__init__.py +52 -0
  215. package/dist/gaia-security/hooks/adapters/base.py +219 -0
  216. package/dist/gaia-security/hooks/adapters/channel.py +17 -0
  217. package/dist/gaia-security/hooks/adapters/claude_code.py +1477 -0
  218. package/dist/gaia-security/hooks/adapters/types.py +194 -0
  219. package/dist/gaia-security/hooks/adapters/utils.py +25 -0
  220. package/dist/gaia-security/hooks/hooks.json +57 -0
  221. package/dist/gaia-security/hooks/modules/__init__.py +15 -0
  222. package/dist/gaia-security/hooks/modules/agents/__init__.py +29 -0
  223. package/dist/gaia-security/hooks/modules/agents/contract_validator.py +647 -0
  224. package/dist/gaia-security/hooks/modules/agents/response_contract.py +496 -0
  225. package/dist/gaia-security/hooks/modules/agents/skill_injection_verifier.py +124 -0
  226. package/dist/gaia-security/hooks/modules/agents/task_info_builder.py +74 -0
  227. package/dist/gaia-security/hooks/modules/agents/transcript_analyzer.py +458 -0
  228. package/dist/gaia-security/hooks/modules/agents/transcript_reader.py +152 -0
  229. package/dist/gaia-security/hooks/modules/audit/__init__.py +28 -0
  230. package/dist/gaia-security/hooks/modules/audit/event_detector.py +168 -0
  231. package/dist/gaia-security/hooks/modules/audit/logger.py +131 -0
  232. package/dist/gaia-security/hooks/modules/audit/metrics.py +134 -0
  233. package/dist/gaia-security/hooks/modules/audit/workflow_auditor.py +576 -0
  234. package/dist/gaia-security/hooks/modules/audit/workflow_recorder.py +296 -0
  235. package/dist/gaia-security/hooks/modules/context/__init__.py +11 -0
  236. package/dist/gaia-security/hooks/modules/context/anchor_tracker.py +317 -0
  237. package/dist/gaia-security/hooks/modules/context/compact_context_builder.py +215 -0
  238. package/dist/gaia-security/hooks/modules/context/context_cache.py +129 -0
  239. package/dist/gaia-security/hooks/modules/context/context_freshness.py +145 -0
  240. package/dist/gaia-security/hooks/modules/context/context_injector.py +427 -0
  241. package/dist/gaia-security/hooks/modules/context/context_writer.py +518 -0
  242. package/dist/gaia-security/hooks/modules/context/contracts_loader.py +161 -0
  243. package/dist/gaia-security/hooks/modules/core/__init__.py +40 -0
  244. package/dist/gaia-security/hooks/modules/core/hook_entry.py +78 -0
  245. package/dist/gaia-security/hooks/modules/core/paths.py +160 -0
  246. package/dist/gaia-security/hooks/modules/core/plugin_mode.py +149 -0
  247. package/dist/gaia-security/hooks/modules/core/plugin_setup.py +558 -0
  248. package/dist/gaia-security/hooks/modules/core/state.py +179 -0
  249. package/dist/gaia-security/hooks/modules/core/stdin.py +24 -0
  250. package/dist/gaia-security/hooks/modules/events/__init__.py +1 -0
  251. package/dist/gaia-security/hooks/modules/events/event_writer.py +210 -0
  252. package/dist/gaia-security/hooks/modules/identity/__init__.py +0 -0
  253. package/dist/gaia-security/hooks/modules/identity/identity_provider.py +21 -0
  254. package/dist/gaia-security/hooks/modules/identity/ops_identity.py +34 -0
  255. package/dist/gaia-security/hooks/modules/identity/security_identity.py +10 -0
  256. package/dist/gaia-security/hooks/modules/memory/__init__.py +8 -0
  257. package/dist/gaia-security/hooks/modules/memory/episode_writer.py +227 -0
  258. package/dist/gaia-security/hooks/modules/orchestrator/__init__.py +1 -0
  259. package/dist/gaia-security/hooks/modules/orchestrator/delegate_mode.py +128 -0
  260. package/dist/gaia-security/hooks/modules/scanning/__init__.py +8 -0
  261. package/dist/gaia-security/hooks/modules/scanning/scan_trigger.py +84 -0
  262. package/dist/gaia-security/hooks/modules/security/__init__.py +89 -0
  263. package/dist/gaia-security/hooks/modules/security/approval_cleanup.py +87 -0
  264. package/dist/gaia-security/hooks/modules/security/approval_constants.py +23 -0
  265. package/dist/gaia-security/hooks/modules/security/approval_grants.py +912 -0
  266. package/dist/gaia-security/hooks/modules/security/approval_messages.py +71 -0
  267. package/dist/gaia-security/hooks/modules/security/approval_scopes.py +153 -0
  268. package/dist/gaia-security/hooks/modules/security/blocked_commands.py +584 -0
  269. package/dist/gaia-security/hooks/modules/security/blocked_message_formatter.py +86 -0
  270. package/dist/gaia-security/hooks/modules/security/command_semantics.py +130 -0
  271. package/dist/gaia-security/hooks/modules/security/gitops_validator.py +179 -0
  272. package/dist/gaia-security/hooks/modules/security/mutative_verbs.py +850 -0
  273. package/dist/gaia-security/hooks/modules/security/prompt_validator.py +40 -0
  274. package/dist/gaia-security/hooks/modules/security/tiers.py +196 -0
  275. package/dist/gaia-security/hooks/modules/session/__init__.py +10 -0
  276. package/dist/gaia-security/hooks/modules/session/session_context_writer.py +100 -0
  277. package/dist/gaia-security/hooks/modules/session/session_event_injector.py +158 -0
  278. package/dist/gaia-security/hooks/modules/session/session_manager.py +31 -0
  279. package/dist/gaia-security/hooks/modules/tools/__init__.py +25 -0
  280. package/dist/gaia-security/hooks/modules/tools/bash_validator.py +708 -0
  281. package/dist/gaia-security/hooks/modules/tools/cloud_pipe_validator.py +181 -0
  282. package/dist/gaia-security/hooks/modules/tools/hook_response.py +55 -0
  283. package/dist/gaia-security/hooks/modules/tools/shell_parser.py +227 -0
  284. package/dist/gaia-security/hooks/modules/tools/task_validator.py +283 -0
  285. package/dist/gaia-security/hooks/modules/validation/__init__.py +23 -0
  286. package/dist/gaia-security/hooks/modules/validation/commit_validator.py +380 -0
  287. package/dist/gaia-security/hooks/post_tool_use.py +54 -0
  288. package/dist/gaia-security/hooks/pre_tool_use.py +383 -0
  289. package/dist/gaia-security/hooks/session_start.py +69 -0
  290. package/dist/gaia-security/hooks/stop_hook.py +69 -0
  291. package/dist/gaia-security/hooks/user_prompt_submit.py +177 -0
  292. package/dist/gaia-security/settings.json +58 -0
  293. package/hooks/README.md +3 -2
  294. package/hooks/adapters/channel.py +0 -25
  295. package/hooks/adapters/claude_code.py +364 -125
  296. package/hooks/elicitation_result.py +132 -0
  297. package/hooks/hooks.json +10 -1
  298. package/hooks/modules/README.md +1 -1
  299. package/hooks/modules/agents/contract_validator.py +3 -51
  300. package/hooks/modules/agents/response_contract.py +4 -8
  301. package/hooks/modules/agents/transcript_reader.py +4 -5
  302. package/hooks/modules/audit/__init__.py +4 -6
  303. package/hooks/modules/audit/event_detector.py +0 -2
  304. package/hooks/modules/audit/metrics.py +108 -187
  305. package/hooks/modules/audit/workflow_auditor.py +0 -4
  306. package/hooks/modules/audit/workflow_recorder.py +0 -5
  307. package/hooks/modules/context/context_cache.py +129 -0
  308. package/hooks/modules/context/context_injector.py +18 -40
  309. package/hooks/modules/context/context_writer.py +1 -25
  310. package/hooks/modules/context/contracts_loader.py +7 -10
  311. package/hooks/modules/core/paths.py +12 -13
  312. package/hooks/modules/core/plugin_setup.py +109 -2
  313. package/hooks/modules/events/__init__.py +1 -0
  314. package/hooks/modules/events/event_writer.py +210 -0
  315. package/hooks/modules/identity/ops_identity.py +18 -22
  316. package/hooks/modules/memory/episode_writer.py +1 -6
  317. package/hooks/modules/orchestrator/__init__.py +1 -0
  318. package/hooks/modules/orchestrator/delegate_mode.py +128 -0
  319. package/hooks/modules/security/__init__.py +2 -4
  320. package/hooks/modules/security/approval_constants.py +5 -1
  321. package/hooks/modules/security/approval_grants.py +189 -6
  322. package/hooks/modules/security/approval_messages.py +0 -2
  323. package/hooks/modules/security/blocked_commands.py +2 -34
  324. package/hooks/modules/security/command_semantics.py +0 -4
  325. package/hooks/modules/security/gitops_validator.py +1 -11
  326. package/hooks/modules/security/mutative_verbs.py +179 -38
  327. package/hooks/modules/security/tiers.py +0 -19
  328. package/hooks/modules/session/session_event_injector.py +0 -25
  329. package/hooks/modules/tools/bash_validator.py +219 -91
  330. package/hooks/modules/tools/shell_parser.py +0 -1
  331. package/hooks/modules/tools/task_validator.py +9 -29
  332. package/hooks/post_tool_use.py +0 -72
  333. package/hooks/pre_tool_use.py +38 -88
  334. package/hooks/subagent_start.py +6 -2
  335. package/hooks/subagent_stop.py +1 -13
  336. package/hooks/user_prompt_submit.py +89 -1
  337. package/index.js +1 -1
  338. package/package.json +3 -2
  339. package/skills/README.md +3 -5
  340. package/skills/agent-protocol/SKILL.md +17 -16
  341. package/skills/agent-protocol/examples.md +6 -6
  342. package/skills/agent-response/SKILL.md +11 -14
  343. package/skills/approval/SKILL.md +28 -13
  344. package/skills/approval/reference.md +2 -2
  345. package/skills/execution/SKILL.md +1 -1
  346. package/skills/gaia-patterns/SKILL.md +2 -3
  347. package/skills/orchestrator-approval/SKILL.md +22 -50
  348. package/skills/security-tiers/SKILL.md +1 -1
  349. package/templates/README.md +3 -13
  350. package/tools/gaia_simulator/runner.py +34 -1
  351. package/tools/scan/orchestrator.py +13 -0
  352. package/tools/scan/scanners/base.py +8 -0
  353. package/tools/scan/scanners/git.py +78 -0
  354. package/tools/scan/scanners/infrastructure.py +65 -0
  355. package/tools/scan/scanners/stack.py +110 -0
  356. package/tools/scan/setup.py +120 -13
  357. package/tools/scan/workspace.py +85 -0
  358. package/config/context-contracts.aws.json +0 -42
  359. package/config/context-contracts.gcp.json +0 -39
  360. package/skills/project-dispatch/SKILL.md +0 -34
@@ -8,7 +8,7 @@
8
8
  {
9
9
  "name": "gaia-security",
10
10
  "description": "Keeps you in the loop only when it matters. Gaia Security analyzes every command and classifies it into risk tiers: read-only queries run freely, simulations and validations pass through, and state-changing operations (create, delete, apply, push) pause for your explicit approval before executing. Irreversible commands like dropping databases or deleting cloud infrastructure are permanently blocked.",
11
- "version": "4.5.0",
11
+ "version": "4.7.2",
12
12
  "source": "./dist/gaia-security"
13
13
  }
14
14
  ]
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gaia-ops",
3
- "version": "4.5.0",
3
+ "version": "4.7.2",
4
4
  "description": "Security-first orchestrator with specialized agents, hooks, and governance for AI coding",
5
5
  "author": {
6
6
  "name": "jaguilar87"
package/ARCHITECTURE.md CHANGED
@@ -25,7 +25,8 @@ User request
25
25
  v
26
26
  user_prompt_submit.py (UserPromptSubmit hook)
27
27
  | Inject orchestrator identity via ops_identity.py
28
- | Skills loaded on-demand: project-dispatch, agent-response
28
+ | Inject surface routing recommendation (deterministic)
29
+ | Skills loaded on-demand: agent-response
29
30
  v
30
31
  Orchestrator dispatches to agent
31
32
  | Routes by surface classification
@@ -49,7 +50,7 @@ subagent_stop.py (SubagentStop hook)
49
50
  v
50
51
  Orchestrator processes json:contract (via agent-response skill)
51
52
  | COMPLETE -> summarize to user
52
- | AWAITING_APPROVAL -> get approval -> resume via SendMessage
53
+ | REVIEW (with approval_id) -> get approval -> resume via SendMessage
53
54
  | NEEDS_INPUT -> ask user -> resume via SendMessage
54
55
  | BLOCKED -> report blocker
55
56
  ```
@@ -170,7 +171,7 @@ Nonce-based T3 approval lifecycle:
170
171
  3. BashValidator generates 128-bit nonce via generate_nonce()
171
172
  4. write_pending_approval() saves pending-{nonce}.json to .claude/cache/approvals/
172
173
  5. Hook returns corrective deny (exit 0) with NONCE:{hex} in message
173
- 6. Agent includes NONCE:{hex} in PENDING_APPROVAL status to orchestrator
174
+ 6. Agent includes NONCE:{hex} in REVIEW status to orchestrator
174
175
  7. Orchestrator presents plan to user, asks for approval
175
176
  8. User approves -> orchestrator resumes agent with "APPROVE:{nonce}"
176
177
  9. pre_tool_use.py detects APPROVE: prefix, calls activate_pending_approval()
@@ -182,7 +183,7 @@ Nonce-based T3 approval lifecycle:
182
183
 
183
184
  Every agent response must end with a `json:contract` block containing `agent_status`. The contract validator (`hooks/modules/agents/contract_validator.py`) enforces:
184
185
 
185
- - **AGENT_STATUS**: PLAN_STATUS (from 6 valid states: COMPLETE, NEEDS_INPUT, REVIEW, AWAITING_APPROVAL, BLOCKED, IN_PROGRESS), PENDING_STEPS, NEXT_ACTION, AGENT_ID
186
+ - **AGENT_STATUS**: PLAN_STATUS (from 5 valid states: COMPLETE, NEEDS_INPUT, REVIEW, BLOCKED, IN_PROGRESS), PENDING_STEPS, NEXT_ACTION, AGENT_ID
186
187
  - **EVIDENCE_REPORT**: required for all valid states. Seven fields: PATTERNS_CHECKED, FILES_CHECKED, COMMANDS_RUN, KEY_OUTPUTS, VERBATIM_OUTPUTS, CROSS_LAYER_IMPACTS, OPEN_GAPS
187
188
  - **CONSOLIDATION_REPORT**: required when multi-surface or cross-check. Fields: OWNERSHIP_ASSESSMENT (enum), CONFIRMED_FINDINGS, SUSPECTED_FINDINGS, CONFLICTS, OPEN_GAPS, NEXT_BEST_AGENT
188
189
 
@@ -256,12 +257,12 @@ The adapter layer connects Claude Code's hook protocol to gaia-ops business logi
256
257
  | **Adapter methods called** | `ClaudeCodeAdapter.format_validation_response()` |
257
258
  | **Business logic modules** | None (pure formatting bridge) |
258
259
 
259
- ### CP-5: `templates/settings.template.json` / `hooks/hooks.json` -- Hook Configuration
260
+ ### CP-5: `hooks/hooks.json` -- Hook Configuration
260
261
 
261
262
  | Attribute | Value |
262
263
  |-----------|-------|
263
- | **File (npm channel)** | `templates/settings.template.json` -- paths use `.claude/hooks/` prefix |
264
264
  | **File (plugin channel)** | `hooks/hooks.json` -- paths use `${CLAUDE_PLUGIN_ROOT}/hooks/` prefix |
265
+ | **File (npm channel)** | `hooks/hooks.json` (symlinked into `.claude/hooks/`) |
265
266
  | **What it does** | Maps Claude Code hook events to handler scripts. Defines which events fire which entry points, the tool matchers (Bash, Task, Agent, `*`), and permissions (allow/deny lists). |
266
267
  | **Events configured** | PreToolUse (Bash, Task, Agent, SendMessage), PostToolUse, SubagentStop, SessionStart, Stop, TaskCompleted, SubagentStart, UserPromptSubmit (identity injection) |
267
268
 
@@ -292,7 +293,7 @@ To add support for a new Claude Code hook event (e.g., a future `PreCompact` eve
292
293
  2. **Add adapter method** to `ClaudeCodeAdapter` in `hooks/adapters/claude_code.py` -- implement `adapt_<event_name>(raw: dict) -> <ResultType>` and the corresponding `format_<result>_response()` if a new result type is needed.
293
294
  3. **Add extract/format methods** for the event type -- the extract method pulls typed data from the raw payload, the format method builds the CLI response JSON.
294
295
  4. **Create hook script entry point** -- a new `hooks/<event_name>.py` file that reads stdin, calls `adapter.parse_event()`, delegates to business logic, and writes the response to stdout.
295
- 5. **Add entry to `hooks/hooks.json`** (plugin channel) and `templates/settings.template.json` (npm channel) mapping the event name to the new script.
296
+ 5. **Add entry to `hooks/hooks.json`** mapping the event name to the new script.
296
297
 
297
298
  **Zero changes to business logic modules required.** The adapter is the only layer that touches CLI-specific JSON.
298
299
 
@@ -311,7 +312,7 @@ To support a CLI other than Claude Code (e.g., a hypothetical Cursor or Windsurf
311
312
  | File | Purpose |
312
313
  |------|---------|
313
314
  | `hooks/modules/identity/ops_identity.py` | Orchestrator identity (injected by UserPromptSubmit) |
314
- | `skills/project-dispatch/SKILL.md` | Agent routing table and dispatch rules (on-demand) |
315
+ | `config/surface-routing.json` | Surface routing config (agent table, signals, dispatch) |
315
316
  | `skills/agent-response/SKILL.md` | Contract status handling protocol (on-demand) |
316
317
  | `hooks/pre_tool_use.py` | PreToolUse hook entry point |
317
318
  | `hooks/subagent_stop.py` | SubagentStop hook entry point |
@@ -19,7 +19,7 @@ skills:
19
19
 
20
20
  1. **Triage first**: When checking infrastructure state, run the fast-queries Terraform or cloud triage script before running plan/apply.
21
21
  2. **Deep analysis**: When investigating drift or complex module dependencies, follow the investigation phases.
22
- 3. **Before T3 operations**: When `terragrunt apply` is needed, present a REVIEW plan first. If a hook blocks it, follow the AWAITING_APPROVAL flow.
22
+ 3. **Before T3 operations**: When `terragrunt apply` is needed, present a REVIEW plan first. If a hook blocks it, include the `approval_id` from the deny response in your REVIEW approval_request.
23
23
  4. **Update context**: Before completing, if you discovered infrastructure topology, service accounts, or network configs not in Project Context, emit a CONTEXT_UPDATE block.
24
24
 
25
25
  ## Identity
@@ -125,7 +125,6 @@ function colorStatus(status) {
125
125
  if (s === 'NEEDS_INPUT') return chalk.yellow(s.padEnd(8));
126
126
  if (s === 'IN_PROGRESS') return chalk.cyan(s.padEnd(8));
127
127
  if (s === 'REVIEW') return chalk.magenta(s.padEnd(8));
128
- if (s === 'AWAITING_APPROVAL') return chalk.yellow(s.padEnd(8));
129
128
  return chalk.gray(s.padEnd(8));
130
129
  }
131
130
 
@@ -450,7 +450,7 @@ function calculateAgentInvocations(workflowMetrics) {
450
450
 
451
451
  /**
452
452
  * Agent outcome distribution from plan_status field.
453
- * Counts COMPLETE, BLOCKED, NEEDS_INPUT, IN_PROGRESS, REVIEW, AWAITING_APPROVAL, and others.
453
+ * Counts COMPLETE, BLOCKED, NEEDS_INPUT, IN_PROGRESS, REVIEW, and others.
454
454
  * Returns null if no entries have the plan_status field (older data).
455
455
  */
456
456
  function calculateAgentOutcomes(workflowMetrics) {
@@ -874,7 +874,7 @@ function displayMetrics(
874
874
  // ── Agent Outcomes ───────────────────────────────────
875
875
  if (agentOutcomes) {
876
876
  console.log(chalk.bold(`\n📋 Agent Outcomes (${agentOutcomes.total} sessions with status)`));
877
- const outcomeColor = { COMPLETE: chalk.green, BLOCKED: chalk.red, NEEDS_INPUT: chalk.yellow, IN_PROGRESS: chalk.cyan, REVIEW: chalk.magenta, AWAITING_APPROVAL: chalk.yellow };
877
+ const outcomeColor = { COMPLETE: chalk.green, BLOCKED: chalk.red, NEEDS_INPUT: chalk.yellow, IN_PROGRESS: chalk.cyan, REVIEW: chalk.magenta };
878
878
  for (const { status, count, percentage } of agentOutcomes.distribution) {
879
879
  const bar = makeBar(percentage, 10);
880
880
  const pct = percentage.toFixed(1).padStart(5);
package/bin/gaia-scan.py CHANGED
@@ -205,6 +205,7 @@ def _mode_fresh(project_root: Path, scan_config: ScanConfig, args) -> int:
205
205
  generate_governance,
206
206
  generate_project_context,
207
207
  install_git_hooks,
208
+ merge_hooks_to_settings_local,
208
209
  )
209
210
  from tools.scan.ui import (
210
211
  RailUI,
@@ -246,6 +247,7 @@ def _mode_fresh(project_root: Path, scan_config: ScanConfig, args) -> int:
246
247
  create_claude_directory(project_root)
247
248
  copy_claude_md(project_root)
248
249
  copy_settings_json(project_root)
250
+ merge_hooks_to_settings_local(project_root)
249
251
  install_git_hooks(project_root)
250
252
  generate_project_context(project_root, config, scan_context=output.context)
251
253
  generate_governance(project_root, config)
@@ -287,6 +289,7 @@ def _mode_existing(project_root: Path, scan_config: ScanConfig, args) -> int:
287
289
  copy_settings_json,
288
290
  create_claude_directory,
289
291
  install_git_hooks,
292
+ merge_hooks_to_settings_local,
290
293
  )
291
294
  from tools.scan.ui import (
292
295
  RailUI,
@@ -317,6 +320,7 @@ def _mode_existing(project_root: Path, scan_config: ScanConfig, args) -> int:
317
320
  # Step 4: SYNC
318
321
  copy_claude_md(project_root)
319
322
  copy_settings_json(project_root)
323
+ merge_hooks_to_settings_local(project_root)
320
324
  create_claude_directory(project_root)
321
325
  install_git_hooks(project_root)
322
326
 
@@ -12,12 +12,14 @@
12
12
  * 2. Run gaia-scan --npm-postinstall to create .claude/, symlinks, settings, project-context
13
13
  * 3. Create plugin-registry.json
14
14
  * 4. Merge permissions into settings.local.json
15
- * 5. Fall through to verification
15
+ * 5. Merge hooks into settings.local.json
16
+ * 6. Fall through to verification
16
17
  * - Update (.claude/ exists):
17
18
  * 1. Show version transition (previous → current)
18
- * 2. settings.json: REPLACE from template (hooks only — no permissions)
19
- * 3. Merge permissions into settings.local.json (union, preserves user config)
20
- * 4. Symlinks: recreate if missing, fix broken ones
19
+ * 2. settings.json: create only if missing (non-invasive, never overwrites)
20
+ * 3. Merge permissions + env vars into settings.local.json (union, preserves user config)
21
+ * 4. Merge hooks from hooks.json into settings.local.json (npm mode requires this)
22
+ * 5. Symlinks: recreate if missing, fix broken ones
21
23
  * 5. Verify: hooks, python, project-context, config files
22
24
  * 6. Report: summary with any issues found
23
25
  *
@@ -78,19 +80,25 @@ async function readPackageVersion(path) {
78
80
  // ============================================================================
79
81
 
80
82
  async function updateSettingsJson() {
81
- const spinner = ora('Updating settings.json...').start();
83
+ const spinner = ora('Checking settings.json...').start();
82
84
  try {
83
- const templatePath = join(__dirname, '../templates/settings.template.json');
84
85
  const settingsPath = join(CWD, '.claude', 'settings.json');
85
86
 
86
- if (!existsSync(templatePath) || !existsSync(join(CWD, '.claude'))) {
87
- spinner.info('Skipped');
87
+ if (!existsSync(join(CWD, '.claude'))) {
88
+ spinner.info('Skipped (.claude/ not found)');
89
+ return false;
90
+ }
91
+
92
+ // Non-invasive: only create if missing. Never overwrite.
93
+ // Hooks come from hooks.json (auto-discovered via symlink).
94
+ // Env vars and permissions live in settings.local.json.
95
+ if (existsSync(settingsPath)) {
96
+ spinner.succeed('settings.json already exists (not overwriting)');
88
97
  return false;
89
98
  }
90
99
 
91
- // Always replace from template -- template is the source of truth (hooks only)
92
- await fs.copyFile(templatePath, settingsPath);
93
- spinner.succeed('settings.json updated from template (hooks)');
100
+ await fs.writeFile(settingsPath, '{}\n');
101
+ spinner.succeed('settings.json created (minimal — hooks from hooks.json)');
94
102
  return true;
95
103
  } catch (error) {
96
104
  spinner.fail(`settings.json: ${error.message}`);
@@ -197,8 +205,14 @@ print(json.dumps(ops_perms))
197
205
  existing.permissions.deny = mergedDeny;
198
206
  existing.permissions.ask = existing.permissions.ask || [];
199
207
 
208
+ // Add env vars (smart merge: add if not present, don't overwrite)
209
+ existing.env = existing.env || {};
210
+ if (!('CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS' in existing.env)) {
211
+ existing.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = '1';
212
+ }
213
+
200
214
  await fs.writeFile(localPath, JSON.stringify(existing, null, 2) + '\n');
201
- spinner.succeed('settings.local.json permissions merged');
215
+ spinner.succeed('settings.local.json permissions and env merged');
202
216
  return true;
203
217
  } catch (error) {
204
218
  spinner.fail(`settings.local.json: ${error.message}`);
@@ -206,6 +220,109 @@ print(json.dumps(ops_perms))
206
220
  }
207
221
  }
208
222
 
223
+ async function updateLocalHooks() {
224
+ const spinner = ora('Merging hooks into settings.local.json...').start();
225
+ try {
226
+ const claudeDir = join(CWD, '.claude');
227
+ const localPath = join(claudeDir, 'settings.local.json');
228
+
229
+ if (!existsSync(claudeDir)) {
230
+ spinner.info('Skipped (.claude/ not found)');
231
+ return false;
232
+ }
233
+
234
+ // Read hooks.json from the installed package
235
+ const hooksJsonPath = join(__dirname, '..', 'hooks', 'hooks.json');
236
+ if (!existsSync(hooksJsonPath)) {
237
+ spinner.warn('hooks.json not found in package');
238
+ return false;
239
+ }
240
+
241
+ let hooksData;
242
+ try {
243
+ hooksData = JSON.parse(await fs.readFile(hooksJsonPath, 'utf-8'));
244
+ } catch {
245
+ spinner.warn('hooks.json is invalid JSON');
246
+ return false;
247
+ }
248
+
249
+ // Unwrap outer "hooks" key if present
250
+ const sourceHooks = hooksData.hooks || hooksData;
251
+
252
+ // Convert ${CLAUDE_PLUGIN_ROOT}/hooks/<script> to .claude/hooks/<script> for npm mode
253
+ const convertCommand = (cmd) => {
254
+ return cmd.replace(/\$\{CLAUDE_PLUGIN_ROOT\}\/hooks\//g, '.claude/hooks/');
255
+ };
256
+
257
+ const convertedHooks = {};
258
+ for (const [event, entries] of Object.entries(sourceHooks)) {
259
+ convertedHooks[event] = entries.map(entry => {
260
+ const converted = { ...entry };
261
+ if (converted.hooks) {
262
+ converted.hooks = converted.hooks.map(h => ({
263
+ ...h,
264
+ command: h.command ? convertCommand(h.command) : h.command,
265
+ }));
266
+ }
267
+ return converted;
268
+ });
269
+ }
270
+
271
+ // Load existing settings.local.json
272
+ let existing = {};
273
+ if (existsSync(localPath)) {
274
+ try {
275
+ existing = JSON.parse(await fs.readFile(localPath, 'utf-8'));
276
+ } catch {
277
+ existing = {};
278
+ }
279
+ }
280
+
281
+ // Smart merge: for each hook event, deduplicate by command string
282
+ const existingHooks = existing.hooks || {};
283
+ let changed = false;
284
+
285
+ for (const [event, newEntries] of Object.entries(convertedHooks)) {
286
+ if (!existingHooks[event]) {
287
+ existingHooks[event] = newEntries;
288
+ changed = true;
289
+ continue;
290
+ }
291
+
292
+ // Collect existing command strings for deduplication
293
+ const existingCommands = new Set();
294
+ for (const entry of existingHooks[event]) {
295
+ for (const h of (entry.hooks || [])) {
296
+ if (h.command) existingCommands.add(h.command);
297
+ }
298
+ }
299
+
300
+ // Add new entries whose commands are not already present
301
+ for (const newEntry of newEntries) {
302
+ const newCommands = (newEntry.hooks || []).map(h => h.command).filter(Boolean);
303
+ const allPresent = newCommands.length > 0 && newCommands.every(c => existingCommands.has(c));
304
+ if (!allPresent) {
305
+ existingHooks[event].push(newEntry);
306
+ changed = true;
307
+ }
308
+ }
309
+ }
310
+
311
+ if (!changed) {
312
+ spinner.succeed('settings.local.json hooks already up to date');
313
+ return false;
314
+ }
315
+
316
+ existing.hooks = existingHooks;
317
+ await fs.writeFile(localPath, JSON.stringify(existing, null, 2) + '\n');
318
+ spinner.succeed('settings.local.json hooks merged');
319
+ return true;
320
+ } catch (error) {
321
+ spinner.fail(`hooks merge: ${error.message}`);
322
+ return false;
323
+ }
324
+ }
325
+
209
326
  async function updateSymlinks() {
210
327
  const spinner = ora('Checking symlinks...').start();
211
328
  try {
@@ -340,18 +457,21 @@ async function runVerification() {
340
457
  checks.push({ name: 'agent definitions', ok: agentsOk === agentFiles.length, detail: `${agentsOk}/${agentFiles.length}` });
341
458
  if (agentsOk < agentFiles.length) issues.push(`${agentFiles.length - agentsOk} agent definition(s) missing`);
342
459
 
343
- // 6. settings.json has hooks configured
344
- const settingsPath = join(CWD, '.claude', 'settings.json');
345
- if (existsSync(settingsPath)) {
460
+ // 6. hooks.json exists (hooks are auto-discovered from hooks directory)
461
+ const hooksJsonPath = join(CWD, '.claude', 'hooks', 'hooks.json');
462
+ if (existsSync(hooksJsonPath)) {
346
463
  try {
347
- const settings = JSON.parse(await fs.readFile(settingsPath, 'utf-8'));
348
- const hasHooks = settings.hooks && Object.keys(settings.hooks).length > 0;
349
- checks.push({ name: 'hooks config', ok: hasHooks });
350
- if (!hasHooks) issues.push('settings.json has no hooks configured');
464
+ const hooksData = JSON.parse(await fs.readFile(hooksJsonPath, 'utf-8'));
465
+ const hasHooks = hooksData.hooks && Object.keys(hooksData.hooks).length > 0;
466
+ checks.push({ name: 'hooks.json', ok: hasHooks });
467
+ if (!hasHooks) issues.push('hooks.json has no hooks configured');
351
468
  } catch {
352
- checks.push({ name: 'hooks config', ok: false });
353
- issues.push('settings.json is invalid');
469
+ checks.push({ name: 'hooks.json', ok: false });
470
+ issues.push('hooks.json is invalid');
354
471
  }
472
+ } else {
473
+ checks.push({ name: 'hooks.json', ok: false });
474
+ issues.push('hooks.json not found (hooks symlink may be broken)');
355
475
  }
356
476
 
357
477
  const passed = checks.filter(c => c.ok).length;
@@ -421,6 +541,9 @@ async function runFreshInstall() {
421
541
 
422
542
  // 4. Merge permissions into settings.local.json (same approach as plugin mode)
423
543
  await updateLocalPermissions();
544
+
545
+ // 5. Merge hooks into settings.local.json (npm mode — Claude Code reads hooks from settings, not hooks.json)
546
+ await updateLocalHooks();
424
547
  }
425
548
 
426
549
  async function main() {
@@ -439,9 +562,10 @@ async function main() {
439
562
 
440
563
  console.log(chalk.cyan(`\n gaia-ops update ${versionLine}\n`));
441
564
 
442
- // Step 1-3: Update files
565
+ // Step 1-4: Update files
443
566
  await updateSettingsJson();
444
567
  await updateLocalPermissions();
568
+ await updateLocalHooks();
445
569
  await updateSymlinks();
446
570
  }
447
571
 
@@ -46,10 +46,10 @@ function detectGaiaOpsRoot(startPath) {
46
46
  // Buscar node_modules de múltiples maneras
47
47
  function findNodeModulesPath(gaiaOpsRoot) {
48
48
  const candidates = [
49
- path.resolve(gaiaOpsRoot, '..', 'node_modules'), // ../node_modules
50
- path.resolve(gaiaOpsRoot, 'node_modules'), // ./node_modules
49
+ path.resolve(gaiaOpsRoot, 'node_modules'), // ./node_modules (CI: npm ci in repo root)
50
+ path.resolve(gaiaOpsRoot, '..', 'node_modules'), // ../node_modules (monorepo consumer)
51
51
  path.resolve(process.cwd(), 'node_modules'), // cwd/node_modules
52
- path.join(gaiaOpsRoot, '..', '..', 'node_modules'), // ../../node_modules
52
+ path.join(gaiaOpsRoot, '..', '..', 'node_modules'), // ../../node_modules (deep nesting)
53
53
  ];
54
54
 
55
55
  for (const candidate of candidates) {
@@ -59,8 +59,16 @@ function findNodeModulesPath(gaiaOpsRoot) {
59
59
  }
60
60
  }
61
61
 
62
- // Si no encuentra, retorna la ruta esperada (será creada por npm install)
63
- return path.resolve(gaiaOpsRoot, '..', 'node_modules');
62
+ // In CI, node_modules exists inside the repo root (npm ci) but won't contain
63
+ // the package itself as a nested dependency. Prefer gaiaOpsRoot/node_modules
64
+ // over going up a level (which breaks in CI checkout structures).
65
+ const localNodeModules = path.resolve(gaiaOpsRoot, 'node_modules');
66
+ if (fs.existsSync(localNodeModules)) {
67
+ return localNodeModules;
68
+ }
69
+
70
+ // Fallback: use __dirname-relative path (repo root) instead of process.cwd()
71
+ return path.resolve(gaiaOpsRoot, 'node_modules');
64
72
  }
65
73
 
66
74
  const GAIA_OPS_ROOT = detectGaiaOpsRoot(path.resolve(__dirname, '..'));
@@ -219,14 +227,21 @@ class PrePublishValidator {
219
227
  }
220
228
 
221
229
  reinstallNodeModules() {
222
- this.log('Reinstalling node_modules in monorepo...', 'info');
230
+ this.log('Reinstalling node_modules...', 'info');
223
231
 
224
232
  if (this.dryRun) {
225
233
  this.log('[DRY RUN] Would run: npm install', 'info');
226
234
  return;
227
235
  }
228
236
 
229
- this.execute('npm install', MONOREPO_ROOT);
237
+ // In CI, MONOREPO_ROOT may resolve incorrectly (one level above checkout).
238
+ // Use GAIA_OPS_ROOT if it contains a package.json (i.e., we ARE the root).
239
+ const installDir = fs.existsSync(path.join(GAIA_OPS_ROOT, 'package-lock.json'))
240
+ ? GAIA_OPS_ROOT
241
+ : MONOREPO_ROOT;
242
+
243
+ this.log(`Install directory: ${installDir}`, 'info');
244
+ this.execute('npm install', installDir);
230
245
  this.log('✓ npm install completed', 'success');
231
246
  }
232
247
 
@@ -238,7 +253,6 @@ class PrePublishValidator {
238
253
  'bin/gaia-scan',
239
254
  'tools/context/context_provider.py',
240
255
  'hooks/pre_tool_use.py',
241
- 'templates/settings.template.json'
242
256
  ];
243
257
 
244
258
  let allValid = true;
@@ -330,7 +344,6 @@ class PrePublishValidator {
330
344
  // Test 1: Validate JSON files
331
345
  this.log('Test 1: Validating JSON configuration files...', 'info');
332
346
  const jsonFiles = [
333
- 'templates/settings.template.json',
334
347
  'config/clarification_rules.json',
335
348
  'config/git_standards.json'
336
349
  ];
@@ -505,12 +518,22 @@ class PrePublishValidator {
505
518
  // Parse command line arguments and run
506
519
  async function main() {
507
520
  const args = process.argv.slice(2);
521
+ const isCI = process.env.CI === 'true' || process.env.GITHUB_ACTIONS === 'true';
522
+
508
523
  const options = {
509
524
  dryRun: args.includes('--dry-run'),
510
- validateOnly: args.includes('--validate-only'),
525
+ validateOnly: args.includes('--validate-only') || isCI,
511
526
  versionBump: 'patch'
512
527
  };
513
528
 
529
+ if (isCI && !args.includes('--validate-only')) {
530
+ console.log(chalk.blue(
531
+ '[CI] Auto-enabling --validate-only: ' +
532
+ 'self-install validation is not possible during npm publish ' +
533
+ '(package is not yet on the registry).'
534
+ ));
535
+ }
536
+
514
537
  if (args.includes('major')) options.versionBump = 'major';
515
538
  if (args.includes('minor')) options.versionBump = 'minor';
516
539
  if (args.includes('patch')) options.versionBump = 'patch';
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: gaia
3
+ description: Invoke the Gaia meta-agent for system architecture analysis, agent design, skill creation, and orchestration debugging
4
+ allowed-tools:
5
+ - Bash(*)
6
+ - Read
7
+ - Edit
8
+ - Write
9
+ - Glob
10
+ - Grep
11
+ - WebSearch
12
+ - WebFetch
13
+ - Task
14
+ - Agent
15
+ - Skill
16
+ ---
17
+
18
+ Invoke the Gaia meta-agent (`gaia-system`) to work on the gaia-ops orchestration
19
+ system itself. This is the entry point for tasks that modify or analyze agents,
20
+ skills, hooks, or system architecture.
21
+
22
+ ## When to use
23
+
24
+ - Analyze or improve the gaia-ops architecture
25
+ - Create or update agent definitions (`.md` files)
26
+ - Create or update skills (`SKILL.md` files)
27
+ - Write or debug Python hooks and tools
28
+ - Update `CLAUDE.md` or system configuration
29
+ - Research best practices for agent orchestration
30
+
31
+ ## How it works
32
+
33
+ This command delegates to the `gaia-system` agent, which is the meta-agent
34
+ specialized in the orchestration system. It follows the standard agent protocol
35
+ and returns a `json:contract` block with findings and status.
36
+
37
+ $ARGUMENTS
package/config/README.md CHANGED
@@ -6,11 +6,9 @@ Central configuration for the orchestration system. Contracts are the SSOT for a
6
6
 
7
7
  | File | Purpose | Read by |
8
8
  |------|---------|---------|
9
- | `context-contracts.json` | Base cloud-agnostic contracts: `read`/`write` sections per agent | `context_provider.py`, `context_writer.py`, `pre_tool_use.py` |
9
+ | `context-contracts.json` | Base cloud-agnostic contracts: `read`/`write` sections per agent, `core_sections` list, `workspace_repos` schema | `context_provider.py`, `context_writer.py`, `pre_tool_use.py` |
10
10
  | `cloud/gcp.json` | GCP extensions: `gcp_services`, `workload_identity`, `static_ips` | Same trio, merged at runtime |
11
11
  | `cloud/aws.json` | AWS extensions: `vpc_mapping`, `load_balancers`, `api_gateway`, `irsa_bindings`, `aws_accounts` | Same trio, merged at runtime |
12
- | `context-contracts.gcp.json` | **Legacy** -- kept for backward compatibility | Fallback if `context-contracts.json` not found |
13
- | `context-contracts.aws.json` | **Legacy** -- kept for backward compatibility | Fallback if `context-contracts.json` not found |
14
12
  | `git_standards.json` | Commit standards (Conventional Commits), allowed types, forbidden footers | `hooks/modules/validation/commit_validator.py` |
15
13
  | `universal-rules.json` | Behavior rules injected into all agents | `context_provider.py` |
16
14
  | `surface-routing.json` | Generic surface classification and investigation-brief rules | `surface_router.py`, `context_provider.py`, Spec-Kit |
@@ -27,18 +25,14 @@ At runtime, `tools/context/context_provider.py` executes the following logic:
27
25
  5. Result: complete contract for the agent on that cloud
28
26
  ```
29
27
 
30
- Fallback if `context-contracts.json` not found: uses `context-contracts.{provider}.json` (legacy).
31
-
32
28
  ## Structure
33
29
 
34
30
  ```
35
31
  config/
36
- ├── context-contracts.json <- agnostic base (all agents)
32
+ ├── context-contracts.json <- agnostic base (all agents, v4)
37
33
  ├── cloud/
38
34
  │ ├── gcp.json <- GCP extensions + section_schemas
39
35
  │ └── aws.json <- AWS extensions + section_schemas
40
- ├── context-contracts.gcp.json <- legacy (fallback)
41
- ├── context-contracts.aws.json <- legacy (fallback)
42
36
  ├── surface-routing.json <- generic surface routing + investigation brief config
43
37
  ├── git_standards.json
44
38
  ├── universal-rules.json
@@ -61,4 +55,4 @@ config/
61
55
 
62
56
  ---
63
57
 
64
- **Updated:** 2026-03-03 | **Active contracts:** base + 2 clouds (GCP, AWS)
58
+ **Updated:** 2026-03-25 | **Active contracts:** base v4 + 2 clouds (GCP, AWS)