@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,589 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ CLI entry point for gaia-scan -- the SINGLE entry point for both fresh
4
+ projects and existing projects.
5
+
6
+ Modes:
7
+ Mode 1 (Fresh): No .claude/ directory -- full setup flow (automatic).
8
+ Mode 2 (Existing): .claude/ exists -- rescan + sync + verify.
9
+ Mode 3 (Scan-only): --scan-only or --json -- scanners only, JSON output.
10
+
11
+ Usage:
12
+ gaia-scan # Auto-detect mode
13
+ gaia-scan --root /some/path # Scan a specific directory
14
+ gaia-scan --scanners stack,git # Run subset of scanners
15
+ gaia-scan --output /tmp/ctx.json # Custom output path
16
+ gaia-scan --check-staleness # Exit 0 if fresh, else scan
17
+ gaia-scan --verbose # Scanner-by-scanner progress
18
+ gaia-scan --json # JSON output to stdout
19
+ gaia-scan --scan-only # Scan only, no setup/sync
20
+
21
+ Exit codes:
22
+ 0 Success
23
+ 1 Error
24
+ """
25
+
26
+ import argparse
27
+ import json
28
+ import logging
29
+ import os
30
+ import sys
31
+ import time
32
+ from datetime import datetime, timezone
33
+ from pathlib import Path
34
+
35
+ # Ensure the plugin root is on sys.path so `tools.scan` resolves correctly.
36
+ _SCRIPT_DIR = Path(__file__).resolve().parent
37
+ _PLUGIN_ROOT = _SCRIPT_DIR.parent
38
+ if str(_PLUGIN_ROOT) not in sys.path:
39
+ sys.path.insert(0, str(_PLUGIN_ROOT))
40
+
41
+ from tools.scan.config import ScanConfig, load_scan_config
42
+ from tools.scan.orchestrator import ScanOrchestrator
43
+ from tools.scan.registry import ScannerRegistry
44
+ from tools.scan.scanners.tools import ToolScanner
45
+
46
+
47
+ def _get_version():
48
+ """Read version from package.json."""
49
+ try:
50
+ pkg_path = Path(__file__).resolve().parent.parent / "package.json"
51
+ with open(pkg_path) as f:
52
+ return json.load(f)["version"]
53
+ except Exception:
54
+ return "unknown"
55
+
56
+
57
+ scanner_version = _get_version()
58
+
59
+
60
+ def _is_context_fresh(project_root: Path, staleness_hours: int) -> bool:
61
+ """Check if project-context.json exists and is within staleness threshold.
62
+
63
+ Returns True if the context file exists and its last_scan timestamp is
64
+ less than staleness_hours old. Falls back to mtime if last_scan is not
65
+ available.
66
+ """
67
+ context_path = project_root / ".claude" / "project-context" / "project-context.json"
68
+
69
+ if not context_path.is_file():
70
+ return False
71
+
72
+ try:
73
+ with open(context_path, "r") as f:
74
+ data = json.load(f)
75
+
76
+ last_scan = (
77
+ data.get("metadata", {}).get("scan_config", {}).get("last_scan")
78
+ )
79
+
80
+ if last_scan:
81
+ scan_dt = datetime.fromisoformat(last_scan)
82
+ now = datetime.now(timezone.utc)
83
+ age_hours = (now - scan_dt).total_seconds() / 3600
84
+ return age_hours < staleness_hours
85
+
86
+ # Fallback: use file mtime
87
+ mtime = context_path.stat().st_mtime
88
+ age_hours = (time.time() - mtime) / 3600
89
+ return age_hours < staleness_hours
90
+
91
+ except (json.JSONDecodeError, OSError, ValueError):
92
+ return False
93
+
94
+
95
+ def _build_summary(output) -> dict:
96
+ """Build a human-friendly summary dict from ScanOutput."""
97
+ return {
98
+ "scanner_version": scanner_version,
99
+ "sections_updated": output.sections_updated,
100
+ "sections_preserved": output.sections_preserved,
101
+ "scanners_run": len(output.scanner_results),
102
+ "warnings_count": len(output.warnings),
103
+ "errors_count": len(output.errors),
104
+ "duration_ms": round(output.duration_ms, 1),
105
+ "warnings": output.warnings[:20],
106
+ "errors": output.errors[:20],
107
+ }
108
+
109
+
110
+ def _run_scan(project_root: Path, scan_config: ScanConfig) -> object:
111
+ """Run the 6 Python scanners and return ScanOutput."""
112
+ registry = ScannerRegistry()
113
+ orchestrator = ScanOrchestrator(registry=registry, config=scan_config)
114
+ return orchestrator.run(project_root=project_root)
115
+
116
+
117
+ def _extract_config_from_scan(output, project_root: Path) -> dict:
118
+ """Extract a config dict from scan output for setup functions.
119
+
120
+ Priority: env vars > scan results > defaults.
121
+ """
122
+ ctx = output.context
123
+ paths = ctx.get("paths", {})
124
+ sections = ctx.get("sections", {})
125
+ meta = ctx.get("metadata", {})
126
+
127
+ infrastructure = sections.get("infrastructure", {})
128
+ cloud_providers = infrastructure.get("cloud_providers", [])
129
+ primary_cloud = cloud_providers[0] if isinstance(cloud_providers, list) and cloud_providers else {}
130
+ ci_cd = infrastructure.get("ci_cd", [])
131
+ primary_ci = ci_cd[0] if isinstance(ci_cd, list) and ci_cd else {}
132
+
133
+ git_section = sections.get("git", {})
134
+ project_identity = sections.get("project_identity", {})
135
+
136
+ # Extract detected values
137
+ detected_cloud = primary_cloud.get("name") or meta.get("cloud_provider") or "gcp"
138
+ detected_project_id = (
139
+ primary_cloud.get("project_id")
140
+ or primary_cloud.get("account_id")
141
+ or meta.get("project_id")
142
+ or ""
143
+ )
144
+ detected_region = primary_cloud.get("region") or meta.get("primary_region") or ""
145
+
146
+ config = {
147
+ "gitops": (
148
+ os.environ.get("CLAUDE_GITOPS_DIR")
149
+ or paths.get("gitops")
150
+ or infrastructure.get("paths", {}).get("gitops")
151
+ or ""
152
+ ),
153
+ "terraform": (
154
+ os.environ.get("CLAUDE_TERRAFORM_DIR")
155
+ or paths.get("terraform")
156
+ or infrastructure.get("paths", {}).get("terraform")
157
+ or ""
158
+ ),
159
+ "app_services": (
160
+ os.environ.get("CLAUDE_APP_SERVICES_DIR")
161
+ or paths.get("app_services")
162
+ or infrastructure.get("paths", {}).get("app_services")
163
+ or ""
164
+ ),
165
+ "cloud_provider": detected_cloud,
166
+ "project_id": (
167
+ os.environ.get("CLAUDE_PROJECT_ID")
168
+ or detected_project_id
169
+ ),
170
+ "region": (
171
+ os.environ.get("CLAUDE_REGION")
172
+ or detected_region
173
+ ),
174
+ "cluster_name": (
175
+ os.environ.get("CLAUDE_CLUSTER_NAME")
176
+ or ""
177
+ ),
178
+ "project_name": (
179
+ project_identity.get("name")
180
+ or meta.get("project_name")
181
+ or project_root.name
182
+ ),
183
+ "git_platform": git_section.get("platform"),
184
+ "ci_platform": primary_ci.get("platform") if isinstance(primary_ci, dict) else None,
185
+ }
186
+
187
+ return config
188
+
189
+
190
+ # ============================================================================
191
+ # Mode 1: Fresh project setup (no .claude/ directory)
192
+ # ============================================================================
193
+
194
+ def _mode_fresh(project_root: Path, scan_config: ScanConfig, args) -> int:
195
+ """Full setup flow for a fresh project. Fully automatic, no prompts.
196
+
197
+ Steps: SCAN -> DISPLAY -> INSTALL -> VERIFY -> SUMMARY
198
+ """
199
+ from tools.scan.setup import (
200
+ copy_claude_md,
201
+ copy_settings_json,
202
+ create_claude_directory,
203
+ ensure_claude_code,
204
+ ensure_gaia_ops_package,
205
+ generate_project_context,
206
+ install_git_hooks,
207
+ merge_hooks_to_settings_local,
208
+ )
209
+ from tools.scan.ui import (
210
+ RailUI,
211
+ collect_created_summary,
212
+ collect_warnings,
213
+ format_scanner_results,
214
+ )
215
+ from tools.scan.verify import run_verification
216
+
217
+ ui = RailUI(version=scanner_version, color=_use_color(args))
218
+
219
+ # Step 1: Header + scanning
220
+ ui.start()
221
+ ui.scanning()
222
+
223
+ # Step 2: SCAN
224
+ output = _run_scan(project_root, scan_config)
225
+
226
+ # Step 3: DISPLAY results
227
+ display_sections = format_scanner_results(output, project_root=project_root)
228
+ for sec in display_sections:
229
+ ui.section(sec["name"], sec["lines"])
230
+
231
+ # Warnings
232
+ warnings = collect_warnings(output)
233
+ if warnings:
234
+ ui.warning(len(warnings), warnings)
235
+
236
+ # Extract config from scan results
237
+ config = _extract_config_from_scan(output, project_root)
238
+
239
+ # Step 4: INSTALL (automatic, no prompts)
240
+ skip_claude = getattr(args, "skip_claude_install", False)
241
+ npm_postinstall = getattr(args, "npm_postinstall", False)
242
+ ensure_claude_code(skip_install=skip_claude)
243
+ if not npm_postinstall:
244
+ # Skip when called from npm postinstall to avoid re-entrance
245
+ ensure_gaia_ops_package(project_root)
246
+ create_claude_directory(project_root)
247
+ copy_claude_md(project_root)
248
+ copy_settings_json(project_root)
249
+ merge_hooks_to_settings_local(project_root)
250
+ install_git_hooks(project_root)
251
+ generate_project_context(project_root, config, scan_context=output.context)
252
+
253
+ # Step 5: VERIFY (silent -- used for health check)
254
+ run_verification(project_root)
255
+
256
+ # Step 6: Done + Created summary
257
+ duration_s = output.duration_ms / 1000
258
+ ui.done(duration_s)
259
+
260
+ created_items = collect_created_summary(project_root, output)
261
+ if created_items:
262
+ ui.created(created_items)
263
+
264
+ ui.footer("Run claude to start. Context will enrich automatically.")
265
+
266
+ # JSON summary to stdout (only when --json or piped)
267
+ summary = _build_summary(output)
268
+ summary["status"] = "success"
269
+ summary["mode"] = "fresh"
270
+ if _should_print_json(args):
271
+ print(json.dumps(summary, indent=2), file=sys.stdout)
272
+
273
+ return 0
274
+
275
+
276
+ # ============================================================================
277
+ # Mode 2: Existing project rescan + sync
278
+ # ============================================================================
279
+
280
+ def _mode_existing(project_root: Path, scan_config: ScanConfig, args) -> int:
281
+ """Rescan + sync flow for an existing project. Fully automatic.
282
+
283
+ Steps: SCAN -> DISPLAY -> SYNC -> SUMMARY
284
+ """
285
+ from tools.scan.setup import (
286
+ copy_claude_md,
287
+ copy_settings_json,
288
+ create_claude_directory,
289
+ install_git_hooks,
290
+ merge_hooks_to_settings_local,
291
+ )
292
+ from tools.scan.ui import (
293
+ RailUI,
294
+ collect_warnings,
295
+ format_scanner_results,
296
+ )
297
+ from tools.scan.verify import run_verification
298
+
299
+ ui = RailUI(version=scanner_version, color=_use_color(args))
300
+
301
+ # Step 1: Header + scanning
302
+ ui.start()
303
+ ui.scanning()
304
+
305
+ # Step 2: SCAN
306
+ output = _run_scan(project_root, scan_config)
307
+
308
+ # Step 3: DISPLAY results
309
+ display_sections = format_scanner_results(output, project_root=project_root)
310
+ for sec in display_sections:
311
+ ui.section(sec["name"], sec["lines"])
312
+
313
+ # Warnings
314
+ warnings = collect_warnings(output)
315
+ if warnings:
316
+ ui.warning(len(warnings), warnings)
317
+
318
+ # Step 4: SYNC
319
+ copy_claude_md(project_root)
320
+ copy_settings_json(project_root)
321
+ merge_hooks_to_settings_local(project_root)
322
+ create_claude_directory(project_root)
323
+ install_git_hooks(project_root)
324
+
325
+ # Step 5: VERIFY (silent)
326
+ run_verification(project_root)
327
+
328
+ # Step 6: Done + Updated summary
329
+ duration_s = output.duration_ms / 1000
330
+ ui.done(duration_s)
331
+
332
+ sections_updated = len(output.sections_updated)
333
+ sections_preserved = len(output.sections_preserved)
334
+ ui.updated(sections_updated, sections_preserved)
335
+
336
+ ui.footer("Ready.")
337
+
338
+ # JSON summary to stdout (only when --json or piped)
339
+ summary = _build_summary(output)
340
+ summary["status"] = "error" if output.errors else "success"
341
+ summary["mode"] = "existing"
342
+ if _should_print_json(args):
343
+ print(json.dumps(summary, indent=2), file=sys.stdout)
344
+
345
+ return 1 if output.errors else 0
346
+
347
+
348
+ # ============================================================================
349
+ # Mode 3: Scan-only mode
350
+ # ============================================================================
351
+
352
+ def _mode_scan_only(project_root: Path, scan_config: ScanConfig, args) -> int:
353
+ """Scan-only mode: run scanners, write context, print JSON.
354
+
355
+ Compact rail output showing section names on one line.
356
+ """
357
+ from tools.scan.ui import RailUI, format_scanner_results
358
+
359
+ ui = RailUI(version=scanner_version, color=_use_color(args))
360
+
361
+ # Header (no scanning indicator for compact mode)
362
+ ui.start()
363
+
364
+ # Scan
365
+ output = _run_scan(project_root, scan_config)
366
+
367
+ # Compact section list
368
+ display_sections = format_scanner_results(output, project_root=project_root)
369
+ section_names = [sec["name"] for sec in display_sections]
370
+ if section_names:
371
+ ui.section_compact(section_names)
372
+
373
+ # Done
374
+ duration_s = output.duration_ms / 1000
375
+ sections_count = len(output.sections_updated)
376
+ ui.done(duration_s, suffix=f"{sections_count} sections updated")
377
+
378
+ ui.footer("project-context.json updated")
379
+
380
+ # JSON summary to stdout (only when --json or piped)
381
+ summary = _build_summary(output)
382
+ summary["status"] = "error" if output.errors else "success"
383
+ if _should_print_json(args):
384
+ print(json.dumps(summary, indent=2), file=sys.stdout)
385
+
386
+ return 1 if output.errors else 0
387
+
388
+
389
+ # ============================================================================
390
+ # Helpers
391
+ # ============================================================================
392
+
393
+ def _should_print_json(args) -> bool:
394
+ """Determine if JSON summary should be printed to stdout.
395
+
396
+ Print JSON only when --json flag is explicitly passed.
397
+ Never auto-print based on TTY detection -- this causes spurious stdout
398
+ output when invoked by tools (e.g., Claude Code) that capture stdout.
399
+ """
400
+ return getattr(args, "json", False)
401
+
402
+
403
+ def _use_color(args) -> bool:
404
+ """Determine if color output should be used."""
405
+ if getattr(args, "no_color", False):
406
+ return False
407
+ if os.environ.get("NO_COLOR"):
408
+ return False
409
+ return True
410
+
411
+
412
+ # ============================================================================
413
+ # CLI
414
+ # ============================================================================
415
+
416
+ def main(argv: list = None) -> int:
417
+ """CLI main entry point. Returns exit code."""
418
+ parser = argparse.ArgumentParser(
419
+ prog="gaia-scan",
420
+ description=(
421
+ "Scan a project and generate/update project-context.json. "
422
+ "For fresh projects (no .claude/), runs the full setup flow. "
423
+ "For existing projects, rescans and syncs configuration."
424
+ ),
425
+ )
426
+
427
+ # Core flags
428
+ parser.add_argument(
429
+ "--root",
430
+ type=str,
431
+ default=None,
432
+ help="Project root directory (default: current working directory)",
433
+ )
434
+ parser.add_argument(
435
+ "--scanners",
436
+ type=str,
437
+ default=None,
438
+ help="Comma-separated list of scanners to run (default: all)",
439
+ )
440
+ parser.add_argument(
441
+ "--output",
442
+ type=str,
443
+ default=None,
444
+ help="Custom output path for project-context.json",
445
+ )
446
+ parser.add_argument(
447
+ "--check-staleness",
448
+ action="store_true",
449
+ default=False,
450
+ help="Only scan if context is stale or missing; exit 0 immediately if fresh",
451
+ )
452
+ parser.add_argument(
453
+ "--verbose", "-v",
454
+ action="store_true",
455
+ default=False,
456
+ help="Print scanner-by-scanner progress",
457
+ )
458
+ parser.add_argument(
459
+ "--json",
460
+ action="store_true",
461
+ default=False,
462
+ help="JSON-only output to stdout (scan-only mode, no setup)",
463
+ )
464
+ parser.add_argument(
465
+ "--version",
466
+ action="version",
467
+ version=f"gaia-scan {_get_version()}",
468
+ )
469
+ parser.add_argument(
470
+ "--scan-only",
471
+ action="store_true",
472
+ default=False,
473
+ dest="scan_only",
474
+ help="Run scanners only, do not perform setup or sync",
475
+ )
476
+ parser.add_argument(
477
+ "--full",
478
+ action="store_true",
479
+ default=False,
480
+ help="Scan all tools including extended (low-value) ones",
481
+ )
482
+ parser.add_argument(
483
+ "--no-color",
484
+ action="store_true",
485
+ default=False,
486
+ dest="no_color",
487
+ help="Disable ANSI color output",
488
+ )
489
+
490
+ # Backward compat flags (no-ops, behavior is now always non-interactive)
491
+ parser.add_argument(
492
+ "--non-interactive", "-y",
493
+ action="store_true",
494
+ default=False,
495
+ help="(no-op, kept for backward compatibility) Accept detected values without confirmation",
496
+ )
497
+ parser.add_argument(
498
+ "--skip-claude-install",
499
+ action="store_true",
500
+ default=False,
501
+ dest="skip_claude_install",
502
+ help="Skip Claude Code CLI installation",
503
+ )
504
+ parser.add_argument(
505
+ "--npm-postinstall",
506
+ action="store_true",
507
+ default=False,
508
+ dest="npm_postinstall",
509
+ help="Called from npm postinstall: skip Claude Code install and npm package install to avoid re-entrance",
510
+ )
511
+
512
+ args = parser.parse_args(argv)
513
+
514
+ # Configure logging
515
+ log_level = logging.DEBUG if args.verbose else logging.WARNING
516
+ logging.basicConfig(
517
+ level=log_level,
518
+ format="%(asctime)s [gaia-scan] %(name)s - %(levelname)s - %(message)s",
519
+ stream=sys.stderr,
520
+ )
521
+
522
+ try:
523
+ project_root = Path(args.root).resolve() if args.root else Path.cwd()
524
+
525
+ if not project_root.is_dir():
526
+ print(
527
+ json.dumps({"error": f"Project root not found: {project_root}"}),
528
+ file=sys.stdout,
529
+ )
530
+ return 1
531
+
532
+ # Set extended tool scanning flag before registry discovers scanners
533
+ if args.full:
534
+ ToolScanner.scan_extended = True
535
+
536
+ # Load scan config from existing project-context.json
537
+ scan_config = load_scan_config(project_root)
538
+ scan_config.project_root = project_root
539
+ scan_config.verbose = args.verbose
540
+
541
+ if args.scanners:
542
+ scan_config.scanners = [
543
+ s.strip() for s in args.scanners.split(",") if s.strip()
544
+ ]
545
+
546
+ if args.output:
547
+ scan_config.output_path = Path(args.output).resolve()
548
+
549
+ # Staleness check (short-circuit)
550
+ if args.check_staleness:
551
+ if _is_context_fresh(project_root, scan_config.staleness_hours):
552
+ result = {
553
+ "status": "fresh",
554
+ "message": "Context is up to date, scan skipped.",
555
+ }
556
+ print(json.dumps(result), file=sys.stdout)
557
+ return 0
558
+
559
+ # --npm-postinstall implies --skip-claude-install and skips ensure_gaia_ops_package
560
+ if args.npm_postinstall:
561
+ args.skip_claude_install = True
562
+
563
+ # Mode selection
564
+ # --json or --scan-only: scan-only mode
565
+ if args.json or args.scan_only:
566
+ return _mode_scan_only(project_root, scan_config, args)
567
+
568
+ # --npm-postinstall: fresh install mode with re-entrance protection
569
+ if args.npm_postinstall:
570
+ return _mode_fresh(project_root, scan_config, args)
571
+
572
+ # Detect mode based on .claude/ existence
573
+ claude_dir = project_root / ".claude"
574
+ if claude_dir.is_dir():
575
+ # Mode 2: Existing project
576
+ return _mode_existing(project_root, scan_config, args)
577
+ else:
578
+ # Mode 1: Fresh project
579
+ return _mode_fresh(project_root, scan_config, args)
580
+
581
+ except Exception as exc:
582
+ error_result = {"status": "error", "error": str(exc)}
583
+ print(json.dumps(error_result), file=sys.stdout)
584
+ logging.exception("gaia-scan failed")
585
+ return 1
586
+
587
+
588
+ if __name__ == "__main__":
589
+ sys.exit(main())