@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,132 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""ElicitationResult hook -- activates T3 approval grants when user approves via AskUserQuestion.
|
|
3
|
+
|
|
4
|
+
This hook fires after the user responds to an AskUserQuestion elicitation.
|
|
5
|
+
It checks if the response indicates approval and, if so, activates all
|
|
6
|
+
pending approval grants for the current session.
|
|
7
|
+
|
|
8
|
+
The hook NEVER blocks (always exits 0). It is purely side-effectful:
|
|
9
|
+
reading the user's answer and activating grants when appropriate.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import sys
|
|
13
|
+
import json
|
|
14
|
+
import logging
|
|
15
|
+
import os
|
|
16
|
+
from datetime import datetime
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
20
|
+
|
|
21
|
+
from modules.core.paths import get_logs_dir
|
|
22
|
+
from modules.core.stdin import has_stdin_data
|
|
23
|
+
|
|
24
|
+
# Configure logging -- file only, no stderr
|
|
25
|
+
_log_file = get_logs_dir() / f"hooks-{datetime.now().strftime('%Y-%m-%d')}.log"
|
|
26
|
+
logging.basicConfig(
|
|
27
|
+
level=logging.INFO,
|
|
28
|
+
format='%(asctime)s [elicitation_result] %(name)s - %(levelname)s - %(message)s',
|
|
29
|
+
handlers=[logging.FileHandler(_log_file)],
|
|
30
|
+
)
|
|
31
|
+
logger = logging.getLogger(__name__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _extract_response(event: dict) -> str | None:
|
|
35
|
+
"""Extract the user's answer from the ElicitationResult event.
|
|
36
|
+
|
|
37
|
+
The exact schema is not fully documented, so we probe multiple
|
|
38
|
+
possible field names defensively.
|
|
39
|
+
"""
|
|
40
|
+
# Try top-level fields first
|
|
41
|
+
for field in ("result", "answer", "response", "selected", "value",
|
|
42
|
+
"hookEventInput", "elicitation_result"):
|
|
43
|
+
val = event.get(field)
|
|
44
|
+
if val is None:
|
|
45
|
+
continue
|
|
46
|
+
if isinstance(val, str) and val.strip():
|
|
47
|
+
return val
|
|
48
|
+
if isinstance(val, dict):
|
|
49
|
+
# Nested -- look for answer/selected inside
|
|
50
|
+
for inner in ("answer", "selected", "value", "result", "label"):
|
|
51
|
+
inner_val = val.get(inner)
|
|
52
|
+
if inner_val and isinstance(inner_val, str):
|
|
53
|
+
return inner_val
|
|
54
|
+
# Check for answers dict (AskUserQuestion structured format)
|
|
55
|
+
answers = val.get("answers", {})
|
|
56
|
+
if answers and isinstance(answers, dict):
|
|
57
|
+
first_val = next(iter(answers.values()), None)
|
|
58
|
+
if first_val:
|
|
59
|
+
return str(first_val)
|
|
60
|
+
# Check for options list selection
|
|
61
|
+
options = val.get("options", [])
|
|
62
|
+
if options and isinstance(options, list):
|
|
63
|
+
for opt in options:
|
|
64
|
+
if isinstance(opt, dict) and opt.get("selected"):
|
|
65
|
+
return str(opt.get("label", opt.get("value", "")))
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _is_approval(response: str) -> bool:
|
|
70
|
+
"""Check if the response indicates approval."""
|
|
71
|
+
normalized = response.lower().strip()
|
|
72
|
+
approval_words = ["approve", "approved", "yes", "accept", "confirm", "allow"]
|
|
73
|
+
return any(word in normalized for word in approval_words)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _activate_grants(session_id: str) -> None:
|
|
77
|
+
"""Activate all pending approval grants for this session."""
|
|
78
|
+
from modules.security.approval_grants import (
|
|
79
|
+
activate_grants_for_session,
|
|
80
|
+
get_pending_approvals_for_session,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
pending = get_pending_approvals_for_session(session_id)
|
|
84
|
+
if not pending:
|
|
85
|
+
logger.info("No pending approvals to activate for session %s", session_id)
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
results = activate_grants_for_session(session_id)
|
|
89
|
+
activated = sum(1 for r in results if r.success)
|
|
90
|
+
logger.info(
|
|
91
|
+
"ElicitationResult activated %d/%d pending approvals for session %s",
|
|
92
|
+
activated, len(results), session_id,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
if __name__ == "__main__":
|
|
97
|
+
if not has_stdin_data():
|
|
98
|
+
sys.exit(0)
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
raw = sys.stdin.read()
|
|
102
|
+
if not raw.strip():
|
|
103
|
+
sys.exit(0)
|
|
104
|
+
|
|
105
|
+
event = json.loads(raw)
|
|
106
|
+
|
|
107
|
+
# Extract session_id from event or environment
|
|
108
|
+
session_id = event.get("session_id") or os.environ.get("CLAUDE_SESSION_ID", "")
|
|
109
|
+
|
|
110
|
+
# Extract user's response
|
|
111
|
+
response = _extract_response(event)
|
|
112
|
+
|
|
113
|
+
if not response:
|
|
114
|
+
logger.info("No extractable response in ElicitationResult event")
|
|
115
|
+
sys.exit(0)
|
|
116
|
+
|
|
117
|
+
logger.info("ElicitationResult response: %s", response[:80])
|
|
118
|
+
|
|
119
|
+
# Check if the response indicates approval
|
|
120
|
+
if _is_approval(response):
|
|
121
|
+
if session_id:
|
|
122
|
+
_activate_grants(session_id)
|
|
123
|
+
else:
|
|
124
|
+
logger.warning("Approval detected but no session_id available")
|
|
125
|
+
else:
|
|
126
|
+
logger.info("ElicitationResult response is not an approval: %s", response[:40])
|
|
127
|
+
|
|
128
|
+
except Exception as e:
|
|
129
|
+
logger.error("Error in elicitation_result hook: %s", e, exc_info=True)
|
|
130
|
+
|
|
131
|
+
# Never block -- always exit 0
|
|
132
|
+
sys.exit(0)
|
package/hooks/hooks.json
CHANGED
|
@@ -16,12 +16,20 @@
|
|
|
16
16
|
{
|
|
17
17
|
"matcher": "SendMessage",
|
|
18
18
|
"hooks": [{"type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/pre_tool_use.py"}]
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"matcher": "Read|Edit|Write|Glob|Grep|WebSearch|WebFetch|NotebookEdit",
|
|
22
|
+
"hooks": [{"type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/pre_tool_use.py"}]
|
|
19
23
|
}
|
|
20
24
|
],
|
|
21
25
|
"PostToolUse": [
|
|
22
26
|
{
|
|
23
27
|
"matcher": "Bash",
|
|
24
28
|
"hooks": [{"type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/post_tool_use.py"}]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"matcher": "AskUserQuestion",
|
|
32
|
+
"hooks": [{"type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/post_tool_use.py"}]
|
|
25
33
|
}
|
|
26
34
|
],
|
|
27
35
|
"SubagentStop": [
|
|
@@ -61,6 +69,7 @@
|
|
|
61
69
|
{
|
|
62
70
|
"hooks": [{"type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/post_compact.py"}]
|
|
63
71
|
}
|
|
64
|
-
]
|
|
72
|
+
],
|
|
73
|
+
"ElicitationResult": []
|
|
65
74
|
}
|
|
66
75
|
}
|
package/hooks/modules/README.md
CHANGED
|
@@ -105,7 +105,7 @@ This enforces the principle: "Orchestrator delegates, agents execute."
|
|
|
105
105
|
SendMessage is validated as a PreToolUse event (not a separate hook event):
|
|
106
106
|
- Agent ID format check (must match `/^a[0-9a-f]{5,}$/`)
|
|
107
107
|
- Non-empty message required
|
|
108
|
-
-
|
|
108
|
+
- Grant activation is handled by ElicitationResult hook (user approval via AskUserQuestion)
|
|
109
109
|
|
|
110
110
|
### Context Enforcement
|
|
111
111
|
Task invocations for project agents inject project-context via `context_provider.py`.
|
|
@@ -173,11 +173,12 @@ All security rules (blocked patterns, mutative verbs, tiers) are hardcoded in th
|
|
|
173
173
|
|
|
174
174
|
### Validation Order (Defense-in-Depth)
|
|
175
175
|
bash_validator checks commands in this order (short-circuit on first match):
|
|
176
|
+
0. **Indirect execution detection** — `bash -c`, `eval`, `python -c` etc. → ask or block
|
|
176
177
|
1. **Blocked commands** (blocked_commands.py) — permanently denied patterns, exit 2
|
|
177
178
|
2. **Claude footer stripping** — transparent via updatedInput
|
|
178
179
|
3. **Commit message validation** — conventional commits enforcement
|
|
179
180
|
4. **Cloud pipe/redirect/chain check** (cloud_pipe_validator.py) — corrective deny
|
|
180
|
-
5. **Mutative verbs** (mutative_verbs.py) — CLI-agnostic verb detector,
|
|
181
|
+
5. **Mutative verbs** (mutative_verbs.py) — CLI-agnostic verb detector, native `ask` dialog
|
|
181
182
|
6. **GitOps validation** (gitops_validator.py) — kubectl/helm/flux policy enforcement
|
|
182
183
|
7. **Everything else** — SAFE by elimination (auto-approved)
|
|
183
184
|
|
|
@@ -118,7 +118,7 @@ def _validate_from_json_contract(contract: dict, task_info: Dict[str, Any]) -> V
|
|
|
118
118
|
plan_status = str(agent_status.get("plan_status", "")).upper()
|
|
119
119
|
|
|
120
120
|
statuses_requiring_evidence = {
|
|
121
|
-
"IN_PROGRESS", "REVIEW",
|
|
121
|
+
"IN_PROGRESS", "REVIEW",
|
|
122
122
|
"COMPLETE", "BLOCKED", "NEEDS_INPUT",
|
|
123
123
|
}
|
|
124
124
|
|
|
@@ -568,48 +568,12 @@ def validate_verbatim_outputs_consistency(
|
|
|
568
568
|
# False pending-approval detection
|
|
569
569
|
# ============================================================================
|
|
570
570
|
|
|
571
|
-
_NONCE_PATTERN = re.compile(r"NONCE:[a-f0-9]{32}")
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
def validate_awaiting_approval_has_nonce(
|
|
575
|
-
transcript_text: str,
|
|
576
|
-
plan_status: str,
|
|
577
|
-
) -> Optional[Dict[str, Any]]:
|
|
578
|
-
"""Detect when an agent returns AWAITING_APPROVAL without a real hook nonce.
|
|
579
|
-
|
|
580
|
-
If plan_status is AWAITING_APPROVAL, a hook should have blocked a T3 command
|
|
581
|
-
and emitted a ``NONCE:<32-hex>`` token. If no such token appears in the
|
|
582
|
-
agent's transcript/output, the agent likely over-escalated.
|
|
583
|
-
|
|
584
|
-
Args:
|
|
585
|
-
transcript_text: The full agent transcript or output text.
|
|
586
|
-
plan_status: The agent's reported plan_status string.
|
|
587
|
-
|
|
588
|
-
Returns:
|
|
589
|
-
An anomaly dict (severity: info) when the check triggers, None otherwise.
|
|
590
|
-
"""
|
|
591
|
-
if plan_status.upper() != "AWAITING_APPROVAL":
|
|
592
|
-
return None
|
|
593
|
-
|
|
594
|
-
if _NONCE_PATTERN.search(transcript_text):
|
|
595
|
-
return None
|
|
596
|
-
|
|
597
|
-
return {
|
|
598
|
-
"type": "awaiting_approval_missing_nonce",
|
|
599
|
-
"severity": "info",
|
|
600
|
-
"detail": (
|
|
601
|
-
"Agent returned AWAITING_APPROVAL without a hook nonce. "
|
|
602
|
-
"This is normal for plan-first workflows; it becomes a concern "
|
|
603
|
-
"only if the agent never proceeds to execution."
|
|
604
|
-
),
|
|
605
|
-
}
|
|
606
|
-
|
|
607
571
|
|
|
608
572
|
# ============================================================================
|
|
609
573
|
# Approval request validation
|
|
610
574
|
# ============================================================================
|
|
611
575
|
|
|
612
|
-
_APPROVAL_STATUSES = {"REVIEW"
|
|
576
|
+
_APPROVAL_STATUSES = {"REVIEW"}
|
|
613
577
|
|
|
614
578
|
_APPROVAL_REQUIRED_FIELDS = [
|
|
615
579
|
"operation", "exact_content", "scope", "risk_level", "rollback", "verification",
|
|
@@ -624,7 +588,7 @@ def validate_approval_request(
|
|
|
624
588
|
contract: dict,
|
|
625
589
|
plan_status: str,
|
|
626
590
|
) -> Optional[Dict[str, Any]]:
|
|
627
|
-
"""Validate the approval_request block when plan_status is REVIEW
|
|
591
|
+
"""Validate the approval_request block when plan_status is REVIEW.
|
|
628
592
|
|
|
629
593
|
Advisory only -- returns an anomaly dict if validation fails, None if OK
|
|
630
594
|
or if the check does not apply.
|
|
@@ -659,14 +623,7 @@ def validate_approval_request(
|
|
|
659
623
|
risk = str(approval_req.get("risk_level", "")).upper()
|
|
660
624
|
invalid_risk = risk and risk not in _VALID_RISK_LEVELS
|
|
661
625
|
|
|
662
|
-
# For AWAITING_APPROVAL, also check nonce
|
|
663
626
|
nonce_issue = None
|
|
664
|
-
if plan_status.upper() == "AWAITING_APPROVAL":
|
|
665
|
-
nonce_val = str(approval_req.get("nonce", ""))
|
|
666
|
-
if not nonce_val:
|
|
667
|
-
nonce_issue = "nonce field missing"
|
|
668
|
-
elif not _NONCE_HEX_RE.match(nonce_val):
|
|
669
|
-
nonce_issue = f"nonce format invalid: {nonce_val}"
|
|
670
627
|
|
|
671
628
|
issues: List[str] = []
|
|
672
629
|
if missing_fields:
|
|
@@ -688,8 +645,3 @@ def validate_approval_request(
|
|
|
688
645
|
),
|
|
689
646
|
"missing_fields": missing_fields,
|
|
690
647
|
}
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
# Aliases for shorter import names
|
|
694
|
-
extract_plan_status = extract_plan_status_from_output
|
|
695
|
-
extract_exit_code = extract_exit_code_from_output
|
|
@@ -29,7 +29,6 @@ from .contract_validator import parse_contract
|
|
|
29
29
|
VALID_PLAN_STATUSES = {
|
|
30
30
|
"IN_PROGRESS",
|
|
31
31
|
"REVIEW",
|
|
32
|
-
"AWAITING_APPROVAL",
|
|
33
32
|
"COMPLETE",
|
|
34
33
|
"BLOCKED",
|
|
35
34
|
"NEEDS_INPUT",
|
|
@@ -64,7 +63,7 @@ CONSOLIDATION_FIELDS = [
|
|
|
64
63
|
RECOMMENDED_ACTION_NONE = "none"
|
|
65
64
|
|
|
66
65
|
# Statuses that should carry an approval_request block
|
|
67
|
-
APPROVAL_REQUEST_STATUSES = {"REVIEW"
|
|
66
|
+
APPROVAL_REQUEST_STATUSES = {"REVIEW"}
|
|
68
67
|
|
|
69
68
|
APPROVAL_REQUEST_REQUIRED_FIELDS = [
|
|
70
69
|
"operation",
|
|
@@ -386,12 +385,9 @@ def validate_response_contract(
|
|
|
386
385
|
risk = str(approval_req.get("risk_level", "")).upper()
|
|
387
386
|
if risk and risk not in VALID_RISK_LEVELS:
|
|
388
387
|
warnings.append(f"APPROVAL_REQUEST_INVALID_RISK_LEVEL:{risk}")
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
warnings.append("APPROVAL_REQUEST_NONCE_MISSING")
|
|
393
|
-
elif not _NONCE_HEX_PATTERN.match(nonce_val):
|
|
394
|
-
warnings.append(f"APPROVAL_REQUEST_NONCE_INVALID:{nonce_val}")
|
|
388
|
+
# Check for approval_id when status is REVIEW
|
|
389
|
+
if status.plan_status == "REVIEW":
|
|
390
|
+
pass # approval_id presence is advisory, not enforced
|
|
395
391
|
|
|
396
392
|
valid = not missing and not invalid
|
|
397
393
|
recommended_action = RECOMMENDED_ACTION_NONE if valid else "resume_same_agent_contract_repair"
|
|
@@ -117,8 +117,8 @@ def extract_task_description_from_transcript(transcript_path: str) -> str:
|
|
|
117
117
|
The first ``role: "user"`` entry is the task prompt sent by the orchestrator --
|
|
118
118
|
which is the most meaningful description of what the agent was asked to do.
|
|
119
119
|
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
Context is delivered via additionalContext (not prompt mutation), so the
|
|
121
|
+
first user message IS the original prompt without any wrapping.
|
|
122
122
|
|
|
123
123
|
Returns empty string on any error so the hook never crashes.
|
|
124
124
|
"""
|
|
@@ -134,9 +134,8 @@ def extract_injected_context_payload_from_transcript(
|
|
|
134
134
|
) -> Dict[str, Any]:
|
|
135
135
|
"""Extract the auto-injected context payload from disk cache.
|
|
136
136
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
fallback has been removed as prompts no longer contain embedded payloads.
|
|
137
|
+
Context is delivered via additionalContext and the payload is persisted to
|
|
138
|
+
disk by context_injector. Prompts do not contain embedded payloads.
|
|
140
139
|
"""
|
|
141
140
|
import os
|
|
142
141
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Audit module - Logging, metrics, and event detection.
|
|
2
|
+
Audit module - Logging, metrics aggregation, and event detection.
|
|
3
3
|
|
|
4
4
|
Provides:
|
|
5
|
-
- logger: AuditLogger for tool executions
|
|
6
|
-
- metrics:
|
|
5
|
+
- logger: AuditLogger for tool executions (write path)
|
|
6
|
+
- metrics: generate_summary reads audit logs and aggregates (read path)
|
|
7
7
|
- event_detector: CriticalEventDetector
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
from .logger import AuditLogger, log_execution
|
|
11
|
-
from .metrics import
|
|
11
|
+
from .metrics import generate_summary
|
|
12
12
|
from .event_detector import (
|
|
13
13
|
CriticalEventDetector,
|
|
14
14
|
detect_critical_event,
|
|
@@ -20,8 +20,6 @@ __all__ = [
|
|
|
20
20
|
"AuditLogger",
|
|
21
21
|
"log_execution",
|
|
22
22
|
# Metrics
|
|
23
|
-
"MetricsCollector",
|
|
24
|
-
"record_metric",
|
|
25
23
|
"generate_summary",
|
|
26
24
|
# Event detector
|
|
27
25
|
"CriticalEventDetector",
|