@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,315 @@
1
+ """
2
+ Stage Decomposer - Shell command stage tracking with operator preservation.
3
+
4
+ Wraps ShellCommandParser to add operator tracking between stages.
5
+ ShellCommandParser.parse() discards operators; StageDecomposer preserves them
6
+ so downstream classifiers know how stages are connected (pipe vs AND vs OR).
7
+
8
+ A "stage" is one command with its arguments plus the operator that links it
9
+ to the next stage. The last stage has operator=None.
10
+
11
+ Handles:
12
+ - Pipes (|), semicolons (;), AND (&&), OR (||)
13
+ - Command substitution $(...)
14
+ - Backtick substitution `...`
15
+ - Nested command substitution
16
+
17
+ Dependencies: Python stdlib only.
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import re
23
+ from dataclasses import dataclass, field
24
+ from typing import List, Optional
25
+
26
+ from .shell_parser import ShellCommandParser
27
+
28
+
29
+ @dataclass
30
+ class Stage:
31
+ """A single command stage in a pipeline or chain."""
32
+
33
+ # The raw command token (e.g., "grep -r foo")
34
+ command: str
35
+ # Arguments as a list parsed from the command token
36
+ args: List[str] = field(default_factory=list)
37
+ # Operator connecting THIS stage to the NEXT stage (None for the last stage)
38
+ operator: Optional[str] = None
39
+
40
+ def __str__(self) -> str:
41
+ return self.command
42
+
43
+ @property
44
+ def executable(self) -> str:
45
+ """Return the executable name (first token of the command)."""
46
+ tokens = self.command.strip().split()
47
+ return tokens[0] if tokens else ""
48
+
49
+
50
+ @dataclass
51
+ class DecomposedCommand:
52
+ """Result of decomposing a raw command string into stages."""
53
+
54
+ # Original command string (before decomposition)
55
+ raw: str
56
+ # Ordered list of stages
57
+ stages: List[Stage] = field(default_factory=list)
58
+ # Command substitutions extracted from the command ($(...) or `...`)
59
+ substitutions: List[str] = field(default_factory=list)
60
+
61
+ @property
62
+ def is_compound(self) -> bool:
63
+ """Return True if the command has more than one stage."""
64
+ return len(self.stages) > 1
65
+
66
+ @property
67
+ def executables(self) -> List[str]:
68
+ """Return the list of executables across all stages."""
69
+ return [s.executable for s in self.stages if s.executable]
70
+
71
+
72
+ class StageDecomposer:
73
+ """
74
+ Decomposes a raw shell command string into ordered Stage objects.
75
+
76
+ Wraps ShellCommandParser for quote-aware splitting, then re-walks the
77
+ original string to recover the operators that ShellCommandParser.parse()
78
+ discards.
79
+
80
+ Zero external dependencies -- Python stdlib only.
81
+
82
+ Usage::
83
+
84
+ decomposer = StageDecomposer()
85
+ result = decomposer.decompose("ls | grep foo && wc -l")
86
+ # result.stages[0].command == "ls"
87
+ # result.stages[0].operator == "|"
88
+ # result.stages[1].command == "grep foo"
89
+ # result.stages[1].operator == "&&"
90
+ # result.stages[2].command == "wc -l"
91
+ # result.stages[2].operator is None
92
+ """
93
+
94
+ def __init__(self) -> None:
95
+ self._parser = ShellCommandParser()
96
+
97
+ # ------------------------------------------------------------------
98
+ # Public API
99
+ # ------------------------------------------------------------------
100
+
101
+ def decompose(self, command: str) -> DecomposedCommand:
102
+ """
103
+ Decompose a raw command string into Stage objects.
104
+
105
+ Args:
106
+ command: Raw shell command string.
107
+
108
+ Returns:
109
+ DecomposedCommand with stages and any extracted substitutions.
110
+ """
111
+ if not command or not command.strip():
112
+ return DecomposedCommand(raw=command or "", stages=[], substitutions=[])
113
+
114
+ command = command.strip()
115
+
116
+ # Extract command substitutions before splitting so they don't
117
+ # interfere with operator detection.
118
+ substitutions = self._extract_substitutions(command)
119
+
120
+ # Walk the command string once to collect (command_text, operator) pairs.
121
+ pairs = self._split_with_operators(command)
122
+
123
+ stages: List[Stage] = []
124
+ for cmd_text, op in pairs:
125
+ cmd_text = cmd_text.strip()
126
+ if not cmd_text:
127
+ continue
128
+ args = self._tokenize_args(cmd_text)
129
+ stages.append(Stage(command=cmd_text, args=args, operator=op))
130
+
131
+ return DecomposedCommand(raw=command, stages=stages, substitutions=substitutions)
132
+
133
+ # ------------------------------------------------------------------
134
+ # Internal: operator-aware splitting
135
+ # ------------------------------------------------------------------
136
+
137
+ def _split_with_operators(self, command: str) -> List[tuple]:
138
+ """
139
+ Walk *command* and return a list of (segment, operator_or_None) tuples.
140
+
141
+ The final segment always has operator=None.
142
+ Quotes and escape sequences are respected so operators inside quoted
143
+ strings or $(...) subshells are not treated as segment boundaries.
144
+ """
145
+ segments: List[tuple] = []
146
+ current: List[str] = []
147
+ i = 0
148
+ n = len(command)
149
+
150
+ in_single_quote = False
151
+ in_double_quote = False
152
+ paren_depth = 0 # tracks $( ... ) nesting
153
+ backtick_depth = 0 # tracks ` ... ` nesting
154
+
155
+ while i < n:
156
+ ch = command[i]
157
+
158
+ # ---- escape sequence (outside single-quotes) ----
159
+ if ch == "\\" and not in_single_quote and i + 1 < n:
160
+ current.append(ch)
161
+ current.append(command[i + 1])
162
+ i += 2
163
+ continue
164
+
165
+ # ---- single quote toggle ----
166
+ if ch == "'" and not in_double_quote:
167
+ in_single_quote = not in_single_quote
168
+ current.append(ch)
169
+ i += 1
170
+ continue
171
+
172
+ # ---- double quote toggle ----
173
+ if ch == '"' and not in_single_quote:
174
+ in_double_quote = not in_double_quote
175
+ current.append(ch)
176
+ i += 1
177
+ continue
178
+
179
+ # ---- inside quotes: pass through ----
180
+ if in_single_quote or in_double_quote:
181
+ current.append(ch)
182
+ i += 1
183
+ continue
184
+
185
+ # ---- $( ... ) command substitution ----
186
+ if ch == "$" and i + 1 < n and command[i + 1] == "(":
187
+ paren_depth += 1
188
+ current.append(ch)
189
+ current.append("(")
190
+ i += 2
191
+ continue
192
+
193
+ if ch == "(" and paren_depth > 0:
194
+ paren_depth += 1
195
+ current.append(ch)
196
+ i += 1
197
+ continue
198
+
199
+ if ch == ")" and paren_depth > 0:
200
+ paren_depth -= 1
201
+ current.append(ch)
202
+ i += 1
203
+ continue
204
+
205
+ # ---- backtick command substitution ----
206
+ if ch == "`":
207
+ if backtick_depth > 0:
208
+ backtick_depth -= 1
209
+ else:
210
+ backtick_depth += 1
211
+ current.append(ch)
212
+ i += 1
213
+ continue
214
+
215
+ # ---- if inside a substitution, pass through ----
216
+ if paren_depth > 0 or backtick_depth > 0:
217
+ current.append(ch)
218
+ i += 1
219
+ continue
220
+
221
+ # ---- two-character operators: &&, || ----
222
+ if i + 1 < n:
223
+ two = command[i : i + 2]
224
+ if two in ("&&", "||"):
225
+ segments.append(("".join(current), two))
226
+ current = []
227
+ i += 2
228
+ continue
229
+
230
+ # ---- single-character operators: |, ;, \n ----
231
+ if ch in ("|", ";", "\n"):
232
+ segments.append(("".join(current), ch))
233
+ current = []
234
+ i += 1
235
+ continue
236
+
237
+ current.append(ch)
238
+ i += 1
239
+
240
+ # Final segment has no following operator.
241
+ if current:
242
+ segments.append(("".join(current), None))
243
+
244
+ return segments
245
+
246
+ # ------------------------------------------------------------------
247
+ # Internal: argument tokenisation
248
+ # ------------------------------------------------------------------
249
+
250
+ def _tokenize_args(self, command_text: str) -> List[str]:
251
+ """
252
+ Split *command_text* into tokens (command + args).
253
+
254
+ Uses a simple quote-aware tokeniser -- does NOT invoke shlex so we
255
+ stay dependency-free and avoid locale issues.
256
+
257
+ Returns a list where element 0 is the executable and the remainder
258
+ are arguments.
259
+ """
260
+ tokens: List[str] = []
261
+ current: List[str] = []
262
+ i = 0
263
+ n = len(command_text)
264
+ in_single = False
265
+ in_double = False
266
+
267
+ while i < n:
268
+ ch = command_text[i]
269
+
270
+ if ch == "\\" and not in_single and i + 1 < n:
271
+ current.append(command_text[i + 1])
272
+ i += 2
273
+ continue
274
+
275
+ if ch == "'" and not in_double:
276
+ in_single = not in_single
277
+ i += 1
278
+ continue
279
+
280
+ if ch == '"' and not in_single:
281
+ in_double = not in_double
282
+ i += 1
283
+ continue
284
+
285
+ if not in_single and not in_double and ch in (" ", "\t"):
286
+ if current:
287
+ tokens.append("".join(current))
288
+ current = []
289
+ i += 1
290
+ continue
291
+
292
+ current.append(ch)
293
+ i += 1
294
+
295
+ if current:
296
+ tokens.append("".join(current))
297
+
298
+ return tokens
299
+
300
+ # ------------------------------------------------------------------
301
+ # Internal: command substitution extraction
302
+ # ------------------------------------------------------------------
303
+
304
+ # Match $(...) -- does not handle arbitrarily deep nesting but covers
305
+ # the common single-level case.
306
+ _SUBST_PAREN_RE = re.compile(r"\$\(([^()]*(?:\([^()]*\)[^()]*)*)\)")
307
+ # Match `...` (non-greedy, no nesting)
308
+ _SUBST_BACKTICK_RE = re.compile(r"`([^`]*)`")
309
+
310
+ def _extract_substitutions(self, command: str) -> List[str]:
311
+ """Return a list of inner strings from $(...) and `...` substitutions."""
312
+ results: List[str] = []
313
+ results.extend(m.group(1).strip() for m in self._SUBST_PAREN_RE.finditer(command))
314
+ results.extend(m.group(1).strip() for m in self._SUBST_BACKTICK_RE.finditer(command))
315
+ return results
@@ -0,0 +1,294 @@
1
+ """
2
+ Task tool validator.
3
+
4
+ Validates Task tool invocations:
5
+ - Agent existence verification
6
+ - Context provisioning enforcement
7
+ - T3 operation detection for user approval workflow
8
+ """
9
+
10
+ import logging
11
+ import re
12
+ from typing import Dict, Any, List, Optional, Tuple
13
+ from dataclasses import dataclass
14
+
15
+ from ..security.tiers import SecurityTier
16
+ from ..security.mutative_verbs import (
17
+ detect_mutative_command,
18
+ MutativeResult,
19
+ CLI_FAMILY_LOOKUP,
20
+ COMMAND_ALIASES,
21
+ )
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+ # Available agents for Task invocation — both bare and plugin-namespaced forms
26
+ _BASE_AGENTS = [
27
+ "terraform-architect",
28
+ "gitops-operator",
29
+ "cloud-troubleshooter",
30
+ "developer",
31
+ "gaia-operator",
32
+ "gaia-system",
33
+ "gaia-planner",
34
+ "Explore",
35
+ "Plan",
36
+ "claude-code-guide",
37
+ "general-purpose",
38
+ ]
39
+ # Support both "cloud-troubleshooter" and "gaia-ops:cloud-troubleshooter"
40
+ AVAILABLE_AGENTS = _BASE_AGENTS + [f"gaia-ops:{a}" for a in _BASE_AGENTS]
41
+
42
+ # Native Claude Code agent types — utility subagents built into the harness,
43
+ # not gaia domain specialists. They don't require context_provider and don't
44
+ # appear in surface routing. They are valid dispatch targets that the
45
+ # orchestrator can legitimately use.
46
+ NATIVE_AGENTS = ["Explore", "Plan", "general-purpose", "claude-code-guide"]
47
+
48
+ # Meta-agents that don't require context_provider.
49
+ # gaia-system was removed: it now receives context injection (has a contract
50
+ # in context-contracts.json) so it can see project state when working on
51
+ # Gaia internals.
52
+ META_AGENTS = list(NATIVE_AGENTS)
53
+
54
+ # T3_KEYWORDS is test-only: used by tests and cross-layer consistency checks
55
+ # to verify that these commands are classified as T3 by the verb detector.
56
+ # NOT used at runtime -- detection is handled entirely by detect_mutative_command().
57
+ T3_KEYWORDS = [
58
+ "git commit",
59
+ "git push",
60
+ "terraform apply",
61
+ "terragrunt apply",
62
+ "terragrunt run-all apply",
63
+ "kubectl apply",
64
+ "kubectl delete",
65
+ "kubectl create",
66
+ "kubectl rollout restart",
67
+ "kubectl scale",
68
+ "kubectl set image",
69
+ "git push origin main",
70
+ "git push origin master",
71
+ "helm install",
72
+ "helm upgrade",
73
+ "flux reconcile",
74
+ "npm publish",
75
+ "docker push",
76
+ "gcloud sql import",
77
+ "gcloud storage cp",
78
+ "gcloud storage rsync",
79
+ ]
80
+
81
+
82
+ _EMBEDDED_COMMAND_QUOTE_CHARS = "\"'`"
83
+
84
+
85
+ def _sanitize_candidate_fragment(fragment: str) -> str:
86
+ """Normalize a prose-embedded command fragment for verb detection.
87
+
88
+ Task prompts often mention commands inside backticks or quotes:
89
+ - Please run `terraform apply` in prod
90
+ - Need to execute "terraform apply" in prod
91
+
92
+ The detector only needs the command skeleton, so strip quote delimiters and
93
+ collapse whitespace before handing the fragment to the dangerous verb
94
+ classifier.
95
+ """
96
+ if not fragment:
97
+ return ""
98
+ cleaned = fragment.translate(str.maketrans({char: " " for char in _EMBEDDED_COMMAND_QUOTE_CHARS}))
99
+ cleaned = re.sub(r"\s+", " ", cleaned).strip()
100
+ return cleaned.rstrip(".,;:!?")
101
+
102
+
103
+ def _extract_command_candidates(text: str) -> List[str]:
104
+ """Extract command-like lines from free-form text for verb detection.
105
+
106
+ Looks for lines that start with known CLI prefixes or contain command-like
107
+ patterns (e.g., "git push", "terraform apply").
108
+
109
+ Args:
110
+ text: Free-form text (prompt or description).
111
+
112
+ Returns:
113
+ List of candidate command strings to scan.
114
+ """
115
+ if not text:
116
+ return []
117
+
118
+ candidates: List[str] = []
119
+ # Derive CLI prefixes from the canonical CLI_FAMILY_LOOKUP and COMMAND_ALIASES
120
+ cli_prefixes = tuple(
121
+ f"{cli} " for cli in sorted(
122
+ set(CLI_FAMILY_LOOKUP.keys()) | set(COMMAND_ALIASES.keys()),
123
+ key=len,
124
+ reverse=True,
125
+ )
126
+ )
127
+
128
+ text_lower = text.lower()
129
+
130
+ # Strategy 1: Scan the full text for known CLI command patterns
131
+ for prefix in cli_prefixes:
132
+ idx = 0
133
+ while True:
134
+ pos = text_lower.find(prefix, idx)
135
+ if pos == -1:
136
+ break
137
+ # Only match at word boundaries (start of string or preceded by whitespace/punctuation)
138
+ if pos > 0 and text_lower[pos - 1].isalnum():
139
+ idx = pos + len(prefix)
140
+ continue
141
+ # Extract from the prefix to end of line (or next sentence boundary)
142
+ end = text.find("\n", pos)
143
+ if end == -1:
144
+ end = len(text)
145
+ fragment = text[pos:end].strip()
146
+ # Trim trailing punctuation/quotes that are part of prose
147
+ fragment = fragment.rstrip(".,;:!?\"')")
148
+ fragment = _sanitize_candidate_fragment(fragment)
149
+ if fragment:
150
+ candidates.append(fragment)
151
+ idx = pos + len(prefix)
152
+
153
+ return candidates
154
+
155
+
156
+ def _scan_text_for_t3(text: str) -> Tuple[bool, str, Optional[MutativeResult]]:
157
+ """Scan free-form text for T3 (dangerous) command intent using the verb detector.
158
+
159
+ Args:
160
+ text: Combined prompt/description text.
161
+
162
+ Returns:
163
+ (is_t3, matched_command, danger_result) tuple.
164
+ """
165
+ candidates = _extract_command_candidates(text)
166
+
167
+ for candidate in candidates:
168
+ result = detect_mutative_command(candidate)
169
+ if result.is_mutative:
170
+ return True, candidate, result
171
+
172
+ return False, "", None
173
+
174
+ __all__ = [
175
+ "TaskValidator",
176
+ "TaskValidationResult",
177
+ "validate_task_invocation",
178
+ "AVAILABLE_AGENTS",
179
+ "META_AGENTS",
180
+ "NATIVE_AGENTS",
181
+ "T3_KEYWORDS",
182
+ ]
183
+
184
+
185
+ @dataclass
186
+ class TaskValidationResult:
187
+ """Result of Task tool validation."""
188
+ allowed: bool
189
+ tier: SecurityTier
190
+ reason: str
191
+ agent_name: str = ""
192
+ has_context: bool = False
193
+ is_t3_operation: bool = False
194
+
195
+
196
+ class TaskValidator:
197
+ """Validator for Task tool invocations."""
198
+
199
+ def __init__(self, available_agents: Optional[List[str]] = None):
200
+ """
201
+ Initialize validator.
202
+
203
+ Args:
204
+ available_agents: Override available agents list
205
+ """
206
+ self.available_agents = available_agents or AVAILABLE_AGENTS
207
+
208
+ def validate(self, parameters: Dict[str, Any]) -> TaskValidationResult:
209
+ """
210
+ Validate Task tool invocation.
211
+
212
+ Args:
213
+ parameters: Task tool parameters
214
+
215
+ Returns:
216
+ TaskValidationResult with validation details
217
+ """
218
+ agent_name = parameters.get("subagent_type", "unknown")
219
+ prompt = parameters.get("prompt", "")
220
+ description = parameters.get("description", "")
221
+
222
+ # additionalContext means prompt is never mutated, so T3 detection
223
+ # runs directly against the original user prompt.
224
+ user_task_for_t3_check = prompt
225
+
226
+ logger.info(f"Task tool validation for agent: {agent_name}")
227
+
228
+ # Check agent exists
229
+ if agent_name not in self.available_agents:
230
+ error_msg = f"Unknown agent: '{agent_name}'\n\n"
231
+ error_msg += f"Available agents:\n"
232
+ for agent in sorted(self.available_agents):
233
+ error_msg += f" - {agent}\n"
234
+ error_msg += "\nRefer to the Surface Routing Recommendation for agent selection.\n"
235
+ error_msg += f"\nCorrect usage: Task(subagent_type=\"<agent-name>\", ...)"
236
+
237
+ return TaskValidationResult(
238
+ allowed=False,
239
+ tier=SecurityTier.T3_BLOCKED,
240
+ reason=error_msg,
241
+ agent_name=agent_name,
242
+ )
243
+
244
+ # Context is injected via additionalContext by the adapter, not by
245
+ # mutating the prompt. The validator cannot check additionalContext
246
+ # (it only sees parameters), so we determine context status by agent type.
247
+ # Meta-agents never receive context by design.
248
+ has_context = agent_name not in META_AGENTS
249
+
250
+ # Check for T3 operations (use original user task to avoid false positives from context)
251
+ is_t3 = self._is_t3_operation(user_task_for_t3_check, description)
252
+
253
+ logger.info(
254
+ f"Task invocation validated: {agent_name} "
255
+ f"(T3={is_t3}, context={has_context})"
256
+ )
257
+
258
+ tier = SecurityTier.T3_BLOCKED if is_t3 else SecurityTier.T0_READ_ONLY
259
+ reason = (
260
+ f"Task invocation allowed for {agent_name}; T3 execution still requires "
261
+ f"nonce-based approval at Bash time"
262
+ if is_t3
263
+ else f"Task invocation allowed for {agent_name}"
264
+ )
265
+
266
+ return TaskValidationResult(
267
+ allowed=True,
268
+ tier=tier,
269
+ reason=reason,
270
+ agent_name=agent_name,
271
+ has_context=has_context,
272
+ is_t3_operation=is_t3,
273
+ )
274
+
275
+ def _is_t3_operation(self, prompt: str, description: str) -> bool:
276
+ """Check if this is a T3 (destructive) operation using the verb detector."""
277
+ combined = f"{description} {prompt}"
278
+ is_t3, _, _ = _scan_text_for_t3(combined)
279
+ return is_t3
280
+
281
+
282
+
283
+ def validate_task_invocation(parameters: Dict[str, Any]) -> TaskValidationResult:
284
+ """
285
+ Validate Task tool invocation (convenience function).
286
+
287
+ Args:
288
+ parameters: Task tool parameters
289
+
290
+ Returns:
291
+ TaskValidationResult
292
+ """
293
+ validator = TaskValidator()
294
+ return validator.validate(parameters)
@@ -0,0 +1,23 @@
1
+ """
2
+ Validation Module: Commit message validation for bash_validator
3
+
4
+ This module provides commit message validation that is exclusively used
5
+ by hooks/modules/tools/bash_validator.py to enforce git commit standards.
6
+
7
+ Note: This is an internal module. Do not import directly in agent code.
8
+ Commit validation is automatically enforced via bash_validator.py.
9
+ """
10
+
11
+ from .commit_validator import (
12
+ CommitMessageValidator,
13
+ ValidationResult,
14
+ validate_commit_message,
15
+ safe_validate_before_commit,
16
+ )
17
+
18
+ __all__ = [
19
+ "CommitMessageValidator",
20
+ "ValidationResult",
21
+ "validate_commit_message",
22
+ "safe_validate_before_commit",
23
+ ]