@jaguilar87/gaia-ops 4.4.0 → 4.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +12 -3
- package/ARCHITECTURE.md +9 -8
- package/CHANGELOG.md +34 -0
- package/README.md +43 -11
- package/agents/terraform-architect.md +1 -1
- package/bin/README.md +2 -2
- package/bin/gaia-doctor.js +18 -5
- package/bin/gaia-history.js +0 -1
- package/bin/gaia-metrics.js +2 -2
- package/bin/gaia-scan.py +23 -1
- package/bin/gaia-update.js +346 -54
- package/bin/pre-publish-validate.js +33 -10
- package/commands/gaia.md +37 -0
- package/config/README.md +3 -9
- package/config/context-contracts.json +47 -15
- package/config/surface-routing.json +9 -1
- package/dist/gaia-ops/.claude-plugin/plugin.json +22 -0
- package/dist/gaia-ops/agents/cloud-troubleshooter.md +73 -0
- package/dist/gaia-ops/agents/devops-developer.md +57 -0
- package/dist/gaia-ops/agents/gaia-system.md +58 -0
- package/dist/gaia-ops/agents/gitops-operator.md +60 -0
- package/dist/gaia-ops/agents/speckit-planner.md +71 -0
- package/dist/gaia-ops/agents/terraform-architect.md +60 -0
- package/dist/gaia-ops/commands/gaia.md +37 -0
- package/dist/gaia-ops/config/README.md +58 -0
- package/dist/gaia-ops/config/cloud/aws.json +140 -0
- package/dist/gaia-ops/config/cloud/gcp.json +145 -0
- package/dist/gaia-ops/config/context-contracts.json +131 -0
- package/dist/gaia-ops/config/git_standards.json +72 -0
- package/dist/gaia-ops/config/surface-routing.json +197 -0
- package/dist/gaia-ops/config/universal-rules.json +10 -0
- package/dist/gaia-ops/hooks/adapters/__init__.py +52 -0
- package/dist/gaia-ops/hooks/adapters/base.py +219 -0
- package/dist/gaia-ops/hooks/adapters/channel.py +17 -0
- package/dist/gaia-ops/hooks/adapters/claude_code.py +1477 -0
- package/dist/gaia-ops/hooks/adapters/types.py +194 -0
- package/dist/gaia-ops/hooks/adapters/utils.py +25 -0
- package/dist/gaia-ops/hooks/hooks.json +126 -0
- package/dist/gaia-ops/hooks/modules/__init__.py +15 -0
- package/dist/gaia-ops/hooks/modules/agents/__init__.py +29 -0
- package/dist/gaia-ops/hooks/modules/agents/contract_validator.py +647 -0
- package/dist/gaia-ops/hooks/modules/agents/response_contract.py +496 -0
- package/dist/gaia-ops/hooks/modules/agents/skill_injection_verifier.py +124 -0
- package/dist/gaia-ops/hooks/modules/agents/task_info_builder.py +74 -0
- package/dist/gaia-ops/hooks/modules/agents/transcript_analyzer.py +458 -0
- package/dist/gaia-ops/hooks/modules/agents/transcript_reader.py +152 -0
- package/dist/gaia-ops/hooks/modules/audit/__init__.py +28 -0
- package/dist/gaia-ops/hooks/modules/audit/event_detector.py +168 -0
- package/dist/gaia-ops/hooks/modules/audit/logger.py +131 -0
- package/dist/gaia-ops/hooks/modules/audit/metrics.py +134 -0
- package/dist/gaia-ops/hooks/modules/audit/workflow_auditor.py +576 -0
- package/dist/gaia-ops/hooks/modules/audit/workflow_recorder.py +296 -0
- package/dist/gaia-ops/hooks/modules/context/__init__.py +11 -0
- package/dist/gaia-ops/hooks/modules/context/anchor_tracker.py +317 -0
- package/dist/gaia-ops/hooks/modules/context/compact_context_builder.py +215 -0
- package/dist/gaia-ops/hooks/modules/context/context_cache.py +129 -0
- package/dist/gaia-ops/hooks/modules/context/context_freshness.py +145 -0
- package/dist/gaia-ops/hooks/modules/context/context_injector.py +427 -0
- package/dist/gaia-ops/hooks/modules/context/context_writer.py +518 -0
- package/dist/gaia-ops/hooks/modules/context/contracts_loader.py +161 -0
- package/dist/gaia-ops/hooks/modules/core/__init__.py +40 -0
- package/dist/gaia-ops/hooks/modules/core/hook_entry.py +78 -0
- package/dist/gaia-ops/hooks/modules/core/paths.py +160 -0
- package/dist/gaia-ops/hooks/modules/core/plugin_mode.py +149 -0
- package/dist/gaia-ops/hooks/modules/core/plugin_setup.py +558 -0
- package/dist/gaia-ops/hooks/modules/core/state.py +179 -0
- package/dist/gaia-ops/hooks/modules/core/stdin.py +24 -0
- package/dist/gaia-ops/hooks/modules/events/__init__.py +1 -0
- package/dist/gaia-ops/hooks/modules/events/event_writer.py +210 -0
- package/dist/gaia-ops/hooks/modules/identity/__init__.py +0 -0
- package/dist/gaia-ops/hooks/modules/identity/identity_provider.py +21 -0
- package/dist/gaia-ops/hooks/modules/identity/ops_identity.py +34 -0
- package/dist/gaia-ops/hooks/modules/identity/security_identity.py +10 -0
- package/dist/gaia-ops/hooks/modules/memory/__init__.py +8 -0
- package/dist/gaia-ops/hooks/modules/memory/episode_writer.py +227 -0
- package/dist/gaia-ops/hooks/modules/orchestrator/__init__.py +1 -0
- package/dist/gaia-ops/hooks/modules/orchestrator/delegate_mode.py +128 -0
- package/dist/gaia-ops/hooks/modules/scanning/__init__.py +8 -0
- package/dist/gaia-ops/hooks/modules/scanning/scan_trigger.py +84 -0
- package/dist/gaia-ops/hooks/modules/security/__init__.py +89 -0
- package/dist/gaia-ops/hooks/modules/security/approval_cleanup.py +87 -0
- package/dist/gaia-ops/hooks/modules/security/approval_constants.py +23 -0
- package/dist/gaia-ops/hooks/modules/security/approval_grants.py +912 -0
- package/dist/gaia-ops/hooks/modules/security/approval_messages.py +71 -0
- package/dist/gaia-ops/hooks/modules/security/approval_scopes.py +153 -0
- package/dist/gaia-ops/hooks/modules/security/blocked_commands.py +584 -0
- package/dist/gaia-ops/hooks/modules/security/blocked_message_formatter.py +86 -0
- package/dist/gaia-ops/hooks/modules/security/command_semantics.py +130 -0
- package/dist/gaia-ops/hooks/modules/security/gitops_validator.py +179 -0
- package/dist/gaia-ops/hooks/modules/security/mutative_verbs.py +850 -0
- package/dist/gaia-ops/hooks/modules/security/prompt_validator.py +40 -0
- package/dist/gaia-ops/hooks/modules/security/tiers.py +196 -0
- package/dist/gaia-ops/hooks/modules/session/__init__.py +10 -0
- package/dist/gaia-ops/hooks/modules/session/session_context_writer.py +100 -0
- package/dist/gaia-ops/hooks/modules/session/session_event_injector.py +158 -0
- package/dist/gaia-ops/hooks/modules/session/session_manager.py +31 -0
- package/dist/gaia-ops/hooks/modules/tools/__init__.py +25 -0
- package/dist/gaia-ops/hooks/modules/tools/bash_validator.py +708 -0
- package/dist/gaia-ops/hooks/modules/tools/cloud_pipe_validator.py +181 -0
- package/dist/gaia-ops/hooks/modules/tools/hook_response.py +55 -0
- package/dist/gaia-ops/hooks/modules/tools/shell_parser.py +227 -0
- package/dist/gaia-ops/hooks/modules/tools/task_validator.py +283 -0
- package/dist/gaia-ops/hooks/modules/validation/__init__.py +23 -0
- package/dist/gaia-ops/hooks/modules/validation/commit_validator.py +380 -0
- package/dist/gaia-ops/hooks/post_compact.py +43 -0
- package/dist/gaia-ops/hooks/post_tool_use.py +54 -0
- package/dist/gaia-ops/hooks/pre_tool_use.py +383 -0
- package/dist/gaia-ops/hooks/session_start.py +69 -0
- package/dist/gaia-ops/hooks/stop_hook.py +69 -0
- package/dist/gaia-ops/hooks/subagent_start.py +71 -0
- package/dist/gaia-ops/hooks/subagent_stop.py +288 -0
- package/dist/gaia-ops/hooks/task_completed.py +70 -0
- package/dist/gaia-ops/hooks/user_prompt_submit.py +177 -0
- package/dist/gaia-ops/settings.json +72 -0
- package/dist/gaia-ops/skills/README.md +109 -0
- package/dist/gaia-ops/skills/agent-protocol/SKILL.md +105 -0
- package/dist/gaia-ops/skills/agent-protocol/examples.md +170 -0
- package/dist/gaia-ops/skills/agent-response/SKILL.md +53 -0
- package/dist/gaia-ops/skills/approval/SKILL.md +85 -0
- package/dist/gaia-ops/skills/approval/examples.md +140 -0
- package/dist/gaia-ops/skills/approval/reference.md +57 -0
- package/dist/gaia-ops/skills/command-execution/SKILL.md +64 -0
- package/dist/gaia-ops/skills/command-execution/reference.md +83 -0
- package/dist/gaia-ops/skills/context-updater/SKILL.md +76 -0
- package/dist/gaia-ops/skills/context-updater/examples.md +71 -0
- package/dist/gaia-ops/skills/developer-patterns/SKILL.md +93 -0
- package/dist/gaia-ops/skills/developer-patterns/reference.md +112 -0
- package/dist/gaia-ops/skills/execution/SKILL.md +66 -0
- package/dist/gaia-ops/skills/fast-queries/SKILL.md +47 -0
- package/dist/gaia-ops/skills/gaia-patterns/SKILL.md +92 -0
- package/dist/gaia-ops/skills/gaia-patterns/reference.md +22 -0
- package/dist/gaia-ops/skills/git-conventions/SKILL.md +48 -0
- package/dist/gaia-ops/skills/gitops-patterns/SKILL.md +73 -0
- package/dist/gaia-ops/skills/gitops-patterns/reference.md +183 -0
- package/dist/gaia-ops/skills/investigation/SKILL.md +77 -0
- package/dist/gaia-ops/skills/orchestrator-approval/SKILL.md +64 -0
- package/dist/gaia-ops/skills/reference.md +134 -0
- package/dist/gaia-ops/skills/security-tiers/SKILL.md +61 -0
- package/dist/gaia-ops/skills/security-tiers/destructive-commands-reference.md +623 -0
- package/dist/gaia-ops/skills/security-tiers/reference.md +39 -0
- package/dist/gaia-ops/skills/skill-creation/SKILL.md +119 -0
- package/dist/gaia-ops/skills/specification/SKILL.md +186 -0
- package/dist/gaia-ops/skills/speckit-workflow/SKILL.md +165 -0
- package/dist/gaia-ops/skills/speckit-workflow/reference.md +117 -0
- package/dist/gaia-ops/skills/terraform-patterns/SKILL.md +63 -0
- package/dist/gaia-ops/skills/terraform-patterns/reference.md +93 -0
- package/dist/gaia-ops/speckit/README.md +516 -0
- package/dist/gaia-ops/speckit/scripts/.gitkeep +0 -0
- package/dist/gaia-ops/speckit/templates/adr-template.md +118 -0
- package/dist/gaia-ops/speckit/templates/agent-file-template.md +23 -0
- package/dist/gaia-ops/speckit/templates/plan-template.md +227 -0
- package/dist/gaia-ops/speckit/templates/spec-template.md +140 -0
- package/dist/gaia-ops/speckit/templates/tasks-template.md +257 -0
- package/dist/gaia-ops/tools/context/README.md +132 -0
- package/dist/gaia-ops/tools/context/__init__.py +42 -0
- package/dist/gaia-ops/tools/context/_paths.py +20 -0
- package/dist/gaia-ops/tools/context/context_provider.py +476 -0
- package/dist/gaia-ops/tools/context/context_section_reader.py +330 -0
- package/dist/gaia-ops/tools/context/deep_merge.py +159 -0
- package/dist/gaia-ops/tools/context/pending_updates.py +760 -0
- package/dist/gaia-ops/tools/context/surface_router.py +278 -0
- package/dist/gaia-ops/tools/fast-queries/README.md +65 -0
- package/dist/gaia-ops/tools/fast-queries/__init__.py +30 -0
- package/dist/gaia-ops/tools/fast-queries/appservices/quicktriage_devops_developer.sh +75 -0
- package/dist/gaia-ops/tools/fast-queries/cloud/aws/quicktriage_aws_troubleshooter.sh +32 -0
- package/dist/gaia-ops/tools/fast-queries/cloud/gcp/quicktriage_gcp_troubleshooter.sh +88 -0
- package/dist/gaia-ops/tools/fast-queries/gitops/quicktriage_gitops_operator.sh +48 -0
- package/dist/gaia-ops/tools/fast-queries/run_triage.sh +59 -0
- package/dist/gaia-ops/tools/fast-queries/terraform/quicktriage_terraform_architect.sh +80 -0
- package/dist/gaia-ops/tools/gaia_simulator/__init__.py +33 -0
- package/dist/gaia-ops/tools/gaia_simulator/cli.py +354 -0
- package/dist/gaia-ops/tools/gaia_simulator/extractor.py +457 -0
- package/dist/gaia-ops/tools/gaia_simulator/reporter.py +258 -0
- package/dist/gaia-ops/tools/gaia_simulator/routing_simulator.py +334 -0
- package/dist/gaia-ops/tools/gaia_simulator/runner.py +539 -0
- package/dist/gaia-ops/tools/gaia_simulator/skills_mapper.py +262 -0
- package/dist/gaia-ops/tools/memory/README.md +0 -0
- package/dist/gaia-ops/tools/memory/__init__.py +20 -0
- package/dist/gaia-ops/tools/memory/episodic.py +1196 -0
- package/dist/gaia-ops/tools/persist_transcript_analysis.py +85 -0
- package/dist/gaia-ops/tools/review/__init__.py +1 -0
- package/dist/gaia-ops/tools/review/review_engine.py +157 -0
- package/dist/gaia-ops/tools/scan/__init__.py +35 -0
- package/dist/gaia-ops/tools/scan/config.py +247 -0
- package/dist/gaia-ops/tools/scan/merge.py +212 -0
- package/dist/gaia-ops/tools/scan/orchestrator.py +549 -0
- package/dist/gaia-ops/tools/scan/registry.py +127 -0
- package/dist/gaia-ops/tools/scan/scanners/__init__.py +18 -0
- package/dist/gaia-ops/tools/scan/scanners/base.py +137 -0
- package/dist/gaia-ops/tools/scan/scanners/environment.py +324 -0
- package/dist/gaia-ops/tools/scan/scanners/git.py +570 -0
- package/dist/gaia-ops/tools/scan/scanners/infrastructure.py +875 -0
- package/dist/gaia-ops/tools/scan/scanners/orchestration.py +600 -0
- package/dist/gaia-ops/tools/scan/scanners/stack.py +1085 -0
- package/dist/gaia-ops/tools/scan/scanners/tools.py +260 -0
- package/dist/gaia-ops/tools/scan/setup.py +753 -0
- package/dist/gaia-ops/tools/scan/tests/__init__.py +1 -0
- package/dist/gaia-ops/tools/scan/tests/conftest.py +796 -0
- package/dist/gaia-ops/tools/scan/tests/test_environment.py +323 -0
- package/dist/gaia-ops/tools/scan/tests/test_git.py +419 -0
- package/dist/gaia-ops/tools/scan/tests/test_infrastructure.py +382 -0
- package/dist/gaia-ops/tools/scan/tests/test_integration.py +920 -0
- package/dist/gaia-ops/tools/scan/tests/test_merge.py +269 -0
- package/dist/gaia-ops/tools/scan/tests/test_orchestration.py +304 -0
- package/dist/gaia-ops/tools/scan/tests/test_stack.py +604 -0
- package/dist/gaia-ops/tools/scan/tests/test_tools.py +349 -0
- package/dist/gaia-ops/tools/scan/ui.py +624 -0
- package/dist/gaia-ops/tools/scan/verify.py +266 -0
- package/dist/gaia-ops/tools/scan/walk.py +118 -0
- package/dist/gaia-ops/tools/scan/workspace.py +85 -0
- package/dist/gaia-ops/tools/validation/README.md +244 -0
- package/dist/gaia-ops/tools/validation/__init__.py +17 -0
- package/dist/gaia-ops/tools/validation/approval_gate.py +321 -0
- package/dist/gaia-ops/tools/validation/validate_skills.py +189 -0
- package/dist/gaia-security/.claude-plugin/plugin.json +22 -0
- package/dist/gaia-security/config/universal-rules.json +10 -0
- package/dist/gaia-security/hooks/adapters/__init__.py +52 -0
- package/dist/gaia-security/hooks/adapters/base.py +219 -0
- package/dist/gaia-security/hooks/adapters/channel.py +17 -0
- package/dist/gaia-security/hooks/adapters/claude_code.py +1477 -0
- package/dist/gaia-security/hooks/adapters/types.py +194 -0
- package/dist/gaia-security/hooks/adapters/utils.py +25 -0
- package/dist/gaia-security/hooks/hooks.json +57 -0
- package/dist/gaia-security/hooks/modules/__init__.py +15 -0
- package/dist/gaia-security/hooks/modules/agents/__init__.py +29 -0
- package/dist/gaia-security/hooks/modules/agents/contract_validator.py +647 -0
- package/dist/gaia-security/hooks/modules/agents/response_contract.py +496 -0
- package/dist/gaia-security/hooks/modules/agents/skill_injection_verifier.py +124 -0
- package/dist/gaia-security/hooks/modules/agents/task_info_builder.py +74 -0
- package/dist/gaia-security/hooks/modules/agents/transcript_analyzer.py +458 -0
- package/dist/gaia-security/hooks/modules/agents/transcript_reader.py +152 -0
- package/dist/gaia-security/hooks/modules/audit/__init__.py +28 -0
- package/dist/gaia-security/hooks/modules/audit/event_detector.py +168 -0
- package/dist/gaia-security/hooks/modules/audit/logger.py +131 -0
- package/dist/gaia-security/hooks/modules/audit/metrics.py +134 -0
- package/dist/gaia-security/hooks/modules/audit/workflow_auditor.py +576 -0
- package/dist/gaia-security/hooks/modules/audit/workflow_recorder.py +296 -0
- package/dist/gaia-security/hooks/modules/context/__init__.py +11 -0
- package/dist/gaia-security/hooks/modules/context/anchor_tracker.py +317 -0
- package/dist/gaia-security/hooks/modules/context/compact_context_builder.py +215 -0
- package/dist/gaia-security/hooks/modules/context/context_cache.py +129 -0
- package/dist/gaia-security/hooks/modules/context/context_freshness.py +145 -0
- package/dist/gaia-security/hooks/modules/context/context_injector.py +427 -0
- package/dist/gaia-security/hooks/modules/context/context_writer.py +518 -0
- package/dist/gaia-security/hooks/modules/context/contracts_loader.py +161 -0
- package/dist/gaia-security/hooks/modules/core/__init__.py +40 -0
- package/dist/gaia-security/hooks/modules/core/hook_entry.py +78 -0
- package/dist/gaia-security/hooks/modules/core/paths.py +160 -0
- package/dist/gaia-security/hooks/modules/core/plugin_mode.py +149 -0
- package/dist/gaia-security/hooks/modules/core/plugin_setup.py +558 -0
- package/dist/gaia-security/hooks/modules/core/state.py +179 -0
- package/dist/gaia-security/hooks/modules/core/stdin.py +24 -0
- package/dist/gaia-security/hooks/modules/events/__init__.py +1 -0
- package/dist/gaia-security/hooks/modules/events/event_writer.py +210 -0
- package/dist/gaia-security/hooks/modules/identity/__init__.py +0 -0
- package/dist/gaia-security/hooks/modules/identity/identity_provider.py +21 -0
- package/dist/gaia-security/hooks/modules/identity/ops_identity.py +34 -0
- package/dist/gaia-security/hooks/modules/identity/security_identity.py +10 -0
- package/dist/gaia-security/hooks/modules/memory/__init__.py +8 -0
- package/dist/gaia-security/hooks/modules/memory/episode_writer.py +227 -0
- package/dist/gaia-security/hooks/modules/orchestrator/__init__.py +1 -0
- package/dist/gaia-security/hooks/modules/orchestrator/delegate_mode.py +128 -0
- package/dist/gaia-security/hooks/modules/scanning/__init__.py +8 -0
- package/dist/gaia-security/hooks/modules/scanning/scan_trigger.py +84 -0
- package/dist/gaia-security/hooks/modules/security/__init__.py +89 -0
- package/dist/gaia-security/hooks/modules/security/approval_cleanup.py +87 -0
- package/dist/gaia-security/hooks/modules/security/approval_constants.py +23 -0
- package/dist/gaia-security/hooks/modules/security/approval_grants.py +912 -0
- package/dist/gaia-security/hooks/modules/security/approval_messages.py +71 -0
- package/dist/gaia-security/hooks/modules/security/approval_scopes.py +153 -0
- package/dist/gaia-security/hooks/modules/security/blocked_commands.py +584 -0
- package/dist/gaia-security/hooks/modules/security/blocked_message_formatter.py +86 -0
- package/dist/gaia-security/hooks/modules/security/command_semantics.py +130 -0
- package/dist/gaia-security/hooks/modules/security/gitops_validator.py +179 -0
- package/dist/gaia-security/hooks/modules/security/mutative_verbs.py +850 -0
- package/dist/gaia-security/hooks/modules/security/prompt_validator.py +40 -0
- package/dist/gaia-security/hooks/modules/security/tiers.py +196 -0
- package/dist/gaia-security/hooks/modules/session/__init__.py +10 -0
- package/dist/gaia-security/hooks/modules/session/session_context_writer.py +100 -0
- package/dist/gaia-security/hooks/modules/session/session_event_injector.py +158 -0
- package/dist/gaia-security/hooks/modules/session/session_manager.py +31 -0
- package/dist/gaia-security/hooks/modules/tools/__init__.py +25 -0
- package/dist/gaia-security/hooks/modules/tools/bash_validator.py +708 -0
- package/dist/gaia-security/hooks/modules/tools/cloud_pipe_validator.py +181 -0
- package/dist/gaia-security/hooks/modules/tools/hook_response.py +55 -0
- package/dist/gaia-security/hooks/modules/tools/shell_parser.py +227 -0
- package/dist/gaia-security/hooks/modules/tools/task_validator.py +283 -0
- package/dist/gaia-security/hooks/modules/validation/__init__.py +23 -0
- package/dist/gaia-security/hooks/modules/validation/commit_validator.py +380 -0
- package/dist/gaia-security/hooks/post_tool_use.py +54 -0
- package/dist/gaia-security/hooks/pre_tool_use.py +383 -0
- package/dist/gaia-security/hooks/session_start.py +69 -0
- package/dist/gaia-security/hooks/stop_hook.py +69 -0
- package/dist/gaia-security/hooks/user_prompt_submit.py +177 -0
- package/dist/gaia-security/settings.json +58 -0
- package/git-hooks/commit-msg +41 -0
- package/hooks/README.md +8 -6
- package/hooks/adapters/channel.py +0 -25
- package/hooks/adapters/claude_code.py +364 -125
- package/hooks/elicitation_result.py +132 -0
- package/hooks/hooks.json +10 -1
- package/hooks/modules/README.md +3 -2
- package/hooks/modules/agents/contract_validator.py +3 -51
- package/hooks/modules/agents/response_contract.py +4 -8
- package/hooks/modules/agents/transcript_reader.py +4 -5
- package/hooks/modules/audit/__init__.py +4 -6
- package/hooks/modules/audit/event_detector.py +0 -2
- package/hooks/modules/audit/metrics.py +108 -187
- package/hooks/modules/audit/workflow_auditor.py +0 -4
- package/hooks/modules/audit/workflow_recorder.py +0 -5
- package/hooks/modules/context/compact_context_builder.py +1 -0
- package/hooks/modules/context/context_cache.py +129 -0
- package/hooks/modules/context/context_injector.py +18 -40
- package/hooks/modules/context/context_writer.py +1 -25
- package/hooks/modules/context/contracts_loader.py +7 -10
- package/hooks/modules/core/hook_entry.py +1 -0
- package/hooks/modules/core/paths.py +12 -13
- package/hooks/modules/core/plugin_mode.py +74 -4
- package/hooks/modules/core/plugin_setup.py +395 -23
- package/hooks/modules/events/__init__.py +1 -0
- package/hooks/modules/events/event_writer.py +210 -0
- package/hooks/modules/identity/ops_identity.py +18 -27
- package/hooks/modules/memory/episode_writer.py +1 -6
- package/hooks/modules/orchestrator/__init__.py +1 -0
- package/hooks/modules/orchestrator/delegate_mode.py +128 -0
- package/hooks/modules/security/__init__.py +2 -4
- package/hooks/modules/security/approval_constants.py +5 -1
- package/hooks/modules/security/approval_grants.py +189 -6
- package/hooks/modules/security/approval_messages.py +9 -21
- package/hooks/modules/security/blocked_commands.py +98 -34
- package/hooks/modules/security/command_semantics.py +0 -4
- package/hooks/modules/security/gitops_validator.py +1 -11
- package/hooks/modules/security/mutative_verbs.py +179 -38
- package/hooks/modules/security/tiers.py +1 -19
- package/hooks/modules/session/session_event_injector.py +1 -25
- package/hooks/modules/tools/bash_validator.py +310 -94
- package/hooks/modules/tools/shell_parser.py +0 -1
- package/hooks/modules/tools/task_validator.py +9 -29
- package/hooks/post_tool_use.py +0 -72
- package/hooks/pre_tool_use.py +42 -102
- package/hooks/session_start.py +4 -2
- package/hooks/subagent_start.py +6 -2
- package/hooks/subagent_stop.py +1 -13
- package/hooks/user_prompt_submit.py +119 -37
- package/index.js +1 -1
- package/package.json +5 -3
- package/skills/README.md +3 -5
- package/skills/agent-protocol/SKILL.md +17 -16
- package/skills/agent-protocol/examples.md +6 -6
- package/skills/agent-response/SKILL.md +11 -14
- package/skills/approval/SKILL.md +28 -13
- package/skills/approval/reference.md +2 -2
- package/skills/execution/SKILL.md +1 -1
- package/skills/gaia-patterns/SKILL.md +2 -3
- package/skills/orchestrator-approval/SKILL.md +22 -50
- package/skills/security-tiers/SKILL.md +1 -1
- package/templates/README.md +9 -9
- package/templates/managed-settings.template.json +43 -0
- package/tools/gaia_simulator/runner.py +34 -1
- package/tools/scan/orchestrator.py +13 -0
- package/tools/scan/scanners/base.py +8 -0
- package/tools/scan/scanners/git.py +78 -0
- package/tools/scan/scanners/infrastructure.py +65 -0
- package/tools/scan/scanners/stack.py +110 -0
- package/tools/scan/setup.py +120 -13
- package/tools/scan/workspace.py +85 -0
- package/config/context-contracts.aws.json +0 -42
- package/config/context-contracts.gcp.json +0 -39
- package/skills/project-dispatch/SKILL.md +0 -34
- package/templates/settings.template.json +0 -226
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit tests for the Git Scanner (T022).
|
|
3
|
+
|
|
4
|
+
Tests platform detection from remote URLs, all remotes listing,
|
|
5
|
+
default branch detection, monorepo workspace detection, and
|
|
6
|
+
branch strategy detection.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import textwrap
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any, Dict
|
|
13
|
+
|
|
14
|
+
import pytest
|
|
15
|
+
|
|
16
|
+
from tools.scan.scanners.git import (
|
|
17
|
+
GitScanner,
|
|
18
|
+
_detect_branch_strategy,
|
|
19
|
+
_detect_default_branch,
|
|
20
|
+
_detect_platform_from_url,
|
|
21
|
+
_determine_primary_platform,
|
|
22
|
+
_parse_git_config,
|
|
23
|
+
)
|
|
24
|
+
from tools.scan.tests.conftest import create_git_dir
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@pytest.fixture
|
|
28
|
+
def scanner() -> GitScanner:
|
|
29
|
+
"""Create a GitScanner instance."""
|
|
30
|
+
return GitScanner()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# ---------------------------------------------------------------------------
|
|
34
|
+
# Scanner basics
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class TestGitScannerBasics:
|
|
39
|
+
"""Test scanner metadata and basic contract."""
|
|
40
|
+
|
|
41
|
+
def test_scanner_name(self, scanner: GitScanner) -> None:
|
|
42
|
+
assert scanner.SCANNER_NAME == "git"
|
|
43
|
+
|
|
44
|
+
def test_scanner_version(self, scanner: GitScanner) -> None:
|
|
45
|
+
assert scanner.SCANNER_VERSION == "1.0.0"
|
|
46
|
+
|
|
47
|
+
def test_owned_sections(self, scanner: GitScanner) -> None:
|
|
48
|
+
assert scanner.OWNED_SECTIONS == ["git"]
|
|
49
|
+
|
|
50
|
+
def test_source_tag(self, scanner: GitScanner) -> None:
|
|
51
|
+
assert scanner.source_tag == "scanner:git"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# ---------------------------------------------------------------------------
|
|
55
|
+
# Platform detection from URL
|
|
56
|
+
# ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class TestPlatformDetection:
|
|
60
|
+
"""Test platform detection from remote URLs."""
|
|
61
|
+
|
|
62
|
+
def test_github_ssh(self) -> None:
|
|
63
|
+
assert _detect_platform_from_url("git@github.com:org/repo.git") == "github"
|
|
64
|
+
|
|
65
|
+
def test_github_https(self) -> None:
|
|
66
|
+
assert _detect_platform_from_url("https://github.com/org/repo.git") == "github"
|
|
67
|
+
|
|
68
|
+
def test_gitlab_ssh(self) -> None:
|
|
69
|
+
assert _detect_platform_from_url("git@gitlab.com:group/project.git") == "gitlab"
|
|
70
|
+
|
|
71
|
+
def test_gitlab_https(self) -> None:
|
|
72
|
+
assert _detect_platform_from_url("https://gitlab.com/group/project.git") == "gitlab"
|
|
73
|
+
|
|
74
|
+
def test_bitbucket_ssh(self) -> None:
|
|
75
|
+
assert _detect_platform_from_url("git@bitbucket.org:team/repo.git") == "bitbucket"
|
|
76
|
+
|
|
77
|
+
def test_bitbucket_https(self) -> None:
|
|
78
|
+
assert _detect_platform_from_url("https://bitbucket.org/team/repo.git") == "bitbucket"
|
|
79
|
+
|
|
80
|
+
def test_self_hosted_ssh(self) -> None:
|
|
81
|
+
assert _detect_platform_from_url("git@git.internal.company.com:team/project.git") == "self-hosted"
|
|
82
|
+
|
|
83
|
+
def test_self_hosted_https(self) -> None:
|
|
84
|
+
assert _detect_platform_from_url("https://git.internal.company.com/team/project.git") == "self-hosted"
|
|
85
|
+
|
|
86
|
+
def test_empty_url(self) -> None:
|
|
87
|
+
assert _detect_platform_from_url("") is None
|
|
88
|
+
|
|
89
|
+
def test_none_url(self) -> None:
|
|
90
|
+
assert _detect_platform_from_url("") is None
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# ---------------------------------------------------------------------------
|
|
94
|
+
# Remote detection
|
|
95
|
+
# ---------------------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class TestRemoteDetection:
|
|
99
|
+
"""Test all remotes listed from git config."""
|
|
100
|
+
|
|
101
|
+
def test_detect_github_platform(
|
|
102
|
+
self, scanner: GitScanner, git_project_github: Path
|
|
103
|
+
) -> None:
|
|
104
|
+
result = scanner.scan(git_project_github)
|
|
105
|
+
git_section = result.sections["git"]
|
|
106
|
+
assert git_section["platform"] == "github"
|
|
107
|
+
|
|
108
|
+
def test_detect_gitlab_platform(
|
|
109
|
+
self, scanner: GitScanner, git_project_gitlab: Path
|
|
110
|
+
) -> None:
|
|
111
|
+
result = scanner.scan(git_project_gitlab)
|
|
112
|
+
git_section = result.sections["git"]
|
|
113
|
+
assert git_section["platform"] == "gitlab"
|
|
114
|
+
|
|
115
|
+
def test_detect_bitbucket_platform(
|
|
116
|
+
self, scanner: GitScanner, git_project_bitbucket: Path
|
|
117
|
+
) -> None:
|
|
118
|
+
result = scanner.scan(git_project_bitbucket)
|
|
119
|
+
git_section = result.sections["git"]
|
|
120
|
+
assert git_section["platform"] == "bitbucket"
|
|
121
|
+
|
|
122
|
+
def test_detect_selfhosted_platform(
|
|
123
|
+
self, scanner: GitScanner, git_project_selfhosted: Path
|
|
124
|
+
) -> None:
|
|
125
|
+
result = scanner.scan(git_project_selfhosted)
|
|
126
|
+
git_section = result.sections["git"]
|
|
127
|
+
assert git_section["platform"] == "self-hosted"
|
|
128
|
+
|
|
129
|
+
def test_all_remotes_listed(
|
|
130
|
+
self, scanner: GitScanner, git_project_github: Path
|
|
131
|
+
) -> None:
|
|
132
|
+
result = scanner.scan(git_project_github)
|
|
133
|
+
git_section = result.sections["git"]
|
|
134
|
+
remote_names = [r["name"] for r in git_section["remotes"]]
|
|
135
|
+
assert "origin" in remote_names
|
|
136
|
+
assert "upstream" in remote_names
|
|
137
|
+
assert len(remote_names) == 2
|
|
138
|
+
|
|
139
|
+
def test_remote_has_url_and_platform(
|
|
140
|
+
self, scanner: GitScanner, git_project_github: Path
|
|
141
|
+
) -> None:
|
|
142
|
+
result = scanner.scan(git_project_github)
|
|
143
|
+
git_section = result.sections["git"]
|
|
144
|
+
origin = [r for r in git_section["remotes"] if r["name"] == "origin"][0]
|
|
145
|
+
assert "github.com" in origin["url"]
|
|
146
|
+
assert origin["platform"] == "github"
|
|
147
|
+
|
|
148
|
+
def test_multiple_remotes_different_platforms(
|
|
149
|
+
self, scanner: GitScanner, tmp_path: Path
|
|
150
|
+
) -> None:
|
|
151
|
+
create_git_dir(
|
|
152
|
+
tmp_path,
|
|
153
|
+
remote_url="git@github.com:org/repo.git",
|
|
154
|
+
extra_remotes={"upstream": "git@gitlab.com:group/repo.git"},
|
|
155
|
+
)
|
|
156
|
+
result = scanner.scan(tmp_path)
|
|
157
|
+
git_section = result.sections["git"]
|
|
158
|
+
remote_names = [r["name"] for r in git_section["remotes"]]
|
|
159
|
+
assert "origin" in remote_names
|
|
160
|
+
assert "upstream" in remote_names
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
# ---------------------------------------------------------------------------
|
|
164
|
+
# Default branch detection
|
|
165
|
+
# ---------------------------------------------------------------------------
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class TestDefaultBranchDetection:
|
|
169
|
+
"""Test default branch detection from HEAD."""
|
|
170
|
+
|
|
171
|
+
def test_detect_main_branch(
|
|
172
|
+
self, scanner: GitScanner, git_project_github: Path
|
|
173
|
+
) -> None:
|
|
174
|
+
result = scanner.scan(git_project_github)
|
|
175
|
+
git_section = result.sections["git"]
|
|
176
|
+
assert git_section["default_branch"] == "main"
|
|
177
|
+
|
|
178
|
+
def test_detect_master_branch(
|
|
179
|
+
self, scanner: GitScanner, git_project_bitbucket: Path
|
|
180
|
+
) -> None:
|
|
181
|
+
result = scanner.scan(git_project_bitbucket)
|
|
182
|
+
git_section = result.sections["git"]
|
|
183
|
+
assert git_section["default_branch"] == "master"
|
|
184
|
+
|
|
185
|
+
def test_detect_custom_branch(
|
|
186
|
+
self, scanner: GitScanner, tmp_path: Path
|
|
187
|
+
) -> None:
|
|
188
|
+
create_git_dir(tmp_path, default_branch="develop")
|
|
189
|
+
result = scanner.scan(tmp_path)
|
|
190
|
+
git_section = result.sections["git"]
|
|
191
|
+
assert git_section["default_branch"] == "develop"
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
# ---------------------------------------------------------------------------
|
|
195
|
+
# Monorepo workspace detection
|
|
196
|
+
# ---------------------------------------------------------------------------
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class TestMonorepoWorkspaceDetection:
|
|
200
|
+
"""Test monorepo workspace field in git scanner.
|
|
201
|
+
|
|
202
|
+
Note: Monorepo detection is now owned by StackScanner. The git scanner
|
|
203
|
+
always returns workspace_config=None. These tests verify the git scanner
|
|
204
|
+
no longer duplicates monorepo detection.
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
def test_monorepo_always_none_with_turbo(
|
|
208
|
+
self, scanner: GitScanner, tmp_path: Path
|
|
209
|
+
) -> None:
|
|
210
|
+
create_git_dir(tmp_path)
|
|
211
|
+
(tmp_path / "turbo.json").write_text("{}")
|
|
212
|
+
result = scanner.scan(tmp_path)
|
|
213
|
+
git_section = result.sections["git"]
|
|
214
|
+
assert git_section["monorepo"]["workspace_config"] is None
|
|
215
|
+
|
|
216
|
+
def test_monorepo_always_none_with_nx(
|
|
217
|
+
self, scanner: GitScanner, tmp_path: Path
|
|
218
|
+
) -> None:
|
|
219
|
+
create_git_dir(tmp_path)
|
|
220
|
+
(tmp_path / "nx.json").write_text("{}")
|
|
221
|
+
result = scanner.scan(tmp_path)
|
|
222
|
+
git_section = result.sections["git"]
|
|
223
|
+
assert git_section["monorepo"]["workspace_config"] is None
|
|
224
|
+
|
|
225
|
+
def test_monorepo_always_none_with_lerna(
|
|
226
|
+
self, scanner: GitScanner, tmp_path: Path
|
|
227
|
+
) -> None:
|
|
228
|
+
create_git_dir(tmp_path)
|
|
229
|
+
(tmp_path / "lerna.json").write_text("{}")
|
|
230
|
+
result = scanner.scan(tmp_path)
|
|
231
|
+
git_section = result.sections["git"]
|
|
232
|
+
assert git_section["monorepo"]["workspace_config"] is None
|
|
233
|
+
|
|
234
|
+
def test_monorepo_always_none_with_pnpm(
|
|
235
|
+
self, scanner: GitScanner, tmp_path: Path
|
|
236
|
+
) -> None:
|
|
237
|
+
create_git_dir(tmp_path)
|
|
238
|
+
(tmp_path / "pnpm-workspace.yaml").write_text("packages:\n - 'packages/*'\n")
|
|
239
|
+
result = scanner.scan(tmp_path)
|
|
240
|
+
git_section = result.sections["git"]
|
|
241
|
+
assert git_section["monorepo"]["workspace_config"] is None
|
|
242
|
+
|
|
243
|
+
def test_monorepo_always_none_with_npm(
|
|
244
|
+
self, scanner: GitScanner, tmp_path: Path
|
|
245
|
+
) -> None:
|
|
246
|
+
create_git_dir(tmp_path)
|
|
247
|
+
pkg = {"name": "test", "workspaces": ["packages/*"]}
|
|
248
|
+
(tmp_path / "package.json").write_text(json.dumps(pkg))
|
|
249
|
+
result = scanner.scan(tmp_path)
|
|
250
|
+
git_section = result.sections["git"]
|
|
251
|
+
assert git_section["monorepo"]["workspace_config"] is None
|
|
252
|
+
|
|
253
|
+
def test_no_workspace_config(
|
|
254
|
+
self, scanner: GitScanner, git_project_github: Path
|
|
255
|
+
) -> None:
|
|
256
|
+
result = scanner.scan(git_project_github)
|
|
257
|
+
git_section = result.sections["git"]
|
|
258
|
+
assert git_section["monorepo"]["workspace_config"] is None
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
# ---------------------------------------------------------------------------
|
|
262
|
+
# Branch strategy detection
|
|
263
|
+
# ---------------------------------------------------------------------------
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class TestBranchStrategyDetection:
|
|
267
|
+
"""Test branch strategy detection from branch patterns."""
|
|
268
|
+
|
|
269
|
+
def test_detect_gitflow(
|
|
270
|
+
self, scanner: GitScanner, git_project_gitflow: Path
|
|
271
|
+
) -> None:
|
|
272
|
+
result = scanner.scan(git_project_gitflow)
|
|
273
|
+
git_section = result.sections["git"]
|
|
274
|
+
strategy = git_section["branch_strategy"]
|
|
275
|
+
assert strategy["detected"] is True
|
|
276
|
+
assert strategy["pattern"] == "gitflow"
|
|
277
|
+
|
|
278
|
+
def test_detect_trunk_based(
|
|
279
|
+
self, scanner: GitScanner, tmp_path: Path
|
|
280
|
+
) -> None:
|
|
281
|
+
create_git_dir(tmp_path, default_branch="main")
|
|
282
|
+
result = scanner.scan(tmp_path)
|
|
283
|
+
git_section = result.sections["git"]
|
|
284
|
+
strategy = git_section["branch_strategy"]
|
|
285
|
+
assert strategy["detected"] is True
|
|
286
|
+
assert strategy["pattern"] == "trunk-based"
|
|
287
|
+
|
|
288
|
+
def test_detect_github_flow(
|
|
289
|
+
self, scanner: GitScanner, tmp_path: Path
|
|
290
|
+
) -> None:
|
|
291
|
+
create_git_dir(
|
|
292
|
+
tmp_path,
|
|
293
|
+
default_branch="main",
|
|
294
|
+
branches=["feature/new-ui", "feature/api-v2", "fix/bug-123", "chore/deps"],
|
|
295
|
+
)
|
|
296
|
+
result = scanner.scan(tmp_path)
|
|
297
|
+
git_section = result.sections["git"]
|
|
298
|
+
strategy = git_section["branch_strategy"]
|
|
299
|
+
assert strategy["detected"] is True
|
|
300
|
+
assert strategy["pattern"] == "github-flow"
|
|
301
|
+
|
|
302
|
+
def test_gitflow_indicators_include_develop(
|
|
303
|
+
self, scanner: GitScanner, git_project_gitflow: Path
|
|
304
|
+
) -> None:
|
|
305
|
+
result = scanner.scan(git_project_gitflow)
|
|
306
|
+
strategy = result.sections["git"]["branch_strategy"]
|
|
307
|
+
assert any("develop" in ind for ind in strategy["indicators"])
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
# ---------------------------------------------------------------------------
|
|
311
|
+
# No .git directory
|
|
312
|
+
# ---------------------------------------------------------------------------
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
class TestNoGitDirectory:
|
|
316
|
+
"""Test behavior when no .git directory exists."""
|
|
317
|
+
|
|
318
|
+
def test_no_git_returns_section_with_nulls(
|
|
319
|
+
self, scanner: GitScanner, empty_project: Path
|
|
320
|
+
) -> None:
|
|
321
|
+
result = scanner.scan(empty_project)
|
|
322
|
+
git_section = result.sections["git"]
|
|
323
|
+
assert git_section["platform"] is None
|
|
324
|
+
assert git_section["remotes"] == []
|
|
325
|
+
assert git_section["default_branch"] is None
|
|
326
|
+
|
|
327
|
+
def test_no_git_branch_strategy_not_detected(
|
|
328
|
+
self, scanner: GitScanner, empty_project: Path
|
|
329
|
+
) -> None:
|
|
330
|
+
result = scanner.scan(empty_project)
|
|
331
|
+
strategy = result.sections["git"]["branch_strategy"]
|
|
332
|
+
assert strategy["detected"] is False
|
|
333
|
+
|
|
334
|
+
def test_no_git_still_has_source_tag(
|
|
335
|
+
self, scanner: GitScanner, empty_project: Path
|
|
336
|
+
) -> None:
|
|
337
|
+
result = scanner.scan(empty_project)
|
|
338
|
+
assert result.sections["git"]["_source"] == "scanner:git"
|
|
339
|
+
|
|
340
|
+
def test_no_git_has_warning(
|
|
341
|
+
self, scanner: GitScanner, empty_project: Path
|
|
342
|
+
) -> None:
|
|
343
|
+
result = scanner.scan(empty_project)
|
|
344
|
+
assert any("No .git" in w for w in result.warnings)
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
# ---------------------------------------------------------------------------
|
|
348
|
+
# Git in subdirectory (monorepo wrapper / nested repo)
|
|
349
|
+
# ---------------------------------------------------------------------------
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
class TestGitInSubdirectory:
|
|
353
|
+
"""Test that scanner finds .git in immediate subdirectories."""
|
|
354
|
+
|
|
355
|
+
def test_finds_git_in_subdirectory(
|
|
356
|
+
self, scanner: GitScanner, tmp_path: Path
|
|
357
|
+
) -> None:
|
|
358
|
+
sub = tmp_path / "my-monorepo"
|
|
359
|
+
sub.mkdir()
|
|
360
|
+
create_git_dir(
|
|
361
|
+
sub,
|
|
362
|
+
remote_url="git@gitlab.com:org/my-monorepo.git",
|
|
363
|
+
default_branch="main",
|
|
364
|
+
)
|
|
365
|
+
result = scanner.scan(tmp_path)
|
|
366
|
+
git_section = result.sections["git"]
|
|
367
|
+
assert git_section["platform"] == "gitlab"
|
|
368
|
+
assert git_section["default_branch"] == "main"
|
|
369
|
+
assert len(git_section["remotes"]) >= 1
|
|
370
|
+
|
|
371
|
+
def test_git_root_field_set_when_in_subdir(
|
|
372
|
+
self, scanner: GitScanner, tmp_path: Path
|
|
373
|
+
) -> None:
|
|
374
|
+
sub = tmp_path / "qxo-monorepo"
|
|
375
|
+
sub.mkdir()
|
|
376
|
+
create_git_dir(sub)
|
|
377
|
+
result = scanner.scan(tmp_path)
|
|
378
|
+
git_section = result.sections["git"]
|
|
379
|
+
assert git_section["git_root"] == "qxo-monorepo"
|
|
380
|
+
|
|
381
|
+
def test_git_root_not_set_when_at_root(
|
|
382
|
+
self, scanner: GitScanner, tmp_path: Path
|
|
383
|
+
) -> None:
|
|
384
|
+
create_git_dir(tmp_path)
|
|
385
|
+
result = scanner.scan(tmp_path)
|
|
386
|
+
git_section = result.sections["git"]
|
|
387
|
+
assert "git_root" not in git_section
|
|
388
|
+
|
|
389
|
+
def test_warning_when_git_in_subdir(
|
|
390
|
+
self, scanner: GitScanner, tmp_path: Path
|
|
391
|
+
) -> None:
|
|
392
|
+
sub = tmp_path / "inner-repo"
|
|
393
|
+
sub.mkdir()
|
|
394
|
+
create_git_dir(sub)
|
|
395
|
+
result = scanner.scan(tmp_path)
|
|
396
|
+
assert any("subdirectory" in w for w in result.warnings)
|
|
397
|
+
|
|
398
|
+
def test_no_git_anywhere_returns_nulls(
|
|
399
|
+
self, scanner: GitScanner, tmp_path: Path
|
|
400
|
+
) -> None:
|
|
401
|
+
(tmp_path / "some-dir").mkdir()
|
|
402
|
+
result = scanner.scan(tmp_path)
|
|
403
|
+
git_section = result.sections["git"]
|
|
404
|
+
assert git_section["platform"] is None
|
|
405
|
+
assert git_section["remotes"] == []
|
|
406
|
+
|
|
407
|
+
def test_skips_dotdirs_and_vendor(
|
|
408
|
+
self, scanner: GitScanner, tmp_path: Path
|
|
409
|
+
) -> None:
|
|
410
|
+
# .git in a hidden dir should not be picked up as a subdirectory match
|
|
411
|
+
hidden = tmp_path / ".hidden-repo"
|
|
412
|
+
hidden.mkdir()
|
|
413
|
+
create_git_dir(hidden, remote_url="git@github.com:hidden/repo.git")
|
|
414
|
+
vendor = tmp_path / "vendor"
|
|
415
|
+
vendor.mkdir()
|
|
416
|
+
create_git_dir(vendor, remote_url="git@github.com:vendor/repo.git")
|
|
417
|
+
result = scanner.scan(tmp_path)
|
|
418
|
+
git_section = result.sections["git"]
|
|
419
|
+
assert git_section["platform"] is None
|