@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,264 @@
1
+ """
2
+ Skills mapper for gaia-ops agent/skill/surface/contract analysis.
3
+
4
+ Builds a complete map of agents, their skills, the surfaces that route
5
+ to them, and the contract permissions they hold. Flags orphan skills.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ import re
12
+ import sys
13
+ from dataclasses import dataclass, field
14
+ from pathlib import Path
15
+ from typing import Any, Optional
16
+
17
+
18
+ _TOOLS_DIR = Path(__file__).resolve().parent.parent
19
+ if str(_TOOLS_DIR) not in sys.path:
20
+ sys.path.insert(0, str(_TOOLS_DIR))
21
+
22
+ from gaia_simulator.routing_simulator import _parse_frontmatter
23
+
24
+
25
+ # ---------------------------------------------------------------------------
26
+ # Data classes
27
+ # ---------------------------------------------------------------------------
28
+
29
+
30
+ @dataclass
31
+ class SkillMapping:
32
+ """Maps a skill to the agents that use it."""
33
+
34
+ skill_name: str
35
+ skill_path: str
36
+ used_by_agents: list[str]
37
+ is_orphan: bool # no agent uses it
38
+
39
+
40
+ @dataclass
41
+ class AgentProfile:
42
+ """Full profile for an agent."""
43
+
44
+ agent_name: str
45
+ skills: list[str]
46
+ invocation_count: int # from metrics/logs
47
+ surfaces: list[str] # which surfaces route to this agent
48
+ read_sections: list[str]
49
+ write_sections: list[str]
50
+
51
+
52
+ # ---------------------------------------------------------------------------
53
+ # SkillsMapper
54
+ # ---------------------------------------------------------------------------
55
+
56
+
57
+ class SkillsMapper:
58
+ """Builds the complete map of agents, skills, surfaces, and contracts."""
59
+
60
+ def __init__(self, agents_dir: Path, skills_dir: Path, config_dir: Path):
61
+ """Initialize the mapper.
62
+
63
+ Args:
64
+ agents_dir: Path to agents/ directory with agent .md files.
65
+ skills_dir: Path to skills/ directory with skill subdirectories.
66
+ config_dir: Path to config/ directory with routing and contracts.
67
+ """
68
+ self._agents_dir = agents_dir
69
+ self._skills_dir = skills_dir
70
+ self._config_dir = config_dir
71
+
72
+ # Load agent frontmatter -- skip README.md (not an agent definition)
73
+ self._agent_frontmatter: dict[str, dict[str, Any]] = {}
74
+ if agents_dir.is_dir():
75
+ for md_file in sorted(agents_dir.glob("*.md")):
76
+ if md_file.name.upper() == "README.MD":
77
+ continue
78
+ content = md_file.read_text(encoding="utf-8", errors="replace")
79
+ fm = _parse_frontmatter(content)
80
+ name = fm.get("name", md_file.stem)
81
+ self._agent_frontmatter[name] = fm
82
+
83
+ # Load surface routing config
84
+ routing_file = config_dir / "surface-routing.json"
85
+ if routing_file.is_file():
86
+ self._routing_config = json.loads(
87
+ routing_file.read_text(encoding="utf-8")
88
+ )
89
+ else:
90
+ self._routing_config = {"surfaces": {}}
91
+
92
+ # Load contracts
93
+ contracts_file = config_dir / "context-contracts.json"
94
+ if contracts_file.is_file():
95
+ self._contracts = json.loads(
96
+ contracts_file.read_text(encoding="utf-8")
97
+ )
98
+ else:
99
+ self._contracts = {"agents": {}}
100
+
101
+ # Discover all skill directories
102
+ self._all_skills: list[str] = []
103
+ if skills_dir.is_dir():
104
+ for item in sorted(skills_dir.iterdir()):
105
+ if item.is_dir() and not item.name.startswith("."):
106
+ self._all_skills.append(item.name)
107
+
108
+ def _get_surfaces_for_agent(self, agent_name: str) -> list[str]:
109
+ """Find which surfaces route to a given agent."""
110
+ surfaces: list[str] = []
111
+ for surface_name, surface_cfg in self._routing_config.get(
112
+ "surfaces", {}
113
+ ).items():
114
+ if surface_cfg.get("primary_agent") == agent_name:
115
+ surfaces.append(surface_name)
116
+ return surfaces
117
+
118
+ def get_agent_profiles(self) -> list[AgentProfile]:
119
+ """Full profile for each agent.
120
+
121
+ Returns:
122
+ List of AgentProfile instances, one per agent.
123
+ """
124
+ profiles: list[AgentProfile] = []
125
+
126
+ for agent_name, fm in self._agent_frontmatter.items():
127
+ skills = fm.get("skills", [])
128
+ surfaces = self._get_surfaces_for_agent(agent_name)
129
+ contract = self._contracts.get("agents", {}).get(agent_name, {})
130
+ read_sections = contract.get("read", [])
131
+ write_sections = contract.get("write", [])
132
+
133
+ profiles.append(
134
+ AgentProfile(
135
+ agent_name=agent_name,
136
+ skills=skills,
137
+ invocation_count=0,
138
+ surfaces=surfaces,
139
+ read_sections=read_sections,
140
+ write_sections=write_sections,
141
+ )
142
+ )
143
+
144
+ return profiles
145
+
146
+ def get_skill_mappings(self) -> list[SkillMapping]:
147
+ """Which skills are used by which agents. Flag orphans.
148
+
149
+ Returns:
150
+ List of SkillMapping instances, one per discovered skill.
151
+ """
152
+ # Build reverse map: skill -> list of agents
153
+ skill_to_agents: dict[str, list[str]] = {}
154
+ for agent_name, fm in self._agent_frontmatter.items():
155
+ for skill in fm.get("skills", []):
156
+ skill_to_agents.setdefault(skill, []).append(agent_name)
157
+
158
+ mappings: list[SkillMapping] = []
159
+ for skill_name in self._all_skills:
160
+ skill_path = str(self._skills_dir / skill_name)
161
+ used_by = skill_to_agents.get(skill_name, [])
162
+ mappings.append(
163
+ SkillMapping(
164
+ skill_name=skill_name,
165
+ skill_path=skill_path,
166
+ used_by_agents=used_by,
167
+ is_orphan=len(used_by) == 0,
168
+ )
169
+ )
170
+
171
+ return mappings
172
+
173
+ def get_unused_skills(self) -> list[str]:
174
+ """Skills that no agent references.
175
+
176
+ Returns:
177
+ List of orphan skill names.
178
+ """
179
+ return [m.skill_name for m in self.get_skill_mappings() if m.is_orphan]
180
+
181
+ def enrich_from_logs(self, metrics_path: Path) -> dict[str, Any]:
182
+ """Cross-reference with production metrics.
183
+
184
+ Reads audit-*.jsonl files to count agent invocations.
185
+
186
+ Args:
187
+ metrics_path: Path to directory containing audit JSONL files.
188
+
189
+ Returns:
190
+ Dict with agent_invocations and skill_loads counts.
191
+ """
192
+ agent_counts: dict[str, int] = {}
193
+ skill_counts: dict[str, int] = {}
194
+
195
+ if not metrics_path.is_dir():
196
+ return {"agent_invocations": agent_counts, "skill_loads": skill_counts}
197
+
198
+ for jsonl_file in sorted(metrics_path.glob("audit-*.jsonl")):
199
+ for line in jsonl_file.read_text(
200
+ encoding="utf-8", errors="replace"
201
+ ).splitlines():
202
+ line = line.strip()
203
+ if not line:
204
+ continue
205
+ try:
206
+ record = json.loads(line)
207
+ except (json.JSONDecodeError, ValueError):
208
+ continue
209
+ agent = record.get("agent", record.get("subagent_type", ""))
210
+ if agent:
211
+ agent_counts[agent] = agent_counts.get(agent, 0) + 1
212
+
213
+ # Map agent counts to skill counts
214
+ for agent_name, count in agent_counts.items():
215
+ fm = self._agent_frontmatter.get(agent_name, {})
216
+ for skill in fm.get("skills", []):
217
+ skill_counts[skill] = skill_counts.get(skill, 0) + count
218
+
219
+ return {"agent_invocations": agent_counts, "skill_loads": skill_counts}
220
+
221
+ def format_report(self) -> str:
222
+ """Human-readable report of the agent/skill/surface map.
223
+
224
+ Returns:
225
+ Formatted multi-line string.
226
+ """
227
+ lines = []
228
+ lines.append("=" * 60)
229
+ lines.append("SKILLS MAPPER REPORT")
230
+ lines.append("=" * 60)
231
+
232
+ # Agent -> Skills table
233
+ lines.append("")
234
+ lines.append("AGENT -> SKILLS:")
235
+ lines.append("-" * 40)
236
+ for profile in self.get_agent_profiles():
237
+ skills_str = ", ".join(profile.skills) or "(none)"
238
+ surfaces_str = ", ".join(profile.surfaces) or "(none)"
239
+ lines.append(" " + profile.agent_name + ":")
240
+ lines.append(" Skills: " + skills_str)
241
+ lines.append(" Surfaces: " + surfaces_str)
242
+ lines.append(" Read: " + str(len(profile.read_sections)) + " sections")
243
+ lines.append(" Write: " + str(len(profile.write_sections)) + " sections")
244
+
245
+ # Skill -> Agents table (reverse)
246
+ lines.append("")
247
+ lines.append("SKILL -> AGENTS:")
248
+ lines.append("-" * 40)
249
+ for mapping in self.get_skill_mappings():
250
+ agents_str = ", ".join(mapping.used_by_agents) or "ORPHAN"
251
+ orphan_tag = " [ORPHAN]" if mapping.is_orphan else ""
252
+ lines.append(" " + mapping.skill_name + ": " + agents_str + orphan_tag)
253
+
254
+ # Orphan skills
255
+ unused = self.get_unused_skills()
256
+ if unused:
257
+ lines.append("")
258
+ lines.append("ORPHAN SKILLS (" + str(len(unused)) + "):")
259
+ lines.append("-" * 40)
260
+ for skill in unused:
261
+ lines.append(" - " + skill)
262
+
263
+ lines.append("=" * 60)
264
+ return chr(10).join(lines)
@@ -0,0 +1,20 @@
1
+ """
2
+ Episodic Memory Module for GAIA-OPS
3
+
4
+ This module provides episodic memory functionality for storing and retrieving
5
+ historical context from user interactions.
6
+ """
7
+
8
+ from .episodic import (
9
+ EpisodicMemory,
10
+ Episode,
11
+ search_episodic_memory
12
+ )
13
+
14
+ __all__ = [
15
+ 'EpisodicMemory',
16
+ 'Episode',
17
+ 'search_episodic_memory'
18
+ ]
19
+
20
+ __version__ = '1.0.0'
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Backfill FTS5 search index from episodes.jsonl.
4
+
5
+ Reads all episodes from the episodic memory JSONL file and indexes them
6
+ into the FTS5 search store. Idempotent -- safe to run multiple times.
7
+
8
+ Usage:
9
+ python3 tools/memory/backfill_fts5.py
10
+ python3 -m tools.memory.backfill_fts5
11
+ """
12
+
13
+ import json
14
+ import sys
15
+ from pathlib import Path
16
+
17
+
18
+ def _find_project_root() -> Path:
19
+ """Walk up from cwd to find the directory containing .claude/."""
20
+ current = Path.cwd()
21
+ for candidate in [current, *current.parents]:
22
+ if (candidate / ".claude").is_dir():
23
+ return candidate
24
+ return current
25
+
26
+
27
+ def main() -> int:
28
+ project_root = _find_project_root()
29
+ episodes_path = project_root / ".claude" / "project-context" / "episodic-memory" / "episodes.jsonl"
30
+
31
+ if not episodes_path.exists():
32
+ print(f"ERROR: episodes.jsonl not found at {episodes_path}", file=sys.stderr)
33
+ return 1
34
+
35
+ # Import here so the script works when run from the project root
36
+ # (tools/memory is on the path via the module run, or cwd is project root)
37
+ try:
38
+ from tools.memory import search_store
39
+ except ImportError:
40
+ # Fallback: add project root to sys.path and retry
41
+ sys.path.insert(0, str(project_root))
42
+ from tools.memory import search_store
43
+
44
+ indexed = 0
45
+ skipped_event = 0
46
+ skipped_malformed = 0
47
+
48
+ with open(episodes_path, encoding="utf-8") as fh:
49
+ for lineno, raw_line in enumerate(fh, start=1):
50
+ line = raw_line.strip()
51
+ if not line:
52
+ continue
53
+
54
+ try:
55
+ record = json.loads(line)
56
+ except json.JSONDecodeError as exc:
57
+ print(f"WARNING: line {lineno} is malformed JSON, skipping ({exc})", file=sys.stderr)
58
+ skipped_malformed += 1
59
+ continue
60
+
61
+ # Skip outcome_update / relationship_added events
62
+ if "event_type" in record:
63
+ skipped_event += 1
64
+ continue
65
+
66
+ episode_id = record.get("episode_id") or record.get("id", "")
67
+ if not episode_id:
68
+ print(f"WARNING: line {lineno} has no episode_id/id, skipping", file=sys.stderr)
69
+ skipped_malformed += 1
70
+ continue
71
+
72
+ prompt = record.get("prompt", "")
73
+ enriched_prompt = record.get("enriched_prompt", "")
74
+ title = record.get("title", "")
75
+
76
+ raw_tags = record.get("tags", [])
77
+ if isinstance(raw_tags, list):
78
+ tags = " ".join(str(t) for t in raw_tags)
79
+ else:
80
+ tags = str(raw_tags)
81
+
82
+ search_store.index_episode(
83
+ episode_id=episode_id,
84
+ prompt=prompt,
85
+ enriched_prompt=enriched_prompt,
86
+ tags=tags,
87
+ title=title,
88
+ )
89
+ indexed += 1
90
+
91
+ if indexed % 1000 == 0:
92
+ print(f"Progress: {indexed} episodes indexed...")
93
+
94
+ if indexed == 0 and skipped_event == 0 and skipped_malformed == 0:
95
+ print("0 episodes indexed (file is empty)")
96
+ else:
97
+ print(
98
+ f"Done: {indexed} episodes indexed"
99
+ + (f", {skipped_event} event records skipped" if skipped_event else "")
100
+ + (f", {skipped_malformed} malformed lines skipped" if skipped_malformed else "")
101
+ )
102
+
103
+ return 0
104
+
105
+
106
+ if __name__ == "__main__":
107
+ sys.exit(main())
@@ -0,0 +1,295 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Conflict Detection Module for GAIA-OPS Memory Files
4
+
5
+ Scans memory .md files for contradictions by:
6
+ 1. Loading all .md files from the memory directory
7
+ 2. Computing Jaccard similarity on word sets (after stopword removal)
8
+ 3. For similar file pairs (Jaccard > threshold), checking for polarity contradictions
9
+ 4. Returning structured conflict reports
10
+
11
+ Inspired by hippo-memory's conflict detection heuristics.
12
+ """
13
+
14
+ import os
15
+ import re
16
+ from pathlib import Path
17
+ from typing import Optional
18
+
19
+
20
+ # 50-word stopword list
21
+ STOPWORDS = frozenset([
22
+ "a", "an", "the", "and", "or", "but", "in", "on", "at", "to", "for",
23
+ "of", "with", "by", "from", "is", "are", "was", "were", "be", "been",
24
+ "being", "have", "has", "had", "do", "does", "did", "will", "would",
25
+ "could", "should", "may", "might", "shall", "that", "this", "these",
26
+ "those", "it", "its", "as", "if", "than", "then", "so", "not", "no",
27
+ "all", "any", "each", "both", "more",
28
+ ])
29
+
30
+
31
+ def _tokenize(text: str) -> set:
32
+ """Tokenize text into lowercase words, removing stopwords."""
33
+ words = re.findall(r"[a-zA-Z0-9]+", text.lower())
34
+ return {w for w in words if w not in STOPWORDS and len(w) > 1}
35
+
36
+
37
+ def _jaccard(set_a: set, set_b: set) -> float:
38
+ """Compute Jaccard similarity between two word sets."""
39
+ if not set_a and not set_b:
40
+ return 0.0
41
+ intersection = len(set_a & set_b)
42
+ union = len(set_a | set_b)
43
+ return intersection / union if union > 0 else 0.0
44
+
45
+
46
+ def _extract_lines(text: str) -> list:
47
+ """Return non-empty, non-heading stripped lines from text."""
48
+ return [line.strip() for line in text.splitlines() if line.strip()]
49
+
50
+
51
+ def _check_polarity_contradictions(lines_a: list, lines_b: list) -> list:
52
+ """
53
+ Check for polarity contradictions between two sets of lines.
54
+
55
+ Patterns checked:
56
+ - "use X" vs "do not use X" / "don't use X"
57
+ - "enabled" vs "disabled"
58
+ - "always" vs "never"
59
+ - "Windows" vs "WSL" / "Linux" in same context
60
+ - Version mismatches (e.g., "v4" vs "v5")
61
+ """
62
+ conflicts = []
63
+
64
+ # Pattern 1: "use X" vs "do not use X" / "don't use X"
65
+ use_pattern = re.compile(r"\buse\s+(\w+)", re.IGNORECASE)
66
+ no_use_pattern = re.compile(r"\b(?:do\s+not|don\'t|never\s+use)\s+(\w+)", re.IGNORECASE)
67
+
68
+ uses_a = {m.group(1).lower(): line for line in lines_a for m in [use_pattern.search(line)] if m}
69
+ uses_b = {m.group(1).lower(): line for line in lines_b for m in [use_pattern.search(line)] if m}
70
+ no_uses_a = {m.group(1).lower(): line for line in lines_a for m in [no_use_pattern.search(line)] if m}
71
+ no_uses_b = {m.group(1).lower(): line for line in lines_b for m in [no_use_pattern.search(line)] if m}
72
+
73
+ for term, line_a in uses_a.items():
74
+ if term in no_uses_b:
75
+ conflicts.append({
76
+ "line_a": line_a,
77
+ "line_b": no_uses_b[term],
78
+ "reason": f"'use {term}' contradicts 'do not use {term}'",
79
+ })
80
+
81
+ for term, line_b in uses_b.items():
82
+ if term in no_uses_a:
83
+ conflicts.append({
84
+ "line_a": no_uses_a[term],
85
+ "line_b": line_b,
86
+ "reason": f"'use {term}' contradicts 'do not use {term}'",
87
+ })
88
+
89
+ # Pattern 2: "enabled" vs "disabled"
90
+ enabled_pattern = re.compile(r"\benabled\b", re.IGNORECASE)
91
+ disabled_pattern = re.compile(r"\bdisabled\b", re.IGNORECASE)
92
+
93
+ enabled_lines_a = [l for l in lines_a if enabled_pattern.search(l)]
94
+ disabled_lines_a = [l for l in lines_a if disabled_pattern.search(l)]
95
+ enabled_lines_b = [l for l in lines_b if enabled_pattern.search(l)]
96
+ disabled_lines_b = [l for l in lines_b if disabled_pattern.search(l)]
97
+
98
+ for line_a in enabled_lines_a:
99
+ for line_b in disabled_lines_b:
100
+ # Only flag if they share a common significant keyword
101
+ shared = _tokenize(line_a) & _tokenize(line_b)
102
+ if shared:
103
+ conflicts.append({
104
+ "line_a": line_a,
105
+ "line_b": line_b,
106
+ "reason": "enabled vs disabled on shared topic",
107
+ })
108
+
109
+ for line_a in disabled_lines_a:
110
+ for line_b in enabled_lines_b:
111
+ shared = _tokenize(line_a) & _tokenize(line_b)
112
+ if shared:
113
+ conflicts.append({
114
+ "line_a": line_a,
115
+ "line_b": line_b,
116
+ "reason": "disabled vs enabled on shared topic",
117
+ })
118
+
119
+ # Pattern 3: "always" vs "never"
120
+ always_pattern = re.compile(r"\balways\b", re.IGNORECASE)
121
+ never_pattern = re.compile(r"\bnever\b", re.IGNORECASE)
122
+
123
+ always_lines_a = [l for l in lines_a if always_pattern.search(l)]
124
+ never_lines_a = [l for l in lines_a if never_pattern.search(l)]
125
+ always_lines_b = [l for l in lines_b if always_pattern.search(l)]
126
+ never_lines_b = [l for l in lines_b if never_pattern.search(l)]
127
+
128
+ for line_a in always_lines_a:
129
+ for line_b in never_lines_b:
130
+ shared = _tokenize(line_a) & _tokenize(line_b)
131
+ if shared:
132
+ conflicts.append({
133
+ "line_a": line_a,
134
+ "line_b": line_b,
135
+ "reason": "always vs never on shared topic",
136
+ })
137
+
138
+ for line_a in never_lines_a:
139
+ for line_b in always_lines_b:
140
+ shared = _tokenize(line_a) & _tokenize(line_b)
141
+ if shared:
142
+ conflicts.append({
143
+ "line_a": line_a,
144
+ "line_b": line_b,
145
+ "reason": "never vs always on shared topic",
146
+ })
147
+
148
+ # Pattern 4: "Windows" vs "WSL/Linux" in same context
149
+ windows_pattern = re.compile(r"\bWindows\b")
150
+ wsl_pattern = re.compile(r"\b(?:WSL|Linux)\b")
151
+
152
+ windows_lines_a = [l for l in lines_a if windows_pattern.search(l)]
153
+ wsl_lines_a = [l for l in lines_a if wsl_pattern.search(l)]
154
+ windows_lines_b = [l for l in lines_b if windows_pattern.search(l)]
155
+ wsl_lines_b = [l for l in lines_b if wsl_pattern.search(l)]
156
+
157
+ # Cross-file: file_a says Windows, file_b says WSL for same context
158
+ for line_a in windows_lines_a:
159
+ for line_b in wsl_lines_b:
160
+ shared = _tokenize(line_a) & _tokenize(line_b)
161
+ if shared:
162
+ conflicts.append({
163
+ "line_a": line_a,
164
+ "line_b": line_b,
165
+ "reason": "Windows vs WSL/Linux on shared topic",
166
+ })
167
+
168
+ for line_a in wsl_lines_a:
169
+ for line_b in windows_lines_b:
170
+ shared = _tokenize(line_a) & _tokenize(line_b)
171
+ if shared:
172
+ conflicts.append({
173
+ "line_a": line_a,
174
+ "line_b": line_b,
175
+ "reason": "WSL/Linux vs Windows on shared topic",
176
+ })
177
+
178
+ # Pattern 5: Version mismatches (e.g., "v4" vs "v5")
179
+ version_pattern = re.compile(r"\bv(\d+)\b", re.IGNORECASE)
180
+
181
+ def extract_versions(lines: list) -> dict:
182
+ """Map version number -> line for lines containing a version."""
183
+ result = {}
184
+ for line in lines:
185
+ for m in version_pattern.finditer(line):
186
+ v = int(m.group(1))
187
+ result[v] = line
188
+ return result
189
+
190
+ versions_a = extract_versions(lines_a)
191
+ versions_b = extract_versions(lines_b)
192
+
193
+ for va, line_a in versions_a.items():
194
+ for vb, line_b in versions_b.items():
195
+ if va != vb:
196
+ # Only flag if the context words (excluding the version itself) overlap
197
+ tokens_a = _tokenize(re.sub(r"\bv\d+\b", "", line_a, flags=re.IGNORECASE))
198
+ tokens_b = _tokenize(re.sub(r"\bv\d+\b", "", line_b, flags=re.IGNORECASE))
199
+ shared = tokens_a & tokens_b
200
+ if shared:
201
+ conflicts.append({
202
+ "line_a": line_a,
203
+ "line_b": line_b,
204
+ "reason": f"version mismatch: v{va} vs v{vb} on shared topic",
205
+ })
206
+
207
+ # Deduplicate: remove identical (line_a, line_b, reason) triples
208
+ seen = set()
209
+ deduped = []
210
+ for c in conflicts:
211
+ key = (c["line_a"], c["line_b"], c["reason"])
212
+ if key not in seen:
213
+ seen.add(key)
214
+ deduped.append(c)
215
+
216
+ return deduped
217
+
218
+
219
+ def _load_memory_files(memory_dir: Path) -> dict:
220
+ """
221
+ Load all .md files from memory_dir.
222
+ Returns dict mapping filename -> file content string.
223
+ """
224
+ files = {}
225
+ try:
226
+ for entry in memory_dir.iterdir():
227
+ if entry.is_file() and entry.suffix == ".md":
228
+ try:
229
+ content = entry.read_text(encoding="utf-8", errors="replace")
230
+ files[str(entry)] = content
231
+ except (PermissionError, OSError):
232
+ pass
233
+ except (PermissionError, OSError, FileNotFoundError):
234
+ pass
235
+ return files
236
+
237
+
238
+ def detect_conflicts(
239
+ memory_dir: Optional[Path] = None,
240
+ threshold: float = 0.3,
241
+ ) -> list:
242
+ """
243
+ Scan memory .md files for contradictions.
244
+
245
+ Args:
246
+ memory_dir: Path to directory containing memory .md files.
247
+ Defaults to ~/.claude/projects/-home-jorge-ws-me/memory/
248
+ threshold: Jaccard similarity threshold above which pairs are checked
249
+ for polarity contradictions. Default 0.3.
250
+
251
+ Returns:
252
+ List of dicts with keys:
253
+ file_a - absolute path of first file
254
+ file_b - absolute path of second file
255
+ similarity - Jaccard similarity score (float)
256
+ conflicts - list of {"line_a", "line_b", "reason"} dicts
257
+ """
258
+ if memory_dir is None:
259
+ default = Path.home() / ".claude" / "projects" / "-home-jorge-ws-me" / "memory"
260
+ memory_dir = default
261
+
262
+ memory_dir = Path(memory_dir)
263
+
264
+ file_contents = _load_memory_files(memory_dir)
265
+ if not file_contents:
266
+ return []
267
+
268
+ file_paths = sorted(file_contents.keys())
269
+ word_sets = {fp: _tokenize(file_contents[fp]) for fp in file_paths}
270
+ file_lines = {fp: _extract_lines(file_contents[fp]) for fp in file_paths}
271
+
272
+ results = []
273
+
274
+ for i in range(len(file_paths)):
275
+ for j in range(i + 1, len(file_paths)):
276
+ fp_a = file_paths[i]
277
+ fp_b = file_paths[j]
278
+
279
+ sim = _jaccard(word_sets[fp_a], word_sets[fp_b])
280
+ if sim <= threshold:
281
+ continue
282
+
283
+ conflicts = _check_polarity_contradictions(
284
+ file_lines[fp_a],
285
+ file_lines[fp_b],
286
+ )
287
+
288
+ results.append({
289
+ "file_a": fp_a,
290
+ "file_b": fp_b,
291
+ "similarity": round(sim, 4),
292
+ "conflicts": conflicts,
293
+ })
294
+
295
+ return results