@jaguilar87/gaia-ops 4.4.0 → 4.7.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +12 -3
- package/ARCHITECTURE.md +9 -8
- package/CHANGELOG.md +34 -0
- package/README.md +43 -11
- package/agents/terraform-architect.md +1 -1
- package/bin/README.md +2 -2
- package/bin/gaia-doctor.js +18 -5
- package/bin/gaia-history.js +0 -1
- package/bin/gaia-metrics.js +2 -2
- package/bin/gaia-scan.py +23 -1
- package/bin/gaia-update.js +346 -54
- package/bin/pre-publish-validate.js +33 -10
- package/commands/gaia.md +37 -0
- package/config/README.md +3 -9
- package/config/context-contracts.json +47 -15
- package/config/surface-routing.json +9 -1
- package/dist/gaia-ops/.claude-plugin/plugin.json +22 -0
- package/dist/gaia-ops/agents/cloud-troubleshooter.md +73 -0
- package/dist/gaia-ops/agents/devops-developer.md +57 -0
- package/dist/gaia-ops/agents/gaia-system.md +58 -0
- package/dist/gaia-ops/agents/gitops-operator.md +60 -0
- package/dist/gaia-ops/agents/speckit-planner.md +71 -0
- package/dist/gaia-ops/agents/terraform-architect.md +60 -0
- package/dist/gaia-ops/commands/gaia.md +37 -0
- package/dist/gaia-ops/config/README.md +58 -0
- package/dist/gaia-ops/config/cloud/aws.json +140 -0
- package/dist/gaia-ops/config/cloud/gcp.json +145 -0
- package/dist/gaia-ops/config/context-contracts.json +131 -0
- package/dist/gaia-ops/config/git_standards.json +72 -0
- package/dist/gaia-ops/config/surface-routing.json +197 -0
- package/dist/gaia-ops/config/universal-rules.json +10 -0
- package/dist/gaia-ops/hooks/adapters/__init__.py +52 -0
- package/dist/gaia-ops/hooks/adapters/base.py +219 -0
- package/dist/gaia-ops/hooks/adapters/channel.py +17 -0
- package/dist/gaia-ops/hooks/adapters/claude_code.py +1477 -0
- package/dist/gaia-ops/hooks/adapters/types.py +194 -0
- package/dist/gaia-ops/hooks/adapters/utils.py +25 -0
- package/dist/gaia-ops/hooks/hooks.json +126 -0
- package/dist/gaia-ops/hooks/modules/__init__.py +15 -0
- package/dist/gaia-ops/hooks/modules/agents/__init__.py +29 -0
- package/dist/gaia-ops/hooks/modules/agents/contract_validator.py +647 -0
- package/dist/gaia-ops/hooks/modules/agents/response_contract.py +496 -0
- package/dist/gaia-ops/hooks/modules/agents/skill_injection_verifier.py +124 -0
- package/dist/gaia-ops/hooks/modules/agents/task_info_builder.py +74 -0
- package/dist/gaia-ops/hooks/modules/agents/transcript_analyzer.py +458 -0
- package/dist/gaia-ops/hooks/modules/agents/transcript_reader.py +152 -0
- package/dist/gaia-ops/hooks/modules/audit/__init__.py +28 -0
- package/dist/gaia-ops/hooks/modules/audit/event_detector.py +168 -0
- package/dist/gaia-ops/hooks/modules/audit/logger.py +131 -0
- package/dist/gaia-ops/hooks/modules/audit/metrics.py +134 -0
- package/dist/gaia-ops/hooks/modules/audit/workflow_auditor.py +576 -0
- package/dist/gaia-ops/hooks/modules/audit/workflow_recorder.py +296 -0
- package/dist/gaia-ops/hooks/modules/context/__init__.py +11 -0
- package/dist/gaia-ops/hooks/modules/context/anchor_tracker.py +317 -0
- package/dist/gaia-ops/hooks/modules/context/compact_context_builder.py +215 -0
- package/dist/gaia-ops/hooks/modules/context/context_cache.py +129 -0
- package/dist/gaia-ops/hooks/modules/context/context_freshness.py +145 -0
- package/dist/gaia-ops/hooks/modules/context/context_injector.py +427 -0
- package/dist/gaia-ops/hooks/modules/context/context_writer.py +518 -0
- package/dist/gaia-ops/hooks/modules/context/contracts_loader.py +161 -0
- package/dist/gaia-ops/hooks/modules/core/__init__.py +40 -0
- package/dist/gaia-ops/hooks/modules/core/hook_entry.py +78 -0
- package/dist/gaia-ops/hooks/modules/core/paths.py +160 -0
- package/dist/gaia-ops/hooks/modules/core/plugin_mode.py +149 -0
- package/dist/gaia-ops/hooks/modules/core/plugin_setup.py +558 -0
- package/dist/gaia-ops/hooks/modules/core/state.py +179 -0
- package/dist/gaia-ops/hooks/modules/core/stdin.py +24 -0
- package/dist/gaia-ops/hooks/modules/events/__init__.py +1 -0
- package/dist/gaia-ops/hooks/modules/events/event_writer.py +210 -0
- package/dist/gaia-ops/hooks/modules/identity/__init__.py +0 -0
- package/dist/gaia-ops/hooks/modules/identity/identity_provider.py +21 -0
- package/dist/gaia-ops/hooks/modules/identity/ops_identity.py +34 -0
- package/dist/gaia-ops/hooks/modules/identity/security_identity.py +10 -0
- package/dist/gaia-ops/hooks/modules/memory/__init__.py +8 -0
- package/dist/gaia-ops/hooks/modules/memory/episode_writer.py +227 -0
- package/dist/gaia-ops/hooks/modules/orchestrator/__init__.py +1 -0
- package/dist/gaia-ops/hooks/modules/orchestrator/delegate_mode.py +128 -0
- package/dist/gaia-ops/hooks/modules/scanning/__init__.py +8 -0
- package/dist/gaia-ops/hooks/modules/scanning/scan_trigger.py +84 -0
- package/dist/gaia-ops/hooks/modules/security/__init__.py +89 -0
- package/dist/gaia-ops/hooks/modules/security/approval_cleanup.py +87 -0
- package/dist/gaia-ops/hooks/modules/security/approval_constants.py +23 -0
- package/dist/gaia-ops/hooks/modules/security/approval_grants.py +912 -0
- package/dist/gaia-ops/hooks/modules/security/approval_messages.py +71 -0
- package/dist/gaia-ops/hooks/modules/security/approval_scopes.py +153 -0
- package/dist/gaia-ops/hooks/modules/security/blocked_commands.py +584 -0
- package/dist/gaia-ops/hooks/modules/security/blocked_message_formatter.py +86 -0
- package/dist/gaia-ops/hooks/modules/security/command_semantics.py +130 -0
- package/dist/gaia-ops/hooks/modules/security/gitops_validator.py +179 -0
- package/dist/gaia-ops/hooks/modules/security/mutative_verbs.py +850 -0
- package/dist/gaia-ops/hooks/modules/security/prompt_validator.py +40 -0
- package/dist/gaia-ops/hooks/modules/security/tiers.py +196 -0
- package/dist/gaia-ops/hooks/modules/session/__init__.py +10 -0
- package/dist/gaia-ops/hooks/modules/session/session_context_writer.py +100 -0
- package/dist/gaia-ops/hooks/modules/session/session_event_injector.py +158 -0
- package/dist/gaia-ops/hooks/modules/session/session_manager.py +31 -0
- package/dist/gaia-ops/hooks/modules/tools/__init__.py +25 -0
- package/dist/gaia-ops/hooks/modules/tools/bash_validator.py +708 -0
- package/dist/gaia-ops/hooks/modules/tools/cloud_pipe_validator.py +181 -0
- package/dist/gaia-ops/hooks/modules/tools/hook_response.py +55 -0
- package/dist/gaia-ops/hooks/modules/tools/shell_parser.py +227 -0
- package/dist/gaia-ops/hooks/modules/tools/task_validator.py +283 -0
- package/dist/gaia-ops/hooks/modules/validation/__init__.py +23 -0
- package/dist/gaia-ops/hooks/modules/validation/commit_validator.py +380 -0
- package/dist/gaia-ops/hooks/post_compact.py +43 -0
- package/dist/gaia-ops/hooks/post_tool_use.py +54 -0
- package/dist/gaia-ops/hooks/pre_tool_use.py +383 -0
- package/dist/gaia-ops/hooks/session_start.py +69 -0
- package/dist/gaia-ops/hooks/stop_hook.py +69 -0
- package/dist/gaia-ops/hooks/subagent_start.py +71 -0
- package/dist/gaia-ops/hooks/subagent_stop.py +288 -0
- package/dist/gaia-ops/hooks/task_completed.py +70 -0
- package/dist/gaia-ops/hooks/user_prompt_submit.py +177 -0
- package/dist/gaia-ops/settings.json +72 -0
- package/dist/gaia-ops/skills/README.md +109 -0
- package/dist/gaia-ops/skills/agent-protocol/SKILL.md +105 -0
- package/dist/gaia-ops/skills/agent-protocol/examples.md +170 -0
- package/dist/gaia-ops/skills/agent-response/SKILL.md +53 -0
- package/dist/gaia-ops/skills/approval/SKILL.md +85 -0
- package/dist/gaia-ops/skills/approval/examples.md +140 -0
- package/dist/gaia-ops/skills/approval/reference.md +57 -0
- package/dist/gaia-ops/skills/command-execution/SKILL.md +64 -0
- package/dist/gaia-ops/skills/command-execution/reference.md +83 -0
- package/dist/gaia-ops/skills/context-updater/SKILL.md +76 -0
- package/dist/gaia-ops/skills/context-updater/examples.md +71 -0
- package/dist/gaia-ops/skills/developer-patterns/SKILL.md +93 -0
- package/dist/gaia-ops/skills/developer-patterns/reference.md +112 -0
- package/dist/gaia-ops/skills/execution/SKILL.md +66 -0
- package/dist/gaia-ops/skills/fast-queries/SKILL.md +47 -0
- package/dist/gaia-ops/skills/gaia-patterns/SKILL.md +92 -0
- package/dist/gaia-ops/skills/gaia-patterns/reference.md +22 -0
- package/dist/gaia-ops/skills/git-conventions/SKILL.md +48 -0
- package/dist/gaia-ops/skills/gitops-patterns/SKILL.md +73 -0
- package/dist/gaia-ops/skills/gitops-patterns/reference.md +183 -0
- package/dist/gaia-ops/skills/investigation/SKILL.md +77 -0
- package/dist/gaia-ops/skills/orchestrator-approval/SKILL.md +64 -0
- package/dist/gaia-ops/skills/reference.md +134 -0
- package/dist/gaia-ops/skills/security-tiers/SKILL.md +61 -0
- package/dist/gaia-ops/skills/security-tiers/destructive-commands-reference.md +623 -0
- package/dist/gaia-ops/skills/security-tiers/reference.md +39 -0
- package/dist/gaia-ops/skills/skill-creation/SKILL.md +119 -0
- package/dist/gaia-ops/skills/specification/SKILL.md +186 -0
- package/dist/gaia-ops/skills/speckit-workflow/SKILL.md +165 -0
- package/dist/gaia-ops/skills/speckit-workflow/reference.md +117 -0
- package/dist/gaia-ops/skills/terraform-patterns/SKILL.md +63 -0
- package/dist/gaia-ops/skills/terraform-patterns/reference.md +93 -0
- package/dist/gaia-ops/speckit/README.md +516 -0
- package/dist/gaia-ops/speckit/scripts/.gitkeep +0 -0
- package/dist/gaia-ops/speckit/templates/adr-template.md +118 -0
- package/dist/gaia-ops/speckit/templates/agent-file-template.md +23 -0
- package/dist/gaia-ops/speckit/templates/plan-template.md +227 -0
- package/dist/gaia-ops/speckit/templates/spec-template.md +140 -0
- package/dist/gaia-ops/speckit/templates/tasks-template.md +257 -0
- package/dist/gaia-ops/tools/context/README.md +132 -0
- package/dist/gaia-ops/tools/context/__init__.py +42 -0
- package/dist/gaia-ops/tools/context/_paths.py +20 -0
- package/dist/gaia-ops/tools/context/context_provider.py +476 -0
- package/dist/gaia-ops/tools/context/context_section_reader.py +330 -0
- package/dist/gaia-ops/tools/context/deep_merge.py +159 -0
- package/dist/gaia-ops/tools/context/pending_updates.py +760 -0
- package/dist/gaia-ops/tools/context/surface_router.py +278 -0
- package/dist/gaia-ops/tools/fast-queries/README.md +65 -0
- package/dist/gaia-ops/tools/fast-queries/__init__.py +30 -0
- package/dist/gaia-ops/tools/fast-queries/appservices/quicktriage_devops_developer.sh +75 -0
- package/dist/gaia-ops/tools/fast-queries/cloud/aws/quicktriage_aws_troubleshooter.sh +32 -0
- package/dist/gaia-ops/tools/fast-queries/cloud/gcp/quicktriage_gcp_troubleshooter.sh +88 -0
- package/dist/gaia-ops/tools/fast-queries/gitops/quicktriage_gitops_operator.sh +48 -0
- package/dist/gaia-ops/tools/fast-queries/run_triage.sh +59 -0
- package/dist/gaia-ops/tools/fast-queries/terraform/quicktriage_terraform_architect.sh +80 -0
- package/dist/gaia-ops/tools/gaia_simulator/__init__.py +33 -0
- package/dist/gaia-ops/tools/gaia_simulator/cli.py +354 -0
- package/dist/gaia-ops/tools/gaia_simulator/extractor.py +457 -0
- package/dist/gaia-ops/tools/gaia_simulator/reporter.py +258 -0
- package/dist/gaia-ops/tools/gaia_simulator/routing_simulator.py +334 -0
- package/dist/gaia-ops/tools/gaia_simulator/runner.py +539 -0
- package/dist/gaia-ops/tools/gaia_simulator/skills_mapper.py +262 -0
- package/dist/gaia-ops/tools/memory/README.md +0 -0
- package/dist/gaia-ops/tools/memory/__init__.py +20 -0
- package/dist/gaia-ops/tools/memory/episodic.py +1196 -0
- package/dist/gaia-ops/tools/persist_transcript_analysis.py +85 -0
- package/dist/gaia-ops/tools/review/__init__.py +1 -0
- package/dist/gaia-ops/tools/review/review_engine.py +157 -0
- package/dist/gaia-ops/tools/scan/__init__.py +35 -0
- package/dist/gaia-ops/tools/scan/config.py +247 -0
- package/dist/gaia-ops/tools/scan/merge.py +212 -0
- package/dist/gaia-ops/tools/scan/orchestrator.py +549 -0
- package/dist/gaia-ops/tools/scan/registry.py +127 -0
- package/dist/gaia-ops/tools/scan/scanners/__init__.py +18 -0
- package/dist/gaia-ops/tools/scan/scanners/base.py +137 -0
- package/dist/gaia-ops/tools/scan/scanners/environment.py +324 -0
- package/dist/gaia-ops/tools/scan/scanners/git.py +570 -0
- package/dist/gaia-ops/tools/scan/scanners/infrastructure.py +875 -0
- package/dist/gaia-ops/tools/scan/scanners/orchestration.py +600 -0
- package/dist/gaia-ops/tools/scan/scanners/stack.py +1085 -0
- package/dist/gaia-ops/tools/scan/scanners/tools.py +260 -0
- package/dist/gaia-ops/tools/scan/setup.py +753 -0
- package/dist/gaia-ops/tools/scan/tests/__init__.py +1 -0
- package/dist/gaia-ops/tools/scan/tests/conftest.py +796 -0
- package/dist/gaia-ops/tools/scan/tests/test_environment.py +323 -0
- package/dist/gaia-ops/tools/scan/tests/test_git.py +419 -0
- package/dist/gaia-ops/tools/scan/tests/test_infrastructure.py +382 -0
- package/dist/gaia-ops/tools/scan/tests/test_integration.py +920 -0
- package/dist/gaia-ops/tools/scan/tests/test_merge.py +269 -0
- package/dist/gaia-ops/tools/scan/tests/test_orchestration.py +304 -0
- package/dist/gaia-ops/tools/scan/tests/test_stack.py +604 -0
- package/dist/gaia-ops/tools/scan/tests/test_tools.py +349 -0
- package/dist/gaia-ops/tools/scan/ui.py +624 -0
- package/dist/gaia-ops/tools/scan/verify.py +266 -0
- package/dist/gaia-ops/tools/scan/walk.py +118 -0
- package/dist/gaia-ops/tools/scan/workspace.py +85 -0
- package/dist/gaia-ops/tools/validation/README.md +244 -0
- package/dist/gaia-ops/tools/validation/__init__.py +17 -0
- package/dist/gaia-ops/tools/validation/approval_gate.py +321 -0
- package/dist/gaia-ops/tools/validation/validate_skills.py +189 -0
- package/dist/gaia-security/.claude-plugin/plugin.json +22 -0
- package/dist/gaia-security/config/universal-rules.json +10 -0
- package/dist/gaia-security/hooks/adapters/__init__.py +52 -0
- package/dist/gaia-security/hooks/adapters/base.py +219 -0
- package/dist/gaia-security/hooks/adapters/channel.py +17 -0
- package/dist/gaia-security/hooks/adapters/claude_code.py +1477 -0
- package/dist/gaia-security/hooks/adapters/types.py +194 -0
- package/dist/gaia-security/hooks/adapters/utils.py +25 -0
- package/dist/gaia-security/hooks/hooks.json +57 -0
- package/dist/gaia-security/hooks/modules/__init__.py +15 -0
- package/dist/gaia-security/hooks/modules/agents/__init__.py +29 -0
- package/dist/gaia-security/hooks/modules/agents/contract_validator.py +647 -0
- package/dist/gaia-security/hooks/modules/agents/response_contract.py +496 -0
- package/dist/gaia-security/hooks/modules/agents/skill_injection_verifier.py +124 -0
- package/dist/gaia-security/hooks/modules/agents/task_info_builder.py +74 -0
- package/dist/gaia-security/hooks/modules/agents/transcript_analyzer.py +458 -0
- package/dist/gaia-security/hooks/modules/agents/transcript_reader.py +152 -0
- package/dist/gaia-security/hooks/modules/audit/__init__.py +28 -0
- package/dist/gaia-security/hooks/modules/audit/event_detector.py +168 -0
- package/dist/gaia-security/hooks/modules/audit/logger.py +131 -0
- package/dist/gaia-security/hooks/modules/audit/metrics.py +134 -0
- package/dist/gaia-security/hooks/modules/audit/workflow_auditor.py +576 -0
- package/dist/gaia-security/hooks/modules/audit/workflow_recorder.py +296 -0
- package/dist/gaia-security/hooks/modules/context/__init__.py +11 -0
- package/dist/gaia-security/hooks/modules/context/anchor_tracker.py +317 -0
- package/dist/gaia-security/hooks/modules/context/compact_context_builder.py +215 -0
- package/dist/gaia-security/hooks/modules/context/context_cache.py +129 -0
- package/dist/gaia-security/hooks/modules/context/context_freshness.py +145 -0
- package/dist/gaia-security/hooks/modules/context/context_injector.py +427 -0
- package/dist/gaia-security/hooks/modules/context/context_writer.py +518 -0
- package/dist/gaia-security/hooks/modules/context/contracts_loader.py +161 -0
- package/dist/gaia-security/hooks/modules/core/__init__.py +40 -0
- package/dist/gaia-security/hooks/modules/core/hook_entry.py +78 -0
- package/dist/gaia-security/hooks/modules/core/paths.py +160 -0
- package/dist/gaia-security/hooks/modules/core/plugin_mode.py +149 -0
- package/dist/gaia-security/hooks/modules/core/plugin_setup.py +558 -0
- package/dist/gaia-security/hooks/modules/core/state.py +179 -0
- package/dist/gaia-security/hooks/modules/core/stdin.py +24 -0
- package/dist/gaia-security/hooks/modules/events/__init__.py +1 -0
- package/dist/gaia-security/hooks/modules/events/event_writer.py +210 -0
- package/dist/gaia-security/hooks/modules/identity/__init__.py +0 -0
- package/dist/gaia-security/hooks/modules/identity/identity_provider.py +21 -0
- package/dist/gaia-security/hooks/modules/identity/ops_identity.py +34 -0
- package/dist/gaia-security/hooks/modules/identity/security_identity.py +10 -0
- package/dist/gaia-security/hooks/modules/memory/__init__.py +8 -0
- package/dist/gaia-security/hooks/modules/memory/episode_writer.py +227 -0
- package/dist/gaia-security/hooks/modules/orchestrator/__init__.py +1 -0
- package/dist/gaia-security/hooks/modules/orchestrator/delegate_mode.py +128 -0
- package/dist/gaia-security/hooks/modules/scanning/__init__.py +8 -0
- package/dist/gaia-security/hooks/modules/scanning/scan_trigger.py +84 -0
- package/dist/gaia-security/hooks/modules/security/__init__.py +89 -0
- package/dist/gaia-security/hooks/modules/security/approval_cleanup.py +87 -0
- package/dist/gaia-security/hooks/modules/security/approval_constants.py +23 -0
- package/dist/gaia-security/hooks/modules/security/approval_grants.py +912 -0
- package/dist/gaia-security/hooks/modules/security/approval_messages.py +71 -0
- package/dist/gaia-security/hooks/modules/security/approval_scopes.py +153 -0
- package/dist/gaia-security/hooks/modules/security/blocked_commands.py +584 -0
- package/dist/gaia-security/hooks/modules/security/blocked_message_formatter.py +86 -0
- package/dist/gaia-security/hooks/modules/security/command_semantics.py +130 -0
- package/dist/gaia-security/hooks/modules/security/gitops_validator.py +179 -0
- package/dist/gaia-security/hooks/modules/security/mutative_verbs.py +850 -0
- package/dist/gaia-security/hooks/modules/security/prompt_validator.py +40 -0
- package/dist/gaia-security/hooks/modules/security/tiers.py +196 -0
- package/dist/gaia-security/hooks/modules/session/__init__.py +10 -0
- package/dist/gaia-security/hooks/modules/session/session_context_writer.py +100 -0
- package/dist/gaia-security/hooks/modules/session/session_event_injector.py +158 -0
- package/dist/gaia-security/hooks/modules/session/session_manager.py +31 -0
- package/dist/gaia-security/hooks/modules/tools/__init__.py +25 -0
- package/dist/gaia-security/hooks/modules/tools/bash_validator.py +708 -0
- package/dist/gaia-security/hooks/modules/tools/cloud_pipe_validator.py +181 -0
- package/dist/gaia-security/hooks/modules/tools/hook_response.py +55 -0
- package/dist/gaia-security/hooks/modules/tools/shell_parser.py +227 -0
- package/dist/gaia-security/hooks/modules/tools/task_validator.py +283 -0
- package/dist/gaia-security/hooks/modules/validation/__init__.py +23 -0
- package/dist/gaia-security/hooks/modules/validation/commit_validator.py +380 -0
- package/dist/gaia-security/hooks/post_tool_use.py +54 -0
- package/dist/gaia-security/hooks/pre_tool_use.py +383 -0
- package/dist/gaia-security/hooks/session_start.py +69 -0
- package/dist/gaia-security/hooks/stop_hook.py +69 -0
- package/dist/gaia-security/hooks/user_prompt_submit.py +177 -0
- package/dist/gaia-security/settings.json +58 -0
- package/git-hooks/commit-msg +41 -0
- package/hooks/README.md +8 -6
- package/hooks/adapters/channel.py +0 -25
- package/hooks/adapters/claude_code.py +364 -125
- package/hooks/elicitation_result.py +132 -0
- package/hooks/hooks.json +10 -1
- package/hooks/modules/README.md +3 -2
- package/hooks/modules/agents/contract_validator.py +3 -51
- package/hooks/modules/agents/response_contract.py +4 -8
- package/hooks/modules/agents/transcript_reader.py +4 -5
- package/hooks/modules/audit/__init__.py +4 -6
- package/hooks/modules/audit/event_detector.py +0 -2
- package/hooks/modules/audit/metrics.py +108 -187
- package/hooks/modules/audit/workflow_auditor.py +0 -4
- package/hooks/modules/audit/workflow_recorder.py +0 -5
- package/hooks/modules/context/compact_context_builder.py +1 -0
- package/hooks/modules/context/context_cache.py +129 -0
- package/hooks/modules/context/context_injector.py +18 -40
- package/hooks/modules/context/context_writer.py +1 -25
- package/hooks/modules/context/contracts_loader.py +7 -10
- package/hooks/modules/core/hook_entry.py +1 -0
- package/hooks/modules/core/paths.py +12 -13
- package/hooks/modules/core/plugin_mode.py +74 -4
- package/hooks/modules/core/plugin_setup.py +395 -23
- package/hooks/modules/events/__init__.py +1 -0
- package/hooks/modules/events/event_writer.py +210 -0
- package/hooks/modules/identity/ops_identity.py +18 -27
- package/hooks/modules/memory/episode_writer.py +1 -6
- package/hooks/modules/orchestrator/__init__.py +1 -0
- package/hooks/modules/orchestrator/delegate_mode.py +128 -0
- package/hooks/modules/security/__init__.py +2 -4
- package/hooks/modules/security/approval_constants.py +5 -1
- package/hooks/modules/security/approval_grants.py +189 -6
- package/hooks/modules/security/approval_messages.py +9 -21
- package/hooks/modules/security/blocked_commands.py +98 -34
- package/hooks/modules/security/command_semantics.py +0 -4
- package/hooks/modules/security/gitops_validator.py +1 -11
- package/hooks/modules/security/mutative_verbs.py +179 -38
- package/hooks/modules/security/tiers.py +1 -19
- package/hooks/modules/session/session_event_injector.py +1 -25
- package/hooks/modules/tools/bash_validator.py +310 -94
- package/hooks/modules/tools/shell_parser.py +0 -1
- package/hooks/modules/tools/task_validator.py +9 -29
- package/hooks/post_tool_use.py +0 -72
- package/hooks/pre_tool_use.py +42 -102
- package/hooks/session_start.py +4 -2
- package/hooks/subagent_start.py +6 -2
- package/hooks/subagent_stop.py +1 -13
- package/hooks/user_prompt_submit.py +119 -37
- package/index.js +1 -1
- package/package.json +5 -3
- package/skills/README.md +3 -5
- package/skills/agent-protocol/SKILL.md +17 -16
- package/skills/agent-protocol/examples.md +6 -6
- package/skills/agent-response/SKILL.md +11 -14
- package/skills/approval/SKILL.md +28 -13
- package/skills/approval/reference.md +2 -2
- package/skills/execution/SKILL.md +1 -1
- package/skills/gaia-patterns/SKILL.md +2 -3
- package/skills/orchestrator-approval/SKILL.md +22 -50
- package/skills/security-tiers/SKILL.md +1 -1
- package/templates/README.md +9 -9
- package/templates/managed-settings.template.json +43 -0
- package/tools/gaia_simulator/runner.py +34 -1
- package/tools/scan/orchestrator.py +13 -0
- package/tools/scan/scanners/base.py +8 -0
- package/tools/scan/scanners/git.py +78 -0
- package/tools/scan/scanners/infrastructure.py +65 -0
- package/tools/scan/scanners/stack.py +110 -0
- package/tools/scan/setup.py +120 -13
- package/tools/scan/workspace.py +85 -0
- package/config/context-contracts.aws.json +0 -42
- package/config/context-contracts.gcp.json +0 -39
- package/skills/project-dispatch/SKILL.md +0 -34
- package/templates/settings.template.json +0 -226
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
import yaml
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from collections import defaultdict
|
|
7
|
+
|
|
8
|
+
def find_skills(base_dirs):
|
|
9
|
+
"""Encuentra todas las skills en los directorios base."""
|
|
10
|
+
skills = {}
|
|
11
|
+
for base_dir in base_dirs:
|
|
12
|
+
path = Path(base_dir)
|
|
13
|
+
if not path.exists():
|
|
14
|
+
continue
|
|
15
|
+
for skill_file in path.rglob("SKILL.md"):
|
|
16
|
+
skill_name = skill_file.parent.name
|
|
17
|
+
skills[skill_name] = {
|
|
18
|
+
"path": str(skill_file),
|
|
19
|
+
"content": skill_file.read_text(encoding="utf-8", errors="ignore")
|
|
20
|
+
}
|
|
21
|
+
return skills
|
|
22
|
+
|
|
23
|
+
def validate_skill_format(skills):
|
|
24
|
+
"""Valida el formato de cada skill."""
|
|
25
|
+
validation_results = {}
|
|
26
|
+
for name, data in skills.items():
|
|
27
|
+
content = data["content"]
|
|
28
|
+
has_title = bool(re.search(r'^#\s+.+', content, re.MULTILINE))
|
|
29
|
+
validation_results[name] = {
|
|
30
|
+
"has_title": has_title,
|
|
31
|
+
"is_empty": len(content.strip()) == 0,
|
|
32
|
+
"path": data["path"]
|
|
33
|
+
}
|
|
34
|
+
return validation_results
|
|
35
|
+
|
|
36
|
+
def find_agents(base_dirs):
|
|
37
|
+
"""Encuentra las definiciones de los agentes."""
|
|
38
|
+
agents = {}
|
|
39
|
+
for base_dir in base_dirs:
|
|
40
|
+
path = Path(base_dir)
|
|
41
|
+
if not path.exists():
|
|
42
|
+
continue
|
|
43
|
+
for agent_file in path.rglob("*.md"):
|
|
44
|
+
if agent_file.name == "README.md":
|
|
45
|
+
continue
|
|
46
|
+
content = agent_file.read_text(encoding="utf-8", errors="ignore")
|
|
47
|
+
# Extraer frontmatter YAML
|
|
48
|
+
match = re.match(r'^---\s*\n(.*?)\n---\s*\n(.*)', content, re.DOTALL)
|
|
49
|
+
if match:
|
|
50
|
+
try:
|
|
51
|
+
frontmatter = yaml.safe_load(match.group(1))
|
|
52
|
+
if isinstance(frontmatter, dict) and "name" in frontmatter:
|
|
53
|
+
agents[frontmatter["name"]] = {
|
|
54
|
+
"path": str(agent_file),
|
|
55
|
+
"skills_declared": frontmatter.get("skills", []),
|
|
56
|
+
"body": match.group(2)
|
|
57
|
+
}
|
|
58
|
+
except yaml.YAMLError:
|
|
59
|
+
pass
|
|
60
|
+
return agents
|
|
61
|
+
|
|
62
|
+
def analyze_injection():
|
|
63
|
+
"""Analiza cómo se inyectan las skills (revisando pre_tool_use.py)."""
|
|
64
|
+
hook_path = Path("gaia-ops/hooks/pre_tool_use.py")
|
|
65
|
+
if not hook_path.exists():
|
|
66
|
+
return "No se encontró gaia-ops/hooks/pre_tool_use.py"
|
|
67
|
+
|
|
68
|
+
content = hook_path.read_text(encoding="utf-8", errors="ignore")
|
|
69
|
+
if "skills are injected natively by Claude Code" in content:
|
|
70
|
+
return "Las skills se inyectan de forma nativa por Claude Code a través del campo 'skills:' en el frontmatter del agente (según pre_tool_use.py)."
|
|
71
|
+
return "Mecanismo de inyección en pre_tool_use.py analizado, pero no se encontró la nota estándar sobre inyección nativa."
|
|
72
|
+
|
|
73
|
+
def generate_report(skills, validation, agents, injection_info):
|
|
74
|
+
"""Genera el reporte en formato Markdown."""
|
|
75
|
+
report = ["# Reporte de Validación de Skills\n"]
|
|
76
|
+
|
|
77
|
+
report.append("## 1. Análisis de Inyección")
|
|
78
|
+
report.append(f"{injection_info}\n")
|
|
79
|
+
|
|
80
|
+
report.append(f"## 2. Skills Encontradas ({len(skills)})")
|
|
81
|
+
for name, val in validation.items():
|
|
82
|
+
status = "✅ OK" if val["has_title"] and not val["is_empty"] else "❌ PROBLEMA"
|
|
83
|
+
issues = []
|
|
84
|
+
if not val["has_title"]: issues.append("Falta título (# Título)")
|
|
85
|
+
if val["is_empty"]: issues.append("Archivo vacío")
|
|
86
|
+
issue_str = f" - Detalles: {', '.join(issues)}" if issues else ""
|
|
87
|
+
report.append(f"- **{name}** ({val['path']}): {status}{issue_str}")
|
|
88
|
+
report.append("")
|
|
89
|
+
|
|
90
|
+
# Analizar uso de skills
|
|
91
|
+
used_skills = defaultdict(list)
|
|
92
|
+
missing_skills = defaultdict(list)
|
|
93
|
+
body_mentions = defaultdict(list)
|
|
94
|
+
|
|
95
|
+
for agent_name, agent_data in agents.items():
|
|
96
|
+
declared = agent_data["skills_declared"] or []
|
|
97
|
+
body = agent_data["body"]
|
|
98
|
+
for skill in declared:
|
|
99
|
+
if skill in skills:
|
|
100
|
+
used_skills[skill].append(agent_name)
|
|
101
|
+
else:
|
|
102
|
+
missing_skills[agent_name].append(skill)
|
|
103
|
+
|
|
104
|
+
# Check for skills mentioned in the body but not declared
|
|
105
|
+
for skill in skills:
|
|
106
|
+
if skill not in declared and skill in body:
|
|
107
|
+
body_mentions[agent_name].append(skill)
|
|
108
|
+
|
|
109
|
+
report.append("## 3. Uso de Skills por Agentes")
|
|
110
|
+
if not agents:
|
|
111
|
+
report.append("No se encontraron definiciones de agentes con frontmatter YAML válido.\n")
|
|
112
|
+
else:
|
|
113
|
+
for agent_name, agent_data in agents.items():
|
|
114
|
+
declared = agent_data["skills_declared"] or []
|
|
115
|
+
mentions = body_mentions[agent_name]
|
|
116
|
+
mention_str = f" (Menciona en texto sin declarar: {', '.join(mentions)})" if mentions else ""
|
|
117
|
+
report.append(f"- **{agent_name}**: {len(declared)} skills declaradas.{mention_str}")
|
|
118
|
+
report.append("")
|
|
119
|
+
|
|
120
|
+
report.append("## 4. Gaps Identificados")
|
|
121
|
+
|
|
122
|
+
# Skills no utilizadas
|
|
123
|
+
# Consideramos una skill como utilizada si está declarada o si se menciona explícitamente en el cuerpo
|
|
124
|
+
all_used_skills = set(used_skills.keys())
|
|
125
|
+
for mentions in body_mentions.values():
|
|
126
|
+
all_used_skills.update(mentions)
|
|
127
|
+
|
|
128
|
+
unused_skills = set(skills.keys()) - all_used_skills
|
|
129
|
+
if unused_skills:
|
|
130
|
+
report.append("### Skills no utilizadas (Huérfanas)")
|
|
131
|
+
for skill in sorted(unused_skills):
|
|
132
|
+
report.append(f"- {skill}")
|
|
133
|
+
else:
|
|
134
|
+
report.append("### Skills no utilizadas (Huérfanas)")
|
|
135
|
+
report.append("- Ninguna. Todas las skills encontradas están asignadas a al menos un agente.")
|
|
136
|
+
report.append("")
|
|
137
|
+
|
|
138
|
+
# Skills declaradas pero inexistentes
|
|
139
|
+
if missing_skills:
|
|
140
|
+
report.append("### Skills declaradas pero no encontradas (Faltantes)")
|
|
141
|
+
for agent, missing in missing_skills.items():
|
|
142
|
+
for m in missing:
|
|
143
|
+
report.append(f"- El agente **{agent}** declara la skill '{m}', pero no se encontró el archivo SKILL.md correspondiente.")
|
|
144
|
+
else:
|
|
145
|
+
report.append("### Skills declaradas pero no encontradas (Faltantes)")
|
|
146
|
+
report.append("- Ninguna. Todas las skills declaradas por los agentes existen.")
|
|
147
|
+
report.append("")
|
|
148
|
+
|
|
149
|
+
# Skills mencionadas en el texto pero no inyectadas formalmente
|
|
150
|
+
report.append("### Skills mencionadas en el texto pero NO declaradas en 'skills:'")
|
|
151
|
+
if body_mentions:
|
|
152
|
+
for agent, mentions in body_mentions.items():
|
|
153
|
+
for m in mentions:
|
|
154
|
+
report.append(f"- **{agent}** menciona '{m}' en su cuerpo pero no está en la lista de inyección.")
|
|
155
|
+
else:
|
|
156
|
+
report.append("- Ninguna.")
|
|
157
|
+
report.append("")
|
|
158
|
+
|
|
159
|
+
return "\n".join(report)
|
|
160
|
+
|
|
161
|
+
def main():
|
|
162
|
+
skill_dirs = ["gaia-ops/skills", ".claude/skills", "conductor-orchestrator/skills"]
|
|
163
|
+
agent_dirs = ["gaia-ops/agents", ".claude/agents", "conductor-orchestrator/agents"]
|
|
164
|
+
|
|
165
|
+
print("Buscando skills...")
|
|
166
|
+
skills = find_skills(skill_dirs)
|
|
167
|
+
|
|
168
|
+
print("Validando formato...")
|
|
169
|
+
validation = validate_skill_format(skills)
|
|
170
|
+
|
|
171
|
+
print("Buscando agentes...")
|
|
172
|
+
agents = find_agents(agent_dirs)
|
|
173
|
+
|
|
174
|
+
print("Analizando inyección...")
|
|
175
|
+
injection_info = analyze_injection()
|
|
176
|
+
|
|
177
|
+
print("Generando reporte...")
|
|
178
|
+
report = generate_report(skills, validation, agents, injection_info)
|
|
179
|
+
|
|
180
|
+
report_path = Path("gaia-ops/tools/validation/skills_report.md")
|
|
181
|
+
report_path.write_text(report, encoding="utf-8")
|
|
182
|
+
print(f"Reporte generado en {report_path}")
|
|
183
|
+
|
|
184
|
+
# Imprimir el reporte en la salida estándar para que el agente lo pueda devolver
|
|
185
|
+
print("\n" + "="*50 + "\n")
|
|
186
|
+
print(report)
|
|
187
|
+
|
|
188
|
+
if __name__ == "__main__":
|
|
189
|
+
main()
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "gaia-security",
|
|
3
|
+
"version": "4.7.2",
|
|
4
|
+
"description": "Keeps you in the loop only when it matters. Gaia Security analyzes every command and classifies it into risk tiers: read-only queries run freely, simulations and validations pass through, and state-changing operations (create, delete, apply, push) pause for your explicit approval before executing. Irreversible commands like dropping databases or deleting cloud infrastructure are permanently blocked.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "jaguilar87"
|
|
7
|
+
},
|
|
8
|
+
"repository": "https://github.com/metraton/gaia-ops",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"security",
|
|
12
|
+
"devops"
|
|
13
|
+
],
|
|
14
|
+
"engines": {
|
|
15
|
+
"claude-code": ">=2.1.0"
|
|
16
|
+
},
|
|
17
|
+
"categories": [
|
|
18
|
+
"devops",
|
|
19
|
+
"security",
|
|
20
|
+
"orchestration"
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Adapter Layer for Gaia-Ops Hooks.
|
|
3
|
+
|
|
4
|
+
Provides CLI-agnostic normalized types and the abstract HookAdapter interface.
|
|
5
|
+
Business logic modules consume and produce these types; concrete adapters
|
|
6
|
+
translate between these types and CLI-specific JSON protocols.
|
|
7
|
+
|
|
8
|
+
Modules:
|
|
9
|
+
- types: Frozen dataclasses and enums for all hook event/response data
|
|
10
|
+
- base: Abstract HookAdapter interface
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from .types import (
|
|
14
|
+
HookEventType,
|
|
15
|
+
PermissionDecision,
|
|
16
|
+
DistributionChannel,
|
|
17
|
+
HookEvent,
|
|
18
|
+
ValidationRequest,
|
|
19
|
+
ValidationResult,
|
|
20
|
+
ToolResult,
|
|
21
|
+
AgentCompletion,
|
|
22
|
+
CompletionResult,
|
|
23
|
+
ContextResult,
|
|
24
|
+
BootstrapResult,
|
|
25
|
+
QualityResult,
|
|
26
|
+
VerificationResult,
|
|
27
|
+
HookResponse,
|
|
28
|
+
)
|
|
29
|
+
from .base import HookAdapter
|
|
30
|
+
from .claude_code import ClaudeCodeAdapter
|
|
31
|
+
from .utils import has_stdin_data, warn_if_dual_channel
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
"HookEventType",
|
|
35
|
+
"PermissionDecision",
|
|
36
|
+
"DistributionChannel",
|
|
37
|
+
"HookEvent",
|
|
38
|
+
"ValidationRequest",
|
|
39
|
+
"ValidationResult",
|
|
40
|
+
"ToolResult",
|
|
41
|
+
"AgentCompletion",
|
|
42
|
+
"CompletionResult",
|
|
43
|
+
"ContextResult",
|
|
44
|
+
"BootstrapResult",
|
|
45
|
+
"QualityResult",
|
|
46
|
+
"VerificationResult",
|
|
47
|
+
"HookResponse",
|
|
48
|
+
"HookAdapter",
|
|
49
|
+
"ClaudeCodeAdapter",
|
|
50
|
+
"has_stdin_data",
|
|
51
|
+
"warn_if_dual_channel",
|
|
52
|
+
]
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Abstract base class defining the adapter contract.
|
|
3
|
+
|
|
4
|
+
Each CLI backend (Claude Code, future CLIs) provides a concrete implementation
|
|
5
|
+
of HookAdapter. Business logic modules interact only with the normalized types;
|
|
6
|
+
they never see raw CLI JSON.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from abc import ABC, abstractmethod
|
|
12
|
+
|
|
13
|
+
from .types import (
|
|
14
|
+
AgentCompletion,
|
|
15
|
+
BootstrapResult,
|
|
16
|
+
CompletionResult,
|
|
17
|
+
ContextResult,
|
|
18
|
+
DistributionChannel,
|
|
19
|
+
HookEvent,
|
|
20
|
+
HookResponse,
|
|
21
|
+
QualityResult,
|
|
22
|
+
ValidationResult,
|
|
23
|
+
VerificationResult,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class HookAdapter(ABC):
|
|
28
|
+
"""Abstract adapter between CLI-specific JSON and normalized types.
|
|
29
|
+
|
|
30
|
+
Invariants (from adapter-interface contract):
|
|
31
|
+
1. Business logic modules NEVER see HookResponse.
|
|
32
|
+
2. The adapter NEVER modifies business logic results -- only translates format.
|
|
33
|
+
3. Adding a new hook event requires ONLY a new adapter method.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
def parse_event(self, stdin_data: str) -> HookEvent:
|
|
38
|
+
"""Parse raw stdin JSON into a normalized HookEvent.
|
|
39
|
+
|
|
40
|
+
Preconditions:
|
|
41
|
+
- stdin_data is a valid JSON string
|
|
42
|
+
- JSON contains at minimum: hook_event_name, session_id
|
|
43
|
+
|
|
44
|
+
Postconditions:
|
|
45
|
+
- Returns HookEvent with event_type set to a valid HookEventType
|
|
46
|
+
- Returns HookEvent with session_id populated
|
|
47
|
+
- payload contains the full raw event data
|
|
48
|
+
|
|
49
|
+
Raises:
|
|
50
|
+
ValueError: If JSON is invalid or event type is unknown.
|
|
51
|
+
"""
|
|
52
|
+
...
|
|
53
|
+
|
|
54
|
+
@abstractmethod
|
|
55
|
+
def format_validation_response(self, result: ValidationResult) -> HookResponse:
|
|
56
|
+
"""Format a ValidationResult for CLI consumption.
|
|
57
|
+
|
|
58
|
+
Preconditions:
|
|
59
|
+
- result.allowed is a valid boolean
|
|
60
|
+
- result.reason is a non-empty string
|
|
61
|
+
|
|
62
|
+
Postconditions:
|
|
63
|
+
- HookResponse.output is a valid JSON-serializable dict
|
|
64
|
+
- HookResponse.exit_code is 0 (corrective deny) or 2 (permanent block)
|
|
65
|
+
- If result.allowed is True, output contains permissionDecision: allow
|
|
66
|
+
- If result.allowed is False, output contains permissionDecision: deny
|
|
67
|
+
- If result.modified_input is set, output contains updatedInput
|
|
68
|
+
"""
|
|
69
|
+
...
|
|
70
|
+
|
|
71
|
+
@abstractmethod
|
|
72
|
+
def format_completion_response(self, result: CompletionResult) -> HookResponse:
|
|
73
|
+
"""Format a CompletionResult for CLI consumption.
|
|
74
|
+
|
|
75
|
+
Postconditions:
|
|
76
|
+
- HookResponse.output contains contract_valid, anomalies_detected
|
|
77
|
+
- HookResponse.exit_code is always 0
|
|
78
|
+
"""
|
|
79
|
+
...
|
|
80
|
+
|
|
81
|
+
@abstractmethod
|
|
82
|
+
def format_context_response(self, result: ContextResult) -> HookResponse:
|
|
83
|
+
"""Format a ContextResult for CLI consumption."""
|
|
84
|
+
...
|
|
85
|
+
|
|
86
|
+
@abstractmethod
|
|
87
|
+
def format_bootstrap_response(self, result: BootstrapResult) -> HookResponse:
|
|
88
|
+
"""Format a BootstrapResult for CLI consumption.
|
|
89
|
+
|
|
90
|
+
Returns session bootstrap status for SessionStart events.
|
|
91
|
+
"""
|
|
92
|
+
...
|
|
93
|
+
|
|
94
|
+
@abstractmethod
|
|
95
|
+
def adapt_session_start(self, raw: dict) -> BootstrapResult:
|
|
96
|
+
"""Parse SessionStart event and return bootstrap actions.
|
|
97
|
+
|
|
98
|
+
Preconditions:
|
|
99
|
+
- raw is the HookEvent.payload dict for a SessionStart event
|
|
100
|
+
|
|
101
|
+
Postconditions:
|
|
102
|
+
- Returns BootstrapResult with should_scan and should_refresh set
|
|
103
|
+
based on session_type
|
|
104
|
+
"""
|
|
105
|
+
...
|
|
106
|
+
|
|
107
|
+
# ------------------------------------------------------------------ #
|
|
108
|
+
# P2 event adapters
|
|
109
|
+
# ------------------------------------------------------------------ #
|
|
110
|
+
|
|
111
|
+
@abstractmethod
|
|
112
|
+
def adapt_stop(self, raw: dict) -> QualityResult:
|
|
113
|
+
"""Parse Stop event and assess response quality.
|
|
114
|
+
|
|
115
|
+
Preconditions:
|
|
116
|
+
- raw is the HookEvent.payload dict for a Stop event
|
|
117
|
+
|
|
118
|
+
Postconditions:
|
|
119
|
+
- Returns QualityResult with quality assessment
|
|
120
|
+
"""
|
|
121
|
+
...
|
|
122
|
+
|
|
123
|
+
@abstractmethod
|
|
124
|
+
def adapt_task_completed(self, raw: dict) -> VerificationResult:
|
|
125
|
+
"""Parse TaskCompleted event and verify completion criteria.
|
|
126
|
+
|
|
127
|
+
Preconditions:
|
|
128
|
+
- raw is the HookEvent.payload dict for a TaskCompleted event
|
|
129
|
+
|
|
130
|
+
Postconditions:
|
|
131
|
+
- Returns VerificationResult with criteria assessment
|
|
132
|
+
"""
|
|
133
|
+
...
|
|
134
|
+
|
|
135
|
+
@abstractmethod
|
|
136
|
+
def adapt_subagent_start(self, raw: dict) -> ContextResult:
|
|
137
|
+
"""Parse SubagentStart event and prepare agent context.
|
|
138
|
+
|
|
139
|
+
Preconditions:
|
|
140
|
+
- raw is the HookEvent.payload dict for a SubagentStart event
|
|
141
|
+
|
|
142
|
+
Postconditions:
|
|
143
|
+
- Returns ContextResult with agent-specific context
|
|
144
|
+
"""
|
|
145
|
+
...
|
|
146
|
+
|
|
147
|
+
# ------------------------------------------------------------------ #
|
|
148
|
+
# P2 formatters
|
|
149
|
+
# ------------------------------------------------------------------ #
|
|
150
|
+
|
|
151
|
+
@abstractmethod
|
|
152
|
+
def format_quality_response(self, result: QualityResult) -> HookResponse:
|
|
153
|
+
"""Format a QualityResult for CLI consumption."""
|
|
154
|
+
...
|
|
155
|
+
|
|
156
|
+
@abstractmethod
|
|
157
|
+
def format_verification_response(self, result: VerificationResult) -> HookResponse:
|
|
158
|
+
"""Format a VerificationResult for CLI consumption."""
|
|
159
|
+
...
|
|
160
|
+
|
|
161
|
+
@abstractmethod
|
|
162
|
+
def detect_channel(self) -> DistributionChannel:
|
|
163
|
+
"""Detect the distribution channel (NPM or PLUGIN).
|
|
164
|
+
|
|
165
|
+
Checks environment variables and filesystem layout to determine
|
|
166
|
+
how gaia-ops was installed.
|
|
167
|
+
"""
|
|
168
|
+
...
|
|
169
|
+
|
|
170
|
+
# ------------------------------------------------------------------ #
|
|
171
|
+
# Full hook lifecycle adapters (thin-gate pattern)
|
|
172
|
+
# ------------------------------------------------------------------ #
|
|
173
|
+
|
|
174
|
+
@abstractmethod
|
|
175
|
+
def adapt_pre_tool_use(self, event: HookEvent) -> HookResponse:
|
|
176
|
+
"""Run all pre-tool-use business logic and return a formatted response.
|
|
177
|
+
|
|
178
|
+
Orchestrates: routing (bash vs task), validation, state management,
|
|
179
|
+
context injection, approval handling, and response formatting.
|
|
180
|
+
|
|
181
|
+
Preconditions:
|
|
182
|
+
- event is a parsed HookEvent with event_type PRE_TOOL_USE
|
|
183
|
+
|
|
184
|
+
Postconditions:
|
|
185
|
+
- Returns HookResponse ready for stdout + sys.exit()
|
|
186
|
+
"""
|
|
187
|
+
...
|
|
188
|
+
|
|
189
|
+
@abstractmethod
|
|
190
|
+
def adapt_post_tool_use(self, event: HookEvent) -> HookResponse:
|
|
191
|
+
"""Run all post-tool-use business logic and return a formatted response.
|
|
192
|
+
|
|
193
|
+
Orchestrates: state retrieval, duration computation, audit logging,
|
|
194
|
+
T3 grant confirmation, critical event detection, session context
|
|
195
|
+
writing, and state cleanup.
|
|
196
|
+
|
|
197
|
+
Preconditions:
|
|
198
|
+
- event is a parsed HookEvent with event_type POST_TOOL_USE
|
|
199
|
+
|
|
200
|
+
Postconditions:
|
|
201
|
+
- Returns HookResponse (always exit 0, post-hook never blocks)
|
|
202
|
+
"""
|
|
203
|
+
...
|
|
204
|
+
|
|
205
|
+
@abstractmethod
|
|
206
|
+
def adapt_subagent_stop(self, event: HookEvent) -> HookResponse:
|
|
207
|
+
"""Run all subagent-stop business logic and return a formatted response.
|
|
208
|
+
|
|
209
|
+
Orchestrates: contract parsing and validation, approval cleanup,
|
|
210
|
+
context updates, workflow recording, response contract validation,
|
|
211
|
+
anomaly detection, episodic memory, and result assembly.
|
|
212
|
+
|
|
213
|
+
Preconditions:
|
|
214
|
+
- event is a parsed HookEvent with event_type SUBAGENT_STOP
|
|
215
|
+
|
|
216
|
+
Postconditions:
|
|
217
|
+
- Returns HookResponse (exit 0 for success, exit 2 for contract rejection)
|
|
218
|
+
"""
|
|
219
|
+
...
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Distribution Channel Detection for Gaia-Ops.
|
|
3
|
+
|
|
4
|
+
Utility module that detects whether gaia-ops is running as a Claude Code plugin
|
|
5
|
+
or via npm. Used by entry points to log coexistence warnings and by business
|
|
6
|
+
logic to adapt behavior per channel.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def is_dual_channel_active() -> bool:
|
|
14
|
+
"""Check if both plugin and npm installations exist simultaneously."""
|
|
15
|
+
has_plugin = bool(os.environ.get("CLAUDE_PLUGIN_ROOT"))
|
|
16
|
+
has_npm = Path(".claude/hooks").is_symlink()
|
|
17
|
+
return has_plugin and has_npm
|