@jaguilar87/gaia 5.0.0-rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (609) hide show
  1. package/.claude-plugin/marketplace.json +33 -0
  2. package/.claude-plugin/plugin.json +26 -0
  3. package/ARCHITECTURE.md +335 -0
  4. package/CHANGELOG.md +1212 -0
  5. package/CODE_OF_CONDUCT.md +11 -0
  6. package/CONTRIBUTING.md +146 -0
  7. package/INSTALL.md +436 -0
  8. package/LICENSE +21 -0
  9. package/README.md +222 -0
  10. package/SECURITY.md +47 -0
  11. package/agents/README.md +78 -0
  12. package/agents/cloud-troubleshooter.md +73 -0
  13. package/agents/developer.md +65 -0
  14. package/agents/gaia-operator.md +64 -0
  15. package/agents/gaia-orchestrator.md +237 -0
  16. package/agents/gaia-planner.md +53 -0
  17. package/agents/gaia-system.md +70 -0
  18. package/agents/gitops-operator.md +61 -0
  19. package/agents/terraform-architect.md +63 -0
  20. package/bin/README.md +106 -0
  21. package/bin/cli/__init__.py +1 -0
  22. package/bin/cli/approvals.py +740 -0
  23. package/bin/cli/cleanup.py +562 -0
  24. package/bin/cli/context.py +283 -0
  25. package/bin/cli/doctor.py +628 -0
  26. package/bin/cli/history.py +305 -0
  27. package/bin/cli/memory.py +464 -0
  28. package/bin/cli/metrics.py +1068 -0
  29. package/bin/cli/plans.py +515 -0
  30. package/bin/cli/status.py +302 -0
  31. package/bin/cli/update.py +382 -0
  32. package/bin/gaia +112 -0
  33. package/bin/gaia-cleanup.js +531 -0
  34. package/bin/gaia-doctor.js +635 -0
  35. package/bin/gaia-evidence +126 -0
  36. package/bin/gaia-history.js +251 -0
  37. package/bin/gaia-metrics.js +1278 -0
  38. package/bin/gaia-review.js +269 -0
  39. package/bin/gaia-scan +44 -0
  40. package/bin/gaia-scan.py +589 -0
  41. package/bin/gaia-skills-diagnose.js +929 -0
  42. package/bin/gaia-status.js +278 -0
  43. package/bin/gaia-uninstall.js +111 -0
  44. package/bin/gaia-update.js +816 -0
  45. package/bin/pre-publish-validate.js +610 -0
  46. package/bin/python-detect.js +60 -0
  47. package/commands/README.md +64 -0
  48. package/commands/gaia.md +37 -0
  49. package/commands/scan-project.md +67 -0
  50. package/config/README.md +71 -0
  51. package/config/cloud/aws.json +134 -0
  52. package/config/cloud/gcp.json +139 -0
  53. package/config/context-contracts.json +158 -0
  54. package/config/crons-schema.md +81 -0
  55. package/config/git_standards.json +72 -0
  56. package/config/surface-routing.json +421 -0
  57. package/config/universal-rules.json +102 -0
  58. package/dist/gaia-ops/.claude-plugin/plugin.json +24 -0
  59. package/dist/gaia-ops/README.md +80 -0
  60. package/dist/gaia-ops/agents/cloud-troubleshooter.md +73 -0
  61. package/dist/gaia-ops/agents/developer.md +65 -0
  62. package/dist/gaia-ops/agents/gaia-operator.md +64 -0
  63. package/dist/gaia-ops/agents/gaia-orchestrator.md +237 -0
  64. package/dist/gaia-ops/agents/gaia-planner.md +53 -0
  65. package/dist/gaia-ops/agents/gaia-system.md +70 -0
  66. package/dist/gaia-ops/agents/gitops-operator.md +61 -0
  67. package/dist/gaia-ops/agents/terraform-architect.md +63 -0
  68. package/dist/gaia-ops/commands/gaia.md +37 -0
  69. package/dist/gaia-ops/config/README.md +71 -0
  70. package/dist/gaia-ops/config/cloud/aws.json +134 -0
  71. package/dist/gaia-ops/config/cloud/gcp.json +139 -0
  72. package/dist/gaia-ops/config/context-contracts.json +158 -0
  73. package/dist/gaia-ops/config/crons-schema.md +81 -0
  74. package/dist/gaia-ops/config/git_standards.json +72 -0
  75. package/dist/gaia-ops/config/surface-routing.json +421 -0
  76. package/dist/gaia-ops/config/universal-rules.json +102 -0
  77. package/dist/gaia-ops/hooks/adapters/__init__.py +52 -0
  78. package/dist/gaia-ops/hooks/adapters/base.py +219 -0
  79. package/dist/gaia-ops/hooks/adapters/channel.py +17 -0
  80. package/dist/gaia-ops/hooks/adapters/claude_code.py +1890 -0
  81. package/dist/gaia-ops/hooks/adapters/types.py +194 -0
  82. package/dist/gaia-ops/hooks/adapters/utils.py +25 -0
  83. package/dist/gaia-ops/hooks/hooks.json +163 -0
  84. package/dist/gaia-ops/hooks/modules/__init__.py +15 -0
  85. package/dist/gaia-ops/hooks/modules/agents/__init__.py +29 -0
  86. package/dist/gaia-ops/hooks/modules/agents/contract_validator.py +647 -0
  87. package/dist/gaia-ops/hooks/modules/agents/response_contract.py +496 -0
  88. package/dist/gaia-ops/hooks/modules/agents/skill_injection_verifier.py +120 -0
  89. package/dist/gaia-ops/hooks/modules/agents/state_tracker.py +267 -0
  90. package/dist/gaia-ops/hooks/modules/agents/task_info_builder.py +74 -0
  91. package/dist/gaia-ops/hooks/modules/agents/transcript_analyzer.py +458 -0
  92. package/dist/gaia-ops/hooks/modules/agents/transcript_reader.py +152 -0
  93. package/dist/gaia-ops/hooks/modules/audit/__init__.py +28 -0
  94. package/dist/gaia-ops/hooks/modules/audit/event_detector.py +168 -0
  95. package/dist/gaia-ops/hooks/modules/audit/logger.py +131 -0
  96. package/dist/gaia-ops/hooks/modules/audit/metrics.py +134 -0
  97. package/dist/gaia-ops/hooks/modules/audit/workflow_auditor.py +611 -0
  98. package/dist/gaia-ops/hooks/modules/audit/workflow_recorder.py +296 -0
  99. package/dist/gaia-ops/hooks/modules/context/__init__.py +11 -0
  100. package/dist/gaia-ops/hooks/modules/context/agentic_loop_detector.py +165 -0
  101. package/dist/gaia-ops/hooks/modules/context/anchor_tracker.py +317 -0
  102. package/dist/gaia-ops/hooks/modules/context/compact_context_builder.py +218 -0
  103. package/dist/gaia-ops/hooks/modules/context/context_freshness.py +145 -0
  104. package/dist/gaia-ops/hooks/modules/context/context_injector.py +558 -0
  105. package/dist/gaia-ops/hooks/modules/context/context_writer.py +530 -0
  106. package/dist/gaia-ops/hooks/modules/context/contracts_loader.py +161 -0
  107. package/dist/gaia-ops/hooks/modules/core/__init__.py +40 -0
  108. package/dist/gaia-ops/hooks/modules/core/hook_entry.py +78 -0
  109. package/dist/gaia-ops/hooks/modules/core/paths.py +160 -0
  110. package/dist/gaia-ops/hooks/modules/core/plugin_mode.py +149 -0
  111. package/dist/gaia-ops/hooks/modules/core/plugin_setup.py +577 -0
  112. package/dist/gaia-ops/hooks/modules/core/state.py +179 -0
  113. package/dist/gaia-ops/hooks/modules/core/stdin.py +24 -0
  114. package/dist/gaia-ops/hooks/modules/events/__init__.py +1 -0
  115. package/dist/gaia-ops/hooks/modules/events/event_writer.py +210 -0
  116. package/dist/gaia-ops/hooks/modules/memory/__init__.py +8 -0
  117. package/dist/gaia-ops/hooks/modules/memory/episode_writer.py +216 -0
  118. package/dist/gaia-ops/hooks/modules/orchestrator/__init__.py +1 -0
  119. package/dist/gaia-ops/hooks/modules/orchestrator/delegate_mode.py +122 -0
  120. package/dist/gaia-ops/hooks/modules/scanning/__init__.py +8 -0
  121. package/dist/gaia-ops/hooks/modules/scanning/scan_trigger.py +84 -0
  122. package/dist/gaia-ops/hooks/modules/security/__init__.py +120 -0
  123. package/dist/gaia-ops/hooks/modules/security/approval_cleanup.py +87 -0
  124. package/dist/gaia-ops/hooks/modules/security/approval_constants.py +23 -0
  125. package/dist/gaia-ops/hooks/modules/security/approval_grants.py +1638 -0
  126. package/dist/gaia-ops/hooks/modules/security/approval_messages.py +71 -0
  127. package/dist/gaia-ops/hooks/modules/security/approval_scopes.py +222 -0
  128. package/dist/gaia-ops/hooks/modules/security/blocked_commands.py +595 -0
  129. package/dist/gaia-ops/hooks/modules/security/blocked_message_formatter.py +87 -0
  130. package/dist/gaia-ops/hooks/modules/security/command_semantics.py +181 -0
  131. package/dist/gaia-ops/hooks/modules/security/composition_rules.py +547 -0
  132. package/dist/gaia-ops/hooks/modules/security/flag_classifiers.py +873 -0
  133. package/dist/gaia-ops/hooks/modules/security/gitops_validator.py +179 -0
  134. package/dist/gaia-ops/hooks/modules/security/mutative_verbs.py +1131 -0
  135. package/dist/gaia-ops/hooks/modules/security/network_hosts.py +481 -0
  136. package/dist/gaia-ops/hooks/modules/security/prompt_validator.py +40 -0
  137. package/dist/gaia-ops/hooks/modules/security/shell_unwrapper.py +165 -0
  138. package/dist/gaia-ops/hooks/modules/security/tiers.py +196 -0
  139. package/dist/gaia-ops/hooks/modules/session/__init__.py +10 -0
  140. package/dist/gaia-ops/hooks/modules/session/pending_scanner.py +174 -0
  141. package/dist/gaia-ops/hooks/modules/session/session_context_writer.py +100 -0
  142. package/dist/gaia-ops/hooks/modules/session/session_event_injector.py +160 -0
  143. package/dist/gaia-ops/hooks/modules/session/session_manager.py +31 -0
  144. package/dist/gaia-ops/hooks/modules/session/session_registry.py +232 -0
  145. package/dist/gaia-ops/hooks/modules/tools/__init__.py +29 -0
  146. package/dist/gaia-ops/hooks/modules/tools/bash_validator.py +1008 -0
  147. package/dist/gaia-ops/hooks/modules/tools/cloud_pipe_validator.py +231 -0
  148. package/dist/gaia-ops/hooks/modules/tools/hook_response.py +55 -0
  149. package/dist/gaia-ops/hooks/modules/tools/shell_parser.py +227 -0
  150. package/dist/gaia-ops/hooks/modules/tools/stage_decomposer.py +315 -0
  151. package/dist/gaia-ops/hooks/modules/tools/task_validator.py +294 -0
  152. package/dist/gaia-ops/hooks/modules/validation/__init__.py +23 -0
  153. package/dist/gaia-ops/hooks/modules/validation/commit_validator.py +380 -0
  154. package/dist/gaia-ops/hooks/post_compact.py +43 -0
  155. package/dist/gaia-ops/hooks/post_tool_use.py +54 -0
  156. package/dist/gaia-ops/hooks/pre_compact.py +60 -0
  157. package/dist/gaia-ops/hooks/pre_tool_use.py +413 -0
  158. package/dist/gaia-ops/hooks/session_start.py +81 -0
  159. package/dist/gaia-ops/hooks/stop_hook.py +82 -0
  160. package/dist/gaia-ops/hooks/subagent_start.py +71 -0
  161. package/dist/gaia-ops/hooks/subagent_stop.py +295 -0
  162. package/dist/gaia-ops/hooks/task_completed.py +70 -0
  163. package/dist/gaia-ops/hooks/user_prompt_submit.py +246 -0
  164. package/dist/gaia-ops/settings.json +72 -0
  165. package/dist/gaia-ops/skills/README.md +154 -0
  166. package/dist/gaia-ops/skills/agent-protocol/SKILL.md +93 -0
  167. package/dist/gaia-ops/skills/agent-protocol/examples.md +223 -0
  168. package/dist/gaia-ops/skills/agent-response/SKILL.md +69 -0
  169. package/dist/gaia-ops/skills/agentic-loop/SKILL.md +80 -0
  170. package/dist/gaia-ops/skills/agentic-loop/reference.md +378 -0
  171. package/dist/gaia-ops/skills/blog-writing/SKILL.md +98 -0
  172. package/dist/gaia-ops/skills/blog-writing/reference.md +130 -0
  173. package/dist/gaia-ops/skills/brief-spec/SKILL.md +182 -0
  174. package/dist/gaia-ops/skills/command-execution/SKILL.md +64 -0
  175. package/dist/gaia-ops/skills/command-execution/reference.md +83 -0
  176. package/dist/gaia-ops/skills/context-updater/SKILL.md +87 -0
  177. package/dist/gaia-ops/skills/context-updater/examples.md +71 -0
  178. package/dist/gaia-ops/skills/developer-patterns/SKILL.md +50 -0
  179. package/dist/gaia-ops/skills/developer-patterns/reference.md +112 -0
  180. package/dist/gaia-ops/skills/execution/SKILL.md +99 -0
  181. package/dist/gaia-ops/skills/fast-queries/SKILL.md +43 -0
  182. package/dist/gaia-ops/skills/gaia-compact/SKILL.md +74 -0
  183. package/dist/gaia-ops/skills/gaia-patterns/SKILL.md +108 -0
  184. package/dist/gaia-ops/skills/gaia-patterns/reference.md +395 -0
  185. package/dist/gaia-ops/skills/gaia-planner/SKILL.md +37 -0
  186. package/dist/gaia-ops/skills/gaia-planner/reference.md +107 -0
  187. package/dist/gaia-ops/skills/gaia-release/SKILL.md +82 -0
  188. package/dist/gaia-ops/skills/gaia-release/reference.md +102 -0
  189. package/dist/gaia-ops/skills/gaia-self-check/SKILL.md +114 -0
  190. package/dist/gaia-ops/skills/gaia-self-check/reference.md +453 -0
  191. package/dist/gaia-ops/skills/gaia-verify/SKILL.md +77 -0
  192. package/dist/gaia-ops/skills/gaia-verify/reference.md +80 -0
  193. package/dist/gaia-ops/skills/git-conventions/SKILL.md +47 -0
  194. package/dist/gaia-ops/skills/gitops-patterns/SKILL.md +60 -0
  195. package/dist/gaia-ops/skills/gitops-patterns/reference.md +183 -0
  196. package/dist/gaia-ops/skills/gmail-policy/SKILL.md +200 -0
  197. package/dist/gaia-ops/skills/gmail-policy/reference.md +150 -0
  198. package/dist/gaia-ops/skills/gmail-triage/SKILL.md +100 -0
  199. package/dist/gaia-ops/skills/gws-setup/SKILL.md +99 -0
  200. package/dist/gaia-ops/skills/gws-setup/reference.md +73 -0
  201. package/dist/gaia-ops/skills/investigation/SKILL.md +100 -0
  202. package/dist/gaia-ops/skills/memory-curation/SKILL.md +83 -0
  203. package/dist/gaia-ops/skills/memory-search/SKILL.md +88 -0
  204. package/dist/gaia-ops/skills/orchestrator-approval/SKILL.md +160 -0
  205. package/dist/gaia-ops/skills/orchestrator-approval/reference.md +174 -0
  206. package/dist/gaia-ops/skills/pending-approvals/SKILL.md +72 -0
  207. package/dist/gaia-ops/skills/pending-approvals/reference.md +214 -0
  208. package/dist/gaia-ops/skills/readme-writing/SKILL.md +71 -0
  209. package/dist/gaia-ops/skills/readme-writing/reference.md +188 -0
  210. package/dist/gaia-ops/skills/reference.md +135 -0
  211. package/dist/gaia-ops/skills/request-approval/SKILL.md +140 -0
  212. package/dist/gaia-ops/skills/request-approval/examples.md +140 -0
  213. package/dist/gaia-ops/skills/request-approval/reference.md +57 -0
  214. package/dist/gaia-ops/skills/schedule-task/SKILL.md +64 -0
  215. package/dist/gaia-ops/skills/schedule-task/reference.md +233 -0
  216. package/dist/gaia-ops/skills/security-tiers/SKILL.md +141 -0
  217. package/dist/gaia-ops/skills/security-tiers/destructive-commands-reference.md +623 -0
  218. package/dist/gaia-ops/skills/security-tiers/reference.md +39 -0
  219. package/dist/gaia-ops/skills/skill-creation/SKILL.md +92 -0
  220. package/dist/gaia-ops/skills/skill-creation/reference.md +29 -0
  221. package/dist/gaia-ops/skills/terraform-patterns/SKILL.md +89 -0
  222. package/dist/gaia-ops/skills/terraform-patterns/reference.md +93 -0
  223. package/dist/gaia-ops/tools/__init__.py +9 -0
  224. package/dist/gaia-ops/tools/agentic-loop/decide-status.py +210 -0
  225. package/dist/gaia-ops/tools/agentic-loop/parse-metric.py +106 -0
  226. package/dist/gaia-ops/tools/agentic-loop/record-iteration.py +221 -0
  227. package/dist/gaia-ops/tools/context/README.md +132 -0
  228. package/dist/gaia-ops/tools/context/__init__.py +42 -0
  229. package/dist/gaia-ops/tools/context/_paths.py +20 -0
  230. package/dist/gaia-ops/tools/context/context_provider.py +721 -0
  231. package/dist/gaia-ops/tools/context/context_section_reader.py +342 -0
  232. package/dist/gaia-ops/tools/context/deep_merge.py +159 -0
  233. package/dist/gaia-ops/tools/context/pending_updates.py +760 -0
  234. package/dist/gaia-ops/tools/context/surface_router.py +278 -0
  235. package/dist/gaia-ops/tools/fast-queries/README.md +65 -0
  236. package/dist/gaia-ops/tools/fast-queries/__init__.py +30 -0
  237. package/dist/gaia-ops/tools/fast-queries/appservices/quicktriage_devops_developer.sh +75 -0
  238. package/dist/gaia-ops/tools/fast-queries/cloud/aws/quicktriage_aws_troubleshooter.sh +32 -0
  239. package/dist/gaia-ops/tools/fast-queries/cloud/gcp/quicktriage_gcp_troubleshooter.sh +88 -0
  240. package/dist/gaia-ops/tools/fast-queries/gitops/quicktriage_gitops_operator.sh +48 -0
  241. package/dist/gaia-ops/tools/fast-queries/run_triage.sh +59 -0
  242. package/dist/gaia-ops/tools/fast-queries/terraform/quicktriage_terraform_architect.sh +80 -0
  243. package/dist/gaia-ops/tools/gaia_simulator/__init__.py +33 -0
  244. package/dist/gaia-ops/tools/gaia_simulator/cli.py +354 -0
  245. package/dist/gaia-ops/tools/gaia_simulator/extractor.py +457 -0
  246. package/dist/gaia-ops/tools/gaia_simulator/reporter.py +258 -0
  247. package/dist/gaia-ops/tools/gaia_simulator/routing_simulator.py +334 -0
  248. package/dist/gaia-ops/tools/gaia_simulator/runner.py +539 -0
  249. package/dist/gaia-ops/tools/gaia_simulator/skills_mapper.py +264 -0
  250. package/dist/gaia-ops/tools/memory/README.md +0 -0
  251. package/dist/gaia-ops/tools/memory/__init__.py +20 -0
  252. package/dist/gaia-ops/tools/memory/backfill_fts5.py +107 -0
  253. package/dist/gaia-ops/tools/memory/conflict_detector.py +295 -0
  254. package/dist/gaia-ops/tools/memory/episodic.py +1210 -0
  255. package/dist/gaia-ops/tools/memory/git_invalidator.py +262 -0
  256. package/dist/gaia-ops/tools/memory/paths.py +102 -0
  257. package/dist/gaia-ops/tools/memory/scoring.py +193 -0
  258. package/dist/gaia-ops/tools/memory/search_store.py +360 -0
  259. package/dist/gaia-ops/tools/persist_transcript_analysis.py +85 -0
  260. package/dist/gaia-ops/tools/review/__init__.py +1 -0
  261. package/dist/gaia-ops/tools/review/review_engine.py +157 -0
  262. package/dist/gaia-ops/tools/scan/__init__.py +35 -0
  263. package/dist/gaia-ops/tools/scan/config.py +247 -0
  264. package/dist/gaia-ops/tools/scan/merge.py +212 -0
  265. package/dist/gaia-ops/tools/scan/orchestrator.py +549 -0
  266. package/dist/gaia-ops/tools/scan/registry.py +127 -0
  267. package/dist/gaia-ops/tools/scan/scanners/__init__.py +18 -0
  268. package/dist/gaia-ops/tools/scan/scanners/base.py +137 -0
  269. package/dist/gaia-ops/tools/scan/scanners/environment.py +349 -0
  270. package/dist/gaia-ops/tools/scan/scanners/git.py +570 -0
  271. package/dist/gaia-ops/tools/scan/scanners/infrastructure.py +875 -0
  272. package/dist/gaia-ops/tools/scan/scanners/orchestration.py +600 -0
  273. package/dist/gaia-ops/tools/scan/scanners/stack.py +1085 -0
  274. package/dist/gaia-ops/tools/scan/scanners/tools.py +260 -0
  275. package/dist/gaia-ops/tools/scan/setup.py +686 -0
  276. package/dist/gaia-ops/tools/scan/tests/__init__.py +1 -0
  277. package/dist/gaia-ops/tools/scan/tests/conftest.py +796 -0
  278. package/dist/gaia-ops/tools/scan/tests/test_environment.py +323 -0
  279. package/dist/gaia-ops/tools/scan/tests/test_git.py +419 -0
  280. package/dist/gaia-ops/tools/scan/tests/test_infrastructure.py +382 -0
  281. package/dist/gaia-ops/tools/scan/tests/test_integration.py +920 -0
  282. package/dist/gaia-ops/tools/scan/tests/test_merge.py +269 -0
  283. package/dist/gaia-ops/tools/scan/tests/test_orchestration.py +304 -0
  284. package/dist/gaia-ops/tools/scan/tests/test_stack.py +604 -0
  285. package/dist/gaia-ops/tools/scan/tests/test_tools.py +349 -0
  286. package/dist/gaia-ops/tools/scan/ui.py +624 -0
  287. package/dist/gaia-ops/tools/scan/verify.py +270 -0
  288. package/dist/gaia-ops/tools/scan/walk.py +118 -0
  289. package/dist/gaia-ops/tools/scan/workspace.py +85 -0
  290. package/dist/gaia-ops/tools/validation/README.md +244 -0
  291. package/dist/gaia-ops/tools/validation/__init__.py +17 -0
  292. package/dist/gaia-ops/tools/validation/approval_gate.py +321 -0
  293. package/dist/gaia-ops/tools/validation/validate_skills.py +189 -0
  294. package/dist/gaia-security/.claude-plugin/plugin.json +24 -0
  295. package/dist/gaia-security/README.md +90 -0
  296. package/dist/gaia-security/config/universal-rules.json +102 -0
  297. package/dist/gaia-security/hooks/adapters/__init__.py +52 -0
  298. package/dist/gaia-security/hooks/adapters/base.py +219 -0
  299. package/dist/gaia-security/hooks/adapters/channel.py +17 -0
  300. package/dist/gaia-security/hooks/adapters/claude_code.py +1890 -0
  301. package/dist/gaia-security/hooks/adapters/types.py +194 -0
  302. package/dist/gaia-security/hooks/adapters/utils.py +25 -0
  303. package/dist/gaia-security/hooks/hooks.json +84 -0
  304. package/dist/gaia-security/hooks/modules/__init__.py +15 -0
  305. package/dist/gaia-security/hooks/modules/agents/__init__.py +29 -0
  306. package/dist/gaia-security/hooks/modules/agents/contract_validator.py +647 -0
  307. package/dist/gaia-security/hooks/modules/agents/response_contract.py +496 -0
  308. package/dist/gaia-security/hooks/modules/agents/skill_injection_verifier.py +120 -0
  309. package/dist/gaia-security/hooks/modules/agents/state_tracker.py +267 -0
  310. package/dist/gaia-security/hooks/modules/agents/task_info_builder.py +74 -0
  311. package/dist/gaia-security/hooks/modules/agents/transcript_analyzer.py +458 -0
  312. package/dist/gaia-security/hooks/modules/agents/transcript_reader.py +152 -0
  313. package/dist/gaia-security/hooks/modules/audit/__init__.py +28 -0
  314. package/dist/gaia-security/hooks/modules/audit/event_detector.py +168 -0
  315. package/dist/gaia-security/hooks/modules/audit/logger.py +131 -0
  316. package/dist/gaia-security/hooks/modules/audit/metrics.py +134 -0
  317. package/dist/gaia-security/hooks/modules/audit/workflow_auditor.py +611 -0
  318. package/dist/gaia-security/hooks/modules/audit/workflow_recorder.py +296 -0
  319. package/dist/gaia-security/hooks/modules/context/__init__.py +11 -0
  320. package/dist/gaia-security/hooks/modules/context/agentic_loop_detector.py +165 -0
  321. package/dist/gaia-security/hooks/modules/context/anchor_tracker.py +317 -0
  322. package/dist/gaia-security/hooks/modules/context/compact_context_builder.py +218 -0
  323. package/dist/gaia-security/hooks/modules/context/context_freshness.py +145 -0
  324. package/dist/gaia-security/hooks/modules/context/context_injector.py +558 -0
  325. package/dist/gaia-security/hooks/modules/context/context_writer.py +530 -0
  326. package/dist/gaia-security/hooks/modules/context/contracts_loader.py +161 -0
  327. package/dist/gaia-security/hooks/modules/core/__init__.py +40 -0
  328. package/dist/gaia-security/hooks/modules/core/hook_entry.py +78 -0
  329. package/dist/gaia-security/hooks/modules/core/paths.py +160 -0
  330. package/dist/gaia-security/hooks/modules/core/plugin_mode.py +149 -0
  331. package/dist/gaia-security/hooks/modules/core/plugin_setup.py +577 -0
  332. package/dist/gaia-security/hooks/modules/core/state.py +179 -0
  333. package/dist/gaia-security/hooks/modules/core/stdin.py +24 -0
  334. package/dist/gaia-security/hooks/modules/events/__init__.py +1 -0
  335. package/dist/gaia-security/hooks/modules/events/event_writer.py +210 -0
  336. package/dist/gaia-security/hooks/modules/memory/__init__.py +8 -0
  337. package/dist/gaia-security/hooks/modules/memory/episode_writer.py +216 -0
  338. package/dist/gaia-security/hooks/modules/orchestrator/__init__.py +1 -0
  339. package/dist/gaia-security/hooks/modules/orchestrator/delegate_mode.py +122 -0
  340. package/dist/gaia-security/hooks/modules/scanning/__init__.py +8 -0
  341. package/dist/gaia-security/hooks/modules/scanning/scan_trigger.py +84 -0
  342. package/dist/gaia-security/hooks/modules/security/__init__.py +120 -0
  343. package/dist/gaia-security/hooks/modules/security/approval_cleanup.py +87 -0
  344. package/dist/gaia-security/hooks/modules/security/approval_constants.py +23 -0
  345. package/dist/gaia-security/hooks/modules/security/approval_grants.py +1638 -0
  346. package/dist/gaia-security/hooks/modules/security/approval_messages.py +71 -0
  347. package/dist/gaia-security/hooks/modules/security/approval_scopes.py +222 -0
  348. package/dist/gaia-security/hooks/modules/security/blocked_commands.py +595 -0
  349. package/dist/gaia-security/hooks/modules/security/blocked_message_formatter.py +87 -0
  350. package/dist/gaia-security/hooks/modules/security/command_semantics.py +181 -0
  351. package/dist/gaia-security/hooks/modules/security/composition_rules.py +547 -0
  352. package/dist/gaia-security/hooks/modules/security/flag_classifiers.py +873 -0
  353. package/dist/gaia-security/hooks/modules/security/gitops_validator.py +179 -0
  354. package/dist/gaia-security/hooks/modules/security/mutative_verbs.py +1131 -0
  355. package/dist/gaia-security/hooks/modules/security/network_hosts.py +481 -0
  356. package/dist/gaia-security/hooks/modules/security/prompt_validator.py +40 -0
  357. package/dist/gaia-security/hooks/modules/security/shell_unwrapper.py +165 -0
  358. package/dist/gaia-security/hooks/modules/security/tiers.py +196 -0
  359. package/dist/gaia-security/hooks/modules/session/__init__.py +10 -0
  360. package/dist/gaia-security/hooks/modules/session/pending_scanner.py +174 -0
  361. package/dist/gaia-security/hooks/modules/session/session_context_writer.py +100 -0
  362. package/dist/gaia-security/hooks/modules/session/session_event_injector.py +160 -0
  363. package/dist/gaia-security/hooks/modules/session/session_manager.py +31 -0
  364. package/dist/gaia-security/hooks/modules/session/session_registry.py +232 -0
  365. package/dist/gaia-security/hooks/modules/tools/__init__.py +29 -0
  366. package/dist/gaia-security/hooks/modules/tools/bash_validator.py +1008 -0
  367. package/dist/gaia-security/hooks/modules/tools/cloud_pipe_validator.py +231 -0
  368. package/dist/gaia-security/hooks/modules/tools/hook_response.py +55 -0
  369. package/dist/gaia-security/hooks/modules/tools/shell_parser.py +227 -0
  370. package/dist/gaia-security/hooks/modules/tools/stage_decomposer.py +315 -0
  371. package/dist/gaia-security/hooks/modules/tools/task_validator.py +294 -0
  372. package/dist/gaia-security/hooks/modules/validation/__init__.py +23 -0
  373. package/dist/gaia-security/hooks/modules/validation/commit_validator.py +380 -0
  374. package/dist/gaia-security/hooks/post_tool_use.py +54 -0
  375. package/dist/gaia-security/hooks/pre_tool_use.py +413 -0
  376. package/dist/gaia-security/hooks/session_start.py +81 -0
  377. package/dist/gaia-security/hooks/stop_hook.py +82 -0
  378. package/dist/gaia-security/hooks/user_prompt_submit.py +246 -0
  379. package/dist/gaia-security/settings.json +58 -0
  380. package/git-hooks/commit-msg +41 -0
  381. package/hooks/README.md +100 -0
  382. package/hooks/adapters/__init__.py +52 -0
  383. package/hooks/adapters/base.py +219 -0
  384. package/hooks/adapters/channel.py +17 -0
  385. package/hooks/adapters/claude_code.py +1890 -0
  386. package/hooks/adapters/types.py +194 -0
  387. package/hooks/adapters/utils.py +25 -0
  388. package/hooks/elicitation_result.py +179 -0
  389. package/hooks/hooks.json +84 -0
  390. package/hooks/modules/README.md +189 -0
  391. package/hooks/modules/__init__.py +15 -0
  392. package/hooks/modules/agents/__init__.py +29 -0
  393. package/hooks/modules/agents/contract_validator.py +647 -0
  394. package/hooks/modules/agents/response_contract.py +496 -0
  395. package/hooks/modules/agents/skill_injection_verifier.py +120 -0
  396. package/hooks/modules/agents/state_tracker.py +267 -0
  397. package/hooks/modules/agents/task_info_builder.py +74 -0
  398. package/hooks/modules/agents/transcript_analyzer.py +458 -0
  399. package/hooks/modules/agents/transcript_reader.py +152 -0
  400. package/hooks/modules/audit/__init__.py +28 -0
  401. package/hooks/modules/audit/event_detector.py +168 -0
  402. package/hooks/modules/audit/logger.py +131 -0
  403. package/hooks/modules/audit/metrics.py +134 -0
  404. package/hooks/modules/audit/workflow_auditor.py +611 -0
  405. package/hooks/modules/audit/workflow_recorder.py +296 -0
  406. package/hooks/modules/context/__init__.py +11 -0
  407. package/hooks/modules/context/agentic_loop_detector.py +165 -0
  408. package/hooks/modules/context/anchor_tracker.py +317 -0
  409. package/hooks/modules/context/compact_context_builder.py +218 -0
  410. package/hooks/modules/context/context_freshness.py +145 -0
  411. package/hooks/modules/context/context_injector.py +558 -0
  412. package/hooks/modules/context/context_writer.py +530 -0
  413. package/hooks/modules/context/contracts_loader.py +161 -0
  414. package/hooks/modules/core/__init__.py +40 -0
  415. package/hooks/modules/core/hook_entry.py +78 -0
  416. package/hooks/modules/core/paths.py +160 -0
  417. package/hooks/modules/core/plugin_mode.py +149 -0
  418. package/hooks/modules/core/plugin_setup.py +577 -0
  419. package/hooks/modules/core/state.py +179 -0
  420. package/hooks/modules/core/stdin.py +24 -0
  421. package/hooks/modules/events/__init__.py +1 -0
  422. package/hooks/modules/events/event_writer.py +210 -0
  423. package/hooks/modules/evidence/__init__.py +34 -0
  424. package/hooks/modules/evidence/assertions.py +137 -0
  425. package/hooks/modules/evidence/index_writer.py +57 -0
  426. package/hooks/modules/evidence/loader.py +126 -0
  427. package/hooks/modules/evidence/runner.py +241 -0
  428. package/hooks/modules/memory/__init__.py +8 -0
  429. package/hooks/modules/memory/episode_writer.py +216 -0
  430. package/hooks/modules/orchestrator/__init__.py +1 -0
  431. package/hooks/modules/orchestrator/delegate_mode.py +122 -0
  432. package/hooks/modules/scanning/__init__.py +8 -0
  433. package/hooks/modules/scanning/scan_trigger.py +84 -0
  434. package/hooks/modules/security/__init__.py +120 -0
  435. package/hooks/modules/security/approval_cleanup.py +87 -0
  436. package/hooks/modules/security/approval_constants.py +23 -0
  437. package/hooks/modules/security/approval_grants.py +1638 -0
  438. package/hooks/modules/security/approval_messages.py +71 -0
  439. package/hooks/modules/security/approval_scopes.py +222 -0
  440. package/hooks/modules/security/blocked_commands.py +595 -0
  441. package/hooks/modules/security/blocked_message_formatter.py +87 -0
  442. package/hooks/modules/security/command_semantics.py +181 -0
  443. package/hooks/modules/security/composition_rules.py +547 -0
  444. package/hooks/modules/security/flag_classifiers.py +873 -0
  445. package/hooks/modules/security/gitops_validator.py +179 -0
  446. package/hooks/modules/security/mutative_verbs.py +1131 -0
  447. package/hooks/modules/security/network_hosts.py +481 -0
  448. package/hooks/modules/security/prompt_validator.py +40 -0
  449. package/hooks/modules/security/shell_unwrapper.py +165 -0
  450. package/hooks/modules/security/tiers.py +196 -0
  451. package/hooks/modules/session/__init__.py +10 -0
  452. package/hooks/modules/session/pending_scanner.py +174 -0
  453. package/hooks/modules/session/session_context_writer.py +100 -0
  454. package/hooks/modules/session/session_event_injector.py +160 -0
  455. package/hooks/modules/session/session_manager.py +31 -0
  456. package/hooks/modules/session/session_registry.py +232 -0
  457. package/hooks/modules/tools/__init__.py +29 -0
  458. package/hooks/modules/tools/bash_validator.py +1008 -0
  459. package/hooks/modules/tools/cloud_pipe_validator.py +231 -0
  460. package/hooks/modules/tools/hook_response.py +55 -0
  461. package/hooks/modules/tools/shell_parser.py +227 -0
  462. package/hooks/modules/tools/stage_decomposer.py +315 -0
  463. package/hooks/modules/tools/task_validator.py +294 -0
  464. package/hooks/modules/validation/__init__.py +23 -0
  465. package/hooks/modules/validation/commit_validator.py +380 -0
  466. package/hooks/post_compact.py +43 -0
  467. package/hooks/post_tool_use.py +54 -0
  468. package/hooks/pre_compact.py +60 -0
  469. package/hooks/pre_tool_use.py +413 -0
  470. package/hooks/session_start.py +81 -0
  471. package/hooks/stop_hook.py +82 -0
  472. package/hooks/subagent_start.py +71 -0
  473. package/hooks/subagent_stop.py +295 -0
  474. package/hooks/task_completed.py +70 -0
  475. package/hooks/user_prompt_submit.py +246 -0
  476. package/index.js +83 -0
  477. package/package.json +99 -0
  478. package/pyproject.toml +32 -0
  479. package/skills/README.md +154 -0
  480. package/skills/agent-protocol/SKILL.md +93 -0
  481. package/skills/agent-protocol/examples.md +223 -0
  482. package/skills/agent-response/SKILL.md +69 -0
  483. package/skills/agentic-loop/SKILL.md +80 -0
  484. package/skills/agentic-loop/reference.md +378 -0
  485. package/skills/blog-writing/SKILL.md +98 -0
  486. package/skills/blog-writing/reference.md +130 -0
  487. package/skills/brief-spec/SKILL.md +182 -0
  488. package/skills/command-execution/SKILL.md +64 -0
  489. package/skills/command-execution/reference.md +83 -0
  490. package/skills/context-updater/SKILL.md +87 -0
  491. package/skills/context-updater/examples.md +71 -0
  492. package/skills/developer-patterns/SKILL.md +50 -0
  493. package/skills/developer-patterns/reference.md +112 -0
  494. package/skills/execution/SKILL.md +99 -0
  495. package/skills/fast-queries/SKILL.md +43 -0
  496. package/skills/gaia-compact/SKILL.md +74 -0
  497. package/skills/gaia-patterns/SKILL.md +108 -0
  498. package/skills/gaia-patterns/reference.md +395 -0
  499. package/skills/gaia-planner/SKILL.md +37 -0
  500. package/skills/gaia-planner/reference.md +107 -0
  501. package/skills/gaia-release/SKILL.md +82 -0
  502. package/skills/gaia-release/reference.md +102 -0
  503. package/skills/gaia-self-check/SKILL.md +114 -0
  504. package/skills/gaia-self-check/reference.md +453 -0
  505. package/skills/gaia-verify/SKILL.md +77 -0
  506. package/skills/gaia-verify/reference.md +80 -0
  507. package/skills/git-conventions/SKILL.md +47 -0
  508. package/skills/gitops-patterns/SKILL.md +60 -0
  509. package/skills/gitops-patterns/reference.md +183 -0
  510. package/skills/gmail-policy/SKILL.md +200 -0
  511. package/skills/gmail-policy/reference.md +150 -0
  512. package/skills/gmail-triage/SKILL.md +100 -0
  513. package/skills/gws-setup/SKILL.md +99 -0
  514. package/skills/gws-setup/reference.md +73 -0
  515. package/skills/investigation/SKILL.md +100 -0
  516. package/skills/memory-curation/SKILL.md +83 -0
  517. package/skills/memory-search/SKILL.md +88 -0
  518. package/skills/orchestrator-approval/SKILL.md +160 -0
  519. package/skills/orchestrator-approval/reference.md +174 -0
  520. package/skills/pending-approvals/SKILL.md +72 -0
  521. package/skills/pending-approvals/reference.md +214 -0
  522. package/skills/readme-writing/SKILL.md +71 -0
  523. package/skills/readme-writing/reference.md +188 -0
  524. package/skills/reference.md +135 -0
  525. package/skills/request-approval/SKILL.md +140 -0
  526. package/skills/request-approval/examples.md +140 -0
  527. package/skills/request-approval/reference.md +57 -0
  528. package/skills/schedule-task/SKILL.md +64 -0
  529. package/skills/schedule-task/reference.md +233 -0
  530. package/skills/security-tiers/SKILL.md +141 -0
  531. package/skills/security-tiers/destructive-commands-reference.md +623 -0
  532. package/skills/security-tiers/reference.md +39 -0
  533. package/skills/skill-creation/SKILL.md +92 -0
  534. package/skills/skill-creation/reference.md +29 -0
  535. package/skills/terraform-patterns/SKILL.md +89 -0
  536. package/skills/terraform-patterns/reference.md +93 -0
  537. package/templates/README.md +69 -0
  538. package/templates/managed-settings.template.json +43 -0
  539. package/tools/__init__.py +9 -0
  540. package/tools/agentic-loop/decide-status.py +210 -0
  541. package/tools/agentic-loop/parse-metric.py +106 -0
  542. package/tools/agentic-loop/record-iteration.py +221 -0
  543. package/tools/context/README.md +132 -0
  544. package/tools/context/__init__.py +42 -0
  545. package/tools/context/_paths.py +20 -0
  546. package/tools/context/context_provider.py +721 -0
  547. package/tools/context/context_section_reader.py +342 -0
  548. package/tools/context/deep_merge.py +159 -0
  549. package/tools/context/pending_updates.py +760 -0
  550. package/tools/context/surface_router.py +278 -0
  551. package/tools/fast-queries/README.md +65 -0
  552. package/tools/fast-queries/__init__.py +30 -0
  553. package/tools/fast-queries/appservices/quicktriage_devops_developer.sh +75 -0
  554. package/tools/fast-queries/cloud/aws/quicktriage_aws_troubleshooter.sh +32 -0
  555. package/tools/fast-queries/cloud/gcp/quicktriage_gcp_troubleshooter.sh +88 -0
  556. package/tools/fast-queries/gitops/quicktriage_gitops_operator.sh +48 -0
  557. package/tools/fast-queries/run_triage.sh +59 -0
  558. package/tools/fast-queries/terraform/quicktriage_terraform_architect.sh +80 -0
  559. package/tools/gaia_simulator/__init__.py +33 -0
  560. package/tools/gaia_simulator/cli.py +354 -0
  561. package/tools/gaia_simulator/extractor.py +457 -0
  562. package/tools/gaia_simulator/reporter.py +258 -0
  563. package/tools/gaia_simulator/routing_simulator.py +334 -0
  564. package/tools/gaia_simulator/runner.py +539 -0
  565. package/tools/gaia_simulator/skills_mapper.py +264 -0
  566. package/tools/memory/README.md +0 -0
  567. package/tools/memory/__init__.py +20 -0
  568. package/tools/memory/backfill_fts5.py +107 -0
  569. package/tools/memory/conflict_detector.py +295 -0
  570. package/tools/memory/episodic.py +1210 -0
  571. package/tools/memory/git_invalidator.py +262 -0
  572. package/tools/memory/paths.py +102 -0
  573. package/tools/memory/scoring.py +193 -0
  574. package/tools/memory/search_store.py +360 -0
  575. package/tools/persist_transcript_analysis.py +85 -0
  576. package/tools/review/__init__.py +1 -0
  577. package/tools/review/review_engine.py +157 -0
  578. package/tools/scan/__init__.py +35 -0
  579. package/tools/scan/config.py +247 -0
  580. package/tools/scan/merge.py +212 -0
  581. package/tools/scan/orchestrator.py +549 -0
  582. package/tools/scan/registry.py +127 -0
  583. package/tools/scan/scanners/__init__.py +18 -0
  584. package/tools/scan/scanners/base.py +137 -0
  585. package/tools/scan/scanners/environment.py +349 -0
  586. package/tools/scan/scanners/git.py +570 -0
  587. package/tools/scan/scanners/infrastructure.py +875 -0
  588. package/tools/scan/scanners/orchestration.py +600 -0
  589. package/tools/scan/scanners/stack.py +1085 -0
  590. package/tools/scan/scanners/tools.py +260 -0
  591. package/tools/scan/setup.py +686 -0
  592. package/tools/scan/tests/__init__.py +1 -0
  593. package/tools/scan/tests/conftest.py +796 -0
  594. package/tools/scan/tests/test_environment.py +323 -0
  595. package/tools/scan/tests/test_git.py +419 -0
  596. package/tools/scan/tests/test_infrastructure.py +382 -0
  597. package/tools/scan/tests/test_integration.py +920 -0
  598. package/tools/scan/tests/test_merge.py +269 -0
  599. package/tools/scan/tests/test_orchestration.py +304 -0
  600. package/tools/scan/tests/test_stack.py +604 -0
  601. package/tools/scan/tests/test_tools.py +349 -0
  602. package/tools/scan/ui.py +624 -0
  603. package/tools/scan/verify.py +270 -0
  604. package/tools/scan/walk.py +118 -0
  605. package/tools/scan/workspace.py +85 -0
  606. package/tools/validation/README.md +244 -0
  607. package/tools/validation/__init__.py +17 -0
  608. package/tools/validation/approval_gate.py +321 -0
  609. package/tools/validation/validate_skills.py +189 -0
