@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,302 @@
1
+ """
2
+ gaia status -- Quick snapshot of the Gaia-Ops system state.
3
+
4
+ Mirrors the output of gaia-status.js:
5
+ - Last agent session (name, time, status)
6
+ - Pending context updates count
7
+ - Active anomaly signals count
8
+ - project-context.json freshness
9
+ - Episodic memory stats
10
+ - Contract validation stats
11
+ """
12
+
13
+ import json
14
+ import os
15
+ import sys
16
+ from datetime import datetime
17
+ from pathlib import Path
18
+
19
+
20
+ def _find_project_root() -> Path:
21
+ """Walk up from cwd until .claude/ is found, or fall back to cwd."""
22
+ init_cwd = os.environ.get("INIT_CWD")
23
+ if init_cwd and (Path(init_cwd) / ".claude").is_dir():
24
+ return Path(init_cwd)
25
+
26
+ current = Path.cwd()
27
+ root = Path(current.anchor)
28
+ while current != root:
29
+ if (current / ".claude").is_dir():
30
+ return current
31
+ current = current.parent
32
+
33
+ return Path(init_cwd) if init_cwd else Path.cwd()
34
+
35
+
36
+ def _read_json(path: Path):
37
+ """Read and parse a JSON file, returning None on any error."""
38
+ try:
39
+ return json.loads(path.read_text())
40
+ except Exception:
41
+ return None
42
+
43
+
44
+ def _read_episodic_index(project_root: Path):
45
+ """Read episodic memory index. Returns dict with episodes, last_agent, source."""
46
+ index_path = project_root / ".claude" / "project-context" / "episodic-memory" / "index.json"
47
+ data = _read_json(index_path)
48
+
49
+ if data and isinstance(data.get("episodes"), list):
50
+ episodes = data["episodes"]
51
+ with_agent = [e for e in episodes if e.get("agent")]
52
+ last_agent = with_agent[-1] if with_agent else None
53
+ return {"episodes": episodes, "last_agent": last_agent, "source": "episodic-memory"}
54
+
55
+ # Fallback: legacy metrics.jsonl
56
+ metrics_path = (
57
+ project_root / ".claude" / "project-context" / "workflow-episodic-memory" / "metrics.jsonl"
58
+ )
59
+ last_agent = None
60
+ if metrics_path.is_file():
61
+ try:
62
+ lines = [ln.strip() for ln in metrics_path.read_text().splitlines() if ln.strip()]
63
+ if lines:
64
+ last_agent = json.loads(lines[-1])
65
+ except Exception:
66
+ pass
67
+
68
+ return {"episodes": [], "last_agent": last_agent, "source": "legacy"}
69
+
70
+
71
+ def _get_pending_count(project_root: Path) -> int:
72
+ """Count pending context updates."""
73
+ path = project_root / ".claude" / "project-context" / "pending-updates" / "pending-index.json"
74
+ data = _read_json(path)
75
+ if data:
76
+ return data.get("pending_count", 0)
77
+ return 0
78
+
79
+
80
+ def _get_anomaly_count(project_root: Path) -> int:
81
+ """Count active signal .flag files."""
82
+ signals_dir = project_root / ".claude" / "project-context" / "workflow-episodic-memory" / "signals"
83
+ if not signals_dir.is_dir():
84
+ return 0
85
+ try:
86
+ return len([f for f in signals_dir.iterdir() if f.suffix == ".flag"])
87
+ except OSError:
88
+ return 0
89
+
90
+
91
+ def _get_context_last_updated(project_root: Path):
92
+ """Get last_updated from project-context.json metadata."""
93
+ path = project_root / ".claude" / "project-context" / "project-context.json"
94
+ data = _read_json(path)
95
+ if data:
96
+ return (data.get("metadata") or {}).get("last_updated")
97
+ return None
98
+
99
+
100
+ def _get_memory_v2_stats(project_root: Path) -> dict:
101
+ """Get Memory v2 stats: indexed count and avg score.
102
+
103
+ Lazily imports tools.memory.search_store and tools.memory.scoring.
104
+ Returns {"indexed": 0, "avg_score": None} on any failure.
105
+ """
106
+ base = {"indexed": 0, "avg_score": None}
107
+ try:
108
+ import tools.memory.search_store as search_store # noqa: PLC0415
109
+ import tools.memory.scoring as scoring # noqa: PLC0415
110
+ except ImportError:
111
+ return base
112
+
113
+ try:
114
+ indexed = search_store.count()
115
+ except Exception:
116
+ return base
117
+
118
+ # Compute avg_score from a sample of index entries
119
+ index_path = (
120
+ project_root / ".claude" / "project-context" / "episodic-memory" / "index.json"
121
+ )
122
+ avg_score = None
123
+ try:
124
+ data = _read_json(index_path)
125
+ if data and isinstance(data.get("episodes"), list):
126
+ episodes = data["episodes"]
127
+ if episodes:
128
+ now = __import__("datetime").datetime.now(__import__("datetime").timezone.utc)
129
+ scores = []
130
+ for ep in episodes[:50]: # sample at most 50 entries
131
+ ts = ep.get("timestamp") or ep.get("created_at")
132
+ retrieval_count = ep.get("retrieval_count", 0) or 0
133
+ if ts:
134
+ try:
135
+ dt = __import__("datetime").datetime.fromisoformat(
136
+ ts.replace("Z", "+00:00")
137
+ )
138
+ days_old = max(0.0, (now - dt).total_seconds() / 86400)
139
+ except Exception:
140
+ days_old = 0.0
141
+ else:
142
+ days_old = 0.0
143
+ scores.append(scoring.score_memory(days_old, retrieval_count))
144
+ if scores:
145
+ avg_score = sum(scores) / len(scores)
146
+ except Exception:
147
+ pass
148
+
149
+ return {"indexed": indexed, "avg_score": avg_score}
150
+
151
+
152
+ def _get_contract_stats(project_root: Path):
153
+ """Get response contract validation stats from session directories."""
154
+ contract_dir = project_root / ".claude" / "session" / "active" / "response-contract"
155
+ if not contract_dir.is_dir():
156
+ return None
157
+
158
+ valid = 0
159
+ total = 0
160
+
161
+ try:
162
+ for entry in contract_dir.iterdir():
163
+ if not entry.is_dir() or not entry.name.startswith("session-"):
164
+ continue
165
+ result_path = entry / "last-result.json"
166
+ data = _read_json(result_path)
167
+ if data and "validation" in data:
168
+ total += 1
169
+ if data["validation"].get("valid"):
170
+ valid += 1
171
+ except OSError:
172
+ return None
173
+
174
+ return {"valid": valid, "total": total} if total > 0 else None
175
+
176
+
177
+ def _format_time(iso_str):
178
+ """Format ISO timestamp to short local time string."""
179
+ if not iso_str:
180
+ return "unknown"
181
+ try:
182
+ dt = datetime.fromisoformat(iso_str.replace("Z", "+00:00"))
183
+ today = datetime.now().strftime("%Y-%m-%d")
184
+ day_str = dt.strftime("%Y-%m-%d")
185
+ if day_str == today:
186
+ return dt.strftime("%H:%M")
187
+ return dt.strftime("%m-%d %H:%M")
188
+ except Exception:
189
+ return str(iso_str)[:16].replace("T", " ")
190
+
191
+
192
+ def _collect_status(project_root: Path) -> dict:
193
+ """Collect all status data into a dict."""
194
+ episodic = _read_episodic_index(project_root)
195
+ pending_count = _get_pending_count(project_root)
196
+ anomaly_count = _get_anomaly_count(project_root)
197
+ context_updated = _get_context_last_updated(project_root)
198
+ contract_stats = _get_contract_stats(project_root)
199
+ memory_v2 = _get_memory_v2_stats(project_root)
200
+
201
+ last_agent = episodic["last_agent"]
202
+ episodes = episodic["episodes"]
203
+ episode_count = len(episodes)
204
+ agent_session_count = len([e for e in episodes if e.get("agent")])
205
+
206
+ return {
207
+ "last_agent": last_agent,
208
+ "episode_count": episode_count,
209
+ "agent_session_count": agent_session_count,
210
+ "pending_count": pending_count,
211
+ "anomaly_count": anomaly_count,
212
+ "context_updated": context_updated,
213
+ "contract_stats": contract_stats,
214
+ "indexed": memory_v2["indexed"],
215
+ "avg_score": memory_v2["avg_score"],
216
+ }
217
+
218
+
219
+ def _print_human(status: dict) -> None:
220
+ """Print human-readable status output."""
221
+ sep = "-" * 50
222
+
223
+ print("\n Gaia System Status")
224
+ print(f" {sep}")
225
+
226
+ # Last agent
227
+ last = status["last_agent"]
228
+ if last:
229
+ time_str = _format_time(last.get("timestamp"))
230
+ plan_status = last.get("plan_status") or ("ok" if last.get("exit_code") == 0 else "failed")
231
+ agent_name = last.get("agent", "(unknown)")
232
+ print(f" Last agent: {agent_name:<22} {time_str} -- {plan_status}")
233
+ else:
234
+ print(" Last agent: no agent sessions recorded yet")
235
+
236
+ # Pending
237
+ pc = status["pending_count"]
238
+ suffix = "" if pc == 1 else "s"
239
+ note = " run: gaia approvals" if pc > 0 else ""
240
+ print(f" Pending: {pc} context update{suffix} to review{note}")
241
+
242
+ # Anomalies
243
+ ac = status["anomaly_count"]
244
+ suffix = "" if ac == 1 else "s"
245
+ note = " check workflow-episodic-memory/signals/" if ac > 0 else ""
246
+ print(f" Anomalies: {ac} active signal{suffix}{note}")
247
+
248
+ # Context
249
+ ctx = status["context_updated"]
250
+ if ctx:
251
+ print(f" Context: project-context.json -- updated {_format_time(ctx)}")
252
+ else:
253
+ print(" Context: project-context.json missing -- run gaia-scan")
254
+
255
+ # Memory
256
+ ep = status["episode_count"]
257
+ ag = status["agent_session_count"]
258
+ ep_str = f"{ep} episodes" if ep else "no episodic-memory"
259
+ ag_str = f"{ag} agent sessions" if ag > 0 else "no agent sessions"
260
+ indexed = status.get("indexed", 0)
261
+ avg_score = status.get("avg_score")
262
+ if avg_score is not None:
263
+ print(f" Memory: {ep_str} | {ag_str} | {indexed} indexed | avg score {avg_score:.2f}")
264
+ else:
265
+ print(f" Memory: {ep_str} | {ag_str} | {indexed} indexed")
266
+
267
+ # Contracts
268
+ cs = status["contract_stats"]
269
+ if cs:
270
+ pct = round((cs["valid"] / cs["total"]) * 100) if cs["total"] else 0
271
+ print(f" Contracts: {cs['valid']} valid / {cs['total']} total ({pct}% success rate)")
272
+
273
+ print(f" {sep}\n")
274
+
275
+
276
+ def register(subparsers):
277
+ """Register the status subcommand."""
278
+ sub = subparsers.add_parser("status", help="Show Gaia system status")
279
+ sub.add_argument("--json", action="store_true", default=False, help="Output as JSON")
280
+
281
+
282
+ def cmd_status(args) -> int:
283
+ """Handler for `gaia status`."""
284
+ project_root = _find_project_root()
285
+ claude_dir = project_root / ".claude"
286
+
287
+ if not claude_dir.is_dir():
288
+ msg = "gaia-ops not installed in this directory. Run: gaia-scan"
289
+ if getattr(args, "json", False):
290
+ print(json.dumps({"error": msg}))
291
+ else:
292
+ print(f"\n {msg}\n")
293
+ return 1
294
+
295
+ status = _collect_status(project_root)
296
+
297
+ if getattr(args, "json", False):
298
+ print(json.dumps(status, indent=2, default=str))
299
+ else:
300
+ _print_human(status)
301
+
302
+ return 0
@@ -0,0 +1,382 @@
1
+ """
2
+ gaia update -- mirror of gaia-update.js
3
+
4
+ Checks and updates the gaia installation:
5
+ - Verify settings.json exists (create if missing)
6
+ - Merge permissions into settings.local.json
7
+ - Check symlinks (recreate if missing or broken)
8
+ - Run verification checks (hooks, python, project-context, config, agents)
9
+
10
+ Flags:
11
+ --dry-run Detect what would change without mutating files
12
+ --verbose Show all check results (including passing ones)
13
+ --json Machine-readable output
14
+ """
15
+
16
+ import json
17
+ import os
18
+ import subprocess
19
+ import sys
20
+ from pathlib import Path
21
+
22
+
23
+ # ---------------------------------------------------------------------------
24
+ # Project root detection
25
+ # ---------------------------------------------------------------------------
26
+
27
+ def _find_project_root() -> Path:
28
+ start = Path(os.environ.get("INIT_CWD", "")) if os.environ.get("INIT_CWD") else None
29
+ if start and (start / ".claude").exists():
30
+ return start
31
+
32
+ current = Path.cwd()
33
+ while True:
34
+ if (current / ".claude").exists():
35
+ return current
36
+ parent = current.parent
37
+ if parent == current:
38
+ break
39
+ current = parent
40
+
41
+ return Path(os.environ.get("INIT_CWD", str(Path.cwd())))
42
+
43
+
44
+ def _find_package_root() -> Path:
45
+ """The gaia-ops package root (where package.json lives)."""
46
+ return Path(__file__).resolve().parents[2]
47
+
48
+
49
+ # ---------------------------------------------------------------------------
50
+ # Version detection
51
+ # ---------------------------------------------------------------------------
52
+
53
+ def _read_package_version(pkg_path: Path) -> str:
54
+ try:
55
+ data = json.loads(pkg_path.read_text(encoding="utf-8"))
56
+ return data.get("version", "unknown")
57
+ except (OSError, json.JSONDecodeError):
58
+ return "unknown"
59
+
60
+
61
+ def _detect_versions(cwd: Path, pkg_root: Path) -> dict:
62
+ current = _read_package_version(pkg_root / "package.json")
63
+ previous = None
64
+
65
+ lock_path = cwd / "package-lock.json"
66
+ if lock_path.exists():
67
+ try:
68
+ lock = json.loads(lock_path.read_text(encoding="utf-8"))
69
+ dep = (
70
+ (lock.get("packages") or {}).get("node_modules/@jaguilar87/gaia")
71
+ or (lock.get("dependencies") or {}).get("@jaguilar87/gaia")
72
+ )
73
+ if dep:
74
+ previous = dep.get("version")
75
+ except (json.JSONDecodeError, OSError):
76
+ pass
77
+
78
+ return {"current": current, "previous": previous}
79
+
80
+
81
+ # ---------------------------------------------------------------------------
82
+ # Update steps
83
+ # ---------------------------------------------------------------------------
84
+
85
+ def _check_settings_json(claude_dir: Path, dry_run: bool) -> dict:
86
+ """Create settings.json if missing (non-invasive, never overwrites)."""
87
+ if not claude_dir.exists():
88
+ return {"status": "skipped", "reason": ".claude/ not found"}
89
+
90
+ settings_path = claude_dir / "settings.json"
91
+ if settings_path.exists():
92
+ return {"status": "ok", "message": "settings.json already exists"}
93
+
94
+ if not dry_run:
95
+ settings_path.write_text("{}\n", encoding="utf-8")
96
+
97
+ return {"status": "created", "dry_run": dry_run}
98
+
99
+
100
+ def _check_symlinks(claude_dir: Path, pkg_root: Path, dry_run: bool) -> dict:
101
+ """Check symlinks exist and are not broken; recreate if needed."""
102
+ if not claude_dir.exists():
103
+ return {"status": "skipped", "reason": ".claude/ not found"}
104
+
105
+ symlink_names = ["agents", "tools", "hooks", "commands", "templates", "config", "skills"]
106
+ fixed = []
107
+ valid = []
108
+ failed = []
109
+
110
+ for name in symlink_names:
111
+ link = claude_dir / name
112
+ target = pkg_root / name
113
+
114
+ if not link.exists() and not link.is_symlink():
115
+ if not dry_run:
116
+ try:
117
+ link.symlink_to(target)
118
+ fixed.append(name)
119
+ except OSError as exc:
120
+ failed.append({"name": name, "error": str(exc)})
121
+ else:
122
+ fixed.append(name)
123
+ else:
124
+ # Check if broken
125
+ try:
126
+ link.resolve(strict=True)
127
+ valid.append(name)
128
+ except OSError:
129
+ if not dry_run:
130
+ try:
131
+ link.unlink()
132
+ link.symlink_to(target)
133
+ fixed.append(name)
134
+ except OSError as exc:
135
+ failed.append({"name": name, "error": str(exc)})
136
+ else:
137
+ fixed.append(name)
138
+
139
+ # CHANGELOG.md
140
+ changelog_link = claude_dir / "CHANGELOG.md"
141
+ changelog_src = pkg_root / "CHANGELOG.md"
142
+ if not changelog_link.exists() and not changelog_link.is_symlink():
143
+ if not dry_run:
144
+ try:
145
+ changelog_link.symlink_to(changelog_src)
146
+ fixed.append("CHANGELOG.md")
147
+ except OSError as exc:
148
+ failed.append({"name": "CHANGELOG.md", "error": str(exc)})
149
+ else:
150
+ fixed.append("CHANGELOG.md")
151
+ else:
152
+ valid.append("CHANGELOG.md")
153
+
154
+ return {
155
+ "status": "ok" if not fixed and not failed else "fixed",
156
+ "fixed": fixed,
157
+ "valid": valid,
158
+ "failed": failed,
159
+ "dry_run": dry_run,
160
+ }
161
+
162
+
163
+ # ---------------------------------------------------------------------------
164
+ # Verification checks
165
+ # ---------------------------------------------------------------------------
166
+
167
+ def _run_verification(claude_dir: Path) -> dict:
168
+ """Run installation health checks (mirrors runVerification in gaia-update.js)."""
169
+ checks = []
170
+ issues = []
171
+
172
+ # 1. Hook files
173
+ hook_files = ["pre_tool_use.py", "post_tool_use.py", "subagent_stop.py"]
174
+ for hook in hook_files:
175
+ path = claude_dir / "hooks" / hook
176
+ ok = path.exists()
177
+ checks.append({"name": hook, "ok": ok})
178
+ if not ok:
179
+ issues.append(f"Hook missing: .claude/hooks/{hook}")
180
+
181
+ # 2. Python available
182
+ py_cmd = None
183
+ for candidate in ["python3", "python"]:
184
+ try:
185
+ result = subprocess.run(
186
+ [candidate, "--version"],
187
+ capture_output=True,
188
+ text=True,
189
+ timeout=5,
190
+ )
191
+ if result.returncode == 0:
192
+ py_cmd = candidate
193
+ detail = (result.stdout or result.stderr).strip()
194
+ checks.append({"name": "python3", "ok": True, "detail": detail})
195
+ break
196
+ except (OSError, subprocess.TimeoutExpired):
197
+ pass
198
+ if py_cmd is None:
199
+ checks.append({"name": "python3", "ok": False})
200
+ issues.append("Python 3 not found (required for hooks)")
201
+
202
+ # 3. project-context.json
203
+ ctx_path = claude_dir / "project-context" / "project-context.json"
204
+ if ctx_path.exists():
205
+ try:
206
+ ctx = json.loads(ctx_path.read_text(encoding="utf-8"))
207
+ sections = len(ctx.get("sections", {}))
208
+ ok = sections >= 3
209
+ checks.append({"name": "project-context.json", "ok": ok, "detail": f"{sections} sections"})
210
+ if not ok:
211
+ issues.append("project-context.json has fewer than 3 sections")
212
+ except (json.JSONDecodeError, OSError):
213
+ checks.append({"name": "project-context.json", "ok": False})
214
+ issues.append("project-context.json is invalid JSON")
215
+ else:
216
+ checks.append({"name": "project-context.json", "ok": False})
217
+ issues.append("project-context.json not found (run gaia-scan)")
218
+
219
+ # 4. Config files
220
+ config_files = ["git_standards.json", "universal-rules.json", "surface-routing.json"]
221
+ for cfg in config_files:
222
+ path = claude_dir / "config" / cfg
223
+ ok = path.exists()
224
+ checks.append({"name": cfg, "ok": ok})
225
+
226
+ # 5. Agent definitions
227
+ agent_files = [
228
+ "gaia-orchestrator.md", "gaia-operator.md", "terraform-architect.md",
229
+ "gitops-operator.md", "cloud-troubleshooter.md", "developer.md",
230
+ "gaia-system.md", "gaia-planner.md",
231
+ ]
232
+ agents_ok = sum(1 for a in agent_files if (claude_dir / "agents" / a).exists())
233
+ checks.append({
234
+ "name": "agent definitions",
235
+ "ok": agents_ok == len(agent_files),
236
+ "detail": f"{agents_ok}/{len(agent_files)}",
237
+ })
238
+ if agents_ok < len(agent_files):
239
+ issues.append(f"{len(agent_files) - agents_ok} agent definition(s) missing")
240
+
241
+ # 6. hooks.json
242
+ hooks_json_path = claude_dir / "hooks" / "hooks.json"
243
+ if hooks_json_path.exists():
244
+ try:
245
+ hdata = json.loads(hooks_json_path.read_text(encoding="utf-8"))
246
+ has_hooks = bool(hdata.get("hooks") and hdata["hooks"])
247
+ checks.append({"name": "hooks.json", "ok": has_hooks})
248
+ if not has_hooks:
249
+ issues.append("hooks.json has no hooks configured")
250
+ except (json.JSONDecodeError, OSError):
251
+ checks.append({"name": "hooks.json", "ok": False})
252
+ issues.append("hooks.json is invalid")
253
+ else:
254
+ checks.append({"name": "hooks.json", "ok": False})
255
+ issues.append("hooks.json not found (hooks symlink may be broken)")
256
+
257
+ passed = sum(1 for c in checks if c["ok"])
258
+ return {"checks": checks, "issues": issues, "passed": passed, "total": len(checks)}
259
+
260
+
261
+ # ---------------------------------------------------------------------------
262
+ # Plugin interface
263
+ # ---------------------------------------------------------------------------
264
+
265
+ def register(subparsers):
266
+ """Register the 'update' subcommand."""
267
+ p = subparsers.add_parser(
268
+ "update",
269
+ help="Check and update the gaia installation (settings, symlinks, verification)",
270
+ description=(
271
+ "Update the gaia installation:\n"
272
+ " - Check settings.json (create if missing)\n"
273
+ " - Check symlinks (recreate missing/broken)\n"
274
+ " - Verify installation health (hooks, python, project-context)\n"
275
+ "\n"
276
+ "--dry-run: print what would change without modifying files.\n"
277
+ ),
278
+ )
279
+ p.add_argument(
280
+ "--dry-run",
281
+ dest="dry_run",
282
+ action="store_true",
283
+ default=False,
284
+ help="Detect what would change without mutating files",
285
+ )
286
+ p.add_argument(
287
+ "--verbose",
288
+ "-v",
289
+ action="store_true",
290
+ default=False,
291
+ help="Show all check results (including passing ones)",
292
+ )
293
+ p.add_argument(
294
+ "--json",
295
+ action="store_true",
296
+ default=False,
297
+ help="Output results as JSON",
298
+ )
299
+ return p
300
+
301
+
302
+ def cmd_update(args) -> int:
303
+ """Execute the update subcommand."""
304
+ root = _find_project_root()
305
+ pkg_root = _find_package_root()
306
+ claude_dir = root / ".claude"
307
+ dry_run = getattr(args, "dry_run", False)
308
+ verbose = getattr(args, "verbose", False)
309
+ as_json = getattr(args, "json", False)
310
+
311
+ versions = _detect_versions(root, pkg_root)
312
+
313
+ if not as_json:
314
+ current = versions.get("current", "unknown")
315
+ previous = versions.get("previous")
316
+ if previous and previous != current:
317
+ print(f"\ngaia-ops update {previous} -> {current}\n")
318
+ else:
319
+ print(f"\ngaia-ops update {current}\n")
320
+ if dry_run:
321
+ print(" (dry-run mode -- no files will be modified)\n")
322
+
323
+ settings_result = _check_settings_json(claude_dir, dry_run)
324
+ symlinks_result = _check_symlinks(claude_dir, pkg_root, dry_run)
325
+ verify_result = _run_verification(claude_dir)
326
+
327
+ result = {
328
+ "root": str(root),
329
+ "versions": versions,
330
+ "dry_run": dry_run,
331
+ "settings_json": settings_result,
332
+ "symlinks": symlinks_result,
333
+ "verification": verify_result,
334
+ }
335
+
336
+ if as_json:
337
+ print(json.dumps(result, indent=2))
338
+ return 0
339
+
340
+ # Settings
341
+ s = settings_result
342
+ if s["status"] == "skipped":
343
+ if verbose:
344
+ print(f" settings.json: skipped ({s.get('reason', '')})")
345
+ elif s["status"] == "ok":
346
+ if verbose:
347
+ print(" settings.json: already exists")
348
+ elif s["status"] == "created":
349
+ verb = "Would create" if dry_run else "Created"
350
+ print(f" settings.json: {verb}")
351
+
352
+ # Symlinks
353
+ sl = symlinks_result
354
+ if sl.get("status") == "skipped":
355
+ if verbose:
356
+ print(f" Symlinks: skipped ({sl.get('reason', '')})")
357
+ elif sl.get("fixed"):
358
+ verb = "Would fix" if dry_run else "Fixed"
359
+ print(f" Symlinks: {verb} {len(sl['fixed'])} ({', '.join(sl['fixed'])})")
360
+ else:
361
+ total = len(sl.get("valid", [])) + len(sl.get("fixed", []))
362
+ if verbose:
363
+ print(f" Symlinks: {total}/{total} valid")
364
+
365
+ # Verification
366
+ v = verify_result
367
+ print()
368
+ if v["issues"]:
369
+ print(f" Health: {v['passed']}/{v['total']} checks passed, {len(v['issues'])} issue(s)")
370
+ for issue in v["issues"]:
371
+ print(f" - {issue}")
372
+ else:
373
+ print(f" Health: {v['passed']}/{v['total']} checks passed -- everything up to date")
374
+
375
+ if verbose:
376
+ for check in v["checks"]:
377
+ status = "pass" if check["ok"] else "FAIL"
378
+ detail = f" ({check.get('detail', '')})" if check.get("detail") else ""
379
+ print(f" [{status}] {check['name']}{detail}")
380
+
381
+ print()
382
+ return 0