@jaguilar87/gaia 5.0.0-rc1

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 (609) hide show
  1. package/.claude-plugin/marketplace.json +33 -0
  2. package/.claude-plugin/plugin.json +26 -0
  3. package/ARCHITECTURE.md +335 -0
  4. package/CHANGELOG.md +1212 -0
  5. package/CODE_OF_CONDUCT.md +11 -0
  6. package/CONTRIBUTING.md +146 -0
  7. package/INSTALL.md +436 -0
  8. package/LICENSE +21 -0
  9. package/README.md +222 -0
  10. package/SECURITY.md +47 -0
  11. package/agents/README.md +78 -0
  12. package/agents/cloud-troubleshooter.md +73 -0
  13. package/agents/developer.md +65 -0
  14. package/agents/gaia-operator.md +64 -0
  15. package/agents/gaia-orchestrator.md +237 -0
  16. package/agents/gaia-planner.md +53 -0
  17. package/agents/gaia-system.md +70 -0
  18. package/agents/gitops-operator.md +61 -0
  19. package/agents/terraform-architect.md +63 -0
  20. package/bin/README.md +106 -0
  21. package/bin/cli/__init__.py +1 -0
  22. package/bin/cli/approvals.py +740 -0
  23. package/bin/cli/cleanup.py +562 -0
  24. package/bin/cli/context.py +283 -0
  25. package/bin/cli/doctor.py +628 -0
  26. package/bin/cli/history.py +305 -0
  27. package/bin/cli/memory.py +464 -0
  28. package/bin/cli/metrics.py +1068 -0
  29. package/bin/cli/plans.py +515 -0
  30. package/bin/cli/status.py +302 -0
  31. package/bin/cli/update.py +382 -0
  32. package/bin/gaia +112 -0
  33. package/bin/gaia-cleanup.js +531 -0
  34. package/bin/gaia-doctor.js +635 -0
  35. package/bin/gaia-evidence +126 -0
  36. package/bin/gaia-history.js +251 -0
  37. package/bin/gaia-metrics.js +1278 -0
  38. package/bin/gaia-review.js +269 -0
  39. package/bin/gaia-scan +44 -0
  40. package/bin/gaia-scan.py +589 -0
  41. package/bin/gaia-skills-diagnose.js +929 -0
  42. package/bin/gaia-status.js +278 -0
  43. package/bin/gaia-uninstall.js +111 -0
  44. package/bin/gaia-update.js +816 -0
  45. package/bin/pre-publish-validate.js +610 -0
  46. package/bin/python-detect.js +60 -0
  47. package/commands/README.md +64 -0
  48. package/commands/gaia.md +37 -0
  49. package/commands/scan-project.md +67 -0
  50. package/config/README.md +71 -0
  51. package/config/cloud/aws.json +134 -0
  52. package/config/cloud/gcp.json +139 -0
  53. package/config/context-contracts.json +158 -0
  54. package/config/crons-schema.md +81 -0
  55. package/config/git_standards.json +72 -0
  56. package/config/surface-routing.json +421 -0
  57. package/config/universal-rules.json +102 -0
  58. package/dist/gaia-ops/.claude-plugin/plugin.json +24 -0
  59. package/dist/gaia-ops/README.md +80 -0
  60. package/dist/gaia-ops/agents/cloud-troubleshooter.md +73 -0
  61. package/dist/gaia-ops/agents/developer.md +65 -0
  62. package/dist/gaia-ops/agents/gaia-operator.md +64 -0
  63. package/dist/gaia-ops/agents/gaia-orchestrator.md +237 -0
  64. package/dist/gaia-ops/agents/gaia-planner.md +53 -0
  65. package/dist/gaia-ops/agents/gaia-system.md +70 -0
  66. package/dist/gaia-ops/agents/gitops-operator.md +61 -0
  67. package/dist/gaia-ops/agents/terraform-architect.md +63 -0
  68. package/dist/gaia-ops/commands/gaia.md +37 -0
  69. package/dist/gaia-ops/config/README.md +71 -0
  70. package/dist/gaia-ops/config/cloud/aws.json +134 -0
  71. package/dist/gaia-ops/config/cloud/gcp.json +139 -0
  72. package/dist/gaia-ops/config/context-contracts.json +158 -0
  73. package/dist/gaia-ops/config/crons-schema.md +81 -0
  74. package/dist/gaia-ops/config/git_standards.json +72 -0
  75. package/dist/gaia-ops/config/surface-routing.json +421 -0
  76. package/dist/gaia-ops/config/universal-rules.json +102 -0
  77. package/dist/gaia-ops/hooks/adapters/__init__.py +52 -0
  78. package/dist/gaia-ops/hooks/adapters/base.py +219 -0
  79. package/dist/gaia-ops/hooks/adapters/channel.py +17 -0
  80. package/dist/gaia-ops/hooks/adapters/claude_code.py +1890 -0
  81. package/dist/gaia-ops/hooks/adapters/types.py +194 -0
  82. package/dist/gaia-ops/hooks/adapters/utils.py +25 -0
  83. package/dist/gaia-ops/hooks/hooks.json +163 -0
  84. package/dist/gaia-ops/hooks/modules/__init__.py +15 -0
  85. package/dist/gaia-ops/hooks/modules/agents/__init__.py +29 -0
  86. package/dist/gaia-ops/hooks/modules/agents/contract_validator.py +647 -0
  87. package/dist/gaia-ops/hooks/modules/agents/response_contract.py +496 -0
  88. package/dist/gaia-ops/hooks/modules/agents/skill_injection_verifier.py +120 -0
  89. package/dist/gaia-ops/hooks/modules/agents/state_tracker.py +267 -0
  90. package/dist/gaia-ops/hooks/modules/agents/task_info_builder.py +74 -0
  91. package/dist/gaia-ops/hooks/modules/agents/transcript_analyzer.py +458 -0
  92. package/dist/gaia-ops/hooks/modules/agents/transcript_reader.py +152 -0
  93. package/dist/gaia-ops/hooks/modules/audit/__init__.py +28 -0
  94. package/dist/gaia-ops/hooks/modules/audit/event_detector.py +168 -0
  95. package/dist/gaia-ops/hooks/modules/audit/logger.py +131 -0
  96. package/dist/gaia-ops/hooks/modules/audit/metrics.py +134 -0
  97. package/dist/gaia-ops/hooks/modules/audit/workflow_auditor.py +611 -0
  98. package/dist/gaia-ops/hooks/modules/audit/workflow_recorder.py +296 -0
  99. package/dist/gaia-ops/hooks/modules/context/__init__.py +11 -0
  100. package/dist/gaia-ops/hooks/modules/context/agentic_loop_detector.py +165 -0
  101. package/dist/gaia-ops/hooks/modules/context/anchor_tracker.py +317 -0
  102. package/dist/gaia-ops/hooks/modules/context/compact_context_builder.py +218 -0
  103. package/dist/gaia-ops/hooks/modules/context/context_freshness.py +145 -0
  104. package/dist/gaia-ops/hooks/modules/context/context_injector.py +558 -0
  105. package/dist/gaia-ops/hooks/modules/context/context_writer.py +530 -0
  106. package/dist/gaia-ops/hooks/modules/context/contracts_loader.py +161 -0
  107. package/dist/gaia-ops/hooks/modules/core/__init__.py +40 -0
  108. package/dist/gaia-ops/hooks/modules/core/hook_entry.py +78 -0
  109. package/dist/gaia-ops/hooks/modules/core/paths.py +160 -0
  110. package/dist/gaia-ops/hooks/modules/core/plugin_mode.py +149 -0
  111. package/dist/gaia-ops/hooks/modules/core/plugin_setup.py +577 -0
  112. package/dist/gaia-ops/hooks/modules/core/state.py +179 -0
  113. package/dist/gaia-ops/hooks/modules/core/stdin.py +24 -0
  114. package/dist/gaia-ops/hooks/modules/events/__init__.py +1 -0
  115. package/dist/gaia-ops/hooks/modules/events/event_writer.py +210 -0
  116. package/dist/gaia-ops/hooks/modules/memory/__init__.py +8 -0
  117. package/dist/gaia-ops/hooks/modules/memory/episode_writer.py +216 -0
  118. package/dist/gaia-ops/hooks/modules/orchestrator/__init__.py +1 -0
  119. package/dist/gaia-ops/hooks/modules/orchestrator/delegate_mode.py +122 -0
  120. package/dist/gaia-ops/hooks/modules/scanning/__init__.py +8 -0
  121. package/dist/gaia-ops/hooks/modules/scanning/scan_trigger.py +84 -0
  122. package/dist/gaia-ops/hooks/modules/security/__init__.py +120 -0
  123. package/dist/gaia-ops/hooks/modules/security/approval_cleanup.py +87 -0
  124. package/dist/gaia-ops/hooks/modules/security/approval_constants.py +23 -0
  125. package/dist/gaia-ops/hooks/modules/security/approval_grants.py +1638 -0
  126. package/dist/gaia-ops/hooks/modules/security/approval_messages.py +71 -0
  127. package/dist/gaia-ops/hooks/modules/security/approval_scopes.py +222 -0
  128. package/dist/gaia-ops/hooks/modules/security/blocked_commands.py +595 -0
  129. package/dist/gaia-ops/hooks/modules/security/blocked_message_formatter.py +87 -0
  130. package/dist/gaia-ops/hooks/modules/security/command_semantics.py +181 -0
  131. package/dist/gaia-ops/hooks/modules/security/composition_rules.py +547 -0
  132. package/dist/gaia-ops/hooks/modules/security/flag_classifiers.py +873 -0
  133. package/dist/gaia-ops/hooks/modules/security/gitops_validator.py +179 -0
  134. package/dist/gaia-ops/hooks/modules/security/mutative_verbs.py +1131 -0
  135. package/dist/gaia-ops/hooks/modules/security/network_hosts.py +481 -0
  136. package/dist/gaia-ops/hooks/modules/security/prompt_validator.py +40 -0
  137. package/dist/gaia-ops/hooks/modules/security/shell_unwrapper.py +165 -0
  138. package/dist/gaia-ops/hooks/modules/security/tiers.py +196 -0
  139. package/dist/gaia-ops/hooks/modules/session/__init__.py +10 -0
  140. package/dist/gaia-ops/hooks/modules/session/pending_scanner.py +174 -0
  141. package/dist/gaia-ops/hooks/modules/session/session_context_writer.py +100 -0
  142. package/dist/gaia-ops/hooks/modules/session/session_event_injector.py +160 -0
  143. package/dist/gaia-ops/hooks/modules/session/session_manager.py +31 -0
  144. package/dist/gaia-ops/hooks/modules/session/session_registry.py +232 -0
  145. package/dist/gaia-ops/hooks/modules/tools/__init__.py +29 -0
  146. package/dist/gaia-ops/hooks/modules/tools/bash_validator.py +1008 -0
  147. package/dist/gaia-ops/hooks/modules/tools/cloud_pipe_validator.py +231 -0
  148. package/dist/gaia-ops/hooks/modules/tools/hook_response.py +55 -0
  149. package/dist/gaia-ops/hooks/modules/tools/shell_parser.py +227 -0
  150. package/dist/gaia-ops/hooks/modules/tools/stage_decomposer.py +315 -0
  151. package/dist/gaia-ops/hooks/modules/tools/task_validator.py +294 -0
  152. package/dist/gaia-ops/hooks/modules/validation/__init__.py +23 -0
  153. package/dist/gaia-ops/hooks/modules/validation/commit_validator.py +380 -0
  154. package/dist/gaia-ops/hooks/post_compact.py +43 -0
  155. package/dist/gaia-ops/hooks/post_tool_use.py +54 -0
  156. package/dist/gaia-ops/hooks/pre_compact.py +60 -0
  157. package/dist/gaia-ops/hooks/pre_tool_use.py +413 -0
  158. package/dist/gaia-ops/hooks/session_start.py +81 -0
  159. package/dist/gaia-ops/hooks/stop_hook.py +82 -0
  160. package/dist/gaia-ops/hooks/subagent_start.py +71 -0
  161. package/dist/gaia-ops/hooks/subagent_stop.py +295 -0
  162. package/dist/gaia-ops/hooks/task_completed.py +70 -0
  163. package/dist/gaia-ops/hooks/user_prompt_submit.py +246 -0
  164. package/dist/gaia-ops/settings.json +72 -0
  165. package/dist/gaia-ops/skills/README.md +154 -0
  166. package/dist/gaia-ops/skills/agent-protocol/SKILL.md +93 -0
  167. package/dist/gaia-ops/skills/agent-protocol/examples.md +223 -0
  168. package/dist/gaia-ops/skills/agent-response/SKILL.md +69 -0
  169. package/dist/gaia-ops/skills/agentic-loop/SKILL.md +80 -0
  170. package/dist/gaia-ops/skills/agentic-loop/reference.md +378 -0
  171. package/dist/gaia-ops/skills/blog-writing/SKILL.md +98 -0
  172. package/dist/gaia-ops/skills/blog-writing/reference.md +130 -0
  173. package/dist/gaia-ops/skills/brief-spec/SKILL.md +182 -0
  174. package/dist/gaia-ops/skills/command-execution/SKILL.md +64 -0
  175. package/dist/gaia-ops/skills/command-execution/reference.md +83 -0
  176. package/dist/gaia-ops/skills/context-updater/SKILL.md +87 -0
  177. package/dist/gaia-ops/skills/context-updater/examples.md +71 -0
  178. package/dist/gaia-ops/skills/developer-patterns/SKILL.md +50 -0
  179. package/dist/gaia-ops/skills/developer-patterns/reference.md +112 -0
  180. package/dist/gaia-ops/skills/execution/SKILL.md +99 -0
  181. package/dist/gaia-ops/skills/fast-queries/SKILL.md +43 -0
  182. package/dist/gaia-ops/skills/gaia-compact/SKILL.md +74 -0
  183. package/dist/gaia-ops/skills/gaia-patterns/SKILL.md +108 -0
  184. package/dist/gaia-ops/skills/gaia-patterns/reference.md +395 -0
  185. package/dist/gaia-ops/skills/gaia-planner/SKILL.md +37 -0
  186. package/dist/gaia-ops/skills/gaia-planner/reference.md +107 -0
  187. package/dist/gaia-ops/skills/gaia-release/SKILL.md +82 -0
  188. package/dist/gaia-ops/skills/gaia-release/reference.md +102 -0
  189. package/dist/gaia-ops/skills/gaia-self-check/SKILL.md +114 -0
  190. package/dist/gaia-ops/skills/gaia-self-check/reference.md +453 -0
  191. package/dist/gaia-ops/skills/gaia-verify/SKILL.md +77 -0
  192. package/dist/gaia-ops/skills/gaia-verify/reference.md +80 -0
  193. package/dist/gaia-ops/skills/git-conventions/SKILL.md +47 -0
  194. package/dist/gaia-ops/skills/gitops-patterns/SKILL.md +60 -0
  195. package/dist/gaia-ops/skills/gitops-patterns/reference.md +183 -0
  196. package/dist/gaia-ops/skills/gmail-policy/SKILL.md +200 -0
  197. package/dist/gaia-ops/skills/gmail-policy/reference.md +150 -0
  198. package/dist/gaia-ops/skills/gmail-triage/SKILL.md +100 -0
  199. package/dist/gaia-ops/skills/gws-setup/SKILL.md +99 -0
  200. package/dist/gaia-ops/skills/gws-setup/reference.md +73 -0
  201. package/dist/gaia-ops/skills/investigation/SKILL.md +100 -0
  202. package/dist/gaia-ops/skills/memory-curation/SKILL.md +83 -0
  203. package/dist/gaia-ops/skills/memory-search/SKILL.md +88 -0
  204. package/dist/gaia-ops/skills/orchestrator-approval/SKILL.md +160 -0
  205. package/dist/gaia-ops/skills/orchestrator-approval/reference.md +174 -0
  206. package/dist/gaia-ops/skills/pending-approvals/SKILL.md +72 -0
  207. package/dist/gaia-ops/skills/pending-approvals/reference.md +214 -0
  208. package/dist/gaia-ops/skills/readme-writing/SKILL.md +71 -0
  209. package/dist/gaia-ops/skills/readme-writing/reference.md +188 -0
  210. package/dist/gaia-ops/skills/reference.md +135 -0
  211. package/dist/gaia-ops/skills/request-approval/SKILL.md +140 -0
  212. package/dist/gaia-ops/skills/request-approval/examples.md +140 -0
  213. package/dist/gaia-ops/skills/request-approval/reference.md +57 -0
  214. package/dist/gaia-ops/skills/schedule-task/SKILL.md +64 -0
  215. package/dist/gaia-ops/skills/schedule-task/reference.md +233 -0
  216. package/dist/gaia-ops/skills/security-tiers/SKILL.md +141 -0
  217. package/dist/gaia-ops/skills/security-tiers/destructive-commands-reference.md +623 -0
  218. package/dist/gaia-ops/skills/security-tiers/reference.md +39 -0
  219. package/dist/gaia-ops/skills/skill-creation/SKILL.md +92 -0
  220. package/dist/gaia-ops/skills/skill-creation/reference.md +29 -0
  221. package/dist/gaia-ops/skills/terraform-patterns/SKILL.md +89 -0
  222. package/dist/gaia-ops/skills/terraform-patterns/reference.md +93 -0
  223. package/dist/gaia-ops/tools/__init__.py +9 -0
  224. package/dist/gaia-ops/tools/agentic-loop/decide-status.py +210 -0
  225. package/dist/gaia-ops/tools/agentic-loop/parse-metric.py +106 -0
  226. package/dist/gaia-ops/tools/agentic-loop/record-iteration.py +221 -0
  227. package/dist/gaia-ops/tools/context/README.md +132 -0
  228. package/dist/gaia-ops/tools/context/__init__.py +42 -0
  229. package/dist/gaia-ops/tools/context/_paths.py +20 -0
  230. package/dist/gaia-ops/tools/context/context_provider.py +721 -0
  231. package/dist/gaia-ops/tools/context/context_section_reader.py +342 -0
  232. package/dist/gaia-ops/tools/context/deep_merge.py +159 -0
  233. package/dist/gaia-ops/tools/context/pending_updates.py +760 -0
  234. package/dist/gaia-ops/tools/context/surface_router.py +278 -0
  235. package/dist/gaia-ops/tools/fast-queries/README.md +65 -0
  236. package/dist/gaia-ops/tools/fast-queries/__init__.py +30 -0
  237. package/dist/gaia-ops/tools/fast-queries/appservices/quicktriage_devops_developer.sh +75 -0
  238. package/dist/gaia-ops/tools/fast-queries/cloud/aws/quicktriage_aws_troubleshooter.sh +32 -0
  239. package/dist/gaia-ops/tools/fast-queries/cloud/gcp/quicktriage_gcp_troubleshooter.sh +88 -0
  240. package/dist/gaia-ops/tools/fast-queries/gitops/quicktriage_gitops_operator.sh +48 -0
  241. package/dist/gaia-ops/tools/fast-queries/run_triage.sh +59 -0
  242. package/dist/gaia-ops/tools/fast-queries/terraform/quicktriage_terraform_architect.sh +80 -0
  243. package/dist/gaia-ops/tools/gaia_simulator/__init__.py +33 -0
  244. package/dist/gaia-ops/tools/gaia_simulator/cli.py +354 -0
  245. package/dist/gaia-ops/tools/gaia_simulator/extractor.py +457 -0
  246. package/dist/gaia-ops/tools/gaia_simulator/reporter.py +258 -0
  247. package/dist/gaia-ops/tools/gaia_simulator/routing_simulator.py +334 -0
  248. package/dist/gaia-ops/tools/gaia_simulator/runner.py +539 -0
  249. package/dist/gaia-ops/tools/gaia_simulator/skills_mapper.py +264 -0
  250. package/dist/gaia-ops/tools/memory/README.md +0 -0
  251. package/dist/gaia-ops/tools/memory/__init__.py +20 -0
  252. package/dist/gaia-ops/tools/memory/backfill_fts5.py +107 -0
  253. package/dist/gaia-ops/tools/memory/conflict_detector.py +295 -0
  254. package/dist/gaia-ops/tools/memory/episodic.py +1210 -0
  255. package/dist/gaia-ops/tools/memory/git_invalidator.py +262 -0
  256. package/dist/gaia-ops/tools/memory/paths.py +102 -0
  257. package/dist/gaia-ops/tools/memory/scoring.py +193 -0
  258. package/dist/gaia-ops/tools/memory/search_store.py +360 -0
  259. package/dist/gaia-ops/tools/persist_transcript_analysis.py +85 -0
  260. package/dist/gaia-ops/tools/review/__init__.py +1 -0
  261. package/dist/gaia-ops/tools/review/review_engine.py +157 -0
  262. package/dist/gaia-ops/tools/scan/__init__.py +35 -0
  263. package/dist/gaia-ops/tools/scan/config.py +247 -0
  264. package/dist/gaia-ops/tools/scan/merge.py +212 -0
  265. package/dist/gaia-ops/tools/scan/orchestrator.py +549 -0
  266. package/dist/gaia-ops/tools/scan/registry.py +127 -0
  267. package/dist/gaia-ops/tools/scan/scanners/__init__.py +18 -0
  268. package/dist/gaia-ops/tools/scan/scanners/base.py +137 -0
  269. package/dist/gaia-ops/tools/scan/scanners/environment.py +349 -0
  270. package/dist/gaia-ops/tools/scan/scanners/git.py +570 -0
  271. package/dist/gaia-ops/tools/scan/scanners/infrastructure.py +875 -0
  272. package/dist/gaia-ops/tools/scan/scanners/orchestration.py +600 -0
  273. package/dist/gaia-ops/tools/scan/scanners/stack.py +1085 -0
  274. package/dist/gaia-ops/tools/scan/scanners/tools.py +260 -0
  275. package/dist/gaia-ops/tools/scan/setup.py +686 -0
  276. package/dist/gaia-ops/tools/scan/tests/__init__.py +1 -0
  277. package/dist/gaia-ops/tools/scan/tests/conftest.py +796 -0
  278. package/dist/gaia-ops/tools/scan/tests/test_environment.py +323 -0
  279. package/dist/gaia-ops/tools/scan/tests/test_git.py +419 -0
  280. package/dist/gaia-ops/tools/scan/tests/test_infrastructure.py +382 -0
  281. package/dist/gaia-ops/tools/scan/tests/test_integration.py +920 -0
  282. package/dist/gaia-ops/tools/scan/tests/test_merge.py +269 -0
  283. package/dist/gaia-ops/tools/scan/tests/test_orchestration.py +304 -0
  284. package/dist/gaia-ops/tools/scan/tests/test_stack.py +604 -0
  285. package/dist/gaia-ops/tools/scan/tests/test_tools.py +349 -0
  286. package/dist/gaia-ops/tools/scan/ui.py +624 -0
  287. package/dist/gaia-ops/tools/scan/verify.py +270 -0
  288. package/dist/gaia-ops/tools/scan/walk.py +118 -0
  289. package/dist/gaia-ops/tools/scan/workspace.py +85 -0
  290. package/dist/gaia-ops/tools/validation/README.md +244 -0
  291. package/dist/gaia-ops/tools/validation/__init__.py +17 -0
  292. package/dist/gaia-ops/tools/validation/approval_gate.py +321 -0
  293. package/dist/gaia-ops/tools/validation/validate_skills.py +189 -0
  294. package/dist/gaia-security/.claude-plugin/plugin.json +24 -0
  295. package/dist/gaia-security/README.md +90 -0
  296. package/dist/gaia-security/config/universal-rules.json +102 -0
  297. package/dist/gaia-security/hooks/adapters/__init__.py +52 -0
  298. package/dist/gaia-security/hooks/adapters/base.py +219 -0
  299. package/dist/gaia-security/hooks/adapters/channel.py +17 -0
  300. package/dist/gaia-security/hooks/adapters/claude_code.py +1890 -0
  301. package/dist/gaia-security/hooks/adapters/types.py +194 -0
  302. package/dist/gaia-security/hooks/adapters/utils.py +25 -0
  303. package/dist/gaia-security/hooks/hooks.json +84 -0
  304. package/dist/gaia-security/hooks/modules/__init__.py +15 -0
  305. package/dist/gaia-security/hooks/modules/agents/__init__.py +29 -0
  306. package/dist/gaia-security/hooks/modules/agents/contract_validator.py +647 -0
  307. package/dist/gaia-security/hooks/modules/agents/response_contract.py +496 -0
  308. package/dist/gaia-security/hooks/modules/agents/skill_injection_verifier.py +120 -0
  309. package/dist/gaia-security/hooks/modules/agents/state_tracker.py +267 -0
  310. package/dist/gaia-security/hooks/modules/agents/task_info_builder.py +74 -0
  311. package/dist/gaia-security/hooks/modules/agents/transcript_analyzer.py +458 -0
  312. package/dist/gaia-security/hooks/modules/agents/transcript_reader.py +152 -0
  313. package/dist/gaia-security/hooks/modules/audit/__init__.py +28 -0
  314. package/dist/gaia-security/hooks/modules/audit/event_detector.py +168 -0
  315. package/dist/gaia-security/hooks/modules/audit/logger.py +131 -0
  316. package/dist/gaia-security/hooks/modules/audit/metrics.py +134 -0
  317. package/dist/gaia-security/hooks/modules/audit/workflow_auditor.py +611 -0
  318. package/dist/gaia-security/hooks/modules/audit/workflow_recorder.py +296 -0
  319. package/dist/gaia-security/hooks/modules/context/__init__.py +11 -0
  320. package/dist/gaia-security/hooks/modules/context/agentic_loop_detector.py +165 -0
  321. package/dist/gaia-security/hooks/modules/context/anchor_tracker.py +317 -0
  322. package/dist/gaia-security/hooks/modules/context/compact_context_builder.py +218 -0
  323. package/dist/gaia-security/hooks/modules/context/context_freshness.py +145 -0
  324. package/dist/gaia-security/hooks/modules/context/context_injector.py +558 -0
  325. package/dist/gaia-security/hooks/modules/context/context_writer.py +530 -0
  326. package/dist/gaia-security/hooks/modules/context/contracts_loader.py +161 -0
  327. package/dist/gaia-security/hooks/modules/core/__init__.py +40 -0
  328. package/dist/gaia-security/hooks/modules/core/hook_entry.py +78 -0
  329. package/dist/gaia-security/hooks/modules/core/paths.py +160 -0
  330. package/dist/gaia-security/hooks/modules/core/plugin_mode.py +149 -0
  331. package/dist/gaia-security/hooks/modules/core/plugin_setup.py +577 -0
  332. package/dist/gaia-security/hooks/modules/core/state.py +179 -0
  333. package/dist/gaia-security/hooks/modules/core/stdin.py +24 -0
  334. package/dist/gaia-security/hooks/modules/events/__init__.py +1 -0
  335. package/dist/gaia-security/hooks/modules/events/event_writer.py +210 -0
  336. package/dist/gaia-security/hooks/modules/memory/__init__.py +8 -0
  337. package/dist/gaia-security/hooks/modules/memory/episode_writer.py +216 -0
  338. package/dist/gaia-security/hooks/modules/orchestrator/__init__.py +1 -0
  339. package/dist/gaia-security/hooks/modules/orchestrator/delegate_mode.py +122 -0
  340. package/dist/gaia-security/hooks/modules/scanning/__init__.py +8 -0
  341. package/dist/gaia-security/hooks/modules/scanning/scan_trigger.py +84 -0
  342. package/dist/gaia-security/hooks/modules/security/__init__.py +120 -0
  343. package/dist/gaia-security/hooks/modules/security/approval_cleanup.py +87 -0
  344. package/dist/gaia-security/hooks/modules/security/approval_constants.py +23 -0
  345. package/dist/gaia-security/hooks/modules/security/approval_grants.py +1638 -0
  346. package/dist/gaia-security/hooks/modules/security/approval_messages.py +71 -0
  347. package/dist/gaia-security/hooks/modules/security/approval_scopes.py +222 -0
  348. package/dist/gaia-security/hooks/modules/security/blocked_commands.py +595 -0
  349. package/dist/gaia-security/hooks/modules/security/blocked_message_formatter.py +87 -0
  350. package/dist/gaia-security/hooks/modules/security/command_semantics.py +181 -0
  351. package/dist/gaia-security/hooks/modules/security/composition_rules.py +547 -0
  352. package/dist/gaia-security/hooks/modules/security/flag_classifiers.py +873 -0
  353. package/dist/gaia-security/hooks/modules/security/gitops_validator.py +179 -0
  354. package/dist/gaia-security/hooks/modules/security/mutative_verbs.py +1131 -0
  355. package/dist/gaia-security/hooks/modules/security/network_hosts.py +481 -0
  356. package/dist/gaia-security/hooks/modules/security/prompt_validator.py +40 -0
  357. package/dist/gaia-security/hooks/modules/security/shell_unwrapper.py +165 -0
  358. package/dist/gaia-security/hooks/modules/security/tiers.py +196 -0
  359. package/dist/gaia-security/hooks/modules/session/__init__.py +10 -0
  360. package/dist/gaia-security/hooks/modules/session/pending_scanner.py +174 -0
  361. package/dist/gaia-security/hooks/modules/session/session_context_writer.py +100 -0
  362. package/dist/gaia-security/hooks/modules/session/session_event_injector.py +160 -0
  363. package/dist/gaia-security/hooks/modules/session/session_manager.py +31 -0
  364. package/dist/gaia-security/hooks/modules/session/session_registry.py +232 -0
  365. package/dist/gaia-security/hooks/modules/tools/__init__.py +29 -0
  366. package/dist/gaia-security/hooks/modules/tools/bash_validator.py +1008 -0
  367. package/dist/gaia-security/hooks/modules/tools/cloud_pipe_validator.py +231 -0
  368. package/dist/gaia-security/hooks/modules/tools/hook_response.py +55 -0
  369. package/dist/gaia-security/hooks/modules/tools/shell_parser.py +227 -0
  370. package/dist/gaia-security/hooks/modules/tools/stage_decomposer.py +315 -0
  371. package/dist/gaia-security/hooks/modules/tools/task_validator.py +294 -0
  372. package/dist/gaia-security/hooks/modules/validation/__init__.py +23 -0
  373. package/dist/gaia-security/hooks/modules/validation/commit_validator.py +380 -0
  374. package/dist/gaia-security/hooks/post_tool_use.py +54 -0
  375. package/dist/gaia-security/hooks/pre_tool_use.py +413 -0
  376. package/dist/gaia-security/hooks/session_start.py +81 -0
  377. package/dist/gaia-security/hooks/stop_hook.py +82 -0
  378. package/dist/gaia-security/hooks/user_prompt_submit.py +246 -0
  379. package/dist/gaia-security/settings.json +58 -0
  380. package/git-hooks/commit-msg +41 -0
  381. package/hooks/README.md +100 -0
  382. package/hooks/adapters/__init__.py +52 -0
  383. package/hooks/adapters/base.py +219 -0
  384. package/hooks/adapters/channel.py +17 -0
  385. package/hooks/adapters/claude_code.py +1890 -0
  386. package/hooks/adapters/types.py +194 -0
  387. package/hooks/adapters/utils.py +25 -0
  388. package/hooks/elicitation_result.py +179 -0
  389. package/hooks/hooks.json +84 -0
  390. package/hooks/modules/README.md +189 -0
  391. package/hooks/modules/__init__.py +15 -0
  392. package/hooks/modules/agents/__init__.py +29 -0
  393. package/hooks/modules/agents/contract_validator.py +647 -0
  394. package/hooks/modules/agents/response_contract.py +496 -0
  395. package/hooks/modules/agents/skill_injection_verifier.py +120 -0
  396. package/hooks/modules/agents/state_tracker.py +267 -0
  397. package/hooks/modules/agents/task_info_builder.py +74 -0
  398. package/hooks/modules/agents/transcript_analyzer.py +458 -0
  399. package/hooks/modules/agents/transcript_reader.py +152 -0
  400. package/hooks/modules/audit/__init__.py +28 -0
  401. package/hooks/modules/audit/event_detector.py +168 -0
  402. package/hooks/modules/audit/logger.py +131 -0
  403. package/hooks/modules/audit/metrics.py +134 -0
  404. package/hooks/modules/audit/workflow_auditor.py +611 -0
  405. package/hooks/modules/audit/workflow_recorder.py +296 -0
  406. package/hooks/modules/context/__init__.py +11 -0
  407. package/hooks/modules/context/agentic_loop_detector.py +165 -0
  408. package/hooks/modules/context/anchor_tracker.py +317 -0
  409. package/hooks/modules/context/compact_context_builder.py +218 -0
  410. package/hooks/modules/context/context_freshness.py +145 -0
  411. package/hooks/modules/context/context_injector.py +558 -0
  412. package/hooks/modules/context/context_writer.py +530 -0
  413. package/hooks/modules/context/contracts_loader.py +161 -0
  414. package/hooks/modules/core/__init__.py +40 -0
  415. package/hooks/modules/core/hook_entry.py +78 -0
  416. package/hooks/modules/core/paths.py +160 -0
  417. package/hooks/modules/core/plugin_mode.py +149 -0
  418. package/hooks/modules/core/plugin_setup.py +577 -0
  419. package/hooks/modules/core/state.py +179 -0
  420. package/hooks/modules/core/stdin.py +24 -0
  421. package/hooks/modules/events/__init__.py +1 -0
  422. package/hooks/modules/events/event_writer.py +210 -0
  423. package/hooks/modules/evidence/__init__.py +34 -0
  424. package/hooks/modules/evidence/assertions.py +137 -0
  425. package/hooks/modules/evidence/index_writer.py +57 -0
  426. package/hooks/modules/evidence/loader.py +126 -0
  427. package/hooks/modules/evidence/runner.py +241 -0
  428. package/hooks/modules/memory/__init__.py +8 -0
  429. package/hooks/modules/memory/episode_writer.py +216 -0
  430. package/hooks/modules/orchestrator/__init__.py +1 -0
  431. package/hooks/modules/orchestrator/delegate_mode.py +122 -0
  432. package/hooks/modules/scanning/__init__.py +8 -0
  433. package/hooks/modules/scanning/scan_trigger.py +84 -0
  434. package/hooks/modules/security/__init__.py +120 -0
  435. package/hooks/modules/security/approval_cleanup.py +87 -0
  436. package/hooks/modules/security/approval_constants.py +23 -0
  437. package/hooks/modules/security/approval_grants.py +1638 -0
  438. package/hooks/modules/security/approval_messages.py +71 -0
  439. package/hooks/modules/security/approval_scopes.py +222 -0
  440. package/hooks/modules/security/blocked_commands.py +595 -0
  441. package/hooks/modules/security/blocked_message_formatter.py +87 -0
  442. package/hooks/modules/security/command_semantics.py +181 -0
  443. package/hooks/modules/security/composition_rules.py +547 -0
  444. package/hooks/modules/security/flag_classifiers.py +873 -0
  445. package/hooks/modules/security/gitops_validator.py +179 -0
  446. package/hooks/modules/security/mutative_verbs.py +1131 -0
  447. package/hooks/modules/security/network_hosts.py +481 -0
  448. package/hooks/modules/security/prompt_validator.py +40 -0
  449. package/hooks/modules/security/shell_unwrapper.py +165 -0
  450. package/hooks/modules/security/tiers.py +196 -0
  451. package/hooks/modules/session/__init__.py +10 -0
  452. package/hooks/modules/session/pending_scanner.py +174 -0
  453. package/hooks/modules/session/session_context_writer.py +100 -0
  454. package/hooks/modules/session/session_event_injector.py +160 -0
  455. package/hooks/modules/session/session_manager.py +31 -0
  456. package/hooks/modules/session/session_registry.py +232 -0
  457. package/hooks/modules/tools/__init__.py +29 -0
  458. package/hooks/modules/tools/bash_validator.py +1008 -0
  459. package/hooks/modules/tools/cloud_pipe_validator.py +231 -0
  460. package/hooks/modules/tools/hook_response.py +55 -0
  461. package/hooks/modules/tools/shell_parser.py +227 -0
  462. package/hooks/modules/tools/stage_decomposer.py +315 -0
  463. package/hooks/modules/tools/task_validator.py +294 -0
  464. package/hooks/modules/validation/__init__.py +23 -0
  465. package/hooks/modules/validation/commit_validator.py +380 -0
  466. package/hooks/post_compact.py +43 -0
  467. package/hooks/post_tool_use.py +54 -0
  468. package/hooks/pre_compact.py +60 -0
  469. package/hooks/pre_tool_use.py +413 -0
  470. package/hooks/session_start.py +81 -0
  471. package/hooks/stop_hook.py +82 -0
  472. package/hooks/subagent_start.py +71 -0
  473. package/hooks/subagent_stop.py +295 -0
  474. package/hooks/task_completed.py +70 -0
  475. package/hooks/user_prompt_submit.py +246 -0
  476. package/index.js +83 -0
  477. package/package.json +99 -0
  478. package/pyproject.toml +32 -0
  479. package/skills/README.md +154 -0
  480. package/skills/agent-protocol/SKILL.md +93 -0
  481. package/skills/agent-protocol/examples.md +223 -0
  482. package/skills/agent-response/SKILL.md +69 -0
  483. package/skills/agentic-loop/SKILL.md +80 -0
  484. package/skills/agentic-loop/reference.md +378 -0
  485. package/skills/blog-writing/SKILL.md +98 -0
  486. package/skills/blog-writing/reference.md +130 -0
  487. package/skills/brief-spec/SKILL.md +182 -0
  488. package/skills/command-execution/SKILL.md +64 -0
  489. package/skills/command-execution/reference.md +83 -0
  490. package/skills/context-updater/SKILL.md +87 -0
  491. package/skills/context-updater/examples.md +71 -0
  492. package/skills/developer-patterns/SKILL.md +50 -0
  493. package/skills/developer-patterns/reference.md +112 -0
  494. package/skills/execution/SKILL.md +99 -0
  495. package/skills/fast-queries/SKILL.md +43 -0
  496. package/skills/gaia-compact/SKILL.md +74 -0
  497. package/skills/gaia-patterns/SKILL.md +108 -0
  498. package/skills/gaia-patterns/reference.md +395 -0
  499. package/skills/gaia-planner/SKILL.md +37 -0
  500. package/skills/gaia-planner/reference.md +107 -0
  501. package/skills/gaia-release/SKILL.md +82 -0
  502. package/skills/gaia-release/reference.md +102 -0
  503. package/skills/gaia-self-check/SKILL.md +114 -0
  504. package/skills/gaia-self-check/reference.md +453 -0
  505. package/skills/gaia-verify/SKILL.md +77 -0
  506. package/skills/gaia-verify/reference.md +80 -0
  507. package/skills/git-conventions/SKILL.md +47 -0
  508. package/skills/gitops-patterns/SKILL.md +60 -0
  509. package/skills/gitops-patterns/reference.md +183 -0
  510. package/skills/gmail-policy/SKILL.md +200 -0
  511. package/skills/gmail-policy/reference.md +150 -0
  512. package/skills/gmail-triage/SKILL.md +100 -0
  513. package/skills/gws-setup/SKILL.md +99 -0
  514. package/skills/gws-setup/reference.md +73 -0
  515. package/skills/investigation/SKILL.md +100 -0
  516. package/skills/memory-curation/SKILL.md +83 -0
  517. package/skills/memory-search/SKILL.md +88 -0
  518. package/skills/orchestrator-approval/SKILL.md +160 -0
  519. package/skills/orchestrator-approval/reference.md +174 -0
  520. package/skills/pending-approvals/SKILL.md +72 -0
  521. package/skills/pending-approvals/reference.md +214 -0
  522. package/skills/readme-writing/SKILL.md +71 -0
  523. package/skills/readme-writing/reference.md +188 -0
  524. package/skills/reference.md +135 -0
  525. package/skills/request-approval/SKILL.md +140 -0
  526. package/skills/request-approval/examples.md +140 -0
  527. package/skills/request-approval/reference.md +57 -0
  528. package/skills/schedule-task/SKILL.md +64 -0
  529. package/skills/schedule-task/reference.md +233 -0
  530. package/skills/security-tiers/SKILL.md +141 -0
  531. package/skills/security-tiers/destructive-commands-reference.md +623 -0
  532. package/skills/security-tiers/reference.md +39 -0
  533. package/skills/skill-creation/SKILL.md +92 -0
  534. package/skills/skill-creation/reference.md +29 -0
  535. package/skills/terraform-patterns/SKILL.md +89 -0
  536. package/skills/terraform-patterns/reference.md +93 -0
  537. package/templates/README.md +69 -0
  538. package/templates/managed-settings.template.json +43 -0
  539. package/tools/__init__.py +9 -0
  540. package/tools/agentic-loop/decide-status.py +210 -0
  541. package/tools/agentic-loop/parse-metric.py +106 -0
  542. package/tools/agentic-loop/record-iteration.py +221 -0
  543. package/tools/context/README.md +132 -0
  544. package/tools/context/__init__.py +42 -0
  545. package/tools/context/_paths.py +20 -0
  546. package/tools/context/context_provider.py +721 -0
  547. package/tools/context/context_section_reader.py +342 -0
  548. package/tools/context/deep_merge.py +159 -0
  549. package/tools/context/pending_updates.py +760 -0
  550. package/tools/context/surface_router.py +278 -0
  551. package/tools/fast-queries/README.md +65 -0
  552. package/tools/fast-queries/__init__.py +30 -0
  553. package/tools/fast-queries/appservices/quicktriage_devops_developer.sh +75 -0
  554. package/tools/fast-queries/cloud/aws/quicktriage_aws_troubleshooter.sh +32 -0
  555. package/tools/fast-queries/cloud/gcp/quicktriage_gcp_troubleshooter.sh +88 -0
  556. package/tools/fast-queries/gitops/quicktriage_gitops_operator.sh +48 -0
  557. package/tools/fast-queries/run_triage.sh +59 -0
  558. package/tools/fast-queries/terraform/quicktriage_terraform_architect.sh +80 -0
  559. package/tools/gaia_simulator/__init__.py +33 -0
  560. package/tools/gaia_simulator/cli.py +354 -0
  561. package/tools/gaia_simulator/extractor.py +457 -0
  562. package/tools/gaia_simulator/reporter.py +258 -0
  563. package/tools/gaia_simulator/routing_simulator.py +334 -0
  564. package/tools/gaia_simulator/runner.py +539 -0
  565. package/tools/gaia_simulator/skills_mapper.py +264 -0
  566. package/tools/memory/README.md +0 -0
  567. package/tools/memory/__init__.py +20 -0
  568. package/tools/memory/backfill_fts5.py +107 -0
  569. package/tools/memory/conflict_detector.py +295 -0
  570. package/tools/memory/episodic.py +1210 -0
  571. package/tools/memory/git_invalidator.py +262 -0
  572. package/tools/memory/paths.py +102 -0
  573. package/tools/memory/scoring.py +193 -0
  574. package/tools/memory/search_store.py +360 -0
  575. package/tools/persist_transcript_analysis.py +85 -0
  576. package/tools/review/__init__.py +1 -0
  577. package/tools/review/review_engine.py +157 -0
  578. package/tools/scan/__init__.py +35 -0
  579. package/tools/scan/config.py +247 -0
  580. package/tools/scan/merge.py +212 -0
  581. package/tools/scan/orchestrator.py +549 -0
  582. package/tools/scan/registry.py +127 -0
  583. package/tools/scan/scanners/__init__.py +18 -0
  584. package/tools/scan/scanners/base.py +137 -0
  585. package/tools/scan/scanners/environment.py +349 -0
  586. package/tools/scan/scanners/git.py +570 -0
  587. package/tools/scan/scanners/infrastructure.py +875 -0
  588. package/tools/scan/scanners/orchestration.py +600 -0
  589. package/tools/scan/scanners/stack.py +1085 -0
  590. package/tools/scan/scanners/tools.py +260 -0
  591. package/tools/scan/setup.py +686 -0
  592. package/tools/scan/tests/__init__.py +1 -0
  593. package/tools/scan/tests/conftest.py +796 -0
  594. package/tools/scan/tests/test_environment.py +323 -0
  595. package/tools/scan/tests/test_git.py +419 -0
  596. package/tools/scan/tests/test_infrastructure.py +382 -0
  597. package/tools/scan/tests/test_integration.py +920 -0
  598. package/tools/scan/tests/test_merge.py +269 -0
  599. package/tools/scan/tests/test_orchestration.py +304 -0
  600. package/tools/scan/tests/test_stack.py +604 -0
  601. package/tools/scan/tests/test_tools.py +349 -0
  602. package/tools/scan/ui.py +624 -0
  603. package/tools/scan/verify.py +270 -0
  604. package/tools/scan/walk.py +118 -0
  605. package/tools/scan/workspace.py +85 -0
  606. package/tools/validation/README.md +244 -0
  607. package/tools/validation/__init__.py +17 -0
  608. package/tools/validation/approval_gate.py +321 -0
  609. package/tools/validation/validate_skills.py +189 -0
