@jaguilar87/gaia-ops 4.4.0 → 4.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +12 -3
- package/ARCHITECTURE.md +9 -8
- package/CHANGELOG.md +34 -0
- package/README.md +43 -11
- package/agents/terraform-architect.md +1 -1
- package/bin/README.md +2 -2
- package/bin/gaia-doctor.js +18 -5
- package/bin/gaia-history.js +0 -1
- package/bin/gaia-metrics.js +2 -2
- package/bin/gaia-scan.py +23 -1
- package/bin/gaia-update.js +346 -54
- package/bin/pre-publish-validate.js +33 -10
- package/commands/gaia.md +37 -0
- package/config/README.md +3 -9
- package/config/context-contracts.json +47 -15
- package/config/surface-routing.json +9 -1
- package/dist/gaia-ops/.claude-plugin/plugin.json +22 -0
- package/dist/gaia-ops/agents/cloud-troubleshooter.md +73 -0
- package/dist/gaia-ops/agents/devops-developer.md +57 -0
- package/dist/gaia-ops/agents/gaia-system.md +58 -0
- package/dist/gaia-ops/agents/gitops-operator.md +60 -0
- package/dist/gaia-ops/agents/speckit-planner.md +71 -0
- package/dist/gaia-ops/agents/terraform-architect.md +60 -0
- package/dist/gaia-ops/commands/gaia.md +37 -0
- package/dist/gaia-ops/config/README.md +58 -0
- package/dist/gaia-ops/config/cloud/aws.json +140 -0
- package/dist/gaia-ops/config/cloud/gcp.json +145 -0
- package/dist/gaia-ops/config/context-contracts.json +131 -0
- package/dist/gaia-ops/config/git_standards.json +72 -0
- package/dist/gaia-ops/config/surface-routing.json +197 -0
- package/dist/gaia-ops/config/universal-rules.json +10 -0
- package/dist/gaia-ops/hooks/adapters/__init__.py +52 -0
- package/dist/gaia-ops/hooks/adapters/base.py +219 -0
- package/dist/gaia-ops/hooks/adapters/channel.py +17 -0
- package/dist/gaia-ops/hooks/adapters/claude_code.py +1477 -0
- package/dist/gaia-ops/hooks/adapters/types.py +194 -0
- package/dist/gaia-ops/hooks/adapters/utils.py +25 -0
- package/dist/gaia-ops/hooks/hooks.json +126 -0
- package/dist/gaia-ops/hooks/modules/__init__.py +15 -0
- package/dist/gaia-ops/hooks/modules/agents/__init__.py +29 -0
- package/dist/gaia-ops/hooks/modules/agents/contract_validator.py +647 -0
- package/dist/gaia-ops/hooks/modules/agents/response_contract.py +496 -0
- package/dist/gaia-ops/hooks/modules/agents/skill_injection_verifier.py +124 -0
- package/dist/gaia-ops/hooks/modules/agents/task_info_builder.py +74 -0
- package/dist/gaia-ops/hooks/modules/agents/transcript_analyzer.py +458 -0
- package/dist/gaia-ops/hooks/modules/agents/transcript_reader.py +152 -0
- package/dist/gaia-ops/hooks/modules/audit/__init__.py +28 -0
- package/dist/gaia-ops/hooks/modules/audit/event_detector.py +168 -0
- package/dist/gaia-ops/hooks/modules/audit/logger.py +131 -0
- package/dist/gaia-ops/hooks/modules/audit/metrics.py +134 -0
- package/dist/gaia-ops/hooks/modules/audit/workflow_auditor.py +576 -0
- package/dist/gaia-ops/hooks/modules/audit/workflow_recorder.py +296 -0
- package/dist/gaia-ops/hooks/modules/context/__init__.py +11 -0
- package/dist/gaia-ops/hooks/modules/context/anchor_tracker.py +317 -0
- package/dist/gaia-ops/hooks/modules/context/compact_context_builder.py +215 -0
- package/dist/gaia-ops/hooks/modules/context/context_cache.py +129 -0
- package/dist/gaia-ops/hooks/modules/context/context_freshness.py +145 -0
- package/dist/gaia-ops/hooks/modules/context/context_injector.py +427 -0
- package/dist/gaia-ops/hooks/modules/context/context_writer.py +518 -0
- package/dist/gaia-ops/hooks/modules/context/contracts_loader.py +161 -0
- package/dist/gaia-ops/hooks/modules/core/__init__.py +40 -0
- package/dist/gaia-ops/hooks/modules/core/hook_entry.py +78 -0
- package/dist/gaia-ops/hooks/modules/core/paths.py +160 -0
- package/dist/gaia-ops/hooks/modules/core/plugin_mode.py +149 -0
- package/dist/gaia-ops/hooks/modules/core/plugin_setup.py +558 -0
- package/dist/gaia-ops/hooks/modules/core/state.py +179 -0
- package/dist/gaia-ops/hooks/modules/core/stdin.py +24 -0
- package/dist/gaia-ops/hooks/modules/events/__init__.py +1 -0
- package/dist/gaia-ops/hooks/modules/events/event_writer.py +210 -0
- package/dist/gaia-ops/hooks/modules/identity/__init__.py +0 -0
- package/dist/gaia-ops/hooks/modules/identity/identity_provider.py +21 -0
- package/dist/gaia-ops/hooks/modules/identity/ops_identity.py +34 -0
- package/dist/gaia-ops/hooks/modules/identity/security_identity.py +10 -0
- package/dist/gaia-ops/hooks/modules/memory/__init__.py +8 -0
- package/dist/gaia-ops/hooks/modules/memory/episode_writer.py +227 -0
- package/dist/gaia-ops/hooks/modules/orchestrator/__init__.py +1 -0
- package/dist/gaia-ops/hooks/modules/orchestrator/delegate_mode.py +128 -0
- package/dist/gaia-ops/hooks/modules/scanning/__init__.py +8 -0
- package/dist/gaia-ops/hooks/modules/scanning/scan_trigger.py +84 -0
- package/dist/gaia-ops/hooks/modules/security/__init__.py +89 -0
- package/dist/gaia-ops/hooks/modules/security/approval_cleanup.py +87 -0
- package/dist/gaia-ops/hooks/modules/security/approval_constants.py +23 -0
- package/dist/gaia-ops/hooks/modules/security/approval_grants.py +912 -0
- package/dist/gaia-ops/hooks/modules/security/approval_messages.py +71 -0
- package/dist/gaia-ops/hooks/modules/security/approval_scopes.py +153 -0
- package/dist/gaia-ops/hooks/modules/security/blocked_commands.py +584 -0
- package/dist/gaia-ops/hooks/modules/security/blocked_message_formatter.py +86 -0
- package/dist/gaia-ops/hooks/modules/security/command_semantics.py +130 -0
- package/dist/gaia-ops/hooks/modules/security/gitops_validator.py +179 -0
- package/dist/gaia-ops/hooks/modules/security/mutative_verbs.py +850 -0
- package/dist/gaia-ops/hooks/modules/security/prompt_validator.py +40 -0
- package/dist/gaia-ops/hooks/modules/security/tiers.py +196 -0
- package/dist/gaia-ops/hooks/modules/session/__init__.py +10 -0
- package/dist/gaia-ops/hooks/modules/session/session_context_writer.py +100 -0
- package/dist/gaia-ops/hooks/modules/session/session_event_injector.py +158 -0
- package/dist/gaia-ops/hooks/modules/session/session_manager.py +31 -0
- package/dist/gaia-ops/hooks/modules/tools/__init__.py +25 -0
- package/dist/gaia-ops/hooks/modules/tools/bash_validator.py +708 -0
- package/dist/gaia-ops/hooks/modules/tools/cloud_pipe_validator.py +181 -0
- package/dist/gaia-ops/hooks/modules/tools/hook_response.py +55 -0
- package/dist/gaia-ops/hooks/modules/tools/shell_parser.py +227 -0
- package/dist/gaia-ops/hooks/modules/tools/task_validator.py +283 -0
- package/dist/gaia-ops/hooks/modules/validation/__init__.py +23 -0
- package/dist/gaia-ops/hooks/modules/validation/commit_validator.py +380 -0
- package/dist/gaia-ops/hooks/post_compact.py +43 -0
- package/dist/gaia-ops/hooks/post_tool_use.py +54 -0
- package/dist/gaia-ops/hooks/pre_tool_use.py +383 -0
- package/dist/gaia-ops/hooks/session_start.py +69 -0
- package/dist/gaia-ops/hooks/stop_hook.py +69 -0
- package/dist/gaia-ops/hooks/subagent_start.py +71 -0
- package/dist/gaia-ops/hooks/subagent_stop.py +288 -0
- package/dist/gaia-ops/hooks/task_completed.py +70 -0
- package/dist/gaia-ops/hooks/user_prompt_submit.py +177 -0
- package/dist/gaia-ops/settings.json +72 -0
- package/dist/gaia-ops/skills/README.md +109 -0
- package/dist/gaia-ops/skills/agent-protocol/SKILL.md +105 -0
- package/dist/gaia-ops/skills/agent-protocol/examples.md +170 -0
- package/dist/gaia-ops/skills/agent-response/SKILL.md +53 -0
- package/dist/gaia-ops/skills/approval/SKILL.md +85 -0
- package/dist/gaia-ops/skills/approval/examples.md +140 -0
- package/dist/gaia-ops/skills/approval/reference.md +57 -0
- package/dist/gaia-ops/skills/command-execution/SKILL.md +64 -0
- package/dist/gaia-ops/skills/command-execution/reference.md +83 -0
- package/dist/gaia-ops/skills/context-updater/SKILL.md +76 -0
- package/dist/gaia-ops/skills/context-updater/examples.md +71 -0
- package/dist/gaia-ops/skills/developer-patterns/SKILL.md +93 -0
- package/dist/gaia-ops/skills/developer-patterns/reference.md +112 -0
- package/dist/gaia-ops/skills/execution/SKILL.md +66 -0
- package/dist/gaia-ops/skills/fast-queries/SKILL.md +47 -0
- package/dist/gaia-ops/skills/gaia-patterns/SKILL.md +92 -0
- package/dist/gaia-ops/skills/gaia-patterns/reference.md +22 -0
- package/dist/gaia-ops/skills/git-conventions/SKILL.md +48 -0
- package/dist/gaia-ops/skills/gitops-patterns/SKILL.md +73 -0
- package/dist/gaia-ops/skills/gitops-patterns/reference.md +183 -0
- package/dist/gaia-ops/skills/investigation/SKILL.md +77 -0
- package/dist/gaia-ops/skills/orchestrator-approval/SKILL.md +64 -0
- package/dist/gaia-ops/skills/reference.md +134 -0
- package/dist/gaia-ops/skills/security-tiers/SKILL.md +61 -0
- package/dist/gaia-ops/skills/security-tiers/destructive-commands-reference.md +623 -0
- package/dist/gaia-ops/skills/security-tiers/reference.md +39 -0
- package/dist/gaia-ops/skills/skill-creation/SKILL.md +119 -0
- package/dist/gaia-ops/skills/specification/SKILL.md +186 -0
- package/dist/gaia-ops/skills/speckit-workflow/SKILL.md +165 -0
- package/dist/gaia-ops/skills/speckit-workflow/reference.md +117 -0
- package/dist/gaia-ops/skills/terraform-patterns/SKILL.md +63 -0
- package/dist/gaia-ops/skills/terraform-patterns/reference.md +93 -0
- package/dist/gaia-ops/speckit/README.md +516 -0
- package/dist/gaia-ops/speckit/scripts/.gitkeep +0 -0
- package/dist/gaia-ops/speckit/templates/adr-template.md +118 -0
- package/dist/gaia-ops/speckit/templates/agent-file-template.md +23 -0
- package/dist/gaia-ops/speckit/templates/plan-template.md +227 -0
- package/dist/gaia-ops/speckit/templates/spec-template.md +140 -0
- package/dist/gaia-ops/speckit/templates/tasks-template.md +257 -0
- package/dist/gaia-ops/tools/context/README.md +132 -0
- package/dist/gaia-ops/tools/context/__init__.py +42 -0
- package/dist/gaia-ops/tools/context/_paths.py +20 -0
- package/dist/gaia-ops/tools/context/context_provider.py +476 -0
- package/dist/gaia-ops/tools/context/context_section_reader.py +330 -0
- package/dist/gaia-ops/tools/context/deep_merge.py +159 -0
- package/dist/gaia-ops/tools/context/pending_updates.py +760 -0
- package/dist/gaia-ops/tools/context/surface_router.py +278 -0
- package/dist/gaia-ops/tools/fast-queries/README.md +65 -0
- package/dist/gaia-ops/tools/fast-queries/__init__.py +30 -0
- package/dist/gaia-ops/tools/fast-queries/appservices/quicktriage_devops_developer.sh +75 -0
- package/dist/gaia-ops/tools/fast-queries/cloud/aws/quicktriage_aws_troubleshooter.sh +32 -0
- package/dist/gaia-ops/tools/fast-queries/cloud/gcp/quicktriage_gcp_troubleshooter.sh +88 -0
- package/dist/gaia-ops/tools/fast-queries/gitops/quicktriage_gitops_operator.sh +48 -0
- package/dist/gaia-ops/tools/fast-queries/run_triage.sh +59 -0
- package/dist/gaia-ops/tools/fast-queries/terraform/quicktriage_terraform_architect.sh +80 -0
- package/dist/gaia-ops/tools/gaia_simulator/__init__.py +33 -0
- package/dist/gaia-ops/tools/gaia_simulator/cli.py +354 -0
- package/dist/gaia-ops/tools/gaia_simulator/extractor.py +457 -0
- package/dist/gaia-ops/tools/gaia_simulator/reporter.py +258 -0
- package/dist/gaia-ops/tools/gaia_simulator/routing_simulator.py +334 -0
- package/dist/gaia-ops/tools/gaia_simulator/runner.py +539 -0
- package/dist/gaia-ops/tools/gaia_simulator/skills_mapper.py +262 -0
- package/dist/gaia-ops/tools/memory/README.md +0 -0
- package/dist/gaia-ops/tools/memory/__init__.py +20 -0
- package/dist/gaia-ops/tools/memory/episodic.py +1196 -0
- package/dist/gaia-ops/tools/persist_transcript_analysis.py +85 -0
- package/dist/gaia-ops/tools/review/__init__.py +1 -0
- package/dist/gaia-ops/tools/review/review_engine.py +157 -0
- package/dist/gaia-ops/tools/scan/__init__.py +35 -0
- package/dist/gaia-ops/tools/scan/config.py +247 -0
- package/dist/gaia-ops/tools/scan/merge.py +212 -0
- package/dist/gaia-ops/tools/scan/orchestrator.py +549 -0
- package/dist/gaia-ops/tools/scan/registry.py +127 -0
- package/dist/gaia-ops/tools/scan/scanners/__init__.py +18 -0
- package/dist/gaia-ops/tools/scan/scanners/base.py +137 -0
- package/dist/gaia-ops/tools/scan/scanners/environment.py +324 -0
- package/dist/gaia-ops/tools/scan/scanners/git.py +570 -0
- package/dist/gaia-ops/tools/scan/scanners/infrastructure.py +875 -0
- package/dist/gaia-ops/tools/scan/scanners/orchestration.py +600 -0
- package/dist/gaia-ops/tools/scan/scanners/stack.py +1085 -0
- package/dist/gaia-ops/tools/scan/scanners/tools.py +260 -0
- package/dist/gaia-ops/tools/scan/setup.py +753 -0
- package/dist/gaia-ops/tools/scan/tests/__init__.py +1 -0
- package/dist/gaia-ops/tools/scan/tests/conftest.py +796 -0
- package/dist/gaia-ops/tools/scan/tests/test_environment.py +323 -0
- package/dist/gaia-ops/tools/scan/tests/test_git.py +419 -0
- package/dist/gaia-ops/tools/scan/tests/test_infrastructure.py +382 -0
- package/dist/gaia-ops/tools/scan/tests/test_integration.py +920 -0
- package/dist/gaia-ops/tools/scan/tests/test_merge.py +269 -0
- package/dist/gaia-ops/tools/scan/tests/test_orchestration.py +304 -0
- package/dist/gaia-ops/tools/scan/tests/test_stack.py +604 -0
- package/dist/gaia-ops/tools/scan/tests/test_tools.py +349 -0
- package/dist/gaia-ops/tools/scan/ui.py +624 -0
- package/dist/gaia-ops/tools/scan/verify.py +266 -0
- package/dist/gaia-ops/tools/scan/walk.py +118 -0
- package/dist/gaia-ops/tools/scan/workspace.py +85 -0
- package/dist/gaia-ops/tools/validation/README.md +244 -0
- package/dist/gaia-ops/tools/validation/__init__.py +17 -0
- package/dist/gaia-ops/tools/validation/approval_gate.py +321 -0
- package/dist/gaia-ops/tools/validation/validate_skills.py +189 -0
- package/dist/gaia-security/.claude-plugin/plugin.json +22 -0
- package/dist/gaia-security/config/universal-rules.json +10 -0
- package/dist/gaia-security/hooks/adapters/__init__.py +52 -0
- package/dist/gaia-security/hooks/adapters/base.py +219 -0
- package/dist/gaia-security/hooks/adapters/channel.py +17 -0
- package/dist/gaia-security/hooks/adapters/claude_code.py +1477 -0
- package/dist/gaia-security/hooks/adapters/types.py +194 -0
- package/dist/gaia-security/hooks/adapters/utils.py +25 -0
- package/dist/gaia-security/hooks/hooks.json +57 -0
- package/dist/gaia-security/hooks/modules/__init__.py +15 -0
- package/dist/gaia-security/hooks/modules/agents/__init__.py +29 -0
- package/dist/gaia-security/hooks/modules/agents/contract_validator.py +647 -0
- package/dist/gaia-security/hooks/modules/agents/response_contract.py +496 -0
- package/dist/gaia-security/hooks/modules/agents/skill_injection_verifier.py +124 -0
- package/dist/gaia-security/hooks/modules/agents/task_info_builder.py +74 -0
- package/dist/gaia-security/hooks/modules/agents/transcript_analyzer.py +458 -0
- package/dist/gaia-security/hooks/modules/agents/transcript_reader.py +152 -0
- package/dist/gaia-security/hooks/modules/audit/__init__.py +28 -0
- package/dist/gaia-security/hooks/modules/audit/event_detector.py +168 -0
- package/dist/gaia-security/hooks/modules/audit/logger.py +131 -0
- package/dist/gaia-security/hooks/modules/audit/metrics.py +134 -0
- package/dist/gaia-security/hooks/modules/audit/workflow_auditor.py +576 -0
- package/dist/gaia-security/hooks/modules/audit/workflow_recorder.py +296 -0
- package/dist/gaia-security/hooks/modules/context/__init__.py +11 -0
- package/dist/gaia-security/hooks/modules/context/anchor_tracker.py +317 -0
- package/dist/gaia-security/hooks/modules/context/compact_context_builder.py +215 -0
- package/dist/gaia-security/hooks/modules/context/context_cache.py +129 -0
- package/dist/gaia-security/hooks/modules/context/context_freshness.py +145 -0
- package/dist/gaia-security/hooks/modules/context/context_injector.py +427 -0
- package/dist/gaia-security/hooks/modules/context/context_writer.py +518 -0
- package/dist/gaia-security/hooks/modules/context/contracts_loader.py +161 -0
- package/dist/gaia-security/hooks/modules/core/__init__.py +40 -0
- package/dist/gaia-security/hooks/modules/core/hook_entry.py +78 -0
- package/dist/gaia-security/hooks/modules/core/paths.py +160 -0
- package/dist/gaia-security/hooks/modules/core/plugin_mode.py +149 -0
- package/dist/gaia-security/hooks/modules/core/plugin_setup.py +558 -0
- package/dist/gaia-security/hooks/modules/core/state.py +179 -0
- package/dist/gaia-security/hooks/modules/core/stdin.py +24 -0
- package/dist/gaia-security/hooks/modules/events/__init__.py +1 -0
- package/dist/gaia-security/hooks/modules/events/event_writer.py +210 -0
- package/dist/gaia-security/hooks/modules/identity/__init__.py +0 -0
- package/dist/gaia-security/hooks/modules/identity/identity_provider.py +21 -0
- package/dist/gaia-security/hooks/modules/identity/ops_identity.py +34 -0
- package/dist/gaia-security/hooks/modules/identity/security_identity.py +10 -0
- package/dist/gaia-security/hooks/modules/memory/__init__.py +8 -0
- package/dist/gaia-security/hooks/modules/memory/episode_writer.py +227 -0
- package/dist/gaia-security/hooks/modules/orchestrator/__init__.py +1 -0
- package/dist/gaia-security/hooks/modules/orchestrator/delegate_mode.py +128 -0
- package/dist/gaia-security/hooks/modules/scanning/__init__.py +8 -0
- package/dist/gaia-security/hooks/modules/scanning/scan_trigger.py +84 -0
- package/dist/gaia-security/hooks/modules/security/__init__.py +89 -0
- package/dist/gaia-security/hooks/modules/security/approval_cleanup.py +87 -0
- package/dist/gaia-security/hooks/modules/security/approval_constants.py +23 -0
- package/dist/gaia-security/hooks/modules/security/approval_grants.py +912 -0
- package/dist/gaia-security/hooks/modules/security/approval_messages.py +71 -0
- package/dist/gaia-security/hooks/modules/security/approval_scopes.py +153 -0
- package/dist/gaia-security/hooks/modules/security/blocked_commands.py +584 -0
- package/dist/gaia-security/hooks/modules/security/blocked_message_formatter.py +86 -0
- package/dist/gaia-security/hooks/modules/security/command_semantics.py +130 -0
- package/dist/gaia-security/hooks/modules/security/gitops_validator.py +179 -0
- package/dist/gaia-security/hooks/modules/security/mutative_verbs.py +850 -0
- package/dist/gaia-security/hooks/modules/security/prompt_validator.py +40 -0
- package/dist/gaia-security/hooks/modules/security/tiers.py +196 -0
- package/dist/gaia-security/hooks/modules/session/__init__.py +10 -0
- package/dist/gaia-security/hooks/modules/session/session_context_writer.py +100 -0
- package/dist/gaia-security/hooks/modules/session/session_event_injector.py +158 -0
- package/dist/gaia-security/hooks/modules/session/session_manager.py +31 -0
- package/dist/gaia-security/hooks/modules/tools/__init__.py +25 -0
- package/dist/gaia-security/hooks/modules/tools/bash_validator.py +708 -0
- package/dist/gaia-security/hooks/modules/tools/cloud_pipe_validator.py +181 -0
- package/dist/gaia-security/hooks/modules/tools/hook_response.py +55 -0
- package/dist/gaia-security/hooks/modules/tools/shell_parser.py +227 -0
- package/dist/gaia-security/hooks/modules/tools/task_validator.py +283 -0
- package/dist/gaia-security/hooks/modules/validation/__init__.py +23 -0
- package/dist/gaia-security/hooks/modules/validation/commit_validator.py +380 -0
- package/dist/gaia-security/hooks/post_tool_use.py +54 -0
- package/dist/gaia-security/hooks/pre_tool_use.py +383 -0
- package/dist/gaia-security/hooks/session_start.py +69 -0
- package/dist/gaia-security/hooks/stop_hook.py +69 -0
- package/dist/gaia-security/hooks/user_prompt_submit.py +177 -0
- package/dist/gaia-security/settings.json +58 -0
- package/git-hooks/commit-msg +41 -0
- package/hooks/README.md +8 -6
- package/hooks/adapters/channel.py +0 -25
- package/hooks/adapters/claude_code.py +364 -125
- package/hooks/elicitation_result.py +132 -0
- package/hooks/hooks.json +10 -1
- package/hooks/modules/README.md +3 -2
- package/hooks/modules/agents/contract_validator.py +3 -51
- package/hooks/modules/agents/response_contract.py +4 -8
- package/hooks/modules/agents/transcript_reader.py +4 -5
- package/hooks/modules/audit/__init__.py +4 -6
- package/hooks/modules/audit/event_detector.py +0 -2
- package/hooks/modules/audit/metrics.py +108 -187
- package/hooks/modules/audit/workflow_auditor.py +0 -4
- package/hooks/modules/audit/workflow_recorder.py +0 -5
- package/hooks/modules/context/compact_context_builder.py +1 -0
- package/hooks/modules/context/context_cache.py +129 -0
- package/hooks/modules/context/context_injector.py +18 -40
- package/hooks/modules/context/context_writer.py +1 -25
- package/hooks/modules/context/contracts_loader.py +7 -10
- package/hooks/modules/core/hook_entry.py +1 -0
- package/hooks/modules/core/paths.py +12 -13
- package/hooks/modules/core/plugin_mode.py +74 -4
- package/hooks/modules/core/plugin_setup.py +395 -23
- package/hooks/modules/events/__init__.py +1 -0
- package/hooks/modules/events/event_writer.py +210 -0
- package/hooks/modules/identity/ops_identity.py +18 -27
- package/hooks/modules/memory/episode_writer.py +1 -6
- package/hooks/modules/orchestrator/__init__.py +1 -0
- package/hooks/modules/orchestrator/delegate_mode.py +128 -0
- package/hooks/modules/security/__init__.py +2 -4
- package/hooks/modules/security/approval_constants.py +5 -1
- package/hooks/modules/security/approval_grants.py +189 -6
- package/hooks/modules/security/approval_messages.py +9 -21
- package/hooks/modules/security/blocked_commands.py +98 -34
- package/hooks/modules/security/command_semantics.py +0 -4
- package/hooks/modules/security/gitops_validator.py +1 -11
- package/hooks/modules/security/mutative_verbs.py +179 -38
- package/hooks/modules/security/tiers.py +1 -19
- package/hooks/modules/session/session_event_injector.py +1 -25
- package/hooks/modules/tools/bash_validator.py +310 -94
- package/hooks/modules/tools/shell_parser.py +0 -1
- package/hooks/modules/tools/task_validator.py +9 -29
- package/hooks/post_tool_use.py +0 -72
- package/hooks/pre_tool_use.py +42 -102
- package/hooks/session_start.py +4 -2
- package/hooks/subagent_start.py +6 -2
- package/hooks/subagent_stop.py +1 -13
- package/hooks/user_prompt_submit.py +119 -37
- package/index.js +1 -1
- package/package.json +5 -3
- package/skills/README.md +3 -5
- package/skills/agent-protocol/SKILL.md +17 -16
- package/skills/agent-protocol/examples.md +6 -6
- package/skills/agent-response/SKILL.md +11 -14
- package/skills/approval/SKILL.md +28 -13
- package/skills/approval/reference.md +2 -2
- package/skills/execution/SKILL.md +1 -1
- package/skills/gaia-patterns/SKILL.md +2 -3
- package/skills/orchestrator-approval/SKILL.md +22 -50
- package/skills/security-tiers/SKILL.md +1 -1
- package/templates/README.md +9 -9
- package/templates/managed-settings.template.json +43 -0
- package/tools/gaia_simulator/runner.py +34 -1
- package/tools/scan/orchestrator.py +13 -0
- package/tools/scan/scanners/base.py +8 -0
- package/tools/scan/scanners/git.py +78 -0
- package/tools/scan/scanners/infrastructure.py +65 -0
- package/tools/scan/scanners/stack.py +110 -0
- package/tools/scan/setup.py +120 -13
- package/tools/scan/workspace.py +85 -0
- package/config/context-contracts.aws.json +0 -42
- package/config/context-contracts.gcp.json +0 -39
- package/skills/project-dispatch/SKILL.md +0 -34
- package/templates/settings.template.json +0 -226
package/bin/gaia-update.js
CHANGED
|
@@ -7,13 +7,21 @@
|
|
|
7
7
|
* Also available as: npx gaia-update
|
|
8
8
|
*
|
|
9
9
|
* Behavior:
|
|
10
|
-
* - First-time install (.claude/ doesn't exist):
|
|
10
|
+
* - First-time install (.claude/ doesn't exist):
|
|
11
|
+
* 1. Check Python 3 is available
|
|
12
|
+
* 2. Run gaia-scan --npm-postinstall to create .claude/, symlinks, settings, project-context
|
|
13
|
+
* 3. Create plugin-registry.json
|
|
14
|
+
* 4. Merge permissions into settings.local.json
|
|
15
|
+
* 5. Merge hooks into settings.local.json
|
|
16
|
+
* 6. Fall through to verification
|
|
11
17
|
* - Update (.claude/ exists):
|
|
12
18
|
* 1. Show version transition (previous → current)
|
|
13
|
-
* 2. settings.json:
|
|
14
|
-
* 3.
|
|
15
|
-
* 4.
|
|
16
|
-
* 5.
|
|
19
|
+
* 2. settings.json: create only if missing (non-invasive, never overwrites)
|
|
20
|
+
* 3. Merge permissions + env vars into settings.local.json (union, preserves user config)
|
|
21
|
+
* 4. Merge hooks from hooks.json into settings.local.json (npm mode requires this)
|
|
22
|
+
* 5. Symlinks: recreate if missing, fix broken ones
|
|
23
|
+
* 5. Verify: hooks, python, project-context, config files
|
|
24
|
+
* 6. Report: summary with any issues found
|
|
17
25
|
*
|
|
18
26
|
* Usage:
|
|
19
27
|
* npm update @jaguilar87/gaia-ops # Automatic via postinstall
|
|
@@ -72,19 +80,25 @@ async function readPackageVersion(path) {
|
|
|
72
80
|
// ============================================================================
|
|
73
81
|
|
|
74
82
|
async function updateSettingsJson() {
|
|
75
|
-
const spinner = ora('
|
|
83
|
+
const spinner = ora('Checking settings.json...').start();
|
|
76
84
|
try {
|
|
77
|
-
const templatePath = join(__dirname, '../templates/settings.template.json');
|
|
78
85
|
const settingsPath = join(CWD, '.claude', 'settings.json');
|
|
79
86
|
|
|
80
|
-
if (!existsSync(
|
|
81
|
-
spinner.info('Skipped');
|
|
87
|
+
if (!existsSync(join(CWD, '.claude'))) {
|
|
88
|
+
spinner.info('Skipped (.claude/ not found)');
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Non-invasive: only create if missing. Never overwrite.
|
|
93
|
+
// Hooks come from hooks.json (auto-discovered via symlink).
|
|
94
|
+
// Env vars and permissions live in settings.local.json.
|
|
95
|
+
if (existsSync(settingsPath)) {
|
|
96
|
+
spinner.succeed('settings.json already exists (not overwriting)');
|
|
82
97
|
return false;
|
|
83
98
|
}
|
|
84
99
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
spinner.succeed('settings.json updated from template');
|
|
100
|
+
await fs.writeFile(settingsPath, '{}\n');
|
|
101
|
+
spinner.succeed('settings.json created (minimal — hooks from hooks.json)');
|
|
88
102
|
return true;
|
|
89
103
|
} catch (error) {
|
|
90
104
|
spinner.fail(`settings.json: ${error.message}`);
|
|
@@ -92,6 +106,223 @@ async function updateSettingsJson() {
|
|
|
92
106
|
}
|
|
93
107
|
}
|
|
94
108
|
|
|
109
|
+
async function updateLocalPermissions() {
|
|
110
|
+
const spinner = ora('Merging permissions into settings.local.json...').start();
|
|
111
|
+
try {
|
|
112
|
+
const claudeDir = join(CWD, '.claude');
|
|
113
|
+
const localPath = join(claudeDir, 'settings.local.json');
|
|
114
|
+
|
|
115
|
+
if (!existsSync(claudeDir)) {
|
|
116
|
+
spinner.info('Skipped (.claude/ not found)');
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Load permissions from plugin_setup.py — the single source of truth.
|
|
121
|
+
// We use ast.literal_eval to extract the constants without importing
|
|
122
|
+
// the module (which has relative imports that fail standalone).
|
|
123
|
+
let gaiaPerms;
|
|
124
|
+
try {
|
|
125
|
+
const setupPath = join(__dirname, '..', 'hooks', 'modules', 'core', 'plugin_setup.py');
|
|
126
|
+
const { stdout } = await execAsync(
|
|
127
|
+
`python3 -c "
|
|
128
|
+
import ast, json, re
|
|
129
|
+
|
|
130
|
+
source = open('${setupPath.replace(/'/g, "\\'")}').read()
|
|
131
|
+
|
|
132
|
+
# Extract _DENY_RULES list
|
|
133
|
+
deny_match = re.search(r'^_DENY_RULES\\s*=\\s*\\[', source, re.MULTILINE)
|
|
134
|
+
if deny_match:
|
|
135
|
+
bracket_start = deny_match.start() + source[deny_match.start():].index('[')
|
|
136
|
+
depth, i = 0, bracket_start
|
|
137
|
+
for i, ch in enumerate(source[bracket_start:], bracket_start):
|
|
138
|
+
if ch == '[': depth += 1
|
|
139
|
+
elif ch == ']': depth -= 1
|
|
140
|
+
if depth == 0: break
|
|
141
|
+
deny_rules = ast.literal_eval(source[bracket_start:i+1])
|
|
142
|
+
else:
|
|
143
|
+
deny_rules = []
|
|
144
|
+
|
|
145
|
+
# Extract OPS_PERMISSIONS allow list
|
|
146
|
+
ops_match = re.search(r'^OPS_PERMISSIONS\\s*=', source, re.MULTILINE)
|
|
147
|
+
if ops_match:
|
|
148
|
+
bracket_start = source.index('{', ops_match.start())
|
|
149
|
+
depth, i = 0, bracket_start
|
|
150
|
+
for i, ch in enumerate(source[bracket_start:], bracket_start):
|
|
151
|
+
if ch == '{': depth += 1
|
|
152
|
+
elif ch == '}': depth -= 1
|
|
153
|
+
if depth == 0: break
|
|
154
|
+
# Replace _DENY_RULES reference with actual list for eval
|
|
155
|
+
ops_str = source[bracket_start:i+1].replace('_DENY_RULES', json.dumps(deny_rules))
|
|
156
|
+
ops_perms = ast.literal_eval(ops_str)
|
|
157
|
+
else:
|
|
158
|
+
ops_perms = {'permissions': {'allow': [], 'deny': deny_rules, 'ask': []}}
|
|
159
|
+
|
|
160
|
+
print(json.dumps(ops_perms))
|
|
161
|
+
"`,
|
|
162
|
+
{ timeout: 10000 }
|
|
163
|
+
);
|
|
164
|
+
gaiaPerms = JSON.parse(stdout.trim());
|
|
165
|
+
} catch (pyError) {
|
|
166
|
+
spinner.warn(`Could not load permissions from Python — ${pyError.message || 'unknown error'}`);
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const ourAllow = new Set(gaiaPerms.permissions.allow || []);
|
|
171
|
+
const ourDeny = new Set(gaiaPerms.permissions.deny || []);
|
|
172
|
+
|
|
173
|
+
// Load existing settings.local.json — preserve everything (enabledPlugins, MCP servers, etc.)
|
|
174
|
+
let existing = {};
|
|
175
|
+
if (existsSync(localPath)) {
|
|
176
|
+
try {
|
|
177
|
+
existing = JSON.parse(await fs.readFile(localPath, 'utf-8'));
|
|
178
|
+
} catch {
|
|
179
|
+
existing = {};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const perms = existing.permissions || {};
|
|
184
|
+
const currentAllow = new Set(perms.allow || []);
|
|
185
|
+
const currentDeny = new Set(perms.deny || []);
|
|
186
|
+
|
|
187
|
+
// Union merge — add ours without removing user's
|
|
188
|
+
const mergedAllow = [...new Set([...currentAllow, ...ourAllow])].sort();
|
|
189
|
+
const mergedDeny = [...new Set([...currentDeny, ...ourDeny])].sort();
|
|
190
|
+
|
|
191
|
+
// Check if anything changed
|
|
192
|
+
const allowChanged = mergedAllow.length !== currentAllow.size
|
|
193
|
+
|| mergedAllow.some(r => !currentAllow.has(r));
|
|
194
|
+
const denyChanged = mergedDeny.length !== currentDeny.size
|
|
195
|
+
|| mergedDeny.some(r => !currentDeny.has(r));
|
|
196
|
+
|
|
197
|
+
if (!allowChanged && !denyChanged) {
|
|
198
|
+
spinner.succeed('settings.local.json permissions already up to date');
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Update only permissions, preserve everything else
|
|
203
|
+
existing.permissions = existing.permissions || {};
|
|
204
|
+
existing.permissions.allow = mergedAllow;
|
|
205
|
+
existing.permissions.deny = mergedDeny;
|
|
206
|
+
existing.permissions.ask = existing.permissions.ask || [];
|
|
207
|
+
|
|
208
|
+
// Add env vars (smart merge: add if not present, don't overwrite)
|
|
209
|
+
existing.env = existing.env || {};
|
|
210
|
+
if (!('CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS' in existing.env)) {
|
|
211
|
+
existing.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = '1';
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
await fs.writeFile(localPath, JSON.stringify(existing, null, 2) + '\n');
|
|
215
|
+
spinner.succeed('settings.local.json permissions and env merged');
|
|
216
|
+
return true;
|
|
217
|
+
} catch (error) {
|
|
218
|
+
spinner.fail(`settings.local.json: ${error.message}`);
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async function updateLocalHooks() {
|
|
224
|
+
const spinner = ora('Merging hooks into settings.local.json...').start();
|
|
225
|
+
try {
|
|
226
|
+
const claudeDir = join(CWD, '.claude');
|
|
227
|
+
const localPath = join(claudeDir, 'settings.local.json');
|
|
228
|
+
|
|
229
|
+
if (!existsSync(claudeDir)) {
|
|
230
|
+
spinner.info('Skipped (.claude/ not found)');
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Read hooks.json from the installed package
|
|
235
|
+
const hooksJsonPath = join(__dirname, '..', 'hooks', 'hooks.json');
|
|
236
|
+
if (!existsSync(hooksJsonPath)) {
|
|
237
|
+
spinner.warn('hooks.json not found in package');
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
let hooksData;
|
|
242
|
+
try {
|
|
243
|
+
hooksData = JSON.parse(await fs.readFile(hooksJsonPath, 'utf-8'));
|
|
244
|
+
} catch {
|
|
245
|
+
spinner.warn('hooks.json is invalid JSON');
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Unwrap outer "hooks" key if present
|
|
250
|
+
const sourceHooks = hooksData.hooks || hooksData;
|
|
251
|
+
|
|
252
|
+
// Convert ${CLAUDE_PLUGIN_ROOT}/hooks/<script> to .claude/hooks/<script> for npm mode
|
|
253
|
+
const convertCommand = (cmd) => {
|
|
254
|
+
return cmd.replace(/\$\{CLAUDE_PLUGIN_ROOT\}\/hooks\//g, '.claude/hooks/');
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const convertedHooks = {};
|
|
258
|
+
for (const [event, entries] of Object.entries(sourceHooks)) {
|
|
259
|
+
convertedHooks[event] = entries.map(entry => {
|
|
260
|
+
const converted = { ...entry };
|
|
261
|
+
if (converted.hooks) {
|
|
262
|
+
converted.hooks = converted.hooks.map(h => ({
|
|
263
|
+
...h,
|
|
264
|
+
command: h.command ? convertCommand(h.command) : h.command,
|
|
265
|
+
}));
|
|
266
|
+
}
|
|
267
|
+
return converted;
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Load existing settings.local.json
|
|
272
|
+
let existing = {};
|
|
273
|
+
if (existsSync(localPath)) {
|
|
274
|
+
try {
|
|
275
|
+
existing = JSON.parse(await fs.readFile(localPath, 'utf-8'));
|
|
276
|
+
} catch {
|
|
277
|
+
existing = {};
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Smart merge: for each hook event, deduplicate by command string
|
|
282
|
+
const existingHooks = existing.hooks || {};
|
|
283
|
+
let changed = false;
|
|
284
|
+
|
|
285
|
+
for (const [event, newEntries] of Object.entries(convertedHooks)) {
|
|
286
|
+
if (!existingHooks[event]) {
|
|
287
|
+
existingHooks[event] = newEntries;
|
|
288
|
+
changed = true;
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Collect existing command strings for deduplication
|
|
293
|
+
const existingCommands = new Set();
|
|
294
|
+
for (const entry of existingHooks[event]) {
|
|
295
|
+
for (const h of (entry.hooks || [])) {
|
|
296
|
+
if (h.command) existingCommands.add(h.command);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Add new entries whose commands are not already present
|
|
301
|
+
for (const newEntry of newEntries) {
|
|
302
|
+
const newCommands = (newEntry.hooks || []).map(h => h.command).filter(Boolean);
|
|
303
|
+
const allPresent = newCommands.length > 0 && newCommands.every(c => existingCommands.has(c));
|
|
304
|
+
if (!allPresent) {
|
|
305
|
+
existingHooks[event].push(newEntry);
|
|
306
|
+
changed = true;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (!changed) {
|
|
312
|
+
spinner.succeed('settings.local.json hooks already up to date');
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
existing.hooks = existingHooks;
|
|
317
|
+
await fs.writeFile(localPath, JSON.stringify(existing, null, 2) + '\n');
|
|
318
|
+
spinner.succeed('settings.local.json hooks merged');
|
|
319
|
+
return true;
|
|
320
|
+
} catch (error) {
|
|
321
|
+
spinner.fail(`hooks merge: ${error.message}`);
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
95
326
|
async function updateSymlinks() {
|
|
96
327
|
const spinner = ora('Checking symlinks...').start();
|
|
97
328
|
try {
|
|
@@ -206,7 +437,7 @@ async function runVerification() {
|
|
|
206
437
|
}
|
|
207
438
|
|
|
208
439
|
// 4. Config files accessible
|
|
209
|
-
const configFiles = ['
|
|
440
|
+
const configFiles = ['git_standards.json', 'universal-rules.json', 'surface-routing.json'];
|
|
210
441
|
for (const cfg of configFiles) {
|
|
211
442
|
const path = join(CWD, '.claude', 'config', cfg);
|
|
212
443
|
if (existsSync(path)) {
|
|
@@ -218,7 +449,7 @@ async function runVerification() {
|
|
|
218
449
|
}
|
|
219
450
|
|
|
220
451
|
// 5. Agent definitions accessible
|
|
221
|
-
const agentFiles = ['terraform-architect.md', 'gitops-operator.md', 'cloud-troubleshooter.md', 'devops-developer.md', 'gaia.md'];
|
|
452
|
+
const agentFiles = ['terraform-architect.md', 'gitops-operator.md', 'cloud-troubleshooter.md', 'devops-developer.md', 'gaia-system.md', 'speckit-planner.md'];
|
|
222
453
|
let agentsOk = 0;
|
|
223
454
|
for (const agent of agentFiles) {
|
|
224
455
|
if (existsSync(join(CWD, '.claude', 'agents', agent))) agentsOk++;
|
|
@@ -226,18 +457,21 @@ async function runVerification() {
|
|
|
226
457
|
checks.push({ name: 'agent definitions', ok: agentsOk === agentFiles.length, detail: `${agentsOk}/${agentFiles.length}` });
|
|
227
458
|
if (agentsOk < agentFiles.length) issues.push(`${agentFiles.length - agentsOk} agent definition(s) missing`);
|
|
228
459
|
|
|
229
|
-
// 6.
|
|
230
|
-
const
|
|
231
|
-
if (existsSync(
|
|
460
|
+
// 6. hooks.json exists (hooks are auto-discovered from hooks directory)
|
|
461
|
+
const hooksJsonPath = join(CWD, '.claude', 'hooks', 'hooks.json');
|
|
462
|
+
if (existsSync(hooksJsonPath)) {
|
|
232
463
|
try {
|
|
233
|
-
const
|
|
234
|
-
const hasHooks =
|
|
235
|
-
checks.push({ name: 'hooks
|
|
236
|
-
if (!hasHooks) issues.push('
|
|
464
|
+
const hooksData = JSON.parse(await fs.readFile(hooksJsonPath, 'utf-8'));
|
|
465
|
+
const hasHooks = hooksData.hooks && Object.keys(hooksData.hooks).length > 0;
|
|
466
|
+
checks.push({ name: 'hooks.json', ok: hasHooks });
|
|
467
|
+
if (!hasHooks) issues.push('hooks.json has no hooks configured');
|
|
237
468
|
} catch {
|
|
238
|
-
checks.push({ name: 'hooks
|
|
239
|
-
issues.push('
|
|
469
|
+
checks.push({ name: 'hooks.json', ok: false });
|
|
470
|
+
issues.push('hooks.json is invalid');
|
|
240
471
|
}
|
|
472
|
+
} else {
|
|
473
|
+
checks.push({ name: 'hooks.json', ok: false });
|
|
474
|
+
issues.push('hooks.json not found (hooks symlink may be broken)');
|
|
241
475
|
}
|
|
242
476
|
|
|
243
477
|
const passed = checks.filter(c => c.ok).length;
|
|
@@ -256,48 +490,106 @@ async function runVerification() {
|
|
|
256
490
|
// Main
|
|
257
491
|
// ============================================================================
|
|
258
492
|
|
|
493
|
+
async function runFreshInstall() {
|
|
494
|
+
const packageDir = join(__dirname, '..');
|
|
495
|
+
const scanScript = join(packageDir, 'bin', 'gaia-scan.py');
|
|
496
|
+
const { current } = await detectVersions();
|
|
497
|
+
|
|
498
|
+
console.log(chalk.cyan(`\n gaia-ops ${chalk.green(current)} — fresh install\n`));
|
|
499
|
+
|
|
500
|
+
// 1. Check Python 3 is available
|
|
501
|
+
const spinner = ora('Checking Python 3...').start();
|
|
502
|
+
try {
|
|
503
|
+
await execAsync('python3 --version', { timeout: 5000 });
|
|
504
|
+
spinner.succeed('Python 3 found');
|
|
505
|
+
} catch {
|
|
506
|
+
spinner.warn('Python 3 not found — skipping project setup');
|
|
507
|
+
console.log(chalk.gray(' Install Python 3.9+ and run: npx gaia-scan\n'));
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// 2. Run gaia-scan --npm-postinstall
|
|
512
|
+
const scanSpinner = ora('Running gaia-scan...').start();
|
|
513
|
+
try {
|
|
514
|
+
const { stdout, stderr } = await execAsync(
|
|
515
|
+
`python3 "${scanScript}" --npm-postinstall --root "${CWD}"`,
|
|
516
|
+
{ timeout: 60000 }
|
|
517
|
+
);
|
|
518
|
+
scanSpinner.succeed('Project scanned and configured');
|
|
519
|
+
if (VERBOSE && stdout) console.log(chalk.gray(stdout));
|
|
520
|
+
if (VERBOSE && stderr) console.log(chalk.yellow(stderr));
|
|
521
|
+
} catch (error) {
|
|
522
|
+
scanSpinner.warn('gaia-scan encountered issues (non-fatal)');
|
|
523
|
+
if (VERBOSE && error.stderr) console.log(chalk.gray(error.stderr));
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// 3. Create plugin-registry.json (in .claude/, same path Python hooks expect)
|
|
527
|
+
try {
|
|
528
|
+
const claudeDirPath = join(CWD, '.claude');
|
|
529
|
+
if (!existsSync(claudeDirPath)) {
|
|
530
|
+
await fs.mkdir(claudeDirPath, { recursive: true });
|
|
531
|
+
}
|
|
532
|
+
const registryPath = join(claudeDirPath, 'plugin-registry.json');
|
|
533
|
+
const registry = {
|
|
534
|
+
installed: [{ name: 'gaia-ops', version: current || 'unknown' }],
|
|
535
|
+
source: 'npm-postinstall',
|
|
536
|
+
};
|
|
537
|
+
await fs.writeFile(registryPath, JSON.stringify(registry, null, 2) + '\n');
|
|
538
|
+
} catch {
|
|
539
|
+
// Non-fatal — plugin-registry is a convenience, not critical
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// 4. Merge permissions into settings.local.json (same approach as plugin mode)
|
|
543
|
+
await updateLocalPermissions();
|
|
544
|
+
|
|
545
|
+
// 5. Merge hooks into settings.local.json (npm mode — Claude Code reads hooks from settings, not hooks.json)
|
|
546
|
+
await updateLocalHooks();
|
|
547
|
+
}
|
|
548
|
+
|
|
259
549
|
async function main() {
|
|
260
550
|
const claudeDir = join(CWD, '.claude');
|
|
261
551
|
const isUpdate = existsSync(claudeDir);
|
|
262
552
|
|
|
263
553
|
if (!isUpdate) {
|
|
264
|
-
// First-time install — gaia-scan
|
|
265
|
-
|
|
554
|
+
// First-time install — run gaia-scan to bootstrap everything
|
|
555
|
+
await runFreshInstall();
|
|
556
|
+
} else {
|
|
557
|
+
// Version info
|
|
558
|
+
const { previous, current } = await detectVersions();
|
|
559
|
+
const versionLine = previous && previous !== current
|
|
560
|
+
? `${chalk.gray(previous)} → ${chalk.green(current)}`
|
|
561
|
+
: chalk.green(current);
|
|
562
|
+
|
|
563
|
+
console.log(chalk.cyan(`\n gaia-ops update ${versionLine}\n`));
|
|
564
|
+
|
|
565
|
+
// Step 1-4: Update files
|
|
566
|
+
await updateSettingsJson();
|
|
567
|
+
await updateLocalPermissions();
|
|
568
|
+
await updateLocalHooks();
|
|
569
|
+
await updateSymlinks();
|
|
266
570
|
}
|
|
267
571
|
|
|
268
|
-
//
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
572
|
+
// Ensure plugin-registry.json exists in .claude/ (both fresh and update)
|
|
573
|
+
try {
|
|
574
|
+
const registryPath = join(CWD, '.claude', 'plugin-registry.json');
|
|
575
|
+
if (!existsSync(registryPath)) {
|
|
576
|
+
const { current } = await detectVersions();
|
|
577
|
+
const registry = {
|
|
578
|
+
installed: [{ name: 'gaia-ops', version: current || 'unknown' }],
|
|
579
|
+
source: 'npm-postinstall',
|
|
580
|
+
};
|
|
581
|
+
await fs.writeFile(registryPath, JSON.stringify(registry, null, 2) + '\n');
|
|
582
|
+
}
|
|
583
|
+
} catch { /* non-fatal */ }
|
|
279
584
|
|
|
280
|
-
//
|
|
585
|
+
// Verify (runs for both fresh install and update)
|
|
281
586
|
const { issues, passed, total } = await runVerification();
|
|
282
587
|
|
|
283
|
-
// Summary
|
|
284
|
-
const changes = [settingsUpdated, symlinksUpdated].filter(Boolean).length;
|
|
285
|
-
|
|
286
588
|
console.log('');
|
|
287
|
-
if (
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
console.log(chalk.
|
|
291
|
-
if (settingsUpdated) console.log(chalk.gray(' settings.json: replaced from template'));
|
|
292
|
-
if (symlinksFix > 0) console.log(chalk.gray(` ${symlinksFix} symlink(s) fixed`));
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Issues
|
|
296
|
-
if (issues.length > 0) {
|
|
297
|
-
console.log(chalk.yellow(`\n ${issues.length} issue(s) found:`));
|
|
298
|
-
for (const issue of issues) {
|
|
299
|
-
console.log(chalk.yellow(` - ${issue}`));
|
|
300
|
-
}
|
|
589
|
+
if (issues.length > 0) {
|
|
590
|
+
console.log(chalk.yellow(` ${issues.length} issue(s) found:`));
|
|
591
|
+
for (const issue of issues) {
|
|
592
|
+
console.log(chalk.yellow(` - ${issue}`));
|
|
301
593
|
}
|
|
302
594
|
} else {
|
|
303
595
|
console.log(chalk.green(' Everything up to date'));
|
|
@@ -46,10 +46,10 @@ function detectGaiaOpsRoot(startPath) {
|
|
|
46
46
|
// Buscar node_modules de múltiples maneras
|
|
47
47
|
function findNodeModulesPath(gaiaOpsRoot) {
|
|
48
48
|
const candidates = [
|
|
49
|
-
path.resolve(gaiaOpsRoot, '
|
|
50
|
-
path.resolve(gaiaOpsRoot, 'node_modules'),
|
|
49
|
+
path.resolve(gaiaOpsRoot, 'node_modules'), // ./node_modules (CI: npm ci in repo root)
|
|
50
|
+
path.resolve(gaiaOpsRoot, '..', 'node_modules'), // ../node_modules (monorepo consumer)
|
|
51
51
|
path.resolve(process.cwd(), 'node_modules'), // cwd/node_modules
|
|
52
|
-
path.join(gaiaOpsRoot, '..', '..', 'node_modules'), // ../../node_modules
|
|
52
|
+
path.join(gaiaOpsRoot, '..', '..', 'node_modules'), // ../../node_modules (deep nesting)
|
|
53
53
|
];
|
|
54
54
|
|
|
55
55
|
for (const candidate of candidates) {
|
|
@@ -59,8 +59,16 @@ function findNodeModulesPath(gaiaOpsRoot) {
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
//
|
|
63
|
-
|
|
62
|
+
// In CI, node_modules exists inside the repo root (npm ci) but won't contain
|
|
63
|
+
// the package itself as a nested dependency. Prefer gaiaOpsRoot/node_modules
|
|
64
|
+
// over going up a level (which breaks in CI checkout structures).
|
|
65
|
+
const localNodeModules = path.resolve(gaiaOpsRoot, 'node_modules');
|
|
66
|
+
if (fs.existsSync(localNodeModules)) {
|
|
67
|
+
return localNodeModules;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Fallback: use __dirname-relative path (repo root) instead of process.cwd()
|
|
71
|
+
return path.resolve(gaiaOpsRoot, 'node_modules');
|
|
64
72
|
}
|
|
65
73
|
|
|
66
74
|
const GAIA_OPS_ROOT = detectGaiaOpsRoot(path.resolve(__dirname, '..'));
|
|
@@ -219,14 +227,21 @@ class PrePublishValidator {
|
|
|
219
227
|
}
|
|
220
228
|
|
|
221
229
|
reinstallNodeModules() {
|
|
222
|
-
this.log('Reinstalling node_modules
|
|
230
|
+
this.log('Reinstalling node_modules...', 'info');
|
|
223
231
|
|
|
224
232
|
if (this.dryRun) {
|
|
225
233
|
this.log('[DRY RUN] Would run: npm install', 'info');
|
|
226
234
|
return;
|
|
227
235
|
}
|
|
228
236
|
|
|
229
|
-
|
|
237
|
+
// In CI, MONOREPO_ROOT may resolve incorrectly (one level above checkout).
|
|
238
|
+
// Use GAIA_OPS_ROOT if it contains a package.json (i.e., we ARE the root).
|
|
239
|
+
const installDir = fs.existsSync(path.join(GAIA_OPS_ROOT, 'package-lock.json'))
|
|
240
|
+
? GAIA_OPS_ROOT
|
|
241
|
+
: MONOREPO_ROOT;
|
|
242
|
+
|
|
243
|
+
this.log(`Install directory: ${installDir}`, 'info');
|
|
244
|
+
this.execute('npm install', installDir);
|
|
230
245
|
this.log('✓ npm install completed', 'success');
|
|
231
246
|
}
|
|
232
247
|
|
|
@@ -238,7 +253,6 @@ class PrePublishValidator {
|
|
|
238
253
|
'bin/gaia-scan',
|
|
239
254
|
'tools/context/context_provider.py',
|
|
240
255
|
'hooks/pre_tool_use.py',
|
|
241
|
-
'templates/settings.template.json'
|
|
242
256
|
];
|
|
243
257
|
|
|
244
258
|
let allValid = true;
|
|
@@ -330,7 +344,6 @@ class PrePublishValidator {
|
|
|
330
344
|
// Test 1: Validate JSON files
|
|
331
345
|
this.log('Test 1: Validating JSON configuration files...', 'info');
|
|
332
346
|
const jsonFiles = [
|
|
333
|
-
'templates/settings.template.json',
|
|
334
347
|
'config/clarification_rules.json',
|
|
335
348
|
'config/git_standards.json'
|
|
336
349
|
];
|
|
@@ -505,12 +518,22 @@ class PrePublishValidator {
|
|
|
505
518
|
// Parse command line arguments and run
|
|
506
519
|
async function main() {
|
|
507
520
|
const args = process.argv.slice(2);
|
|
521
|
+
const isCI = process.env.CI === 'true' || process.env.GITHUB_ACTIONS === 'true';
|
|
522
|
+
|
|
508
523
|
const options = {
|
|
509
524
|
dryRun: args.includes('--dry-run'),
|
|
510
|
-
validateOnly: args.includes('--validate-only'),
|
|
525
|
+
validateOnly: args.includes('--validate-only') || isCI,
|
|
511
526
|
versionBump: 'patch'
|
|
512
527
|
};
|
|
513
528
|
|
|
529
|
+
if (isCI && !args.includes('--validate-only')) {
|
|
530
|
+
console.log(chalk.blue(
|
|
531
|
+
'[CI] Auto-enabling --validate-only: ' +
|
|
532
|
+
'self-install validation is not possible during npm publish ' +
|
|
533
|
+
'(package is not yet on the registry).'
|
|
534
|
+
));
|
|
535
|
+
}
|
|
536
|
+
|
|
514
537
|
if (args.includes('major')) options.versionBump = 'major';
|
|
515
538
|
if (args.includes('minor')) options.versionBump = 'minor';
|
|
516
539
|
if (args.includes('patch')) options.versionBump = 'patch';
|
package/commands/gaia.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gaia
|
|
3
|
+
description: Invoke the Gaia meta-agent for system architecture analysis, agent design, skill creation, and orchestration debugging
|
|
4
|
+
allowed-tools:
|
|
5
|
+
- Bash(*)
|
|
6
|
+
- Read
|
|
7
|
+
- Edit
|
|
8
|
+
- Write
|
|
9
|
+
- Glob
|
|
10
|
+
- Grep
|
|
11
|
+
- WebSearch
|
|
12
|
+
- WebFetch
|
|
13
|
+
- Task
|
|
14
|
+
- Agent
|
|
15
|
+
- Skill
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
Invoke the Gaia meta-agent (`gaia-system`) to work on the gaia-ops orchestration
|
|
19
|
+
system itself. This is the entry point for tasks that modify or analyze agents,
|
|
20
|
+
skills, hooks, or system architecture.
|
|
21
|
+
|
|
22
|
+
## When to use
|
|
23
|
+
|
|
24
|
+
- Analyze or improve the gaia-ops architecture
|
|
25
|
+
- Create or update agent definitions (`.md` files)
|
|
26
|
+
- Create or update skills (`SKILL.md` files)
|
|
27
|
+
- Write or debug Python hooks and tools
|
|
28
|
+
- Update `CLAUDE.md` or system configuration
|
|
29
|
+
- Research best practices for agent orchestration
|
|
30
|
+
|
|
31
|
+
## How it works
|
|
32
|
+
|
|
33
|
+
This command delegates to the `gaia-system` agent, which is the meta-agent
|
|
34
|
+
specialized in the orchestration system. It follows the standard agent protocol
|
|
35
|
+
and returns a `json:contract` block with findings and status.
|
|
36
|
+
|
|
37
|
+
$ARGUMENTS
|
package/config/README.md
CHANGED
|
@@ -6,11 +6,9 @@ Central configuration for the orchestration system. Contracts are the SSOT for a
|
|
|
6
6
|
|
|
7
7
|
| File | Purpose | Read by |
|
|
8
8
|
|------|---------|---------|
|
|
9
|
-
| `context-contracts.json` | Base cloud-agnostic contracts: `read`/`write` sections per agent | `context_provider.py`, `context_writer.py`, `pre_tool_use.py` |
|
|
9
|
+
| `context-contracts.json` | Base cloud-agnostic contracts: `read`/`write` sections per agent, `core_sections` list, `workspace_repos` schema | `context_provider.py`, `context_writer.py`, `pre_tool_use.py` |
|
|
10
10
|
| `cloud/gcp.json` | GCP extensions: `gcp_services`, `workload_identity`, `static_ips` | Same trio, merged at runtime |
|
|
11
11
|
| `cloud/aws.json` | AWS extensions: `vpc_mapping`, `load_balancers`, `api_gateway`, `irsa_bindings`, `aws_accounts` | Same trio, merged at runtime |
|
|
12
|
-
| `context-contracts.gcp.json` | **Legacy** -- kept for backward compatibility | Fallback if `context-contracts.json` not found |
|
|
13
|
-
| `context-contracts.aws.json` | **Legacy** -- kept for backward compatibility | Fallback if `context-contracts.json` not found |
|
|
14
12
|
| `git_standards.json` | Commit standards (Conventional Commits), allowed types, forbidden footers | `hooks/modules/validation/commit_validator.py` |
|
|
15
13
|
| `universal-rules.json` | Behavior rules injected into all agents | `context_provider.py` |
|
|
16
14
|
| `surface-routing.json` | Generic surface classification and investigation-brief rules | `surface_router.py`, `context_provider.py`, Spec-Kit |
|
|
@@ -27,18 +25,14 @@ At runtime, `tools/context/context_provider.py` executes the following logic:
|
|
|
27
25
|
5. Result: complete contract for the agent on that cloud
|
|
28
26
|
```
|
|
29
27
|
|
|
30
|
-
Fallback if `context-contracts.json` not found: uses `context-contracts.{provider}.json` (legacy).
|
|
31
|
-
|
|
32
28
|
## Structure
|
|
33
29
|
|
|
34
30
|
```
|
|
35
31
|
config/
|
|
36
|
-
├── context-contracts.json <- agnostic base (all agents)
|
|
32
|
+
├── context-contracts.json <- agnostic base (all agents, v4)
|
|
37
33
|
├── cloud/
|
|
38
34
|
│ ├── gcp.json <- GCP extensions + section_schemas
|
|
39
35
|
│ └── aws.json <- AWS extensions + section_schemas
|
|
40
|
-
├── context-contracts.gcp.json <- legacy (fallback)
|
|
41
|
-
├── context-contracts.aws.json <- legacy (fallback)
|
|
42
36
|
├── surface-routing.json <- generic surface routing + investigation brief config
|
|
43
37
|
├── git_standards.json
|
|
44
38
|
├── universal-rules.json
|
|
@@ -61,4 +55,4 @@ config/
|
|
|
61
55
|
|
|
62
56
|
---
|
|
63
57
|
|
|
64
|
-
**Updated:** 2026-03-
|
|
58
|
+
**Updated:** 2026-03-25 | **Active contracts:** base v4 + 2 clouds (GCP, AWS)
|