@jaguilar87/gaia 5.0.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (621) 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 +1298 -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 +111 -0
  16. package/agents/gaia-planner.md +53 -0
  17. package/agents/gaia-system.md +71 -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 +651 -0
  26. package/bin/cli/history.py +305 -0
  27. package/bin/cli/memory.py +483 -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 +919 -0
  45. package/bin/pre-publish-validate.js +610 -0
  46. package/bin/python-detect.js +60 -0
  47. package/bin/validate-sandbox.sh +601 -0
  48. package/commands/README.md +64 -0
  49. package/commands/gaia.md +37 -0
  50. package/commands/scan-project.md +67 -0
  51. package/config/README.md +71 -0
  52. package/config/cloud/aws.json +134 -0
  53. package/config/cloud/gcp.json +139 -0
  54. package/config/context-contracts.json +158 -0
  55. package/config/crons-schema.md +81 -0
  56. package/config/git_standards.json +72 -0
  57. package/config/surface-routing.json +417 -0
  58. package/config/universal-rules.json +102 -0
  59. package/dist/gaia-ops/.claude-plugin/plugin.json +24 -0
  60. package/dist/gaia-ops/README.md +80 -0
  61. package/dist/gaia-ops/agents/cloud-troubleshooter.md +73 -0
  62. package/dist/gaia-ops/agents/developer.md +65 -0
  63. package/dist/gaia-ops/agents/gaia-operator.md +64 -0
  64. package/dist/gaia-ops/agents/gaia-orchestrator.md +111 -0
  65. package/dist/gaia-ops/agents/gaia-planner.md +53 -0
  66. package/dist/gaia-ops/agents/gaia-system.md +71 -0
  67. package/dist/gaia-ops/agents/gitops-operator.md +61 -0
  68. package/dist/gaia-ops/agents/terraform-architect.md +63 -0
  69. package/dist/gaia-ops/commands/gaia.md +37 -0
  70. package/dist/gaia-ops/config/README.md +71 -0
  71. package/dist/gaia-ops/config/cloud/aws.json +134 -0
  72. package/dist/gaia-ops/config/cloud/gcp.json +139 -0
  73. package/dist/gaia-ops/config/context-contracts.json +158 -0
  74. package/dist/gaia-ops/config/crons-schema.md +81 -0
  75. package/dist/gaia-ops/config/git_standards.json +72 -0
  76. package/dist/gaia-ops/config/surface-routing.json +417 -0
  77. package/dist/gaia-ops/config/universal-rules.json +102 -0
  78. package/dist/gaia-ops/hooks/adapters/__init__.py +52 -0
  79. package/dist/gaia-ops/hooks/adapters/base.py +219 -0
  80. package/dist/gaia-ops/hooks/adapters/channel.py +17 -0
  81. package/dist/gaia-ops/hooks/adapters/claude_code.py +1890 -0
  82. package/dist/gaia-ops/hooks/adapters/types.py +194 -0
  83. package/dist/gaia-ops/hooks/adapters/utils.py +25 -0
  84. package/dist/gaia-ops/hooks/hooks.json +192 -0
  85. package/dist/gaia-ops/hooks/modules/__init__.py +15 -0
  86. package/dist/gaia-ops/hooks/modules/agents/__init__.py +29 -0
  87. package/dist/gaia-ops/hooks/modules/agents/contract_validator.py +647 -0
  88. package/dist/gaia-ops/hooks/modules/agents/response_contract.py +496 -0
  89. package/dist/gaia-ops/hooks/modules/agents/skill_injection_verifier.py +120 -0
  90. package/dist/gaia-ops/hooks/modules/agents/state_tracker.py +267 -0
  91. package/dist/gaia-ops/hooks/modules/agents/task_info_builder.py +74 -0
  92. package/dist/gaia-ops/hooks/modules/agents/transcript_analyzer.py +458 -0
  93. package/dist/gaia-ops/hooks/modules/agents/transcript_reader.py +152 -0
  94. package/dist/gaia-ops/hooks/modules/audit/__init__.py +28 -0
  95. package/dist/gaia-ops/hooks/modules/audit/event_detector.py +168 -0
  96. package/dist/gaia-ops/hooks/modules/audit/logger.py +131 -0
  97. package/dist/gaia-ops/hooks/modules/audit/metrics.py +134 -0
  98. package/dist/gaia-ops/hooks/modules/audit/workflow_auditor.py +611 -0
  99. package/dist/gaia-ops/hooks/modules/audit/workflow_recorder.py +296 -0
  100. package/dist/gaia-ops/hooks/modules/context/__init__.py +11 -0
  101. package/dist/gaia-ops/hooks/modules/context/agentic_loop_detector.py +165 -0
  102. package/dist/gaia-ops/hooks/modules/context/anchor_tracker.py +317 -0
  103. package/dist/gaia-ops/hooks/modules/context/compact_context_builder.py +218 -0
  104. package/dist/gaia-ops/hooks/modules/context/context_freshness.py +145 -0
  105. package/dist/gaia-ops/hooks/modules/context/context_injector.py +558 -0
  106. package/dist/gaia-ops/hooks/modules/context/context_writer.py +530 -0
  107. package/dist/gaia-ops/hooks/modules/context/contracts_loader.py +161 -0
  108. package/dist/gaia-ops/hooks/modules/core/__init__.py +40 -0
  109. package/dist/gaia-ops/hooks/modules/core/hook_entry.py +78 -0
  110. package/dist/gaia-ops/hooks/modules/core/paths.py +160 -0
  111. package/dist/gaia-ops/hooks/modules/core/plugin_mode.py +149 -0
  112. package/dist/gaia-ops/hooks/modules/core/plugin_setup.py +577 -0
  113. package/dist/gaia-ops/hooks/modules/core/state.py +179 -0
  114. package/dist/gaia-ops/hooks/modules/core/stdin.py +24 -0
  115. package/dist/gaia-ops/hooks/modules/events/__init__.py +1 -0
  116. package/dist/gaia-ops/hooks/modules/events/event_writer.py +210 -0
  117. package/dist/gaia-ops/hooks/modules/memory/__init__.py +8 -0
  118. package/dist/gaia-ops/hooks/modules/memory/episode_writer.py +216 -0
  119. package/dist/gaia-ops/hooks/modules/orchestrator/__init__.py +1 -0
  120. package/dist/gaia-ops/hooks/modules/orchestrator/delegate_mode.py +122 -0
  121. package/dist/gaia-ops/hooks/modules/scanning/__init__.py +8 -0
  122. package/dist/gaia-ops/hooks/modules/scanning/scan_trigger.py +84 -0
  123. package/dist/gaia-ops/hooks/modules/security/__init__.py +120 -0
  124. package/dist/gaia-ops/hooks/modules/security/approval_cleanup.py +87 -0
  125. package/dist/gaia-ops/hooks/modules/security/approval_constants.py +23 -0
  126. package/dist/gaia-ops/hooks/modules/security/approval_grants.py +1638 -0
  127. package/dist/gaia-ops/hooks/modules/security/approval_messages.py +71 -0
  128. package/dist/gaia-ops/hooks/modules/security/approval_scopes.py +222 -0
  129. package/dist/gaia-ops/hooks/modules/security/blocked_commands.py +595 -0
  130. package/dist/gaia-ops/hooks/modules/security/blocked_message_formatter.py +87 -0
  131. package/dist/gaia-ops/hooks/modules/security/command_semantics.py +181 -0
  132. package/dist/gaia-ops/hooks/modules/security/composition_rules.py +547 -0
  133. package/dist/gaia-ops/hooks/modules/security/flag_classifiers.py +873 -0
  134. package/dist/gaia-ops/hooks/modules/security/gitops_validator.py +179 -0
  135. package/dist/gaia-ops/hooks/modules/security/mutative_verbs.py +1131 -0
  136. package/dist/gaia-ops/hooks/modules/security/network_hosts.py +481 -0
  137. package/dist/gaia-ops/hooks/modules/security/prompt_validator.py +40 -0
  138. package/dist/gaia-ops/hooks/modules/security/shell_unwrapper.py +165 -0
  139. package/dist/gaia-ops/hooks/modules/security/tiers.py +196 -0
  140. package/dist/gaia-ops/hooks/modules/session/__init__.py +10 -0
  141. package/dist/gaia-ops/hooks/modules/session/pending_scanner.py +174 -0
  142. package/dist/gaia-ops/hooks/modules/session/session_context_writer.py +100 -0
  143. package/dist/gaia-ops/hooks/modules/session/session_event_injector.py +160 -0
  144. package/dist/gaia-ops/hooks/modules/session/session_manager.py +31 -0
  145. package/dist/gaia-ops/hooks/modules/session/session_registry.py +333 -0
  146. package/dist/gaia-ops/hooks/modules/tools/__init__.py +29 -0
  147. package/dist/gaia-ops/hooks/modules/tools/bash_validator.py +1008 -0
  148. package/dist/gaia-ops/hooks/modules/tools/cloud_pipe_validator.py +231 -0
  149. package/dist/gaia-ops/hooks/modules/tools/hook_response.py +55 -0
  150. package/dist/gaia-ops/hooks/modules/tools/shell_parser.py +227 -0
  151. package/dist/gaia-ops/hooks/modules/tools/stage_decomposer.py +315 -0
  152. package/dist/gaia-ops/hooks/modules/tools/task_validator.py +294 -0
  153. package/dist/gaia-ops/hooks/modules/validation/__init__.py +23 -0
  154. package/dist/gaia-ops/hooks/modules/validation/commit_validator.py +380 -0
  155. package/dist/gaia-ops/hooks/post_compact.py +43 -0
  156. package/dist/gaia-ops/hooks/post_tool_use.py +54 -0
  157. package/dist/gaia-ops/hooks/pre_compact.py +60 -0
  158. package/dist/gaia-ops/hooks/pre_tool_use.py +413 -0
  159. package/dist/gaia-ops/hooks/session_end_hook.py +77 -0
  160. package/dist/gaia-ops/hooks/session_start.py +81 -0
  161. package/dist/gaia-ops/hooks/stop_hook.py +70 -0
  162. package/dist/gaia-ops/hooks/subagent_start.py +71 -0
  163. package/dist/gaia-ops/hooks/subagent_stop.py +295 -0
  164. package/dist/gaia-ops/hooks/task_completed.py +70 -0
  165. package/dist/gaia-ops/hooks/user_prompt_submit.py +246 -0
  166. package/dist/gaia-ops/settings.json +72 -0
  167. package/dist/gaia-ops/skills/README.md +158 -0
  168. package/dist/gaia-ops/skills/agent-creation/SKILL.md +87 -0
  169. package/dist/gaia-ops/skills/agent-creation/examples.md +170 -0
  170. package/dist/gaia-ops/skills/agent-creation/reference.md +191 -0
  171. package/dist/gaia-ops/skills/agent-protocol/SKILL.md +93 -0
  172. package/dist/gaia-ops/skills/agent-protocol/examples.md +223 -0
  173. package/dist/gaia-ops/skills/agent-response/SKILL.md +69 -0
  174. package/dist/gaia-ops/skills/agentic-loop/SKILL.md +80 -0
  175. package/dist/gaia-ops/skills/agentic-loop/reference.md +378 -0
  176. package/dist/gaia-ops/skills/blog-writing/SKILL.md +98 -0
  177. package/dist/gaia-ops/skills/blog-writing/reference.md +130 -0
  178. package/dist/gaia-ops/skills/brief-spec/SKILL.md +185 -0
  179. package/dist/gaia-ops/skills/command-execution/SKILL.md +64 -0
  180. package/dist/gaia-ops/skills/command-execution/reference.md +83 -0
  181. package/dist/gaia-ops/skills/context-updater/SKILL.md +87 -0
  182. package/dist/gaia-ops/skills/context-updater/examples.md +71 -0
  183. package/dist/gaia-ops/skills/developer-patterns/SKILL.md +50 -0
  184. package/dist/gaia-ops/skills/developer-patterns/reference.md +112 -0
  185. package/dist/gaia-ops/skills/execution/SKILL.md +99 -0
  186. package/dist/gaia-ops/skills/fast-queries/SKILL.md +43 -0
  187. package/dist/gaia-ops/skills/gaia-compact/SKILL.md +74 -0
  188. package/dist/gaia-ops/skills/gaia-patterns/SKILL.md +108 -0
  189. package/dist/gaia-ops/skills/gaia-patterns/reference.md +395 -0
  190. package/dist/gaia-ops/skills/gaia-planner/SKILL.md +37 -0
  191. package/dist/gaia-ops/skills/gaia-planner/reference.md +107 -0
  192. package/dist/gaia-ops/skills/gaia-release/SKILL.md +85 -0
  193. package/dist/gaia-ops/skills/gaia-release/reference.md +92 -0
  194. package/dist/gaia-ops/skills/gaia-self-check/SKILL.md +114 -0
  195. package/dist/gaia-ops/skills/gaia-self-check/reference.md +453 -0
  196. package/dist/gaia-ops/skills/gaia-verify/SKILL.md +77 -0
  197. package/dist/gaia-ops/skills/gaia-verify/reference.md +80 -0
  198. package/dist/gaia-ops/skills/git-conventions/SKILL.md +47 -0
  199. package/dist/gaia-ops/skills/gitops-patterns/SKILL.md +60 -0
  200. package/dist/gaia-ops/skills/gitops-patterns/reference.md +183 -0
  201. package/dist/gaia-ops/skills/gmail-policy/SKILL.md +200 -0
  202. package/dist/gaia-ops/skills/gmail-policy/reference.md +150 -0
  203. package/dist/gaia-ops/skills/gmail-triage/SKILL.md +100 -0
  204. package/dist/gaia-ops/skills/gws-setup/SKILL.md +99 -0
  205. package/dist/gaia-ops/skills/gws-setup/reference.md +73 -0
  206. package/dist/gaia-ops/skills/investigation/SKILL.md +100 -0
  207. package/dist/gaia-ops/skills/memory-curation/SKILL.md +83 -0
  208. package/dist/gaia-ops/skills/memory-search/SKILL.md +88 -0
  209. package/dist/gaia-ops/skills/orchestrator-approval/SKILL.md +160 -0
  210. package/dist/gaia-ops/skills/orchestrator-approval/reference.md +174 -0
  211. package/dist/gaia-ops/skills/pending-approvals/SKILL.md +72 -0
  212. package/dist/gaia-ops/skills/pending-approvals/reference.md +214 -0
  213. package/dist/gaia-ops/skills/readme-writing/SKILL.md +71 -0
  214. package/dist/gaia-ops/skills/readme-writing/reference.md +188 -0
  215. package/dist/gaia-ops/skills/reference.md +135 -0
  216. package/dist/gaia-ops/skills/request-approval/SKILL.md +140 -0
  217. package/dist/gaia-ops/skills/request-approval/examples.md +140 -0
  218. package/dist/gaia-ops/skills/request-approval/reference.md +57 -0
  219. package/dist/gaia-ops/skills/schedule-task/SKILL.md +64 -0
  220. package/dist/gaia-ops/skills/schedule-task/reference.md +233 -0
  221. package/dist/gaia-ops/skills/security-tiers/SKILL.md +141 -0
  222. package/dist/gaia-ops/skills/security-tiers/destructive-commands-reference.md +623 -0
  223. package/dist/gaia-ops/skills/security-tiers/reference.md +39 -0
  224. package/dist/gaia-ops/skills/session-reflection/SKILL.md +69 -0
  225. package/dist/gaia-ops/skills/skill-creation/SKILL.md +92 -0
  226. package/dist/gaia-ops/skills/skill-creation/reference.md +29 -0
  227. package/dist/gaia-ops/skills/terraform-patterns/SKILL.md +89 -0
  228. package/dist/gaia-ops/skills/terraform-patterns/reference.md +93 -0
  229. package/dist/gaia-ops/tools/__init__.py +9 -0
  230. package/dist/gaia-ops/tools/agentic-loop/decide-status.py +210 -0
  231. package/dist/gaia-ops/tools/agentic-loop/parse-metric.py +106 -0
  232. package/dist/gaia-ops/tools/agentic-loop/record-iteration.py +221 -0
  233. package/dist/gaia-ops/tools/context/README.md +132 -0
  234. package/dist/gaia-ops/tools/context/__init__.py +42 -0
  235. package/dist/gaia-ops/tools/context/_paths.py +20 -0
  236. package/dist/gaia-ops/tools/context/context_provider.py +721 -0
  237. package/dist/gaia-ops/tools/context/context_section_reader.py +342 -0
  238. package/dist/gaia-ops/tools/context/deep_merge.py +159 -0
  239. package/dist/gaia-ops/tools/context/pending_updates.py +760 -0
  240. package/dist/gaia-ops/tools/context/surface_router.py +278 -0
  241. package/dist/gaia-ops/tools/fast-queries/README.md +65 -0
  242. package/dist/gaia-ops/tools/fast-queries/__init__.py +30 -0
  243. package/dist/gaia-ops/tools/fast-queries/appservices/quicktriage_devops_developer.sh +75 -0
  244. package/dist/gaia-ops/tools/fast-queries/cloud/aws/quicktriage_aws_troubleshooter.sh +32 -0
  245. package/dist/gaia-ops/tools/fast-queries/cloud/gcp/quicktriage_gcp_troubleshooter.sh +88 -0
  246. package/dist/gaia-ops/tools/fast-queries/gitops/quicktriage_gitops_operator.sh +48 -0
  247. package/dist/gaia-ops/tools/fast-queries/run_triage.sh +59 -0
  248. package/dist/gaia-ops/tools/fast-queries/terraform/quicktriage_terraform_architect.sh +80 -0
  249. package/dist/gaia-ops/tools/gaia_simulator/__init__.py +33 -0
  250. package/dist/gaia-ops/tools/gaia_simulator/cli.py +354 -0
  251. package/dist/gaia-ops/tools/gaia_simulator/extractor.py +457 -0
  252. package/dist/gaia-ops/tools/gaia_simulator/reporter.py +258 -0
  253. package/dist/gaia-ops/tools/gaia_simulator/routing_simulator.py +334 -0
  254. package/dist/gaia-ops/tools/gaia_simulator/runner.py +539 -0
  255. package/dist/gaia-ops/tools/gaia_simulator/skills_mapper.py +264 -0
  256. package/dist/gaia-ops/tools/memory/README.md +0 -0
  257. package/dist/gaia-ops/tools/memory/__init__.py +20 -0
  258. package/dist/gaia-ops/tools/memory/backfill_fts5.py +107 -0
  259. package/dist/gaia-ops/tools/memory/conflict_detector.py +295 -0
  260. package/dist/gaia-ops/tools/memory/episodic.py +1210 -0
  261. package/dist/gaia-ops/tools/memory/git_invalidator.py +262 -0
  262. package/dist/gaia-ops/tools/memory/paths.py +102 -0
  263. package/dist/gaia-ops/tools/memory/scoring.py +193 -0
  264. package/dist/gaia-ops/tools/memory/search_store.py +375 -0
  265. package/dist/gaia-ops/tools/persist_transcript_analysis.py +85 -0
  266. package/dist/gaia-ops/tools/review/__init__.py +1 -0
  267. package/dist/gaia-ops/tools/review/review_engine.py +157 -0
  268. package/dist/gaia-ops/tools/scan/__init__.py +35 -0
  269. package/dist/gaia-ops/tools/scan/config.py +247 -0
  270. package/dist/gaia-ops/tools/scan/merge.py +212 -0
  271. package/dist/gaia-ops/tools/scan/orchestrator.py +549 -0
  272. package/dist/gaia-ops/tools/scan/registry.py +127 -0
  273. package/dist/gaia-ops/tools/scan/scanners/__init__.py +18 -0
  274. package/dist/gaia-ops/tools/scan/scanners/base.py +137 -0
  275. package/dist/gaia-ops/tools/scan/scanners/environment.py +349 -0
  276. package/dist/gaia-ops/tools/scan/scanners/git.py +570 -0
  277. package/dist/gaia-ops/tools/scan/scanners/infrastructure.py +875 -0
  278. package/dist/gaia-ops/tools/scan/scanners/orchestration.py +600 -0
  279. package/dist/gaia-ops/tools/scan/scanners/stack.py +1085 -0
  280. package/dist/gaia-ops/tools/scan/scanners/tools.py +260 -0
  281. package/dist/gaia-ops/tools/scan/setup.py +686 -0
  282. package/dist/gaia-ops/tools/scan/tests/__init__.py +1 -0
  283. package/dist/gaia-ops/tools/scan/tests/conftest.py +796 -0
  284. package/dist/gaia-ops/tools/scan/tests/test_environment.py +323 -0
  285. package/dist/gaia-ops/tools/scan/tests/test_git.py +419 -0
  286. package/dist/gaia-ops/tools/scan/tests/test_infrastructure.py +382 -0
  287. package/dist/gaia-ops/tools/scan/tests/test_integration.py +920 -0
  288. package/dist/gaia-ops/tools/scan/tests/test_merge.py +269 -0
  289. package/dist/gaia-ops/tools/scan/tests/test_orchestration.py +304 -0
  290. package/dist/gaia-ops/tools/scan/tests/test_stack.py +604 -0
  291. package/dist/gaia-ops/tools/scan/tests/test_tools.py +349 -0
  292. package/dist/gaia-ops/tools/scan/ui.py +624 -0
  293. package/dist/gaia-ops/tools/scan/verify.py +270 -0
  294. package/dist/gaia-ops/tools/scan/walk.py +118 -0
  295. package/dist/gaia-ops/tools/scan/workspace.py +85 -0
  296. package/dist/gaia-ops/tools/validation/README.md +244 -0
  297. package/dist/gaia-ops/tools/validation/__init__.py +17 -0
  298. package/dist/gaia-ops/tools/validation/approval_gate.py +321 -0
  299. package/dist/gaia-ops/tools/validation/validate_skills.py +189 -0
  300. package/dist/gaia-security/.claude-plugin/plugin.json +24 -0
  301. package/dist/gaia-security/README.md +90 -0
  302. package/dist/gaia-security/config/universal-rules.json +102 -0
  303. package/dist/gaia-security/hooks/adapters/__init__.py +52 -0
  304. package/dist/gaia-security/hooks/adapters/base.py +219 -0
  305. package/dist/gaia-security/hooks/adapters/channel.py +17 -0
  306. package/dist/gaia-security/hooks/adapters/claude_code.py +1890 -0
  307. package/dist/gaia-security/hooks/adapters/types.py +194 -0
  308. package/dist/gaia-security/hooks/adapters/utils.py +25 -0
  309. package/dist/gaia-security/hooks/hooks.json +113 -0
  310. package/dist/gaia-security/hooks/modules/__init__.py +15 -0
  311. package/dist/gaia-security/hooks/modules/agents/__init__.py +29 -0
  312. package/dist/gaia-security/hooks/modules/agents/contract_validator.py +647 -0
  313. package/dist/gaia-security/hooks/modules/agents/response_contract.py +496 -0
  314. package/dist/gaia-security/hooks/modules/agents/skill_injection_verifier.py +120 -0
  315. package/dist/gaia-security/hooks/modules/agents/state_tracker.py +267 -0
  316. package/dist/gaia-security/hooks/modules/agents/task_info_builder.py +74 -0
  317. package/dist/gaia-security/hooks/modules/agents/transcript_analyzer.py +458 -0
  318. package/dist/gaia-security/hooks/modules/agents/transcript_reader.py +152 -0
  319. package/dist/gaia-security/hooks/modules/audit/__init__.py +28 -0
  320. package/dist/gaia-security/hooks/modules/audit/event_detector.py +168 -0
  321. package/dist/gaia-security/hooks/modules/audit/logger.py +131 -0
  322. package/dist/gaia-security/hooks/modules/audit/metrics.py +134 -0
  323. package/dist/gaia-security/hooks/modules/audit/workflow_auditor.py +611 -0
  324. package/dist/gaia-security/hooks/modules/audit/workflow_recorder.py +296 -0
  325. package/dist/gaia-security/hooks/modules/context/__init__.py +11 -0
  326. package/dist/gaia-security/hooks/modules/context/agentic_loop_detector.py +165 -0
  327. package/dist/gaia-security/hooks/modules/context/anchor_tracker.py +317 -0
  328. package/dist/gaia-security/hooks/modules/context/compact_context_builder.py +218 -0
  329. package/dist/gaia-security/hooks/modules/context/context_freshness.py +145 -0
  330. package/dist/gaia-security/hooks/modules/context/context_injector.py +558 -0
  331. package/dist/gaia-security/hooks/modules/context/context_writer.py +530 -0
  332. package/dist/gaia-security/hooks/modules/context/contracts_loader.py +161 -0
  333. package/dist/gaia-security/hooks/modules/core/__init__.py +40 -0
  334. package/dist/gaia-security/hooks/modules/core/hook_entry.py +78 -0
  335. package/dist/gaia-security/hooks/modules/core/paths.py +160 -0
  336. package/dist/gaia-security/hooks/modules/core/plugin_mode.py +149 -0
  337. package/dist/gaia-security/hooks/modules/core/plugin_setup.py +577 -0
  338. package/dist/gaia-security/hooks/modules/core/state.py +179 -0
  339. package/dist/gaia-security/hooks/modules/core/stdin.py +24 -0
  340. package/dist/gaia-security/hooks/modules/events/__init__.py +1 -0
  341. package/dist/gaia-security/hooks/modules/events/event_writer.py +210 -0
  342. package/dist/gaia-security/hooks/modules/memory/__init__.py +8 -0
  343. package/dist/gaia-security/hooks/modules/memory/episode_writer.py +216 -0
  344. package/dist/gaia-security/hooks/modules/orchestrator/__init__.py +1 -0
  345. package/dist/gaia-security/hooks/modules/orchestrator/delegate_mode.py +122 -0
  346. package/dist/gaia-security/hooks/modules/scanning/__init__.py +8 -0
  347. package/dist/gaia-security/hooks/modules/scanning/scan_trigger.py +84 -0
  348. package/dist/gaia-security/hooks/modules/security/__init__.py +120 -0
  349. package/dist/gaia-security/hooks/modules/security/approval_cleanup.py +87 -0
  350. package/dist/gaia-security/hooks/modules/security/approval_constants.py +23 -0
  351. package/dist/gaia-security/hooks/modules/security/approval_grants.py +1638 -0
  352. package/dist/gaia-security/hooks/modules/security/approval_messages.py +71 -0
  353. package/dist/gaia-security/hooks/modules/security/approval_scopes.py +222 -0
  354. package/dist/gaia-security/hooks/modules/security/blocked_commands.py +595 -0
  355. package/dist/gaia-security/hooks/modules/security/blocked_message_formatter.py +87 -0
  356. package/dist/gaia-security/hooks/modules/security/command_semantics.py +181 -0
  357. package/dist/gaia-security/hooks/modules/security/composition_rules.py +547 -0
  358. package/dist/gaia-security/hooks/modules/security/flag_classifiers.py +873 -0
  359. package/dist/gaia-security/hooks/modules/security/gitops_validator.py +179 -0
  360. package/dist/gaia-security/hooks/modules/security/mutative_verbs.py +1131 -0
  361. package/dist/gaia-security/hooks/modules/security/network_hosts.py +481 -0
  362. package/dist/gaia-security/hooks/modules/security/prompt_validator.py +40 -0
  363. package/dist/gaia-security/hooks/modules/security/shell_unwrapper.py +165 -0
  364. package/dist/gaia-security/hooks/modules/security/tiers.py +196 -0
  365. package/dist/gaia-security/hooks/modules/session/__init__.py +10 -0
  366. package/dist/gaia-security/hooks/modules/session/pending_scanner.py +174 -0
  367. package/dist/gaia-security/hooks/modules/session/session_context_writer.py +100 -0
  368. package/dist/gaia-security/hooks/modules/session/session_event_injector.py +160 -0
  369. package/dist/gaia-security/hooks/modules/session/session_manager.py +31 -0
  370. package/dist/gaia-security/hooks/modules/session/session_registry.py +333 -0
  371. package/dist/gaia-security/hooks/modules/tools/__init__.py +29 -0
  372. package/dist/gaia-security/hooks/modules/tools/bash_validator.py +1008 -0
  373. package/dist/gaia-security/hooks/modules/tools/cloud_pipe_validator.py +231 -0
  374. package/dist/gaia-security/hooks/modules/tools/hook_response.py +55 -0
  375. package/dist/gaia-security/hooks/modules/tools/shell_parser.py +227 -0
  376. package/dist/gaia-security/hooks/modules/tools/stage_decomposer.py +315 -0
  377. package/dist/gaia-security/hooks/modules/tools/task_validator.py +294 -0
  378. package/dist/gaia-security/hooks/modules/validation/__init__.py +23 -0
  379. package/dist/gaia-security/hooks/modules/validation/commit_validator.py +380 -0
  380. package/dist/gaia-security/hooks/post_tool_use.py +54 -0
  381. package/dist/gaia-security/hooks/pre_tool_use.py +413 -0
  382. package/dist/gaia-security/hooks/session_end_hook.py +77 -0
  383. package/dist/gaia-security/hooks/session_start.py +81 -0
  384. package/dist/gaia-security/hooks/stop_hook.py +70 -0
  385. package/dist/gaia-security/hooks/user_prompt_submit.py +246 -0
  386. package/dist/gaia-security/settings.json +58 -0
  387. package/git-hooks/commit-msg +41 -0
  388. package/hooks/README.md +100 -0
  389. package/hooks/adapters/__init__.py +52 -0
  390. package/hooks/adapters/base.py +219 -0
  391. package/hooks/adapters/channel.py +17 -0
  392. package/hooks/adapters/claude_code.py +1890 -0
  393. package/hooks/adapters/types.py +194 -0
  394. package/hooks/adapters/utils.py +25 -0
  395. package/hooks/elicitation_result.py +179 -0
  396. package/hooks/hooks.json +84 -0
  397. package/hooks/modules/README.md +189 -0
  398. package/hooks/modules/__init__.py +15 -0
  399. package/hooks/modules/agents/__init__.py +29 -0
  400. package/hooks/modules/agents/contract_validator.py +647 -0
  401. package/hooks/modules/agents/response_contract.py +496 -0
  402. package/hooks/modules/agents/skill_injection_verifier.py +120 -0
  403. package/hooks/modules/agents/state_tracker.py +267 -0
  404. package/hooks/modules/agents/task_info_builder.py +74 -0
  405. package/hooks/modules/agents/transcript_analyzer.py +458 -0
  406. package/hooks/modules/agents/transcript_reader.py +152 -0
  407. package/hooks/modules/audit/__init__.py +28 -0
  408. package/hooks/modules/audit/event_detector.py +168 -0
  409. package/hooks/modules/audit/logger.py +131 -0
  410. package/hooks/modules/audit/metrics.py +134 -0
  411. package/hooks/modules/audit/workflow_auditor.py +611 -0
  412. package/hooks/modules/audit/workflow_recorder.py +296 -0
  413. package/hooks/modules/context/__init__.py +11 -0
  414. package/hooks/modules/context/agentic_loop_detector.py +165 -0
  415. package/hooks/modules/context/anchor_tracker.py +317 -0
  416. package/hooks/modules/context/compact_context_builder.py +218 -0
  417. package/hooks/modules/context/context_freshness.py +145 -0
  418. package/hooks/modules/context/context_injector.py +558 -0
  419. package/hooks/modules/context/context_writer.py +530 -0
  420. package/hooks/modules/context/contracts_loader.py +161 -0
  421. package/hooks/modules/core/__init__.py +40 -0
  422. package/hooks/modules/core/hook_entry.py +78 -0
  423. package/hooks/modules/core/paths.py +160 -0
  424. package/hooks/modules/core/plugin_mode.py +149 -0
  425. package/hooks/modules/core/plugin_setup.py +577 -0
  426. package/hooks/modules/core/state.py +179 -0
  427. package/hooks/modules/core/stdin.py +24 -0
  428. package/hooks/modules/events/__init__.py +1 -0
  429. package/hooks/modules/events/event_writer.py +210 -0
  430. package/hooks/modules/evidence/__init__.py +34 -0
  431. package/hooks/modules/evidence/assertions.py +137 -0
  432. package/hooks/modules/evidence/index_writer.py +57 -0
  433. package/hooks/modules/evidence/loader.py +126 -0
  434. package/hooks/modules/evidence/runner.py +241 -0
  435. package/hooks/modules/memory/__init__.py +8 -0
  436. package/hooks/modules/memory/episode_writer.py +216 -0
  437. package/hooks/modules/orchestrator/__init__.py +1 -0
  438. package/hooks/modules/orchestrator/delegate_mode.py +122 -0
  439. package/hooks/modules/scanning/__init__.py +8 -0
  440. package/hooks/modules/scanning/scan_trigger.py +84 -0
  441. package/hooks/modules/security/__init__.py +120 -0
  442. package/hooks/modules/security/approval_cleanup.py +87 -0
  443. package/hooks/modules/security/approval_constants.py +23 -0
  444. package/hooks/modules/security/approval_grants.py +1638 -0
  445. package/hooks/modules/security/approval_messages.py +71 -0
  446. package/hooks/modules/security/approval_scopes.py +222 -0
  447. package/hooks/modules/security/blocked_commands.py +595 -0
  448. package/hooks/modules/security/blocked_message_formatter.py +87 -0
  449. package/hooks/modules/security/command_semantics.py +181 -0
  450. package/hooks/modules/security/composition_rules.py +547 -0
  451. package/hooks/modules/security/flag_classifiers.py +873 -0
  452. package/hooks/modules/security/gitops_validator.py +179 -0
  453. package/hooks/modules/security/mutative_verbs.py +1131 -0
  454. package/hooks/modules/security/network_hosts.py +481 -0
  455. package/hooks/modules/security/prompt_validator.py +40 -0
  456. package/hooks/modules/security/shell_unwrapper.py +165 -0
  457. package/hooks/modules/security/tiers.py +196 -0
  458. package/hooks/modules/session/__init__.py +10 -0
  459. package/hooks/modules/session/pending_scanner.py +174 -0
  460. package/hooks/modules/session/session_context_writer.py +100 -0
  461. package/hooks/modules/session/session_event_injector.py +160 -0
  462. package/hooks/modules/session/session_manager.py +31 -0
  463. package/hooks/modules/session/session_registry.py +333 -0
  464. package/hooks/modules/tools/__init__.py +29 -0
  465. package/hooks/modules/tools/bash_validator.py +1008 -0
  466. package/hooks/modules/tools/cloud_pipe_validator.py +231 -0
  467. package/hooks/modules/tools/hook_response.py +55 -0
  468. package/hooks/modules/tools/shell_parser.py +227 -0
  469. package/hooks/modules/tools/stage_decomposer.py +315 -0
  470. package/hooks/modules/tools/task_validator.py +294 -0
  471. package/hooks/modules/validation/__init__.py +23 -0
  472. package/hooks/modules/validation/commit_validator.py +380 -0
  473. package/hooks/post_compact.py +43 -0
  474. package/hooks/post_tool_use.py +54 -0
  475. package/hooks/pre_compact.py +60 -0
  476. package/hooks/pre_tool_use.py +413 -0
  477. package/hooks/session_end_hook.py +77 -0
  478. package/hooks/session_start.py +81 -0
  479. package/hooks/stop_hook.py +70 -0
  480. package/hooks/subagent_start.py +71 -0
  481. package/hooks/subagent_stop.py +295 -0
  482. package/hooks/task_completed.py +70 -0
  483. package/hooks/user_prompt_submit.py +246 -0
  484. package/index.js +83 -0
  485. package/package.json +103 -0
  486. package/pyproject.toml +32 -0
  487. package/skills/README.md +158 -0
  488. package/skills/agent-creation/SKILL.md +87 -0
  489. package/skills/agent-creation/examples.md +170 -0
  490. package/skills/agent-creation/reference.md +191 -0
  491. package/skills/agent-protocol/SKILL.md +93 -0
  492. package/skills/agent-protocol/examples.md +223 -0
  493. package/skills/agent-response/SKILL.md +69 -0
  494. package/skills/agentic-loop/SKILL.md +80 -0
  495. package/skills/agentic-loop/reference.md +378 -0
  496. package/skills/blog-writing/SKILL.md +98 -0
  497. package/skills/blog-writing/reference.md +130 -0
  498. package/skills/brief-spec/SKILL.md +185 -0
  499. package/skills/command-execution/SKILL.md +64 -0
  500. package/skills/command-execution/reference.md +83 -0
  501. package/skills/context-updater/SKILL.md +87 -0
  502. package/skills/context-updater/examples.md +71 -0
  503. package/skills/developer-patterns/SKILL.md +50 -0
  504. package/skills/developer-patterns/reference.md +112 -0
  505. package/skills/execution/SKILL.md +99 -0
  506. package/skills/fast-queries/SKILL.md +43 -0
  507. package/skills/gaia-compact/SKILL.md +74 -0
  508. package/skills/gaia-patterns/SKILL.md +108 -0
  509. package/skills/gaia-patterns/reference.md +395 -0
  510. package/skills/gaia-planner/SKILL.md +37 -0
  511. package/skills/gaia-planner/reference.md +107 -0
  512. package/skills/gaia-release/SKILL.md +85 -0
  513. package/skills/gaia-release/reference.md +92 -0
  514. package/skills/gaia-self-check/SKILL.md +114 -0
  515. package/skills/gaia-self-check/reference.md +453 -0
  516. package/skills/gaia-verify/SKILL.md +77 -0
  517. package/skills/gaia-verify/reference.md +80 -0
  518. package/skills/git-conventions/SKILL.md +47 -0
  519. package/skills/gitops-patterns/SKILL.md +60 -0
  520. package/skills/gitops-patterns/reference.md +183 -0
  521. package/skills/gmail-policy/SKILL.md +200 -0
  522. package/skills/gmail-policy/reference.md +150 -0
  523. package/skills/gmail-triage/SKILL.md +100 -0
  524. package/skills/gws-setup/SKILL.md +99 -0
  525. package/skills/gws-setup/reference.md +73 -0
  526. package/skills/investigation/SKILL.md +100 -0
  527. package/skills/memory-curation/SKILL.md +83 -0
  528. package/skills/memory-search/SKILL.md +88 -0
  529. package/skills/orchestrator-approval/SKILL.md +160 -0
  530. package/skills/orchestrator-approval/reference.md +174 -0
  531. package/skills/pending-approvals/SKILL.md +72 -0
  532. package/skills/pending-approvals/reference.md +214 -0
  533. package/skills/readme-writing/SKILL.md +71 -0
  534. package/skills/readme-writing/reference.md +188 -0
  535. package/skills/reference.md +135 -0
  536. package/skills/request-approval/SKILL.md +140 -0
  537. package/skills/request-approval/examples.md +140 -0
  538. package/skills/request-approval/reference.md +57 -0
  539. package/skills/schedule-task/SKILL.md +64 -0
  540. package/skills/schedule-task/reference.md +233 -0
  541. package/skills/security-tiers/SKILL.md +141 -0
  542. package/skills/security-tiers/destructive-commands-reference.md +623 -0
  543. package/skills/security-tiers/reference.md +39 -0
  544. package/skills/session-reflection/SKILL.md +69 -0
  545. package/skills/skill-creation/SKILL.md +92 -0
  546. package/skills/skill-creation/reference.md +29 -0
  547. package/skills/terraform-patterns/SKILL.md +89 -0
  548. package/skills/terraform-patterns/reference.md +93 -0
  549. package/templates/README.md +69 -0
  550. package/templates/managed-settings.template.json +43 -0
  551. package/tools/__init__.py +9 -0
  552. package/tools/agentic-loop/decide-status.py +210 -0
  553. package/tools/agentic-loop/parse-metric.py +106 -0
  554. package/tools/agentic-loop/record-iteration.py +221 -0
  555. package/tools/context/README.md +132 -0
  556. package/tools/context/__init__.py +42 -0
  557. package/tools/context/_paths.py +20 -0
  558. package/tools/context/context_provider.py +721 -0
  559. package/tools/context/context_section_reader.py +342 -0
  560. package/tools/context/deep_merge.py +159 -0
  561. package/tools/context/pending_updates.py +760 -0
  562. package/tools/context/surface_router.py +278 -0
  563. package/tools/fast-queries/README.md +65 -0
  564. package/tools/fast-queries/__init__.py +30 -0
  565. package/tools/fast-queries/appservices/quicktriage_devops_developer.sh +75 -0
  566. package/tools/fast-queries/cloud/aws/quicktriage_aws_troubleshooter.sh +32 -0
  567. package/tools/fast-queries/cloud/gcp/quicktriage_gcp_troubleshooter.sh +88 -0
  568. package/tools/fast-queries/gitops/quicktriage_gitops_operator.sh +48 -0
  569. package/tools/fast-queries/run_triage.sh +59 -0
  570. package/tools/fast-queries/terraform/quicktriage_terraform_architect.sh +80 -0
  571. package/tools/gaia_simulator/__init__.py +33 -0
  572. package/tools/gaia_simulator/cli.py +354 -0
  573. package/tools/gaia_simulator/extractor.py +457 -0
  574. package/tools/gaia_simulator/reporter.py +258 -0
  575. package/tools/gaia_simulator/routing_simulator.py +334 -0
  576. package/tools/gaia_simulator/runner.py +539 -0
  577. package/tools/gaia_simulator/skills_mapper.py +264 -0
  578. package/tools/memory/README.md +0 -0
  579. package/tools/memory/__init__.py +20 -0
  580. package/tools/memory/backfill_fts5.py +107 -0
  581. package/tools/memory/conflict_detector.py +295 -0
  582. package/tools/memory/episodic.py +1210 -0
  583. package/tools/memory/git_invalidator.py +262 -0
  584. package/tools/memory/paths.py +102 -0
  585. package/tools/memory/scoring.py +193 -0
  586. package/tools/memory/search_store.py +375 -0
  587. package/tools/persist_transcript_analysis.py +85 -0
  588. package/tools/review/__init__.py +1 -0
  589. package/tools/review/review_engine.py +157 -0
  590. package/tools/scan/__init__.py +35 -0
  591. package/tools/scan/config.py +247 -0
  592. package/tools/scan/merge.py +212 -0
  593. package/tools/scan/orchestrator.py +549 -0
  594. package/tools/scan/registry.py +127 -0
  595. package/tools/scan/scanners/__init__.py +18 -0
  596. package/tools/scan/scanners/base.py +137 -0
  597. package/tools/scan/scanners/environment.py +349 -0
  598. package/tools/scan/scanners/git.py +570 -0
  599. package/tools/scan/scanners/infrastructure.py +875 -0
  600. package/tools/scan/scanners/orchestration.py +600 -0
  601. package/tools/scan/scanners/stack.py +1085 -0
  602. package/tools/scan/scanners/tools.py +260 -0
  603. package/tools/scan/setup.py +686 -0
  604. package/tools/scan/tests/__init__.py +1 -0
  605. package/tools/scan/tests/conftest.py +796 -0
  606. package/tools/scan/tests/test_environment.py +323 -0
  607. package/tools/scan/tests/test_git.py +419 -0
  608. package/tools/scan/tests/test_infrastructure.py +382 -0
  609. package/tools/scan/tests/test_integration.py +920 -0
  610. package/tools/scan/tests/test_merge.py +269 -0
  611. package/tools/scan/tests/test_orchestration.py +304 -0
  612. package/tools/scan/tests/test_stack.py +604 -0
  613. package/tools/scan/tests/test_tools.py +349 -0
  614. package/tools/scan/ui.py +624 -0
  615. package/tools/scan/verify.py +270 -0
  616. package/tools/scan/walk.py +118 -0
  617. package/tools/scan/workspace.py +85 -0
  618. package/tools/validation/README.md +244 -0
  619. package/tools/validation/__init__.py +17 -0
  620. package/tools/validation/approval_gate.py +321 -0
  621. package/tools/validation/validate_skills.py +189 -0
