@danmoisan/drm-copilot-mcp 0.0.1
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/LICENSE +21 -0
- package/README.md +50 -0
- package/out/mcp-server.js +17323 -0
- package/package.json +36 -0
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/MEMORY.md +3 -0
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_repo_root_is_source_of_truth.md +11 -0
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_vsce_verify_package_location.md +19 -0
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/project_extension_location.md +11 -0
- package/resources/claude-customizations/.claude/agent-memory/prd-feature/MEMORY.md +1 -0
- package/resources/claude-customizations/.claude/agent-memory/prd-feature/project_push_down_pattern.md +13 -0
- package/resources/claude-customizations/.claude/agent-memory/task-researcher/MEMORY.md +3 -0
- package/resources/claude-customizations/.claude/agent-memory/task-researcher/project_push_down_claude_dir.md +11 -0
- package/resources/claude-customizations/.claude/agents/atomic-executor.md +135 -0
- package/resources/claude-customizations/.claude/agents/atomic-planner.md +71 -0
- package/resources/claude-customizations/.claude/agents/csharp-typed-engineer.md +69 -0
- package/resources/claude-customizations/.claude/agents/epic-review.md +40 -0
- package/resources/claude-customizations/.claude/agents/feature-review.md +136 -0
- package/resources/claude-customizations/.claude/agents/orchestrator.md +83 -0
- package/resources/claude-customizations/.claude/agents/powershell-typed-engineer.md +80 -0
- package/resources/claude-customizations/.claude/agents/prd-feature.md +42 -0
- package/resources/claude-customizations/.claude/agents/python-typed-engineer.md +72 -0
- package/resources/claude-customizations/.claude/agents/staged-review.md +41 -0
- package/resources/claude-customizations/.claude/agents/status-updater.md +41 -0
- package/resources/claude-customizations/.claude/agents/task-researcher.md +81 -0
- package/resources/claude-customizations/.claude/agents/typescript-engineer.md +24 -0
- package/resources/claude-customizations/.claude/hooks/check-powershell-test-purity.ps1 +111 -0
- package/resources/claude-customizations/.claude/hooks/check-python-test-purity.ps1 +146 -0
- package/resources/claude-customizations/.claude/hooks/enforce-evidence-locations.ps1 +150 -0
- package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +238 -0
- package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +147 -0
- package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +235 -0
- package/resources/claude-customizations/.claude/hooks/validate-bash.ps1 +69 -0
- package/resources/claude-customizations/.claude/hooks/validate-executor-output.ps1 +296 -0
- package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage.ps1 +389 -0
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +141 -0
- package/resources/claude-customizations/.claude/hooks/validate-planner-output.ps1 +288 -0
- package/resources/claude-customizations/.claude/hooks/validate-required-artifact-output.ps1 +171 -0
- package/resources/claude-customizations/.claude/hooks/validate-task-researcher-output.ps1 +142 -0
- package/resources/claude-customizations/.claude/rules/csharp.md +62 -0
- package/resources/claude-customizations/.claude/rules/general-code-change.md +71 -0
- package/resources/claude-customizations/.claude/rules/general-unit-test.md +60 -0
- package/resources/claude-customizations/.claude/rules/powershell.md +97 -0
- package/resources/claude-customizations/.claude/rules/python-suppressions.md +143 -0
- package/resources/claude-customizations/.claude/rules/python.md +99 -0
- package/resources/claude-customizations/.claude/rules/self-explanatory-code-commenting.md +97 -0
- package/resources/claude-customizations/.claude/rules/tonality.md +80 -0
- package/resources/claude-customizations/.claude/rules/typescript-suppressions.md +66 -0
- package/resources/claude-customizations/.claude/rules/typescript.md +45 -0
- package/resources/claude-customizations/.claude/settings.json +144 -0
- package/resources/claude-customizations/.claude/skills/acceptance-criteria-tracking/SKILL.md +102 -0
- package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +189 -0
- package/resources/claude-customizations/.claude/skills/commit-message/SKILL.md +65 -0
- package/resources/claude-customizations/.claude/skills/csharp-change-budget-router/SKILL.md +90 -0
- package/resources/claude-customizations/.claude/skills/csharp-orchestration-state-machine/SKILL.md +58 -0
- package/resources/claude-customizations/.claude/skills/csharp-qa-gate/SKILL.md +77 -0
- package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +164 -0
- package/resources/claude-customizations/.claude/skills/execute-hard-lock/SKILL.md +82 -0
- package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +115 -0
- package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +167 -0
- package/resources/claude-customizations/.claude/skills/fill-feature-docs/SKILL.md +22 -0
- package/resources/claude-customizations/.claude/skills/invoke-csharp-engineer/SKILL.md +64 -0
- package/resources/claude-customizations/.claude/skills/invoke-powershell-engineer/SKILL.md +65 -0
- package/resources/claude-customizations/.claude/skills/invoke-python-engineer/SKILL.md +64 -0
- package/resources/claude-customizations/.claude/skills/make-skill-template/SKILL.md +147 -0
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +132 -0
- package/resources/claude-customizations/.claude/skills/policy-audit-template-usage/SKILL.md +49 -0
- package/resources/claude-customizations/.claude/skills/policy-compliance-order/SKILL.md +40 -0
- package/resources/claude-customizations/.claude/skills/powershell-change-budget-router/SKILL.md +49 -0
- package/resources/claude-customizations/.claude/skills/powershell-orchestration-state-machine/SKILL.md +58 -0
- package/resources/claude-customizations/.claude/skills/powershell-qa-gate/SKILL.md +77 -0
- package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +50 -0
- package/resources/claude-customizations/.claude/skills/pr-base-branch-merge-base/SKILL.md +56 -0
- package/resources/claude-customizations/.claude/skills/pr-context-artifacts/SKILL.md +30 -0
- package/resources/claude-customizations/.claude/skills/python-change-budget-router/SKILL.md +79 -0
- package/resources/claude-customizations/.claude/skills/python-qa-gate/SKILL.md +77 -0
- package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +40 -0
- package/resources/claude-customizations/.claude/skills/research-issue/SKILL.md +67 -0
- package/resources/claude-customizations/.claude/skills/review-epic/SKILL.md +21 -0
- package/resources/claude-customizations/.claude/skills/review-feature/SKILL.md +25 -0
- package/resources/claude-customizations/.claude/skills/review-staged/SKILL.md +21 -0
- package/resources/claude-customizations/.claude/skills/skill-canonical-location-audit/SKILL.md +49 -0
- package/resources/claude-customizations/.claude/skills/translate-copilot-to-claude/SKILL.md +295 -0
- package/resources/claude-customizations/.claude/skills/update-status/SKILL.md +21 -0
- package/resources/claude-dir-customizations/.mcp.json +8 -0
- package/resources/codex-and-agents-customizations/.agents/README.md +86 -0
- package/resources/codex-and-agents-customizations/.agents/skills/README.md +49 -0
- package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +107 -0
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-executor/SKILL.md +73 -0
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +194 -0
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +87 -0
- package/resources/codex-and-agents-customizations/.agents/skills/commit-message/SKILL.md +70 -0
- package/resources/codex-and-agents-customizations/.agents/skills/commit-message-conventions/SKILL.md +95 -0
- package/resources/codex-and-agents-customizations/.agents/skills/csharp/SKILL.md +67 -0
- package/resources/codex-and-agents-customizations/.agents/skills/csharp-change-budget-router/SKILL.md +94 -0
- package/resources/codex-and-agents-customizations/.agents/skills/csharp-orchestration-state-machine/SKILL.md +64 -0
- package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +82 -0
- package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +168 -0
- package/resources/codex-and-agents-customizations/.agents/skills/execute-hard-lock/SKILL.md +88 -0
- package/resources/codex-and-agents-customizations/.agents/skills/feature-promotion-lifecycle/SKILL.md +129 -0
- package/resources/codex-and-agents-customizations/.agents/skills/feature-review/SKILL.md +106 -0
- package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +181 -0
- package/resources/codex-and-agents-customizations/.agents/skills/fill-feature-docs/SKILL.md +27 -0
- package/resources/codex-and-agents-customizations/.agents/skills/invoke-csharp-engineer/SKILL.md +73 -0
- package/resources/codex-and-agents-customizations/.agents/skills/invoke-powershell-engineer/SKILL.md +74 -0
- package/resources/codex-and-agents-customizations/.agents/skills/invoke-python-engineer/SKILL.md +73 -0
- package/resources/codex-and-agents-customizations/.agents/skills/make-skill-template/SKILL.md +152 -0
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +143 -0
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +317 -0
- package/resources/codex-and-agents-customizations/.agents/skills/policy-audit-template-usage/SKILL.md +53 -0
- package/resources/codex-and-agents-customizations/.agents/skills/policy-compliance-order/SKILL.md +49 -0
- package/resources/codex-and-agents-customizations/.agents/skills/powershell/SKILL.md +102 -0
- package/resources/codex-and-agents-customizations/.agents/skills/powershell-change-budget-router/SKILL.md +53 -0
- package/resources/codex-and-agents-customizations/.agents/skills/powershell-orchestration-state-machine/SKILL.md +64 -0
- package/resources/codex-and-agents-customizations/.agents/skills/powershell-qa-gate/SKILL.md +83 -0
- package/resources/codex-and-agents-customizations/.agents/skills/pr-author/SKILL.md +55 -0
- package/resources/codex-and-agents-customizations/.agents/skills/pr-authoring/SKILL.md +124 -0
- package/resources/codex-and-agents-customizations/.agents/skills/pr-base-branch-merge-base/SKILL.md +60 -0
- package/resources/codex-and-agents-customizations/.agents/skills/pr-context-artifacts/SKILL.md +34 -0
- package/resources/codex-and-agents-customizations/.agents/skills/python/SKILL.md +104 -0
- package/resources/codex-and-agents-customizations/.agents/skills/python-change-budget-router/SKILL.md +84 -0
- package/resources/codex-and-agents-customizations/.agents/skills/python-qa-gate/SKILL.md +82 -0
- package/resources/codex-and-agents-customizations/.agents/skills/python-suppressions/SKILL.md +148 -0
- package/resources/codex-and-agents-customizations/.agents/skills/remediation-handoff-atomic-planner/SKILL.md +49 -0
- package/resources/codex-and-agents-customizations/.agents/skills/repo-automation-adapter/SKILL.md +142 -0
- package/resources/codex-and-agents-customizations/.agents/skills/repo-automation-adapter/agents/openai.yaml +5 -0
- package/resources/codex-and-agents-customizations/.agents/skills/research-issue/SKILL.md +72 -0
- package/resources/codex-and-agents-customizations/.agents/skills/review-epic/SKILL.md +26 -0
- package/resources/codex-and-agents-customizations/.agents/skills/review-feature/SKILL.md +30 -0
- package/resources/codex-and-agents-customizations/.agents/skills/review-staged/SKILL.md +26 -0
- package/resources/codex-and-agents-customizations/.agents/skills/self-explanatory-code-commenting/SKILL.md +102 -0
- package/resources/codex-and-agents-customizations/.agents/skills/skill-canonical-location-audit/SKILL.md +52 -0
- package/resources/codex-and-agents-customizations/.agents/skills/translate-copilot-to-claude/SKILL.md +317 -0
- package/resources/codex-and-agents-customizations/.agents/skills/typescript/SKILL.md +50 -0
- package/resources/codex-and-agents-customizations/.agents/skills/typescript-suppressions/SKILL.md +71 -0
- package/resources/codex-and-agents-customizations/.agents/skills/update-status/SKILL.md +26 -0
- package/resources/codex-and-agents-customizations/.codex/agents/5.1-beast-adjusted.toml +23 -0
- package/resources/codex-and-agents-customizations/.codex/agents/5.1-thinking-beast-mode-adjusted.toml +23 -0
- package/resources/codex-and-agents-customizations/.codex/agents/api-architect.toml +23 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +151 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +93 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planning.toml +24 -0
- package/resources/codex-and-agents-customizations/.codex/agents/commentary-remediation.toml +23 -0
- package/resources/codex-and-agents-customizations/.codex/agents/commit-steward.toml +20 -0
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-atomic-executor.toml +24 -0
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-atomic-planning.toml +25 -0
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-orchestrator.toml +56 -0
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer.toml +97 -0
- package/resources/codex-and-agents-customizations/.codex/agents/epic-review.toml +52 -0
- package/resources/codex-and-agents-customizations/.codex/agents/expert-nextjs-developer.toml +23 -0
- package/resources/codex-and-agents-customizations/.codex/agents/expert-react-frontend-engineer.toml +23 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +149 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer.toml +60 -0
- package/resources/codex-and-agents-customizations/.codex/agents/gpt-5-beast-mode.toml +23 -0
- package/resources/codex-and-agents-customizations/.codex/agents/hlbpa.toml +23 -0
- package/resources/codex-and-agents-customizations/.codex/agents/mentor.toml +23 -0
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +121 -0
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-executor.toml +24 -0
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-planning.toml +25 -0
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-di-unit-test-engineer.toml +24 -0
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-orchestrator.toml +56 -0
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer.toml +108 -0
- package/resources/codex-and-agents-customizations/.codex/agents/pr-author.toml +26 -0
- package/resources/codex-and-agents-customizations/.codex/agents/prd-feature.toml +53 -0
- package/resources/codex-and-agents-customizations/.codex/agents/prd.toml +23 -0
- package/resources/codex-and-agents-customizations/.codex/agents/pytest-unit-test-coding.toml +24 -0
- package/resources/codex-and-agents-customizations/.codex/agents/python-atomic-executor.toml +24 -0
- package/resources/codex-and-agents-customizations/.codex/agents/python-atomic-planning.toml +25 -0
- package/resources/codex-and-agents-customizations/.codex/agents/python-execution-only-typed.toml +24 -0
- package/resources/codex-and-agents-customizations/.codex/agents/python-orchestrator.toml +54 -0
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer.toml +100 -0
- package/resources/codex-and-agents-customizations/.codex/agents/staged-review.toml +53 -0
- package/resources/codex-and-agents-customizations/.codex/agents/status-updater.toml +53 -0
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher.toml +103 -0
- package/resources/codex-and-agents-customizations/.codex/agents/tdd-green.toml +23 -0
- package/resources/codex-and-agents-customizations/.codex/agents/tdd-red.toml +23 -0
- package/resources/codex-and-agents-customizations/.codex/agents/tdd-refactor.toml +23 -0
- package/resources/codex-and-agents-customizations/.codex/agents/typescript-engineer.toml +48 -0
- package/resources/codex-and-agents-customizations/.codex/agents/voidbeast-gpt41enhanced.toml +23 -0
- package/resources/codex-and-agents-customizations/.codex/codex-web-setup.plan.md +26 -0
- package/resources/codex-and-agents-customizations/.codex/codex-web-setup.sh +384 -0
- package/resources/codex-and-agents-customizations/.codex/config.toml +137 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/check-powershell-test-purity.ps1 +113 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/check-python-test-purity.ps1 +149 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-evidence-locations.ps1 +153 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +241 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +150 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +238 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-bash.ps1 +72 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-feature-review-coverage.ps1 +265 -0
- package/resources/codex-and-agents-customizations/.codex/prompts/feature-review-remediate.md +10 -0
- package/resources/codex-and-agents-customizations/.codex/prompts/generate-commit-message-repo.md +11 -0
- package/resources/codex-and-agents-customizations/.codex/prompts/generate-pr.md +15 -0
- package/resources/codex-and-agents-customizations/.codex/prompts/orchestrate-work.md +22 -0
- package/resources/codex-and-agents-customizations/AGENTS.md +317 -0
- package/resources/customizations/.github/agents/5.1-Beast-adjusted.agent.md +181 -0
- package/resources/customizations/.github/agents/5.1-Thinking-Beast-Mode-adjusted.agent.md +361 -0
- package/resources/customizations/.github/agents/Powershell DI Unit Test Engineer.agent.md +192 -0
- package/resources/customizations/.github/agents/api-architect.agent.md +40 -0
- package/resources/customizations/.github/agents/atomic_executor.agent.md +251 -0
- package/resources/customizations/.github/agents/atomic_planning.agent.md +658 -0
- package/resources/customizations/.github/agents/commentary-remediation.agent.md +35 -0
- package/resources/customizations/.github/agents/commit-steward.agent.md +200 -0
- package/resources/customizations/.github/agents/csharp-atomic-executor.agent.md +288 -0
- package/resources/customizations/.github/agents/csharp-atomic-planning.agent.md +354 -0
- package/resources/customizations/.github/agents/csharp-orchestrator.agent.md +375 -0
- package/resources/customizations/.github/agents/csharp-typed-engineer.agent.md +285 -0
- package/resources/customizations/.github/agents/epic-review.agent.md +374 -0
- package/resources/customizations/.github/agents/expert-nextjs-developer.agent.md +477 -0
- package/resources/customizations/.github/agents/expert-react-frontend-engineer.agent.md +739 -0
- package/resources/customizations/.github/agents/feature-review.agent.md +49 -0
- package/resources/customizations/.github/agents/gpt-5-beast-mode.agent.md +116 -0
- package/resources/customizations/.github/agents/hlbpa.agent.md +219 -0
- package/resources/customizations/.github/agents/mentor.agent.md +32 -0
- package/resources/customizations/.github/agents/orchestrator.agent.md +449 -0
- package/resources/customizations/.github/agents/powershell-atomic-executor.agent.md +287 -0
- package/resources/customizations/.github/agents/powershell-atomic-planning.agent.md +647 -0
- package/resources/customizations/.github/agents/powershell-orchestrator.agent.md +382 -0
- package/resources/customizations/.github/agents/powershell-typed-engineer.agent.md +293 -0
- package/resources/customizations/.github/agents/pr-author.agent.md +138 -0
- package/resources/customizations/.github/agents/prd-feature.agent.md +52 -0
- package/resources/customizations/.github/agents/prd.agent.md +202 -0
- package/resources/customizations/.github/agents/pytest-unit-test-coding.agent.md +202 -0
- package/resources/customizations/.github/agents/python-atomic-executor.agent.md +289 -0
- package/resources/customizations/.github/agents/python-atomic-planning.agent.md +429 -0
- package/resources/customizations/.github/agents/python-execution-only-typed.agent.md +217 -0
- package/resources/customizations/.github/agents/python-orchestrator.agent.md +380 -0
- package/resources/customizations/.github/agents/python-typed-engineer.agent.md +271 -0
- package/resources/customizations/.github/agents/staged-review.agent.md +246 -0
- package/resources/customizations/.github/agents/status_updater.agent.md +279 -0
- package/resources/customizations/.github/agents/task-researcher.agent.md +298 -0
- package/resources/customizations/.github/agents/tdd-green.agent.md +60 -0
- package/resources/customizations/.github/agents/tdd-red.agent.md +66 -0
- package/resources/customizations/.github/agents/tdd-refactor.agent.md +94 -0
- package/resources/customizations/.github/agents/typescript-engineer.agent.md +167 -0
- package/resources/customizations/.github/agents/voidbeast-gpt41enhanced.agent.md +230 -0
- package/resources/customizations/.github/codex/execute-hard-lock.prompt.md +105 -0
- package/resources/customizations/.github/codex/resume-hard-lock.prompt.md +92 -0
- package/resources/customizations/.github/copilot-instructions.md +7 -0
- package/resources/customizations/.github/instructions/csharp-code-change.instructions.md +184 -0
- package/resources/customizations/.github/instructions/csharp-unit-test.instructions.md +52 -0
- package/resources/customizations/.github/instructions/general-code-change.instructions.md +290 -0
- package/resources/customizations/.github/instructions/general-unit-test.instructions.md +106 -0
- package/resources/customizations/.github/instructions/github-actions-ci-cd-best-practices.instructions.md +607 -0
- package/resources/customizations/.github/instructions/github-actions.instructions.md +23 -0
- package/resources/customizations/.github/instructions/powershell-code-change.instructions.md +81 -0
- package/resources/customizations/.github/instructions/powershell-unit-test.instructions.md +69 -0
- package/resources/customizations/.github/instructions/python-code-change.instructions.md +232 -0
- package/resources/customizations/.github/instructions/python-suppressions.instructions.md +609 -0
- package/resources/customizations/.github/instructions/python-unit-test.instructions.md +71 -0
- package/resources/customizations/.github/instructions/self-explanatory-code-commenting.instructions.md +238 -0
- package/resources/customizations/.github/instructions/tonality.instructions.md +133 -0
- package/resources/customizations/.github/instructions/typescript-code-change.instructions.md +203 -0
- package/resources/customizations/.github/instructions/typescript-suppressions.instructions.md +157 -0
- package/resources/customizations/.github/instructions/typescript-unit-test.instructions.md +112 -0
- package/resources/customizations/.github/prompts/add-educational-comments.prompt.md +129 -0
- package/resources/customizations/.github/prompts/breakdown-bug-prd.prompt.md +29 -0
- package/resources/customizations/.github/prompts/breakdown-epic-arch.prompt.md +66 -0
- package/resources/customizations/.github/prompts/breakdown-epic-pm.prompt.md +58 -0
- package/resources/customizations/.github/prompts/breakdown-feature-implementation.prompt.md +128 -0
- package/resources/customizations/.github/prompts/breakdown-feature-prd.prompt.md +61 -0
- package/resources/customizations/.github/prompts/code-exemplars-blueprint-generator.prompt.md +126 -0
- package/resources/customizations/.github/prompts/drafts/create-github-issues-feature-from-implementation-plan.prompt.md +28 -0
- package/resources/customizations/.github/prompts/drafts/create-implementation-plan.prompt.md +158 -0
- package/resources/customizations/.github/prompts/drafts/create-technical-spike.prompt.md +231 -0
- package/resources/customizations/.github/prompts/drafts/potential-feature-prd.prompt.md +19 -0
- package/resources/customizations/.github/prompts/drafts/update-implementation-plan.prompt.md +158 -0
- package/resources/customizations/.github/prompts/execute-plan-template.md +21 -0
- package/resources/customizations/.github/prompts/export-chat.prompt.md +7 -0
- package/resources/customizations/.github/prompts/fillout-prd-feature.prompt.md +46 -0
- package/resources/customizations/.github/prompts/generate-atomic-plan.prompt.md +96 -0
- package/resources/customizations/.github/prompts/generate-commit-message-repo.prompt.md +108 -0
- package/resources/customizations/.github/prompts/generate-pr.prompt.md +151 -0
- package/resources/customizations/.github/prompts/javascript-typescript-jest.prompt.md +44 -0
- package/resources/customizations/.github/prompts/orchestrate-csharp-work.prompt.md +66 -0
- package/resources/customizations/.github/prompts/orchestrate-powershell-work.prompt.md +50 -0
- package/resources/customizations/.github/prompts/orchestrate-python-work.prompt.md +50 -0
- package/resources/customizations/.github/prompts/orchestrate-work.prompt.md +66 -0
- package/resources/customizations/.github/prompts/remediate-comments.prompt.md +53 -0
- package/resources/customizations/.github/prompts/research-issue.prompt.md +125 -0
- package/resources/customizations/.github/prompts/review-epic.prompt.md +94 -0
- package/resources/customizations/.github/prompts/review-feature.prompt.md +130 -0
- package/resources/customizations/.github/prompts/review-staged.prompt.md +43 -0
- package/resources/customizations/.github/prompts/update_status.prompt.md +68 -0
- package/resources/customizations/.github/skills/README.md +26 -0
- package/resources/customizations/.github/skills/acceptance-criteria-tracking/SKILL.md +102 -0
- package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +174 -0
- package/resources/customizations/.github/skills/csharp-change-budget-router/SKILL.md +48 -0
- package/resources/customizations/.github/skills/csharp-orchestration-state-machine/SKILL.md +57 -0
- package/resources/customizations/.github/skills/evidence-and-timestamp-conventions/SKILL.md +135 -0
- package/resources/customizations/.github/skills/feature-promotion-lifecycle/SKILL.md +121 -0
- package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +153 -0
- package/resources/customizations/.github/skills/make-skill-template/SKILL.md +147 -0
- package/resources/customizations/.github/skills/policy-audit-template-usage/SKILL.md +48 -0
- package/resources/customizations/.github/skills/policy-compliance-order/SKILL.md +37 -0
- package/resources/customizations/.github/skills/powershell-change-budget-router/SKILL.md +48 -0
- package/resources/customizations/.github/skills/powershell-orchestration-state-machine/SKILL.md +57 -0
- package/resources/customizations/.github/skills/pr-base-branch-merge-base/SKILL.md +55 -0
- package/resources/customizations/.github/skills/pr-context-artifacts/SKILL.md +29 -0
- package/resources/customizations/.github/skills/remediation-handoff-atomic-planner/SKILL.md +39 -0
- package/resources/customizations/.github/skills/skill-canonical-location-audit/SKILL.md +48 -0
- package/resources/feature-templates/bug/plan.yyyy-MM-ddTHH-mm.md +44 -0
- package/resources/feature-templates/bug/potential_bug.md +59 -0
- package/resources/feature-templates/bug/spec.md +99 -0
- package/resources/feature-templates/epic/initiative.md +43 -0
- package/resources/feature-templates/feature/plan.yyyy-MM-ddTHH-mm.md +53 -0
- package/resources/feature-templates/feature/spec.md +66 -0
- package/resources/feature-templates/feature/user-story.md +42 -0
- package/resources/feature-templates/potential/template.md +33 -0
- package/resources/feature-templates/refactor/plan.yyyy-MM-ddTHH-mm.md +52 -0
- package/resources/feature-templates/refactor/spec.md +69 -0
- package/resources/powershell/PoshQC/PoshQC.Analyzer.psm1 +254 -0
- package/resources/powershell/PoshQC/PoshQC.FileDiscovery.psm1 +138 -0
- package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +409 -0
- package/resources/powershell/PoshQC/PoshQC.psd1 +31 -0
- package/resources/powershell/PoshQC/PoshQC.psm1 +101 -0
- package/resources/powershell/PoshQC/README.md +80 -0
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +59 -0
- package/resources/powershell/PoshQC/settings/pssa.settings.psd1 +55 -0
- package/resources/scripts/dev_tools/__init__.py +0 -0
- package/resources/scripts/dev_tools/agentic_sync.py +819 -0
- package/resources/scripts/dev_tools/codex_native_converter/__init__.py +11 -0
- package/resources/scripts/dev_tools/codex_native_converter/__main__.py +6 -0
- package/resources/scripts/dev_tools/codex_native_converter/cli.py +11 -0
- package/resources/scripts/dev_tools/new_active_feature_folder.py +79 -0
- package/resources/scripts/dev_tools/new_active_feature_folder_docs.py +268 -0
- package/resources/scripts/dev_tools/new_active_feature_folder_flow.py +366 -0
- package/resources/scripts/dev_tools/new_active_feature_folder_io.py +306 -0
- package/resources/scripts/dev_tools/new_active_feature_folder_markdown.py +252 -0
- package/resources/scripts/dev_tools/new_active_feature_folder_models.py +136 -0
- package/resources/scripts/dev_tools/new_potential_bug_entry.py +465 -0
- package/resources/scripts/dev_tools/potential_to_issue.py +421 -0
- package/resources/scripts/dev_tools/potential_to_issue_content.py +212 -0
- package/resources/scripts/dev_tools/pr_context/__init__.py +0 -0
- package/resources/scripts/dev_tools/pr_context/collector.py +619 -0
- package/resources/scripts/dev_tools/pr_context/feature_docs.py +349 -0
- package/resources/scripts/dev_tools/pr_context/git.py +153 -0
- package/resources/scripts/dev_tools/pr_context/github.py +549 -0
- package/resources/scripts/dev_tools/pr_context/models.py +198 -0
- package/resources/scripts/dev_tools/pr_context/render.py +342 -0
- package/resources/scripts/dev_tools/pr_context/render_feature_excerpts.py +256 -0
- package/resources/scripts/dev_tools/pr_context/render_pr_helpers.py +291 -0
- package/resources/scripts/dev_tools/pr_context/summary_helpers.py +386 -0
- package/resources/scripts/dev_tools/pr_context/verification_evidence.py +171 -0
- package/resources/scripts/dev_tools/prompt_mode_contract.py +152 -0
- package/resources/scripts/dev_tools/push_down_claude_customizations.py +188 -0
- package/resources/scripts/dev_tools/push_down_codex_and_agents_customizations.py +139 -0
- package/resources/scripts/dev_tools/push_down_copilot_customizations.py +504 -0
- package/resources/scripts/dev_tools/push_down_copilot_customizations_filesystem.py +217 -0
- package/resources/scripts/dev_tools/push_down_copilot_customizations_rewrites.py +293 -0
- package/resources/scripts/dev_tools/resolve_file_prompt.py +457 -0
- package/resources/scripts/dev_tools/resolve_hard_lock_prompt.py +444 -0
- package/resources/scripts/dev_tools/validate_orchestration_artifacts.py +554 -0
- package/resources/templates/codex_native_converter.py +35 -0
- package/resources/templates/collect_commit_context.py +212 -0
- package/resources/templates/collect_pr_context.py +74 -0
- package/resources/templates/hello_pwsh.ps1 +3 -0
- package/resources/templates/hello_python.py +11 -0
- package/resources/templates/link-parent-child.ps1 +480 -0
- package/resources/templates/new-claude-worktree-session.ps1 +232 -0
- package/resources/templates/new-potential-entry.ps1 +187 -0
- package/resources/templates/new_active_feature_folder.py +67 -0
- package/resources/templates/new_potential_bug_entry.py +54 -0
- package/resources/templates/policy_audit/AGENTS.md +117 -0
- package/resources/templates/policy_audit/code-review.yyyy-MM-ddTHH-mm.md +165 -0
- package/resources/templates/policy_audit/feature-audit.yyyy-MM-ddTHH-mm.md +124 -0
- package/resources/templates/policy_audit/policy-audit.yyyy-MM-ddTHH-mm.md +649 -0
- package/resources/templates/potential_to_issue.py +55 -0
- package/resources/templates/push_down_claude_customizations.py +188 -0
- package/resources/templates/push_down_codex_and_agents_customizations.py +95 -0
- package/resources/templates/push_down_copilot_customizations.py +124 -0
- package/resources/templates/resolve_atomic_plan_prompt.py +75 -0
- package/resources/templates/resolve_hard_lock_prompt.py +65 -0
- package/resources/templates/run-poshqc-analyze-autofix.ps1 +16 -0
- package/resources/templates/run-poshqc-analyze.ps1 +26 -0
- package/resources/templates/run-poshqc-format.ps1 +26 -0
- package/resources/templates/run-poshqc-suite.ps1 +24 -0
- package/resources/templates/run-poshqc-test.ps1 +32 -0
- package/resources/templates/sync-agents-from-instructions.ps1 +400 -0
- package/resources/templates/validate_orchestration_artifacts.py +55 -0
- package/resources/templates/vscode-cli.helpers.ps1 +63 -0
|
@@ -0,0 +1,819 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Synchronize shared .github documents between two local repos.
|
|
3
|
+
|
|
4
|
+
Purpose:
|
|
5
|
+
Ensures that shared governance documents under .github/agents,
|
|
6
|
+
.github/instructions, .github/prompts, and .github/skills stay aligned
|
|
7
|
+
between two repository workspaces. The newest content is propagated when
|
|
8
|
+
files differ, and timestamps are normalized to match when changes are made.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import argparse
|
|
14
|
+
import json
|
|
15
|
+
import logging
|
|
16
|
+
import os
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
from datetime import datetime, timezone
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Literal, Protocol, TypedDict
|
|
21
|
+
|
|
22
|
+
LOGGER = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
ROOT_FOLDERS: tuple[Path, ...] = (
|
|
25
|
+
Path(".github/agents"),
|
|
26
|
+
Path(".github/instructions"),
|
|
27
|
+
Path(".github/prompts"),
|
|
28
|
+
Path(".github/skills"),
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
DecisionType = Literal["equivalent-mtime", "equivalent-content", "synced"]
|
|
32
|
+
ForceDirection = Literal["left-to-right", "right-to-left"]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class SyncActionPayload(TypedDict):
|
|
36
|
+
"""JSON payload schema for a serialized ``SyncAction`` entry."""
|
|
37
|
+
|
|
38
|
+
root: str
|
|
39
|
+
relative_path: str
|
|
40
|
+
left_path: str
|
|
41
|
+
right_path: str
|
|
42
|
+
left_mtime: float
|
|
43
|
+
right_mtime: float
|
|
44
|
+
decision: DecisionType
|
|
45
|
+
source: str | None
|
|
46
|
+
sync_mtime: float | None
|
|
47
|
+
forced: bool
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class SyncSummaryPayload(TypedDict):
|
|
51
|
+
"""JSON payload schema for a serialized ``SyncSummary`` artifact."""
|
|
52
|
+
|
|
53
|
+
repo_left: str
|
|
54
|
+
repo_right: str
|
|
55
|
+
started_at: str
|
|
56
|
+
finished_at: str
|
|
57
|
+
force_direction: ForceDirection | None
|
|
58
|
+
actions: list[SyncActionPayload]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class SyncFileSystem(Protocol):
|
|
62
|
+
"""
|
|
63
|
+
Protocol for filesystem operations required by the sync engine.
|
|
64
|
+
|
|
65
|
+
Purpose:
|
|
66
|
+
Abstracts file system access so that unit tests can run without touching
|
|
67
|
+
the real filesystem, while production uses a concrete implementation.
|
|
68
|
+
|
|
69
|
+
Usage:
|
|
70
|
+
Implement with RealSyncFileSystem for production or an in-memory test
|
|
71
|
+
double for unit tests.
|
|
72
|
+
|
|
73
|
+
Invariants / Constraints:
|
|
74
|
+
- Paths passed in are absolute or workspace-relative Path objects.
|
|
75
|
+
- get_mtime raises FileNotFoundError for missing paths.
|
|
76
|
+
- read_text raises FileNotFoundError for missing paths.
|
|
77
|
+
|
|
78
|
+
Side Effects:
|
|
79
|
+
Implementations may read/write disk, or mutate in-memory stores.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
def list_files(self, root: Path) -> list[Path]:
|
|
83
|
+
"""Return all files under root (recursive)."""
|
|
84
|
+
...
|
|
85
|
+
|
|
86
|
+
def is_file(self, path: Path) -> bool:
|
|
87
|
+
"""Return True when path exists and is a file."""
|
|
88
|
+
...
|
|
89
|
+
|
|
90
|
+
def read_text(self, path: Path) -> str:
|
|
91
|
+
"""Read UTF-8 text from path."""
|
|
92
|
+
...
|
|
93
|
+
|
|
94
|
+
def write_text(self, path: Path, content: str) -> None:
|
|
95
|
+
"""Write UTF-8 text to path."""
|
|
96
|
+
...
|
|
97
|
+
|
|
98
|
+
def get_mtime(self, path: Path) -> float:
|
|
99
|
+
"""Return file modification time (seconds since epoch)."""
|
|
100
|
+
...
|
|
101
|
+
|
|
102
|
+
def set_mtime(self, path: Path, mtime: float) -> None:
|
|
103
|
+
"""Set file modification time (seconds since epoch)."""
|
|
104
|
+
...
|
|
105
|
+
|
|
106
|
+
def ensure_dir(self, path: Path) -> None:
|
|
107
|
+
"""Ensure directory exists (create if missing)."""
|
|
108
|
+
...
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class RealSyncFileSystem:
|
|
112
|
+
"""
|
|
113
|
+
Concrete filesystem implementation for sync operations.
|
|
114
|
+
|
|
115
|
+
Purpose:
|
|
116
|
+
Uses pathlib and os utilities to read/write files and mutate timestamps
|
|
117
|
+
on the real filesystem.
|
|
118
|
+
|
|
119
|
+
Usage:
|
|
120
|
+
Injected into AgenticSyncer for production runs.
|
|
121
|
+
|
|
122
|
+
Side Effects:
|
|
123
|
+
Reads and writes files on disk and updates file timestamps.
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
def list_files(self, root: Path) -> list[Path]:
|
|
127
|
+
"""
|
|
128
|
+
Return all files under root (recursive).
|
|
129
|
+
|
|
130
|
+
Purpose:
|
|
131
|
+
Provide a deterministic list of files to sync under a root folder.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
root (Path): Directory to scan.
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
list[Path]: Sorted list of files under root.
|
|
138
|
+
|
|
139
|
+
Raises:
|
|
140
|
+
None.
|
|
141
|
+
|
|
142
|
+
Side Effects:
|
|
143
|
+
Reads directory contents from disk.
|
|
144
|
+
"""
|
|
145
|
+
if not root.is_dir():
|
|
146
|
+
return []
|
|
147
|
+
|
|
148
|
+
files: list[Path] = []
|
|
149
|
+
# Collect file paths to provide a deterministic, sorted list for sync.
|
|
150
|
+
for path in root.rglob("*"):
|
|
151
|
+
if path.is_file():
|
|
152
|
+
files.append(path)
|
|
153
|
+
return sorted(files)
|
|
154
|
+
|
|
155
|
+
def is_file(self, path: Path) -> bool:
|
|
156
|
+
"""
|
|
157
|
+
Return True when path exists and is a file.
|
|
158
|
+
|
|
159
|
+
Purpose:
|
|
160
|
+
Provide a lightweight file existence check for sync logic.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
path (Path): File path to check.
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
bool: True if path is a file, False otherwise.
|
|
167
|
+
|
|
168
|
+
Raises:
|
|
169
|
+
None.
|
|
170
|
+
|
|
171
|
+
Side Effects:
|
|
172
|
+
Reads filesystem metadata.
|
|
173
|
+
"""
|
|
174
|
+
return path.is_file()
|
|
175
|
+
|
|
176
|
+
def read_text(self, path: Path) -> str:
|
|
177
|
+
"""
|
|
178
|
+
Read UTF-8 text from path.
|
|
179
|
+
|
|
180
|
+
Purpose:
|
|
181
|
+
Load file content for comparison and syncing.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
path (Path): File path to read.
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
str: File contents.
|
|
188
|
+
|
|
189
|
+
Raises:
|
|
190
|
+
FileNotFoundError: When the file is missing.
|
|
191
|
+
|
|
192
|
+
Side Effects:
|
|
193
|
+
Reads file content from disk.
|
|
194
|
+
"""
|
|
195
|
+
return path.read_text(encoding="utf-8")
|
|
196
|
+
|
|
197
|
+
def write_text(self, path: Path, content: str) -> None:
|
|
198
|
+
"""
|
|
199
|
+
Write UTF-8 text to path.
|
|
200
|
+
|
|
201
|
+
Purpose:
|
|
202
|
+
Persist updated file content to disk during synchronization.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
path (Path): File path to write.
|
|
206
|
+
content (str): Content to write.
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
None.
|
|
210
|
+
|
|
211
|
+
Raises:
|
|
212
|
+
None.
|
|
213
|
+
|
|
214
|
+
Side Effects:
|
|
215
|
+
Writes file content to disk and updates modification time.
|
|
216
|
+
"""
|
|
217
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
218
|
+
path.write_text(content, encoding="utf-8")
|
|
219
|
+
|
|
220
|
+
def get_mtime(self, path: Path) -> float:
|
|
221
|
+
"""
|
|
222
|
+
Return file modification time (seconds since epoch).
|
|
223
|
+
|
|
224
|
+
Purpose:
|
|
225
|
+
Provide mtime values for timestamp comparisons.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
path (Path): File path to inspect.
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
float: Modification time in seconds since epoch.
|
|
232
|
+
|
|
233
|
+
Raises:
|
|
234
|
+
FileNotFoundError: When the file is missing.
|
|
235
|
+
|
|
236
|
+
Side Effects:
|
|
237
|
+
Reads filesystem metadata.
|
|
238
|
+
"""
|
|
239
|
+
return path.stat().st_mtime
|
|
240
|
+
|
|
241
|
+
def set_mtime(self, path: Path, mtime: float) -> None:
|
|
242
|
+
"""
|
|
243
|
+
Set file modification time (seconds since epoch).
|
|
244
|
+
|
|
245
|
+
Purpose:
|
|
246
|
+
Normalize timestamps after syncing content.
|
|
247
|
+
|
|
248
|
+
Args:
|
|
249
|
+
path (Path): File path to update.
|
|
250
|
+
mtime (float): Target modification time.
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
None.
|
|
254
|
+
|
|
255
|
+
Raises:
|
|
256
|
+
None.
|
|
257
|
+
|
|
258
|
+
Side Effects:
|
|
259
|
+
Updates file timestamps on disk.
|
|
260
|
+
"""
|
|
261
|
+
os.utime(path, (mtime, mtime))
|
|
262
|
+
|
|
263
|
+
def ensure_dir(self, path: Path) -> None:
|
|
264
|
+
"""
|
|
265
|
+
Ensure directory exists (create if missing).
|
|
266
|
+
|
|
267
|
+
Purpose:
|
|
268
|
+
Prepare artifact output directories before writing logs.
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
path (Path): Directory path to create.
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
None.
|
|
275
|
+
|
|
276
|
+
Raises:
|
|
277
|
+
None.
|
|
278
|
+
|
|
279
|
+
Side Effects:
|
|
280
|
+
Creates directories on disk.
|
|
281
|
+
"""
|
|
282
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
@dataclass(frozen=True, slots=True)
|
|
286
|
+
class SyncAction:
|
|
287
|
+
"""
|
|
288
|
+
Represents the outcome of syncing a single file pair.
|
|
289
|
+
|
|
290
|
+
Purpose:
|
|
291
|
+
Capture the decision made for a matched file and any synchronization
|
|
292
|
+
details needed for auditing and artifact logging.
|
|
293
|
+
|
|
294
|
+
Usage:
|
|
295
|
+
Populated by AgenticSyncer during the sync process and serialized to
|
|
296
|
+
an artifact log.
|
|
297
|
+
|
|
298
|
+
Invariants / Constraints:
|
|
299
|
+
- root and relative_path identify a unique file within the scoped
|
|
300
|
+
.github folders.
|
|
301
|
+
- decision reflects whether a sync occurred or equivalence was assumed.
|
|
302
|
+
- source is populated only when decision == "synced".
|
|
303
|
+
|
|
304
|
+
Side Effects:
|
|
305
|
+
None (pure data container).
|
|
306
|
+
|
|
307
|
+
Attributes:
|
|
308
|
+
root (str): Root folder (e.g., ".github/agents").
|
|
309
|
+
relative_path (str): File path relative to root.
|
|
310
|
+
left_path (str): Full path to the left repo file.
|
|
311
|
+
right_path (str): Full path to the right repo file.
|
|
312
|
+
left_mtime (float): Left file modification time before sync.
|
|
313
|
+
right_mtime (float): Right file modification time before sync.
|
|
314
|
+
decision (DecisionType): Sync decision type.
|
|
315
|
+
source (str | None): "left" or "right" when synced.
|
|
316
|
+
sync_mtime (float | None): Applied mtime when synced.
|
|
317
|
+
forced (bool): Whether forced direction influenced the decision.
|
|
318
|
+
"""
|
|
319
|
+
|
|
320
|
+
root: str
|
|
321
|
+
relative_path: str
|
|
322
|
+
left_path: str
|
|
323
|
+
right_path: str
|
|
324
|
+
left_mtime: float
|
|
325
|
+
right_mtime: float
|
|
326
|
+
decision: DecisionType
|
|
327
|
+
source: str | None
|
|
328
|
+
sync_mtime: float | None
|
|
329
|
+
forced: bool
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
@dataclass(frozen=True, slots=True)
|
|
333
|
+
class SyncSummary:
|
|
334
|
+
"""
|
|
335
|
+
Aggregated summary of a sync run.
|
|
336
|
+
|
|
337
|
+
Purpose:
|
|
338
|
+
Provide a top-level record of the sync operation for artifact logging,
|
|
339
|
+
including timing and per-file outcomes.
|
|
340
|
+
|
|
341
|
+
Usage:
|
|
342
|
+
Returned by AgenticSyncer.sync_repos and serialized to JSON artifacts.
|
|
343
|
+
|
|
344
|
+
Invariants / Constraints:
|
|
345
|
+
- started_at and finished_at are timezone-aware UTC datetimes.
|
|
346
|
+
- actions list preserves the order of processing for traceability.
|
|
347
|
+
|
|
348
|
+
Side Effects:
|
|
349
|
+
None (pure data container).
|
|
350
|
+
|
|
351
|
+
Attributes:
|
|
352
|
+
repo_left (str): Left repository path.
|
|
353
|
+
repo_right (str): Right repository path.
|
|
354
|
+
started_at (datetime): UTC start timestamp.
|
|
355
|
+
finished_at (datetime): UTC finish timestamp.
|
|
356
|
+
force_direction (ForceDirection | None): Forced direction, if any.
|
|
357
|
+
actions (list[SyncAction]): Per-file outcomes.
|
|
358
|
+
"""
|
|
359
|
+
|
|
360
|
+
repo_left: str
|
|
361
|
+
repo_right: str
|
|
362
|
+
started_at: datetime
|
|
363
|
+
finished_at: datetime
|
|
364
|
+
force_direction: ForceDirection | None
|
|
365
|
+
actions: list[SyncAction]
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
class AgenticSyncer:
|
|
369
|
+
"""
|
|
370
|
+
Synchronize matching .github files between two repositories.
|
|
371
|
+
|
|
372
|
+
Purpose:
|
|
373
|
+
Implements the comparison rules for shared .github documents, including
|
|
374
|
+
timestamp equivalence logic, content comparison, and bidirectional sync.
|
|
375
|
+
|
|
376
|
+
Usage:
|
|
377
|
+
syncer = AgenticSyncer(RealSyncFileSystem())
|
|
378
|
+
summary = syncer.sync_repos(left_repo, right_repo)
|
|
379
|
+
|
|
380
|
+
Flow:
|
|
381
|
+
1. Enumerate files under the scoped .github roots for each repo.
|
|
382
|
+
2. Match files by relative path within each root.
|
|
383
|
+
3. Apply timestamp/content comparison rules to determine equivalence.
|
|
384
|
+
4. Sync content from the newer (or forced) file when differences exist.
|
|
385
|
+
5. Normalize timestamps when writes occur.
|
|
386
|
+
|
|
387
|
+
Invariants / Constraints:
|
|
388
|
+
- Only files present in both repos are considered.
|
|
389
|
+
- Timestamp equivalence threshold defaults to 180 seconds.
|
|
390
|
+
- Forced direction bypasses timestamp-equivalence short-circuiting.
|
|
391
|
+
|
|
392
|
+
Side Effects:
|
|
393
|
+
Reads and writes files via the injected filesystem.
|
|
394
|
+
"""
|
|
395
|
+
|
|
396
|
+
def __init__(
|
|
397
|
+
self,
|
|
398
|
+
fs: SyncFileSystem,
|
|
399
|
+
*,
|
|
400
|
+
threshold_seconds: int = 180,
|
|
401
|
+
force_direction: ForceDirection | None = None,
|
|
402
|
+
) -> None:
|
|
403
|
+
"""
|
|
404
|
+
Initialize the sync engine.
|
|
405
|
+
|
|
406
|
+
Args:
|
|
407
|
+
fs (SyncFileSystem): Filesystem abstraction for I/O.
|
|
408
|
+
threshold_seconds (int): Threshold in seconds for mtime equivalence.
|
|
409
|
+
force_direction (ForceDirection | None): Optional forced direction.
|
|
410
|
+
clock (callable): Clock returning epoch seconds for timestamps.
|
|
411
|
+
|
|
412
|
+
Raises:
|
|
413
|
+
ValueError: If threshold_seconds is negative.
|
|
414
|
+
"""
|
|
415
|
+
if threshold_seconds < 0:
|
|
416
|
+
raise ValueError("threshold_seconds must be non-negative")
|
|
417
|
+
self._fs = fs
|
|
418
|
+
self._threshold_seconds = threshold_seconds
|
|
419
|
+
self._force_direction: ForceDirection | None = force_direction
|
|
420
|
+
|
|
421
|
+
def sync_repos(self, left_repo: Path, right_repo: Path) -> SyncSummary:
|
|
422
|
+
"""
|
|
423
|
+
Synchronize matching .github files between two repos.
|
|
424
|
+
|
|
425
|
+
Purpose:
|
|
426
|
+
Applies the sync rules across all scoped .github roots and returns
|
|
427
|
+
a detailed summary of actions.
|
|
428
|
+
|
|
429
|
+
Args:
|
|
430
|
+
left_repo (Path): Left repository workspace.
|
|
431
|
+
right_repo (Path): Right repository workspace.
|
|
432
|
+
|
|
433
|
+
Returns:
|
|
434
|
+
SyncSummary: Summary of per-file decisions and timing.
|
|
435
|
+
"""
|
|
436
|
+
started = datetime.now(timezone.utc)
|
|
437
|
+
actions: list[SyncAction] = []
|
|
438
|
+
|
|
439
|
+
# Process each governance root separately to avoid cross-category matches.
|
|
440
|
+
for root in ROOT_FOLDERS:
|
|
441
|
+
left_root = left_repo / root
|
|
442
|
+
right_root = right_repo / root
|
|
443
|
+
left_map = self._collect_files(left_root)
|
|
444
|
+
right_map = self._collect_files(right_root)
|
|
445
|
+
|
|
446
|
+
# Compare only files present in both repos to avoid one-way copies.
|
|
447
|
+
shared_relatives = sorted(set(left_map) & set(right_map))
|
|
448
|
+
for relative_path in shared_relatives:
|
|
449
|
+
action = self._sync_pair(
|
|
450
|
+
root=root.as_posix(),
|
|
451
|
+
relative_path=relative_path,
|
|
452
|
+
left_path=left_map[relative_path],
|
|
453
|
+
right_path=right_map[relative_path],
|
|
454
|
+
)
|
|
455
|
+
actions.append(action)
|
|
456
|
+
|
|
457
|
+
finished = datetime.now(timezone.utc)
|
|
458
|
+
return SyncSummary(
|
|
459
|
+
repo_left=str(left_repo),
|
|
460
|
+
repo_right=str(right_repo),
|
|
461
|
+
started_at=started,
|
|
462
|
+
finished_at=finished,
|
|
463
|
+
force_direction=self._force_direction,
|
|
464
|
+
actions=actions,
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
def _collect_files(self, root: Path) -> dict[str, Path]:
|
|
468
|
+
"""
|
|
469
|
+
Collect files under a root directory keyed by relative path.
|
|
470
|
+
|
|
471
|
+
Purpose:
|
|
472
|
+
Builds a mapping to support matching by relative file paths.
|
|
473
|
+
|
|
474
|
+
Args:
|
|
475
|
+
root (Path): Root directory to scan.
|
|
476
|
+
|
|
477
|
+
Returns:
|
|
478
|
+
dict[str, Path]: Mapping of relative POSIX paths to full paths.
|
|
479
|
+
|
|
480
|
+
Raises:
|
|
481
|
+
None.
|
|
482
|
+
|
|
483
|
+
Side Effects:
|
|
484
|
+
Reads directory contents via the filesystem abstraction.
|
|
485
|
+
"""
|
|
486
|
+
files: dict[str, Path] = {}
|
|
487
|
+
|
|
488
|
+
# Gather files under root so matching uses consistent relative keys.
|
|
489
|
+
for path in self._fs.list_files(root):
|
|
490
|
+
if not self._fs.is_file(path):
|
|
491
|
+
continue
|
|
492
|
+
relative = path.relative_to(root).as_posix()
|
|
493
|
+
files[relative] = path
|
|
494
|
+
return files
|
|
495
|
+
|
|
496
|
+
def _sync_pair(
|
|
497
|
+
self,
|
|
498
|
+
*,
|
|
499
|
+
root: str,
|
|
500
|
+
relative_path: str,
|
|
501
|
+
left_path: Path,
|
|
502
|
+
right_path: Path,
|
|
503
|
+
) -> SyncAction:
|
|
504
|
+
"""
|
|
505
|
+
Apply sync rules to a single file pair.
|
|
506
|
+
|
|
507
|
+
Purpose:
|
|
508
|
+
Implements the timestamp equivalence logic, content comparison, and
|
|
509
|
+
bidirectional sync behavior for a matched file pair.
|
|
510
|
+
|
|
511
|
+
Args:
|
|
512
|
+
root (str): Root folder name (e.g., ".github/agents").
|
|
513
|
+
relative_path (str): File path relative to root.
|
|
514
|
+
left_path (Path): Full path to the left repo file.
|
|
515
|
+
right_path (Path): Full path to the right repo file.
|
|
516
|
+
|
|
517
|
+
Returns:
|
|
518
|
+
SyncAction: Recorded outcome for this file pair.
|
|
519
|
+
"""
|
|
520
|
+
left_mtime = self._fs.get_mtime(left_path)
|
|
521
|
+
right_mtime = self._fs.get_mtime(right_path)
|
|
522
|
+
|
|
523
|
+
# Decision tree: when not forced, short-circuit on near-equal mtimes.
|
|
524
|
+
if self._force_direction is None:
|
|
525
|
+
if self._mtimes_equivalent(left_mtime, right_mtime):
|
|
526
|
+
return SyncAction(
|
|
527
|
+
root=root,
|
|
528
|
+
relative_path=relative_path,
|
|
529
|
+
left_path=left_path.as_posix(),
|
|
530
|
+
right_path=right_path.as_posix(),
|
|
531
|
+
left_mtime=left_mtime,
|
|
532
|
+
right_mtime=right_mtime,
|
|
533
|
+
decision="equivalent-mtime",
|
|
534
|
+
source=None,
|
|
535
|
+
sync_mtime=None,
|
|
536
|
+
forced=False,
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
left_content = self._fs.read_text(left_path)
|
|
540
|
+
right_content = self._fs.read_text(right_path)
|
|
541
|
+
|
|
542
|
+
# Branch by content equality: only sync when content differs.
|
|
543
|
+
if left_content == right_content:
|
|
544
|
+
return SyncAction(
|
|
545
|
+
root=root,
|
|
546
|
+
relative_path=relative_path,
|
|
547
|
+
left_path=left_path.as_posix(),
|
|
548
|
+
right_path=right_path.as_posix(),
|
|
549
|
+
left_mtime=left_mtime,
|
|
550
|
+
right_mtime=right_mtime,
|
|
551
|
+
decision="equivalent-content",
|
|
552
|
+
source=None,
|
|
553
|
+
sync_mtime=None,
|
|
554
|
+
forced=False,
|
|
555
|
+
)
|
|
556
|
+
|
|
557
|
+
source_label, source_content, source_mtime = self._select_source(
|
|
558
|
+
left_content=left_content,
|
|
559
|
+
right_content=right_content,
|
|
560
|
+
left_mtime=left_mtime,
|
|
561
|
+
right_mtime=right_mtime,
|
|
562
|
+
)
|
|
563
|
+
|
|
564
|
+
# Sync both files to the chosen content and normalize timestamps.
|
|
565
|
+
self._fs.write_text(left_path, source_content)
|
|
566
|
+
self._fs.write_text(right_path, source_content)
|
|
567
|
+
self._fs.set_mtime(left_path, source_mtime)
|
|
568
|
+
self._fs.set_mtime(right_path, source_mtime)
|
|
569
|
+
|
|
570
|
+
return SyncAction(
|
|
571
|
+
root=root,
|
|
572
|
+
relative_path=relative_path,
|
|
573
|
+
left_path=left_path.as_posix(),
|
|
574
|
+
right_path=right_path.as_posix(),
|
|
575
|
+
left_mtime=left_mtime,
|
|
576
|
+
right_mtime=right_mtime,
|
|
577
|
+
decision="synced",
|
|
578
|
+
source=source_label,
|
|
579
|
+
sync_mtime=source_mtime,
|
|
580
|
+
forced=self._force_direction is not None,
|
|
581
|
+
)
|
|
582
|
+
|
|
583
|
+
def _mtimes_equivalent(self, left_mtime: float, right_mtime: float) -> bool:
|
|
584
|
+
"""
|
|
585
|
+
Determine whether two mtimes are within the equivalence threshold.
|
|
586
|
+
|
|
587
|
+
Purpose:
|
|
588
|
+
Treat near-equal timestamps as equivalent to avoid redundant syncs.
|
|
589
|
+
|
|
590
|
+
Args:
|
|
591
|
+
left_mtime (float): Left file mtime.
|
|
592
|
+
right_mtime (float): Right file mtime.
|
|
593
|
+
|
|
594
|
+
Returns:
|
|
595
|
+
bool: True if mtimes are within threshold, False otherwise.
|
|
596
|
+
|
|
597
|
+
Raises:
|
|
598
|
+
None.
|
|
599
|
+
|
|
600
|
+
Side Effects:
|
|
601
|
+
None.
|
|
602
|
+
"""
|
|
603
|
+
return abs(left_mtime - right_mtime) <= self._threshold_seconds
|
|
604
|
+
|
|
605
|
+
def _select_source(
|
|
606
|
+
self,
|
|
607
|
+
*,
|
|
608
|
+
left_content: str,
|
|
609
|
+
right_content: str,
|
|
610
|
+
left_mtime: float,
|
|
611
|
+
right_mtime: float,
|
|
612
|
+
) -> tuple[str, str, float]:
|
|
613
|
+
"""
|
|
614
|
+
Select the source content and mtime for syncing.
|
|
615
|
+
|
|
616
|
+
Purpose:
|
|
617
|
+
Choose the content to propagate based on forced direction or
|
|
618
|
+
modification timestamps when no force is applied.
|
|
619
|
+
|
|
620
|
+
Args:
|
|
621
|
+
left_content (str): Left file content.
|
|
622
|
+
right_content (str): Right file content.
|
|
623
|
+
left_mtime (float): Left file mtime.
|
|
624
|
+
right_mtime (float): Right file mtime.
|
|
625
|
+
|
|
626
|
+
Returns:
|
|
627
|
+
tuple[str, str, float]: (source_label, content, mtime)
|
|
628
|
+
|
|
629
|
+
Raises:
|
|
630
|
+
None.
|
|
631
|
+
|
|
632
|
+
Side Effects:
|
|
633
|
+
None.
|
|
634
|
+
"""
|
|
635
|
+
# Forced direction overrides timestamp comparison to ensure one-way sync.
|
|
636
|
+
if self._force_direction == "left-to-right":
|
|
637
|
+
return "left", left_content, left_mtime
|
|
638
|
+
if self._force_direction == "right-to-left":
|
|
639
|
+
return "right", right_content, right_mtime
|
|
640
|
+
|
|
641
|
+
# Branch by mtime to select the newer file; ties default to left.
|
|
642
|
+
if left_mtime > right_mtime:
|
|
643
|
+
return "left", left_content, left_mtime
|
|
644
|
+
if right_mtime > left_mtime:
|
|
645
|
+
return "right", right_content, right_mtime
|
|
646
|
+
return "left", left_content, left_mtime
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
def build_artifact_path(repo_root: Path, started_at: datetime) -> Path:
|
|
650
|
+
"""
|
|
651
|
+
Build the artifact path for a sync run.
|
|
652
|
+
|
|
653
|
+
Args:
|
|
654
|
+
repo_root (Path): Repository root to anchor the artifacts folder.
|
|
655
|
+
started_at (datetime): Start time used to name the artifact.
|
|
656
|
+
|
|
657
|
+
Returns:
|
|
658
|
+
Path: Artifact file path under artifacts/agentic-sync.
|
|
659
|
+
"""
|
|
660
|
+
timestamp = started_at.strftime("%Y%m%dT%H%M%SZ")
|
|
661
|
+
return repo_root / "artifacts" / "agentic-sync" / f"sync-{timestamp}.json"
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
def render_sync_summary(summary: SyncSummary) -> str:
|
|
665
|
+
"""
|
|
666
|
+
Render a sync summary as JSON.
|
|
667
|
+
|
|
668
|
+
Purpose:
|
|
669
|
+
Produce a deterministic artifact payload for auditing.
|
|
670
|
+
|
|
671
|
+
Args:
|
|
672
|
+
summary (SyncSummary): Summary data to serialize.
|
|
673
|
+
|
|
674
|
+
Returns:
|
|
675
|
+
str: JSON string containing the sync summary.
|
|
676
|
+
"""
|
|
677
|
+
|
|
678
|
+
# Serialize dataclasses into JSON-friendly structures.
|
|
679
|
+
actions_payload: list[SyncActionPayload] = []
|
|
680
|
+
# Serialize actions in order to preserve traceability.
|
|
681
|
+
for action in summary.actions:
|
|
682
|
+
actions_payload.append(
|
|
683
|
+
{
|
|
684
|
+
"root": action.root,
|
|
685
|
+
"relative_path": action.relative_path,
|
|
686
|
+
"left_path": action.left_path,
|
|
687
|
+
"right_path": action.right_path,
|
|
688
|
+
"left_mtime": action.left_mtime,
|
|
689
|
+
"right_mtime": action.right_mtime,
|
|
690
|
+
"decision": action.decision,
|
|
691
|
+
"source": action.source,
|
|
692
|
+
"sync_mtime": action.sync_mtime,
|
|
693
|
+
"forced": action.forced,
|
|
694
|
+
}
|
|
695
|
+
)
|
|
696
|
+
|
|
697
|
+
payload: SyncSummaryPayload = {
|
|
698
|
+
"repo_left": summary.repo_left,
|
|
699
|
+
"repo_right": summary.repo_right,
|
|
700
|
+
"started_at": summary.started_at.isoformat(),
|
|
701
|
+
"finished_at": summary.finished_at.isoformat(),
|
|
702
|
+
"force_direction": summary.force_direction,
|
|
703
|
+
"actions": actions_payload,
|
|
704
|
+
}
|
|
705
|
+
return json.dumps(payload, indent=2, sort_keys=True)
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
def write_sync_artifact(
|
|
709
|
+
fs: SyncFileSystem, repo_root: Path, summary: SyncSummary
|
|
710
|
+
) -> Path:
|
|
711
|
+
"""
|
|
712
|
+
Write the sync summary artifact to disk.
|
|
713
|
+
|
|
714
|
+
Args:
|
|
715
|
+
fs (SyncFileSystem): Filesystem abstraction for I/O.
|
|
716
|
+
repo_root (Path): Root directory to anchor artifacts folder.
|
|
717
|
+
summary (SyncSummary): Summary to serialize and write.
|
|
718
|
+
|
|
719
|
+
Returns:
|
|
720
|
+
Path: Path to the written artifact.
|
|
721
|
+
|
|
722
|
+
Side Effects:
|
|
723
|
+
Creates the artifacts/agentic-sync directory and writes a JSON file.
|
|
724
|
+
"""
|
|
725
|
+
artifact_path = build_artifact_path(repo_root, summary.started_at)
|
|
726
|
+
fs.ensure_dir(artifact_path.parent)
|
|
727
|
+
fs.write_text(artifact_path, render_sync_summary(summary))
|
|
728
|
+
return artifact_path
|
|
729
|
+
|
|
730
|
+
|
|
731
|
+
def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
|
732
|
+
"""
|
|
733
|
+
Parse command-line arguments.
|
|
734
|
+
|
|
735
|
+
Args:
|
|
736
|
+
argv (list[str] | None): Optional argv override for testing.
|
|
737
|
+
|
|
738
|
+
Returns:
|
|
739
|
+
argparse.Namespace: Parsed arguments.
|
|
740
|
+
"""
|
|
741
|
+
parser = argparse.ArgumentParser(
|
|
742
|
+
description="Sync shared .github documents between two repos."
|
|
743
|
+
)
|
|
744
|
+
parser.add_argument(
|
|
745
|
+
"left_repo",
|
|
746
|
+
help="Path to the left repository workspace.",
|
|
747
|
+
)
|
|
748
|
+
parser.add_argument(
|
|
749
|
+
"right_repo",
|
|
750
|
+
help="Path to the right repository workspace.",
|
|
751
|
+
)
|
|
752
|
+
group = parser.add_mutually_exclusive_group()
|
|
753
|
+
group.add_argument(
|
|
754
|
+
"--force-left-to-right",
|
|
755
|
+
action="store_true",
|
|
756
|
+
help="Force sync from left repo to right repo.",
|
|
757
|
+
)
|
|
758
|
+
group.add_argument(
|
|
759
|
+
"--force-right-to-left",
|
|
760
|
+
action="store_true",
|
|
761
|
+
help="Force sync from right repo to left repo.",
|
|
762
|
+
)
|
|
763
|
+
parser.add_argument(
|
|
764
|
+
"--threshold-seconds",
|
|
765
|
+
type=int,
|
|
766
|
+
default=180,
|
|
767
|
+
help="Seconds for mtime equivalence (default: 180).",
|
|
768
|
+
)
|
|
769
|
+
return parser.parse_args(argv)
|
|
770
|
+
|
|
771
|
+
|
|
772
|
+
def main(argv: list[str] | None = None) -> int:
|
|
773
|
+
"""
|
|
774
|
+
CLI entry point for agentic sync.
|
|
775
|
+
|
|
776
|
+
Purpose:
|
|
777
|
+
Runs the sync process and writes a JSON artifact summarizing the run.
|
|
778
|
+
|
|
779
|
+
Args:
|
|
780
|
+
argv (list[str] | None): Optional argv override.
|
|
781
|
+
|
|
782
|
+
Returns:
|
|
783
|
+
int: Exit code (0 for success).
|
|
784
|
+
|
|
785
|
+
Raises:
|
|
786
|
+
ValueError: When repo paths are invalid.
|
|
787
|
+
"""
|
|
788
|
+
args = parse_args(argv)
|
|
789
|
+
left_repo = Path(args.left_repo).expanduser().resolve()
|
|
790
|
+
right_repo = Path(args.right_repo).expanduser().resolve()
|
|
791
|
+
|
|
792
|
+
# Validate repository paths before sync to avoid silent no-ops.
|
|
793
|
+
if not left_repo.is_dir():
|
|
794
|
+
raise ValueError(f"Left repo is not a directory: {left_repo}")
|
|
795
|
+
if not right_repo.is_dir():
|
|
796
|
+
raise ValueError(f"Right repo is not a directory: {right_repo}")
|
|
797
|
+
|
|
798
|
+
force_direction: ForceDirection | None = None
|
|
799
|
+
# Choose forced direction flag if specified by the user.
|
|
800
|
+
if args.force_left_to_right:
|
|
801
|
+
force_direction = "left-to-right"
|
|
802
|
+
elif args.force_right_to_left:
|
|
803
|
+
force_direction = "right-to-left"
|
|
804
|
+
|
|
805
|
+
syncer = AgenticSyncer(
|
|
806
|
+
RealSyncFileSystem(),
|
|
807
|
+
threshold_seconds=args.threshold_seconds,
|
|
808
|
+
force_direction=force_direction,
|
|
809
|
+
)
|
|
810
|
+
summary = syncer.sync_repos(left_repo, right_repo)
|
|
811
|
+
|
|
812
|
+
artifact_path = write_sync_artifact(RealSyncFileSystem(), Path.cwd(), summary)
|
|
813
|
+
LOGGER.info("Wrote sync artifact to %s", artifact_path.as_posix())
|
|
814
|
+
print(f"Wrote sync artifact to: {artifact_path}")
|
|
815
|
+
return 0
|
|
816
|
+
|
|
817
|
+
|
|
818
|
+
if __name__ == "__main__":
|
|
819
|
+
raise SystemExit(main())
|