@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.
Files changed (380) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +50 -0
  3. package/out/mcp-server.js +17323 -0
  4. package/package.json +36 -0
  5. package/resources/claude-customizations/.claude/agent-memory/orchestrator/MEMORY.md +3 -0
  6. package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_repo_root_is_source_of_truth.md +11 -0
  7. package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_vsce_verify_package_location.md +19 -0
  8. package/resources/claude-customizations/.claude/agent-memory/orchestrator/project_extension_location.md +11 -0
  9. package/resources/claude-customizations/.claude/agent-memory/prd-feature/MEMORY.md +1 -0
  10. package/resources/claude-customizations/.claude/agent-memory/prd-feature/project_push_down_pattern.md +13 -0
  11. package/resources/claude-customizations/.claude/agent-memory/task-researcher/MEMORY.md +3 -0
  12. package/resources/claude-customizations/.claude/agent-memory/task-researcher/project_push_down_claude_dir.md +11 -0
  13. package/resources/claude-customizations/.claude/agents/atomic-executor.md +135 -0
  14. package/resources/claude-customizations/.claude/agents/atomic-planner.md +71 -0
  15. package/resources/claude-customizations/.claude/agents/csharp-typed-engineer.md +69 -0
  16. package/resources/claude-customizations/.claude/agents/epic-review.md +40 -0
  17. package/resources/claude-customizations/.claude/agents/feature-review.md +136 -0
  18. package/resources/claude-customizations/.claude/agents/orchestrator.md +83 -0
  19. package/resources/claude-customizations/.claude/agents/powershell-typed-engineer.md +80 -0
  20. package/resources/claude-customizations/.claude/agents/prd-feature.md +42 -0
  21. package/resources/claude-customizations/.claude/agents/python-typed-engineer.md +72 -0
  22. package/resources/claude-customizations/.claude/agents/staged-review.md +41 -0
  23. package/resources/claude-customizations/.claude/agents/status-updater.md +41 -0
  24. package/resources/claude-customizations/.claude/agents/task-researcher.md +81 -0
  25. package/resources/claude-customizations/.claude/agents/typescript-engineer.md +24 -0
  26. package/resources/claude-customizations/.claude/hooks/check-powershell-test-purity.ps1 +111 -0
  27. package/resources/claude-customizations/.claude/hooks/check-python-test-purity.ps1 +146 -0
  28. package/resources/claude-customizations/.claude/hooks/enforce-evidence-locations.ps1 +150 -0
  29. package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +238 -0
  30. package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +147 -0
  31. package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +235 -0
  32. package/resources/claude-customizations/.claude/hooks/validate-bash.ps1 +69 -0
  33. package/resources/claude-customizations/.claude/hooks/validate-executor-output.ps1 +296 -0
  34. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage.ps1 +389 -0
  35. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +141 -0
  36. package/resources/claude-customizations/.claude/hooks/validate-planner-output.ps1 +288 -0
  37. package/resources/claude-customizations/.claude/hooks/validate-required-artifact-output.ps1 +171 -0
  38. package/resources/claude-customizations/.claude/hooks/validate-task-researcher-output.ps1 +142 -0
  39. package/resources/claude-customizations/.claude/rules/csharp.md +62 -0
  40. package/resources/claude-customizations/.claude/rules/general-code-change.md +71 -0
  41. package/resources/claude-customizations/.claude/rules/general-unit-test.md +60 -0
  42. package/resources/claude-customizations/.claude/rules/powershell.md +97 -0
  43. package/resources/claude-customizations/.claude/rules/python-suppressions.md +143 -0
  44. package/resources/claude-customizations/.claude/rules/python.md +99 -0
  45. package/resources/claude-customizations/.claude/rules/self-explanatory-code-commenting.md +97 -0
  46. package/resources/claude-customizations/.claude/rules/tonality.md +80 -0
  47. package/resources/claude-customizations/.claude/rules/typescript-suppressions.md +66 -0
  48. package/resources/claude-customizations/.claude/rules/typescript.md +45 -0
  49. package/resources/claude-customizations/.claude/settings.json +144 -0
  50. package/resources/claude-customizations/.claude/skills/acceptance-criteria-tracking/SKILL.md +102 -0
  51. package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +189 -0
  52. package/resources/claude-customizations/.claude/skills/commit-message/SKILL.md +65 -0
  53. package/resources/claude-customizations/.claude/skills/csharp-change-budget-router/SKILL.md +90 -0
  54. package/resources/claude-customizations/.claude/skills/csharp-orchestration-state-machine/SKILL.md +58 -0
  55. package/resources/claude-customizations/.claude/skills/csharp-qa-gate/SKILL.md +77 -0
  56. package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +164 -0
  57. package/resources/claude-customizations/.claude/skills/execute-hard-lock/SKILL.md +82 -0
  58. package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +115 -0
  59. package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +167 -0
  60. package/resources/claude-customizations/.claude/skills/fill-feature-docs/SKILL.md +22 -0
  61. package/resources/claude-customizations/.claude/skills/invoke-csharp-engineer/SKILL.md +64 -0
  62. package/resources/claude-customizations/.claude/skills/invoke-powershell-engineer/SKILL.md +65 -0
  63. package/resources/claude-customizations/.claude/skills/invoke-python-engineer/SKILL.md +64 -0
  64. package/resources/claude-customizations/.claude/skills/make-skill-template/SKILL.md +147 -0
  65. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +132 -0
  66. package/resources/claude-customizations/.claude/skills/policy-audit-template-usage/SKILL.md +49 -0
  67. package/resources/claude-customizations/.claude/skills/policy-compliance-order/SKILL.md +40 -0
  68. package/resources/claude-customizations/.claude/skills/powershell-change-budget-router/SKILL.md +49 -0
  69. package/resources/claude-customizations/.claude/skills/powershell-orchestration-state-machine/SKILL.md +58 -0
  70. package/resources/claude-customizations/.claude/skills/powershell-qa-gate/SKILL.md +77 -0
  71. package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +50 -0
  72. package/resources/claude-customizations/.claude/skills/pr-base-branch-merge-base/SKILL.md +56 -0
  73. package/resources/claude-customizations/.claude/skills/pr-context-artifacts/SKILL.md +30 -0
  74. package/resources/claude-customizations/.claude/skills/python-change-budget-router/SKILL.md +79 -0
  75. package/resources/claude-customizations/.claude/skills/python-qa-gate/SKILL.md +77 -0
  76. package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +40 -0
  77. package/resources/claude-customizations/.claude/skills/research-issue/SKILL.md +67 -0
  78. package/resources/claude-customizations/.claude/skills/review-epic/SKILL.md +21 -0
  79. package/resources/claude-customizations/.claude/skills/review-feature/SKILL.md +25 -0
  80. package/resources/claude-customizations/.claude/skills/review-staged/SKILL.md +21 -0
  81. package/resources/claude-customizations/.claude/skills/skill-canonical-location-audit/SKILL.md +49 -0
  82. package/resources/claude-customizations/.claude/skills/translate-copilot-to-claude/SKILL.md +295 -0
  83. package/resources/claude-customizations/.claude/skills/update-status/SKILL.md +21 -0
  84. package/resources/claude-dir-customizations/.mcp.json +8 -0
  85. package/resources/codex-and-agents-customizations/.agents/README.md +86 -0
  86. package/resources/codex-and-agents-customizations/.agents/skills/README.md +49 -0
  87. package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +107 -0
  88. package/resources/codex-and-agents-customizations/.agents/skills/atomic-executor/SKILL.md +73 -0
  89. package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +194 -0
  90. package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +87 -0
  91. package/resources/codex-and-agents-customizations/.agents/skills/commit-message/SKILL.md +70 -0
  92. package/resources/codex-and-agents-customizations/.agents/skills/commit-message-conventions/SKILL.md +95 -0
  93. package/resources/codex-and-agents-customizations/.agents/skills/csharp/SKILL.md +67 -0
  94. package/resources/codex-and-agents-customizations/.agents/skills/csharp-change-budget-router/SKILL.md +94 -0
  95. package/resources/codex-and-agents-customizations/.agents/skills/csharp-orchestration-state-machine/SKILL.md +64 -0
  96. package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +82 -0
  97. package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +168 -0
  98. package/resources/codex-and-agents-customizations/.agents/skills/execute-hard-lock/SKILL.md +88 -0
  99. package/resources/codex-and-agents-customizations/.agents/skills/feature-promotion-lifecycle/SKILL.md +129 -0
  100. package/resources/codex-and-agents-customizations/.agents/skills/feature-review/SKILL.md +106 -0
  101. package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +181 -0
  102. package/resources/codex-and-agents-customizations/.agents/skills/fill-feature-docs/SKILL.md +27 -0
  103. package/resources/codex-and-agents-customizations/.agents/skills/invoke-csharp-engineer/SKILL.md +73 -0
  104. package/resources/codex-and-agents-customizations/.agents/skills/invoke-powershell-engineer/SKILL.md +74 -0
  105. package/resources/codex-and-agents-customizations/.agents/skills/invoke-python-engineer/SKILL.md +73 -0
  106. package/resources/codex-and-agents-customizations/.agents/skills/make-skill-template/SKILL.md +152 -0
  107. package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +143 -0
  108. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +317 -0
  109. package/resources/codex-and-agents-customizations/.agents/skills/policy-audit-template-usage/SKILL.md +53 -0
  110. package/resources/codex-and-agents-customizations/.agents/skills/policy-compliance-order/SKILL.md +49 -0
  111. package/resources/codex-and-agents-customizations/.agents/skills/powershell/SKILL.md +102 -0
  112. package/resources/codex-and-agents-customizations/.agents/skills/powershell-change-budget-router/SKILL.md +53 -0
  113. package/resources/codex-and-agents-customizations/.agents/skills/powershell-orchestration-state-machine/SKILL.md +64 -0
  114. package/resources/codex-and-agents-customizations/.agents/skills/powershell-qa-gate/SKILL.md +83 -0
  115. package/resources/codex-and-agents-customizations/.agents/skills/pr-author/SKILL.md +55 -0
  116. package/resources/codex-and-agents-customizations/.agents/skills/pr-authoring/SKILL.md +124 -0
  117. package/resources/codex-and-agents-customizations/.agents/skills/pr-base-branch-merge-base/SKILL.md +60 -0
  118. package/resources/codex-and-agents-customizations/.agents/skills/pr-context-artifacts/SKILL.md +34 -0
  119. package/resources/codex-and-agents-customizations/.agents/skills/python/SKILL.md +104 -0
  120. package/resources/codex-and-agents-customizations/.agents/skills/python-change-budget-router/SKILL.md +84 -0
  121. package/resources/codex-and-agents-customizations/.agents/skills/python-qa-gate/SKILL.md +82 -0
  122. package/resources/codex-and-agents-customizations/.agents/skills/python-suppressions/SKILL.md +148 -0
  123. package/resources/codex-and-agents-customizations/.agents/skills/remediation-handoff-atomic-planner/SKILL.md +49 -0
  124. package/resources/codex-and-agents-customizations/.agents/skills/repo-automation-adapter/SKILL.md +142 -0
  125. package/resources/codex-and-agents-customizations/.agents/skills/repo-automation-adapter/agents/openai.yaml +5 -0
  126. package/resources/codex-and-agents-customizations/.agents/skills/research-issue/SKILL.md +72 -0
  127. package/resources/codex-and-agents-customizations/.agents/skills/review-epic/SKILL.md +26 -0
  128. package/resources/codex-and-agents-customizations/.agents/skills/review-feature/SKILL.md +30 -0
  129. package/resources/codex-and-agents-customizations/.agents/skills/review-staged/SKILL.md +26 -0
  130. package/resources/codex-and-agents-customizations/.agents/skills/self-explanatory-code-commenting/SKILL.md +102 -0
  131. package/resources/codex-and-agents-customizations/.agents/skills/skill-canonical-location-audit/SKILL.md +52 -0
  132. package/resources/codex-and-agents-customizations/.agents/skills/translate-copilot-to-claude/SKILL.md +317 -0
  133. package/resources/codex-and-agents-customizations/.agents/skills/typescript/SKILL.md +50 -0
  134. package/resources/codex-and-agents-customizations/.agents/skills/typescript-suppressions/SKILL.md +71 -0
  135. package/resources/codex-and-agents-customizations/.agents/skills/update-status/SKILL.md +26 -0
  136. package/resources/codex-and-agents-customizations/.codex/agents/5.1-beast-adjusted.toml +23 -0
  137. package/resources/codex-and-agents-customizations/.codex/agents/5.1-thinking-beast-mode-adjusted.toml +23 -0
  138. package/resources/codex-and-agents-customizations/.codex/agents/api-architect.toml +23 -0
  139. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +151 -0
  140. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +93 -0
  141. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planning.toml +24 -0
  142. package/resources/codex-and-agents-customizations/.codex/agents/commentary-remediation.toml +23 -0
  143. package/resources/codex-and-agents-customizations/.codex/agents/commit-steward.toml +20 -0
  144. package/resources/codex-and-agents-customizations/.codex/agents/csharp-atomic-executor.toml +24 -0
  145. package/resources/codex-and-agents-customizations/.codex/agents/csharp-atomic-planning.toml +25 -0
  146. package/resources/codex-and-agents-customizations/.codex/agents/csharp-orchestrator.toml +56 -0
  147. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer.toml +97 -0
  148. package/resources/codex-and-agents-customizations/.codex/agents/epic-review.toml +52 -0
  149. package/resources/codex-and-agents-customizations/.codex/agents/expert-nextjs-developer.toml +23 -0
  150. package/resources/codex-and-agents-customizations/.codex/agents/expert-react-frontend-engineer.toml +23 -0
  151. package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +149 -0
  152. package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer.toml +60 -0
  153. package/resources/codex-and-agents-customizations/.codex/agents/gpt-5-beast-mode.toml +23 -0
  154. package/resources/codex-and-agents-customizations/.codex/agents/hlbpa.toml +23 -0
  155. package/resources/codex-and-agents-customizations/.codex/agents/mentor.toml +23 -0
  156. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +121 -0
  157. package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-executor.toml +24 -0
  158. package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-planning.toml +25 -0
  159. package/resources/codex-and-agents-customizations/.codex/agents/powershell-di-unit-test-engineer.toml +24 -0
  160. package/resources/codex-and-agents-customizations/.codex/agents/powershell-orchestrator.toml +56 -0
  161. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer.toml +108 -0
  162. package/resources/codex-and-agents-customizations/.codex/agents/pr-author.toml +26 -0
  163. package/resources/codex-and-agents-customizations/.codex/agents/prd-feature.toml +53 -0
  164. package/resources/codex-and-agents-customizations/.codex/agents/prd.toml +23 -0
  165. package/resources/codex-and-agents-customizations/.codex/agents/pytest-unit-test-coding.toml +24 -0
  166. package/resources/codex-and-agents-customizations/.codex/agents/python-atomic-executor.toml +24 -0
  167. package/resources/codex-and-agents-customizations/.codex/agents/python-atomic-planning.toml +25 -0
  168. package/resources/codex-and-agents-customizations/.codex/agents/python-execution-only-typed.toml +24 -0
  169. package/resources/codex-and-agents-customizations/.codex/agents/python-orchestrator.toml +54 -0
  170. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer.toml +100 -0
  171. package/resources/codex-and-agents-customizations/.codex/agents/staged-review.toml +53 -0
  172. package/resources/codex-and-agents-customizations/.codex/agents/status-updater.toml +53 -0
  173. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher.toml +103 -0
  174. package/resources/codex-and-agents-customizations/.codex/agents/tdd-green.toml +23 -0
  175. package/resources/codex-and-agents-customizations/.codex/agents/tdd-red.toml +23 -0
  176. package/resources/codex-and-agents-customizations/.codex/agents/tdd-refactor.toml +23 -0
  177. package/resources/codex-and-agents-customizations/.codex/agents/typescript-engineer.toml +48 -0
  178. package/resources/codex-and-agents-customizations/.codex/agents/voidbeast-gpt41enhanced.toml +23 -0
  179. package/resources/codex-and-agents-customizations/.codex/codex-web-setup.plan.md +26 -0
  180. package/resources/codex-and-agents-customizations/.codex/codex-web-setup.sh +384 -0
  181. package/resources/codex-and-agents-customizations/.codex/config.toml +137 -0
  182. package/resources/codex-and-agents-customizations/.codex/hooks/check-powershell-test-purity.ps1 +113 -0
  183. package/resources/codex-and-agents-customizations/.codex/hooks/check-python-test-purity.ps1 +149 -0
  184. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-evidence-locations.ps1 +153 -0
  185. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +241 -0
  186. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +150 -0
  187. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +238 -0
  188. package/resources/codex-and-agents-customizations/.codex/hooks/validate-bash.ps1 +72 -0
  189. package/resources/codex-and-agents-customizations/.codex/hooks/validate-feature-review-coverage.ps1 +265 -0
  190. package/resources/codex-and-agents-customizations/.codex/prompts/feature-review-remediate.md +10 -0
  191. package/resources/codex-and-agents-customizations/.codex/prompts/generate-commit-message-repo.md +11 -0
  192. package/resources/codex-and-agents-customizations/.codex/prompts/generate-pr.md +15 -0
  193. package/resources/codex-and-agents-customizations/.codex/prompts/orchestrate-work.md +22 -0
  194. package/resources/codex-and-agents-customizations/AGENTS.md +317 -0
  195. package/resources/customizations/.github/agents/5.1-Beast-adjusted.agent.md +181 -0
  196. package/resources/customizations/.github/agents/5.1-Thinking-Beast-Mode-adjusted.agent.md +361 -0
  197. package/resources/customizations/.github/agents/Powershell DI Unit Test Engineer.agent.md +192 -0
  198. package/resources/customizations/.github/agents/api-architect.agent.md +40 -0
  199. package/resources/customizations/.github/agents/atomic_executor.agent.md +251 -0
  200. package/resources/customizations/.github/agents/atomic_planning.agent.md +658 -0
  201. package/resources/customizations/.github/agents/commentary-remediation.agent.md +35 -0
  202. package/resources/customizations/.github/agents/commit-steward.agent.md +200 -0
  203. package/resources/customizations/.github/agents/csharp-atomic-executor.agent.md +288 -0
  204. package/resources/customizations/.github/agents/csharp-atomic-planning.agent.md +354 -0
  205. package/resources/customizations/.github/agents/csharp-orchestrator.agent.md +375 -0
  206. package/resources/customizations/.github/agents/csharp-typed-engineer.agent.md +285 -0
  207. package/resources/customizations/.github/agents/epic-review.agent.md +374 -0
  208. package/resources/customizations/.github/agents/expert-nextjs-developer.agent.md +477 -0
  209. package/resources/customizations/.github/agents/expert-react-frontend-engineer.agent.md +739 -0
  210. package/resources/customizations/.github/agents/feature-review.agent.md +49 -0
  211. package/resources/customizations/.github/agents/gpt-5-beast-mode.agent.md +116 -0
  212. package/resources/customizations/.github/agents/hlbpa.agent.md +219 -0
  213. package/resources/customizations/.github/agents/mentor.agent.md +32 -0
  214. package/resources/customizations/.github/agents/orchestrator.agent.md +449 -0
  215. package/resources/customizations/.github/agents/powershell-atomic-executor.agent.md +287 -0
  216. package/resources/customizations/.github/agents/powershell-atomic-planning.agent.md +647 -0
  217. package/resources/customizations/.github/agents/powershell-orchestrator.agent.md +382 -0
  218. package/resources/customizations/.github/agents/powershell-typed-engineer.agent.md +293 -0
  219. package/resources/customizations/.github/agents/pr-author.agent.md +138 -0
  220. package/resources/customizations/.github/agents/prd-feature.agent.md +52 -0
  221. package/resources/customizations/.github/agents/prd.agent.md +202 -0
  222. package/resources/customizations/.github/agents/pytest-unit-test-coding.agent.md +202 -0
  223. package/resources/customizations/.github/agents/python-atomic-executor.agent.md +289 -0
  224. package/resources/customizations/.github/agents/python-atomic-planning.agent.md +429 -0
  225. package/resources/customizations/.github/agents/python-execution-only-typed.agent.md +217 -0
  226. package/resources/customizations/.github/agents/python-orchestrator.agent.md +380 -0
  227. package/resources/customizations/.github/agents/python-typed-engineer.agent.md +271 -0
  228. package/resources/customizations/.github/agents/staged-review.agent.md +246 -0
  229. package/resources/customizations/.github/agents/status_updater.agent.md +279 -0
  230. package/resources/customizations/.github/agents/task-researcher.agent.md +298 -0
  231. package/resources/customizations/.github/agents/tdd-green.agent.md +60 -0
  232. package/resources/customizations/.github/agents/tdd-red.agent.md +66 -0
  233. package/resources/customizations/.github/agents/tdd-refactor.agent.md +94 -0
  234. package/resources/customizations/.github/agents/typescript-engineer.agent.md +167 -0
  235. package/resources/customizations/.github/agents/voidbeast-gpt41enhanced.agent.md +230 -0
  236. package/resources/customizations/.github/codex/execute-hard-lock.prompt.md +105 -0
  237. package/resources/customizations/.github/codex/resume-hard-lock.prompt.md +92 -0
  238. package/resources/customizations/.github/copilot-instructions.md +7 -0
  239. package/resources/customizations/.github/instructions/csharp-code-change.instructions.md +184 -0
  240. package/resources/customizations/.github/instructions/csharp-unit-test.instructions.md +52 -0
  241. package/resources/customizations/.github/instructions/general-code-change.instructions.md +290 -0
  242. package/resources/customizations/.github/instructions/general-unit-test.instructions.md +106 -0
  243. package/resources/customizations/.github/instructions/github-actions-ci-cd-best-practices.instructions.md +607 -0
  244. package/resources/customizations/.github/instructions/github-actions.instructions.md +23 -0
  245. package/resources/customizations/.github/instructions/powershell-code-change.instructions.md +81 -0
  246. package/resources/customizations/.github/instructions/powershell-unit-test.instructions.md +69 -0
  247. package/resources/customizations/.github/instructions/python-code-change.instructions.md +232 -0
  248. package/resources/customizations/.github/instructions/python-suppressions.instructions.md +609 -0
  249. package/resources/customizations/.github/instructions/python-unit-test.instructions.md +71 -0
  250. package/resources/customizations/.github/instructions/self-explanatory-code-commenting.instructions.md +238 -0
  251. package/resources/customizations/.github/instructions/tonality.instructions.md +133 -0
  252. package/resources/customizations/.github/instructions/typescript-code-change.instructions.md +203 -0
  253. package/resources/customizations/.github/instructions/typescript-suppressions.instructions.md +157 -0
  254. package/resources/customizations/.github/instructions/typescript-unit-test.instructions.md +112 -0
  255. package/resources/customizations/.github/prompts/add-educational-comments.prompt.md +129 -0
  256. package/resources/customizations/.github/prompts/breakdown-bug-prd.prompt.md +29 -0
  257. package/resources/customizations/.github/prompts/breakdown-epic-arch.prompt.md +66 -0
  258. package/resources/customizations/.github/prompts/breakdown-epic-pm.prompt.md +58 -0
  259. package/resources/customizations/.github/prompts/breakdown-feature-implementation.prompt.md +128 -0
  260. package/resources/customizations/.github/prompts/breakdown-feature-prd.prompt.md +61 -0
  261. package/resources/customizations/.github/prompts/code-exemplars-blueprint-generator.prompt.md +126 -0
  262. package/resources/customizations/.github/prompts/drafts/create-github-issues-feature-from-implementation-plan.prompt.md +28 -0
  263. package/resources/customizations/.github/prompts/drafts/create-implementation-plan.prompt.md +158 -0
  264. package/resources/customizations/.github/prompts/drafts/create-technical-spike.prompt.md +231 -0
  265. package/resources/customizations/.github/prompts/drafts/potential-feature-prd.prompt.md +19 -0
  266. package/resources/customizations/.github/prompts/drafts/update-implementation-plan.prompt.md +158 -0
  267. package/resources/customizations/.github/prompts/execute-plan-template.md +21 -0
  268. package/resources/customizations/.github/prompts/export-chat.prompt.md +7 -0
  269. package/resources/customizations/.github/prompts/fillout-prd-feature.prompt.md +46 -0
  270. package/resources/customizations/.github/prompts/generate-atomic-plan.prompt.md +96 -0
  271. package/resources/customizations/.github/prompts/generate-commit-message-repo.prompt.md +108 -0
  272. package/resources/customizations/.github/prompts/generate-pr.prompt.md +151 -0
  273. package/resources/customizations/.github/prompts/javascript-typescript-jest.prompt.md +44 -0
  274. package/resources/customizations/.github/prompts/orchestrate-csharp-work.prompt.md +66 -0
  275. package/resources/customizations/.github/prompts/orchestrate-powershell-work.prompt.md +50 -0
  276. package/resources/customizations/.github/prompts/orchestrate-python-work.prompt.md +50 -0
  277. package/resources/customizations/.github/prompts/orchestrate-work.prompt.md +66 -0
  278. package/resources/customizations/.github/prompts/remediate-comments.prompt.md +53 -0
  279. package/resources/customizations/.github/prompts/research-issue.prompt.md +125 -0
  280. package/resources/customizations/.github/prompts/review-epic.prompt.md +94 -0
  281. package/resources/customizations/.github/prompts/review-feature.prompt.md +130 -0
  282. package/resources/customizations/.github/prompts/review-staged.prompt.md +43 -0
  283. package/resources/customizations/.github/prompts/update_status.prompt.md +68 -0
  284. package/resources/customizations/.github/skills/README.md +26 -0
  285. package/resources/customizations/.github/skills/acceptance-criteria-tracking/SKILL.md +102 -0
  286. package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +174 -0
  287. package/resources/customizations/.github/skills/csharp-change-budget-router/SKILL.md +48 -0
  288. package/resources/customizations/.github/skills/csharp-orchestration-state-machine/SKILL.md +57 -0
  289. package/resources/customizations/.github/skills/evidence-and-timestamp-conventions/SKILL.md +135 -0
  290. package/resources/customizations/.github/skills/feature-promotion-lifecycle/SKILL.md +121 -0
  291. package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +153 -0
  292. package/resources/customizations/.github/skills/make-skill-template/SKILL.md +147 -0
  293. package/resources/customizations/.github/skills/policy-audit-template-usage/SKILL.md +48 -0
  294. package/resources/customizations/.github/skills/policy-compliance-order/SKILL.md +37 -0
  295. package/resources/customizations/.github/skills/powershell-change-budget-router/SKILL.md +48 -0
  296. package/resources/customizations/.github/skills/powershell-orchestration-state-machine/SKILL.md +57 -0
  297. package/resources/customizations/.github/skills/pr-base-branch-merge-base/SKILL.md +55 -0
  298. package/resources/customizations/.github/skills/pr-context-artifacts/SKILL.md +29 -0
  299. package/resources/customizations/.github/skills/remediation-handoff-atomic-planner/SKILL.md +39 -0
  300. package/resources/customizations/.github/skills/skill-canonical-location-audit/SKILL.md +48 -0
  301. package/resources/feature-templates/bug/plan.yyyy-MM-ddTHH-mm.md +44 -0
  302. package/resources/feature-templates/bug/potential_bug.md +59 -0
  303. package/resources/feature-templates/bug/spec.md +99 -0
  304. package/resources/feature-templates/epic/initiative.md +43 -0
  305. package/resources/feature-templates/feature/plan.yyyy-MM-ddTHH-mm.md +53 -0
  306. package/resources/feature-templates/feature/spec.md +66 -0
  307. package/resources/feature-templates/feature/user-story.md +42 -0
  308. package/resources/feature-templates/potential/template.md +33 -0
  309. package/resources/feature-templates/refactor/plan.yyyy-MM-ddTHH-mm.md +52 -0
  310. package/resources/feature-templates/refactor/spec.md +69 -0
  311. package/resources/powershell/PoshQC/PoshQC.Analyzer.psm1 +254 -0
  312. package/resources/powershell/PoshQC/PoshQC.FileDiscovery.psm1 +138 -0
  313. package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +409 -0
  314. package/resources/powershell/PoshQC/PoshQC.psd1 +31 -0
  315. package/resources/powershell/PoshQC/PoshQC.psm1 +101 -0
  316. package/resources/powershell/PoshQC/README.md +80 -0
  317. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +59 -0
  318. package/resources/powershell/PoshQC/settings/pssa.settings.psd1 +55 -0
  319. package/resources/scripts/dev_tools/__init__.py +0 -0
  320. package/resources/scripts/dev_tools/agentic_sync.py +819 -0
  321. package/resources/scripts/dev_tools/codex_native_converter/__init__.py +11 -0
  322. package/resources/scripts/dev_tools/codex_native_converter/__main__.py +6 -0
  323. package/resources/scripts/dev_tools/codex_native_converter/cli.py +11 -0
  324. package/resources/scripts/dev_tools/new_active_feature_folder.py +79 -0
  325. package/resources/scripts/dev_tools/new_active_feature_folder_docs.py +268 -0
  326. package/resources/scripts/dev_tools/new_active_feature_folder_flow.py +366 -0
  327. package/resources/scripts/dev_tools/new_active_feature_folder_io.py +306 -0
  328. package/resources/scripts/dev_tools/new_active_feature_folder_markdown.py +252 -0
  329. package/resources/scripts/dev_tools/new_active_feature_folder_models.py +136 -0
  330. package/resources/scripts/dev_tools/new_potential_bug_entry.py +465 -0
  331. package/resources/scripts/dev_tools/potential_to_issue.py +421 -0
  332. package/resources/scripts/dev_tools/potential_to_issue_content.py +212 -0
  333. package/resources/scripts/dev_tools/pr_context/__init__.py +0 -0
  334. package/resources/scripts/dev_tools/pr_context/collector.py +619 -0
  335. package/resources/scripts/dev_tools/pr_context/feature_docs.py +349 -0
  336. package/resources/scripts/dev_tools/pr_context/git.py +153 -0
  337. package/resources/scripts/dev_tools/pr_context/github.py +549 -0
  338. package/resources/scripts/dev_tools/pr_context/models.py +198 -0
  339. package/resources/scripts/dev_tools/pr_context/render.py +342 -0
  340. package/resources/scripts/dev_tools/pr_context/render_feature_excerpts.py +256 -0
  341. package/resources/scripts/dev_tools/pr_context/render_pr_helpers.py +291 -0
  342. package/resources/scripts/dev_tools/pr_context/summary_helpers.py +386 -0
  343. package/resources/scripts/dev_tools/pr_context/verification_evidence.py +171 -0
  344. package/resources/scripts/dev_tools/prompt_mode_contract.py +152 -0
  345. package/resources/scripts/dev_tools/push_down_claude_customizations.py +188 -0
  346. package/resources/scripts/dev_tools/push_down_codex_and_agents_customizations.py +139 -0
  347. package/resources/scripts/dev_tools/push_down_copilot_customizations.py +504 -0
  348. package/resources/scripts/dev_tools/push_down_copilot_customizations_filesystem.py +217 -0
  349. package/resources/scripts/dev_tools/push_down_copilot_customizations_rewrites.py +293 -0
  350. package/resources/scripts/dev_tools/resolve_file_prompt.py +457 -0
  351. package/resources/scripts/dev_tools/resolve_hard_lock_prompt.py +444 -0
  352. package/resources/scripts/dev_tools/validate_orchestration_artifacts.py +554 -0
  353. package/resources/templates/codex_native_converter.py +35 -0
  354. package/resources/templates/collect_commit_context.py +212 -0
  355. package/resources/templates/collect_pr_context.py +74 -0
  356. package/resources/templates/hello_pwsh.ps1 +3 -0
  357. package/resources/templates/hello_python.py +11 -0
  358. package/resources/templates/link-parent-child.ps1 +480 -0
  359. package/resources/templates/new-claude-worktree-session.ps1 +232 -0
  360. package/resources/templates/new-potential-entry.ps1 +187 -0
  361. package/resources/templates/new_active_feature_folder.py +67 -0
  362. package/resources/templates/new_potential_bug_entry.py +54 -0
  363. package/resources/templates/policy_audit/AGENTS.md +117 -0
  364. package/resources/templates/policy_audit/code-review.yyyy-MM-ddTHH-mm.md +165 -0
  365. package/resources/templates/policy_audit/feature-audit.yyyy-MM-ddTHH-mm.md +124 -0
  366. package/resources/templates/policy_audit/policy-audit.yyyy-MM-ddTHH-mm.md +649 -0
  367. package/resources/templates/potential_to_issue.py +55 -0
  368. package/resources/templates/push_down_claude_customizations.py +188 -0
  369. package/resources/templates/push_down_codex_and_agents_customizations.py +95 -0
  370. package/resources/templates/push_down_copilot_customizations.py +124 -0
  371. package/resources/templates/resolve_atomic_plan_prompt.py +75 -0
  372. package/resources/templates/resolve_hard_lock_prompt.py +65 -0
  373. package/resources/templates/run-poshqc-analyze-autofix.ps1 +16 -0
  374. package/resources/templates/run-poshqc-analyze.ps1 +26 -0
  375. package/resources/templates/run-poshqc-format.ps1 +26 -0
  376. package/resources/templates/run-poshqc-suite.ps1 +24 -0
  377. package/resources/templates/run-poshqc-test.ps1 +32 -0
  378. package/resources/templates/sync-agents-from-instructions.ps1 +400 -0
  379. package/resources/templates/validate_orchestration_artifacts.py +55 -0
  380. package/resources/templates/vscode-cli.helpers.ps1 +63 -0