@@ -0,0 +1,126 @@
1
+ """
2
+ Brief frontmatter loader.
3
+
4
+ Contract:
5
+ load_acs(brief_path: Path) -> list[Evidence]
6
+
7
+ Evidence fields:
8
+ id: str # "AC-1", "AC-2", ...
9
+ description: str
10
+ type: str # command | url | playwright | artifact | metric
11
+ shape: dict # type-specific
12
+ artifact: str # relative artifact path
13
+
14
+ Exceptions:
15
+ InvalidBriefFrontmatter -- YAML parse error or missing '---' markers.
16
+ UnknownEvidenceType -- evidence.type outside the allowed set.
17
+ """
18
+ from __future__ import annotations
19
+
20
+ from dataclasses import dataclass
21
+ from pathlib import Path
22
+ from typing import Any
23
+
24
+ import yaml
25
+
26
+
27
+ _ALLOWED_EVIDENCE_TYPES = frozenset({
28
+ "command",
29
+ "url",
30
+ "playwright",
31
+ "artifact",
32
+ "metric",
33
+ })
34
+
35
+
36
+ class InvalidBriefFrontmatter(Exception):
37
+ """Raised when the brief has no frontmatter or the YAML block is malformed."""
38
+
39
+
40
+ class UnknownEvidenceType(Exception):
41
+ """Raised when evidence.type is not in the allowed set."""
42
+
43
+
44
+ @dataclass
45
+ class Evidence:
46
+ """One acceptance criterion with its evidence spec."""
47
+
48
+ id: str
49
+ description: str
50
+ type: str
51
+ shape: dict
52
+ artifact: str
53
+
54
+
55
+ def _extract_frontmatter(text: str) -> dict:
56
+ """Parse the YAML block between two '---' markers at the top of `text`.
57
+
58
+ Raises InvalidBriefFrontmatter when markers are absent or YAML is malformed.
59
+ """
60
+ lines = text.splitlines(keepends=True)
61
+ if not lines or lines[0].rstrip() != "---":
62
+ raise InvalidBriefFrontmatter("Brief is missing opening '---' marker")
63
+
64
+ # Find the closing '---' on its own line.
65
+ closing_index = None
66
+ for i in range(1, len(lines)):
67
+ if lines[i].rstrip() == "---":
68
+ closing_index = i
69
+ break
70
+ if closing_index is None:
71
+ raise InvalidBriefFrontmatter("Brief is missing closing '---' marker")
72
+
73
+ yaml_block = "".join(lines[1:closing_index])
74
+ try:
75
+ parsed = yaml.safe_load(yaml_block)
76
+ except yaml.YAMLError as exc:
77
+ raise InvalidBriefFrontmatter(f"Invalid YAML in frontmatter: {exc}") from exc
78
+
79
+ if parsed is None:
80
+ return {}
81
+ if not isinstance(parsed, dict):
82
+ raise InvalidBriefFrontmatter(
83
+ f"Frontmatter must be a mapping, got {type(parsed).__name__}"
84
+ )
85
+ return parsed
86
+
87
+
88
+ def load_acs(brief_path: Path) -> list[Evidence]:
89
+ """Parse `brief_path` and return its acceptance_criteria as Evidence objects."""
90
+ text = Path(brief_path).read_text(encoding="utf-8")
91
+ frontmatter = _extract_frontmatter(text)
92
+
93
+ raw_acs: Any = frontmatter.get("acceptance_criteria")
94
+ if raw_acs is None:
95
+ return []
96
+ if not isinstance(raw_acs, list):
97
+ raise InvalidBriefFrontmatter(
98
+ "acceptance_criteria must be a list"
99
+ )
100
+
101
+ evidences: list[Evidence] = []
102
+ for entry in raw_acs:
103
+ if not isinstance(entry, dict):
104
+ raise InvalidBriefFrontmatter(
105
+ "Each acceptance_criteria entry must be a mapping"
106
+ )
107
+
108
+ ev_spec = entry.get("evidence") or {}
109
+ ev_type = ev_spec.get("type", "")
110
+ if ev_type not in _ALLOWED_EVIDENCE_TYPES:
111
+ raise UnknownEvidenceType(
112
+ f"Unknown evidence type: {ev_type!r} "
113
+ f"(allowed: {sorted(_ALLOWED_EVIDENCE_TYPES)})"
114
+ )
115
+
116
+ shape = ev_spec.get("shape") or {}
117
+ evidences.append(
118
+ Evidence(
119
+ id=str(entry.get("id", "")),
120
+ description=str(entry.get("description", "")),
121
+ type=ev_type,
122
+ shape=dict(shape) if isinstance(shape, dict) else {},
123
+ artifact=str(entry.get("artifact", "")),
124
+ )
125
+ )
126
+ return evidences
@@ -0,0 +1,241 @@
1
+ """
2
+ Command + artifact evidence runners.
3
+
4
+ Contract:
5
+ run_command(shape, artifact_path) -> EvidenceResult
6
+ run_artifact(shape, artifact_path) -> EvidenceResult
7
+
8
+ EvidenceResult:
9
+ passed: bool
10
+ output: str # stdout + stderr
11
+ artifact_path: Path # file written -- stdout + stderr + exit for commands,
12
+ # raw target bytes for artifacts
13
+ error: Optional[str] # "timeout" | "spawn_error" | "yaml: ..." | "json: ..." | None
14
+ """
15
+ from __future__ import annotations
16
+
17
+ import json
18
+ import subprocess
19
+ from dataclasses import dataclass
20
+ from pathlib import Path
21
+ from typing import Any, Optional
22
+
23
+ import yaml
24
+
25
+ from .assertions import evaluate
26
+
27
+
28
+ _DEFAULT_TIMEOUT = 60
29
+ _DEFAULT_EXPECT = "exit 0"
30
+
31
+
32
+ @dataclass
33
+ class EvidenceResult:
34
+ """Outcome of a single evidence run."""
35
+
36
+ passed: bool
37
+ output: str
38
+ artifact_path: Path
39
+ error: Optional[str] = None
40
+
41
+
42
+ # ---------------------------------------------------------------------------
43
+ # command
44
+ # ---------------------------------------------------------------------------
45
+
46
+ def _write_command_artifact(
47
+ artifact_path: Path,
48
+ stdout: str,
49
+ stderr: str,
50
+ exit_code: int,
51
+ error: Optional[str],
52
+ ) -> None:
53
+ """Persist stdout + stderr + exit code to the artifact file."""
54
+ artifact_path.parent.mkdir(parents=True, exist_ok=True)
55
+ parts = [
56
+ "=== stdout ===",
57
+ stdout.rstrip("\n"),
58
+ "=== stderr ===",
59
+ stderr.rstrip("\n"),
60
+ f"exit_code: {exit_code}",
61
+ ]
62
+ if error:
63
+ parts.append(f"error: {error}")
64
+ artifact_path.write_text("\n".join(parts) + "\n", encoding="utf-8")
65
+
66
+
67
+ def _check_expect(expect: str, stdout: str, stderr: str, exit_code: int) -> bool:
68
+ """Evaluate the expect clause against the run output."""
69
+ if expect == "exit 0":
70
+ return exit_code == 0
71
+ if expect.startswith("substring "):
72
+ needle = expect[len("substring "):]
73
+ return needle in stdout or needle in stderr
74
+ # Unknown expect pattern -- fail closed.
75
+ return False
76
+
77
+
78
+ def run_command(shape: dict, artifact_path: Path) -> EvidenceResult:
79
+ """Execute `shape.run` through bash and evaluate against `shape.expect`."""
80
+ run_cmd = shape.get("run", "")
81
+ expect = shape.get("expect", _DEFAULT_EXPECT)
82
+ timeout = int(shape.get("timeout", _DEFAULT_TIMEOUT))
83
+
84
+ try:
85
+ completed = subprocess.run(
86
+ ["bash", "-c", run_cmd],
87
+ capture_output=True,
88
+ text=True,
89
+ timeout=timeout,
90
+ check=False,
91
+ )
92
+ except subprocess.TimeoutExpired as exc:
93
+ stdout = exc.stdout.decode("utf-8", errors="replace") if isinstance(
94
+ exc.stdout, (bytes, bytearray)
95
+ ) else (exc.stdout or "")
96
+ stderr = exc.stderr.decode("utf-8", errors="replace") if isinstance(
97
+ exc.stderr, (bytes, bytearray)
98
+ ) else (exc.stderr or "")
99
+ combined = stdout + stderr
100
+ _write_command_artifact(artifact_path, stdout, stderr, -1, "timeout")
101
+ return EvidenceResult(
102
+ passed=False,
103
+ output=combined,
104
+ artifact_path=artifact_path,
105
+ error="timeout",
106
+ )
107
+ except (FileNotFoundError, OSError) as exc:
108
+ msg = f"spawn_error: {exc}"
109
+ _write_command_artifact(artifact_path, "", msg, -1, "spawn_error")
110
+ return EvidenceResult(
111
+ passed=False,
112
+ output=msg,
113
+ artifact_path=artifact_path,
114
+ error="spawn_error",
115
+ )
116
+
117
+ stdout = completed.stdout or ""
118
+ stderr = completed.stderr or ""
119
+ combined = stdout + stderr
120
+ passed = _check_expect(expect, stdout, stderr, completed.returncode)
121
+
122
+ _write_command_artifact(
123
+ artifact_path, stdout, stderr, completed.returncode, None
124
+ )
125
+ return EvidenceResult(
126
+ passed=passed,
127
+ output=combined,
128
+ artifact_path=artifact_path,
129
+ error=None,
130
+ )
131
+
132
+
133
+ # ---------------------------------------------------------------------------
134
+ # artifact
135
+ # ---------------------------------------------------------------------------
136
+
137
+ def _select_target(path_str: str, select: Optional[str]) -> Path:
138
+ """Resolve the file to parse. If path is a directory and select=latest,
139
+ pick the newest file in it (by mtime). Otherwise return path as-is."""
140
+ p = Path(path_str)
141
+ if p.is_dir() and select == "latest":
142
+ candidates = [c for c in p.iterdir() if c.is_file()]
143
+ if not candidates:
144
+ return p # Will fail downstream with a clear error.
145
+ candidates.sort(key=lambda c: c.stat().st_mtime, reverse=True)
146
+ return candidates[0]
147
+ return p
148
+
149
+
150
+ def _parse_target(target: Path, kind: str) -> Any:
151
+ """Parse the target file according to `kind`. Returns the raw Python object."""
152
+ text = target.read_text(encoding="utf-8")
153
+ if kind == "yaml":
154
+ return yaml.safe_load(text)
155
+ if kind == "json":
156
+ return json.loads(text)
157
+ raise ValueError(f"Unknown artifact kind: {kind!r}")
158
+
159
+
160
+ def _write_artifact_snapshot(
161
+ artifact_path: Path,
162
+ target: Path,
163
+ passed: bool,
164
+ error: Optional[str],
165
+ ) -> None:
166
+ """Persist a summary of the artifact check to the artifact file."""
167
+ artifact_path.parent.mkdir(parents=True, exist_ok=True)
168
+ parts = [
169
+ f"target: {target}",
170
+ f"passed: {passed}",
171
+ ]
172
+ if error:
173
+ parts.append(f"error: {error}")
174
+ try:
175
+ snippet = target.read_text(encoding="utf-8")
176
+ parts.append("=== content ===")
177
+ parts.append(snippet)
178
+ except OSError as exc:
179
+ parts.append(f"(unable to read target: {exc})")
180
+ artifact_path.write_text("\n".join(parts) + "\n", encoding="utf-8")
181
+
182
+
183
+ def run_artifact(shape: dict, artifact_path: Path) -> EvidenceResult:
184
+ """Load a file, optionally pick latest from a directory, assert on its parsed
185
+ contents via the assertions DSL."""
186
+ path_str = shape.get("path", "")
187
+ kind = shape.get("kind", "")
188
+ select = shape.get("select")
189
+ assert_spec = shape.get("assert") or {}
190
+
191
+ target = _select_target(path_str, select)
192
+
193
+ try:
194
+ data = _parse_target(target, kind)
195
+ except yaml.YAMLError as exc:
196
+ err = f"yaml parse error: {exc}"
197
+ _write_artifact_snapshot(artifact_path, target, False, err)
198
+ return EvidenceResult(
199
+ passed=False,
200
+ output=err,
201
+ artifact_path=artifact_path,
202
+ error=err,
203
+ )
204
+ except json.JSONDecodeError as exc:
205
+ err = f"json parse error: {exc}"
206
+ _write_artifact_snapshot(artifact_path, target, False, err)
207
+ return EvidenceResult(
208
+ passed=False,
209
+ output=err,
210
+ artifact_path=artifact_path,
211
+ error=err,
212
+ )
213
+ except (OSError, ValueError) as exc:
214
+ err = f"artifact error: {exc}"
215
+ _write_artifact_snapshot(artifact_path, target, False, err)
216
+ return EvidenceResult(
217
+ passed=False,
218
+ output=err,
219
+ artifact_path=artifact_path,
220
+ error=err,
221
+ )
222
+
223
+ try:
224
+ passed = evaluate(assert_spec, data)
225
+ except ValueError as exc:
226
+ err = f"assert error: {exc}"
227
+ _write_artifact_snapshot(artifact_path, target, False, err)
228
+ return EvidenceResult(
229
+ passed=False,
230
+ output=err,
231
+ artifact_path=artifact_path,
232
+ error=err,
233
+ )
234
+
235
+ _write_artifact_snapshot(artifact_path, target, passed, None)
236
+ return EvidenceResult(
237
+ passed=passed,
238
+ output=f"target={target} passed={passed}",
239
+ artifact_path=artifact_path,
240
+ error=None,
241
+ )
@@ -0,0 +1,8 @@
1
+ """
2
+ Memory module - Episodic memory capture for workflow episodes.
3
+
4
+ Provides:
5
+ - episode_writer: Workflow episode storage + session events (replaces episode_capture + session_state)
6
+ """
7
+
8
+ __all__ = []
@@ -0,0 +1,216 @@
1
+ """
2
+ Episodic memory capture for workflow episodes.
3
+
4
+ Renamed from episode_capture.py. Absorbs get_session_events() from
5
+ session_state.py directly into this module.
6
+
7
+ Provides:
8
+ - write(): Store workflow as episodic memory
9
+ - get_session_events(): Read context.json, categorize events
10
+ """
11
+
12
+ import json
13
+ import logging
14
+ from pathlib import Path
15
+ from typing import Any, Dict, List, Optional
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ # ============================================================================
21
+ # Session events (absorbed from session_state.py)
22
+ # ============================================================================
23
+
24
+ def get_session_events() -> Dict[str, Any]:
25
+ """
26
+ Get critical events from active session context.
27
+
28
+ Returns:
29
+ Dict with categorized session events (commits, pushes, file_mods)
30
+ """
31
+ context_path = Path(".claude/session/active/context.json")
32
+
33
+ if not context_path.exists():
34
+ logger.debug("No session context found")
35
+ return {}
36
+
37
+ try:
38
+ with open(context_path, "r") as f:
39
+ context = json.load(f)
40
+
41
+ critical_events = context.get("critical_events", [])
42
+
43
+ if not critical_events:
44
+ return {}
45
+
46
+ commits = [
47
+ {
48
+ "hash": e.get("commit_hash", ""),
49
+ "message": e.get("commit_message", ""),
50
+ "timestamp": e.get("timestamp", "")
51
+ }
52
+ for e in critical_events
53
+ if e.get("event_type") == "git_commit" and e.get("commit_hash")
54
+ ]
55
+
56
+ pushes = [
57
+ {
58
+ "branch": e.get("branch", ""),
59
+ "timestamp": e.get("timestamp", "")
60
+ }
61
+ for e in critical_events
62
+ if e.get("event_type") == "git_push" and e.get("branch")
63
+ ]
64
+
65
+ file_mods = [
66
+ {
67
+ "count": e.get("modification_count", 0),
68
+ "timestamp": e.get("timestamp", "")
69
+ }
70
+ for e in critical_events
71
+ if e.get("event_type") == "file_modifications"
72
+ ]
73
+
74
+ result = {}
75
+ if commits:
76
+ result["git_commits"] = commits
77
+ if pushes:
78
+ result["git_pushes"] = pushes
79
+ if file_mods:
80
+ result["file_modifications"] = file_mods
81
+
82
+ if result:
83
+ logger.info(f"Found {len(critical_events)} session events")
84
+
85
+ return result
86
+
87
+ except Exception as e:
88
+ logger.warning(f"Failed to read session events: {e}")
89
+ return {}
90
+
91
+
92
+ # ============================================================================
93
+ # Episodic memory capture
94
+ # ============================================================================
95
+
96
+ def write(
97
+ metrics: Dict[str, Any],
98
+ anomalies: Optional[List[Dict[str, str]]] = None,
99
+ commands_executed: Optional[List[str]] = None,
100
+ ) -> Optional[str]:
101
+ """
102
+ Capture workflow as episodic memory.
103
+
104
+ Args:
105
+ metrics: Subagent metrics from workflow (includes plan_status, tier, task description)
106
+ anomalies: Detected anomalies from audit(), stored in episode context
107
+ commands_executed: List of commands extracted from EVIDENCE_REPORT
108
+
109
+ Returns:
110
+ Episode ID if stored, None otherwise
111
+ """
112
+ try:
113
+ import importlib.util
114
+
115
+ candidates = [
116
+ Path(__file__).parent.parent.parent.parent / "tools" / "memory" / "episodic.py",
117
+ Path(".claude/tools/memory/episodic.py"),
118
+ ]
119
+
120
+ episodic_module = None
121
+ for path in candidates:
122
+ if path.exists():
123
+ try:
124
+ spec = importlib.util.spec_from_file_location("episodic", path)
125
+ if spec and spec.loader:
126
+ episodic_module = importlib.util.module_from_spec(spec)
127
+ spec.loader.exec_module(episodic_module)
128
+ logger.debug(f"Loaded episodic module from {path}")
129
+ break
130
+ except Exception as e:
131
+ logger.debug(f"Could not load episodic from {path}: {e}")
132
+ continue
133
+
134
+ if not episodic_module:
135
+ logger.debug("Episodic memory module not found - skipping episode capture")
136
+ return None
137
+
138
+ memory = episodic_module.EpisodicMemory()
139
+
140
+ # Use the real task description captured from the transcript.
141
+ # metrics["prompt"] now holds the first user message (task description)
142
+ # rather than the generic "SubagentStop for <agent>".
143
+ prompt = metrics.get("prompt", "")
144
+ if not prompt:
145
+ prompt = f"Task for {metrics.get('agent', 'unknown')}"
146
+
147
+ subagent_type = metrics.get("agent", "unknown")
148
+ duration_seconds = metrics.get("duration_ms", 0) / 1000.0 if metrics.get("duration_ms") else None
149
+
150
+ # Determine outcome: prefer plan_status string, fall back to exit_code
151
+ plan_status = metrics.get("plan_status", "")
152
+ exit_code = metrics.get("exit_code", 0)
153
+ if plan_status:
154
+ if "COMPLETE" in plan_status:
155
+ outcome = "success"
156
+ success = True
157
+ elif "BLOCKED" in plan_status or "ERROR" in plan_status:
158
+ outcome = "failed"
159
+ success = False
160
+ else:
161
+ # IN_PROGRESS, REVIEW, NEEDS_INPUT -> partial
162
+ outcome = "partial"
163
+ success = None
164
+ elif exit_code == 0:
165
+ outcome = "success"
166
+ success = True
167
+ else:
168
+ outcome = "failed"
169
+ success = False
170
+
171
+ # Tags from metrics -- filter empty strings defensively
172
+ tags = [t for t in metrics.get("tags", []) if t]
173
+ if not tags and subagent_type and subagent_type != "unknown":
174
+ tags = [subagent_type]
175
+
176
+ # Enrich with session events and anomalies
177
+ session_events = get_session_events()
178
+ context = {"metrics": metrics}
179
+ if session_events:
180
+ context["session_events"] = session_events
181
+ logger.info(f"Enriched episode with session events: {list(session_events.keys())}")
182
+ if anomalies:
183
+ context["anomalies"] = anomalies
184
+ logger.info(f"Episode has {len(anomalies)} anomaly/anomalies")
185
+
186
+ # Include context anchor hit tracking if available
187
+ anchor_hits = metrics.get("context_anchor_hits")
188
+ if anchor_hits:
189
+ context["context_anchor_hits"] = anchor_hits
190
+ logger.info(
191
+ "Episode anchor hits: %d/%d (%.0f%%)",
192
+ anchor_hits.get("hits", 0),
193
+ anchor_hits.get("total_checked", 0),
194
+ anchor_hits.get("hit_rate", 0) * 100,
195
+ )
196
+
197
+ # P3 CLI compatibility fields
198
+ episode_id = memory.store_episode(
199
+ prompt=prompt,
200
+ clarifications={},
201
+ enriched_prompt=prompt,
202
+ context=context,
203
+ tags=tags,
204
+ outcome=outcome,
205
+ success=success,
206
+ duration_seconds=duration_seconds,
207
+ commands_executed=commands_executed or [],
208
+ workflow_metrics=metrics,
209
+ )
210
+
211
+ logger.info(f"Captured episode: {episode_id} (outcome: {outcome}, plan_status: {plan_status})")
212
+ return episode_id
213
+
214
+ except Exception as e:
215
+ logger.debug(f"Failed to capture episodic memory: {e}")
216
+ return None
@@ -0,0 +1 @@
1
+ # Orchestrator enforcement modules.