@@ -0,0 +1,258 @@
1
+ """
2
+ Results reporter for gaia-ops replay testing.
3
+
4
+ Formats and presents ReplayResult data for human consumption and
5
+ programmatic analysis. Completely decoupled from execution.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ from collections import Counter
12
+ from dataclasses import asdict
13
+ from pathlib import Path
14
+ from typing import Any
15
+
16
+ from gaia_simulator.extractor import ReplayEvent
17
+ from gaia_simulator.runner import ReplayResult
18
+
19
+
20
+ class ReplayReporter:
21
+ """Formats replay results for human consumption and export."""
22
+
23
+ def events_payload(self, events: list[ReplayEvent]) -> list[dict[str, Any]]:
24
+ """Convert extracted events to JSON-serializable dicts."""
25
+ return [asdict(event) for event in events]
26
+
27
+ def results_payload(self, results: list[ReplayResult]) -> list[dict[str, Any]]:
28
+ """Convert replay results to JSON-serializable dicts."""
29
+ output: list[dict[str, Any]] = []
30
+ for r in results:
31
+ entry = {
32
+ "timestamp": r.event.timestamp,
33
+ "hook_name": r.event.hook_name,
34
+ "tool_name": r.event.tool_name,
35
+ "source_file": r.event.source_file,
36
+ "limitations": list(r.event.limitations),
37
+ "expected": {
38
+ "decision": r.event.expected_decision,
39
+ "exit_code": r.event.expected_exit_code,
40
+ "tier": r.event.expected_tier,
41
+ "metadata": r.event.expected_metadata,
42
+ },
43
+ "actual": {
44
+ "decision": r.actual_decision,
45
+ "exit_code": r.actual_exit_code,
46
+ "tier": r.actual_tier,
47
+ "metadata": r.actual_metadata,
48
+ },
49
+ "matched": r.matched,
50
+ "regression_type": r.regression_type,
51
+ }
52
+
53
+ tool_input = r.event.stdin_payload.get("tool_input", {})
54
+ if r.event.tool_name == "Bash":
55
+ entry["command"] = tool_input.get("command", "")
56
+ elif r.event.tool_name == "Agent":
57
+ entry["agent"] = tool_input.get("subagent_type", "")
58
+
59
+ output.append(entry)
60
+ return output
61
+
62
+ def summary(self, results: list[ReplayResult]) -> str:
63
+ """Quick summary: X events, Y matched, Z regressions.
64
+
65
+ Args:
66
+ results: List of ReplayResult instances.
67
+
68
+ Returns:
69
+ Multi-line summary string.
70
+ """
71
+ if not results:
72
+ return "No events replayed."
73
+
74
+ total = len(results)
75
+ matched = sum(1 for r in results if r.matched)
76
+ regressions = total - matched
77
+
78
+ lines = [
79
+ "=" * 60,
80
+ "REPLAY SUMMARY",
81
+ "=" * 60,
82
+ f"Total events: {total}",
83
+ f"Matched: {matched}",
84
+ f"Regressions: {regressions}",
85
+ ]
86
+
87
+ if regressions > 0:
88
+ lines.append("")
89
+ lines.append("Regression breakdown:")
90
+ reg_types = Counter(
91
+ r.regression_type for r in results if not r.matched
92
+ )
93
+ for rtype, count in reg_types.most_common():
94
+ lines.append(f" {rtype}: {count}")
95
+
96
+ # Quick stats by decision
97
+ lines.append("")
98
+ decision_counts = Counter(r.event.expected_decision for r in results)
99
+ lines.append("Events by expected decision:")
100
+ for dec, count in decision_counts.most_common():
101
+ lines.append(f" {dec}: {count}")
102
+
103
+ lines.append("=" * 60)
104
+ return "\n".join(lines)
105
+
106
+ def regressions_only(self, results: list[ReplayResult]) -> str:
107
+ """Show only regressions with details.
108
+
109
+ Args:
110
+ results: List of ReplayResult instances.
111
+
112
+ Returns:
113
+ Formatted string showing regression details, or a success message.
114
+ """
115
+ regressions = [r for r in results if not r.matched]
116
+
117
+ if not regressions:
118
+ return "No regressions detected. All events matched expected behavior."
119
+
120
+ lines = [
121
+ "=" * 60,
122
+ f"REGRESSIONS FOUND: {len(regressions)}",
123
+ "=" * 60,
124
+ ]
125
+
126
+ for i, r in enumerate(regressions, 1):
127
+ lines.append("")
128
+ lines.append(f"--- Regression #{i} [{r.regression_type}] ---")
129
+ lines.append(f" Timestamp: {r.event.timestamp}")
130
+ lines.append(f" Hook: {r.event.hook_name}")
131
+ lines.append(f" Tool: {r.event.tool_name}")
132
+ lines.append(f" Source: {r.event.source_file}")
133
+
134
+ # Show the command or agent name
135
+ tool_input = r.event.stdin_payload.get("tool_input", {})
136
+ if r.event.tool_name == "Bash":
137
+ cmd = tool_input.get("command", "")
138
+ if len(cmd) > 120:
139
+ cmd = cmd[:120] + "..."
140
+ lines.append(f" Command: {cmd}")
141
+ elif r.event.tool_name == "Agent":
142
+ agent = tool_input.get("subagent_type", tool_input.get("description", ""))
143
+ lines.append(f" Agent: {agent}")
144
+
145
+ lines.append(f" Expected: decision={r.event.expected_decision}, "
146
+ f"exit_code={r.event.expected_exit_code}, "
147
+ f"tier={r.event.expected_tier or 'n/a'}")
148
+ lines.append(f" Actual: decision={r.actual_decision}, "
149
+ f"exit_code={r.actual_exit_code}, "
150
+ f"tier={r.actual_tier or 'n/a'}")
151
+
152
+ if r.actual_stderr and len(r.actual_stderr) < 200:
153
+ lines.append(f" Stderr: {r.actual_stderr.strip()}")
154
+
155
+ lines.append("")
156
+ lines.append("=" * 60)
157
+ return "\n".join(lines)
158
+
159
+ def full_report(self, results: list[ReplayResult]) -> str:
160
+ """Full report with stats per hook, per tier, per decision type.
161
+
162
+ Args:
163
+ results: List of ReplayResult instances.
164
+
165
+ Returns:
166
+ Comprehensive formatted report string.
167
+ """
168
+ if not results:
169
+ return "No events to report on."
170
+
171
+ lines = [self.summary(results)]
172
+
173
+ # Stats by hook
174
+ hooks: dict[str, list[ReplayResult]] = {}
175
+ for r in results:
176
+ hooks.setdefault(r.event.hook_name, []).append(r)
177
+
178
+ lines.append("")
179
+ lines.append("BREAKDOWN BY HOOK:")
180
+ lines.append("-" * 40)
181
+ for hook_name, hook_results in sorted(hooks.items()):
182
+ total = len(hook_results)
183
+ matched = sum(1 for r in hook_results if r.matched)
184
+ lines.append(f" {hook_name}: {total} events, {matched} matched, "
185
+ f"{total - matched} regressions")
186
+
187
+ # Stats by tier
188
+ tiers: dict[str, list[ReplayResult]] = {}
189
+ for r in results:
190
+ tier = r.event.expected_tier or "n/a"
191
+ tiers.setdefault(tier, []).append(r)
192
+
193
+ lines.append("")
194
+ lines.append("BREAKDOWN BY TIER:")
195
+ lines.append("-" * 40)
196
+ for tier_name, tier_results in sorted(tiers.items()):
197
+ total = len(tier_results)
198
+ matched = sum(1 for r in tier_results if r.matched)
199
+ lines.append(f" {tier_name}: {total} events, {matched} matched, "
200
+ f"{total - matched} regressions")
201
+
202
+ # Stats by tool
203
+ tools: dict[str, list[ReplayResult]] = {}
204
+ for r in results:
205
+ tools.setdefault(r.event.tool_name, []).append(r)
206
+
207
+ lines.append("")
208
+ lines.append("BREAKDOWN BY TOOL:")
209
+ lines.append("-" * 40)
210
+ for tool_name, tool_results in sorted(tools.items()):
211
+ total = len(tool_results)
212
+ matched = sum(1 for r in tool_results if r.matched)
213
+ lines.append(f" {tool_name}: {total} events, {matched} matched, "
214
+ f"{total - matched} regressions")
215
+
216
+ # Stats by source file / artifact
217
+ sources: dict[str, list[ReplayResult]] = {}
218
+ for r in results:
219
+ sources.setdefault(r.event.source_file, []).append(r)
220
+
221
+ lines.append("")
222
+ lines.append("BREAKDOWN BY SOURCE:")
223
+ lines.append("-" * 40)
224
+ for source_name, source_results in sorted(sources.items()):
225
+ total = len(source_results)
226
+ matched = sum(1 for r in source_results if r.matched)
227
+ lines.append(f" {source_name}: {total} events, {matched} matched, "
228
+ f"{total - matched} regressions")
229
+
230
+ limitations = sorted({
231
+ limitation
232
+ for r in results
233
+ for limitation in r.event.limitations
234
+ if limitation
235
+ })
236
+ if limitations:
237
+ lines.append("")
238
+ lines.append("SOURCE LIMITATIONS:")
239
+ lines.append("-" * 40)
240
+ for limitation in limitations:
241
+ lines.append(f" - {limitation}")
242
+
243
+ # Show regressions if any
244
+ regressions = [r for r in results if not r.matched]
245
+ if regressions:
246
+ lines.append("")
247
+ lines.append(self.regressions_only(results))
248
+
249
+ return "\n".join(lines)
250
+
251
+ def save_json(self, results: list[ReplayResult], path: Path) -> None:
252
+ """Save results as JSON for programmatic analysis.
253
+
254
+ Args:
255
+ results: List of ReplayResult instances.
256
+ path: Output file path.
257
+ """
258
+ path.write_text(json.dumps(self.results_payload(results), indent=2, default=str))
@@ -0,0 +1,334 @@
1
+ """
2
+ Routing simulator for gaia-ops surface routing analysis.
3
+
4
+ Simulates what would happen when a prompt enters the orchestrator:
5
+ which surfaces activate, which agent is selected, what skills load,
6
+ what context sections are injected, and what contract permissions apply.
7
+
8
+ Uses the real classify_surfaces() from surface_router.py for fidelity.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import json
14
+ import re
15
+ import sys
16
+ from dataclasses import dataclass
17
+ from pathlib import Path
18
+ from typing import Any, Optional
19
+
20
+ _TOOLS_DIR = Path(__file__).resolve().parent.parent
21
+ if str(_TOOLS_DIR) not in sys.path:
22
+ sys.path.insert(0, str(_TOOLS_DIR))
23
+
24
+ from context.surface_router import classify_surfaces, load_surface_routing_config
25
+
26
+
27
+ # ---------------------------------------------------------------------------
28
+ # Data classes
29
+ # ---------------------------------------------------------------------------
30
+
31
+
32
+ @dataclass
33
+ class RoutingResult:
34
+ """Result of simulating a prompt through the routing pipeline."""
35
+
36
+ prompt: str
37
+ surfaces_active: list[str]
38
+ primary_agent: str
39
+ adjacent_agents: list[str]
40
+ skills_loaded: list[str]
41
+ context_sections: list[str]
42
+ tokens_estimate: int
43
+ contracts: dict[str, list[str]] # {"read": [...], "write": [...]}
44
+ confidence: float
45
+ multi_surface: bool
46
+
47
+
48
+ # ---------------------------------------------------------------------------
49
+ # Frontmatter parsing
50
+ # ---------------------------------------------------------------------------
51
+
52
+ _RE_FRONTMATTER = re.compile(r"^---\s*\n(.*?)\n---", re.DOTALL)
53
+ _RE_SKILLS_LINE = re.compile(r"^\s+-\s+(.+)$", re.MULTILINE)
54
+
55
+
56
+ def _parse_frontmatter(content: str) -> dict[str, Any]:
57
+ """Parse YAML frontmatter from agent .md files."""
58
+ if not content.startswith("---"):
59
+ return {}
60
+
61
+ match = _RE_FRONTMATTER.match(content)
62
+ if not match:
63
+ return {}
64
+
65
+ yaml_block = match.group(1)
66
+ result: dict[str, Any] = {}
67
+
68
+ for line in yaml_block.splitlines():
69
+ line_stripped = line.strip()
70
+ if not line_stripped or line_stripped.startswith("#"):
71
+ continue
72
+ if ":" not in line_stripped:
73
+ continue
74
+
75
+ key, _, value = line_stripped.partition(":")
76
+ key = key.strip()
77
+ value = value.strip()
78
+
79
+ if key == "skills":
80
+ skills: list[str] = []
81
+ if value.startswith("[") and value.endswith("]"):
82
+ skills = [s.strip() for s in value[1:-1].split(",") if s.strip()]
83
+ else:
84
+ skills_start = yaml_block.index("skills:")
85
+ skills_section = yaml_block[skills_start:]
86
+ for skill_match in _RE_SKILLS_LINE.finditer(skills_section):
87
+ skill_name = skill_match.group(1).strip()
88
+ if skill_name:
89
+ skills.append(skill_name)
90
+ result["skills"] = skills
91
+ else:
92
+ result[key] = value
93
+
94
+ return result
95
+
96
+
97
+ def _load_agent_skills(agents_dir: Path) -> dict[str, list[str]]:
98
+ """Load skills lists from all agent .md files in a directory.
99
+
100
+ Returns:
101
+ Dict mapping agent name to list of skill names.
102
+ """
103
+ agent_skills: dict[str, list[str]] = {}
104
+
105
+ if not agents_dir.is_dir():
106
+ return agent_skills
107
+
108
+ for md_file in sorted(agents_dir.glob("*.md")):
109
+ content = md_file.read_text(encoding="utf-8", errors="replace")
110
+ frontmatter = _parse_frontmatter(content)
111
+ agent_name = frontmatter.get("name", md_file.stem)
112
+ agent_skills[agent_name] = frontmatter.get("skills", [])
113
+
114
+ return agent_skills
115
+
116
+
117
+ # ---------------------------------------------------------------------------
118
+ # RoutingSimulator
119
+ # ---------------------------------------------------------------------------
120
+
121
+
122
+ class RoutingSimulator:
123
+ """Simulates the gaia-ops routing pipeline for a given prompt.
124
+
125
+ Loads surface-routing.json, context-contracts.json, and agent frontmatter
126
+ to predict: which surfaces activate, which agent handles, what skills and
127
+ context sections are injected, and what contract permissions apply.
128
+ """
129
+
130
+ def __init__(self, config_dir: Path, agents_dir: Path):
131
+ """Initialize the simulator with config and agents directories.
132
+
133
+ Args:
134
+ config_dir: Path to the config/ directory containing
135
+ surface-routing.json and context-contracts.json.
136
+ agents_dir: Path to the agents/ directory containing agent .md files.
137
+ """
138
+ self._config_dir = config_dir
139
+ self._agents_dir = agents_dir
140
+
141
+ # Load routing config
142
+ routing_file = config_dir / "surface-routing.json"
143
+ self._routing_config = load_surface_routing_config(routing_file)
144
+
145
+ # Load contracts
146
+ contracts_file = config_dir / "context-contracts.json"
147
+ if contracts_file.is_file():
148
+ self._contracts = json.loads(contracts_file.read_text(encoding="utf-8"))
149
+ else:
150
+ self._contracts = {"agents": {}}
151
+
152
+ # Load agent skills from frontmatter
153
+ self._agent_skills = _load_agent_skills(agents_dir)
154
+
155
+ def simulate(self, prompt: str, agent_type: Optional[str] = None) -> RoutingResult:
156
+ """Simulate routing for a prompt.
157
+
158
+ Args:
159
+ prompt: The user prompt to classify.
160
+ agent_type: If provided, show what this specific agent would receive.
161
+ If not, determine the agent from surface routing.
162
+
163
+ Returns:
164
+ RoutingResult with full routing prediction.
165
+ """
166
+ routing = classify_surfaces(
167
+ prompt,
168
+ current_agent=agent_type or "",
169
+ routing_config=self._routing_config,
170
+ )
171
+
172
+ active_surfaces = routing.get("active_surfaces", [])
173
+ confidence = routing.get("confidence", 0.0)
174
+ multi_surface = routing.get("multi_surface", False)
175
+ recommended_agents = routing.get("recommended_agents", [])
176
+
177
+ surfaces_cfg = self._routing_config.get("surfaces", {})
178
+ if agent_type:
179
+ primary_agent = agent_type
180
+ elif recommended_agents:
181
+ primary_agent = recommended_agents[0]
182
+ else:
183
+ primary_agent = self._routing_config.get(
184
+ "reconnaissance_agent", "developer"
185
+ )
186
+
187
+ skills = self._agent_skills.get(primary_agent, [])
188
+
189
+ context_sections: list[str] = []
190
+ seen_sections: set[str] = set()
191
+ for surface in active_surfaces:
192
+ surface_cfg = surfaces_cfg.get(surface, {})
193
+ for section in surface_cfg.get("contract_sections", []):
194
+ if section not in seen_sections:
195
+ context_sections.append(section)
196
+ seen_sections.add(section)
197
+
198
+ agent_contract = self._contracts.get("agents", {}).get(primary_agent, {})
199
+ read_sections = agent_contract.get("read", [])
200
+ write_sections = agent_contract.get("write", [])
201
+
202
+ tokens_estimate = len(context_sections) * 100
203
+
204
+ adjacent_agents = [a for a in recommended_agents if a != primary_agent]
205
+
206
+ return RoutingResult(
207
+ prompt=prompt,
208
+ surfaces_active=active_surfaces,
209
+ primary_agent=primary_agent,
210
+ adjacent_agents=adjacent_agents,
211
+ skills_loaded=skills,
212
+ context_sections=context_sections,
213
+ tokens_estimate=tokens_estimate,
214
+ contracts={"read": read_sections, "write": write_sections},
215
+ confidence=confidence,
216
+ multi_surface=multi_surface,
217
+ )
218
+
219
+ def simulate_from_log(self, events: list[dict[str, Any]]) -> list[RoutingResult]:
220
+ """Simulate routing for all events from logs.
221
+
222
+ Args:
223
+ events: List of event dicts, each with prompt or tool_input data.
224
+
225
+ Returns:
226
+ List of RoutingResult instances.
227
+ """
228
+ results: list[RoutingResult] = []
229
+ for event in events:
230
+ prompt = event.get("prompt", "")
231
+ if not prompt:
232
+ tool_input = event.get("tool_input", {})
233
+ if isinstance(tool_input, dict):
234
+ prompt = tool_input.get(
235
+ "command", tool_input.get("description", "")
236
+ )
237
+ if not prompt:
238
+ continue
239
+ agent_used = event.get("agent", event.get("subagent_type", None))
240
+ result = self.simulate(prompt, agent_type=agent_used)
241
+ results.append(result)
242
+ return results
243
+
244
+ def compare_routing(self, events: list[dict[str, Any]]) -> dict[str, Any]:
245
+ """Compare simulated routing vs actual agent used in logs.
246
+
247
+ Args:
248
+ events: List of event dicts, each with prompt and agent keys.
249
+
250
+ Returns:
251
+ Dict with matches, mismatches, and statistics.
252
+ """
253
+ matches: list[dict[str, Any]] = []
254
+ mismatches: list[dict[str, Any]] = []
255
+
256
+ for event in events:
257
+ prompt = event.get("prompt", "")
258
+ actual_agent = event.get("agent", event.get("subagent_type", ""))
259
+ if not prompt or not actual_agent:
260
+ continue
261
+
262
+ result = self.simulate(prompt)
263
+
264
+ entry = {
265
+ "prompt": prompt[:120],
266
+ "simulated_agent": result.primary_agent,
267
+ "actual_agent": actual_agent,
268
+ "surfaces": result.surfaces_active,
269
+ "confidence": result.confidence,
270
+ }
271
+
272
+ if result.primary_agent == actual_agent:
273
+ matches.append(entry)
274
+ else:
275
+ mismatches.append(entry)
276
+
277
+ total = len(matches) + len(mismatches)
278
+ return {
279
+ "total": total,
280
+ "matches": len(matches),
281
+ "mismatches": len(mismatches),
282
+ "match_rate": round(len(matches) / max(total, 1), 2),
283
+ "match_details": matches,
284
+ "mismatch_details": mismatches,
285
+ }
286
+
287
+
288
+ def format_routing_result(result: RoutingResult) -> str:
289
+ """Format a RoutingResult as human-readable text.
290
+
291
+ Args:
292
+ result: The routing simulation result.
293
+
294
+ Returns:
295
+ Formatted multi-line string.
296
+ """
297
+ lines = [
298
+ "=" * 60,
299
+ "ROUTING SIMULATION",
300
+ "=" * 60,
301
+ ]
302
+ lines.append("Prompt: " + result.prompt[:100])
303
+ lines.append("Primary agent: " + result.primary_agent)
304
+ adj = ", ".join(result.adjacent_agents) or "none"
305
+ lines.append("Adjacent agents: " + adj)
306
+ lines.append("Confidence: " + str(result.confidence))
307
+ lines.append("Multi-surface: " + str(result.multi_surface))
308
+ lines.append("")
309
+ lines.append("Active surfaces:")
310
+ for surface in result.surfaces_active:
311
+ lines.append(" - " + surface)
312
+ if not result.surfaces_active:
313
+ lines.append(" (none)")
314
+ lines.append("")
315
+ lines.append("Skills loaded:")
316
+ for skill in result.skills_loaded:
317
+ lines.append(" - " + skill)
318
+ if not result.skills_loaded:
319
+ lines.append(" (none)")
320
+ lines.append("")
321
+ lines.append("Context sections:")
322
+ for section in result.context_sections:
323
+ lines.append(" - " + section)
324
+ if not result.context_sections:
325
+ lines.append(" (none)")
326
+ lines.append("Tokens estimate: ~" + str(result.tokens_estimate))
327
+ lines.append("")
328
+ lines.append("Contracts:")
329
+ read_str = ", ".join(result.contracts.get("read", [])) or "none"
330
+ write_str = ", ".join(result.contracts.get("write", [])) or "none"
331
+ lines.append(" Read: " + read_str)
332
+ lines.append(" Write: " + write_str)
333
+ lines.append("=" * 60)
334
+ return chr(10).join(lines)