@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
@@ -18,8 +18,6 @@ sys.path.insert(0, str(Path(__file__).parent))
18
18
 
19
19
  from modules.core.paths import get_logs_dir
20
20
  from adapters.claude_code import ClaudeCodeAdapter
21
- from modules.core.stdin import has_stdin_data
22
- from adapters.utils import warn_if_dual_channel
23
21
  from modules.core.hook_entry import run_hook
24
22
 
25
23
  # Configure logging
@@ -48,79 +46,9 @@ def _handle_post_tool_use(event) -> None:
48
46
  sys.exit(response.exit_code)
49
47
 
50
48
 
51
- # ============================================================================
52
- # Backward-compatible API for direct callers (e.g. --test mode)
53
- # ============================================================================
54
-
55
- def post_tool_use_hook(
56
- tool_name: str,
57
- parameters: dict,
58
- result: object,
59
- duration: float,
60
- success: bool = True,
61
- ) -> None:
62
- """Post-tool use hook: audit + event detection + context update.
63
-
64
- Backward-compatible entry point that calls the same modules as the adapter.
65
- """
66
- import time
67
- from modules.core.state import get_hook_state, clear_hook_state
68
- from modules.audit.logger import log_execution
69
- from modules.audit.event_detector import detect_critical_event
70
- from modules.session.session_context_writer import SessionContextWriter
71
- from modules.security.approval_grants import check_approval_grant, confirm_grant
72
-
73
- try:
74
- pre_state = get_hook_state()
75
- tier = pre_state.tier if pre_state else "unknown"
76
-
77
- computed_duration = duration
78
- if pre_state and pre_state.start_time_epoch > 0:
79
- computed_duration = time.time() - pre_state.start_time_epoch
80
-
81
- log_execution(
82
- tool_name=tool_name,
83
- parameters=parameters,
84
- result=result,
85
- duration=computed_duration,
86
- exit_code=0 if success else 1,
87
- tier=tier,
88
- )
89
-
90
- if tool_name == "Bash" and success:
91
- command = parameters.get("command", "")
92
- if command:
93
- grant = check_approval_grant(command)
94
- if grant is not None and not grant.confirmed:
95
- confirm_grant(command)
96
- logger.info(
97
- "T3 grant confirmed post-execution: %s", command[:80],
98
- )
99
-
100
- events = detect_critical_event(tool_name, parameters, result, success)
101
- if events:
102
- writer = SessionContextWriter()
103
- for event in events:
104
- writer.update_context(event.to_dict())
105
-
106
- clear_hook_state()
107
- logger.debug("Post-hook completed for %s", tool_name)
108
-
109
- except Exception as e:
110
- logger.error("Error in post_tool_use_hook: %s", e, exc_info=True)
111
-
112
-
113
49
  # ============================================================================
114
50
  # STDIN HANDLER (Claude Code integration)
115
51
  # ============================================================================
116
52
 
117
53
  if __name__ == "__main__":
118
- if len(sys.argv) > 1 and sys.argv[1] == "--test":
119
- post_tool_use_hook(
120
- "bash", {"command": "kubectl get pods"},
121
- "pod/test-pod 1/1 Running 0 1m", 0.5, True,
122
- )
123
- print(f"Test completed. Check {get_logs_dir()} for audit logs")
124
- sys.exit(0)
125
-
126
54
  run_hook(_handle_post_tool_use, hook_name="post_tool_use")
@@ -11,6 +11,7 @@ Architecture:
11
11
  - All business logic lives in ClaudeCodeAdapter.adapt_pre_tool_use()
12
12
  - This file is stdin/stdout glue only
