@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,262 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Git Invalidation Module for GAIA-OPS Episodic Memory
4
+
5
+ Scans recent git commits for migration/deprecation patterns and weakens
6
+ matching episodic memories by reducing their relevance_score.
7
+
8
+ Inspired by hippo-memory's invalidation.ts patterns.
9
+ """
10
+
11
+ import json
12
+ import re
13
+ import subprocess
14
+ from pathlib import Path
15
+ from typing import Dict, List, Any, Optional
16
+
17
+
18
+ # Patterns that signal a tool/library/concept has changed or been removed.
19
+ # Each entry is a (compiled_regex, group_extractor_fn) tuple.
20
+ # The extractor receives a re.Match and returns a list of affected term strings.
21
+ _PATTERNS: List[tuple] = []
22
+
23
+ def _build_patterns() -> List[tuple]:
24
+ """Build compiled pattern list once."""
25
+ raw = [
26
+ # "migrate from X" / "migrated from X to Y"
27
+ (
28
+ re.compile(r'\bmigrate(?:d)?\s+from\s+([\w\-./]+)', re.IGNORECASE),
29
+ lambda m: [m.group(1)],
30
+ ),
31
+ # "deprecate X" / "deprecated X"
32
+ (
33
+ re.compile(r'\bdeprecate[sd]?\s+([\w\-./]+)', re.IGNORECASE),
34
+ lambda m: [m.group(1)],
35
+ ),
36
+ # "remove X" (at least 3-char word to skip noise)
37
+ (
38
+ re.compile(r'\bremove[sd]?\s+([\w\-./]{3,})', re.IGNORECASE),
39
+ lambda m: [m.group(1)],
40
+ ),
41
+ # "replace X with Y"
42
+ (
43
+ re.compile(r'\breplace\s+([\w\-./]+)\s+with\s+([\w\-./]+)', re.IGNORECASE),
44
+ lambda m: [m.group(1), m.group(2)],
45
+ ),
46
+ # "upgrade from X" / "upgraded from X to Y"
47
+ (
48
+ re.compile(r'\bupgrade[sd]?\s+from\s+([\w\-./]+)', re.IGNORECASE),
49
+ lambda m: [m.group(1)],
50
+ ),
51
+ # "drop support for X" / "drop X"
52
+ (
53
+ re.compile(r'\bdrop(?:\s+support(?:\s+for)?)?\s+([\w\-./]{3,})', re.IGNORECASE),
54
+ lambda m: [m.group(1)],
55
+ ),
56
+ # version bump: "v3 to v4" or "3.x to 4.x"
57
+ (
58
+ re.compile(r'\bv?(\d+)(?:\.\w+)?\s+to\s+v?(\d+)(?:\.\w+)?\b', re.IGNORECASE),
59
+ lambda m: [f"v{m.group(1)}", f"v{m.group(2)}"],
60
+ ),
61
+ ]
62
+ return raw
63
+
64
+
65
+ _COMPILED_PATTERNS = _build_patterns()
66
+
67
+
68
+ def _get_git_log(commit_count: int, cwd: Optional[Path] = None) -> Optional[str]:
69
+ """
70
+ Run git log and return stdout, or None if not in a git repo.
71
+ """
72
+ try:
73
+ result = subprocess.run(
74
+ ["git", "log", "--oneline", f"-{commit_count}", "--no-merges"],
75
+ capture_output=True,
76
+ text=True,
77
+ cwd=str(cwd) if cwd else None,
78
+ )
79
+ if result.returncode != 0:
80
+ return None
81
+ return result.stdout
82
+ except FileNotFoundError:
83
+ # git not available
84
+ return None
85
+
86
+
87
+ def _scan_commits(log_output: str) -> tuple:
88
+ """
89
+ Scan git log output for migration/deprecation patterns.
90
+
91
+ Returns:
92
+ (patterns_detected, affected_terms) where patterns_detected is a list
93
+ of matched pattern strings and affected_terms is a set of lowercased
94
+ affected tool/library names.
95
+ """
96
+ patterns_detected: List[str] = []
97
+ affected_terms: set = set()
98
+
99
+ for line in log_output.splitlines():
100
+ # Strip the short SHA prefix (7-char hex + space)
101
+ message = re.sub(r'^[0-9a-f]{7,}\s+', '', line).strip()
102
+ if not message:
103
+ continue
104
+
105
+ for regex, extractor in _COMPILED_PATTERNS:
106
+ match = regex.search(message)
107
+ if match:
108
+ terms = extractor(match)
109
+ patterns_detected.append(message)
110
+ for term in terms:
111
+ affected_terms.add(term.lower())
112
+ break # one pattern per commit line is enough
113
+
114
+ return patterns_detected, affected_terms
115
+
116
+
117
+ def _find_index_json() -> Optional[Path]:
118
+ """
119
+ Locate episodic memory index.json relative to the cwd.
120
+ Checks the canonical path: .claude/project-context/episodic-memory/index.json
121
+ """
122
+ candidate = Path(".claude/project-context/episodic-memory/index.json")
123
+ if candidate.exists():
124
+ return candidate
125
+ return None
126
+
127
+
128
+ def _episode_mentions_terms(episode: Dict[str, Any], terms: set) -> bool:
129
+ """
130
+ Return True if the episode's searchable text overlaps with any affected term.
131
+ Checks: keywords, tags, title.
132
+ """
133
+ if not terms:
134
+ return False
135
+
136
+ searchable: List[str] = []
137
+
138
+ keywords = episode.get("keywords", [])
139
+ if isinstance(keywords, list):
140
+ searchable.extend(str(k).lower() for k in keywords)
141
+
142
+ tags = episode.get("tags", [])
143
+ if isinstance(tags, list):
144
+ searchable.extend(str(t).lower() for t in tags)
145
+
146
+ title = episode.get("title", "")
147
+ if title:
148
+ searchable.extend(re.split(r'\W+', title.lower()))
149
+
150
+ for word in searchable:
151
+ for term in terms:
152
+ # term could be "v3" etc — use substring match for flexibility
153
+ if term and (term in word or word in term):
154
+ return True
155
+ return False
156
+
157
+
158
+ def check_recent_commits(
159
+ dry_run: bool = True,
160
+ commit_count: int = 20,
161
+ ) -> Dict[str, Any]:
162
+ """
163
+ Scan recent git commits for migration/deprecation patterns and weaken
164
+ matching episodic memories.
165
+
166
+ Args:
167
+ dry_run: If True, identify affected episodes but do NOT modify index.json.
168
+ commit_count: Number of recent commits to scan (default 20).
169
+
170
+ Returns:
171
+ dict with keys:
172
+ affected_episodes: list of episode IDs that match
173
+ patterns_detected: list of commit message strings that matched a pattern
174
+ would_modify: count of episodes that would be (or were) modified
175
+ commits_scanned: number of commits examined
176
+ """
177
+ empty_result: Dict[str, Any] = {
178
+ "affected_episodes": [],
179
+ "patterns_detected": [],
180
+ "would_modify": 0,
181
+ "commits_scanned": 0,
182
+ }
183
+
184
+ # Step 1: get git log
185
+ log_output = _get_git_log(commit_count)
186
+ if log_output is None:
187
+ return empty_result
188
+
189
+ commits_scanned = len([l for l in log_output.splitlines() if l.strip()])
190
+
191
+ # Step 2: detect patterns
192
+ patterns_detected, affected_terms = _scan_commits(log_output)
193
+
194
+ # If nothing matched, short-circuit
195
+ if not affected_terms:
196
+ return {
197
+ "affected_episodes": [],
198
+ "patterns_detected": patterns_detected,
199
+ "would_modify": 0,
200
+ "commits_scanned": commits_scanned,
201
+ }
202
+
203
+ # Step 3: load index.json
204
+ index_path = _find_index_json()
205
+ if index_path is None:
206
+ return {
207
+ "affected_episodes": [],
208
+ "patterns_detected": patterns_detected,
209
+ "would_modify": 0,
210
+ "commits_scanned": commits_scanned,
211
+ }
212
+
213
+ try:
214
+ index_data = json.loads(index_path.read_text(encoding="utf-8"))
215
+ except (json.JSONDecodeError, OSError):
216
+ return {
217
+ "affected_episodes": [],
218
+ "patterns_detected": patterns_detected,
219
+ "would_modify": 0,
220
+ "commits_scanned": commits_scanned,
221
+ }
222
+
223
+ episodes: List[Dict[str, Any]] = index_data.get("episodes", [])
224
+
225
+ # Step 4: find matching episodes
226
+ affected_ids: List[str] = []
227
+ for ep in episodes:
228
+ if _episode_mentions_terms(ep, affected_terms):
229
+ ep_id = ep.get("id") or ep.get("episode_id", "")
230
+ if ep_id:
231
+ affected_ids.append(ep_id)
232
+
233
+ would_modify = len(affected_ids)
234
+
235
+ # Step 5: apply changes if not dry_run
236
+ if not dry_run and affected_ids:
237
+ affected_set = set(affected_ids)
238
+ for ep in episodes:
239
+ ep_id = ep.get("id") or ep.get("episode_id", "")
240
+ if ep_id in affected_set:
241
+ current_score = ep.get("relevance_score", 1.0)
242
+ ep["relevance_score"] = current_score * 0.5
243
+
244
+ index_data["episodes"] = episodes
245
+ index_path.write_text(
246
+ json.dumps(index_data, indent=2, ensure_ascii=False),
247
+ encoding="utf-8",
248
+ )
249
+
250
+ return {
251
+ "affected_episodes": affected_ids,
252
+ "patterns_detected": patterns_detected,
253
+ "would_modify": would_modify,
254
+ "commits_scanned": commits_scanned,
255
+ }
256
+
257
+
258
+ if __name__ == "__main__":
259
+ import sys
260
+ dry = "--apply" not in sys.argv
261
+ result = check_recent_commits(dry_run=dry)
262
+ print(json.dumps(result, indent=2))
@@ -0,0 +1,102 @@
1
+ """
2
+ Shared path-resolution helpers for Gaia episodic memory.
3
+
4
+ The central function, ``find_highest_claude_root``, walks upward from a
5
+ starting directory and returns the ``.claude/``-bearing ancestor that is
6
+ *closest to HOME* and looks like a Gaia instance — i.e. the top-most one
7
+ that has Gaia-specific subdirectories under ``.claude/``.
8
+
9
+ This prevents two classes of problems:
10
+ 1. A nested ``.claude/`` in a sub-repository or dev checkout (e.g.
11
+ ``gaia-ops-dev/.claude/``) shadowing the real Gaia instance.
12
+ 2. Claude Code's own user settings dir (``$HOME/.claude/``) being mistaken
13
+ for a Gaia instance — it has a ``.claude/`` but no Gaia-specific layout.
14
+ """
15
+
16
+ from pathlib import Path
17
+ from typing import Optional
18
+
19
+ # Subdirectories that signal "this .claude/ belongs to a Gaia instance".
20
+ # Claude Code's own $HOME/.claude/ never has hooks/ or agents/.
21
+ _GAIA_MARKERS = ("hooks", "agents", "skills")
22
+
23
+
24
+ def _is_gaia_instance(directory: Path) -> bool:
25
+ """Return True if *directory*/.claude/ looks like a Gaia instance.
26
+
27
+ A directory qualifies as a Gaia instance root if its ``.claude/``
28
+ subdirectory contains at least one of the Gaia-specific marker dirs
29
+ (``hooks/``, ``agents/``, ``skills/``). This distinguishes Gaia
30
+ instance roots from Claude Code's own ``$HOME/.claude/`` which only
31
+ contains user settings.
32
+
33
+ Parameters
34
+ ----------
35
+ directory:
36
+ The candidate root directory (the parent of ``.claude/``).
37
+ """
38
+ claude_dir = directory / ".claude"
39
+ if not claude_dir.is_dir():
40
+ return False
41
+ return any((claude_dir / marker).is_dir() for marker in _GAIA_MARKERS)
42
+
43
+
44
+ def find_highest_claude_root(start: Optional[Path] = None) -> Optional[Path]:
45
+ """Return the highest Gaia instance root at or above *start*.
46
+
47
+ Walk from *start* (defaults to ``Path.cwd()``) up to ``Path.home()``
48
+ (inclusive). Every directory that has a ``.claude/`` child **and**
49
+ passes the Gaia-instance check (has ``hooks/``, ``agents/``, or
50
+ ``skills/`` under ``.claude/``) is collected; the one *closest to HOME*
51
+ (highest in the hierarchy) is returned.
52
+
53
+ If no Gaia-qualified ``.claude/`` is found anywhere in the walk, the
54
+ function falls back to returning the highest plain ``.claude/`` ancestor
55
+ (matching the original semantics), so callers that don't have a full
56
+ Gaia layout still get a reasonable result.
57
+
58
+ Edge cases
59
+ ----------
60
+ - If no ``.claude/`` directory is found at all, returns ``None``.
61
+ - If *start* is already above ``Path.home()`` the walk is bounded at
62
+ ``Path.home()``; if that yields no candidates the function returns
63
+ ``None``.
64
+ - Symlinks in the path are **not** resolved — the walk uses the logical
65
+ path reported by the OS, consistent with the rest of the project.
66
+
67
+ Parameters
68
+ ----------
69
+ start:
70
+ Directory from which to begin the upward walk. Defaults to
71
+ ``Path.cwd()``.
72
+
73
+ Returns
74
+ -------
75
+ Path or None
76
+ The ancestor path (not the ``.claude/`` child itself) that should
77
+ be used as the Gaia instance root, or ``None`` if no such ancestor
78
+ exists within the walk range.
79
+ """
80
+ if start is None:
81
+ start = Path.cwd()
82
+
83
+ home = Path.home()
84
+
85
+ # Walk from start up to home (inclusive).
86
+ candidates: list[Path] = [start, *start.parents]
87
+
88
+ highest_gaia: Optional[Path] = None # best match: Gaia-qualified
89
+ highest_any: Optional[Path] = None # fallback: any .claude/
90
+
91
+ for directory in candidates:
92
+ if (directory / ".claude").is_dir():
93
+ highest_any = directory # keep updating — last = highest
94
+ if _is_gaia_instance(directory):
95
+ highest_gaia = directory # keep updating — last = highest
96
+
97
+ # Stop at HOME; don't traverse system directories above it.
98
+ if directory == home:
99
+ break
100
+
101
+ # Prefer a Gaia-qualified root; fall back to any .claude/ if none found.
102
+ return highest_gaia if highest_gaia is not None else highest_any
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Memory Scoring Module for GAIA-OPS
4
+
5
+ Provides strength-based scoring for episodic memories using a decay formula
6
+ inspired by the hippo-memory model. Memories decay over time (recency bias)
7
+ and are strengthened by repeated retrieval (usage reinforcement).
8
+
9
+ Formula:
10
+ strength = base_strength * (0.5 ^ (days_old / half_life))
11
+ * (1 + log(1 + retrieval_count) * boost_factor)
12
+
13
+ Functions:
14
+ score_memory -- compute strength score for a single memory
15
+ rank_episodes -- rank a list of episode dicts by combined relevance + strength
16
+ """
17
+
18
+ import math
19
+ import re
20
+ from datetime import datetime, timezone
21
+ from typing import Any, Dict, List, Optional
22
+
23
+
24
+ # ---------------------------------------------------------------------------
25
+ # Core scoring
26
+ # ---------------------------------------------------------------------------
27
+
28
+ def score_memory(
29
+ days_old: float,
30
+ retrieval_count: int,
31
+ half_life: float = 7.0,
32
+ boost_factor: float = 0.3,
33
+ base_strength: float = 1.0,
34
+ ) -> float:
35
+ """Compute a strength score for a single memory.
36
+
37
+ Parameters
38
+ ----------
39
+ days_old:
40
+ Age of the memory in days (>= 0).
41
+ retrieval_count:
42
+ Number of times the memory has been retrieved (>= 0).
43
+ half_life:
44
+ Number of days after which unaccessed memory retains 50% strength.
45
+ Default: 7.0 days.
46
+ boost_factor:
47
+ Multiplier that scales the logarithmic retrieval boost.
48
+ Default: 0.3.
49
+ base_strength:
50
+ Starting strength before decay and boost are applied.
51
+ Default: 1.0.
52
+
53
+ Returns
54
+ -------
55
+ float
56
+ Strength score in the range (0, base_strength * (1 + boost)].
57
+ A higher value means the memory is more relevant/fresh.
58
+
59
+ Examples
60
+ --------
61
+ >>> score_memory(days_old=0, retrieval_count=0)
62
+ 1.0
63
+ >>> score_memory(days_old=7, retrieval_count=0) # half-life point
64
+ 0.5
65
+ """
66
+ if half_life <= 0:
67
+ raise ValueError("half_life must be positive")
68
+ if days_old < 0:
69
+ raise ValueError("days_old must be non-negative")
70
+ if retrieval_count < 0:
71
+ raise ValueError("retrieval_count must be non-negative")
72
+
73
+ decay = 0.5 ** (days_old / half_life)
74
+ retrieval_boost = 1.0 + math.log(1 + retrieval_count) * boost_factor
75
+ return base_strength * decay * retrieval_boost
76
+
77
+
78
+ # ---------------------------------------------------------------------------
79
+ # Episode ranking
80
+ # ---------------------------------------------------------------------------
81
+
82
+ def _extract_text(episode: Dict[str, Any]) -> str:
83
+ """Combine all text fields from an episode into a single string for matching."""
84
+ parts: List[str] = []
85
+ for key in ("prompt", "enriched_prompt", "title", "type"):
86
+ value = episode.get(key)
87
+ if isinstance(value, str) and value:
88
+ parts.append(value)
89
+ tags = episode.get("tags")
90
+ if isinstance(tags, list):
91
+ parts.extend(t for t in tags if isinstance(t, str))
92
+ keywords = episode.get("keywords")
93
+ if isinstance(keywords, list):
94
+ parts.extend(k for k in keywords if isinstance(k, str))
95
+ return " ".join(parts)
96
+
97
+
98
+ def _tokenize(text: str) -> set:
99
+ """Return a set of lowercase word tokens from *text*."""
100
+ return set(re.findall(r"[a-z0-9]+", text.lower()))
101
+
102
+
103
+ def _keyword_overlap(episode_text: str, user_task: str) -> float:
104
+ """Compute a normalised keyword overlap score in [0, 1].
105
+
106
+ Uses the Jaccard-like formula:
107
+ overlap_count / max(1, len(task_tokens))
108
+
109
+ This measures what fraction of the user's task words appear in the
110
+ episode text, so short tasks aren't penalised.
111
+ """
112
+ task_tokens = _tokenize(user_task)
113
+ if not task_tokens:
114
+ return 0.0
115
+ episode_tokens = _tokenize(episode_text)
116
+ common = task_tokens & episode_tokens
117
+ return len(common) / len(task_tokens)
118
+
119
+
120
+ def _days_old_from_episode(episode: Dict[str, Any]) -> float:
121
+ """Derive days_old from the episode's 'timestamp' field.
122
+
123
+ Falls back to 0.0 if the field is absent or unparseable.
124
+ """
125
+ ts = episode.get("timestamp")
126
+ if not ts:
127
+ return 0.0
128
+ try:
129
+ # Support ISO-8601 strings with or without timezone info.
130
+ if ts.endswith("Z"):
131
+ ts = ts[:-1] + "+00:00"
132
+ recorded = datetime.fromisoformat(ts)
133
+ if recorded.tzinfo is None:
134
+ recorded = recorded.replace(tzinfo=timezone.utc)
135
+ now = datetime.now(tz=timezone.utc)
136
+ delta = now - recorded
137
+ return max(0.0, delta.total_seconds() / 86400.0)
138
+ except (ValueError, AttributeError):
139
+ return 0.0
140
+
141
+
142
+ def rank_episodes(
143
+ episodes: List[Dict[str, Any]],
144
+ user_task: str,
145
+ half_life: float = 7.0,
146
+ boost_factor: float = 0.3,
147
+ ) -> List[Dict[str, Any]]:
148
+ """Rank episodes by combined keyword relevance and memory strength.
149
+
150
+ The composite score is:
151
+ final_score = keyword_overlap * score_memory(...)
152
+
153
+ Episodes with no keyword overlap receive a score of 0 and are still
154
+ included so the caller can decide whether to filter them.
155
+
156
+ Parameters
157
+ ----------
158
+ episodes:
159
+ List of episode dicts. Each dict may contain: ``prompt``,
160
+ ``enriched_prompt``, ``timestamp``, ``retrieval_count``, ``title``,
161
+ ``type``, ``tags``, ``keywords``.
162
+ user_task:
163
+ Free-text description of what the user is trying to accomplish.
164
+ half_life:
165
+ Forwarded to :func:`score_memory`.
166
+ boost_factor:
167
+ Forwarded to :func:`score_memory`.
168
+
169
+ Returns
170
+ -------
171
+ list
172
+ A new list of episode dicts sorted by ``_score`` descending.
173
+ Each returned dict has an additional ``_score`` key (float) for
174
+ inspection and debugging.
175
+ """
176
+ scored: List[Dict[str, Any]] = []
177
+ for episode in episodes:
178
+ days_old = _days_old_from_episode(episode)
179
+ retrieval_count = int(episode.get("retrieval_count", 0))
180
+ strength = score_memory(
181
+ days_old=days_old,
182
+ retrieval_count=retrieval_count,
183
+ half_life=half_life,
184
+ boost_factor=boost_factor,
185
+ )
186
+ overlap = _keyword_overlap(_extract_text(episode), user_task)
187
+ final_score = overlap * strength
188
+ entry = dict(episode)
189
+ entry["_score"] = final_score
190
+ scored.append(entry)
191
+
192
+ scored.sort(key=lambda e: e["_score"], reverse=True)
193
+ return scored