@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,40 @@
|
|
|
1
|
+
"""Validate and classify resume prompts for security decision-making.
|
|
2
|
+
|
|
3
|
+
Subsystem 1 of the pre_tool_use Task/Agent path.
|
|
4
|
+
Runs FIRST -- if invalid, nothing else loads.
|
|
5
|
+
|
|
6
|
+
Responsibilities:
|
|
7
|
+
- Validates prompt is not empty, not malformed
|
|
8
|
+
- Detects deprecated approval phrases
|
|
9
|
+
- Detects nonce patterns
|
|
10
|
+
- Returns: classification (nonce/malformed_nonce/deprecated/standard)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from .approval_constants import (
|
|
14
|
+
NONCE_APPROVAL_PREFIX,
|
|
15
|
+
NONCE_APPROVAL_PATTERN,
|
|
16
|
+
DEPRECATED_APPROVAL_PHRASES,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def classify_resume_prompt(prompt: str) -> str:
|
|
21
|
+
"""Classify a resume prompt into one of four categories.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
prompt: The resume prompt string.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
'nonce' -- valid nonce approval token present
|
|
28
|
+
'malformed_nonce' -- APPROVE: prefix present but invalid nonce
|
|
29
|
+
'deprecated' -- deprecated approval phrase detected
|
|
30
|
+
'standard' -- normal resume prompt (no approval indicators)
|
|
31
|
+
"""
|
|
32
|
+
stripped_prompt = prompt.strip()
|
|
33
|
+
if NONCE_APPROVAL_PATTERN.search(prompt):
|
|
34
|
+
return "nonce"
|
|
35
|
+
if stripped_prompt.startswith(NONCE_APPROVAL_PREFIX):
|
|
36
|
+
return "malformed_nonce"
|
|
37
|
+
prompt_lower = prompt.lower()
|
|
38
|
+
if any(phrase in prompt_lower for phrase in DEPRECATED_APPROVAL_PHRASES):
|
|
39
|
+
return "deprecated"
|
|
40
|
+
return "standard"
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Security tier definitions and classification.
|
|
3
|
+
|
|
4
|
+
Provides tier metadata for commands after bash_validator has already
|
|
5
|
+
enforced security decisions. The bash_validator is the primary gate;
|
|
6
|
+
this module's classify_command_tier() is used for logging and state
|
|
7
|
+
tracking.
|
|
8
|
+
|
|
9
|
+
Tiers:
|
|
10
|
+
- T0: Read-only operations (safe by elimination)
|
|
11
|
+
- T1: Validation operations (validate, lint, fmt, check) -- local only
|
|
12
|
+
- T2: Simulation operations (plan, diff, dry-run) -- may contact remote APIs
|
|
13
|
+
- T3: State-modifying operations (mutative_verbs.py detection,
|
|
14
|
+
nonce-based approval via approval_grants.py)
|
|
15
|
+
"""
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import re
|
|
19
|
+
import logging
|
|
20
|
+
from enum import Enum
|
|
21
|
+
from functools import lru_cache
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class SecurityTier(str, Enum):
|
|
27
|
+
"""Security tier classification for commands."""
|
|
28
|
+
|
|
29
|
+
T0_READ_ONLY = "T0" # describe, get, show, list operations
|
|
30
|
+
T1_VALIDATION = "T1" # validate, lint, fmt, check (local only)
|
|
31
|
+
T2_DRY_RUN = "T2" # plan, diff, dry-run, template (simulation)
|
|
32
|
+
T3_BLOCKED = "T3" # apply, reconcile, deploy operations (require approval)
|
|
33
|
+
|
|
34
|
+
def __str__(self) -> str:
|
|
35
|
+
return self.value
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def requires_approval(self) -> bool:
|
|
39
|
+
"""Check if this tier requires user approval."""
|
|
40
|
+
return self == SecurityTier.T3_BLOCKED
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def description(self) -> str:
|
|
44
|
+
"""Human-readable description of the tier."""
|
|
45
|
+
descriptions = {
|
|
46
|
+
SecurityTier.T0_READ_ONLY: "Read-only operation",
|
|
47
|
+
SecurityTier.T1_VALIDATION: "Validation operation",
|
|
48
|
+
SecurityTier.T2_DRY_RUN: "Dry-run operation",
|
|
49
|
+
SecurityTier.T3_BLOCKED: "State-modifying operation (requires approval)",
|
|
50
|
+
}
|
|
51
|
+
return descriptions.get(self, "Unknown tier")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# T1: Local validation (no remote API calls)
|
|
55
|
+
T1_PATTERNS = [
|
|
56
|
+
r"\bvalidate\b",
|
|
57
|
+
r"\blint\b",
|
|
58
|
+
r"\bcheck\b",
|
|
59
|
+
r"\bfmt\b",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
# T2: Simulation (may contact remote APIs, but no state changes)
|
|
63
|
+
T2_PATTERNS = [
|
|
64
|
+
r"\bplan\b",
|
|
65
|
+
r"\btemplate\b",
|
|
66
|
+
r"\bdiff\b",
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
# Ultra-common commands that should fast-path to T0
|
|
70
|
+
# These are commands that appear in >80% of sessions
|
|
71
|
+
# NOTE: Only include commands that are ALWAYS read-only regardless of flags.
|
|
72
|
+
# "git branch" was removed because it has mutative variants (-D, -m, -M, etc.).
|
|
73
|
+
ULTRA_COMMON_T0_COMMANDS = frozenset({
|
|
74
|
+
"ls", "pwd", "cat", "echo", "git status", "git diff",
|
|
75
|
+
"git log", "kubectl get",
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@lru_cache(maxsize=512)
|
|
80
|
+
def _classify_command_tier_cached(
|
|
81
|
+
command: str,
|
|
82
|
+
has_blocked_patterns: bool = False,
|
|
83
|
+
) -> SecurityTier:
|
|
84
|
+
"""
|
|
85
|
+
Classify command into security tier with LRU cache.
|
|
86
|
+
|
|
87
|
+
This is the internal cached implementation. Use classify_command_tier() instead.
|
|
88
|
+
"""
|
|
89
|
+
if not command or not command.strip():
|
|
90
|
+
return SecurityTier.T3_BLOCKED
|
|
91
|
+
|
|
92
|
+
command = command.strip()
|
|
93
|
+
|
|
94
|
+
# Fast-path: Ultra-common T0 commands
|
|
95
|
+
words = command.split()
|
|
96
|
+
if len(words) >= 2:
|
|
97
|
+
prefix2 = f"{words[0]} {words[1]}"
|
|
98
|
+
if prefix2 in ULTRA_COMMON_T0_COMMANDS:
|
|
99
|
+
return SecurityTier.T0_READ_ONLY
|
|
100
|
+
if len(words) >= 1:
|
|
101
|
+
if words[0] in ULTRA_COMMON_T0_COMMANDS:
|
|
102
|
+
return SecurityTier.T0_READ_ONLY
|
|
103
|
+
|
|
104
|
+
# Blocked patterns already checked externally
|
|
105
|
+
if has_blocked_patterns:
|
|
106
|
+
return SecurityTier.T3_BLOCKED
|
|
107
|
+
|
|
108
|
+
# Check for dry-run operations (T2)
|
|
109
|
+
if "--dry-run" in command or "--plan-only" in command:
|
|
110
|
+
return SecurityTier.T2_DRY_RUN
|
|
111
|
+
|
|
112
|
+
# Check for simulation operations (T2: plan, diff, template)
|
|
113
|
+
for pattern in T2_PATTERNS:
|
|
114
|
+
if re.search(pattern, command, re.IGNORECASE):
|
|
115
|
+
return SecurityTier.T2_DRY_RUN
|
|
116
|
+
|
|
117
|
+
# Check for local validation operations (T1: validate, lint, fmt, check)
|
|
118
|
+
for pattern in T1_PATTERNS:
|
|
119
|
+
if re.search(pattern, command, re.IGNORECASE):
|
|
120
|
+
return SecurityTier.T1_VALIDATION
|
|
121
|
+
|
|
122
|
+
# Use the mutative verb detector for T3 classification
|
|
123
|
+
from .mutative_verbs import (
|
|
124
|
+
detect_mutative_command,
|
|
125
|
+
CATEGORY_MUTATIVE,
|
|
126
|
+
CATEGORY_READ_ONLY,
|
|
127
|
+
CATEGORY_SIMULATION,
|
|
128
|
+
)
|
|
129
|
+
result = detect_mutative_command(command)
|
|
130
|
+
if result.is_mutative:
|
|
131
|
+
return SecurityTier.T3_BLOCKED
|
|
132
|
+
if result.category == CATEGORY_SIMULATION:
|
|
133
|
+
return SecurityTier.T2_DRY_RUN
|
|
134
|
+
if result.category == CATEGORY_READ_ONLY:
|
|
135
|
+
return SecurityTier.T0_READ_ONLY
|
|
136
|
+
|
|
137
|
+
# Not blocked, not mutative -> safe by elimination (T0)
|
|
138
|
+
return SecurityTier.T0_READ_ONLY
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def classify_command_tier(
|
|
142
|
+
command: str,
|
|
143
|
+
*,
|
|
144
|
+
pre_computed_tier: "SecurityTier | None" = None,
|
|
145
|
+
) -> SecurityTier:
|
|
146
|
+
"""
|
|
147
|
+
Classify command into security tier.
|
|
148
|
+
|
|
149
|
+
NOTE: This function is used for tier metadata AFTER the bash_validator has
|
|
150
|
+
already enforced security decisions. The bash_validator's own validation
|
|
151
|
+
order (blocked -> safe -> dangerous verbs -> GitOps -> tier) is the primary
|
|
152
|
+
security gate.
|
|
153
|
+
|
|
154
|
+
If *pre_computed_tier* is provided (e.g. from a ``BashValidationResult``
|
|
155
|
+
that already determined the tier during validation), it is returned
|
|
156
|
+
immediately without re-computing.
|
|
157
|
+
|
|
158
|
+
Classification order (when no pre-computed tier):
|
|
159
|
+
1. Ultra-common T0 fast-path (ls, git status, etc.)
|
|
160
|
+
2. Blocked patterns (T3) -- checked against pre-compiled patterns
|
|
161
|
+
3. Dry-run/simulation (T2) -- --dry-run, plan, diff, template
|
|
162
|
+
4. Local validation (T1) -- validate, lint, fmt, check
|
|
163
|
+
5. Mutative verb detector (T3) -- MUTATIVE verbs
|
|
164
|
+
6. Default T0 for everything else (safe by elimination)
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
command: Shell command to classify
|
|
168
|
+
pre_computed_tier: Optional tier already determined by an upstream
|
|
169
|
+
validator. When provided the function returns it directly.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
SecurityTier classification
|
|
173
|
+
"""
|
|
174
|
+
# Fast path: caller already knows the tier (e.g. BashValidationResult).
|
|
175
|
+
if pre_computed_tier is not None:
|
|
176
|
+
return pre_computed_tier
|
|
177
|
+
|
|
178
|
+
if not command or not command.strip():
|
|
179
|
+
return SecurityTier.T3_BLOCKED
|
|
180
|
+
|
|
181
|
+
command = command.strip()
|
|
182
|
+
|
|
183
|
+
# Import here to avoid circular imports
|
|
184
|
+
from .blocked_commands import get_blocked_patterns
|
|
185
|
+
blocked_patterns = get_blocked_patterns()
|
|
186
|
+
|
|
187
|
+
# Check for blocked operations first (T3)
|
|
188
|
+
# This must be done before caching since blocked_patterns come from module state
|
|
189
|
+
has_blocked = False
|
|
190
|
+
for pattern in blocked_patterns:
|
|
191
|
+
if pattern.search(command):
|
|
192
|
+
has_blocked = True
|
|
193
|
+
break
|
|
194
|
+
|
|
195
|
+
# Use cached classification
|
|
196
|
+
return _classify_command_tier_cached(command, has_blocked)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""Session management module.
|
|
2
|
+
|
|
3
|
+
Provides:
|
|
4
|
+
- session_manager: Session ID generation and management
|
|
5
|
+
- session_event_injector: Filter and inject session events into agent prompts
|
|
6
|
+
|
|
7
|
+
Note: session_state.py has been absorbed into modules.memory.episode_writer.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
__all__ = []
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Session context writer for PostToolUse hook.
|
|
3
|
+
|
|
4
|
+
Manages the active session context file, appending critical events
|
|
5
|
+
(git commits, pushes, etc.) and applying a time-based retention policy.
|
|
6
|
+
|
|
7
|
+
Public API:
|
|
8
|
+
- SessionContextWriter (class with update_context method)
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import fcntl
|
|
12
|
+
import json
|
|
13
|
+
import logging
|
|
14
|
+
import os
|
|
15
|
+
from datetime import datetime, timedelta
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Dict, Any
|
|
18
|
+
|
|
19
|
+
from ..core.paths import get_session_dir
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
# Default retention period for session events
|
|
24
|
+
DEFAULT_RETENTION_HOURS = 24
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class SessionContextWriter:
|
|
28
|
+
"""Update active session context for critical events.
|
|
29
|
+
|
|
30
|
+
Thread-safe via file locking. Applies a configurable retention
|
|
31
|
+
policy (default 24h, override via SESSION_RETENTION_HOURS env var).
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, context_path: Path = None):
|
|
35
|
+
"""Initialize with optional custom context path.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
context_path: Override path. Defaults to session_dir/context.json.
|
|
39
|
+
"""
|
|
40
|
+
self.context_path = context_path or (get_session_dir() / "context.json")
|
|
41
|
+
|
|
42
|
+
def update_context(self, event_data: Dict[str, Any]) -> None:
|
|
43
|
+
"""Update active context with event data.
|
|
44
|
+
|
|
45
|
+
Appends the event to the critical_events list, applies retention
|
|
46
|
+
policy, and writes back atomically with file locking.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
event_data: Event dict (must include at least event_type).
|
|
50
|
+
"""
|
|
51
|
+
try:
|
|
52
|
+
self.context_path.parent.mkdir(parents=True, exist_ok=True)
|
|
53
|
+
|
|
54
|
+
# File lock to prevent race conditions from parallel tool calls
|
|
55
|
+
lock_path = self.context_path.with_suffix(".lock")
|
|
56
|
+
with open(lock_path, "w") as lock_file:
|
|
57
|
+
fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX)
|
|
58
|
+
try:
|
|
59
|
+
context: Dict[str, Any] = {}
|
|
60
|
+
if self.context_path.exists():
|
|
61
|
+
with open(self.context_path, "r") as f:
|
|
62
|
+
context = json.load(f)
|
|
63
|
+
|
|
64
|
+
if "critical_events" not in context:
|
|
65
|
+
context["critical_events"] = []
|
|
66
|
+
|
|
67
|
+
event_data["timestamp"] = datetime.now().isoformat()
|
|
68
|
+
|
|
69
|
+
context["critical_events"].append(event_data)
|
|
70
|
+
|
|
71
|
+
# Keep only events within retention window
|
|
72
|
+
retention_hours = int(
|
|
73
|
+
os.environ.get(
|
|
74
|
+
"SESSION_RETENTION_HOURS",
|
|
75
|
+
str(DEFAULT_RETENTION_HOURS),
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
cutoff = datetime.now() - timedelta(hours=retention_hours)
|
|
79
|
+
|
|
80
|
+
context["critical_events"] = [
|
|
81
|
+
event
|
|
82
|
+
for event in context["critical_events"]
|
|
83
|
+
if event.get("timestamp")
|
|
84
|
+
and datetime.fromisoformat(event["timestamp"]) > cutoff
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
context["last_modified"] = datetime.now().isoformat()
|
|
88
|
+
|
|
89
|
+
with open(self.context_path, "w") as f:
|
|
90
|
+
json.dump(context, f, indent=2)
|
|
91
|
+
finally:
|
|
92
|
+
fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)
|
|
93
|
+
|
|
94
|
+
logger.info(
|
|
95
|
+
"Updated context with event: %s",
|
|
96
|
+
event_data.get("event_type", "unknown"),
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
except Exception as e:
|
|
100
|
+
logger.error("Error updating active context: %s", e)
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""Session event injection for agent context.
|
|
2
|
+
|
|
3
|
+
Subsystem 4 of the pre_tool_use Task/Agent path.
|
|
4
|
+
|
|
5
|
+
Filters events by agent domain and injects them into agent prompts.
|
|
6
|
+
Includes the hardcoded agent-to-event mapping.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import logging
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
# Agent-to-event-type mapping: which events each agent should see
|
|
17
|
+
AGENT_EVENT_FILTERS = {
|
|
18
|
+
"terraform-architect": ["git_commit", "infrastructure_change"],
|
|
19
|
+
"gitops-operator": ["git_commit", "git_push", "infrastructure_change"],
|
|
20
|
+
"devops-developer": ["git_commit", "file_modifications"],
|
|
21
|
+
"cloud-troubleshooter": "*", # All events (needs full history for diagnosis)
|
|
22
|
+
"gaia-system": "*" # All events (workflow analysis)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def filter_events_for_agent(events: list, agent: str) -> list:
|
|
27
|
+
"""
|
|
28
|
+
Filter events relevant to agent domain.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
events: List of critical events from session
|
|
32
|
+
agent: Agent type (e.g., "gitops-operator")
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Filtered list of events relevant to agent
|
|
36
|
+
"""
|
|
37
|
+
agent_filter = AGENT_EVENT_FILTERS.get(agent, [])
|
|
38
|
+
|
|
39
|
+
# Return all events for wildcard agents
|
|
40
|
+
if agent_filter == "*":
|
|
41
|
+
return events[-10:] # Last 10 events
|
|
42
|
+
|
|
43
|
+
# Filter by event type and return max 10
|
|
44
|
+
filtered = [
|
|
45
|
+
e for e in events[-20:] # Search last 20
|
|
46
|
+
if e.get("event_type") in agent_filter
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
return filtered[:10] # Return max 10
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def format_events_summary(events: list) -> str:
|
|
53
|
+
"""
|
|
54
|
+
Format events as readable summary for agent context.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
events: List of filtered events
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
Formatted markdown string
|
|
61
|
+
"""
|
|
62
|
+
if not events:
|
|
63
|
+
return "No recent events"
|
|
64
|
+
|
|
65
|
+
lines = []
|
|
66
|
+
|
|
67
|
+
for event in events:
|
|
68
|
+
etype = event.get("event_type", "")
|
|
69
|
+
ts = event.get("timestamp", "")[:16] # YYYY-MM-DDTHH:MM
|
|
70
|
+
|
|
71
|
+
if etype == "git_commit":
|
|
72
|
+
msg = event.get("commit_message", "")
|
|
73
|
+
hash_val = event.get("commit_hash", "")[:7]
|
|
74
|
+
if hash_val and msg:
|
|
75
|
+
lines.append(f"- [{ts}] Commit {hash_val}: {msg}")
|
|
76
|
+
|
|
77
|
+
elif etype == "git_push":
|
|
78
|
+
branch = event.get("branch", "")
|
|
79
|
+
if branch:
|
|
80
|
+
lines.append(f"- [{ts}] Pushed to {branch}")
|
|
81
|
+
|
|
82
|
+
elif etype == "file_modifications":
|
|
83
|
+
count = event.get("modification_count", 0)
|
|
84
|
+
if count:
|
|
85
|
+
lines.append(f"- [{ts}] Modified {count} files")
|
|
86
|
+
|
|
87
|
+
elif etype == "infrastructure_change":
|
|
88
|
+
cmd = event.get("command", "")
|
|
89
|
+
if cmd:
|
|
90
|
+
lines.append(f"- [{ts}] Infrastructure: {cmd}")
|
|
91
|
+
|
|
92
|
+
return "\n".join(lines) if lines else "No recent events"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def build_session_events(
|
|
96
|
+
parameters: dict,
|
|
97
|
+
project_agents: list,
|
|
98
|
+
) -> str | None:
|
|
99
|
+
"""
|
|
100
|
+
Build session events string for agent context without mutating parameters.
|
|
101
|
+
|
|
102
|
+
Filters events by agent domain to avoid noise.
|
|
103
|
+
Returns the events string suitable for additionalContext injection,
|
|
104
|
+
or None if no events to inject.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
parameters: Task tool parameters (read-only).
|
|
108
|
+
project_agents: List of valid project agent names.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
Session events string, or None if nothing to inject.
|
|
112
|
+
"""
|
|
113
|
+
subagent_type = parameters.get("subagent_type", "")
|
|
114
|
+
|
|
115
|
+
# Only inject for project agents
|
|
116
|
+
if subagent_type not in project_agents:
|
|
117
|
+
logger.debug(f"Skipping session events for non-project agent: {subagent_type}")
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
# Get session events
|
|
121
|
+
from ..core.paths import get_session_dir
|
|
122
|
+
context_path = get_session_dir() / "context.json"
|
|
123
|
+
if not context_path.exists():
|
|
124
|
+
logger.debug("No session context file found")
|
|
125
|
+
return None
|
|
126
|
+
|
|
127
|
+
try:
|
|
128
|
+
with open(context_path, 'r') as f:
|
|
129
|
+
context = json.load(f)
|
|
130
|
+
|
|
131
|
+
events = context.get("critical_events", [])
|
|
132
|
+
if not events:
|
|
133
|
+
logger.debug("No critical events in session")
|
|
134
|
+
return None
|
|
135
|
+
|
|
136
|
+
# Filter by agent domain
|
|
137
|
+
filtered = filter_events_for_agent(events, subagent_type)
|
|
138
|
+
|
|
139
|
+
if not filtered:
|
|
140
|
+
logger.debug(f"No relevant events for {subagent_type}")
|
|
141
|
+
return None
|
|
142
|
+
|
|
143
|
+
# Format events summary
|
|
144
|
+
events_summary = format_events_summary(filtered)
|
|
145
|
+
|
|
146
|
+
events_string = (
|
|
147
|
+
"# Recent Session Events (Auto-Injected, Last 24h)\n"
|
|
148
|
+
f"{events_summary}"
|
|
149
|
+
)
|
|
150
|
+
logger.info(f"Session events built for {subagent_type} ({len(filtered)} events)")
|
|
151
|
+
|
|
152
|
+
return events_string
|
|
153
|
+
|
|
154
|
+
except Exception as e:
|
|
155
|
+
logger.warning(f"Failed to build session events: {e}")
|
|
156
|
+
return None
|
|
157
|
+
|
|
158
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Session ID generation and retrieval.
|
|
3
|
+
|
|
4
|
+
Provides:
|
|
5
|
+
- get_or_create_session_id(): Get existing session ID or create new one
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import hashlib
|
|
9
|
+
import logging
|
|
10
|
+
import os
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_or_create_session_id() -> str:
|
|
17
|
+
"""Get existing session ID or create new one.
|
|
18
|
+
|
|
19
|
+
Checks the CLAUDE_SESSION_ID env var first. If absent, generates a
|
|
20
|
+
new session ID from the current time and PID, stores it in the env var,
|
|
21
|
+
and returns it.
|
|
22
|
+
"""
|
|
23
|
+
session_id = os.environ.get("CLAUDE_SESSION_ID")
|
|
24
|
+
if not session_id:
|
|
25
|
+
timestamp = datetime.now().strftime("%H%M%S")
|
|
26
|
+
hash_input = f"{timestamp}-{os.getpid()}"
|
|
27
|
+
session_hash = hashlib.sha256(hash_input.encode()).hexdigest()[:8]
|
|
28
|
+
session_id = f"session-{timestamp}-{session_hash}"
|
|
29
|
+
os.environ["CLAUDE_SESSION_ID"] = session_id
|
|
30
|
+
logger.debug(f"Generated new session_id: {session_id}")
|
|
31
|
+
return session_id
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tools module - Tool-specific validators.
|
|
3
|
+
|
|
4
|
+
Provides:
|
|
5
|
+
- bash_validator: Bash command validation
|
|
6
|
+
- task_validator: Task tool validation with context enforcement
|
|
7
|
+
- shell_parser: Shell command parsing (pipes, chains, etc.)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from .shell_parser import ShellCommandParser, get_shell_parser, parse_command
|
|
11
|
+
from .bash_validator import BashValidator, validate_bash_command
|
|
12
|
+
from .task_validator import TaskValidator, validate_task_invocation
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
# Shell parser
|
|
16
|
+
"ShellCommandParser",
|
|
17
|
+
"get_shell_parser",
|
|
18
|
+
"parse_command",
|
|
19
|
+
# Bash validator
|
|
20
|
+
"BashValidator",
|
|
21
|
+
"validate_bash_command",
|
|
22
|
+
# Task validator
|
|
23
|
+
"TaskValidator",
|
|
24
|
+
"validate_task_invocation",
|
|
25
|
+
]
|