13
13
  """
14
+ from __future__ import annotations
14
15
 
15
16
  import sys
16
17
  import json
@@ -47,20 +48,10 @@ logger = logging.getLogger(__name__)
47
48
  from modules.tools.bash_validator import BashValidator
48
49
  from modules.tools.task_validator import TaskValidator, AVAILABLE_AGENTS, META_AGENTS
49
50
  from modules.security.prompt_validator import classify_resume_prompt
50
- from modules.context.context_injector import inject_project_context, build_project_context
51
- from modules.session.session_event_injector import inject_session_events, build_session_events
51
+ from modules.context.context_injector import build_project_context
52
+ from modules.session.session_event_injector import build_session_events
52
53
  from modules.core.state import create_pre_hook_state, save_hook_state
53
- from modules.security.approval_constants import (
54
- NONCE_APPROVAL_PREFIX,
55
- NONCE_APPROVAL_PATTERN,
56
- )
57
- from modules.security.approval_messages import (
58
- build_activation_failed_message,
59
- build_deprecated_approval_message,
60
- build_invalid_nonce_message,
61
- )
62
54
  from modules.security.approval_grants import (
63
- activate_pending_approval,
64
55
  cleanup_expired_grants,
65
56
  )
66
57
 
@@ -74,16 +65,6 @@ def _classify_resume_prompt(prompt: str) -> str:
74
65
  return classify_resume_prompt(prompt)
75
66
 
76
67
 
77
- def _inject_project_context(parameters: dict) -> dict:
78
- """Inject project context. Delegates to modules.context.context_injector."""
79
- return inject_project_context(parameters, PROJECT_AGENTS, _HOOKS_DIR)
80
-
81
-
82
- def _inject_session_events(parameters: dict) -> dict:
83
- """Inject session events. Delegates to modules.session.session_event_injector."""
84
- return inject_session_events(parameters, PROJECT_AGENTS)
85
-
86
-
87
68
  def pre_tool_use_hook(tool_name: str, parameters: dict) -> str | dict | None:
88
69
  """
89
70
  Pre-tool use hook implementation (backward-compatible API).
@@ -137,24 +118,13 @@ def _handle_bash(tool_name: str, parameters: dict) -> str | dict | None:
137
118
  result = validator.validate(command)
138
119
 
139
120
  if not result.allowed:
140
- from modules.core.plugin_mode import is_ops_mode
141
121
  logger.warning(f"BLOCKED: {command[:100]} - {result.reason}")
142
122
 
143
- # Security mode: delegate to native Claude Code approval
144
- if not is_ops_mode():
145
- reason_line = result.reason.split('\n')[0] if result.reason else f"T3 operation: {command[:80]}"
146
- logger.info(f"SECURITY MODE: returning 'ask' for T3: {command[:80]}")
147
- return {
148
- "hookSpecificOutput": {
149
- "hookEventName": "PreToolUse",
150
- "permissionDecision": "ask",
151
- "permissionDecisionReason": f"[{result.tier}] {reason_line}",
152
- }
153
- }
154
-
155
- # Ops mode: block with nonce
123
+ # Structured response from bash_validator (ask or deny)
156
124
  if result.block_response is not None:
157
125
  return result.block_response
126
+
127
+ # Permanently blocked (no structured response) — hard block (exit 2)
158
128
  return _format_blocked_message(result)
159
129
 
160
130
  effective_command = result.modified_input.get("command", command) if result.modified_input else command
@@ -185,8 +155,9 @@ def _handle_task(tool_name: str, parameters: dict) -> str | dict | None:
185
155
  """
186
156
  Handle Task/Agent tool validation for new task dispatches.
187
157
 
188
- Uses additionalContext (Phase 2) instead of prompt mutation.
189
- Validation runs against the original prompt, eliminating T3 false positives.
158
+ Context is built here and cached for SubagentStart to forward to the
159
+ subagent. PreToolUse no longer returns additionalContext (that would
160
+ inject it into the orchestrator, not the subagent).
190
161
  """
191
162
  context_text, _telemetry = build_project_context(parameters, PROJECT_AGENTS, _HOOKS_DIR)
192
163
  events_text = build_session_events(parameters, PROJECT_AGENTS)
@@ -210,17 +181,31 @@ def _handle_task(tool_name: str, parameters: dict) -> str | dict | None:
210
181
 
211
182
  logger.info(f"ALLOWED Task: {result.agent_name}")
212
183
 
184
+ # Cache context for SubagentStart to pick up and forward to the subagent.
213
185
  additional = "\n".join(filter(None, [context_text, events_text]))
186
+
187
+ # Fallback: if build_project_context returned None because the
188
+ # orchestrator already embedded context in the prompt (dedup guard),
189
+ # extract the embedded context so SubagentStart can still inject it.
190
+ if not additional:
191
+ prompt = parameters.get("prompt", "")
192
+ marker = "# Project Context"
193
+ if marker in prompt:
194
+ idx = prompt.index(marker)
195
+ additional = prompt[idx:]
196
+ logger.info(
197
+ "Extracted embedded context from prompt for caching "
198
+ "(len=%d, agent=%s)",
199
+ len(additional), result.agent_name,
200
+ )
201
+
214
202
  if additional:
215
- logger.info(f"Returning additionalContext for {result.agent_name} (context injected)")
216
- return {
217
- "hookSpecificOutput": {
218
- "hookEventName": "PreToolUse",
219
- "permissionDecision": "allow",
220
- "permissionDecisionReason": f"Context injected for {result.agent_name}",
221
- "additionalContext": additional,
222
- }
223
- }
203
+ from adapters.claude_code import ClaudeCodeAdapter
204
+ adapter = ClaudeCodeAdapter()
205
+ session_id = parameters.get("session_id", "") or "unknown"
206
+ agent_type = result.agent_name or "unknown"
207
+ adapter._cache_context_for_subagent(session_id, agent_type, additional)
208
+ logger.info(f"Cached context for SubagentStart: agent={agent_type}")
224
209
 
225
210
  return None
226
211
 
@@ -229,8 +214,9 @@ def _handle_send_message(tool_name: str, parameters: dict) -> str | None:
229
214
  """
230
215
  Handle SendMessage tool validation for agent resumption.
231
216
 
232
- Validates agent ID format and message content, then runs nonce
233
- approval checks. Does NOT inject project context (it's a resume).
217
+ Validates agent ID format and message content. Does NOT inject
218
+ project context (it's a resume). Nonce relay is no longer processed
219
+ here -- approval grants are activated by the UserPromptSubmit hook.
234
220
 
235
221
  Returns:
236
222
  None: allowed (no modification)
@@ -265,17 +251,13 @@ def _handle_send_message(tool_name: str, parameters: dict) -> str | None:
265
251
 
266
252
  logger.info(f"SENDMESSAGE: Resuming agent {agent_id}")
267
253
 
268
- approval_error, has_approval = _handle_resume_approval(agent_id, message)
269
- if approval_error:
270
- return approval_error
271
-
272
254
  state = create_pre_hook_state(
273
255
  tool_name=tool_name,
274
256
  command=f"SendMessage:{agent_id}",
275
257
  tier="T0",
276
258
  allowed=True,
277
259
  is_t3=False,
278
- has_approval=has_approval,
260
+ has_approval=False,
279
261
  )
280
262
  save_hook_state(state)
281
263
 
@@ -283,53 +265,6 @@ def _handle_send_message(tool_name: str, parameters: dict) -> str | None:
283
265
  return None
284
266
 
285
267
 
286
- def _handle_resume_approval(resume_id: str, prompt: str) -> tuple[str | None, bool]:
287
- """Process nonce approval indicators for Task resume."""
288
- classification = _classify_resume_prompt(prompt)
289
-
290
- if classification == "nonce":
291
- nonce = NONCE_APPROVAL_PATTERN.search(prompt).group(1)
292
- activation = activate_pending_approval(nonce)
293
- status_text = getattr(activation.status, "value", str(activation.status))
294
- if activation.success:
295
- grant_path = activation.grant_path
296
- grant_name = grant_path.name if grant_path else "<unknown>"
297
- logger.info(
298
- "Nonce approval activated for resume %s: nonce=%s, file=%s",
299
- resume_id,
300
- nonce,
301
- grant_name,
302
- )
303
- return None, True
304
-
305
- logger.warning(
306
- "Denied resume %s: nonce approval activation failed for nonce=%s "
307
- "(status=%s, reason=%s)",
308
- resume_id,
309
- nonce,
310
- status_text,
311
- activation.reason,
312
- )
313
- return build_activation_failed_message(nonce, status_text, activation.reason), False
314
-
315
- if classification == "malformed_nonce":
316
- logger.warning(
317
- "Denied resume %s: malformed nonce approval token in prompt='%s'",
318
- resume_id,
319
- prompt[:120],
320
- )
321
- return build_invalid_nonce_message(), False
322
-
323
- if classification == "deprecated":
324
- logger.warning(
325
- "Denied resume %s: deprecated legacy approval phrase detected",
326
- resume_id,
327
- )
328
- return build_deprecated_approval_message(), False
329
-
330
- return None, False
331
-
332
-
333
268
  def _format_blocked_message(result) -> str:
334
269
  """Format blocked command message. Delegates to blocked_message_formatter."""
335
270
  from modules.security.blocked_message_formatter import format_blocked_message
@@ -413,10 +348,15 @@ if __name__ == "__main__":
413
348
 
414
349
  if isinstance(response.output, dict) and response.output:
415
350
  hook_output = response.output.get("hookSpecificOutput", {})
416
- if hook_output.get("permissionDecision") in ("block", "deny"):
351
+ decision = hook_output.get("permissionDecision")
352
+ if decision in ("block", "deny"):
417
353
  reason = hook_output.get("permissionDecisionReason", "Command blocked by hook policy")
418
354
  summary = reason.split('\n')[0]
419
355
  print(f"BLOCKED: {summary}", file=sys.stderr)
356
+ elif decision == "ask":
357
+ reason = hook_output.get("permissionDecisionReason", "")
358
+ summary = reason.split('\n')[0]
359
+ print(f"T3: {summary}", file=sys.stderr)
420
360
  print(json.dumps(response.output))
421
361
  sys.exit(response.exit_code)
422
362
  elif isinstance(response.output, str) and response.output:
@@ -31,8 +31,10 @@ if __name__ == "__main__":
31
31
  try:
32
32
  sys.stdin.read()
33
33
 
34
- # First-time setup: create project permissions if needed
35
- setup_message = run_first_time_setup()
34
+ # First-time setup: create project permissions if needed.
35
+ # mark_done=False so UserPromptSubmit can detect first-run
36
+ # and show the welcome message before marking initialized.
37
+ setup_message = run_first_time_setup(mark_done=False)
36
38
  if setup_message:
37
39
  logger.info("First-time setup: %s", setup_message)
38
40
 
@@ -1,6 +1,10 @@
1
1
  #!/usr/bin/env python3
2
- """SubagentStart hook — logs agent dispatch and records skill snapshots.
3
- Context injection is handled exclusively by PreToolUse (Agent matcher)."""
2
+ """SubagentStart hook — logs agent dispatch, records skill snapshots,
3
+ and forwards cached project context into the subagent.
4
+
5
+ PreToolUse:Agent builds and caches the context; this hook reads the
6
+ cache and returns it as additionalContext so it reaches the subagent
7
+ (not the orchestrator)."""
4
8
 
5
9
  import sys
6
10
  import json
@@ -32,8 +32,6 @@ sys.path.insert(0, str(Path(__file__).parent))
32
32
 
33
33
  # Adapter layer
34
34
  from adapters.claude_code import ClaudeCodeAdapter
35
- from modules.core.stdin import has_stdin_data
36
- from adapters.utils import warn_if_dual_channel
37
35
  from modules.core.hook_entry import run_hook
38
36
 
39
37
  # Configure structured logging with file handler
@@ -62,7 +60,6 @@ logger = logging.getLogger(__name__)
62
60
  from modules.agents.contract_validator import (
63
61
  extract_commands_from_evidence,
64
62
  extract_exit_code_from_output,
65
- extract_plan_status_from_output,
66
63
  parse_contract,
67
64
  requires_consolidation_report,
68
65
  validate as validate_contract,
@@ -75,7 +72,7 @@ from modules.agents.response_contract import (
75
72
  from modules.agents.task_info_builder import build_task_info_from_hook_data
76
73
  from modules.agents.transcript_reader import read_transcript
77
74
  from modules.audit.workflow_auditor import audit as audit_workflow, signal_gaia_analysis
78
- from modules.audit.workflow_recorder import record as record_workflow, get_workflow_memory_dir
75
+ from modules.audit.workflow_recorder import record as record_workflow
79
76
  from modules.context.context_writer import process_context_updates
80
77
  from modules.memory.episode_writer import write as write_episode
81
78
  from modules.security.approval_cleanup import cleanup as cleanup_approval
@@ -83,18 +80,9 @@ from modules.session.session_manager import get_or_create_session_id
83
80
 
84
81
  _extract_commands_from_evidence = extract_commands_from_evidence
85
82
  _extract_exit_code_from_output = extract_exit_code_from_output
86
- _extract_plan_status_from_output = extract_plan_status_from_output
87
- _requires_consolidation_report = requires_consolidation_report
88
83
  _read_transcript = read_transcript
89
- _consume_approval_file = cleanup_approval
90
84
  _process_context_updates = process_context_updates
91
85
 
92
- # Backward-compatible aliases for old function names used in tests
93
- capture_episodic_memory = write_episode
94
- detect_anomalies = audit_workflow
95
- capture_workflow_metrics = record_workflow
96
- consume_approval_file = cleanup_approval
97
-
98
86
 
99
87
  def _build_task_info_from_hook_data(
100
88
  hook_data: Dict[str, Any],
@@ -24,39 +24,101 @@ logging.basicConfig(
24
24
  logger = logging.getLogger(__name__)
25
25
 
26
26
 
27
+ def _extract_user_prompt(raw_input: str) -> str:
28
+ """Extract user prompt text from stdin event.
29
+
30
+ The UserPromptSubmit event is JSON with the user's message.
31
+ Try known field names; return empty string if extraction fails.
32
+ """
33
+ try:
34
+ event = json.loads(raw_input)
35
+ # Try known field names from Claude Code hook events
36
+ for field in ("user_message", "prompt", "message", "content"):
37
+ if field in event and isinstance(event[field], str):
38
+ return event[field]
39
+ # Check nested hookEventInput
40
+ hook_input = event.get("hookEventInput", {})
41
+ if isinstance(hook_input, dict):
42
+ for field in ("user_message", "prompt", "message", "content"):
43
+ if field in hook_input and isinstance(hook_input[field], str):
44
+ return hook_input[field]
45
+ except (json.JSONDecodeError, TypeError, AttributeError):
46
+ pass
47
+ return ""
48
+
49
+
50
+ def _build_routing_recommendation(prompt_text: str) -> str:
51
+ """Run surface classification and format as a routing recommendation block.
52
+
53
+ Returns empty string if classification fails or produces no active surfaces.
54
+ This is advisory — never raises exceptions.
55
+ """
56
+ try:
57
+ # Import surface_router from tools/context
58
+ tools_dir = Path(__file__).resolve().parent.parent / "tools" / "context"
59
+ if str(tools_dir) not in sys.path:
60
+ sys.path.insert(0, str(tools_dir))
61
+
62
+ from surface_router import classify_surfaces
63
+
64
+ routing = classify_surfaces(prompt_text)
65
+
66
+ active_surfaces = routing.get("active_surfaces", [])
67
+ if not active_surfaces:
68
+ logger.info("Surface routing: no active surfaces for prompt")
69
+ return ""
70
+
71
+ agents = routing.get("recommended_agents", [])
72
+ dispatch_mode = routing.get("dispatch_mode", "single_surface")
73
+ confidence = routing.get("confidence", 0.0)
74
+ matched_signals = routing.get("matched_signals", {})
75
+
76
+ # Flatten matched signals into a single list for display
77
+ all_signals = []
78
+ for surface_signals in matched_signals.values():
79
+ all_signals.extend(surface_signals)
80
+
81
+ lines = [
82
+ "\n\n## Surface Routing Recommendation",
83
+ f"- Recommended agents: {agents}",
84
+ f"- Dispatch mode: {dispatch_mode}",
85
+ f"- Confidence: {confidence}",
86
+ f"- Matched signals: {json.dumps(all_signals)}",
87
+ ]
88
+
89
+ logger.info(
90
+ "Surface routing: agents=%s mode=%s confidence=%.2f signals=%s",
91
+ agents, dispatch_mode, confidence, all_signals,
92
+ )
93
+ return "\n".join(lines)
94
+
95
+ except Exception as e:
96
+ logger.warning("Surface routing failed (advisory, skipping): %s", e)
97
+ return ""
98
+
99
+
27
100
  def _build_welcome(mode: str) -> str:
28
- """Build first-run welcome message for the user."""
101
+ """Build first-run welcome message for the user.
102
+
103
+ This message is injected as additionalContext so Claude receives it,
104
+ not the user directly. The framing explains WHY the user needs to
105
+ know — Claude should naturally relay it because it is actionable
106
+ and the user cannot use the plugin fully without restarting.
107
+ """
29
108
  plugin_name = f"GAIA {'Ops' if mode == 'ops' else 'Security'}"
30
- repo_url = "https://github.com/metraton/gaia-ops"
31
- tiers_info = (
32
- "Security tiers: T0 (read-only, auto-allowed) → T1 (validation) → "
33
- "T2 (simulation) → T3 (state change, requires approval) → "
34
- "T3-blocked (irreversible, permanently blocked)."
35
- )
36
- learn_more = (
37
- f"To learn more about security tiers, ask: 'explain the security tiers'. "
38
- f"Docs and source: {repo_url}"
39
- )
40
109
 
41
- if mode == "ops":
42
- return (
43
- f"FIRST-TIME SETUP: {plugin_name} is now active. "
44
- "Your permissions have been configured to allow GAIA to manage "
45
- "security classification and agent dispatch. "
46
- "To activate these permissions, close this session and reopen Claude Code. "
47
- "After that, GAIA will handle command classification automatically. "
48
- f"{tiers_info} "
49
- f"{learn_more} "
50
- "You must tell the user this message exactly."
51
- )
52
110
  return (
53
- f"FIRST-TIME SETUP: {plugin_name} is now active. "
54
- "Your permissions have been configured to delegate security decisions to GAIA. "
55
- "To activate these permissions, close this session and reopen Claude Code. "
56
- "After that, GAIA Security will classify every command by risk automatically. "
57
- f"{tiers_info} "
58
- f"{learn_more} "
59
- "You must tell the user this message exactly."
111
+ f"{plugin_name} was just installed for the first time in this project. "
112
+ f"The plugin detected the installation and configured security permissions, "
113
+ f"but these permissions only take effect after restarting Claude Code. "
114
+ f"Until the user closes this session and reopens Claude Code, "
115
+ f"{plugin_name} cannot protect their commands — destructive operations "
116
+ f"will not be blocked and mutative operations will not require approval. "
117
+ f"The user needs to know this because they just installed {plugin_name} "
118
+ f"expecting it to work, but it will not be active until they restart. "
119
+ f"Tell the user that {plugin_name} is installed, that they need to close "
120
+ f"and reopen Claude Code to activate it, and that until they do, "
121
+ f"security protections are not active."
60
122
  )
61
123
 
62
124
 
@@ -65,20 +127,40 @@ if __name__ == "__main__":
65
127
  sys.exit(0)
66
128
 
67
129
  try:
68
- sys.stdin.read()
69
- from modules.core.plugin_setup import is_first_run
130
+ raw_input = sys.stdin.read()
131
+
132
+ # Check first-run BEFORE setup (SessionStart does setup with
133
+ # mark_done=False so the marker doesn't exist yet on first run).
134
+ from modules.core.plugin_setup import is_first_run, mark_initialized
70
135
  first_run = is_first_run()
71
136
 
72
- # Ensure registry + permissions exist
73
- setup_msg = run_first_time_setup()
137
+ # Ensure registry + permissions exist (idempotent, no mark).
138
+ setup_msg = run_first_time_setup(mark_done=False)
74
139
  identity = build_identity()
75
140
  mode = "ops" if "Orchestrator" in identity else "security"
76
141
 
77
- # First-time welcome: tell user what happened and what to do
78
- if first_run and setup_msg:
142
+ # First-time welcome: the marker does not exist yet because
143
+ # neither SessionStart nor this call marked it.
144
+ if first_run:
79
145
  welcome = _build_welcome(mode)
80
- identity += f"\n\n{welcome}"
81
- logger.info("First-run welcome appended for %s mode", mode)
146
+ identity = f"{welcome}\n\n{identity}"
147
+ mark_initialized() # Mark AFTER building the welcome
148
+ logger.info("First-run welcome prepended for %s mode", mode)
149
+
150
+ # Append deterministic surface routing recommendation (ops mode only)
151
+ if mode == "ops":
152
+ prompt_text = _extract_user_prompt(raw_input)
153
+
154
+ # NOTE: Approval activation moved to ElicitationResult hook.
155
+ # AskUserQuestion responses trigger ElicitationResult, not
156
+ # UserPromptSubmit, so approval detection lives there now.
157
+
158
+ if prompt_text:
159
+ routing_block = _build_routing_recommendation(prompt_text)
160
+ if routing_block:
161
+ identity = identity + routing_block
162
+ else:
163
+ logger.info("Could not extract user prompt from stdin, skipping routing")
82
164
 
83
165
  logger.info("Identity injected: %s mode (%d chars)", mode, len(identity))
84
166
 
package/index.js CHANGED
@@ -56,7 +56,7 @@ export function getCommandPath(commandName) {
56
56
 
57
57
  /**
58
58
  * Get absolute path to a template
59
- * @param {string} templateName - Name of the template (e.g., 'settings.template.json')
59
+ * @param {string} templateName - Name of the template (e.g., 'governance.template.md')
60
60
  * @returns {string} Absolute path to template file
61
61
  */
62
62
  export function getTemplatePath(templateName) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jaguilar87/gaia-ops",
3
- "version": "4.4.0",
3
+ "version": "4.7.2",
4
4
  "description": "Multi-agent orchestration system for Claude Code - DevOps automation toolkit",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -53,6 +53,8 @@
53
53
  "config/",
54
54
  "speckit/",
55
55
  "skills/",
56
+ "dist/",
57
+ "git-hooks/",
56
58
  "README.md",
57
59
  "INSTALL.md",
58
60
  "CHANGELOG.md",
@@ -77,11 +79,11 @@
77
79
  "pre-publish:dry": "node bin/pre-publish-validate.js --dry-run",
78
80
  "pre-publish:validate": "node bin/pre-publish-validate.js --validate-only",
79
81
  "prepack": "npm run clean",
80
- "prepublishOnly": "npm run clean",
82
+ "prepublishOnly": "npm run build:plugins && node bin/pre-publish-validate.js",
81
83
  "postinstall": "node bin/gaia-update.js",
82
84
  "preuninstall": "node bin/gaia-cleanup.js"
83
85
  },
84
- "_postinstall_note": "postinstall: CLAUDE.md overwritten (static), settings.json merged (custom rules preserved)",
86
+ "_postinstall_note": "postinstall: settings.json replaced (hooks only), permissions merged into settings.local.json (union, user config preserved)",
85
87
  "dependencies": {
86
88
  "chalk": "^5.3.0",
87
89
  "ora": "^7.0.1",
package/skills/README.md CHANGED
@@ -8,7 +8,6 @@ Skills are knowledge modules that extend agent capabilities. They use Claude Cod
8
8
  .claude/skills/
9
9
  ├── agent-protocol/ # json:contract format, state machine, repair flow
10
10
  ├── agent-response/ # Orchestrator: interpret agent json:contract responses
11
- ├── project-dispatch/ # Orchestrator: agent table and dispatch rules
12
11
  ├── security-tiers/ # T0-T3 classification
13
12
  │ └── reference.md
14
13
  ├── investigation/ # Diagnosis methodology and pattern analysis
@@ -28,7 +27,7 @@ Skills are knowledge modules that extend agent capabilities. They use Claude Cod
28
27
  ├── fast-queries/ # Quick diagnostic scripts
29
28
  ├── speckit-workflow/ # Speckit phase management
30
29
  ├── specification/ # Feature specification workflow
31
- ├── orchestrator-approval/ # T3 nonce approval relay for orchestrator
30
+ ├── orchestrator-approval/ # T3 approval presentation for orchestrator
32
31
  ├── approval/ # T3 plan presentation and approval workflow
33
32
  │ └── examples.md
34
33
  ├── execution/ # Post-approval execution protocol
@@ -65,9 +64,8 @@ skills:
65
64
  | speckit-planner | agent-protocol, security-tiers, speckit-workflow | - |
66
65
 
67
66
  Orchestrator skills (loaded on-demand via Skill tool, not assigned to agents):
68
- - **project-dispatch** -- agent table and dispatch rules
69
67
  - **agent-response** -- contract status interpretation and presentation
70
- - **orchestrator-approval** -- T3 nonce approval relay
68
+ - **orchestrator-approval** -- T3 approval presentation and grant activation
71
69
 
72
70
  ## Skill Types
73
71
 
@@ -77,7 +75,7 @@ Orchestrator skills (loaded on-demand via Skill tool, not assigned to agents):
77
75
  | **Common** | Most agents via `skills:` | command-execution, context-updater |
78
76
  | **Domain** | Per-agent via `skills:` | terraform-patterns, gitops-patterns, developer-patterns, gaia-patterns |
79
77
  | **Workflow** | On-demand (agent reads file) | approval, execution, git-conventions |
80
- | **Orchestrator** | On-demand via Skill tool | project-dispatch, agent-response, orchestrator-approval |
78
+ | **Orchestrator** | On-demand via Skill tool | agent-response, orchestrator-approval |
81
79
 
82
80
  Workflow skills are loaded on-demand -- agents read them from disk when needed rather than receiving them at startup. Supporting files (`examples.md`, `reference.md`) are also read on-demand.
83
81