@@ -0,0 +1,409 @@
1
+ <#
2
+ .SYNOPSIS
3
+ Runs the bundled PoshQC suite in one pass.
4
+ .DESCRIPTION
5
+ Executes formatting, analysis, and Pester checks against the selected workspace root and scan folders.
6
+ #>
7
+ function Invoke-PoshQCSuite {
8
+ [CmdletBinding()]
9
+ param(
10
+ [string] $Root,
11
+ [string[]] $ScanFolders,
12
+ [string] $PssaSettingsPath = $script:PssaSettings,
13
+ [string] $PesterSettingsPath = $script:PesterSettings,
14
+ [string[]] $ExcludeDirs = $script:DefaultExcludedDirs,
15
+ [switch] $DisableKoverageCopy,
16
+ [string] $KoverageOutputPath
17
+ )
18
+
19
+ $ErrorActionPreference = 'Stop'
20
+
21
+ Invoke-PoshQCFormat -Root $Root -ScanFolders $ScanFolders -SettingsPath $PssaSettingsPath -ExcludeDirs $ExcludeDirs
22
+ Invoke-PoshQCAnalyze -Root $Root -ScanFolders $ScanFolders -SettingsPath $PssaSettingsPath -ExcludeDirs $ExcludeDirs
23
+ Invoke-PoshQCTest -Root $Root -ScanFolders $ScanFolders -SettingsPath $PesterSettingsPath -ExcludeDirs $ExcludeDirs -DisableKoverageCopy:$DisableKoverageCopy -KoverageOutputPath $KoverageOutputPath
24
+ }
25
+
26
+ <#
27
+ .SYNOPSIS
28
+ Converts coverage XML paths from absolute to relative.
29
+ .DESCRIPTION
30
+ Rewrites Pester coverage XML file paths to be relative to the repo root for Koverage compatibility.
31
+ .PARAMETER InputPath
32
+ Path to input coverage XML file.
33
+ .PARAMETER OutputPath
34
+ Path to write relative-path coverage XML.
35
+ .PARAMETER RepoRoot
36
+ Repository root directory for path relativization.
37
+ .PARAMETER InputContent
38
+ Alternative to InputPath - provide XML content directly.
39
+ .PARAMETER PassThru
40
+ Return the converted XML content as output.
41
+ #>
42
+ function Convert-PoshQCCoverageToRelative {
43
+ [CmdletBinding()]
44
+ param(
45
+ [Parameter()][string] $InputPath,
46
+ [Parameter()][string] $OutputPath,
47
+ [Parameter()][string] $RepoRoot,
48
+ [Parameter()][string] $InputContent,
49
+ [switch] $PassThru,
50
+ [scriptblock] $ResolvePath = { param([string] $Path) (Resolve-Path -Path $Path -ErrorAction Stop).Path },
51
+ [scriptblock] $JoinPath = { param([string] $Parent, [string] $Child) Join-Path -Path $Parent -ChildPath $Child },
52
+ [scriptblock] $TestPathExists = { param([string] $Path) Test-Path $Path },
53
+ [scriptblock] $ReadContent = { param([string] $Path) Get-Content -Path $Path -Raw },
54
+ [scriptblock] $WriteContent = { param([string] $Path, [string] $Content) Set-Content -Path $Path -Value $Content -Encoding UTF8 },
55
+ [scriptblock] $EnsureDirectory = {
56
+ param([string] $Path)
57
+ $dir = Split-Path -Parent $Path
58
+ if ($dir -and -not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
59
+ },
60
+ [scriptblock] $GetDefaultOutputPath = {
61
+ param([string] $ResolvedInputPath, [string] $ResolvedRoot)
62
+ $coverageDir = if ($ResolvedInputPath) { Split-Path -Parent $ResolvedInputPath } else { $ResolvedRoot }
63
+ $coverageBase = if ($ResolvedInputPath) { [IO.Path]::GetFileNameWithoutExtension($ResolvedInputPath) } else { 'powershell-coverage' }
64
+ Join-Path -Path $coverageDir -ChildPath "$coverageBase.koverage.xml"
65
+ },
66
+ [scriptblock] $Logger = {
67
+ param([string] $Message)
68
+ Write-Information $Message -InformationAction Continue
69
+ }
70
+ )
71
+
72
+ $ErrorActionPreference = 'Stop'
73
+
74
+ if (-not $RepoRoot) {
75
+ $RepoRoot = $PWD.ProviderPath
76
+ }
77
+
78
+ if (-not $InputPath -and -not $InputContent) {
79
+ & $Logger 'No coverage input provided; skipping conversion.'
80
+ return
81
+ }
82
+
83
+ $resolvedRoot = $RepoRoot
84
+ try {
85
+ $maybeResolvedRoot = & $ResolvePath $RepoRoot
86
+ if ($maybeResolvedRoot) {
87
+ $resolvedRoot = if ($maybeResolvedRoot -is [string]) { $maybeResolvedRoot } else { $maybeResolvedRoot.Path }
88
+ }
89
+ }
90
+ catch {
91
+ $resolvedRoot = $RepoRoot
92
+ }
93
+
94
+ $resolvedInputPath = $null
95
+ if ($InputPath) {
96
+ $resolvedInputPath = if ([IO.Path]::IsPathRooted($InputPath)) { $InputPath } else { & $JoinPath $resolvedRoot $InputPath }
97
+ if (-not (& $TestPathExists $resolvedInputPath)) {
98
+ & $Logger "Coverage file not found; skipping Koverage output: $resolvedInputPath"
99
+ return
100
+ }
101
+
102
+ if (-not $InputContent) {
103
+ $InputContent = & $ReadContent $resolvedInputPath
104
+ }
105
+ }
106
+
107
+ $repoRootClean = ConvertTo-PoshQCPath $resolvedRoot
108
+ # Normalize to forward slashes for consistent regex matching across platforms
109
+ $repoRootNormalized = $repoRootClean -replace '\\', '/'
110
+ $escapedRoot = [regex]::Escape($repoRootNormalized)
111
+ # Replace forward slashes with character class that matches both separators
112
+ $flexiblePattern = $escapedRoot -replace '/', '[\\/]'
113
+ # Match both forward and backslashes after the path
114
+ $escapedPrefixPattern = "$flexiblePattern[\\/]"
115
+ $fixedContent = $InputContent -replace $escapedPrefixPattern, ''
116
+
117
+ if ($PassThru) {
118
+ return $fixedContent
119
+ }
120
+
121
+ if (-not $OutputPath) {
122
+ $OutputPath = & $GetDefaultOutputPath $resolvedInputPath $resolvedRoot
123
+ }
124
+
125
+ $resolvedOutputPath = if ([IO.Path]::IsPathRooted($OutputPath)) { $OutputPath } else { & $JoinPath $resolvedRoot $OutputPath }
126
+ & $EnsureDirectory $resolvedOutputPath
127
+ & $WriteContent $resolvedOutputPath $fixedContent
128
+ & $Logger "Wrote Koverage coverage copy: $resolvedOutputPath"
129
+ }
130
+
131
+ <#
132
+ .SYNOPSIS
133
+ Runs Pester tests with coverage reporting.
134
+ .DESCRIPTION
135
+ Executes Pester tests using repo configuration, generates coverage reports in multiple formats.
136
+ .PARAMETER Root
137
+ Root directory for test discovery. Defaults to current location.
138
+ .PARAMETER ScanFolders
139
+ Optional workspace-relative or workspace-contained folders to scan instead of the entire root.
140
+ .PARAMETER SettingsPath
141
+ Path to Pester configuration file.
142
+ .PARAMETER ExcludeDirs
143
+ Directory names to exclude from test/coverage paths.
144
+ .PARAMETER KoverageOutputPath
145
+ Custom path for Koverage-compatible coverage XML output.
146
+ .PARAMETER DisableKoverageCopy
147
+ Skip creation of Koverage-friendly coverage copy.
148
+ .PARAMETER ScanFolders
149
+ Optional workspace-relative or workspace-contained folders to scan instead of the entire root.
150
+ #>
151
+ function Invoke-PoshQCTest {
152
+ [CmdletBinding()]
153
+ param(
154
+ [string] $Root,
155
+ [string[]] $ScanFolders,
156
+ [string] $SettingsPath = $script:PesterSettings,
157
+ [string[]] $ExcludeDirs = $script:DefaultExcludedDirs,
158
+ [string] $KoverageOutputPath,
159
+ [switch] $DisableKoverageCopy,
160
+ [scriptblock] $EnsureModule = {
161
+ param([string] $Name, [string] $ErrorMessage)
162
+ if (-not (Get-Module -ListAvailable -Name $Name)) { throw $ErrorMessage }
163
+ Import-Module $Name -ErrorAction Stop
164
+ },
165
+ [scriptblock] $TestPathExists = { param([string] $Path) Test-Path $Path },
166
+ [scriptblock] $LoadSettings = { param([string] $Path) Import-PowerShellDataFile -Path $Path },
167
+ [scriptblock] $BuildConfiguration = { param($Settings) New-PesterConfiguration -Hashtable $Settings },
168
+ [scriptblock] $ExpandRunPaths = {
169
+ param($Config, [string] $RootPath, [string[]] $Excluded)
170
+ $initialPaths = if ($Config.Run.Path -is [System.Array]) { @($Config.Run.Path) } elseif ($Config.Run.Path -and $Config.Run.Path.Value) { @($Config.Run.Path.Value) } else { @() }
171
+ if ($initialPaths) {
172
+ $resolvedPaths = @(
173
+ $initialPaths |
174
+ ForEach-Object { Join-Path $RootPath $_ } |
175
+ Where-Object { $Excluded -notcontains (Split-Path -Path $_ -Leaf) }
176
+ )
177
+ $Config.Run.Path = $resolvedPaths
178
+ }
179
+
180
+ if ($Excluded) {
181
+ $excludedPaths = @($Excluded | ForEach-Object { Join-Path $RootPath $_ })
182
+ $existingExclude = if ($Config.Run.ExcludePath -is [System.Array]) { @($Config.Run.ExcludePath | ForEach-Object { Join-Path $RootPath $_ }) } elseif ($Config.Run.ExcludePath -and $Config.Run.ExcludePath.Value) { @($Config.Run.ExcludePath.Value | ForEach-Object { Join-Path $RootPath $_ }) } else { @() }
183
+ $Config.Run.ExcludePath = $existingExclude + $excludedPaths
184
+ }
185
+
186
+ $Config
187
+ },
188
+ [scriptblock] $EnsureResultPath = {
189
+ param($Config, [string] $RootPath)
190
+ if ($Config.TestResult.Enabled.Value -and $Config.TestResult.OutputPath.Value) {
191
+ $resultPath = $Config.TestResult.OutputPath.Value
192
+ $resultDir = Split-Path -Parent $resultPath
193
+ if (-not [string]::IsNullOrWhiteSpace($resultDir)) {
194
+ $resolvedResultDir = if ([IO.Path]::IsPathRooted($resultDir)) { $resultDir } else { Join-Path $RootPath $resultDir }
195
+ New-Item -ItemType Directory -Path $resolvedResultDir -Force | Out-Null
196
+ }
197
+ $Config.TestResult.OutputPath = if ([IO.Path]::IsPathRooted($resultPath)) {
198
+ $resultPath
199
+ } else {
200
+ Join-Path $RootPath $resultPath
201
+ }
202
+ }
203
+ $Config
204
+ },
205
+ [scriptblock] $ResolveScanFolders = {
206
+ param([string] $RootPath, [string[]] $Folders)
207
+ Resolve-PoshQCScanFolder -Root $RootPath -ScanFolders $Folders
208
+ },
209
+ [scriptblock] $ExpandCoveragePaths = {
210
+ param($Config, [string] $RootPath)
211
+ if (-not $Config.CodeCoverage) { return $Config }
212
+
213
+ if ($Config.CodeCoverage.Path.Value) {
214
+ $Config.CodeCoverage.Path = @(
215
+ $Config.CodeCoverage.Path.Value | ForEach-Object { if ([IO.Path]::IsPathRooted($_)) { $_ } else { Join-Path $RootPath $_ } }
216
+ )
217
+ }
218
+
219
+ if ($Config.CodeCoverage.OutputPath.Value) {
220
+ $coveragePath = $Config.CodeCoverage.OutputPath.Value
221
+ $coverageDir = Split-Path -Parent $coveragePath
222
+ if (-not [string]::IsNullOrWhiteSpace($coverageDir)) {
223
+ $resolvedCoverageDir = if ([IO.Path]::IsPathRooted($coverageDir)) { $coverageDir } else { Join-Path $RootPath $coverageDir }
224
+ New-Item -ItemType Directory -Path $resolvedCoverageDir -Force | Out-Null
225
+ }
226
+ $Config.CodeCoverage.OutputPath = if ([IO.Path]::IsPathRooted($coveragePath)) {
227
+ $coveragePath
228
+ } else {
229
+ Join-Path $RootPath $coveragePath
230
+ }
231
+ }
232
+
233
+ $Config
234
+ },
235
+ [scriptblock] $EnumerateTests = {
236
+ param([string[]] $Paths, [string[]] $Excluded, [scriptblock] $TestPathFn)
237
+ $tests = @()
238
+ foreach ($path in $Paths) {
239
+ if (-not (& $TestPathFn $path)) { continue }
240
+ $tests += Get-ChildItem -Path $path -Recurse -Include *.Tests.ps1 | Where-Object {
241
+ $parts = $_.FullName -split '[\\/]+' | Where-Object { $_ -ne '' }
242
+ foreach ($dir in $Excluded) {
243
+ if ($parts -contains $dir) { return $false }
244
+ }
245
+ return $true
246
+ }
247
+ }
248
+ @($tests | Sort-Object -Property FullName -Stable)
249
+ },
250
+ [scriptblock] $Logger = {
251
+ param([string] $Message)
252
+ # Use Write-Information so the replayed summary stays visible while respecting approved verbs.
253
+ Write-Information $Message -InformationAction Continue
254
+ },
255
+ [scriptblock] $InvokePester = { param($Config) Invoke-Pester -Configuration $Config },
256
+ [scriptblock] $CopyCoverage = {
257
+ param([string] $CoveragePath, [string] $RepoRoot, [string] $KoveragePath)
258
+ Convert-PoshQCCoverageToRelative -InputPath $CoveragePath -OutputPath $KoveragePath -RepoRoot $RepoRoot
259
+ }
260
+ )
261
+
262
+ $ErrorActionPreference = 'Stop'
263
+
264
+ if (-not $Root) {
265
+ $Root = $PWD.ProviderPath
266
+ }
267
+
268
+ & $EnsureModule 'Pester' "Pester is not installed. Run Install-PoshQCTool (alias Install-PoshQCTools) first."
269
+
270
+ if (-not (& $TestPathExists $SettingsPath)) {
271
+ throw "Settings not found: $SettingsPath"
272
+ }
273
+
274
+ $settings = & $LoadSettings $SettingsPath
275
+ $config = & $BuildConfiguration $settings
276
+ $config = & $ExpandRunPaths $config $Root $ExcludeDirs
277
+ $config = & $EnsureResultPath $config $Root
278
+ $config = & $ExpandCoveragePaths $config $Root
279
+ if ($ScanFolders -and $ScanFolders.Count -gt 0) {
280
+ $resolvedScanFolders = @(& $ResolveScanFolders $Root $ScanFolders)
281
+ if ($resolvedScanFolders.Count -gt 0) {
282
+ $config.Run.Path = $resolvedScanFolders
283
+ }
284
+ }
285
+
286
+ # Reduce console noise from Pester while we replay a concise summary at the end.
287
+ if ($config.Output -and $config.Output.Verbosity) {
288
+ $config.Output.Verbosity = 'Normal'
289
+ }
290
+
291
+ if (-not $config.Run.PassThru) {
292
+ $config.Run.PassThru = $true
293
+ }
294
+
295
+ $coverageEnabled = $false
296
+ if ($config.CodeCoverage) {
297
+ if ($config.CodeCoverage.Enabled -is [bool]) {
298
+ $coverageEnabled = $config.CodeCoverage.Enabled
299
+ } elseif ($config.CodeCoverage.Enabled -and $config.CodeCoverage.Enabled.Value) {
300
+ $coverageEnabled = [bool]$config.CodeCoverage.Enabled.Value
301
+ }
302
+ }
303
+
304
+ if ($coverageEnabled -and $config.CodeCoverage) {
305
+ if ($config.CodeCoverage.Path.Value) {
306
+ $resolvedCoveragePaths = @(
307
+ $config.CodeCoverage.Path.Value |
308
+ ForEach-Object {
309
+ if ([IO.Path]::IsPathRooted($_)) { $_ } else { Join-Path $Root $_ }
310
+ }
311
+ )
312
+ $config.CodeCoverage.Path = $resolvedCoveragePaths
313
+ }
314
+
315
+ if ($config.CodeCoverage.OutputPath.Value) {
316
+ $coveragePath = $config.CodeCoverage.OutputPath.Value
317
+ $coverageDir = Split-Path -Parent $coveragePath
318
+ if (-not [string]::IsNullOrWhiteSpace($coverageDir)) {
319
+ $resolvedCoverageDir = if ([IO.Path]::IsPathRooted($coverageDir)) { $coverageDir } else { Join-Path $Root $coverageDir }
320
+ New-Item -ItemType Directory -Path $resolvedCoverageDir -Force | Out-Null
321
+ }
322
+ $config.CodeCoverage.OutputPath = if ([IO.Path]::IsPathRooted($coveragePath)) {
323
+ $coveragePath
324
+ } else {
325
+ Join-Path $Root $coveragePath
326
+ }
327
+ }
328
+ }
329
+
330
+ $coverageOutputPath = $null
331
+ if ($config.CodeCoverage) {
332
+ if ($config.CodeCoverage.OutputPath -is [string]) {
333
+ $coverageOutputPath = $config.CodeCoverage.OutputPath
334
+ } elseif ($config.CodeCoverage.OutputPath -and $config.CodeCoverage.OutputPath.Value) {
335
+ $coverageOutputPath = $config.CodeCoverage.OutputPath.Value
336
+ }
337
+ }
338
+
339
+ $runPaths = if ($config.Run.Path -is [System.Array]) { [string[]]$config.Run.Path } elseif ($config.Run.Path -and $config.Run.Path.Value) { [string[]]$config.Run.Path.Value } else { @() }
340
+ $testFiles = & $EnumerateTests $runPaths $ExcludeDirs $TestPathExists
341
+ if (-not $testFiles) {
342
+ & $Logger "No Pester test files found under configured paths for root $Root"
343
+ return
344
+ }
345
+
346
+ $pesterResult = & $InvokePester $config
347
+
348
+ $shouldEmitKoverageCopy = -not $DisableKoverageCopy
349
+ if ($shouldEmitKoverageCopy -and $coverageEnabled -and $coverageOutputPath) {
350
+ $derivedKoveragePath = $null
351
+ if ($coverageOutputPath) {
352
+ $coverageBaseName = [IO.Path]::GetFileNameWithoutExtension($coverageOutputPath)
353
+ $coverageParent = Split-Path -Parent $coverageOutputPath
354
+ $derivedKoveragePath = Join-Path $coverageParent "$coverageBaseName.koverage.xml"
355
+ }
356
+
357
+ $effectiveKoveragePath = if ($PSBoundParameters.ContainsKey('KoverageOutputPath') -and -not [string]::IsNullOrWhiteSpace($KoverageOutputPath)) {
358
+ $KoverageOutputPath
359
+ } else {
360
+ $derivedKoveragePath
361
+ }
362
+
363
+ & $CopyCoverage $coverageOutputPath $Root $effectiveKoveragePath
364
+ }
365
+
366
+ if ($pesterResult) {
367
+ $durationSeconds = [math]::Round($pesterResult.Duration.TotalSeconds, 2)
368
+ $testsSummary = "Tests completed in {0:N2}s" -f $durationSeconds
369
+ $countsSummary = "Tests Passed: {0}, Failed: {1}, Skipped: {2}, Inconclusive: {3}, NotRun: {4}" -f `
370
+ $pesterResult.PassedCount, `
371
+ $pesterResult.FailedCount, `
372
+ $pesterResult.SkippedCount, `
373
+ $pesterResult.InconclusiveCount, `
374
+ $pesterResult.NotRunCount
375
+
376
+ $coverageLines = $null
377
+ if ($coverageEnabled -and $pesterResult.CodeCoverage) {
378
+ $coverageReport = $pesterResult.CodeCoverage.CoverageReport
379
+ if ($coverageReport -is [string] -and -not [string]::IsNullOrWhiteSpace($coverageReport)) {
380
+ $rawCoverageLines = @($coverageReport -split "`r?`n")
381
+ $trimmedCoverageLines = @()
382
+
383
+ foreach ($line in $rawCoverageLines) {
384
+ if ([string]::IsNullOrWhiteSpace($line)) { continue }
385
+ if ($line -match '^\s*Missed commands') { break }
386
+ $trimmedCoverageLines += $line.TrimEnd()
387
+ }
388
+
389
+ if (-not $trimmedCoverageLines -and $rawCoverageLines) {
390
+ $trimmedCoverageLines = @($rawCoverageLines[0])
391
+ }
392
+
393
+ if ($trimmedCoverageLines) {
394
+ $coverageLines = $trimmedCoverageLines
395
+ }
396
+ }
397
+ }
398
+
399
+ & $Logger ''
400
+ & $Logger 'Pester summary (replayed for readability):'
401
+ & $Logger $testsSummary
402
+ & $Logger $countsSummary
403
+ if ($coverageLines) {
404
+ foreach ($line in $coverageLines) {
405
+ & $Logger $line
406
+ }
407
+ }
408
+ }
409
+ }
@@ -0,0 +1,31 @@
1
+ @{
2
+ RootModule = 'PoshQC.psm1'
3
+ ModuleVersion = '0.1.1'
4
+ GUID = '6a9b1a8b-1e8b-4c1f-9d6a-34b8e8c2f0e4'
5
+ Author = 'Dan Moisan'
6
+ CompanyName = 'Dan Moisan'
7
+ Copyright = '(c) Dan Moisan. All rights reserved.'
8
+ Description = 'Reusable PowerShell QC helpers (format, analyze, test) using Invoke-Formatter, PSScriptAnalyzer, and Pester.'
9
+ PowerShellVersion = '5.1'
10
+ CompatiblePSEditions = @('Desktop', 'Core')
11
+ RequiredModules = @(
12
+ @{ ModuleName = 'PSScriptAnalyzer'; ModuleVersion = '1.22.0' },
13
+ @{ ModuleName = 'Pester'; ModuleVersion = '5.6.1' }
14
+ )
15
+ FunctionsToExport = @(
16
+ 'Convert-PoshQCCoverageToRelative',
17
+ 'Get-PoshQCFileList',
18
+ 'Install-PoshQCTool',
19
+ 'Invoke-PoshQCFormat',
20
+ 'Invoke-PoshQCAnalyze',
21
+ 'Invoke-PoshQCAnalyzeAutofix',
22
+ 'Invoke-PoshQCSuite',
23
+ 'Invoke-PoshQCTest'
24
+ )
25
+ CmdletsToExport = @()
26
+ VariablesToExport = @()
27
+ AliasesToExport = @('Install-PoshQCTools')
28
+ PrivateData = @{}
29
+ }
30
+
31
+
@@ -0,0 +1,101 @@
1
+ $script:ModuleRoot = Split-Path -Parent $PSCommandPath
2
+ $script:PssaSettings = Join-Path $ModuleRoot 'settings/pssa.settings.psd1'
3
+ $script:PesterSettings = Join-Path $ModuleRoot 'settings/pester.runsettings.psd1'
4
+
5
+ $script:DefaultExcludedDirs = @(
6
+ '.git', '.venv', 'venv', 'node_modules', 'dist', 'build', '.pytest_cache',
7
+ '__pycache__', '.mypy_cache', '.ruff_cache', '.vscode', '.idea', 'artifacts',
8
+ '.vscode-test'
9
+ )
10
+
11
+ <#
12
+ .SYNOPSIS
13
+ Installs PoshQC dependencies (PSScriptAnalyzer and Pester).
14
+ .DESCRIPTION
15
+ Ensures PSGallery is trusted and installs required module versions in the CurrentUser scope.
16
+ #>
17
+ function Install-PoshQCTool {
18
+ [CmdletBinding()]
19
+ param(
20
+ [scriptblock] $SetTls = { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 },
21
+ [scriptblock] $GetRepository = { param([string] $Name) Get-PSRepository -Name $Name -ErrorAction SilentlyContinue },
22
+ [scriptblock] $RegisterRepository = { Register-PSRepository -Default -InstallationPolicy Trusted },
23
+ [scriptblock] $SetRepository = { param([string] $Name, [string] $Policy) Set-PSRepository -Name $Name -InstallationPolicy $Policy -ErrorAction Stop },
24
+ [scriptblock] $FindModule = { param([string] $Name) Get-Module -ListAvailable -Name $Name },
25
+ [scriptblock] $InstallModule = { param([string] $Name, [string] $Version) Install-Module -Name $Name -RequiredVersion $Version -Scope CurrentUser -AllowClobber -Force },
26
+ [scriptblock] $Logger = {
27
+ param([string] $Message, [string] $Level = 'Information')
28
+ switch ($Level) {
29
+ 'Warning' { Write-Warning $Message }
30
+ 'Verbose' { Write-Verbose $Message }
31
+ default { Write-Information $Message -InformationAction Continue }
32
+ }
33
+ }
34
+ )
35
+
36
+ $ErrorActionPreference = 'Stop'
37
+
38
+ try {
39
+ & $SetTls
40
+ } catch {
41
+ & $Logger "Unable to enforce TLS 1.2 for module install: $($_.Exception.Message)" 'Verbose'
42
+ }
43
+ $gallery = & $GetRepository 'PSGallery'
44
+ if (-not $gallery) {
45
+ & $Logger 'PSGallery not found; registering.'
46
+ & $RegisterRepository
47
+ } elseif ($gallery.InstallationPolicy -ne 'Trusted') {
48
+ try {
49
+ & $SetRepository 'PSGallery' 'Trusted'
50
+ } catch {
51
+ & $Logger 'Could not set PSGallery as trusted automatically. You may be prompted during install.' 'Warning'
52
+ }
53
+ }
54
+
55
+ $requiredModules = @(
56
+ @{ Name = 'PSScriptAnalyzer'; Version = '1.22.0' },
57
+ @{ Name = 'Pester'; Version = '5.6.1' }
58
+ )
59
+
60
+ foreach ($module in $requiredModules) {
61
+ $installed = & $FindModule $module.Name | Where-Object { $_.Version -ge [version]$module.Version } | Select-Object -First 1
62
+ if ($installed) {
63
+ & $Logger "$($module.Name) $($installed.Version) already present."
64
+ continue
65
+ }
66
+
67
+ & $Logger "Installing $($module.Name) $($module.Version) (CurrentUser scope)..."
68
+ try {
69
+ & $InstallModule $module.Name $module.Version
70
+ } catch {
71
+ throw "Failed to install $($module.Name) $($module.Version): $($_.Exception.Message)"
72
+ }
73
+
74
+ $post = & $FindModule $module.Name | Where-Object { $_.Version -ge [version]$module.Version } | Select-Object -First 1
75
+ if (-not $post) {
76
+ throw "Failed to install $($module.Name) $($module.Version)."
77
+ }
78
+ & $Logger "$($module.Name) $($module.Version) installed."
79
+ }
80
+ }
81
+
82
+ # PowerShell 7.6+ treats dot-sourced .psm1 files as isolated modules, so
83
+ # functions defined in them do not enter the parent module scope. Loading the
84
+ # file content as a scriptblock bypasses that behaviour.
85
+ . ([scriptblock]::Create((Get-Content (Join-Path $script:ModuleRoot 'PoshQC.FileDiscovery.psm1') -Raw)))
86
+ . ([scriptblock]::Create((Get-Content (Join-Path $script:ModuleRoot 'PoshQC.Analyzer.psm1') -Raw)))
87
+ . ([scriptblock]::Create((Get-Content (Join-Path $script:ModuleRoot 'PoshQC.Testing.psm1') -Raw)))
88
+
89
+ Set-Alias -Name Install-PoshQCTools -Value Install-PoshQCTool
90
+
91
+ Export-ModuleMember -Function @(
92
+ 'Get-PoshQCFileList',
93
+ 'Install-PoshQCTool',
94
+ 'Invoke-PoshQCFormat',
95
+ 'Invoke-PoshQCAnalyze',
96
+ 'Invoke-PoshQCAnalyzeAutofix',
97
+ 'Invoke-PoshQCSuite',
98
+ 'Invoke-PoshQCTest',
99
+ 'Convert-PoshQCCoverageToRelative'
100
+ ) -Alias @('Install-PoshQCTools')
101
+
@@ -0,0 +1,80 @@
1
+ # PoshQC
2
+
3
+ PoshQC is a lightweight PowerShell quality gate that wraps Invoke-Formatter, PSScriptAnalyzer, and Pester with repo-safe defaults. It targets both Windows PowerShell 5.1 and PowerShell 7.6+.
4
+
5
+ ## What it does
6
+
7
+ - Formats PowerShell code with consistent indentation and pipeline alignment.
8
+ - Lints with PSScriptAnalyzer using a strict, dual-runtime ruleset.
9
+ - Runs Pester tests with code coverage (CoverageGutters output), plus an optional Koverage-friendly copy.
10
+ - Provides a single suite entry point that can format, analyze, and test a selected workspace scope.
11
+ - Provides a one-time dependency installer for PSScriptAnalyzer and Pester.
12
+
13
+ ## Requirements
14
+
15
+ - PowerShell 5.1 or 7.6+
16
+ - Modules: PSScriptAnalyzer 1.22.0, Pester 5.6.1 (installed automatically via the helper if missing)
17
+
18
+ ## Getting started
19
+
20
+ 1) Install dependencies (CurrentUser scope):
21
+
22
+ ```powershell
23
+ Import-Module ./PoshQC.psm1
24
+ Install-PoshQCTool # alias: Install-PoshQCTools
25
+ ```
26
+ 2) Format everything under the repo root:
27
+
28
+ ```powershell
29
+ Import-Module ./PoshQC.psm1
30
+ Invoke-PoshQCFormat -Root .
31
+ ```
32
+ 3) Lint with PSScriptAnalyzer:
33
+
34
+ ```powershell
35
+ Invoke-PoshQCAnalyze -Root .
36
+ ```
37
+ 4) Test with Pester + coverage:
38
+
39
+ ```powershell
40
+ Invoke-PoshQCTest -Root .
41
+ ```
42
+
43
+ 5) Run the full suite against a selected scope:
44
+
45
+ ```powershell
46
+ Import-Module ./PoshQC.psm1
47
+ Invoke-PoshQCSuite -Root . -ScanFolders @('scripts', 'tests/powershell')
48
+ ```
49
+
50
+ - Writes JUnit XML to `artifacts/pester/pester-junit.xml`.
51
+ - Writes CoverageGutters XML to `artifacts/pester/powershell-coverage.xml`.
52
+ - Also emits `*.koverage.xml` (relative paths) for VS Code Coverage Gutters and Koverage. Disable with `-DisableKoverageCopy` or override the path with `-KoverageOutputPath`.
53
+
54
+ ## Functions
55
+
56
+ - `Install-PoshQCTool` / `Install-PoshQCTools`: install required modules.
57
+ - `Invoke-PoshQCFormat`: run Invoke-Formatter across the repo, honoring exclusions.
58
+ - `Invoke-PoshQCAnalyze`: run PSScriptAnalyzer with the bundled settings.
59
+ - `Invoke-PoshQCSuite`: run format, analyze, and test in one pass, optionally narrowing the scan scope with `-ScanFolders`.
60
+ - `Invoke-PoshQCTest`: run Pester using the bundled runsettings, including coverage and optional Koverage copy.
61
+ - `Convert-PoshQCCoverageToRelative`: utility to strip repo-root prefixes and write a `.koverage.xml` copy (used internally by `Invoke-PoshQCTest`).
62
+
63
+ ## Configuration
64
+
65
+ - PSScriptAnalyzer settings: `./settings/pssa.settings.psd1`
66
+ - Enforces compatible syntax for 5.1 and 7.6, 4-space indentation, ShouldProcess for state-changing functions, and safety guards (no Invoke-Expression, no global vars, etc.).
67
+ - Pester settings: `./settings/pester.runsettings.psd1`
68
+ - Runs tests under `scripts` and `tests/powershell`, outputs JUnit XML and CoverageGutters coverage, and enables coverage over `scripts/dev-tools/*.ps1`, `scripts/powershell/**/*.psm1`, and `src/**/*.ps1`.
69
+ - When `-ScanFolders` is supplied to `Invoke-PoshQCSuite` or the lower-level commands, the suite narrows discovery to those workspace-relative or workspace-contained folders.
70
+
71
+ ## Typical workflow
72
+
73
+ - Day-to-day: run `Invoke-PoshQCFormat`, `Invoke-PoshQCAnalyze`, then `Invoke-PoshQCTest` before committing.
74
+ - Bundled / scoped: run `Invoke-PoshQCSuite -Root . -ScanFolders @('scripts', 'tests/powershell')` when you want the suite to focus on selected workspace folders.
75
+ - CI: call `Invoke-PoshQCTest` to get tests + coverage and consume JUnit/CoverageGutters artifacts.
76
+
77
+ ## Notes for standalone use
78
+
79
+ - Place `PoshQC.psm1`, `PoshQC.psd1`, and the `settings/` folder together; import the module from that directory.
80
+ - Adjust `settings/pester.runsettings.psd1` and `settings/pssa.settings.psd1` to match your repo paths and policies.
@@ -0,0 +1,59 @@
1
+ @{
2
+ Run = @{
3
+ Path = @('scripts', 'tests/powershell', 'tests/scripts')
4
+ Exit = $true
5
+ }
6
+ Should = @{
7
+ ErrorAction = 'Stop'
8
+ }
9
+ Output = @{
10
+ Verbosity = 'Detailed'
11
+ }
12
+ TestResult = @{
13
+ Enabled = $true
14
+ OutputFormat = 'JUnitXml'
15
+ OutputPath = 'artifacts/pester/pester-junit.xml'
16
+ }
17
+ CodeCoverage = @{
18
+ Enabled = $true
19
+ # Use Pester's CoverageGutters format so VS Code Coverage Gutters
20
+ # can map file paths correctly.
21
+ OutputFormat = 'CoverageGutters'
22
+ OutputPath = 'artifacts/pester/powershell-coverage.xml'
23
+ Path = @(
24
+ '.claude/hooks/validate-bash.ps1'
25
+ '.claude/hooks/check-python-test-purity.ps1'
26
+ '.claude/hooks/check-powershell-test-purity.ps1'
27
+ '.claude/hooks/enforce-python-batch-budget.ps1'
28
+ '.claude/hooks/enforce-powershell-batch-budget.ps1'
29
+ )
30
+ ExcludedPath = @(
31
+ '.claude/hooks/validate-feature-review-coverage.ps1' # Feature-review wrapper around repository evidence; not deterministic in normal unit-test execution.
32
+ 'scripts/dev-tools/bootstrap-host.ps1' # Host bootstrap entrypoint for external setup; not deterministic in normal unit-test execution.
33
+ 'scripts/dev-tools/bootstrap-host.helpers.ps1' # Bootstrap helper for host setup; not deterministic in normal unit-test execution.
34
+ 'scripts/dev-tools/format-powershell.ps1' # Thin formatter wrapper around external tooling; not deterministic in normal unit-test execution.
35
+ 'scripts/dev-tools/load-openai-key.ps1' # Local secret-loading wrapper; not deterministic in normal unit-test execution.
36
+ 'scripts/dev-tools/publish-sideloaded-extension.ps1' # VS Code extension publication wrapper; not deterministic in normal unit-test execution.
37
+ 'scripts/dev-tools/run-actionlint.ps1' # External actionlint wrapper; not deterministic in normal unit-test execution.
38
+ 'scripts/dev-tools/run-pester.ps1' # Pester CLI wrapper; not deterministic in normal unit-test execution.
39
+ 'scripts/dev-tools/run-poshqc-suite.ps1' # PoshQC CLI wrapper; not deterministic in normal unit-test execution.
40
+ 'scripts/dev-tools/run-psscriptanalyzer.ps1' # PSScriptAnalyzer CLI wrapper; not deterministic in normal unit-test execution.
41
+ 'scripts/dev-tools/verify-host.ps1' # Host verification wrapper for local tools; not deterministic in normal unit-test execution.
42
+ 'scripts/dev-tools/vscode-cli.helpers.ps1' # VS Code CLI helper around external processes; not deterministic in normal unit-test execution.
43
+ 'scripts/powershell/PoshQC/PoshQC.Analyzer.psm1' # Analyzer orchestration module over external tooling; not deterministic in normal unit-test execution.
44
+ 'scripts/powershell/PoshQC/PoshQC.FileDiscovery.psm1' # File-discovery module coupled to workspace traversal; not deterministic in normal unit-test execution.
45
+ 'scripts/powershell/PoshQC/PoshQC.Testing.psm1' # Pester orchestration module over external tooling; not deterministic in normal unit-test execution.
46
+ )
47
+ # Optional: don't fail the run on coverage percentage
48
+ CoveragePercentTarget = 0
49
+ }
50
+ }
51
+
52
+
53
+
54
+
55
+
56
+
57
+
58
+
59
+