@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,86 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Actionable BLOCKED message formatter for permanently blocked commands.
|
|
3
|
+
|
|
4
|
+
When the hook blocks a T3 command (exit 2), this module produces messages that:
|
|
5
|
+
- Line 1: What domain was blocked and why (irreversible)
|
|
6
|
+
- Line 2: The specific suggestion (from BLOCKED_COMMAND_SUGGESTIONS if available)
|
|
7
|
+
- Line 3: Which agent to dispatch to (mapped from command category)
|
|
8
|
+
|
|
9
|
+
This replaces the generic "[BLOCKED] Command blocked by security policy" message
|
|
10
|
+
with actionable guidance for the orchestrator.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import logging
|
|
14
|
+
from typing import Optional
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Category-to-agent mapping: which specialist handles remediation
|
|
20
|
+
CATEGORY_AGENT_MAP = {
|
|
21
|
+
"aws_critical": "terraform-architect",
|
|
22
|
+
"gcp_critical": "terraform-architect",
|
|
23
|
+
"terraform_destroy": "terraform-architect",
|
|
24
|
+
"kubernetes_critical": "gitops-operator",
|
|
25
|
+
"flux_critical": "gitops-operator",
|
|
26
|
+
"git_destructive": "devops-developer",
|
|
27
|
+
"docker_critical": "devops-developer",
|
|
28
|
+
"npm_critical": "devops-developer",
|
|
29
|
+
"sql_critical": "devops-developer",
|
|
30
|
+
"disk_operations": "devops-developer",
|
|
31
|
+
"rm_critical": "devops-developer",
|
|
32
|
+
"repo_delete": "devops-developer",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def format_blocked_message(result) -> str:
|
|
37
|
+
"""Format a blocked command result into an actionable message.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
result: A BashValidationResult with tier, reason, and suggestions fields.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
A multi-line message with domain, suggestion, and agent routing.
|
|
44
|
+
"""
|
|
45
|
+
# Extract category from reason (format: "Command blocked by security policy: <category>")
|
|
46
|
+
category = _extract_category(result.reason)
|
|
47
|
+
|
|
48
|
+
# Line 1: What was blocked and why
|
|
49
|
+
msg = f"[BLOCKED] {result.reason} (irreversible operation)\n"
|
|
50
|
+
|
|
51
|
+
# Line 2: Specific suggestion
|
|
52
|
+
suggestion = _get_suggestion(result, category)
|
|
53
|
+
if suggestion:
|
|
54
|
+
msg += f"Suggestion: {suggestion}\n"
|
|
55
|
+
|
|
56
|
+
# Line 3: Agent to dispatch to
|
|
57
|
+
agent = CATEGORY_AGENT_MAP.get(category)
|
|
58
|
+
if agent:
|
|
59
|
+
msg += f"Dispatch to: {agent}\n"
|
|
60
|
+
|
|
61
|
+
return msg
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _extract_category(reason: str) -> Optional[str]:
|
|
65
|
+
"""Extract category name from the reason string.
|
|
66
|
+
|
|
67
|
+
The reason format from bash_validator is:
|
|
68
|
+
"Command blocked by security policy: <category>"
|
|
69
|
+
"""
|
|
70
|
+
prefix = "Command blocked by security policy: "
|
|
71
|
+
if prefix in reason:
|
|
72
|
+
return reason.split(prefix, 1)[1].strip()
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _get_suggestion(result, category: Optional[str]) -> Optional[str]:
|
|
77
|
+
"""Get the best suggestion for the blocked command.
|
|
78
|
+
|
|
79
|
+
Prefers the suggestion from the result (sourced from BLOCKED_COMMAND_SUGGESTIONS),
|
|
80
|
+
falls back to the reason text.
|
|
81
|
+
"""
|
|
82
|
+
if result.suggestions:
|
|
83
|
+
return result.suggestions[0]
|
|
84
|
+
if category:
|
|
85
|
+
return f"Category '{category}' commands are permanently blocked"
|
|
86
|
+
return result.reason
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Semantic command analysis helpers for security decisions.
|
|
3
|
+
|
|
4
|
+
This module builds an analysis-friendly representation of a shell command
|
|
5
|
+
without mutating the original command string that will be executed.
|
|
6
|
+
|
|
7
|
+
Key properties:
|
|
8
|
+
- Idempotent: analyzing a normalized command produces the same semantic view.
|
|
9
|
+
- CLI-agnostic: relies on token structure, not a large per-CLI global-flag table.
|
|
10
|
+
- Non-destructive: the real command is never rewritten for execution.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import functools
|
|
14
|
+
import shlex
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
from typing import Iterable, Tuple
|
|
17
|
+
|
|
18
|
+
# Scan enough semantic tokens to cover CLIs with multiple resource segments and
|
|
19
|
+
# several global flag/value pairs before the real verb.
|
|
20
|
+
SEMANTIC_SCAN_LIMIT = 12
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True)
|
|
24
|
+
class CommandSemantics:
|
|
25
|
+
"""Semantic view of a shell command for policy analysis."""
|
|
26
|
+
|
|
27
|
+
raw_command: str = ""
|
|
28
|
+
tokens: Tuple[str, ...] = ()
|
|
29
|
+
base_cmd: str = ""
|
|
30
|
+
args: Tuple[str, ...] = ()
|
|
31
|
+
flag_tokens: Tuple[str, ...] = ()
|
|
32
|
+
non_flag_tokens: Tuple[str, ...] = ()
|
|
33
|
+
semantic_tokens: Tuple[str, ...] = ()
|
|
34
|
+
semantic_head_tokens: Tuple[str, ...] = ()
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def normalized_command(self) -> str:
|
|
38
|
+
"""Return the canonical analysis form of the command."""
|
|
39
|
+
return " ".join(self.semantic_tokens)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def tokenize_command(command: str) -> Tuple[str, ...]:
|
|
43
|
+
"""Tokenize a shell command safely, preserving quoted substrings."""
|
|
44
|
+
if not command or not command.strip():
|
|
45
|
+
return ()
|
|
46
|
+
try:
|
|
47
|
+
return tuple(shlex.split(command.strip()))
|
|
48
|
+
except ValueError:
|
|
49
|
+
# Fall back to a simple split for malformed quoting. This keeps the
|
|
50
|
+
# security layer best-effort instead of crashing on parse errors.
|
|
51
|
+
return tuple(command.strip().split())
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@functools.lru_cache(maxsize=128)
|
|
55
|
+
def analyze_command(command: str, semantic_scan_limit: int = SEMANTIC_SCAN_LIMIT) -> CommandSemantics:
|
|
56
|
+
"""Build an idempotent semantic representation for security analysis."""
|
|
57
|
+
raw_command = command.strip() if command else ""
|
|
58
|
+
tokens = tokenize_command(raw_command)
|
|
59
|
+
if not tokens:
|
|
60
|
+
return CommandSemantics(raw_command=raw_command)
|
|
61
|
+
|
|
62
|
+
base_cmd = _pathless(tokens[0]).lower()
|
|
63
|
+
args = tuple(tokens[1:])
|
|
64
|
+
|
|
65
|
+
flag_tokens = []
|
|
66
|
+
non_flag_tokens = []
|
|
67
|
+
for token in args:
|
|
68
|
+
if _is_flag(token):
|
|
69
|
+
flag_tokens.extend(_normalize_flag_token(token))
|
|
70
|
+
continue
|
|
71
|
+
non_flag_tokens.append(token.lower())
|
|
72
|
+
|
|
73
|
+
semantic_tokens = (base_cmd, *non_flag_tokens)
|
|
74
|
+
head_size = max(1, semantic_scan_limit + 1)
|
|
75
|
+
|
|
76
|
+
return CommandSemantics(
|
|
77
|
+
raw_command=raw_command,
|
|
78
|
+
tokens=tokens,
|
|
79
|
+
base_cmd=base_cmd,
|
|
80
|
+
args=args,
|
|
81
|
+
flag_tokens=tuple(flag_tokens),
|
|
82
|
+
non_flag_tokens=tuple(non_flag_tokens),
|
|
83
|
+
semantic_tokens=tuple(semantic_tokens),
|
|
84
|
+
semantic_head_tokens=tuple(semantic_tokens[:head_size]),
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _contains_ordered_sequence(tokens: Iterable[str], sequence: Iterable[str]) -> bool:
|
|
89
|
+
"""Return True when all sequence tokens appear in order, allowing gaps.
|
|
90
|
+
|
|
91
|
+
Internal helper -- callers must supply pre-lowercased inputs.
|
|
92
|
+
Both ``tokens`` (semantic_head_tokens) and ``sequence``
|
|
93
|
+
(SemanticBlockedRule.sequence) are already lowercase when produced by
|
|
94
|
+
:func:`analyze_command` and :class:`SemanticBlockedRule`.
|
|
95
|
+
"""
|
|
96
|
+
needles = tuple(sequence)
|
|
97
|
+
if not needles:
|
|
98
|
+
return False
|
|
99
|
+
|
|
100
|
+
index = 0
|
|
101
|
+
for token in tokens:
|
|
102
|
+
if token == needles[index]:
|
|
103
|
+
index += 1
|
|
104
|
+
if index == len(needles):
|
|
105
|
+
return True
|
|
106
|
+
return False
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _pathless(token: str) -> str:
|
|
110
|
+
"""Strip a leading path prefix from an executable token."""
|
|
111
|
+
return token.rsplit("/", 1)[-1] if "/" in token else token
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _is_flag(token: str) -> bool:
|
|
115
|
+
"""Check whether a token is flag-shaped."""
|
|
116
|
+
return token.startswith("-") and token != "-"
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _normalize_flag_token(token: str) -> Tuple[str, ...]:
|
|
120
|
+
"""Normalize flag tokens for matching while preserving exact variants."""
|
|
121
|
+
token_lower = token.lower()
|
|
122
|
+
|
|
123
|
+
if token_lower.startswith("--"):
|
|
124
|
+
return (token_lower.split("=", 1)[0],)
|
|
125
|
+
|
|
126
|
+
normalized = [token_lower]
|
|
127
|
+
short_body = token_lower[1:]
|
|
128
|
+
if len(short_body) > 1 and short_body.isalpha():
|
|
129
|
+
normalized.extend(f"-{char}" for char in short_body)
|
|
130
|
+
return tuple(normalized)
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""
|
|
2
|
+
GitOps workflow validation for kubectl, helm, and flux commands.
|
|
3
|
+
|
|
4
|
+
Ensures commands follow GitOps principles:
|
|
5
|
+
- No direct cluster modifications
|
|
6
|
+
- Use --dry-run for apply operations
|
|
7
|
+
- Prefer read-only commands
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import re
|
|
11
|
+
import logging
|
|
12
|
+
from typing import List, Optional
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class GitOpsValidationResult:
|
|
20
|
+
"""Result of GitOps validation."""
|
|
21
|
+
allowed: bool
|
|
22
|
+
reason: str
|
|
23
|
+
severity: str = "info" # info, warning, high, critical
|
|
24
|
+
suggestions: List[str] = field(default_factory=list)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# Safe read-only commands (always allowed)
|
|
28
|
+
SAFE_KUBECTL_COMMANDS = [
|
|
29
|
+
r'kubectl\s+get',
|
|
30
|
+
r'kubectl\s+describe',
|
|
31
|
+
r'kubectl\s+logs',
|
|
32
|
+
r'kubectl\s+top',
|
|
33
|
+
r'kubectl\s+explain',
|
|
34
|
+
r'kubectl\s+version',
|
|
35
|
+
r'kubectl\s+cluster-info',
|
|
36
|
+
r'kubectl\s+config\s+view',
|
|
37
|
+
r'kubectl\s+api-resources',
|
|
38
|
+
r'kubectl\s+api-versions',
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
SAFE_FLUX_COMMANDS = [
|
|
42
|
+
r'flux\s+get',
|
|
43
|
+
r'flux\s+check',
|
|
44
|
+
r'flux\s+version',
|
|
45
|
+
r'flux\s+logs',
|
|
46
|
+
r'flux\s+stats',
|
|
47
|
+
r'flux\s+tree',
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
SAFE_HELM_COMMANDS = [
|
|
51
|
+
r'helm\s+list',
|
|
52
|
+
r'helm\s+status',
|
|
53
|
+
r'helm\s+history',
|
|
54
|
+
r'helm\s+template',
|
|
55
|
+
r'helm\s+lint',
|
|
56
|
+
r'helm\s+version',
|
|
57
|
+
r'helm\s+show',
|
|
58
|
+
r'helm\s+search',
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
# Forbidden commands (modify cluster state)
|
|
62
|
+
FORBIDDEN_KUBECTL_COMMANDS = [
|
|
63
|
+
r'kubectl\s+apply(?!\s+.*--dry-run)',
|
|
64
|
+
r'kubectl\s+create(?!\s+.*--dry-run)',
|
|
65
|
+
r'kubectl\s+patch',
|
|
66
|
+
r'kubectl\s+replace',
|
|
67
|
+
r'kubectl\s+delete',
|
|
68
|
+
r'kubectl\s+scale',
|
|
69
|
+
r'kubectl\s+rollout\s+restart',
|
|
70
|
+
r'kubectl\s+annotate(?!\s+.*--dry-run)',
|
|
71
|
+
r'kubectl\s+label(?!\s+.*--dry-run)',
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
FORBIDDEN_FLUX_COMMANDS = [
|
|
75
|
+
r'flux\s+create',
|
|
76
|
+
r'flux\s+delete',
|
|
77
|
+
r'flux\s+suspend',
|
|
78
|
+
r'flux\s+resume',
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
FORBIDDEN_HELM_COMMANDS = [
|
|
82
|
+
r'helm\s+install(?!\s+.*--dry-run)',
|
|
83
|
+
r'helm\s+upgrade(?!\s+.*--dry-run)',
|
|
84
|
+
r'helm\s+uninstall',
|
|
85
|
+
r'helm\s+rollback',
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def is_safe_gitops_command(command: str) -> bool:
|
|
90
|
+
"""Check if command is explicitly safe (read-only)."""
|
|
91
|
+
safe_patterns = SAFE_KUBECTL_COMMANDS + SAFE_FLUX_COMMANDS + SAFE_HELM_COMMANDS
|
|
92
|
+
for pattern in safe_patterns:
|
|
93
|
+
if re.search(pattern, command, re.IGNORECASE):
|
|
94
|
+
return True
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def is_forbidden_gitops_command(command: str) -> bool:
|
|
99
|
+
"""Check if command is forbidden (modifies cluster state)."""
|
|
100
|
+
forbidden_patterns = (
|
|
101
|
+
FORBIDDEN_KUBECTL_COMMANDS +
|
|
102
|
+
FORBIDDEN_FLUX_COMMANDS +
|
|
103
|
+
FORBIDDEN_HELM_COMMANDS
|
|
104
|
+
)
|
|
105
|
+
for pattern in forbidden_patterns:
|
|
106
|
+
if re.search(pattern, command, re.IGNORECASE):
|
|
107
|
+
return True
|
|
108
|
+
return False
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def validate_gitops_workflow(
|
|
112
|
+
command: str,
|
|
113
|
+
agent_type: Optional[str] = None
|
|
114
|
+
) -> GitOpsValidationResult:
|
|
115
|
+
"""
|
|
116
|
+
Validate command against GitOps security principles.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
command: Shell command to validate
|
|
120
|
+
agent_type: Optional agent type for stricter validation
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
GitOpsValidationResult with status and suggestions
|
|
124
|
+
"""
|
|
125
|
+
# Check if command is explicitly safe
|
|
126
|
+
if is_safe_gitops_command(command):
|
|
127
|
+
return GitOpsValidationResult(
|
|
128
|
+
allowed=True,
|
|
129
|
+
reason="Read-only operation - safe to execute",
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Check if command is forbidden
|
|
133
|
+
if is_forbidden_gitops_command(command):
|
|
134
|
+
suggestions = []
|
|
135
|
+
|
|
136
|
+
# Provide specific suggestions based on command type
|
|
137
|
+
if "kubectl apply" in command and "--dry-run" not in command:
|
|
138
|
+
suggestions.extend([
|
|
139
|
+
"Use: kubectl apply --dry-run=client -f <file>",
|
|
140
|
+
"Create manifests in gitops repository first",
|
|
141
|
+
"Commit changes and let Flux CD reconcile"
|
|
142
|
+
])
|
|
143
|
+
elif "flux reconcile" in command and "--dry-run" not in command:
|
|
144
|
+
suggestions.extend([
|
|
145
|
+
"Use: flux reconcile <resource> --dry-run",
|
|
146
|
+
"Follow GitOps workflow: commit -> push -> automatic reconciliation"
|
|
147
|
+
])
|
|
148
|
+
elif "helm install" in command or "helm upgrade" in command:
|
|
149
|
+
suggestions.extend([
|
|
150
|
+
"Use: helm template or helm upgrade --dry-run",
|
|
151
|
+
"Deploy via HelmRelease manifests in gitops repository"
|
|
152
|
+
])
|
|
153
|
+
else:
|
|
154
|
+
suggestions.append("Use read-only commands or --dry-run alternatives")
|
|
155
|
+
|
|
156
|
+
return GitOpsValidationResult(
|
|
157
|
+
allowed=False,
|
|
158
|
+
reason="Command violates GitOps principles - modifies cluster state directly",
|
|
159
|
+
severity="critical",
|
|
160
|
+
suggestions=suggestions,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# For gitops-operator agent, be extra strict
|
|
164
|
+
if agent_type == "gitops-operator":
|
|
165
|
+
if ("apply" in command or "create" in command) and "--dry-run" not in command:
|
|
166
|
+
return GitOpsValidationResult(
|
|
167
|
+
allowed=False,
|
|
168
|
+
reason="GitOps operator must use --dry-run for all apply operations",
|
|
169
|
+
severity="high",
|
|
170
|
+
suggestions=["Add --dry-run=client flag to command"],
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# Default: allow but warn about unclear intent
|
|
174
|
+
return GitOpsValidationResult(
|
|
175
|
+
allowed=True,
|
|
176
|
+
reason="Command not explicitly validated - proceed with caution",
|
|
177
|
+
severity="warning",
|
|
178
|
+
suggestions=["Verify command follows GitOps principles"],
|
|
179
|
+
)
|