@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,558 @@
1
+ """Core context injection subsystem for project agents.
2
+
3
+ Handles:
4
+ - build_project_context: builds context string for additionalContext injection
5
+ - check_pending_updates_threshold: warns when pending updates accumulate
6
+ - check_recent_critical_anomalies: surfaces critical anomalies from JSONL log
7
+ - consume_anomaly_flag: reads and deletes anomaly signal flags
8
+ """
9
+
10
+ import json
11
+ import logging
12
+ import os
13
+ import shutil
14
+ import subprocess
15
+ import sys
16
+ from datetime import datetime
17
+ from pathlib import Path
18
+
19
+ from ..core.paths import get_plugin_data_dir
20
+ from ..session.session_manager import get_or_create_session_id
21
+ from .anchor_tracker import extract_anchors, save_anchors
22
+ from .contracts_loader import build_context_update_reminder
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+ # Inline explanations for every field the investigation brief can contain.
27
+ # Appended as YAML comments when the brief is rendered via _dict_to_yaml_annotated().
28
+ BRIEF_FIELD_DESCRIPTIONS: dict[str, str] = {
29
+ "goal": "the task as stated by the orchestrator",
30
+ "agent_role": "your relationship to this task: primary (owns it), cross_check (verify peer's work), adjacent (related surface), reconnaissance (explore)",
31
+ "primary_surface": "the surface that owns the fix or decision",
32
+ "active_surfaces": "all surfaces involved; drives cross_check_required",
33
+ "adjacent_surfaces": "surfaces that may be impacted but do not own the fix",
34
+ "dispatch_mode": "single_surface = one agent owns it; multi_surface = multiple agents coordinate",
35
+ "cross_check_required": "true when >1 active surface or this agent is not primary; fill CROSS_LAYER_IMPACTS",
36
+ "patterns_required": "true means load the domain skill for this surface before executing",
37
+ "contract_sections_to_anchor": "project-context sections to cite as evidence in your contract",
38
+ "required_checks": "mandatory verifications before declaring COMPLETE",
39
+ "evidence_required": "fields that must appear in your evidence_report json:contract block",
40
+ "consolidation_required": "true when cross_check_required; fill consolidation_report with ownership_assessment and conflicts",
41
+ "consolidation_fields": "fields required inside consolidation_report when consolidation_required is true",
42
+ "recommended_peer_agents": "agents to delegate to or coordinate with for non-primary surfaces",
43
+ "stop_conditions": "conditions under which further investigation adds no value",
44
+ }
45
+
46
+
47
+ def _dict_to_yaml_annotated(d: dict, descriptions: dict[str, str], indent: int = 0) -> str:
48
+ """Render a dict as YAML-like key-value pairs with inline description comments.
49
+
50
+ Works like _dict_to_yaml but appends ``# <description>`` after each
51
+ top-level scalar or list header when a matching entry exists in
52
+ *descriptions*. Nested structures and list items are rendered without
53
+ annotations to keep the output readable.
54
+ """
55
+ lines = []
56
+ prefix = " " * indent
57
+ for key, value in d.items():
58
+ desc = descriptions.get(key, "") if indent == 0 else ""
59
+ comment = f" # {desc}" if desc else ""
60
+ if value is None or value == "" or value == [] or value == {}:
61
+ continue
62
+ if isinstance(value, dict):
63
+ lines.append(f"{prefix}{key}:{comment}")
64
+ lines.append(_dict_to_yaml(value, indent + 1))
65
+ elif isinstance(value, list):
66
+ lines.append(f"{prefix}{key}:{comment}")
67
+ for item in value:
68
+ if isinstance(item, dict):
69
+ item_lines = _dict_to_yaml(item, indent + 1).splitlines()
70
+ if item_lines:
71
+ first = item_lines[0].lstrip()
72
+ lines.append(f"{prefix} - {first}")
73
+ for il in item_lines[1:]:
74
+ lines.append(f"{prefix} {il}")
75
+ else:
76
+ lines.append(f"{prefix} - {item}")
77
+ else:
78
+ lines.append(f"{prefix}{key}: {value}{comment}")
79
+ return "\n".join(lines)
80
+
81
+
82
+ def _find_python() -> str:
83
+ """Return the Python 3 command name for this platform.
84
+
85
+ Tries ``python3`` first (Linux/macOS), then ``python`` (Windows).
86
+ Falls back to ``sys.executable`` (the current interpreter) as a
87
+ last resort -- this always works since hooks are already running
88
+ under Python.
89
+ """
90
+ for cmd in ("python3", "python"):
91
+ if shutil.which(cmd):
92
+ return cmd
93
+ return sys.executable
94
+
95
+
96
+ def _dict_to_yaml(d, indent: int = 0) -> str:
97
+ """Convert a dict to indented YAML-like key-value pairs (no external dependency).
98
+
99
+ - Nested dicts are indented by 2 spaces per level.
100
+ - Lists are rendered as markdown bullet lists (- item).
101
+ - Scalar values are rendered inline.
102
+ - None/empty values are skipped.
103
+ """
104
+ lines = []
105
+ prefix = " " * indent
106
+ for key, value in d.items():
107
+ if value is None or value == "" or value == [] or value == {}:
108
+ continue
109
+ if isinstance(value, dict):
110
+ lines.append(f"{prefix}{key}:")
111
+ lines.append(_dict_to_yaml(value, indent + 1))
112
+ elif isinstance(value, list):
113
+ lines.append(f"{prefix}{key}:")
114
+ for item in value:
115
+ if isinstance(item, dict):
116
+ # Render first key inline, rest indented
117
+ item_lines = _dict_to_yaml(item, indent + 1).splitlines()
118
+ if item_lines:
119
+ first = item_lines[0].lstrip()
120
+ lines.append(f"{prefix} - {first}")
121
+ for il in item_lines[1:]:
122
+ lines.append(f"{prefix} {il}")
123
+ else:
124
+ lines.append(f"{prefix} - {item}")
125
+ else:
126
+ lines.append(f"{prefix}{key}: {value}")
127
+ return "\n".join(lines)
128
+
129
+
130
+ def _prune_empty_values(data: dict) -> dict:
131
+ """Drop keys with empty telemetry values while preserving False/0."""
132
+ pruned = {}
133
+ for key, value in data.items():
134
+ if value in (None, "", [], {}):
135
+ continue
136
+ pruned[key] = value
137
+ return pruned
138
+
139
+
140
+ def build_context_telemetry_snapshot(context_payload: dict) -> dict:
141
+ """Build a compact telemetry snapshot from injected context payload data."""
142
+ if not isinstance(context_payload, dict) or not context_payload:
143
+ return {}
144
+
145
+ project_knowledge = context_payload.get("project_knowledge") or {}
146
+ metadata = context_payload.get("metadata") or {}
147
+ surface_routing = context_payload.get("surface_routing") or {}
148
+ investigation_brief = context_payload.get("investigation_brief") or {}
149
+ write_permissions = context_payload.get("write_permissions") or {}
150
+
151
+ contract_sections = sorted(project_knowledge.keys())
152
+ readable_sections = sorted(write_permissions.get("readable_sections") or [])
153
+ writable_sections = sorted(write_permissions.get("writable_sections") or [])
154
+
155
+ return _prune_empty_values({
156
+ "contract_sections": contract_sections,
157
+ "contract_sections_count": len(contract_sections),
158
+ "metadata": _prune_empty_values({
159
+ "cloud_provider": metadata.get("cloud_provider"),
160
+ "contract_version": metadata.get("contract_version"),
161
+ "rules_count": metadata.get("rules_count"),
162
+ "historical_episodes_count": metadata.get("historical_episodes_count"),
163
+ "surface_routing_version": metadata.get("surface_routing_version"),
164
+ "active_surfaces_count": metadata.get("active_surfaces_count"),
165
+ "surface_routing_confidence": metadata.get("surface_routing_confidence"),
166
+ }),
167
+ "surface_routing": _prune_empty_values({
168
+ "primary_surface": surface_routing.get("primary_surface"),
169
+ "active_surfaces": sorted(surface_routing.get("active_surfaces") or []),
170
+ "dispatch_mode": surface_routing.get("dispatch_mode"),
171
+ "multi_surface": surface_routing.get("multi_surface"),
172
+ "recommended_agents": sorted(surface_routing.get("recommended_agents") or []),
173
+ }),
174
+ "investigation_brief": _prune_empty_values({
175
+ "agent_role": investigation_brief.get("agent_role"),
176
+ "primary_surface": investigation_brief.get("primary_surface"),
177
+ "adjacent_surfaces": sorted(investigation_brief.get("adjacent_surfaces") or []),
178
+ "cross_check_required": investigation_brief.get("cross_check_required"),
179
+ "consolidation_required": investigation_brief.get("consolidation_required"),
180
+ "required_checks_count": len(investigation_brief.get("required_checks") or []),
181
+ "evidence_required": sorted(investigation_brief.get("evidence_required") or []),
182
+ }),
183
+ "context_update_scope": _prune_empty_values({
184
+ "readable_sections": readable_sections,
185
+ "readable_sections_count": len(readable_sections),
186
+ "writable_sections": writable_sections,
187
+ "writable_sections_count": len(writable_sections),
188
+ }),
189
+ })
190
+
191
+
192
+ def check_pending_updates_threshold() -> str:
193
+ """
194
+ Check if pending updates count exceeds threshold and return warning text.
195
+
196
+ Returns warning string to inject into prompt, or empty string if below threshold.
197
+ Must NEVER block or slow down context injection (target: <50ms).
198
+ """
199
+ try:
200
+ threshold = int(os.environ.get("PENDING_UPDATE_THRESHOLD", "10"))
201
+
202
+ # Fast path: try to read index directly (no module import)
203
+ index_path = Path(".claude/project-context/pending-updates/pending-index.json")
204
+ if not index_path.exists():
205
+ return ""
206
+
207
+ with open(index_path, 'r') as f:
208
+ index_data = json.load(f)
209
+
210
+ pending_count = index_data.get("pending_count", 0)
211
+ if pending_count < threshold:
212
+ return ""
213
+
214
+ logger.info(f"Pending updates threshold reached: {pending_count} >= {threshold}")
215
+ return (
216
+ f"\n# Pending Context Updates Warning\n"
217
+ f"There are {pending_count} pending context update suggestions awaiting review. "
218
+ f"Run `gaia-review` or `python3 tools/review/review_engine.py list` to review them.\n\n"
219
+ )
220
+
221
+ except Exception as e:
222
+ logger.debug(f"Pending updates check failed (non-fatal): {e}")
223
+ return ""
224
+
225
+
226
+ def check_recent_critical_anomalies() -> str:
227
+ """Check anomalies.jsonl for recent critical anomalies and return a summary.
228
+
229
+ Scans the last 20 lines of the anomaly log for critical-severity entries
230
+ from the past hour. Returns a short warning string suitable for context
231
+ injection, or empty string if nothing noteworthy is found.
232
+
233
+ This is intentionally lightweight: reads only the tail of the file and
234
+ returns at most a one-line count + type summary.
235
+ """
236
+ anomaly_log = (
237
+ get_plugin_data_dir() / "project-context" / "workflow-episodic-memory" / "anomalies.jsonl"
238
+ )
239
+ if not anomaly_log.exists():
240
+ return ""
241
+
242
+ try:
243
+ # Read only the tail (last 20 lines) for speed
244
+ lines = anomaly_log.read_text().splitlines()[-20:]
245
+ one_hour_ago = datetime.now().timestamp() - 3600
246
+ critical_types: list[str] = []
247
+
248
+ for line in lines:
249
+ if not line.strip():
250
+ continue
251
+ try:
252
+ entry = json.loads(line)
253
+ except json.JSONDecodeError:
254
+ continue
255
+ ts = entry.get("timestamp", "")
256
+ if ts:
257
+ try:
258
+ entry_time = datetime.fromisoformat(ts).timestamp()
259
+ except (ValueError, TypeError):
260
+ continue
261
+ if entry_time < one_hour_ago:
262
+ continue
263
+ for anomaly in entry.get("anomalies", []):
264
+ if anomaly.get("severity") == "critical":
265
+ critical_types.append(anomaly.get("type", "unknown"))
266
+
267
+ if not critical_types:
268
+ return ""
269
+
270
+ # Deduplicate and summarize
271
+ unique_types = sorted(set(critical_types))
272
+ return (
273
+ f"\n# Recent Critical Anomalies\n"
274
+ f"{len(critical_types)} critical anomaly(ies) in the last hour "
275
+ f"(types: {', '.join(unique_types)}). "
276
+ f"Consider investigating with /gaia.\n"
277
+ )
278
+ except Exception as e:
279
+ logger.debug(f"Critical anomaly check failed (non-fatal): {e}")
280
+ return ""
281
+
282
+
283
+ def consume_anomaly_flag(enriched_prompt: str) -> str:
284
+ """Read and delete the needs_analysis.flag if it exists, appending a warning.
285
+
286
+ The flag is created by subagent_stop.py when workflow anomalies are
287
+ detected. Reading it once and deleting ensures the warning is shown
288
+ exactly once. Must not slow down context injection -- returns
289
+ immediately if the file does not exist.
290
+
291
+ TTL enforcement: flags older than 1 hour (by created_at or file mtime)
292
+ are auto-expired and deleted without injecting a warning.
293
+ """
294
+ flag_path = get_plugin_data_dir() / "project-context" / "workflow-episodic-memory" / "signals" / "needs_analysis.flag"
295
+ if not flag_path.exists():
296
+ return enriched_prompt
297
+ try:
298
+ signal_data = json.loads(flag_path.read_text())
299
+
300
+ # TTL check: auto-expire flags older than ttl_hours (default 1 hour)
301
+ ttl_hours = signal_data.get("ttl_hours", 1)
302
+ ttl_seconds = ttl_hours * 3600
303
+ created_at = signal_data.get("created_at") or signal_data.get("timestamp")
304
+ if created_at:
305
+ created_dt = datetime.fromisoformat(created_at)
306
+ age_seconds = (datetime.now() - created_dt).total_seconds()
307
+ if age_seconds > ttl_seconds:
308
+ flag_path.unlink()
309
+ logger.info(
310
+ "Auto-expired anomaly flag (age: %.0fs, ttl: %ds)",
311
+ age_seconds, ttl_seconds,
312
+ )
313
+ return enriched_prompt
314
+ else:
315
+ # Fallback: check file modification time
316
+ mtime = flag_path.stat().st_mtime
317
+ age_seconds = datetime.now().timestamp() - mtime
318
+ if age_seconds > ttl_seconds:
319
+ flag_path.unlink()
320
+ logger.info(
321
+ "Auto-expired anomaly flag by mtime (age: %.0fs, ttl: %ds)",
322
+ age_seconds, ttl_seconds,
323
+ )
324
+ return enriched_prompt
325
+
326
+ anomalies = signal_data.get("anomalies", [])
327
+ summary = "; ".join(a.get("message", "") for a in anomalies if a.get("message"))
328
+ if summary:
329
+ enriched_prompt += (
330
+ f"\n# Anomaly Alert\n"
331
+ f"Recent anomalies detected: {summary}. "
332
+ f"Consider investigating with /gaia.\n"
333
+ )
334
+ flag_path.unlink()
335
+ logger.info("Consumed anomaly flag and injected warning")
336
+ except Exception as e:
337
+ logger.debug(f"Failed to consume anomaly flag (non-fatal): {e}")
338
+ return enriched_prompt
339
+
340
+
341
+ def build_project_context(
342
+ parameters: dict,
343
+ project_agents: list,
344
+ hooks_dir: Path = None,
345
+ ) -> tuple:
346
+ """
347
+ Build project context string for a project agent without mutating parameters.
348
+
349
+ Returns the context string suitable for additionalContext injection, plus a
350
+ telemetry snapshot. Does NOT modify parameters in any way.
351
+
352
+ Args:
353
+ parameters: Task tool parameters (read-only).
354
+ project_agents: List of valid project agent names.
355
+ hooks_dir: Path to the hooks directory (for fallback paths).
356
+ Defaults to Path(__file__).parent.parent.parent if None.
357
+
358
+ Returns:
359
+ (context_string, telemetry_snapshot) or (None, {}) if no context to inject.
360
+ """
361
+ if hooks_dir is None:
362
+ hooks_dir = Path(__file__).parent.parent.parent
363
+
364
+ subagent_type = parameters.get("subagent_type", "")
365
+
366
+ # Only inject for project agents (not for generic agents like Explore, general-purpose, etc.)
367
+ if subagent_type not in project_agents:
368
+ logger.debug(f"Skipping context injection for non-project agent: {subagent_type}")
369
+ return None, {}
370
+
371
+ prompt = parameters.get("prompt", "")
372
+ if not prompt:
373
+ logger.warning(f"No prompt provided for {subagent_type}, skipping context injection")
374
+ return None, {}
375
+
376
+ try:
377
+ # Find context_provider.py
378
+ context_provider_paths = [
379
+ hooks_dir.parent / "tools" / "context" / "context_provider.py", # plugin root (works in both modes)
380
+ Path(".claude/tools/context/context_provider.py"), # npm symlink fallback
381
+ ]
382
+
383
+ context_provider = None
384
+ for path in context_provider_paths:
385
+ if path.exists():
386
+ context_provider = path
387
+ break
388
+
389
+ if not context_provider:
390
+ logger.warning("context_provider.py not found, skipping context injection")
391
+ return None, {}
392
+
393
+ # Execute context_provider.py to get filtered context
394
+ logger.info(f"Building context for {subagent_type}...")
395
+ result = subprocess.run(
396
+ [_find_python(), str(context_provider), subagent_type, prompt],
397
+ capture_output=True,
398
+ text=True,
399
+ timeout=15,
400
+ cwd=os.getcwd()
401
+ )
402
+
403
+ if result.returncode != 0:
404
+ logger.error(f"context_provider.py failed: {result.stderr}")
405
+ return None, {}
406
+
407
+ # Parse context JSON
408
+ try:
409
+ context_payload = json.loads(result.stdout)
410
+ except json.JSONDecodeError as e:
411
+ logger.error(f"Failed to parse context JSON: {e}")
412
+ return None, {}
413
+
414
+ # Extract and save context anchors for hit tracking
415
+ try:
416
+ anchors = extract_anchors(context_payload)
417
+ if anchors:
418
+ session_id = get_or_create_session_id()
419
+ save_anchors(session_id, subagent_type, anchors)
420
+ logger.debug(
421
+ "Saved %d context anchors for %s", len(anchors), subagent_type,
422
+ )
423
+ except Exception as exc:
424
+ logger.debug("Anchor extraction failed (non-fatal): %s", exc)
425
+
426
+ # Check pending update count (non-blocking, fast path)
427
+ pending_warning = check_pending_updates_threshold()
428
+
429
+ # Build context update reminder for empty writable sections
430
+ update_reminder = build_context_update_reminder(
431
+ subagent_type, project_agents, hooks_dir
432
+ )
433
+
434
+ # Build context sections from payload
435
+ project_knowledge = context_payload.get("project_knowledge", {})
436
+ write_perms = context_payload.get("write_permissions", {})
437
+ investigation_brief = context_payload.get("investigation_brief", {})
438
+ rules = context_payload.get("rules", {})
439
+ surface_routing_data = context_payload.get("surface_routing", {})
440
+ metadata = context_payload.get("metadata", {})
441
+ historical = context_payload.get("historical_context", {})
442
+
443
+ # Extract memory_index from historical before JSON rendering to avoid duplication
444
+ memory_index_text = historical.pop("memory_index", "") if historical else ""
445
+ memory_index_section = f"\n### Memory Index\n\n{memory_index_text}\n" if memory_index_text else ""
446
+
447
+ # Optional sections
448
+ rules_section = f"\n## Rules\n\n{json.dumps(rules, indent=2)}\n" if rules.get("universal") or rules.get("agent_specific") else ""
449
+ routing_section = f"\n## Surface Routing\n\n{json.dumps(surface_routing_data, indent=2)}\n" if surface_routing_data else ""
450
+ metadata_section = f"\n## Metadata\n\n{json.dumps(metadata, indent=2)}\n" if metadata else ""
451
+ historical_section = f"\n## Historical Context\n\n{json.dumps(historical, indent=2)}\n" if historical else ""
452
+
453
+ # Build Context Orientation header listing which sections are present
454
+ orientation_lines = ["# Context Orientation\n"]
455
+ orientation_lines.append("Sections present in this payload:\n")
456
+ if project_knowledge:
457
+ orientation_lines.append("- **Project Context** -- structured knowledge about the current project; guides scope and conventions")
458
+ if rules_section:
459
+ orientation_lines.append("- **Rules** -- universal and agent-specific constraints that override default behavior")
460
+ if routing_section:
461
+ orientation_lines.append("- **Surface Routing** -- intent-to-agent mapping; use when delegating or checking ownership")
462
+ if investigation_brief:
463
+ orientation_lines.append("- **Brief** -- goal, acceptance criteria, and scope for the current task")
464
+ if write_perms:
465
+ orientation_lines.append("- **Permissions** -- which context sections are writable vs readable; required before emitting CONTEXT_UPDATE")
466
+ if memory_index_section:
467
+ orientation_lines.append("- **Memory Index** -- ranked memory documents relevant to this session; read high-score entries first")
468
+ if historical_section:
469
+ orientation_lines.append("- **Historical Context** -- past episodes and learned patterns; consult before repeating prior work")
470
+ if metadata_section:
471
+ orientation_lines.append("- **Metadata** -- session and environment metadata; use for debugging and traceability")
472
+ orientation_section = "\n".join(orientation_lines) + "\n"
473
+
474
+ # Save context_payload to disk for downstream hooks (SubagentStop)
475
+ try:
476
+ payload_dir = Path(os.environ.get("TMPDIR", "/tmp")) / "gaia-context-payloads"
477
+ payload_dir.mkdir(parents=True, exist_ok=True)
478
+ agent_id = parameters.get("_agent_id", "") or subagent_type
479
+ payload_path = payload_dir / f"{agent_id}.json"
480
+ payload_path.write_text(json.dumps(context_payload, separators=(',', ':')))
481
+ logger.debug(f"Context payload saved to {payload_path}")
482
+ except Exception as exc:
483
+ logger.debug(f"Failed to save context payload to disk (non-fatal): {exc}")
484
+
485
+ # Build brief as YAML/Markdown-KV with inline field descriptions
486
+ brief_mkv = _dict_to_yaml_annotated(investigation_brief, BRIEF_FIELD_DESCRIPTIONS) if investigation_brief else ""
487
+
488
+ # Build write permissions as YAML/Markdown-KV
489
+ write_perms_dict = {
490
+ "writable": write_perms.get("writable_sections", []),
491
+ "readable": write_perms.get("readable_sections", []),
492
+ "context_update_required": [s for s in write_perms.get("writable_sections", [])
493
+ if not project_knowledge.get(s)],
494
+ }
495
+ write_perms_mkv = _dict_to_yaml(write_perms_dict)
496
+
497
+ context_string = f"""{orientation_section}{rules_section}
498
+ # Project Context
499
+
500
+ {_dict_to_yaml(project_knowledge)}
501
+
502
+ # Routing
503
+ {routing_section}
504
+ # Brief
505
+
506
+ {brief_mkv}
507
+
508
+ # Permissions
509
+
510
+ {write_perms_mkv}
511
+ {memory_index_section}{pending_warning}{update_reminder}{metadata_section}{historical_section}"""
512
+
513
+ # Append anomaly signal flag (consume once)
514
+ context_string = consume_anomaly_flag(context_string)
515
+
516
+ # Surface recent critical anomalies from the JSONL log
517
+ critical_summary = check_recent_critical_anomalies()
518
+ if critical_summary:
519
+ context_string += critical_summary
520
+
521
+ # Inject recent operational events (non-blocking)
522
+ try:
523
+ from ..events.event_writer import read_events
524
+ recent = read_events(hours=24, limit=20)
525
+ if recent:
526
+ lines = ["\n# Recent Events (last 24h)"]
527
+ for evt in recent:
528
+ ts_short = evt.get("ts", "")[:16]
529
+ etype = evt.get("type", "")
530
+ agent_name = evt.get("agent", "")
531
+ result_str = evt.get("result", "")
532
+ label = f"{agent_name}: " if agent_name else ""
533
+ lines.append(f"- [{ts_short}] {etype}: {label}{result_str}")
534
+ context_string += "\n".join(lines) + "\n"
535
+ except Exception as exc:
536
+ logger.debug("Event context injection failed (non-fatal): %s", exc)
537
+
538
+ # Build telemetry snapshot
539
+ telemetry = build_context_telemetry_snapshot(context_payload)
540
+
541
+ sections_count = len(context_payload.get("project_knowledge", {}))
542
+ rules_count = context_payload.get("metadata", {}).get("rules_count", 0)
543
+
544
+ logger.info(
545
+ f"Context built for {subagent_type} "
546
+ f"(sections={sections_count}, rules={rules_count})"
547
+ )
548
+
549
+ return context_string, telemetry
550
+
551
+ except subprocess.TimeoutExpired:
552
+ logger.error("context_provider.py timed out (15s)")
553
+ return None, {}
554
+ except Exception as e:
555
+ logger.error(f"Error building context: {e}", exc_info=True)
556
+ return None, {}
557
+
558
+