@@ -0,0 +1,413 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Pre-tool use hook - Thin Gate Architecture.
4
+
5
+ Entry point for Bash and Task/Agent tool validation. The hook is the primary
6
+ security gate: with Bash(*) in the settings.json allow list, all commands
7
+ reach this hook regardless of settings.json permissions.
8
+
9
+ Architecture:
10
+ - Uses adapter layer to parse and process the full PreToolUse lifecycle
11
+ - All business logic lives in ClaudeCodeAdapter.adapt_pre_tool_use()
12
+ - This file is stdin/stdout glue only
13
+ """
14
+ from __future__ import annotations
15
+
16
+ import sys
17
+ import json
18
+ import logging
19
+ from pathlib import Path
20
+ from datetime import datetime
21
+
22
+ sys.path.insert(0, str(Path(__file__).parent))
23
+ from modules.core.paths import get_logs_dir
24
+
25
+ # Adapter layer
26
+ from adapters.claude_code import ClaudeCodeAdapter
27
+ from modules.core.stdin import has_stdin_data
28
+ from adapters.utils import warn_if_dual_channel
29
+
30
+ # Configure logging -- all hooks share hooks-YYYY-MM-DD.log for easy tailing
31
+ log_file = get_logs_dir() / f"hooks-{datetime.now().strftime('%Y-%m-%d')}.log"
32
+ logging.basicConfig(
33
+ level=logging.INFO,
34
+ format='%(asctime)s [pre_tool_use] %(name)s - %(levelname)s - %(message)s',
35
+ handlers=[
36
+ logging.FileHandler(log_file),
37
+ ]
38
+ )
39
+ logger = logging.getLogger(__name__)
40
+
41
+
42
+ # ============================================================================
43
+ # BACKWARD-COMPATIBLE API
44
+ # ============================================================================
45
+ # Tests and e2e scripts import these names directly. They delegate to the
46
+ # adapter internally but preserve the original call signatures.
47
+
48
+ from modules.tools.bash_validator import BashValidator
49
+ from modules.tools.task_validator import TaskValidator, AVAILABLE_AGENTS, META_AGENTS
50
+ from modules.security.prompt_validator import classify_resume_prompt
51
+ from modules.context.context_injector import build_project_context
52
+ from modules.session.session_event_injector import build_session_events
53
+ from modules.core.state import create_pre_hook_state, save_hook_state, get_session_id
54
+ from modules.security.approval_grants import (
55
+ cleanup_expired_grants,
56
+ )
57
+
58
+ # Derived constants used by backward-compat wrappers
59
+ PROJECT_AGENTS = [a for a in AVAILABLE_AGENTS if a not in META_AGENTS]
60
+ _HOOKS_DIR = Path(__file__).parent
61
+
62
+
63
+ def _classify_resume_prompt(prompt: str) -> str:
64
+ """Classify a resume prompt. Delegates to modules.security.prompt_validator."""
65
+ return classify_resume_prompt(prompt)
66
+
67
+
68
+ def pre_tool_use_hook(tool_name: str, parameters: dict) -> str | dict | None:
69
+ """
70
+ Pre-tool use hook implementation (backward-compatible API).
71
+
72
+ Delegates to adapter but preserves the original return signature:
73
+ None: allowed (no modification)
74
+ str: blocked (error message)
75
+ dict: allowed with modification (JSON with updatedInput)
76
+ """
77
+ adapter = ClaudeCodeAdapter()
78
+
79
+ # Build a minimal HookEvent-like payload for the adapter's internal methods
80
+ logger.info(f"Hook invoked: tool={tool_name}, params={json.dumps(parameters)[:200]}")
81
+
82
+ try:
83
+ cleanup_expired_grants()
84
+
85
+ if not isinstance(tool_name, str):
86
+ return "Error: Invalid tool name"
87
+ if not isinstance(parameters, dict):
88
+ return "Error: Invalid parameters"
89
+
90
+ if tool_name.lower() == "bash":
91
+ return _handle_bash(tool_name, parameters)
92
+ elif tool_name.lower() in ("task", "agent"):
93
+ return _handle_task(tool_name, parameters)
94
+ elif tool_name.lower() == "sendmessage":
95
+ return _handle_send_message(tool_name, parameters)
96
+ elif tool_name.lower() in ("write", "edit"):
97
+ return _handle_write_edit(tool_name, parameters)
98
+ else:
99
+ return None
100
+
101
+ except Exception as e:
102
+ logger.error(f"Unexpected error in pre_tool_use_hook: {e}", exc_info=True)
103
+ return f"Error during security validation: {str(e)}"
104
+
105
+
106
+ def _handle_bash(tool_name: str, parameters: dict) -> str | dict | None:
107
+ """
108
+ Handle Bash tool validation.
109
+
110
+ Returns:
111
+ None: allowed (no modification)
112
+ str: blocked (error message)
113
+ dict: allowed with modification (hookSpecificOutput JSON)
114
+ """
115
+ command = parameters.get("command", "")
116
+ if not command:
117
+ return "Error: Bash tool requires a command"
118
+
119
+ validator = BashValidator()
120
+ result = validator.validate(command)
121
+
122
+ if not result.allowed:
123
+ logger.warning(f"BLOCKED: {command[:100]} - {result.reason}")
124
+
125
+ # Structured response from bash_validator (ask or deny)
126
+ if result.block_response is not None:
127
+ return result.block_response
128
+
129
+ # Permanently blocked (no structured response) — hard block (exit 2)
130
+ return _format_blocked_message(result)
131
+
132
+ effective_command = result.modified_input.get("command", command) if result.modified_input else command
133
+ state = create_pre_hook_state(
134
+ tool_name=tool_name,
135
+ command=effective_command,
136
+ tier=str(result.tier),
137
+ allowed=True,
138
+ )
139
+ save_hook_state(state)
140
+
141
+ if result.modified_input:
142
+ logger.info(f"MODIFIED: {command[:80]} -> stripped footer - tier={result.tier}")
143
+ return {
144
+ "hookSpecificOutput": {
145
+ "hookEventName": "PreToolUse",
146
+ "permissionDecision": "allow",
147
+ "permissionDecisionReason": result.reason,
148
+ "updatedInput": result.modified_input
149
+ }
150
+ }
151
+
152
+ logger.info(f"ALLOWED: {command[:100]} - tier={result.tier}")
153
+ return None
154
+
155
+
156
+ def _handle_task(tool_name: str, parameters: dict) -> str | dict | None:
157
+ """
158
+ Handle Task/Agent tool validation for new task dispatches.
159
+
160
+ Context is built here and cached for SubagentStart to forward to the
161
+ subagent. PreToolUse no longer returns additionalContext (that would
162
+ inject it into the orchestrator, not the subagent).
163
+ """
164
+ context_text, _telemetry = build_project_context(parameters, PROJECT_AGENTS, _HOOKS_DIR)
165
+ events_text = build_session_events(parameters, PROJECT_AGENTS)
166
+
167
+ # Standard task validation (runs against ORIGINAL prompt -- no workaround needed)
168
+ validator = TaskValidator()
169
+ result = validator.validate(parameters)
170
+
171
+ if not result.allowed:
172
+ logger.warning(f"BLOCKED Task: {result.agent_name} - {result.reason}")
173
+ return result.reason
174
+
175
+ state = create_pre_hook_state(
176
+ tool_name=tool_name,
177
+ command=f"Task:{result.agent_name}",
178
+ tier=str(result.tier),
179
+ allowed=True,
180
+ is_t3=result.is_t3_operation,
181
+ )
182
+ save_hook_state(state)
183
+
184
+ logger.info(f"ALLOWED Task: {result.agent_name}")
185
+
186
+ # Cache context for SubagentStart to pick up and forward to the subagent.
187
+ additional = "\n".join(filter(None, [context_text, events_text]))
188
+
189
+ # Fallback: if build_project_context returned None because the
190
+ # orchestrator already embedded context in the prompt (dedup guard),
191
+ # extract the embedded context so SubagentStart can still inject it.
192
+ if not additional:
193
+ prompt = parameters.get("prompt", "")
194
+ marker = "# Project Context"
195
+ if marker in prompt:
196
+ idx = prompt.index(marker)
197
+ additional = prompt[idx:]
198
+ logger.info(
199
+ "Extracted embedded context from prompt for caching "
200
+ "(len=%d, agent=%s)",
201
+ len(additional), result.agent_name,
202
+ )
203
+
204
+ if additional:
205
+ from adapters.claude_code import ClaudeCodeAdapter
206
+ adapter = ClaudeCodeAdapter()
207
+ session_id = parameters.get("session_id", "") or "unknown"
208
+ agent_type = result.agent_name or "unknown"
209
+ adapter._cache_context_for_subagent(session_id, agent_type, additional)
210
+ logger.info(f"Cached context for SubagentStart: agent={agent_type}")
211
+
212
+ return None
213
+
214
+
215
+ def _handle_send_message(tool_name: str, parameters: dict) -> str | None:
216
+ """
217
+ Handle SendMessage tool validation for agent resumption.
218
+
219
+ Validates agent ID format and message content. Does NOT inject
220
+ project context (it's a resume). Nonce relay is no longer processed
221
+ here -- approval grants are activated by the UserPromptSubmit hook.
222
+
223
+ Returns:
224
+ None: allowed (no modification)
225
+ str: blocked (error message)
226
+ """
227
+ import re
228
+
229
+ agent_id = parameters.get("to", "")
230
+ message = parameters.get("message", "")
231
+
232
+ if not agent_id or not re.match(r'^a[0-9a-f]{5,}$', agent_id):
233
+ logger.warning(f"BLOCKED SendMessage: Invalid agentId format '{agent_id}'")
234
+ return (
235
+ f"[ERROR] Invalid agent ID format: '{agent_id}'\n\n"
236
+ "Agent ID should be 'a' followed by hex characters.\n"
237
+ "Example: a12345f or a51a0cbbf6afb831d\n\n"
238
+ "The agent ID is returned at the end of agent responses.\n"
239
+ "Look for: 'agentId: a...' in the previous agent output."
240
+ )
241
+
242
+ if not message or not message.strip():
243
+ logger.warning(f"BLOCKED SendMessage: Missing message for agent {agent_id}")
244
+ return (
245
+ "[ERROR] SendMessage requires a message\n\n"
246
+ "When resuming an agent, you must provide a message:\n\n"
247
+ "SendMessage(\n"
248
+ " to=\"a12345\",\n"
249
+ " message=\"Continue with the latest user instruction.\"\n"
250
+ ")\n\n"
251
+ "The message tells the agent what to do next."
252
+ )
253
+
254
+ logger.info(f"SENDMESSAGE: Resuming agent {agent_id}")
255
+
256
+ state = create_pre_hook_state(
257
+ tool_name=tool_name,
258
+ command=f"SendMessage:{agent_id}",
259
+ tier="T0",
260
+ allowed=True,
261
+ is_t3=False,
262
+ has_approval=False,
263
+ )
264
+ save_hook_state(state)
265
+
266
+ logger.info(f"ALLOWED SendMessage: agent {agent_id} - message length: {len(message)}")
267
+ return None
268
+
269
+
270
+ def _handle_write_edit(tool_name: str, parameters: dict) -> str | dict | None:
271
+ """
272
+ Handle Write/Edit tool path protection (backward-compatible API).
273
+
274
+ Delegates to the adapter's _adapt_write_edit method for consistency.
275
+ session_id comes from get_session_id() and is_subagent defaults to False
276
+ because the CLI test interface always runs as the main session.
277
+
278
+ Returns:
279
+ None: allowed (no modification)
280
+ dict: requires user approval (hookSpecificOutput with ask)
281
+ """
282
+ from adapters.claude_code import ClaudeCodeAdapter
283
+ from adapters.types import HookResponse
284
+
285
+ adapter = ClaudeCodeAdapter()
286
+ response = adapter._adapt_write_edit(
287
+ tool_name, parameters,
288
+ session_id=get_session_id(),
289
+ is_subagent=False,
290
+ )
291
+
292
+ # Convert HookResponse back to backward-compat API format
293
+ if isinstance(response.output, dict) and response.output:
294
+ return response.output
295
+ return None
296
+
297
+
298
+ def _format_blocked_message(result) -> str:
299
+ """Format blocked command message. Delegates to blocked_message_formatter."""
300
+ from modules.security.blocked_message_formatter import format_blocked_message
301
+ return format_blocked_message(result)
302
+
303
+
304
+ # ============================================================================
305
+ # CLI INTERFACE
306
+ # ============================================================================
307
+
308
+ def main():
309
+ """CLI interface for testing."""
310
+ if len(sys.argv) < 2:
311
+ print("Usage: python pre_tool_use.py <command>")
312
+ print(" python pre_tool_use.py --test")
313
+ sys.exit(1)
314
+
315
+ if sys.argv[1] == "--test":
316
+ _run_tests()
317
+ else:
318
+ command = " ".join(sys.argv[1:])
319
+ result = pre_tool_use_hook("bash", {"command": command})
320
+ if result:
321
+ print(f"BLOCKED: {result}")
322
+ sys.exit(1)
323
+ else:
324
+ print(f"ALLOWED: {command}")
325
+
326
+
327
+ def _run_tests():
328
+ """Run validation tests."""
329
+ print("Testing Pre-Tool Use Hook...\n")
330
+
331
+ test_cases = [
332
+ ("terraform validate", True, "T1"),
333
+ ("terraform apply", False, "T3"),
334
+ ("kubectl get pods", True, "T0"),
335
+ ("kubectl apply -f manifest.yaml", False, "T3"),
336
+ ("kubectl apply -f manifest.yaml --dry-run=client", True, "T2"),
337
+ ("ls -la", True, "T0"),
338
+ ("rm -rf /", False, "T3"),
339
+ ]
340
+
341
+ for command, expected_allowed, expected_tier in test_cases:
342
+ result = pre_tool_use_hook("bash", {"command": command})
343
+ actual_allowed = result is None
344
+ status = "PASS" if actual_allowed == expected_allowed else "FAIL"
345
+ print(f"{status}: {command}")
346
+ if status == "FAIL":
347
+ print(f" Expected: allowed={expected_allowed}, Got: allowed={actual_allowed}")
348
+
349
+ print("\nTest completed")
350
+
351
+
352
+ # ============================================================================
353
+ # STDIN HANDLER (Claude Code integration)
354
+ # ============================================================================
355
+
356
+ if __name__ == "__main__":
357
+ # Check if running from CLI with arguments
358
+ if len(sys.argv) > 1:
359
+ main()
360
+ elif has_stdin_data():
361
+ try:
362
+ adapter = ClaudeCodeAdapter()
363
+ warn_if_dual_channel()
364
+
365
+ stdin_data = sys.stdin.read()
366
+
367
+ try:
368
+ event = adapter.parse_event(stdin_data)
369
+ except ValueError as e:
370
+ error_msg = str(e)
371
+ logger.error(f"Adapter parse failed: {error_msg}")
372
+ print(f"HOOK ERROR: {error_msg}", file=sys.stderr)
373
+ if "Empty stdin" in error_msg:
374
+ print(f"Error: {error_msg}")
375
+ sys.exit(1)
376
+
377
+ response = adapter.adapt_pre_tool_use(event)
378
+
379
+ if isinstance(response.output, dict) and response.output:
380
+ hook_output = response.output.get("hookSpecificOutput", {})
381
+ decision = hook_output.get("permissionDecision")
382
+ if decision in ("block", "deny"):
383
+ reason = hook_output.get("permissionDecisionReason", "Command blocked by hook policy")
384
+ summary = reason.split('\n')[0]
385
+ print(f"BLOCKED: {summary}", file=sys.stderr)
386
+ elif decision == "ask":
387
+ reason = hook_output.get("permissionDecisionReason", "")
388
+ summary = reason.split('\n')[0]
389
+ print(f"T3: {summary}", file=sys.stderr)
390
+ print(json.dumps(response.output))
391
+ sys.exit(response.exit_code)
392
+ elif isinstance(response.output, str) and response.output:
393
+ summary = response.output.split('\n')[0]
394
+ print(f"BLOCKED: {summary}", file=sys.stderr)
395
+ print(response.output)
396
+ sys.exit(response.exit_code)
397
+ else:
398
+ sys.exit(0)
399
+
400
+ except json.JSONDecodeError as e:
401
+ logger.error(f"Invalid JSON from stdin: {e}")
402
+ print(f"HOOK ERROR: Invalid JSON from stdin: {e}", file=sys.stderr)
403
+ sys.exit(1)
404
+ except Exception as e:
405
+ logger.error(f"Error processing hook: {e}", exc_info=True)
406
+ print(f"HOOK ERROR: {str(e)}", file=sys.stderr)
407
+ print(f"Hook error: {str(e)}")
408
+ sys.exit(1)
409
+ else:
410
+ print("Usage: python pre_tool_use.py <command>")
411
+ print(" python pre_tool_use.py --test")
412
+ print(" echo '{\"tool_name\":\"bash\",\"tool_input\":{\"command\":\"ls\"}}' | python pre_tool_use.py")
413
+ sys.exit(1)
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env python3
2
+ """SessionStart hook — first-time setup + project scan (ops only)."""
3
+
4
+ import os
5
+ import sys
6
+ import json
7
+ import logging
8
+ from datetime import datetime
9
+ from pathlib import Path
10
+
11
+ sys.path.insert(0, str(Path(__file__).parent))
12
+
13
+ from modules.core.stdin import has_stdin_data
14
+ from modules.core.paths import get_logs_dir
15
+ from modules.core.plugin_mode import is_ops_mode
16
+ from modules.core.plugin_setup import run_first_time_setup
17
+ from modules.session.session_registry import register_session, SessionRegistryError
18
+
19
+ # Configure logging — file only
20
+ _log_file = get_logs_dir() / f"hooks-{datetime.now().strftime('%Y-%m-%d')}.log"
21
+ logging.basicConfig(
22
+ level=logging.INFO,
23
+ format='%(asctime)s [session_start] %(name)s - %(levelname)s - %(message)s',
24
+ handlers=[logging.FileHandler(_log_file)],
25
+ )
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ if __name__ == "__main__":
30
+ if not has_stdin_data():
31
+ sys.exit(0)
32
+
33
+ try:
34
+ sys.stdin.read()
35
+
36
+ # Register this session in the user-scoped session registry.
37
+ # Base infrastructure for T12/T13 liveness filter. Failures are
38
+ # non-fatal: a missing registry entry must never block session start.
39
+ try:
40
+ _sid = os.environ.get("CLAUDE_SESSION_ID")
41
+ if _sid:
42
+ register_session(session_id=_sid, pid=os.getpid())
43
+ except SessionRegistryError as _reg_exc:
44
+ logger.warning("session_registry register failed (non-fatal): %s", _reg_exc)
45
+
46
+ # First-time setup: create project permissions if needed.
47
+ # mark_done=False so UserPromptSubmit can detect first-run
48
+ # and show the welcome message before marking initialized.
49
+ setup_message = run_first_time_setup(mark_done=False)
50
+ if setup_message:
51
+ logger.info("First-time setup: %s", setup_message)
52
+
53
+ # Project scan: only in ops mode
54
+ project_scanned = False
55
+ if is_ops_mode():
56
+ from modules.context.context_freshness import check_freshness
57
+ from modules.scanning.scan_trigger import trigger_lightweight_scan
58
+
59
+ freshness = check_freshness()
60
+ if freshness.is_fresh:
61
+ logger.info("SessionStart: skipped scan (fresh)")
62
+ else:
63
+ logger.info("SessionStart: %s — running lightweight scan", freshness.reason)
64
+ scan_ok = trigger_lightweight_scan(Path.cwd())
65
+ if scan_ok:
66
+ project_scanned = True
67
+ logger.info("Auto-refresh completed successfully")
68
+ else:
69
+ logger.warning("Auto-refresh failed")
70
+
71
+ response = {"session_type": "startup", "project_scanned": project_scanned}
72
+ if setup_message:
73
+ response["setup_message"] = setup_message
74
+
75
+ print(json.dumps(response))
76
+ sys.exit(0)
77
+
78
+ except Exception as e:
79
+ logger.error("SessionStart error (non-fatal): %s", e)
80
+ print(json.dumps({}))
81
+ sys.exit(0)
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Stop hook for Claude Code Agent System.
4
+
5
+ Fires when Claude finishes responding. Evaluates whether the response has
6
+ adequate evidence quality. For MVP: logs the event and allows stop (exit 0).
7
+ Quality check logic will be wired in a future iteration.
8
+
9
+ Architecture:
10
+ - Uses adapter layer to parse Stop event
11
+ - Calls adapter.adapt_stop() for quality assessment
12
+ - Returns quality result via adapter format_quality_response()
13
+ - Exit code 0 = allow stop, exit code 2 = continue instead of stop
14
+ """
15
+
16
+ import os
17
+ import sys
18
+ import json
19
+ import logging
20
+ from datetime import datetime
21
+ from pathlib import Path
22
+
23
+ sys.path.insert(0, str(Path(__file__).parent))
24
+
25
+ from adapters.claude_code import ClaudeCodeAdapter
26
+ from modules.core.hook_entry import run_hook
27
+ from modules.core.paths import get_logs_dir
28
+ from modules.session.session_registry import unregister_session, SessionRegistryError
29
+
30
+ # Configure logging
31
+ _log_file = get_logs_dir() / f"hooks-{datetime.now().strftime('%Y-%m-%d')}.log"
32
+ logging.basicConfig(
33
+ level=logging.INFO,
34
+ format='%(asctime)s [stop_hook] %(name)s - %(levelname)s - %(message)s',
35
+ handlers=[logging.FileHandler(_log_file)],
36
+ )
37
+ logger = logging.getLogger(__name__)
38
+
39
+
40
+ def _handle_stop(event) -> None:
41
+ """Process a Stop event.
42
+
43
+ Evaluates response quality and decides whether to allow the stop.
44
+ For MVP, always allows stop (exit 0).
45
+
46
+ Args:
47
+ event: Parsed HookEvent from the adapter layer.
48
+ """
49
+ adapter = ClaudeCodeAdapter()
50
+
51
+ quality_result = adapter.adapt_stop(event.payload)
52
+ stop_reason = event.payload.get("stop_reason", "unknown")
53
+
54
+ logger.info(
55
+ "Stop: reason=%s, quality_sufficient=%s, score=%.2f",
56
+ stop_reason,
57
+ quality_result.quality_sufficient,
58
+ quality_result.score,
59
+ )
60
+
61
+ # Unregister this session from the user-scoped session registry.
62
+ # Non-fatal: "session not found" is already a silent no-op inside the
63
+ # registry; SessionRegistryError here only signals I/O failure, which
64
+ # is expected in shutdown race conditions.
65
+ try:
66
+ _sid = os.environ.get("CLAUDE_SESSION_ID")
67
+ if _sid:
68
+ unregister_session(session_id=_sid)
69
+ except SessionRegistryError as _reg_exc:
70
+ logger.debug("session_registry unregister failed (non-fatal): %s", _reg_exc)
71
+
72
+ response = adapter.format_quality_response(quality_result)
73
+ print(json.dumps(response.output))
74
+ sys.exit(0)
75
+
76
+
77
+ # ============================================================================
78
+ # STDIN HANDLER (Claude Code integration)
79
+ # ============================================================================
80
+
81
+ if __name__ == "__main__":
82
+ run_hook(_handle_stop, hook_name="stop_hook")