@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,476 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Context Provider for Claude Agent System
|
|
4
|
+
|
|
5
|
+
Generates structured context payloads for agents based on:
|
|
6
|
+
1. Agent contracts (context-contracts.json + cloud overlays)
|
|
7
|
+
2. Universal rules (universal-rules.json)
|
|
8
|
+
3. Historical episodes (episodic memory)
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
python3 context_provider.py <agent_name> [user_task] [--context-file PATH]
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import argparse
|
|
16
|
+
import sys
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Dict, List, Any, Optional
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
from ._paths import resolve_config_dir
|
|
22
|
+
from .surface_router import (
|
|
23
|
+
build_investigation_brief,
|
|
24
|
+
classify_surfaces,
|
|
25
|
+
load_surface_routing_config,
|
|
26
|
+
)
|
|
27
|
+
except ImportError:
|
|
28
|
+
from _paths import resolve_config_dir
|
|
29
|
+
from surface_router import (
|
|
30
|
+
build_investigation_brief,
|
|
31
|
+
classify_surfaces,
|
|
32
|
+
load_surface_routing_config,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Default paths
|
|
36
|
+
DEFAULT_CONTEXT_PATH = Path(".claude/project-context/project-context.json")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# ============================================================================
|
|
40
|
+
# CONTRACTS DIRECTORY RESOLUTION
|
|
41
|
+
# ============================================================================
|
|
42
|
+
|
|
43
|
+
def get_contracts_dir():
|
|
44
|
+
"""Determines the correct contracts directory based on execution context."""
|
|
45
|
+
return resolve_config_dir()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
DEFAULT_CONTRACTS_DIR = get_contracts_dir()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# ============================================================================
|
|
52
|
+
# UNIVERSAL RULES SYSTEM
|
|
53
|
+
# ============================================================================
|
|
54
|
+
|
|
55
|
+
DEFAULT_RULES_FILE = "universal-rules.json"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def load_universal_rules(agent_name: str, rules_file: Optional[Path] = None) -> Dict[str, Any]:
|
|
59
|
+
"""Load universal rules and agent-specific rules from JSON file."""
|
|
60
|
+
if rules_file is None:
|
|
61
|
+
rules_file = get_contracts_dir() / DEFAULT_RULES_FILE
|
|
62
|
+
|
|
63
|
+
if not rules_file.is_file():
|
|
64
|
+
print(f"Warning: Rules file not found: {rules_file}", file=sys.stderr)
|
|
65
|
+
return {"universal": [], "agent_specific": []}
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
with open(rules_file, 'r', encoding='utf-8') as f:
|
|
69
|
+
rules_data = json.load(f)
|
|
70
|
+
|
|
71
|
+
universal = [r["rule"] for r in rules_data.get("rules", {}).get("universal", [])]
|
|
72
|
+
agent_specific = [
|
|
73
|
+
r["rule"]
|
|
74
|
+
for r in rules_data.get("rules", {}).get("agent_specific", {}).get(agent_name, [])
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
total_rules = len(universal) + len(agent_specific)
|
|
78
|
+
if total_rules > 0:
|
|
79
|
+
print(f"Loaded {len(universal)} universal rules, {len(agent_specific)} agent-specific", file=sys.stderr)
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
"universal": universal,
|
|
83
|
+
"agent_specific": agent_specific
|
|
84
|
+
}
|
|
85
|
+
except Exception as e:
|
|
86
|
+
print(f"Warning: Could not load rules: {e}", file=sys.stderr)
|
|
87
|
+
return {"universal": [], "agent_specific": []}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# ============================================================================
|
|
91
|
+
# CLOUD PROVIDER DETECTION
|
|
92
|
+
# ============================================================================
|
|
93
|
+
|
|
94
|
+
def detect_cloud_provider(project_context: Dict[str, Any]) -> str:
|
|
95
|
+
"""Detects the cloud provider from project-context.json.
|
|
96
|
+
|
|
97
|
+
Detection priority:
|
|
98
|
+
1. metadata.cloud_provider (explicit user/scanner setting)
|
|
99
|
+
2. infrastructure.cloud_providers[0].name (v2 scanner section)
|
|
100
|
+
3. metadata.project_id presence -> gcp
|
|
101
|
+
4. Fallback -> gcp
|
|
102
|
+
"""
|
|
103
|
+
metadata = project_context.get("metadata", {})
|
|
104
|
+
if "cloud_provider" in metadata:
|
|
105
|
+
provider = metadata["cloud_provider"].lower()
|
|
106
|
+
if provider == "multi-cloud":
|
|
107
|
+
print("Multi-cloud detected, using GCP contracts as primary", file=sys.stderr)
|
|
108
|
+
return "gcp"
|
|
109
|
+
return provider
|
|
110
|
+
|
|
111
|
+
sections = project_context.get("sections", {})
|
|
112
|
+
|
|
113
|
+
# v2: read from infrastructure.cloud_providers
|
|
114
|
+
infra = sections.get("infrastructure", {})
|
|
115
|
+
if isinstance(infra, dict):
|
|
116
|
+
cloud_providers = infra.get("cloud_providers", [])
|
|
117
|
+
if isinstance(cloud_providers, list) and cloud_providers:
|
|
118
|
+
primary = cloud_providers[0]
|
|
119
|
+
if isinstance(primary, dict):
|
|
120
|
+
name = primary.get("name", "")
|
|
121
|
+
if name:
|
|
122
|
+
provider = name.lower()
|
|
123
|
+
if provider == "multi-cloud":
|
|
124
|
+
return "gcp"
|
|
125
|
+
return provider
|
|
126
|
+
|
|
127
|
+
if "project_id" in metadata:
|
|
128
|
+
return "gcp"
|
|
129
|
+
|
|
130
|
+
print("Could not detect cloud provider, defaulting to GCP", file=sys.stderr)
|
|
131
|
+
return "gcp"
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def load_provider_contracts(cloud_provider: str, contracts_dir: Path = DEFAULT_CONTRACTS_DIR) -> Dict[str, Any]:
|
|
135
|
+
"""
|
|
136
|
+
Loads context contracts using the base+cloud merge strategy.
|
|
137
|
+
|
|
138
|
+
Strategy:
|
|
139
|
+
1. Load base contracts from context-contracts.json (cloud-agnostic)
|
|
140
|
+
2. Load cloud overrides from cloud/{provider}.json and merge (extend) read/write lists
|
|
141
|
+
3. If base contracts missing → error (contracts are the single source of truth)
|
|
142
|
+
"""
|
|
143
|
+
base_file = contracts_dir / "context-contracts.json"
|
|
144
|
+
cloud_file = contracts_dir / "cloud" / f"{cloud_provider}.json"
|
|
145
|
+
|
|
146
|
+
# --- Step 1: Load base contracts ---
|
|
147
|
+
if not base_file.is_file():
|
|
148
|
+
print(f"Error: Contract file not found at {base_file}", file=sys.stderr)
|
|
149
|
+
sys.exit(1)
|
|
150
|
+
|
|
151
|
+
try:
|
|
152
|
+
with open(base_file, 'r', encoding='utf-8') as f:
|
|
153
|
+
base_contracts = json.load(f)
|
|
154
|
+
print(f"Loaded base contracts from {base_file}", file=sys.stderr)
|
|
155
|
+
except json.JSONDecodeError as e:
|
|
156
|
+
print(f"Error: Invalid JSON in {base_file}: {e}", file=sys.stderr)
|
|
157
|
+
sys.exit(1)
|
|
158
|
+
|
|
159
|
+
# --- Step 2: Merge cloud-specific overrides ---
|
|
160
|
+
if cloud_file.is_file():
|
|
161
|
+
try:
|
|
162
|
+
with open(cloud_file, 'r', encoding='utf-8') as f:
|
|
163
|
+
cloud_overrides = json.load(f)
|
|
164
|
+
print(f"Loaded {cloud_provider.upper()} cloud overrides from {cloud_file}", file=sys.stderr)
|
|
165
|
+
|
|
166
|
+
for agent_name, agent_overrides in cloud_overrides.get("agents", {}).items():
|
|
167
|
+
if agent_name in base_contracts.get("agents", {}):
|
|
168
|
+
existing_read = base_contracts["agents"][agent_name].get("read", [])
|
|
169
|
+
existing_write = base_contracts["agents"][agent_name].get("write", [])
|
|
170
|
+
extra_read = [s for s in agent_overrides.get("read", []) if s not in existing_read]
|
|
171
|
+
extra_write = [s for s in agent_overrides.get("write", []) if s not in existing_write]
|
|
172
|
+
base_contracts["agents"][agent_name]["read"] = existing_read + extra_read
|
|
173
|
+
base_contracts["agents"][agent_name]["write"] = existing_write + extra_write
|
|
174
|
+
else:
|
|
175
|
+
base_contracts["agents"][agent_name] = agent_overrides
|
|
176
|
+
|
|
177
|
+
except json.JSONDecodeError as e:
|
|
178
|
+
print(f"Warning: Invalid JSON in {cloud_file}: {e} — skipping cloud overrides", file=sys.stderr)
|
|
179
|
+
else:
|
|
180
|
+
print(f"No cloud overrides found at {cloud_file}, using base contracts only", file=sys.stderr)
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
"version": base_contracts.get("version", "unknown"),
|
|
184
|
+
"provider": cloud_provider,
|
|
185
|
+
"agents": base_contracts.get("agents", {})
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def load_project_context(context_path: Path) -> Dict[str, Any]:
|
|
190
|
+
"""Loads the project context from the specified JSON file."""
|
|
191
|
+
if not context_path.is_file():
|
|
192
|
+
print(f"Error: Context file not found at {context_path}", file=sys.stderr)
|
|
193
|
+
sys.exit(1)
|
|
194
|
+
with open(context_path, 'r', encoding='utf-8') as f:
|
|
195
|
+
return json.load(f)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
# ============================================================================
|
|
199
|
+
# CONTEXT EXTRACTION
|
|
200
|
+
# ============================================================================
|
|
201
|
+
|
|
202
|
+
def get_relevant_sections(
|
|
203
|
+
sections: Dict[str, Any],
|
|
204
|
+
contract_keys: List[str],
|
|
205
|
+
surface_routing: Optional[Dict[str, Any]] = None,
|
|
206
|
+
routing_config: Optional[Dict[str, Any]] = None,
|
|
207
|
+
) -> Dict[str, Any]:
|
|
208
|
+
"""Filter sections by surface relevance, with fallback to all readable sections.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
sections: All available sections from project-context.json.
|
|
212
|
+
contract_keys: The agent's permitted read keys (from context-contracts).
|
|
213
|
+
surface_routing: The routing result from classify_surfaces().
|
|
214
|
+
routing_config: The full surface-routing.json config (has contract_sections per surface).
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
Filtered dict of sections. Falls back to all readable sections when:
|
|
218
|
+
- No surface_routing or routing_config provided
|
|
219
|
+
- No active surfaces detected
|
|
220
|
+
- Surface has no contract_sections defined
|
|
221
|
+
- Intersection of surface sections and agent permissions is empty
|
|
222
|
+
"""
|
|
223
|
+
all_readable = {k: sections[k] for k in contract_keys if k in sections}
|
|
224
|
+
|
|
225
|
+
if not surface_routing or not routing_config:
|
|
226
|
+
return all_readable
|
|
227
|
+
|
|
228
|
+
active_surfaces = surface_routing.get("active_surfaces", [])
|
|
229
|
+
if not active_surfaces:
|
|
230
|
+
return all_readable
|
|
231
|
+
|
|
232
|
+
surfaces_cfg = routing_config.get("surfaces", {})
|
|
233
|
+
|
|
234
|
+
# Collect relevant sections from all active surfaces
|
|
235
|
+
relevant: set = set()
|
|
236
|
+
for surface in active_surfaces:
|
|
237
|
+
surface_config = surfaces_cfg.get(surface, {})
|
|
238
|
+
surface_sections = surface_config.get("contract_sections", [])
|
|
239
|
+
relevant.update(surface_sections)
|
|
240
|
+
|
|
241
|
+
if not relevant:
|
|
242
|
+
# Surfaces have no contract_sections defined -- inject all (fallback)
|
|
243
|
+
return all_readable
|
|
244
|
+
|
|
245
|
+
# Filter: agent permissions AND surface relevance
|
|
246
|
+
filtered = {k: sections[k] for k in contract_keys if k in sections and k in relevant}
|
|
247
|
+
|
|
248
|
+
if not filtered:
|
|
249
|
+
# Nothing matched -- inject all (fallback)
|
|
250
|
+
return all_readable
|
|
251
|
+
|
|
252
|
+
omitted = set(all_readable.keys()) - set(filtered.keys())
|
|
253
|
+
if omitted:
|
|
254
|
+
print(
|
|
255
|
+
f"Surface gating: {len(filtered)} sections injected, "
|
|
256
|
+
f"{len(omitted)} omitted ({', '.join(sorted(omitted))})",
|
|
257
|
+
file=sys.stderr,
|
|
258
|
+
)
|
|
259
|
+
else:
|
|
260
|
+
print(
|
|
261
|
+
f"Surface gating: all {len(filtered)} readable sections match active surfaces",
|
|
262
|
+
file=sys.stderr,
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
return filtered
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def get_contract_context(
|
|
269
|
+
project_context: Dict[str, Any],
|
|
270
|
+
agent_name: str,
|
|
271
|
+
provider_contracts: Dict[str, Any],
|
|
272
|
+
surface_routing: Optional[Dict[str, Any]] = None,
|
|
273
|
+
routing_config: Optional[Dict[str, Any]] = None,
|
|
274
|
+
) -> Dict[str, Any]:
|
|
275
|
+
"""Extracts the contract-defined context sections for a given agent.
|
|
276
|
+
|
|
277
|
+
When surface_routing and routing_config are provided, sections are filtered
|
|
278
|
+
to only those relevant to the active surface(s). Falls back to returning
|
|
279
|
+
all readable sections when routing is unavailable or yields an empty set.
|
|
280
|
+
"""
|
|
281
|
+
agent_contract = provider_contracts.get("agents", {}).get(agent_name)
|
|
282
|
+
if not agent_contract:
|
|
283
|
+
print(f"ERROR: Invalid agent '{agent_name}'. Available: {list(provider_contracts.get('agents', {}).keys())}", file=sys.stderr)
|
|
284
|
+
sys.exit(1)
|
|
285
|
+
|
|
286
|
+
contract_keys = agent_contract.get("read", [])
|
|
287
|
+
|
|
288
|
+
sections = project_context.get("sections", {})
|
|
289
|
+
if not sections:
|
|
290
|
+
raise KeyError("project-context.json must contain a 'sections' object.")
|
|
291
|
+
|
|
292
|
+
return get_relevant_sections(
|
|
293
|
+
sections, contract_keys,
|
|
294
|
+
surface_routing=surface_routing,
|
|
295
|
+
routing_config=routing_config,
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def get_context_update_contract(
|
|
300
|
+
agent_name: str,
|
|
301
|
+
provider_contracts: Dict[str, Any]
|
|
302
|
+
) -> Dict[str, Any]:
|
|
303
|
+
"""Return the SSOT contract agents should use for CONTEXT_UPDATE decisions."""
|
|
304
|
+
agent_contract = provider_contracts.get("agents", {}).get(agent_name)
|
|
305
|
+
if not agent_contract:
|
|
306
|
+
print(f"ERROR: Invalid agent '{agent_name}'. Available: {list(provider_contracts.get('agents', {}).keys())}", file=sys.stderr)
|
|
307
|
+
sys.exit(1)
|
|
308
|
+
|
|
309
|
+
return {
|
|
310
|
+
"readable_sections": agent_contract.get("read", []),
|
|
311
|
+
"writable_sections": agent_contract.get("write", []),
|
|
312
|
+
"source": "config/context-contracts.json + config/cloud/{provider}.json",
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
# ============================================================================
|
|
317
|
+
# EPISODIC MEMORY
|
|
318
|
+
# ============================================================================
|
|
319
|
+
|
|
320
|
+
def load_relevant_episodes(user_task: str, max_episodes: int = 2) -> Dict[str, Any]:
|
|
321
|
+
"""Load relevant historical episodes for the user's task."""
|
|
322
|
+
try:
|
|
323
|
+
index_file = Path(".claude/project-context/episodic-memory/index.json")
|
|
324
|
+
if not index_file.exists():
|
|
325
|
+
return {}
|
|
326
|
+
|
|
327
|
+
with open(index_file) as f:
|
|
328
|
+
index = json.load(f)
|
|
329
|
+
|
|
330
|
+
task_lower = user_task.lower()
|
|
331
|
+
task_words = set(task_lower.split())
|
|
332
|
+
|
|
333
|
+
relevant_episodes = []
|
|
334
|
+
for episode in index.get("episodes", []):
|
|
335
|
+
score = 0.0
|
|
336
|
+
for tag in episode.get("tags", []):
|
|
337
|
+
if tag.lower() in task_lower:
|
|
338
|
+
score += 0.4
|
|
339
|
+
title_words = set(episode.get("title", "").lower().split())
|
|
340
|
+
common_words = task_words & title_words
|
|
341
|
+
if common_words:
|
|
342
|
+
score += 0.3 * (len(common_words) / max(len(title_words), 1))
|
|
343
|
+
|
|
344
|
+
final_score = score * episode.get("relevance_score", 0.5)
|
|
345
|
+
|
|
346
|
+
if final_score > 0.1:
|
|
347
|
+
full_episode = load_full_episode(episode["id"], index_file.parent)
|
|
348
|
+
if full_episode:
|
|
349
|
+
relevant_episodes.append({
|
|
350
|
+
"id": full_episode["id"],
|
|
351
|
+
"title": full_episode["title"],
|
|
352
|
+
"type": full_episode["type"],
|
|
353
|
+
"relevance": final_score,
|
|
354
|
+
"lessons_learned": full_episode.get("lessons_learned", [])[:2],
|
|
355
|
+
"resolution": full_episode.get("resolution", "")[:200]
|
|
356
|
+
})
|
|
357
|
+
|
|
358
|
+
relevant_episodes.sort(key=lambda x: x["relevance"], reverse=True)
|
|
359
|
+
relevant_episodes = relevant_episodes[:max_episodes]
|
|
360
|
+
|
|
361
|
+
if relevant_episodes:
|
|
362
|
+
print(f"Added {len(relevant_episodes)} historical episodes to context", file=sys.stderr)
|
|
363
|
+
return {
|
|
364
|
+
"episodes": relevant_episodes,
|
|
365
|
+
"summary": f"Found {len(relevant_episodes)} relevant historical episodes"
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return {}
|
|
369
|
+
|
|
370
|
+
except Exception as e:
|
|
371
|
+
print(f"Warning: Could not load episodic memory: {e}", file=sys.stderr)
|
|
372
|
+
return {}
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def load_full_episode(episode_id: str, memory_dir: Path) -> Optional[Dict[str, Any]]:
|
|
376
|
+
"""Load full episode details from JSONL file."""
|
|
377
|
+
try:
|
|
378
|
+
episodes_file = memory_dir / "episodes.jsonl"
|
|
379
|
+
if episodes_file.exists():
|
|
380
|
+
with open(episodes_file) as f:
|
|
381
|
+
for line in f:
|
|
382
|
+
try:
|
|
383
|
+
episode = json.loads(line)
|
|
384
|
+
if episode.get("id") == episode_id:
|
|
385
|
+
return episode
|
|
386
|
+
except Exception:
|
|
387
|
+
continue
|
|
388
|
+
except Exception:
|
|
389
|
+
pass
|
|
390
|
+
return None
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
# ============================================================================
|
|
394
|
+
# MAIN FUNCTION
|
|
395
|
+
# ============================================================================
|
|
396
|
+
|
|
397
|
+
def main():
|
|
398
|
+
"""Main function to generate and print the context payload."""
|
|
399
|
+
parser = argparse.ArgumentParser(
|
|
400
|
+
description="Generates a structured context payload for a Claude agent."
|
|
401
|
+
)
|
|
402
|
+
parser.add_argument("agent_name", help="The name of the agent being invoked.")
|
|
403
|
+
parser.add_argument("user_task", nargs="?", default="General inquiry",
|
|
404
|
+
help="The user's task or query for the agent.")
|
|
405
|
+
parser.add_argument(
|
|
406
|
+
"--context-file",
|
|
407
|
+
type=Path,
|
|
408
|
+
default=DEFAULT_CONTEXT_PATH,
|
|
409
|
+
help=f"Path to the project-context.json file. Defaults to '{DEFAULT_CONTEXT_PATH}'"
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
args = parser.parse_args()
|
|
413
|
+
|
|
414
|
+
# Load project context
|
|
415
|
+
project_context = load_project_context(args.context_file)
|
|
416
|
+
|
|
417
|
+
# Detect cloud provider and load contracts
|
|
418
|
+
cloud_provider = detect_cloud_provider(project_context)
|
|
419
|
+
provider_contracts = load_provider_contracts(cloud_provider)
|
|
420
|
+
|
|
421
|
+
# Compute surface routing BEFORE extracting sections so we can gate by surface
|
|
422
|
+
surface_routing_config = load_surface_routing_config()
|
|
423
|
+
surface_routing = classify_surfaces(
|
|
424
|
+
args.user_task,
|
|
425
|
+
current_agent=args.agent_name,
|
|
426
|
+
routing_config=surface_routing_config,
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
# Extract contracted sections (surface-gated when routing is available)
|
|
430
|
+
contract_context = get_contract_context(
|
|
431
|
+
project_context, args.agent_name, provider_contracts,
|
|
432
|
+
surface_routing=surface_routing,
|
|
433
|
+
routing_config=surface_routing_config,
|
|
434
|
+
)
|
|
435
|
+
context_update_contract = get_context_update_contract(args.agent_name, provider_contracts)
|
|
436
|
+
|
|
437
|
+
# Load historical episodes
|
|
438
|
+
historical_context = load_relevant_episodes(args.user_task)
|
|
439
|
+
|
|
440
|
+
# Load universal rules
|
|
441
|
+
rules_context = load_universal_rules(args.agent_name)
|
|
442
|
+
investigation_brief = build_investigation_brief(
|
|
443
|
+
args.user_task,
|
|
444
|
+
args.agent_name,
|
|
445
|
+
contract_context,
|
|
446
|
+
routing_config=surface_routing_config,
|
|
447
|
+
routing=surface_routing,
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
# Build final payload
|
|
451
|
+
final_payload = {
|
|
452
|
+
"project_knowledge": contract_context,
|
|
453
|
+
"write_permissions": context_update_contract,
|
|
454
|
+
"rules": rules_context,
|
|
455
|
+
"surface_routing": surface_routing,
|
|
456
|
+
"investigation_brief": investigation_brief,
|
|
457
|
+
"metadata": {
|
|
458
|
+
"cloud_provider": cloud_provider,
|
|
459
|
+
"contract_version": provider_contracts.get("version", "unknown"),
|
|
460
|
+
"historical_episodes_count": len(historical_context.get("episodes", [])),
|
|
461
|
+
"rules_count": len(rules_context.get("universal", [])) + len(rules_context.get("agent_specific", [])),
|
|
462
|
+
"surface_routing_version": surface_routing_config.get("version", "unknown"),
|
|
463
|
+
"active_surfaces_count": len(surface_routing.get("active_surfaces", [])),
|
|
464
|
+
"surface_routing_confidence": surface_routing.get("confidence", 0.0),
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
# Add historical context if episodes found
|
|
469
|
+
if historical_context:
|
|
470
|
+
final_payload["historical_context"] = historical_context
|
|
471
|
+
|
|
472
|
+
print(json.dumps(final_payload, indent=2))
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
if __name__ == "__main__":
|
|
476
|
+
main()
|