@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
@@ -7,13 +7,21 @@
7
7
  * Also available as: npx gaia-update
8
8
  *
9
9
  * Behavior:
10
- * - First-time install (.claude/ doesn't exist): skip silently (gaia-scan handles it)
10
+ * - First-time install (.claude/ doesn't exist):
11
+ * 1. Check Python 3 is available
12
+ * 2. Run gaia-scan --npm-postinstall to create .claude/, symlinks, settings, project-context
13
+ * 3. Create plugin-registry.json
14
+ * 4. Merge permissions into settings.local.json
15
+ * 5. Merge hooks into settings.local.json
16
+ * 6. Fall through to verification
11
17
  * - Update (.claude/ exists):
12
18
  * 1. Show version transition (previous → current)
13
- * 2. settings.json: REPLACE from template (template is source of truth)
14
- * 3. Symlinks: recreate if missing, fix broken ones
15
- * 4. Verify: hooks, python, project-context, config files
16
- * 5. Report: summary with any issues found
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
23
+ * 5. Verify: hooks, python, project-context, config files
24
+ * 6. Report: summary with any issues found
17
25
  *
18
26
  * Usage:
19
27
  * npm update @jaguilar87/gaia-ops # Automatic via postinstall
@@ -72,19 +80,25 @@ async function readPackageVersion(path) {
72
80
  // ============================================================================
73
81
 
74
82
  async function updateSettingsJson() {
75
- const spinner = ora('Updating settings.json...').start();
83
+ const spinner = ora('Checking settings.json...').start();
76
84
  try {
77
- const templatePath = join(__dirname, '../templates/settings.template.json');
78
85
  const settingsPath = join(CWD, '.claude', 'settings.json');
79
86
 
80
- if (!existsSync(templatePath) || !existsSync(join(CWD, '.claude'))) {
81
- 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)');
82
97
  return false;
83
98
  }
84
99
 
85
- // Always replace from template -- template is the source of truth
86
- await fs.copyFile(templatePath, settingsPath);
87
- spinner.succeed('settings.json updated from template');
100
+ await fs.writeFile(settingsPath, '{}\n');
101
+ spinner.succeed('settings.json created (minimal — hooks from hooks.json)');
88
102
  return true;
89
103
  } catch (error) {
90
104
  spinner.fail(`settings.json: ${error.message}`);
@@ -92,6 +106,223 @@ async function updateSettingsJson() {
92
106
  }
93
107
  }
94
108
 
109
+ async function updateLocalPermissions() {
110
+ const spinner = ora('Merging permissions into settings.local.json...').start();
111
+ try {
112
+ const claudeDir = join(CWD, '.claude');
113
+ const localPath = join(claudeDir, 'settings.local.json');
114
+
115
+ if (!existsSync(claudeDir)) {
116
+ spinner.info('Skipped (.claude/ not found)');
117
+ return false;
118
+ }
119
+
120
+ // Load permissions from plugin_setup.py — the single source of truth.
121
+ // We use ast.literal_eval to extract the constants without importing
122
+ // the module (which has relative imports that fail standalone).
123
+ let gaiaPerms;
124
+ try {
125
+ const setupPath = join(__dirname, '..', 'hooks', 'modules', 'core', 'plugin_setup.py');
126
+ const { stdout } = await execAsync(
127
+ `python3 -c "
128
+ import ast, json, re
129
+
130
+ source = open('${setupPath.replace(/'/g, "\\'")}').read()
131
+
132
+ # Extract _DENY_RULES list
133
+ deny_match = re.search(r'^_DENY_RULES\\s*=\\s*\\[', source, re.MULTILINE)
134
+ if deny_match:
135
+ bracket_start = deny_match.start() + source[deny_match.start():].index('[')
136
+ depth, i = 0, bracket_start
137
+ for i, ch in enumerate(source[bracket_start:], bracket_start):
138
+ if ch == '[': depth += 1
139
+ elif ch == ']': depth -= 1
140
+ if depth == 0: break
141
+ deny_rules = ast.literal_eval(source[bracket_start:i+1])
142
+ else:
143
+ deny_rules = []
144
+
145
+ # Extract OPS_PERMISSIONS allow list
146
+ ops_match = re.search(r'^OPS_PERMISSIONS\\s*=', source, re.MULTILINE)
147
+ if ops_match:
148
+ bracket_start = source.index('{', ops_match.start())
149
+ depth, i = 0, bracket_start
150
+ for i, ch in enumerate(source[bracket_start:], bracket_start):
151
+ if ch == '{': depth += 1
152
+ elif ch == '}': depth -= 1
153
+ if depth == 0: break
154
+ # Replace _DENY_RULES reference with actual list for eval
155
+ ops_str = source[bracket_start:i+1].replace('_DENY_RULES', json.dumps(deny_rules))
156
+ ops_perms = ast.literal_eval(ops_str)
157
+ else:
158
+ ops_perms = {'permissions': {'allow': [], 'deny': deny_rules, 'ask': []}}
159
+
160
+ print(json.dumps(ops_perms))
161
+ "`,
162
+ { timeout: 10000 }
163
+ );
164
+ gaiaPerms = JSON.parse(stdout.trim());
165
+ } catch (pyError) {
166
+ spinner.warn(`Could not load permissions from Python — ${pyError.message || 'unknown error'}`);
167
+ return false;
168
+ }
169
+
170
+ const ourAllow = new Set(gaiaPerms.permissions.allow || []);
171
+ const ourDeny = new Set(gaiaPerms.permissions.deny || []);
172
+
173
+ // Load existing settings.local.json — preserve everything (enabledPlugins, MCP servers, etc.)
174
+ let existing = {};
175
+ if (existsSync(localPath)) {
176
+ try {
177
+ existing = JSON.parse(await fs.readFile(localPath, 'utf-8'));
178
+ } catch {
179
+ existing = {};
180
+ }
181
+ }
182
+
183
+ const perms = existing.permissions || {};
184
+ const currentAllow = new Set(perms.allow || []);
185
+ const currentDeny = new Set(perms.deny || []);
186
+
187
+ // Union merge — add ours without removing user's
188
+ const mergedAllow = [...new Set([...currentAllow, ...ourAllow])].sort();
189
+ const mergedDeny = [...new Set([...currentDeny, ...ourDeny])].sort();
190
+
191
+ // Check if anything changed
192
+ const allowChanged = mergedAllow.length !== currentAllow.size
193
+ || mergedAllow.some(r => !currentAllow.has(r));
194
+ const denyChanged = mergedDeny.length !== currentDeny.size
195
+ || mergedDeny.some(r => !currentDeny.has(r));
196
+
197
+ if (!allowChanged && !denyChanged) {
198
+ spinner.succeed('settings.local.json permissions already up to date');
199
+ return false;
200
+ }
201
+
202
+ // Update only permissions, preserve everything else
203
+ existing.permissions = existing.permissions || {};
204
+ existing.permissions.allow = mergedAllow;
205
+ existing.permissions.deny = mergedDeny;
206
+ existing.permissions.ask = existing.permissions.ask || [];
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
+
214
+ await fs.writeFile(localPath, JSON.stringify(existing, null, 2) + '\n');
215
+ spinner.succeed('settings.local.json permissions and env merged');
216
+ return true;
217
+ } catch (error) {
218
+ spinner.fail(`settings.local.json: ${error.message}`);
219
+ return false;
220
+ }
221
+ }
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
+
95
326
  async function updateSymlinks() {
96
327
  const spinner = ora('Checking symlinks...').start();
97
328
  try {
@@ -206,7 +437,7 @@ async function runVerification() {
206
437
  }
207
438
 
208
439
  // 4. Config files accessible
209
- const configFiles = ['classification-rules.json', 'git_standards.json', 'universal-rules.json'];
440
+ const configFiles = ['git_standards.json', 'universal-rules.json', 'surface-routing.json'];
210
441
  for (const cfg of configFiles) {
211
442
  const path = join(CWD, '.claude', 'config', cfg);
212
443
  if (existsSync(path)) {
@@ -218,7 +449,7 @@ async function runVerification() {
218
449
  }
219
450
 
220
451
  // 5. Agent definitions accessible
221
- const agentFiles = ['terraform-architect.md', 'gitops-operator.md', 'cloud-troubleshooter.md', 'devops-developer.md', 'gaia.md'];
452
+ const agentFiles = ['terraform-architect.md', 'gitops-operator.md', 'cloud-troubleshooter.md', 'devops-developer.md', 'gaia-system.md', 'speckit-planner.md'];
222
453
  let agentsOk = 0;
223
454
  for (const agent of agentFiles) {
224
455
  if (existsSync(join(CWD, '.claude', 'agents', agent))) agentsOk++;
@@ -226,18 +457,21 @@ async function runVerification() {
226
457
  checks.push({ name: 'agent definitions', ok: agentsOk === agentFiles.length, detail: `${agentsOk}/${agentFiles.length}` });
227
458
  if (agentsOk < agentFiles.length) issues.push(`${agentFiles.length - agentsOk} agent definition(s) missing`);
228
459
 
229
- // 6. settings.json has hooks configured
230
- const settingsPath = join(CWD, '.claude', 'settings.json');
231
- 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)) {
232
463
  try {
233
- const settings = JSON.parse(await fs.readFile(settingsPath, 'utf-8'));
234
- const hasHooks = settings.hooks && Object.keys(settings.hooks).length > 0;
235
- checks.push({ name: 'hooks config', ok: hasHooks });
236
- 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');
237
468
  } catch {
238
- checks.push({ name: 'hooks config', ok: false });
239
- issues.push('settings.json is invalid');
469
+ checks.push({ name: 'hooks.json', ok: false });
470
+ issues.push('hooks.json is invalid');
240
471
  }
472
+ } else {
473
+ checks.push({ name: 'hooks.json', ok: false });
474
+ issues.push('hooks.json not found (hooks symlink may be broken)');
241
475
  }
242
476
 
243
477
  const passed = checks.filter(c => c.ok).length;
@@ -256,48 +490,106 @@ async function runVerification() {
256
490
  // Main
257
491
  // ============================================================================
258
492
 
493
+ async function runFreshInstall() {
494
+ const packageDir = join(__dirname, '..');
495
+ const scanScript = join(packageDir, 'bin', 'gaia-scan.py');
496
+ const { current } = await detectVersions();
497
+
498
+ console.log(chalk.cyan(`\n gaia-ops ${chalk.green(current)} — fresh install\n`));
499
+
500
+ // 1. Check Python 3 is available
501
+ const spinner = ora('Checking Python 3...').start();
502
+ try {
503
+ await execAsync('python3 --version', { timeout: 5000 });
504
+ spinner.succeed('Python 3 found');
505
+ } catch {
506
+ spinner.warn('Python 3 not found — skipping project setup');
507
+ console.log(chalk.gray(' Install Python 3.9+ and run: npx gaia-scan\n'));
508
+ return;
509
+ }
510
+
511
+ // 2. Run gaia-scan --npm-postinstall
512
+ const scanSpinner = ora('Running gaia-scan...').start();
513
+ try {
514
+ const { stdout, stderr } = await execAsync(
515
+ `python3 "${scanScript}" --npm-postinstall --root "${CWD}"`,
516
+ { timeout: 60000 }
517
+ );
518
+ scanSpinner.succeed('Project scanned and configured');
519
+ if (VERBOSE && stdout) console.log(chalk.gray(stdout));
520
+ if (VERBOSE && stderr) console.log(chalk.yellow(stderr));
521
+ } catch (error) {
522
+ scanSpinner.warn('gaia-scan encountered issues (non-fatal)');
523
+ if (VERBOSE && error.stderr) console.log(chalk.gray(error.stderr));
524
+ }
525
+
526
+ // 3. Create plugin-registry.json (in .claude/, same path Python hooks expect)
527
+ try {
528
+ const claudeDirPath = join(CWD, '.claude');
529
+ if (!existsSync(claudeDirPath)) {
530
+ await fs.mkdir(claudeDirPath, { recursive: true });
531
+ }
532
+ const registryPath = join(claudeDirPath, 'plugin-registry.json');
533
+ const registry = {
534
+ installed: [{ name: 'gaia-ops', version: current || 'unknown' }],
535
+ source: 'npm-postinstall',
536
+ };
537
+ await fs.writeFile(registryPath, JSON.stringify(registry, null, 2) + '\n');
538
+ } catch {
539
+ // Non-fatal — plugin-registry is a convenience, not critical
540
+ }
541
+
542
+ // 4. Merge permissions into settings.local.json (same approach as plugin mode)
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();
547
+ }
548
+
259
549
  async function main() {
260
550
  const claudeDir = join(CWD, '.claude');
261
551
  const isUpdate = existsSync(claudeDir);
262
552
 
263
553
  if (!isUpdate) {
264
- // First-time install — gaia-scan handles everything
265
- process.exit(0);
554
+ // First-time install — run gaia-scan to bootstrap everything
555
+ await runFreshInstall();
556
+ } else {
557
+ // Version info
558
+ const { previous, current } = await detectVersions();
559
+ const versionLine = previous && previous !== current
560
+ ? `${chalk.gray(previous)} → ${chalk.green(current)}`
561
+ : chalk.green(current);
562
+
563
+ console.log(chalk.cyan(`\n gaia-ops update ${versionLine}\n`));
564
+
565
+ // Step 1-4: Update files
566
+ await updateSettingsJson();
567
+ await updateLocalPermissions();
568
+ await updateLocalHooks();
569
+ await updateSymlinks();
266
570
  }
267
571
 
268
- // Version info
269
- const { previous, current } = await detectVersions();
270
- const versionLine = previous && previous !== current
271
- ? `${chalk.gray(previous)} → ${chalk.green(current)}`
272
- : chalk.green(current);
273
-
274
- console.log(chalk.cyan(`\n gaia-ops update ${versionLine}\n`));
275
-
276
- // Step 1-2: Update files
277
- const settingsUpdated = await updateSettingsJson();
278
- const { updated: symlinksUpdated, fixed: symlinksFix } = await updateSymlinks();
572
+ // Ensure plugin-registry.json exists in .claude/ (both fresh and update)
573
+ try {
574
+ const registryPath = join(CWD, '.claude', 'plugin-registry.json');
575
+ if (!existsSync(registryPath)) {
576
+ const { current } = await detectVersions();
577
+ const registry = {
578
+ installed: [{ name: 'gaia-ops', version: current || 'unknown' }],
579
+ source: 'npm-postinstall',
580
+ };
581
+ await fs.writeFile(registryPath, JSON.stringify(registry, null, 2) + '\n');
582
+ }
583
+ } catch { /* non-fatal */ }
279
584
 
280
- // Step 3: Verify
585
+ // Verify (runs for both fresh install and update)
281
586
  const { issues, passed, total } = await runVerification();
282
587
 
283
- // Summary
284
- const changes = [settingsUpdated, symlinksUpdated].filter(Boolean).length;
285
-
286
588
  console.log('');
287
- if (changes > 0 || issues.length > 0) {
288
- // Changes summary
289
- if (changes > 0) {
290
- console.log(chalk.green(` ${changes} file(s) updated`));
291
- if (settingsUpdated) console.log(chalk.gray(' settings.json: replaced from template'));
292
- if (symlinksFix > 0) console.log(chalk.gray(` ${symlinksFix} symlink(s) fixed`));
293
- }
294
-
295
- // Issues
296
- if (issues.length > 0) {
297
- console.log(chalk.yellow(`\n ${issues.length} issue(s) found:`));
298
- for (const issue of issues) {
299
- console.log(chalk.yellow(` - ${issue}`));
300
- }
589
+ if (issues.length > 0) {
590
+ console.log(chalk.yellow(` ${issues.length} issue(s) found:`));
591
+ for (const issue of issues) {
592
+ console.log(chalk.yellow(` - ${issue}`));
301
593
  }
302
594
  } else {
303
595
  console.log(chalk.green(' Everything up to date'));
@@ -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)