@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,262 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Git Invalidation Module for GAIA-OPS Episodic Memory
|
|
4
|
+
|
|
5
|
+
Scans recent git commits for migration/deprecation patterns and weakens
|
|
6
|
+
matching episodic memories by reducing their relevance_score.
|
|
7
|
+
|
|
8
|
+
Inspired by hippo-memory's invalidation.ts patterns.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import re
|
|
13
|
+
import subprocess
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Dict, List, Any, Optional
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# Patterns that signal a tool/library/concept has changed or been removed.
|
|
19
|
+
# Each entry is a (compiled_regex, group_extractor_fn) tuple.
|
|
20
|
+
# The extractor receives a re.Match and returns a list of affected term strings.
|
|
21
|
+
_PATTERNS: List[tuple] = []
|
|
22
|
+
|
|
23
|
+
def _build_patterns() -> List[tuple]:
|
|
24
|
+
"""Build compiled pattern list once."""
|
|
25
|
+
raw = [
|
|
26
|
+
# "migrate from X" / "migrated from X to Y"
|
|
27
|
+
(
|
|
28
|
+
re.compile(r'\bmigrate(?:d)?\s+from\s+([\w\-./]+)', re.IGNORECASE),
|
|
29
|
+
lambda m: [m.group(1)],
|
|
30
|
+
),
|
|
31
|
+
# "deprecate X" / "deprecated X"
|
|
32
|
+
(
|
|
33
|
+
re.compile(r'\bdeprecate[sd]?\s+([\w\-./]+)', re.IGNORECASE),
|
|
34
|
+
lambda m: [m.group(1)],
|
|
35
|
+
),
|
|
36
|
+
# "remove X" (at least 3-char word to skip noise)
|
|
37
|
+
(
|
|
38
|
+
re.compile(r'\bremove[sd]?\s+([\w\-./]{3,})', re.IGNORECASE),
|
|
39
|
+
lambda m: [m.group(1)],
|
|
40
|
+
),
|
|
41
|
+
# "replace X with Y"
|
|
42
|
+
(
|
|
43
|
+
re.compile(r'\breplace\s+([\w\-./]+)\s+with\s+([\w\-./]+)', re.IGNORECASE),
|
|
44
|
+
lambda m: [m.group(1), m.group(2)],
|
|
45
|
+
),
|
|
46
|
+
# "upgrade from X" / "upgraded from X to Y"
|
|
47
|
+
(
|
|
48
|
+
re.compile(r'\bupgrade[sd]?\s+from\s+([\w\-./]+)', re.IGNORECASE),
|
|
49
|
+
lambda m: [m.group(1)],
|
|
50
|
+
),
|
|
51
|
+
# "drop support for X" / "drop X"
|
|
52
|
+
(
|
|
53
|
+
re.compile(r'\bdrop(?:\s+support(?:\s+for)?)?\s+([\w\-./]{3,})', re.IGNORECASE),
|
|
54
|
+
lambda m: [m.group(1)],
|
|
55
|
+
),
|
|
56
|
+
# version bump: "v3 to v4" or "3.x to 4.x"
|
|
57
|
+
(
|
|
58
|
+
re.compile(r'\bv?(\d+)(?:\.\w+)?\s+to\s+v?(\d+)(?:\.\w+)?\b', re.IGNORECASE),
|
|
59
|
+
lambda m: [f"v{m.group(1)}", f"v{m.group(2)}"],
|
|
60
|
+
),
|
|
61
|
+
]
|
|
62
|
+
return raw
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
_COMPILED_PATTERNS = _build_patterns()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _get_git_log(commit_count: int, cwd: Optional[Path] = None) -> Optional[str]:
|
|
69
|
+
"""
|
|
70
|
+
Run git log and return stdout, or None if not in a git repo.
|
|
71
|
+
"""
|
|
72
|
+
try:
|
|
73
|
+
result = subprocess.run(
|
|
74
|
+
["git", "log", "--oneline", f"-{commit_count}", "--no-merges"],
|
|
75
|
+
capture_output=True,
|
|
76
|
+
text=True,
|
|
77
|
+
cwd=str(cwd) if cwd else None,
|
|
78
|
+
)
|
|
79
|
+
if result.returncode != 0:
|
|
80
|
+
return None
|
|
81
|
+
return result.stdout
|
|
82
|
+
except FileNotFoundError:
|
|
83
|
+
# git not available
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _scan_commits(log_output: str) -> tuple:
|
|
88
|
+
"""
|
|
89
|
+
Scan git log output for migration/deprecation patterns.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
(patterns_detected, affected_terms) where patterns_detected is a list
|
|
93
|
+
of matched pattern strings and affected_terms is a set of lowercased
|
|
94
|
+
affected tool/library names.
|
|
95
|
+
"""
|
|
96
|
+
patterns_detected: List[str] = []
|
|
97
|
+
affected_terms: set = set()
|
|
98
|
+
|
|
99
|
+
for line in log_output.splitlines():
|
|
100
|
+
# Strip the short SHA prefix (7-char hex + space)
|
|
101
|
+
message = re.sub(r'^[0-9a-f]{7,}\s+', '', line).strip()
|
|
102
|
+
if not message:
|
|
103
|
+
continue
|
|
104
|
+
|
|
105
|
+
for regex, extractor in _COMPILED_PATTERNS:
|
|
106
|
+
match = regex.search(message)
|
|
107
|
+
if match:
|
|
108
|
+
terms = extractor(match)
|
|
109
|
+
patterns_detected.append(message)
|
|
110
|
+
for term in terms:
|
|
111
|
+
affected_terms.add(term.lower())
|
|
112
|
+
break # one pattern per commit line is enough
|
|
113
|
+
|
|
114
|
+
return patterns_detected, affected_terms
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _find_index_json() -> Optional[Path]:
|
|
118
|
+
"""
|
|
119
|
+
Locate episodic memory index.json relative to the cwd.
|
|
120
|
+
Checks the canonical path: .claude/project-context/episodic-memory/index.json
|
|
121
|
+
"""
|
|
122
|
+
candidate = Path(".claude/project-context/episodic-memory/index.json")
|
|
123
|
+
if candidate.exists():
|
|
124
|
+
return candidate
|
|
125
|
+
return None
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _episode_mentions_terms(episode: Dict[str, Any], terms: set) -> bool:
|
|
129
|
+
"""
|
|
130
|
+
Return True if the episode's searchable text overlaps with any affected term.
|
|
131
|
+
Checks: keywords, tags, title.
|
|
132
|
+
"""
|
|
133
|
+
if not terms:
|
|
134
|
+
return False
|
|
135
|
+
|
|
136
|
+
searchable: List[str] = []
|
|
137
|
+
|
|
138
|
+
keywords = episode.get("keywords", [])
|
|
139
|
+
if isinstance(keywords, list):
|
|
140
|
+
searchable.extend(str(k).lower() for k in keywords)
|
|
141
|
+
|
|
142
|
+
tags = episode.get("tags", [])
|
|
143
|
+
if isinstance(tags, list):
|
|
144
|
+
searchable.extend(str(t).lower() for t in tags)
|
|
145
|
+
|
|
146
|
+
title = episode.get("title", "")
|
|
147
|
+
if title:
|
|
148
|
+
searchable.extend(re.split(r'\W+', title.lower()))
|
|
149
|
+
|
|
150
|
+
for word in searchable:
|
|
151
|
+
for term in terms:
|
|
152
|
+
# term could be "v3" etc — use substring match for flexibility
|
|
153
|
+
if term and (term in word or word in term):
|
|
154
|
+
return True
|
|
155
|
+
return False
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def check_recent_commits(
|
|
159
|
+
dry_run: bool = True,
|
|
160
|
+
commit_count: int = 20,
|
|
161
|
+
) -> Dict[str, Any]:
|
|
162
|
+
"""
|
|
163
|
+
Scan recent git commits for migration/deprecation patterns and weaken
|
|
164
|
+
matching episodic memories.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
dry_run: If True, identify affected episodes but do NOT modify index.json.
|
|
168
|
+
commit_count: Number of recent commits to scan (default 20).
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
dict with keys:
|
|
172
|
+
affected_episodes: list of episode IDs that match
|
|
173
|
+
patterns_detected: list of commit message strings that matched a pattern
|
|
174
|
+
would_modify: count of episodes that would be (or were) modified
|
|
175
|
+
commits_scanned: number of commits examined
|
|
176
|
+
"""
|
|
177
|
+
empty_result: Dict[str, Any] = {
|
|
178
|
+
"affected_episodes": [],
|
|
179
|
+
"patterns_detected": [],
|
|
180
|
+
"would_modify": 0,
|
|
181
|
+
"commits_scanned": 0,
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
# Step 1: get git log
|
|
185
|
+
log_output = _get_git_log(commit_count)
|
|
186
|
+
if log_output is None:
|
|
187
|
+
return empty_result
|
|
188
|
+
|
|
189
|
+
commits_scanned = len([l for l in log_output.splitlines() if l.strip()])
|
|
190
|
+
|
|
191
|
+
# Step 2: detect patterns
|
|
192
|
+
patterns_detected, affected_terms = _scan_commits(log_output)
|
|
193
|
+
|
|
194
|
+
# If nothing matched, short-circuit
|
|
195
|
+
if not affected_terms:
|
|
196
|
+
return {
|
|
197
|
+
"affected_episodes": [],
|
|
198
|
+
"patterns_detected": patterns_detected,
|
|
199
|
+
"would_modify": 0,
|
|
200
|
+
"commits_scanned": commits_scanned,
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
# Step 3: load index.json
|
|
204
|
+
index_path = _find_index_json()
|
|
205
|
+
if index_path is None:
|
|
206
|
+
return {
|
|
207
|
+
"affected_episodes": [],
|
|
208
|
+
"patterns_detected": patterns_detected,
|
|
209
|
+
"would_modify": 0,
|
|
210
|
+
"commits_scanned": commits_scanned,
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
try:
|
|
214
|
+
index_data = json.loads(index_path.read_text(encoding="utf-8"))
|
|
215
|
+
except (json.JSONDecodeError, OSError):
|
|
216
|
+
return {
|
|
217
|
+
"affected_episodes": [],
|
|
218
|
+
"patterns_detected": patterns_detected,
|
|
219
|
+
"would_modify": 0,
|
|
220
|
+
"commits_scanned": commits_scanned,
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
episodes: List[Dict[str, Any]] = index_data.get("episodes", [])
|
|
224
|
+
|
|
225
|
+
# Step 4: find matching episodes
|
|
226
|
+
affected_ids: List[str] = []
|
|
227
|
+
for ep in episodes:
|
|
228
|
+
if _episode_mentions_terms(ep, affected_terms):
|
|
229
|
+
ep_id = ep.get("id") or ep.get("episode_id", "")
|
|
230
|
+
if ep_id:
|
|
231
|
+
affected_ids.append(ep_id)
|
|
232
|
+
|
|
233
|
+
would_modify = len(affected_ids)
|
|
234
|
+
|
|
235
|
+
# Step 5: apply changes if not dry_run
|
|
236
|
+
if not dry_run and affected_ids:
|
|
237
|
+
affected_set = set(affected_ids)
|
|
238
|
+
for ep in episodes:
|
|
239
|
+
ep_id = ep.get("id") or ep.get("episode_id", "")
|
|
240
|
+
if ep_id in affected_set:
|
|
241
|
+
current_score = ep.get("relevance_score", 1.0)
|
|
242
|
+
ep["relevance_score"] = current_score * 0.5
|
|
243
|
+
|
|
244
|
+
index_data["episodes"] = episodes
|
|
245
|
+
index_path.write_text(
|
|
246
|
+
json.dumps(index_data, indent=2, ensure_ascii=False),
|
|
247
|
+
encoding="utf-8",
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
"affected_episodes": affected_ids,
|
|
252
|
+
"patterns_detected": patterns_detected,
|
|
253
|
+
"would_modify": would_modify,
|
|
254
|
+
"commits_scanned": commits_scanned,
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
if __name__ == "__main__":
|
|
259
|
+
import sys
|
|
260
|
+
dry = "--apply" not in sys.argv
|
|
261
|
+
result = check_recent_commits(dry_run=dry)
|
|
262
|
+
print(json.dumps(result, indent=2))
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Shared path-resolution helpers for Gaia episodic memory.
|
|
3
|
+
|
|
4
|
+
The central function, ``find_highest_claude_root``, walks upward from a
|
|
5
|
+
starting directory and returns the ``.claude/``-bearing ancestor that is
|
|
6
|
+
*closest to HOME* and looks like a Gaia instance — i.e. the top-most one
|
|
7
|
+
that has Gaia-specific subdirectories under ``.claude/``.
|
|
8
|
+
|
|
9
|
+
This prevents two classes of problems:
|
|
10
|
+
1. A nested ``.claude/`` in a sub-repository or dev checkout (e.g.
|
|
11
|
+
``gaia-ops-dev/.claude/``) shadowing the real Gaia instance.
|
|
12
|
+
2. Claude Code's own user settings dir (``$HOME/.claude/``) being mistaken
|
|
13
|
+
for a Gaia instance — it has a ``.claude/`` but no Gaia-specific layout.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Optional
|
|
18
|
+
|
|
19
|
+
# Subdirectories that signal "this .claude/ belongs to a Gaia instance".
|
|
20
|
+
# Claude Code's own $HOME/.claude/ never has hooks/ or agents/.
|
|
21
|
+
_GAIA_MARKERS = ("hooks", "agents", "skills")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _is_gaia_instance(directory: Path) -> bool:
|
|
25
|
+
"""Return True if *directory*/.claude/ looks like a Gaia instance.
|
|
26
|
+
|
|
27
|
+
A directory qualifies as a Gaia instance root if its ``.claude/``
|
|
28
|
+
subdirectory contains at least one of the Gaia-specific marker dirs
|
|
29
|
+
(``hooks/``, ``agents/``, ``skills/``). This distinguishes Gaia
|
|
30
|
+
instance roots from Claude Code's own ``$HOME/.claude/`` which only
|
|
31
|
+
contains user settings.
|
|
32
|
+
|
|
33
|
+
Parameters
|
|
34
|
+
----------
|
|
35
|
+
directory:
|
|
36
|
+
The candidate root directory (the parent of ``.claude/``).
|
|
37
|
+
"""
|
|
38
|
+
claude_dir = directory / ".claude"
|
|
39
|
+
if not claude_dir.is_dir():
|
|
40
|
+
return False
|
|
41
|
+
return any((claude_dir / marker).is_dir() for marker in _GAIA_MARKERS)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def find_highest_claude_root(start: Optional[Path] = None) -> Optional[Path]:
|
|
45
|
+
"""Return the highest Gaia instance root at or above *start*.
|
|
46
|
+
|
|
47
|
+
Walk from *start* (defaults to ``Path.cwd()``) up to ``Path.home()``
|
|
48
|
+
(inclusive). Every directory that has a ``.claude/`` child **and**
|
|
49
|
+
passes the Gaia-instance check (has ``hooks/``, ``agents/``, or
|
|
50
|
+
``skills/`` under ``.claude/``) is collected; the one *closest to HOME*
|
|
51
|
+
(highest in the hierarchy) is returned.
|
|
52
|
+
|
|
53
|
+
If no Gaia-qualified ``.claude/`` is found anywhere in the walk, the
|
|
54
|
+
function falls back to returning the highest plain ``.claude/`` ancestor
|
|
55
|
+
(matching the original semantics), so callers that don't have a full
|
|
56
|
+
Gaia layout still get a reasonable result.
|
|
57
|
+
|
|
58
|
+
Edge cases
|
|
59
|
+
----------
|
|
60
|
+
- If no ``.claude/`` directory is found at all, returns ``None``.
|
|
61
|
+
- If *start* is already above ``Path.home()`` the walk is bounded at
|
|
62
|
+
``Path.home()``; if that yields no candidates the function returns
|
|
63
|
+
``None``.
|
|
64
|
+
- Symlinks in the path are **not** resolved — the walk uses the logical
|
|
65
|
+
path reported by the OS, consistent with the rest of the project.
|
|
66
|
+
|
|
67
|
+
Parameters
|
|
68
|
+
----------
|
|
69
|
+
start:
|
|
70
|
+
Directory from which to begin the upward walk. Defaults to
|
|
71
|
+
``Path.cwd()``.
|
|
72
|
+
|
|
73
|
+
Returns
|
|
74
|
+
-------
|
|
75
|
+
Path or None
|
|
76
|
+
The ancestor path (not the ``.claude/`` child itself) that should
|
|
77
|
+
be used as the Gaia instance root, or ``None`` if no such ancestor
|
|
78
|
+
exists within the walk range.
|
|
79
|
+
"""
|
|
80
|
+
if start is None:
|
|
81
|
+
start = Path.cwd()
|
|
82
|
+
|
|
83
|
+
home = Path.home()
|
|
84
|
+
|
|
85
|
+
# Walk from start up to home (inclusive).
|
|
86
|
+
candidates: list[Path] = [start, *start.parents]
|
|
87
|
+
|
|
88
|
+
highest_gaia: Optional[Path] = None # best match: Gaia-qualified
|
|
89
|
+
highest_any: Optional[Path] = None # fallback: any .claude/
|
|
90
|
+
|
|
91
|
+
for directory in candidates:
|
|
92
|
+
if (directory / ".claude").is_dir():
|
|
93
|
+
highest_any = directory # keep updating — last = highest
|
|
94
|
+
if _is_gaia_instance(directory):
|
|
95
|
+
highest_gaia = directory # keep updating — last = highest
|
|
96
|
+
|
|
97
|
+
# Stop at HOME; don't traverse system directories above it.
|
|
98
|
+
if directory == home:
|
|
99
|
+
break
|
|
100
|
+
|
|
101
|
+
# Prefer a Gaia-qualified root; fall back to any .claude/ if none found.
|
|
102
|
+
return highest_gaia if highest_gaia is not None else highest_any
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Memory Scoring Module for GAIA-OPS
|
|
4
|
+
|
|
5
|
+
Provides strength-based scoring for episodic memories using a decay formula
|
|
6
|
+
inspired by the hippo-memory model. Memories decay over time (recency bias)
|
|
7
|
+
and are strengthened by repeated retrieval (usage reinforcement).
|
|
8
|
+
|
|
9
|
+
Formula:
|
|
10
|
+
strength = base_strength * (0.5 ^ (days_old / half_life))
|
|
11
|
+
* (1 + log(1 + retrieval_count) * boost_factor)
|
|
12
|
+
|
|
13
|
+
Functions:
|
|
14
|
+
score_memory -- compute strength score for a single memory
|
|
15
|
+
rank_episodes -- rank a list of episode dicts by combined relevance + strength
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import math
|
|
19
|
+
import re
|
|
20
|
+
from datetime import datetime, timezone
|
|
21
|
+
from typing import Any, Dict, List, Optional
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
# Core scoring
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
def score_memory(
|
|
29
|
+
days_old: float,
|
|
30
|
+
retrieval_count: int,
|
|
31
|
+
half_life: float = 7.0,
|
|
32
|
+
boost_factor: float = 0.3,
|
|
33
|
+
base_strength: float = 1.0,
|
|
34
|
+
) -> float:
|
|
35
|
+
"""Compute a strength score for a single memory.
|
|
36
|
+
|
|
37
|
+
Parameters
|
|
38
|
+
----------
|
|
39
|
+
days_old:
|
|
40
|
+
Age of the memory in days (>= 0).
|
|
41
|
+
retrieval_count:
|
|
42
|
+
Number of times the memory has been retrieved (>= 0).
|
|
43
|
+
half_life:
|
|
44
|
+
Number of days after which unaccessed memory retains 50% strength.
|
|
45
|
+
Default: 7.0 days.
|
|
46
|
+
boost_factor:
|
|
47
|
+
Multiplier that scales the logarithmic retrieval boost.
|
|
48
|
+
Default: 0.3.
|
|
49
|
+
base_strength:
|
|
50
|
+
Starting strength before decay and boost are applied.
|
|
51
|
+
Default: 1.0.
|
|
52
|
+
|
|
53
|
+
Returns
|
|
54
|
+
-------
|
|
55
|
+
float
|
|
56
|
+
Strength score in the range (0, base_strength * (1 + boost)].
|
|
57
|
+
A higher value means the memory is more relevant/fresh.
|
|
58
|
+
|
|
59
|
+
Examples
|
|
60
|
+
--------
|
|
61
|
+
>>> score_memory(days_old=0, retrieval_count=0)
|
|
62
|
+
1.0
|
|
63
|
+
>>> score_memory(days_old=7, retrieval_count=0) # half-life point
|
|
64
|
+
0.5
|
|
65
|
+
"""
|
|
66
|
+
if half_life <= 0:
|
|
67
|
+
raise ValueError("half_life must be positive")
|
|
68
|
+
if days_old < 0:
|
|
69
|
+
raise ValueError("days_old must be non-negative")
|
|
70
|
+
if retrieval_count < 0:
|
|
71
|
+
raise ValueError("retrieval_count must be non-negative")
|
|
72
|
+
|
|
73
|
+
decay = 0.5 ** (days_old / half_life)
|
|
74
|
+
retrieval_boost = 1.0 + math.log(1 + retrieval_count) * boost_factor
|
|
75
|
+
return base_strength * decay * retrieval_boost
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# ---------------------------------------------------------------------------
|
|
79
|
+
# Episode ranking
|
|
80
|
+
# ---------------------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
def _extract_text(episode: Dict[str, Any]) -> str:
|
|
83
|
+
"""Combine all text fields from an episode into a single string for matching."""
|
|
84
|
+
parts: List[str] = []
|
|
85
|
+
for key in ("prompt", "enriched_prompt", "title", "type"):
|
|
86
|
+
value = episode.get(key)
|
|
87
|
+
if isinstance(value, str) and value:
|
|
88
|
+
parts.append(value)
|
|
89
|
+
tags = episode.get("tags")
|
|
90
|
+
if isinstance(tags, list):
|
|
91
|
+
parts.extend(t for t in tags if isinstance(t, str))
|
|
92
|
+
keywords = episode.get("keywords")
|
|
93
|
+
if isinstance(keywords, list):
|
|
94
|
+
parts.extend(k for k in keywords if isinstance(k, str))
|
|
95
|
+
return " ".join(parts)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _tokenize(text: str) -> set:
|
|
99
|
+
"""Return a set of lowercase word tokens from *text*."""
|
|
100
|
+
return set(re.findall(r"[a-z0-9]+", text.lower()))
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _keyword_overlap(episode_text: str, user_task: str) -> float:
|
|
104
|
+
"""Compute a normalised keyword overlap score in [0, 1].
|
|
105
|
+
|
|
106
|
+
Uses the Jaccard-like formula:
|
|
107
|
+
overlap_count / max(1, len(task_tokens))
|
|
108
|
+
|
|
109
|
+
This measures what fraction of the user's task words appear in the
|
|
110
|
+
episode text, so short tasks aren't penalised.
|
|
111
|
+
"""
|
|
112
|
+
task_tokens = _tokenize(user_task)
|
|
113
|
+
if not task_tokens:
|
|
114
|
+
return 0.0
|
|
115
|
+
episode_tokens = _tokenize(episode_text)
|
|
116
|
+
common = task_tokens & episode_tokens
|
|
117
|
+
return len(common) / len(task_tokens)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _days_old_from_episode(episode: Dict[str, Any]) -> float:
|
|
121
|
+
"""Derive days_old from the episode's 'timestamp' field.
|
|
122
|
+
|
|
123
|
+
Falls back to 0.0 if the field is absent or unparseable.
|
|
124
|
+
"""
|
|
125
|
+
ts = episode.get("timestamp")
|
|
126
|
+
if not ts:
|
|
127
|
+
return 0.0
|
|
128
|
+
try:
|
|
129
|
+
# Support ISO-8601 strings with or without timezone info.
|
|
130
|
+
if ts.endswith("Z"):
|
|
131
|
+
ts = ts[:-1] + "+00:00"
|
|
132
|
+
recorded = datetime.fromisoformat(ts)
|
|
133
|
+
if recorded.tzinfo is None:
|
|
134
|
+
recorded = recorded.replace(tzinfo=timezone.utc)
|
|
135
|
+
now = datetime.now(tz=timezone.utc)
|
|
136
|
+
delta = now - recorded
|
|
137
|
+
return max(0.0, delta.total_seconds() / 86400.0)
|
|
138
|
+
except (ValueError, AttributeError):
|
|
139
|
+
return 0.0
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def rank_episodes(
|
|
143
|
+
episodes: List[Dict[str, Any]],
|
|
144
|
+
user_task: str,
|
|
145
|
+
half_life: float = 7.0,
|
|
146
|
+
boost_factor: float = 0.3,
|
|
147
|
+
) -> List[Dict[str, Any]]:
|
|
148
|
+
"""Rank episodes by combined keyword relevance and memory strength.
|
|
149
|
+
|
|
150
|
+
The composite score is:
|
|
151
|
+
final_score = keyword_overlap * score_memory(...)
|
|
152
|
+
|
|
153
|
+
Episodes with no keyword overlap receive a score of 0 and are still
|
|
154
|
+
included so the caller can decide whether to filter them.
|
|
155
|
+
|
|
156
|
+
Parameters
|
|
157
|
+
----------
|
|
158
|
+
episodes:
|
|
159
|
+
List of episode dicts. Each dict may contain: ``prompt``,
|
|
160
|
+
``enriched_prompt``, ``timestamp``, ``retrieval_count``, ``title``,
|
|
161
|
+
``type``, ``tags``, ``keywords``.
|
|
162
|
+
user_task:
|
|
163
|
+
Free-text description of what the user is trying to accomplish.
|
|
164
|
+
half_life:
|
|
165
|
+
Forwarded to :func:`score_memory`.
|
|
166
|
+
boost_factor:
|
|
167
|
+
Forwarded to :func:`score_memory`.
|
|
168
|
+
|
|
169
|
+
Returns
|
|
170
|
+
-------
|
|
171
|
+
list
|
|
172
|
+
A new list of episode dicts sorted by ``_score`` descending.
|
|
173
|
+
Each returned dict has an additional ``_score`` key (float) for
|
|
174
|
+
inspection and debugging.
|
|
175
|
+
"""
|
|
176
|
+
scored: List[Dict[str, Any]] = []
|
|
177
|
+
for episode in episodes:
|
|
178
|
+
days_old = _days_old_from_episode(episode)
|
|
179
|
+
retrieval_count = int(episode.get("retrieval_count", 0))
|
|
180
|
+
strength = score_memory(
|
|
181
|
+
days_old=days_old,
|
|
182
|
+
retrieval_count=retrieval_count,
|
|
183
|
+
half_life=half_life,
|
|
184
|
+
boost_factor=boost_factor,
|
|
185
|
+
)
|
|
186
|
+
overlap = _keyword_overlap(_extract_text(episode), user_task)
|
|
187
|
+
final_score = overlap * strength
|
|
188
|
+
entry = dict(episode)
|
|
189
|
+
entry["_score"] = final_score
|
|
190
|
+
scored.append(entry)
|
|
191
|
+
|
|
192
|
+
scored.sort(key=lambda e: e["_score"], reverse=True)
|
|
193
|
+
return scored
|