@jaguilar87/gaia 5.0.0-rc.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 +33 -0
- package/.claude-plugin/plugin.json +26 -0
- package/ARCHITECTURE.md +335 -0
- package/CHANGELOG.md +1298 -0
- package/CODE_OF_CONDUCT.md +11 -0
- package/CONTRIBUTING.md +146 -0
- package/INSTALL.md +436 -0
- package/LICENSE +21 -0
- package/README.md +222 -0
- package/SECURITY.md +47 -0
- package/agents/README.md +78 -0
- package/agents/cloud-troubleshooter.md +73 -0
- package/agents/developer.md +65 -0
- package/agents/gaia-operator.md +64 -0
- package/agents/gaia-orchestrator.md +111 -0
- package/agents/gaia-planner.md +53 -0
- package/agents/gaia-system.md +71 -0
- package/agents/gitops-operator.md +61 -0
- package/agents/terraform-architect.md +63 -0
- package/bin/README.md +106 -0
- package/bin/cli/__init__.py +1 -0
- package/bin/cli/approvals.py +740 -0
- package/bin/cli/cleanup.py +562 -0
- package/bin/cli/context.py +283 -0
- package/bin/cli/doctor.py +651 -0
- package/bin/cli/history.py +305 -0
- package/bin/cli/memory.py +483 -0
- package/bin/cli/metrics.py +1068 -0
- package/bin/cli/plans.py +515 -0
- package/bin/cli/status.py +302 -0
- package/bin/cli/update.py +382 -0
- package/bin/gaia +112 -0
- package/bin/gaia-cleanup.js +531 -0
- package/bin/gaia-doctor.js +635 -0
- package/bin/gaia-evidence +126 -0
- package/bin/gaia-history.js +251 -0
- package/bin/gaia-metrics.js +1278 -0
- package/bin/gaia-review.js +269 -0
- package/bin/gaia-scan +44 -0
- package/bin/gaia-scan.py +589 -0
- package/bin/gaia-skills-diagnose.js +929 -0
- package/bin/gaia-status.js +278 -0
- package/bin/gaia-uninstall.js +111 -0
- package/bin/gaia-update.js +919 -0
- package/bin/pre-publish-validate.js +610 -0
- package/bin/python-detect.js +60 -0
- package/bin/validate-sandbox.sh +601 -0
- package/commands/README.md +64 -0
- package/commands/gaia.md +37 -0
- package/commands/scan-project.md +67 -0
- package/config/README.md +71 -0
- package/config/cloud/aws.json +134 -0
- package/config/cloud/gcp.json +139 -0
- package/config/context-contracts.json +158 -0
- package/config/crons-schema.md +81 -0
- package/config/git_standards.json +72 -0
- package/config/surface-routing.json +417 -0
- package/config/universal-rules.json +102 -0
- package/dist/gaia-ops/.claude-plugin/plugin.json +24 -0
- package/dist/gaia-ops/README.md +80 -0
- package/dist/gaia-ops/agents/cloud-troubleshooter.md +73 -0
- package/dist/gaia-ops/agents/developer.md +65 -0
- package/dist/gaia-ops/agents/gaia-operator.md +64 -0
- package/dist/gaia-ops/agents/gaia-orchestrator.md +111 -0
- package/dist/gaia-ops/agents/gaia-planner.md +53 -0
- package/dist/gaia-ops/agents/gaia-system.md +71 -0
- package/dist/gaia-ops/agents/gitops-operator.md +61 -0
- package/dist/gaia-ops/agents/terraform-architect.md +63 -0
- package/dist/gaia-ops/commands/gaia.md +37 -0
- package/dist/gaia-ops/config/README.md +71 -0
- package/dist/gaia-ops/config/cloud/aws.json +134 -0
- package/dist/gaia-ops/config/cloud/gcp.json +139 -0
- package/dist/gaia-ops/config/context-contracts.json +158 -0
- package/dist/gaia-ops/config/crons-schema.md +81 -0
- package/dist/gaia-ops/config/git_standards.json +72 -0
- package/dist/gaia-ops/config/surface-routing.json +417 -0
- package/dist/gaia-ops/config/universal-rules.json +102 -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 +1890 -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 +192 -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 +120 -0
- package/dist/gaia-ops/hooks/modules/agents/state_tracker.py +267 -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 +611 -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/agentic_loop_detector.py +165 -0
- package/dist/gaia-ops/hooks/modules/context/anchor_tracker.py +317 -0
- package/dist/gaia-ops/hooks/modules/context/compact_context_builder.py +218 -0
- package/dist/gaia-ops/hooks/modules/context/context_freshness.py +145 -0
- package/dist/gaia-ops/hooks/modules/context/context_injector.py +558 -0
- package/dist/gaia-ops/hooks/modules/context/context_writer.py +530 -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 +577 -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/memory/__init__.py +8 -0
- package/dist/gaia-ops/hooks/modules/memory/episode_writer.py +216 -0
- package/dist/gaia-ops/hooks/modules/orchestrator/__init__.py +1 -0
- package/dist/gaia-ops/hooks/modules/orchestrator/delegate_mode.py +122 -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 +120 -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 +1638 -0
- package/dist/gaia-ops/hooks/modules/security/approval_messages.py +71 -0
- package/dist/gaia-ops/hooks/modules/security/approval_scopes.py +222 -0
- package/dist/gaia-ops/hooks/modules/security/blocked_commands.py +595 -0
- package/dist/gaia-ops/hooks/modules/security/blocked_message_formatter.py +87 -0
- package/dist/gaia-ops/hooks/modules/security/command_semantics.py +181 -0
- package/dist/gaia-ops/hooks/modules/security/composition_rules.py +547 -0
- package/dist/gaia-ops/hooks/modules/security/flag_classifiers.py +873 -0
- package/dist/gaia-ops/hooks/modules/security/gitops_validator.py +179 -0
- package/dist/gaia-ops/hooks/modules/security/mutative_verbs.py +1131 -0
- package/dist/gaia-ops/hooks/modules/security/network_hosts.py +481 -0
- package/dist/gaia-ops/hooks/modules/security/prompt_validator.py +40 -0
- package/dist/gaia-ops/hooks/modules/security/shell_unwrapper.py +165 -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/pending_scanner.py +174 -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 +160 -0
- package/dist/gaia-ops/hooks/modules/session/session_manager.py +31 -0
- package/dist/gaia-ops/hooks/modules/session/session_registry.py +333 -0
- package/dist/gaia-ops/hooks/modules/tools/__init__.py +29 -0
- package/dist/gaia-ops/hooks/modules/tools/bash_validator.py +1008 -0
- package/dist/gaia-ops/hooks/modules/tools/cloud_pipe_validator.py +231 -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/stage_decomposer.py +315 -0
- package/dist/gaia-ops/hooks/modules/tools/task_validator.py +294 -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_compact.py +60 -0
- package/dist/gaia-ops/hooks/pre_tool_use.py +413 -0
- package/dist/gaia-ops/hooks/session_end_hook.py +77 -0
- package/dist/gaia-ops/hooks/session_start.py +81 -0
- package/dist/gaia-ops/hooks/stop_hook.py +70 -0
- package/dist/gaia-ops/hooks/subagent_start.py +71 -0
- package/dist/gaia-ops/hooks/subagent_stop.py +295 -0
- package/dist/gaia-ops/hooks/task_completed.py +70 -0
- package/dist/gaia-ops/hooks/user_prompt_submit.py +246 -0
- package/dist/gaia-ops/settings.json +72 -0
- package/dist/gaia-ops/skills/README.md +158 -0
- package/dist/gaia-ops/skills/agent-creation/SKILL.md +87 -0
- package/dist/gaia-ops/skills/agent-creation/examples.md +170 -0
- package/dist/gaia-ops/skills/agent-creation/reference.md +191 -0
- package/dist/gaia-ops/skills/agent-protocol/SKILL.md +93 -0
- package/dist/gaia-ops/skills/agent-protocol/examples.md +223 -0
- package/dist/gaia-ops/skills/agent-response/SKILL.md +69 -0
- package/dist/gaia-ops/skills/agentic-loop/SKILL.md +80 -0
- package/dist/gaia-ops/skills/agentic-loop/reference.md +378 -0
- package/dist/gaia-ops/skills/blog-writing/SKILL.md +98 -0
- package/dist/gaia-ops/skills/blog-writing/reference.md +130 -0
- package/dist/gaia-ops/skills/brief-spec/SKILL.md +185 -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 +87 -0
- package/dist/gaia-ops/skills/context-updater/examples.md +71 -0
- package/dist/gaia-ops/skills/developer-patterns/SKILL.md +50 -0
- package/dist/gaia-ops/skills/developer-patterns/reference.md +112 -0
- package/dist/gaia-ops/skills/execution/SKILL.md +99 -0
- package/dist/gaia-ops/skills/fast-queries/SKILL.md +43 -0
- package/dist/gaia-ops/skills/gaia-compact/SKILL.md +74 -0
- package/dist/gaia-ops/skills/gaia-patterns/SKILL.md +108 -0
- package/dist/gaia-ops/skills/gaia-patterns/reference.md +395 -0
- package/dist/gaia-ops/skills/gaia-planner/SKILL.md +37 -0
- package/dist/gaia-ops/skills/gaia-planner/reference.md +107 -0
- package/dist/gaia-ops/skills/gaia-release/SKILL.md +85 -0
- package/dist/gaia-ops/skills/gaia-release/reference.md +92 -0
- package/dist/gaia-ops/skills/gaia-self-check/SKILL.md +114 -0
- package/dist/gaia-ops/skills/gaia-self-check/reference.md +453 -0
- package/dist/gaia-ops/skills/gaia-verify/SKILL.md +77 -0
- package/dist/gaia-ops/skills/gaia-verify/reference.md +80 -0
- package/dist/gaia-ops/skills/git-conventions/SKILL.md +47 -0
- package/dist/gaia-ops/skills/gitops-patterns/SKILL.md +60 -0
- package/dist/gaia-ops/skills/gitops-patterns/reference.md +183 -0
- package/dist/gaia-ops/skills/gmail-policy/SKILL.md +200 -0
- package/dist/gaia-ops/skills/gmail-policy/reference.md +150 -0
- package/dist/gaia-ops/skills/gmail-triage/SKILL.md +100 -0
- package/dist/gaia-ops/skills/gws-setup/SKILL.md +99 -0
- package/dist/gaia-ops/skills/gws-setup/reference.md +73 -0
- package/dist/gaia-ops/skills/investigation/SKILL.md +100 -0
- package/dist/gaia-ops/skills/memory-curation/SKILL.md +83 -0
- package/dist/gaia-ops/skills/memory-search/SKILL.md +88 -0
- package/dist/gaia-ops/skills/orchestrator-approval/SKILL.md +160 -0
- package/dist/gaia-ops/skills/orchestrator-approval/reference.md +174 -0
- package/dist/gaia-ops/skills/pending-approvals/SKILL.md +72 -0
- package/dist/gaia-ops/skills/pending-approvals/reference.md +214 -0
- package/dist/gaia-ops/skills/readme-writing/SKILL.md +71 -0
- package/dist/gaia-ops/skills/readme-writing/reference.md +188 -0
- package/dist/gaia-ops/skills/reference.md +135 -0
- package/dist/gaia-ops/skills/request-approval/SKILL.md +140 -0
- package/dist/gaia-ops/skills/request-approval/examples.md +140 -0
- package/dist/gaia-ops/skills/request-approval/reference.md +57 -0
- package/dist/gaia-ops/skills/schedule-task/SKILL.md +64 -0
- package/dist/gaia-ops/skills/schedule-task/reference.md +233 -0
- package/dist/gaia-ops/skills/security-tiers/SKILL.md +141 -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/session-reflection/SKILL.md +69 -0
- package/dist/gaia-ops/skills/skill-creation/SKILL.md +92 -0
- package/dist/gaia-ops/skills/skill-creation/reference.md +29 -0
- package/dist/gaia-ops/skills/terraform-patterns/SKILL.md +89 -0
- package/dist/gaia-ops/skills/terraform-patterns/reference.md +93 -0
- package/dist/gaia-ops/tools/__init__.py +9 -0
- package/dist/gaia-ops/tools/agentic-loop/decide-status.py +210 -0
- package/dist/gaia-ops/tools/agentic-loop/parse-metric.py +106 -0
- package/dist/gaia-ops/tools/agentic-loop/record-iteration.py +221 -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 +721 -0
- package/dist/gaia-ops/tools/context/context_section_reader.py +342 -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 +264 -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/backfill_fts5.py +107 -0
- package/dist/gaia-ops/tools/memory/conflict_detector.py +295 -0
- package/dist/gaia-ops/tools/memory/episodic.py +1210 -0
- package/dist/gaia-ops/tools/memory/git_invalidator.py +262 -0
- package/dist/gaia-ops/tools/memory/paths.py +102 -0
- package/dist/gaia-ops/tools/memory/scoring.py +193 -0
- package/dist/gaia-ops/tools/memory/search_store.py +375 -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 +349 -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 +686 -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 +270 -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 +24 -0
- package/dist/gaia-security/README.md +90 -0
- package/dist/gaia-security/config/universal-rules.json +102 -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 +1890 -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 +113 -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 +120 -0
- package/dist/gaia-security/hooks/modules/agents/state_tracker.py +267 -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 +611 -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/agentic_loop_detector.py +165 -0
- package/dist/gaia-security/hooks/modules/context/anchor_tracker.py +317 -0
- package/dist/gaia-security/hooks/modules/context/compact_context_builder.py +218 -0
- package/dist/gaia-security/hooks/modules/context/context_freshness.py +145 -0
- package/dist/gaia-security/hooks/modules/context/context_injector.py +558 -0
- package/dist/gaia-security/hooks/modules/context/context_writer.py +530 -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 +577 -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/memory/__init__.py +8 -0
- package/dist/gaia-security/hooks/modules/memory/episode_writer.py +216 -0
- package/dist/gaia-security/hooks/modules/orchestrator/__init__.py +1 -0
- package/dist/gaia-security/hooks/modules/orchestrator/delegate_mode.py +122 -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 +120 -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 +1638 -0
- package/dist/gaia-security/hooks/modules/security/approval_messages.py +71 -0
- package/dist/gaia-security/hooks/modules/security/approval_scopes.py +222 -0
- package/dist/gaia-security/hooks/modules/security/blocked_commands.py +595 -0
- package/dist/gaia-security/hooks/modules/security/blocked_message_formatter.py +87 -0
- package/dist/gaia-security/hooks/modules/security/command_semantics.py +181 -0
- package/dist/gaia-security/hooks/modules/security/composition_rules.py +547 -0
- package/dist/gaia-security/hooks/modules/security/flag_classifiers.py +873 -0
- package/dist/gaia-security/hooks/modules/security/gitops_validator.py +179 -0
- package/dist/gaia-security/hooks/modules/security/mutative_verbs.py +1131 -0
- package/dist/gaia-security/hooks/modules/security/network_hosts.py +481 -0
- package/dist/gaia-security/hooks/modules/security/prompt_validator.py +40 -0
- package/dist/gaia-security/hooks/modules/security/shell_unwrapper.py +165 -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/pending_scanner.py +174 -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 +160 -0
- package/dist/gaia-security/hooks/modules/session/session_manager.py +31 -0
- package/dist/gaia-security/hooks/modules/session/session_registry.py +333 -0
- package/dist/gaia-security/hooks/modules/tools/__init__.py +29 -0
- package/dist/gaia-security/hooks/modules/tools/bash_validator.py +1008 -0
- package/dist/gaia-security/hooks/modules/tools/cloud_pipe_validator.py +231 -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/stage_decomposer.py +315 -0
- package/dist/gaia-security/hooks/modules/tools/task_validator.py +294 -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 +413 -0
- package/dist/gaia-security/hooks/session_end_hook.py +77 -0
- package/dist/gaia-security/hooks/session_start.py +81 -0
- package/dist/gaia-security/hooks/stop_hook.py +70 -0
- package/dist/gaia-security/hooks/user_prompt_submit.py +246 -0
- package/dist/gaia-security/settings.json +58 -0
- package/git-hooks/commit-msg +41 -0
- package/hooks/README.md +100 -0
- package/hooks/adapters/__init__.py +52 -0
- package/hooks/adapters/base.py +219 -0
- package/hooks/adapters/channel.py +17 -0
- package/hooks/adapters/claude_code.py +1890 -0
- package/hooks/adapters/types.py +194 -0
- package/hooks/adapters/utils.py +25 -0
- package/hooks/elicitation_result.py +179 -0
- package/hooks/hooks.json +84 -0
- package/hooks/modules/README.md +189 -0
- package/hooks/modules/__init__.py +15 -0
- package/hooks/modules/agents/__init__.py +29 -0
- package/hooks/modules/agents/contract_validator.py +647 -0
- package/hooks/modules/agents/response_contract.py +496 -0
- package/hooks/modules/agents/skill_injection_verifier.py +120 -0
- package/hooks/modules/agents/state_tracker.py +267 -0
- package/hooks/modules/agents/task_info_builder.py +74 -0
- package/hooks/modules/agents/transcript_analyzer.py +458 -0
- package/hooks/modules/agents/transcript_reader.py +152 -0
- package/hooks/modules/audit/__init__.py +28 -0
- package/hooks/modules/audit/event_detector.py +168 -0
- package/hooks/modules/audit/logger.py +131 -0
- package/hooks/modules/audit/metrics.py +134 -0
- package/hooks/modules/audit/workflow_auditor.py +611 -0
- package/hooks/modules/audit/workflow_recorder.py +296 -0
- package/hooks/modules/context/__init__.py +11 -0
- package/hooks/modules/context/agentic_loop_detector.py +165 -0
- package/hooks/modules/context/anchor_tracker.py +317 -0
- package/hooks/modules/context/compact_context_builder.py +218 -0
- package/hooks/modules/context/context_freshness.py +145 -0
- package/hooks/modules/context/context_injector.py +558 -0
- package/hooks/modules/context/context_writer.py +530 -0
- package/hooks/modules/context/contracts_loader.py +161 -0
- package/hooks/modules/core/__init__.py +40 -0
- package/hooks/modules/core/hook_entry.py +78 -0
- package/hooks/modules/core/paths.py +160 -0
- package/hooks/modules/core/plugin_mode.py +149 -0
- package/hooks/modules/core/plugin_setup.py +577 -0
- package/hooks/modules/core/state.py +179 -0
- package/hooks/modules/core/stdin.py +24 -0
- package/hooks/modules/events/__init__.py +1 -0
- package/hooks/modules/events/event_writer.py +210 -0
- package/hooks/modules/evidence/__init__.py +34 -0
- package/hooks/modules/evidence/assertions.py +137 -0
- package/hooks/modules/evidence/index_writer.py +57 -0
- package/hooks/modules/evidence/loader.py +126 -0
- package/hooks/modules/evidence/runner.py +241 -0
- package/hooks/modules/memory/__init__.py +8 -0
- package/hooks/modules/memory/episode_writer.py +216 -0
- package/hooks/modules/orchestrator/__init__.py +1 -0
- package/hooks/modules/orchestrator/delegate_mode.py +122 -0
- package/hooks/modules/scanning/__init__.py +8 -0
- package/hooks/modules/scanning/scan_trigger.py +84 -0
- package/hooks/modules/security/__init__.py +120 -0
- package/hooks/modules/security/approval_cleanup.py +87 -0
- package/hooks/modules/security/approval_constants.py +23 -0
- package/hooks/modules/security/approval_grants.py +1638 -0
- package/hooks/modules/security/approval_messages.py +71 -0
- package/hooks/modules/security/approval_scopes.py +222 -0
- package/hooks/modules/security/blocked_commands.py +595 -0
- package/hooks/modules/security/blocked_message_formatter.py +87 -0
- package/hooks/modules/security/command_semantics.py +181 -0
- package/hooks/modules/security/composition_rules.py +547 -0
- package/hooks/modules/security/flag_classifiers.py +873 -0
- package/hooks/modules/security/gitops_validator.py +179 -0
- package/hooks/modules/security/mutative_verbs.py +1131 -0
- package/hooks/modules/security/network_hosts.py +481 -0
- package/hooks/modules/security/prompt_validator.py +40 -0
- package/hooks/modules/security/shell_unwrapper.py +165 -0
- package/hooks/modules/security/tiers.py +196 -0
- package/hooks/modules/session/__init__.py +10 -0
- package/hooks/modules/session/pending_scanner.py +174 -0
- package/hooks/modules/session/session_context_writer.py +100 -0
- package/hooks/modules/session/session_event_injector.py +160 -0
- package/hooks/modules/session/session_manager.py +31 -0
- package/hooks/modules/session/session_registry.py +333 -0
- package/hooks/modules/tools/__init__.py +29 -0
- package/hooks/modules/tools/bash_validator.py +1008 -0
- package/hooks/modules/tools/cloud_pipe_validator.py +231 -0
- package/hooks/modules/tools/hook_response.py +55 -0
- package/hooks/modules/tools/shell_parser.py +227 -0
- package/hooks/modules/tools/stage_decomposer.py +315 -0
- package/hooks/modules/tools/task_validator.py +294 -0
- package/hooks/modules/validation/__init__.py +23 -0
- package/hooks/modules/validation/commit_validator.py +380 -0
- package/hooks/post_compact.py +43 -0
- package/hooks/post_tool_use.py +54 -0
- package/hooks/pre_compact.py +60 -0
- package/hooks/pre_tool_use.py +413 -0
- package/hooks/session_end_hook.py +77 -0
- package/hooks/session_start.py +81 -0
- package/hooks/stop_hook.py +70 -0
- package/hooks/subagent_start.py +71 -0
- package/hooks/subagent_stop.py +295 -0
- package/hooks/task_completed.py +70 -0
- package/hooks/user_prompt_submit.py +246 -0
- package/index.js +83 -0
- package/package.json +103 -0
- package/pyproject.toml +32 -0
- package/skills/README.md +158 -0
- package/skills/agent-creation/SKILL.md +87 -0
- package/skills/agent-creation/examples.md +170 -0
- package/skills/agent-creation/reference.md +191 -0
- package/skills/agent-protocol/SKILL.md +93 -0
- package/skills/agent-protocol/examples.md +223 -0
- package/skills/agent-response/SKILL.md +69 -0
- package/skills/agentic-loop/SKILL.md +80 -0
- package/skills/agentic-loop/reference.md +378 -0
- package/skills/blog-writing/SKILL.md +98 -0
- package/skills/blog-writing/reference.md +130 -0
- package/skills/brief-spec/SKILL.md +185 -0
- package/skills/command-execution/SKILL.md +64 -0
- package/skills/command-execution/reference.md +83 -0
- package/skills/context-updater/SKILL.md +87 -0
- package/skills/context-updater/examples.md +71 -0
- package/skills/developer-patterns/SKILL.md +50 -0
- package/skills/developer-patterns/reference.md +112 -0
- package/skills/execution/SKILL.md +99 -0
- package/skills/fast-queries/SKILL.md +43 -0
- package/skills/gaia-compact/SKILL.md +74 -0
- package/skills/gaia-patterns/SKILL.md +108 -0
- package/skills/gaia-patterns/reference.md +395 -0
- package/skills/gaia-planner/SKILL.md +37 -0
- package/skills/gaia-planner/reference.md +107 -0
- package/skills/gaia-release/SKILL.md +85 -0
- package/skills/gaia-release/reference.md +92 -0
- package/skills/gaia-self-check/SKILL.md +114 -0
- package/skills/gaia-self-check/reference.md +453 -0
- package/skills/gaia-verify/SKILL.md +77 -0
- package/skills/gaia-verify/reference.md +80 -0
- package/skills/git-conventions/SKILL.md +47 -0
- package/skills/gitops-patterns/SKILL.md +60 -0
- package/skills/gitops-patterns/reference.md +183 -0
- package/skills/gmail-policy/SKILL.md +200 -0
- package/skills/gmail-policy/reference.md +150 -0
- package/skills/gmail-triage/SKILL.md +100 -0
- package/skills/gws-setup/SKILL.md +99 -0
- package/skills/gws-setup/reference.md +73 -0
- package/skills/investigation/SKILL.md +100 -0
- package/skills/memory-curation/SKILL.md +83 -0
- package/skills/memory-search/SKILL.md +88 -0
- package/skills/orchestrator-approval/SKILL.md +160 -0
- package/skills/orchestrator-approval/reference.md +174 -0
- package/skills/pending-approvals/SKILL.md +72 -0
- package/skills/pending-approvals/reference.md +214 -0
- package/skills/readme-writing/SKILL.md +71 -0
- package/skills/readme-writing/reference.md +188 -0
- package/skills/reference.md +135 -0
- package/skills/request-approval/SKILL.md +140 -0
- package/skills/request-approval/examples.md +140 -0
- package/skills/request-approval/reference.md +57 -0
- package/skills/schedule-task/SKILL.md +64 -0
- package/skills/schedule-task/reference.md +233 -0
- package/skills/security-tiers/SKILL.md +141 -0
- package/skills/security-tiers/destructive-commands-reference.md +623 -0
- package/skills/security-tiers/reference.md +39 -0
- package/skills/session-reflection/SKILL.md +69 -0
- package/skills/skill-creation/SKILL.md +92 -0
- package/skills/skill-creation/reference.md +29 -0
- package/skills/terraform-patterns/SKILL.md +89 -0
- package/skills/terraform-patterns/reference.md +93 -0
- package/templates/README.md +69 -0
- package/templates/managed-settings.template.json +43 -0
- package/tools/__init__.py +9 -0
- package/tools/agentic-loop/decide-status.py +210 -0
- package/tools/agentic-loop/parse-metric.py +106 -0
- package/tools/agentic-loop/record-iteration.py +221 -0
- package/tools/context/README.md +132 -0
- package/tools/context/__init__.py +42 -0
- package/tools/context/_paths.py +20 -0
- package/tools/context/context_provider.py +721 -0
- package/tools/context/context_section_reader.py +342 -0
- package/tools/context/deep_merge.py +159 -0
- package/tools/context/pending_updates.py +760 -0
- package/tools/context/surface_router.py +278 -0
- package/tools/fast-queries/README.md +65 -0
- package/tools/fast-queries/__init__.py +30 -0
- package/tools/fast-queries/appservices/quicktriage_devops_developer.sh +75 -0
- package/tools/fast-queries/cloud/aws/quicktriage_aws_troubleshooter.sh +32 -0
- package/tools/fast-queries/cloud/gcp/quicktriage_gcp_troubleshooter.sh +88 -0
- package/tools/fast-queries/gitops/quicktriage_gitops_operator.sh +48 -0
- package/tools/fast-queries/run_triage.sh +59 -0
- package/tools/fast-queries/terraform/quicktriage_terraform_architect.sh +80 -0
- package/tools/gaia_simulator/__init__.py +33 -0
- package/tools/gaia_simulator/cli.py +354 -0
- package/tools/gaia_simulator/extractor.py +457 -0
- package/tools/gaia_simulator/reporter.py +258 -0
- package/tools/gaia_simulator/routing_simulator.py +334 -0
- package/tools/gaia_simulator/runner.py +539 -0
- package/tools/gaia_simulator/skills_mapper.py +264 -0
- package/tools/memory/README.md +0 -0
- package/tools/memory/__init__.py +20 -0
- package/tools/memory/backfill_fts5.py +107 -0
- package/tools/memory/conflict_detector.py +295 -0
- package/tools/memory/episodic.py +1210 -0
- package/tools/memory/git_invalidator.py +262 -0
- package/tools/memory/paths.py +102 -0
- package/tools/memory/scoring.py +193 -0
- package/tools/memory/search_store.py +375 -0
- package/tools/persist_transcript_analysis.py +85 -0
- package/tools/review/__init__.py +1 -0
- package/tools/review/review_engine.py +157 -0
- package/tools/scan/__init__.py +35 -0
- package/tools/scan/config.py +247 -0
- package/tools/scan/merge.py +212 -0
- package/tools/scan/orchestrator.py +549 -0
- package/tools/scan/registry.py +127 -0
- package/tools/scan/scanners/__init__.py +18 -0
- package/tools/scan/scanners/base.py +137 -0
- package/tools/scan/scanners/environment.py +349 -0
- package/tools/scan/scanners/git.py +570 -0
- package/tools/scan/scanners/infrastructure.py +875 -0
- package/tools/scan/scanners/orchestration.py +600 -0
- package/tools/scan/scanners/stack.py +1085 -0
- package/tools/scan/scanners/tools.py +260 -0
- package/tools/scan/setup.py +686 -0
- package/tools/scan/tests/__init__.py +1 -0
- package/tools/scan/tests/conftest.py +796 -0
- package/tools/scan/tests/test_environment.py +323 -0
- package/tools/scan/tests/test_git.py +419 -0
- package/tools/scan/tests/test_infrastructure.py +382 -0
- package/tools/scan/tests/test_integration.py +920 -0
- package/tools/scan/tests/test_merge.py +269 -0
- package/tools/scan/tests/test_orchestration.py +304 -0
- package/tools/scan/tests/test_stack.py +604 -0
- package/tools/scan/tests/test_tools.py +349 -0
- package/tools/scan/ui.py +624 -0
- package/tools/scan/verify.py +270 -0
- package/tools/scan/walk.py +118 -0
- package/tools/scan/workspace.py +85 -0
- package/tools/validation/README.md +244 -0
- package/tools/validation/__init__.py +17 -0
- package/tools/validation/approval_gate.py +321 -0
- package/tools/validation/validate_skills.py +189 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Hook state management - Share state between pre and post hooks.
|
|
3
|
+
|
|
4
|
+
Uses a temporary file to pass information from pre_tool_use to post_tool_use,
|
|
5
|
+
since they run in separate processes.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
import time
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
from typing import Dict, Any, Optional
|
|
15
|
+
from dataclasses import dataclass, asdict, field
|
|
16
|
+
|
|
17
|
+
from .paths import find_claude_dir
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
# State file location
|
|
22
|
+
STATE_FILE_NAME = ".hooks_state.json"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_session_id() -> str:
|
|
26
|
+
"""Return the current Claude session ID, defaulting to 'default'."""
|
|
27
|
+
return os.environ.get("CLAUDE_SESSION_ID", "default")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class HookState:
|
|
32
|
+
"""
|
|
33
|
+
State passed from pre-hook to post-hook.
|
|
34
|
+
|
|
35
|
+
Attributes:
|
|
36
|
+
tool_name: Name of the tool being executed
|
|
37
|
+
command: Command being executed (for Bash)
|
|
38
|
+
tier: Security tier assigned by pre-hook
|
|
39
|
+
start_time: ISO timestamp when pre-hook ran
|
|
40
|
+
session_id: Current session identifier
|
|
41
|
+
pre_hook_result: Result from pre-hook validation
|
|
42
|
+
metadata: Additional context data
|
|
43
|
+
"""
|
|
44
|
+
tool_name: str = ""
|
|
45
|
+
command: str = ""
|
|
46
|
+
tier: str = "unknown"
|
|
47
|
+
start_time: str = ""
|
|
48
|
+
start_time_epoch: float = 0.0
|
|
49
|
+
session_id: str = ""
|
|
50
|
+
pre_hook_result: str = "allowed"
|
|
51
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
52
|
+
|
|
53
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
54
|
+
"""Convert to dictionary."""
|
|
55
|
+
return asdict(self)
|
|
56
|
+
|
|
57
|
+
@classmethod
|
|
58
|
+
def from_dict(cls, data: Dict[str, Any]) -> "HookState":
|
|
59
|
+
"""Create from dictionary."""
|
|
60
|
+
return cls(
|
|
61
|
+
tool_name=data.get("tool_name", ""),
|
|
62
|
+
command=data.get("command", ""),
|
|
63
|
+
tier=data.get("tier", "unknown"),
|
|
64
|
+
start_time=data.get("start_time", ""),
|
|
65
|
+
start_time_epoch=float(data.get("start_time_epoch", 0.0)),
|
|
66
|
+
session_id=data.get("session_id", ""),
|
|
67
|
+
pre_hook_result=data.get("pre_hook_result", "allowed"),
|
|
68
|
+
metadata=data.get("metadata", {}),
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _get_state_file_path() -> Path:
|
|
73
|
+
"""Get path to state file."""
|
|
74
|
+
claude_dir = find_claude_dir()
|
|
75
|
+
return claude_dir / STATE_FILE_NAME
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def save_hook_state(state: HookState) -> bool:
|
|
79
|
+
"""
|
|
80
|
+
Save hook state for post-hook to read.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
state: HookState to save
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
True if saved successfully
|
|
87
|
+
"""
|
|
88
|
+
try:
|
|
89
|
+
state_file = _get_state_file_path()
|
|
90
|
+
state_file.parent.mkdir(parents=True, exist_ok=True)
|
|
91
|
+
|
|
92
|
+
with open(state_file, "w") as f:
|
|
93
|
+
json.dump(state.to_dict(), f)
|
|
94
|
+
|
|
95
|
+
logger.debug(f"Saved hook state: {state.tool_name} / {state.tier}")
|
|
96
|
+
return True
|
|
97
|
+
|
|
98
|
+
except Exception as e:
|
|
99
|
+
logger.warning(f"Could not save hook state: {e}")
|
|
100
|
+
return False
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def get_hook_state() -> Optional[HookState]:
|
|
104
|
+
"""
|
|
105
|
+
Get hook state saved by pre-hook.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
HookState if found, None otherwise
|
|
109
|
+
"""
|
|
110
|
+
try:
|
|
111
|
+
state_file = _get_state_file_path()
|
|
112
|
+
|
|
113
|
+
if not state_file.exists():
|
|
114
|
+
logger.debug("No hook state file found")
|
|
115
|
+
return None
|
|
116
|
+
|
|
117
|
+
with open(state_file, "r") as f:
|
|
118
|
+
data = json.load(f)
|
|
119
|
+
|
|
120
|
+
return HookState.from_dict(data)
|
|
121
|
+
|
|
122
|
+
except Exception as e:
|
|
123
|
+
logger.warning(f"Could not read hook state: {e}")
|
|
124
|
+
return None
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def clear_hook_state() -> bool:
|
|
128
|
+
"""
|
|
129
|
+
Clear hook state after post-hook has processed it.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
True if cleared successfully
|
|
133
|
+
"""
|
|
134
|
+
try:
|
|
135
|
+
state_file = _get_state_file_path()
|
|
136
|
+
|
|
137
|
+
if state_file.exists():
|
|
138
|
+
state_file.unlink()
|
|
139
|
+
logger.debug("Cleared hook state")
|
|
140
|
+
|
|
141
|
+
return True
|
|
142
|
+
|
|
143
|
+
except Exception as e:
|
|
144
|
+
logger.warning(f"Could not clear hook state: {e}")
|
|
145
|
+
return False
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def create_pre_hook_state(
|
|
149
|
+
tool_name: str,
|
|
150
|
+
command: str = "",
|
|
151
|
+
tier: str = "unknown",
|
|
152
|
+
**metadata
|
|
153
|
+
) -> HookState:
|
|
154
|
+
"""
|
|
155
|
+
Create a new hook state for pre-hook.
|
|
156
|
+
|
|
157
|
+
Convenience function that sets common fields automatically.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
tool_name: Name of the tool
|
|
161
|
+
command: Command being executed
|
|
162
|
+
tier: Security tier
|
|
163
|
+
**metadata: Additional metadata
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
New HookState instance
|
|
167
|
+
"""
|
|
168
|
+
session_id = get_session_id()
|
|
169
|
+
|
|
170
|
+
return HookState(
|
|
171
|
+
tool_name=tool_name,
|
|
172
|
+
command=command,
|
|
173
|
+
tier=tier,
|
|
174
|
+
start_time=datetime.now().isoformat(),
|
|
175
|
+
start_time_epoch=time.time(),
|
|
176
|
+
session_id=session_id,
|
|
177
|
+
pre_hook_result="allowed",
|
|
178
|
+
metadata=metadata,
|
|
179
|
+
)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Stdin availability check for hook entrypoints.
|
|
3
|
+
|
|
4
|
+
Provides a single ``has_stdin_data()`` helper that determines whether the
|
|
5
|
+
current process has data available on stdin. This replaces the duplicate
|
|
6
|
+
implementations that previously lived in ``adapters.utils``.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
import select
|
|
11
|
+
import sys
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def has_stdin_data() -> bool:
|
|
17
|
+
"""Check if there is data available on stdin."""
|
|
18
|
+
if sys.stdin.isatty():
|
|
19
|
+
return False
|
|
20
|
+
try:
|
|
21
|
+
readable, _, _ = select.select([sys.stdin], [], [], 0)
|
|
22
|
+
return bool(readable)
|
|
23
|
+
except Exception:
|
|
24
|
+
return not sys.stdin.isatty()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Event context system for cross-session operational event logging."""
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"""Event writer and reader for the GAIA Event Context system.
|
|
2
|
+
|
|
3
|
+
Provides:
|
|
4
|
+
- EventWriter: append-only JSONL writer with file locking
|
|
5
|
+
- read_events(): read events from last N hours with optional filtering
|
|
6
|
+
- cleanup_old_events(): remove events older than N days
|
|
7
|
+
- Event type constants
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import fcntl
|
|
11
|
+
import json
|
|
12
|
+
import logging
|
|
13
|
+
import os
|
|
14
|
+
from datetime import datetime, timedelta, timezone
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any, Dict, List, Optional
|
|
17
|
+
|
|
18
|
+
from ..core.paths import get_events_dir
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
# ---------------------------------------------------------------------------
|
|
23
|
+
# Event type constants
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
AGENT_DISPATCH = "agent.dispatch"
|
|
27
|
+
AGENT_COMPLETE = "agent.complete"
|
|
28
|
+
COMMAND_EXECUTED = "command.executed"
|
|
29
|
+
SESSION_END = "session.end"
|
|
30
|
+
TRIGGER_SCHEDULED = "trigger.scheduled"
|
|
31
|
+
HEARTBEAT = "heartbeat"
|
|
32
|
+
USER_NOTE = "user.note"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class EventWriter:
|
|
36
|
+
"""Append-only JSONL event writer with file locking.
|
|
37
|
+
|
|
38
|
+
All writes are wrapped in try/except -- events are non-critical and
|
|
39
|
+
must never block the hook pipeline.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(self, events_dir: Optional[Path] = None):
|
|
43
|
+
self.events_dir = events_dir or get_events_dir()
|
|
44
|
+
self.events_file = self.events_dir / "events.jsonl"
|
|
45
|
+
self.lock_file = self.events_dir / "events.jsonl.lock"
|
|
46
|
+
|
|
47
|
+
def write_event(
|
|
48
|
+
self,
|
|
49
|
+
event_type: str,
|
|
50
|
+
source: str,
|
|
51
|
+
agent: str,
|
|
52
|
+
result: str,
|
|
53
|
+
severity: str = "info",
|
|
54
|
+
meta: Optional[Dict[str, Any]] = None,
|
|
55
|
+
) -> None:
|
|
56
|
+
"""Append a single event to the JSONL log.
|
|
57
|
+
|
|
58
|
+
Thread-safe via exclusive file lock. Fails silently on any error
|
|
59
|
+
to avoid disrupting the hook pipeline.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
event_type: Dotted event category (e.g. "agent.dispatch").
|
|
63
|
+
source: Who wrote the event (e.g. "hook").
|
|
64
|
+
agent: Agent involved, or empty string for non-agent events.
|
|
65
|
+
result: Outcome summary string.
|
|
66
|
+
severity: info | warning | error.
|
|
67
|
+
meta: Optional type-specific structured data.
|
|
68
|
+
"""
|
|
69
|
+
try:
|
|
70
|
+
self.events_dir.mkdir(parents=True, exist_ok=True)
|
|
71
|
+
|
|
72
|
+
record: Dict[str, Any] = {
|
|
73
|
+
"ts": datetime.now(timezone.utc).isoformat(),
|
|
74
|
+
"type": event_type,
|
|
75
|
+
"source": source,
|
|
76
|
+
"agent": agent,
|
|
77
|
+
"result": result,
|
|
78
|
+
"severity": severity,
|
|
79
|
+
}
|
|
80
|
+
if meta:
|
|
81
|
+
record["meta"] = meta
|
|
82
|
+
|
|
83
|
+
with open(self.lock_file, "w") as lf:
|
|
84
|
+
fcntl.flock(lf.fileno(), fcntl.LOCK_EX)
|
|
85
|
+
try:
|
|
86
|
+
with open(self.events_file, "a") as f:
|
|
87
|
+
f.write(json.dumps(record, separators=(",", ":")) + "\n")
|
|
88
|
+
finally:
|
|
89
|
+
fcntl.flock(lf.fileno(), fcntl.LOCK_UN)
|
|
90
|
+
|
|
91
|
+
except Exception as exc:
|
|
92
|
+
logger.debug("Event write failed (non-fatal): %s", exc)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def read_events(
|
|
96
|
+
hours: int = 24,
|
|
97
|
+
event_type: Optional[str] = None,
|
|
98
|
+
limit: int = 50,
|
|
99
|
+
events_dir: Optional[Path] = None,
|
|
100
|
+
) -> List[Dict[str, Any]]:
|
|
101
|
+
"""Read recent events from the JSONL log.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
hours: How far back to look (default 24h).
|
|
105
|
+
event_type: Optional filter by event type (exact match).
|
|
106
|
+
limit: Maximum number of events to return.
|
|
107
|
+
events_dir: Override events directory (for testing).
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
List of event dicts, most recent last, capped at *limit*.
|
|
111
|
+
"""
|
|
112
|
+
try:
|
|
113
|
+
edir = events_dir or get_events_dir()
|
|
114
|
+
events_file = edir / "events.jsonl"
|
|
115
|
+
if not events_file.exists():
|
|
116
|
+
return []
|
|
117
|
+
|
|
118
|
+
cutoff = datetime.now(timezone.utc) - timedelta(hours=hours)
|
|
119
|
+
results: List[Dict[str, Any]] = []
|
|
120
|
+
|
|
121
|
+
with open(events_file, "r") as f:
|
|
122
|
+
for line in f:
|
|
123
|
+
line = line.strip()
|
|
124
|
+
if not line:
|
|
125
|
+
continue
|
|
126
|
+
try:
|
|
127
|
+
evt = json.loads(line)
|
|
128
|
+
except json.JSONDecodeError:
|
|
129
|
+
continue
|
|
130
|
+
|
|
131
|
+
# Time filter
|
|
132
|
+
try:
|
|
133
|
+
ts = datetime.fromisoformat(evt.get("ts", ""))
|
|
134
|
+
if ts < cutoff:
|
|
135
|
+
continue
|
|
136
|
+
except (ValueError, TypeError):
|
|
137
|
+
continue
|
|
138
|
+
|
|
139
|
+
# Type filter
|
|
140
|
+
if event_type and evt.get("type") != event_type:
|
|
141
|
+
continue
|
|
142
|
+
|
|
143
|
+
results.append(evt)
|
|
144
|
+
|
|
145
|
+
# Return the most recent events, capped at limit
|
|
146
|
+
return results[-limit:]
|
|
147
|
+
|
|
148
|
+
except Exception as exc:
|
|
149
|
+
logger.debug("Event read failed (non-fatal): %s", exc)
|
|
150
|
+
return []
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def cleanup_old_events(
|
|
154
|
+
days: int = 7,
|
|
155
|
+
events_dir: Optional[Path] = None,
|
|
156
|
+
) -> int:
|
|
157
|
+
"""Remove events older than *days* from the JSONL log.
|
|
158
|
+
|
|
159
|
+
Uses file locking to avoid conflicts with concurrent writers.
|
|
160
|
+
Retains lines that cannot be parsed (conservative).
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
days: Retention window in days (default 7).
|
|
164
|
+
events_dir: Override events directory (for testing).
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
Number of events removed.
|
|
168
|
+
"""
|
|
169
|
+
try:
|
|
170
|
+
edir = events_dir or get_events_dir()
|
|
171
|
+
events_file = edir / "events.jsonl"
|
|
172
|
+
lock_file = edir / "events.jsonl.lock"
|
|
173
|
+
|
|
174
|
+
if not events_file.exists():
|
|
175
|
+
return 0
|
|
176
|
+
|
|
177
|
+
retention_days = int(os.environ.get("GAIA_EVENT_RETENTION_DAYS", str(days)))
|
|
178
|
+
cutoff = datetime.now(timezone.utc) - timedelta(days=retention_days)
|
|
179
|
+
kept: List[str] = []
|
|
180
|
+
removed = 0
|
|
181
|
+
|
|
182
|
+
with open(lock_file, "w") as lf:
|
|
183
|
+
fcntl.flock(lf.fileno(), fcntl.LOCK_EX)
|
|
184
|
+
try:
|
|
185
|
+
with open(events_file, "r") as f:
|
|
186
|
+
for line in f:
|
|
187
|
+
line = line.strip()
|
|
188
|
+
if not line:
|
|
189
|
+
continue
|
|
190
|
+
try:
|
|
191
|
+
evt = json.loads(line)
|
|
192
|
+
ts = datetime.fromisoformat(evt["ts"])
|
|
193
|
+
if ts < cutoff:
|
|
194
|
+
removed += 1
|
|
195
|
+
continue
|
|
196
|
+
except (json.JSONDecodeError, KeyError, ValueError):
|
|
197
|
+
pass # Keep unparseable lines
|
|
198
|
+
kept.append(line)
|
|
199
|
+
|
|
200
|
+
with open(events_file, "w") as f:
|
|
201
|
+
for line in kept:
|
|
202
|
+
f.write(line + "\n")
|
|
203
|
+
finally:
|
|
204
|
+
fcntl.flock(lf.fileno(), fcntl.LOCK_UN)
|
|
205
|
+
|
|
206
|
+
return removed
|
|
207
|
+
|
|
208
|
+
except Exception as exc:
|
|
209
|
+
logger.debug("Event cleanup failed (non-fatal): %s", exc)
|
|
210
|
+
return 0
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Evidence Runner module.
|
|
3
|
+
|
|
4
|
+
Executes evidence.shape blocks declared in brief frontmatter
|
|
5
|
+
and persists results under .claude/project-context/briefs/{feature}/evidence/.
|
|
6
|
+
|
|
7
|
+
Sub-modules:
|
|
8
|
+
- assertions: Declarative DSL for evaluating assert specs against data.
|
|
9
|
+
- loader: Parses brief frontmatter into Evidence dataclass list.
|
|
10
|
+
- runner: Dispatches command and artifact evidence types.
|
|
11
|
+
- index_writer: Generates INDEX.md summary from run results.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from .assertions import evaluate
|
|
15
|
+
from .index_writer import write_index
|
|
16
|
+
from .loader import (
|
|
17
|
+
Evidence,
|
|
18
|
+
InvalidBriefFrontmatter,
|
|
19
|
+
UnknownEvidenceType,
|
|
20
|
+
load_acs,
|
|
21
|
+
)
|
|
22
|
+
from .runner import EvidenceResult, run_artifact, run_command
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
"evaluate",
|
|
26
|
+
"Evidence",
|
|
27
|
+
"InvalidBriefFrontmatter",
|
|
28
|
+
"UnknownEvidenceType",
|
|
29
|
+
"load_acs",
|
|
30
|
+
"EvidenceResult",
|
|
31
|
+
"run_artifact",
|
|
32
|
+
"run_command",
|
|
33
|
+
"write_index",
|
|
34
|
+
]
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Declarative assertion DSL for evidence runner.
|
|
3
|
+
|
|
4
|
+
Contract:
|
|
5
|
+
evaluate(assert_spec: dict, data: Any) -> bool
|
|
6
|
+
|
|
7
|
+
Ops (10 total):
|
|
8
|
+
contains, equals, gte, lte, eq, ne,
|
|
9
|
+
has_field, length_gte, length_eq, matches
|
|
10
|
+
|
|
11
|
+
Path resolution:
|
|
12
|
+
- Dotted segments traverse dict keys ("a.b.c").
|
|
13
|
+
- Root "" (empty path) targets `data` itself.
|
|
14
|
+
- Missing segment -> evaluate returns False (KeyError swallowed).
|
|
15
|
+
|
|
16
|
+
Unknown ops raise ValueError -- fail fast rather than silently return False.
|
|
17
|
+
"""
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import re
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
_ALLOWED_OPS = frozenset({
|
|
24
|
+
"contains",
|
|
25
|
+
"equals",
|
|
26
|
+
"eq",
|
|
27
|
+
"ne",
|
|
28
|
+
"gte",
|
|
29
|
+
"lte",
|
|
30
|
+
"has_field",
|
|
31
|
+
"length_gte",
|
|
32
|
+
"length_eq",
|
|
33
|
+
"matches",
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class _Missing:
|
|
38
|
+
"""Sentinel used by _resolve_path to distinguish 'absent' from 'None'."""
|
|
39
|
+
|
|
40
|
+
_instance: "_Missing | None" = None
|
|
41
|
+
|
|
42
|
+
def __new__(cls) -> "_Missing":
|
|
43
|
+
if cls._instance is None:
|
|
44
|
+
cls._instance = super().__new__(cls)
|
|
45
|
+
return cls._instance
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
_MISSING = _Missing()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _resolve_path(data: Any, path: str) -> Any:
|
|
52
|
+
"""Return the value at `path` or _MISSING if any segment is absent.
|
|
53
|
+
|
|
54
|
+
Empty path -> data itself.
|
|
55
|
+
"""
|
|
56
|
+
if path == "" or path is None:
|
|
57
|
+
return data
|
|
58
|
+
|
|
59
|
+
current: Any = data
|
|
60
|
+
for segment in path.split("."):
|
|
61
|
+
if isinstance(current, dict) and segment in current:
|
|
62
|
+
current = current[segment]
|
|
63
|
+
else:
|
|
64
|
+
return _MISSING
|
|
65
|
+
return current
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def evaluate(assert_spec: dict, data: Any) -> bool:
|
|
69
|
+
"""Evaluate a single assert spec against `data` and return a boolean.
|
|
70
|
+
|
|
71
|
+
assert_spec shape:
|
|
72
|
+
{ op: <str>, path: <dotted str>, value: <literal> }
|
|
73
|
+
- value is optional for has_field.
|
|
74
|
+
"""
|
|
75
|
+
op = assert_spec.get("op")
|
|
76
|
+
if op not in _ALLOWED_OPS:
|
|
77
|
+
raise ValueError(f"Unknown assert op: {op!r}")
|
|
78
|
+
|
|
79
|
+
path = assert_spec.get("path", "")
|
|
80
|
+
value = assert_spec.get("value")
|
|
81
|
+
|
|
82
|
+
resolved = _resolve_path(data, path)
|
|
83
|
+
|
|
84
|
+
# has_field: resolves entirely on presence/absence.
|
|
85
|
+
if op == "has_field":
|
|
86
|
+
return resolved is not _MISSING
|
|
87
|
+
|
|
88
|
+
# Every other op requires a resolved value.
|
|
89
|
+
if resolved is _MISSING:
|
|
90
|
+
return False
|
|
91
|
+
|
|
92
|
+
if op == "contains":
|
|
93
|
+
try:
|
|
94
|
+
return value in resolved
|
|
95
|
+
except TypeError:
|
|
96
|
+
return False
|
|
97
|
+
|
|
98
|
+
if op in ("equals", "eq"):
|
|
99
|
+
return resolved == value
|
|
100
|
+
|
|
101
|
+
if op == "ne":
|
|
102
|
+
return resolved != value
|
|
103
|
+
|
|
104
|
+
if op == "gte":
|
|
105
|
+
try:
|
|
106
|
+
return resolved >= value
|
|
107
|
+
except TypeError:
|
|
108
|
+
return False
|
|
109
|
+
|
|
110
|
+
if op == "lte":
|
|
111
|
+
try:
|
|
112
|
+
return resolved <= value
|
|
113
|
+
except TypeError:
|
|
114
|
+
return False
|
|
115
|
+
|
|
116
|
+
if op == "length_gte":
|
|
117
|
+
try:
|
|
118
|
+
return len(resolved) >= value
|
|
119
|
+
except TypeError:
|
|
120
|
+
return False
|
|
121
|
+
|
|
122
|
+
if op == "length_eq":
|
|
123
|
+
try:
|
|
124
|
+
return len(resolved) == value
|
|
125
|
+
except TypeError:
|
|
126
|
+
return False
|
|
127
|
+
|
|
128
|
+
if op == "matches":
|
|
129
|
+
if not isinstance(resolved, str) or not isinstance(value, str):
|
|
130
|
+
return False
|
|
131
|
+
try:
|
|
132
|
+
return re.search(value, resolved) is not None
|
|
133
|
+
except re.error:
|
|
134
|
+
return False
|
|
135
|
+
|
|
136
|
+
# Defensive; _ALLOWED_OPS was checked up front.
|
|
137
|
+
raise ValueError(f"Unhandled op: {op!r}")
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""
|
|
2
|
+
INDEX.md writer for evidence runs.
|
|
3
|
+
|
|
4
|
+
Contract:
|
|
5
|
+
write_index(evidence_dir: Path, results: list[tuple[str, EvidenceResult]]) -> Path
|
|
6
|
+
|
|
7
|
+
Produces `evidence_dir/INDEX.md` with:
|
|
8
|
+
- One table row per AC: id, type, passed, artifact, error.
|
|
9
|
+
- Deterministic output (same input -> same bytes).
|
|
10
|
+
- Re-execution replaces the file; it does not append.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Iterable, Tuple
|
|
16
|
+
|
|
17
|
+
from .runner import EvidenceResult
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
_HEADER = "# Evidence Index\n\n"
|
|
21
|
+
_TABLE_HEADER = (
|
|
22
|
+
"| AC | Status | Error |\n"
|
|
23
|
+
"|----|--------|-------|\n"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _format_status(passed: bool) -> str:
|
|
28
|
+
return "pass" if passed else "fail"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _format_row(ac_id: str, result: EvidenceResult) -> str:
|
|
32
|
+
status = _format_status(result.passed)
|
|
33
|
+
error = result.error or ""
|
|
34
|
+
# Escape pipes in error messages so the table stays well-formed.
|
|
35
|
+
error = error.replace("|", "\\|")
|
|
36
|
+
return f"| {ac_id} | {status} | {error} |\n"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def write_index(
|
|
40
|
+
evidence_dir: Path,
|
|
41
|
+
results: Iterable[Tuple[str, EvidenceResult]],
|
|
42
|
+
) -> Path:
|
|
43
|
+
"""Render INDEX.md under `evidence_dir` from `results`.
|
|
44
|
+
|
|
45
|
+
Idempotent: identical `results` produce identical bytes.
|
|
46
|
+
Replaces any existing INDEX.md (does not append).
|
|
47
|
+
"""
|
|
48
|
+
evidence_dir = Path(evidence_dir)
|
|
49
|
+
evidence_dir.mkdir(parents=True, exist_ok=True)
|
|
50
|
+
|
|
51
|
+
rows = [_format_row(ac_id, result) for ac_id, result in results]
|
|
52
|
+
|
|
53
|
+
content = _HEADER + _TABLE_HEADER + "".join(rows)
|
|
54
|
+
|
|
55
|
+
index_path = evidence_dir / "INDEX.md"
|
|
56
|
+
index_path.write_text(content, encoding="utf-8")
|
|
57
|
+
return index_path
|