@event4u/agent-config 1.9.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 (446) hide show
  1. package/.agent-src/README.md +64 -0
  2. package/.agent-src/commands/agent-handoff.md +64 -0
  3. package/.agent-src/commands/agent-status.md +83 -0
  4. package/.agent-src/commands/agents-audit.md +243 -0
  5. package/.agent-src/commands/agents-cleanup.md +169 -0
  6. package/.agent-src/commands/agents-prepare.md +137 -0
  7. package/.agent-src/commands/analyze-reference-repo.md +191 -0
  8. package/.agent-src/commands/bug-fix.md +181 -0
  9. package/.agent-src/commands/bug-investigate.md +175 -0
  10. package/.agent-src/commands/commit.md +121 -0
  11. package/.agent-src/commands/compress.md +177 -0
  12. package/.agent-src/commands/config-agent-settings.md +126 -0
  13. package/.agent-src/commands/context-create.md +167 -0
  14. package/.agent-src/commands/context-refactor.md +170 -0
  15. package/.agent-src/commands/copilot-agents-init.md +150 -0
  16. package/.agent-src/commands/copilot-agents-optimize.md +251 -0
  17. package/.agent-src/commands/create-pr-description.md +112 -0
  18. package/.agent-src/commands/create-pr.md +76 -0
  19. package/.agent-src/commands/do-and-judge.md +114 -0
  20. package/.agent-src/commands/do-in-steps.md +84 -0
  21. package/.agent-src/commands/e2e-heal.md +98 -0
  22. package/.agent-src/commands/e2e-plan.md +85 -0
  23. package/.agent-src/commands/estimate-ticket.md +80 -0
  24. package/.agent-src/commands/feature-dev.md +111 -0
  25. package/.agent-src/commands/feature-explore.md +180 -0
  26. package/.agent-src/commands/feature-plan.md +288 -0
  27. package/.agent-src/commands/feature-refactor.md +181 -0
  28. package/.agent-src/commands/feature-roadmap.md +184 -0
  29. package/.agent-src/commands/fix-ci.md +48 -0
  30. package/.agent-src/commands/fix-portability.md +97 -0
  31. package/.agent-src/commands/fix-pr-bot-comments.md +146 -0
  32. package/.agent-src/commands/fix-pr-comments.md +58 -0
  33. package/.agent-src/commands/fix-pr-developer-comments.md +152 -0
  34. package/.agent-src/commands/fix-references.md +94 -0
  35. package/.agent-src/commands/fix-seeder.md +146 -0
  36. package/.agent-src/commands/implement-ticket.md +133 -0
  37. package/.agent-src/commands/jira-ticket.md +71 -0
  38. package/.agent-src/commands/judge.md +86 -0
  39. package/.agent-src/commands/memory-add.md +130 -0
  40. package/.agent-src/commands/memory-full.md +97 -0
  41. package/.agent-src/commands/memory-promote.md +144 -0
  42. package/.agent-src/commands/mode.md +121 -0
  43. package/.agent-src/commands/module-create.md +132 -0
  44. package/.agent-src/commands/module-explore.md +157 -0
  45. package/.agent-src/commands/optimize-agents.md +139 -0
  46. package/.agent-src/commands/optimize-augmentignore.md +262 -0
  47. package/.agent-src/commands/optimize-rtk-filters.md +120 -0
  48. package/.agent-src/commands/optimize-skills.md +121 -0
  49. package/.agent-src/commands/override-create.md +97 -0
  50. package/.agent-src/commands/override-manage.md +96 -0
  51. package/.agent-src/commands/package-reset.md +154 -0
  52. package/.agent-src/commands/package-test.md +154 -0
  53. package/.agent-src/commands/prepare-for-review.md +91 -0
  54. package/.agent-src/commands/project-analyze.md +300 -0
  55. package/.agent-src/commands/project-health.md +95 -0
  56. package/.agent-src/commands/propose-memory.md +108 -0
  57. package/.agent-src/commands/quality-fix.md +106 -0
  58. package/.agent-src/commands/refine-ticket.md +81 -0
  59. package/.agent-src/commands/review-changes.md +130 -0
  60. package/.agent-src/commands/review-routing.md +111 -0
  61. package/.agent-src/commands/roadmap-create.md +110 -0
  62. package/.agent-src/commands/roadmap-execute.md +68 -0
  63. package/.agent-src/commands/rule-compliance-audit.md +139 -0
  64. package/.agent-src/commands/tests-create.md +73 -0
  65. package/.agent-src/commands/tests-execute.md +58 -0
  66. package/.agent-src/commands/threat-model.md +115 -0
  67. package/.agent-src/commands/update-form-request-messages.md +189 -0
  68. package/.agent-src/commands/upstream-contribute.md +171 -0
  69. package/.agent-src/contexts/augment-infrastructure.md +181 -0
  70. package/.agent-src/contexts/documentation-hierarchy.md +142 -0
  71. package/.agent-src/contexts/model-recommendations.md +142 -0
  72. package/.agent-src/contexts/override-system.md +187 -0
  73. package/.agent-src/contexts/skills-and-commands.md +154 -0
  74. package/.agent-src/contexts/subagent-configuration.md +62 -0
  75. package/.agent-src/guidelines/agent-infra/agent-interaction-and-decision-quality.md +110 -0
  76. package/.agent-src/guidelines/agent-infra/break-glass-usage.md +113 -0
  77. package/.agent-src/guidelines/agent-infra/developer-judgment.md +82 -0
  78. package/.agent-src/guidelines/agent-infra/engineering-memory-data-format.md +117 -0
  79. package/.agent-src/guidelines/agent-infra/layered-settings.md +158 -0
  80. package/.agent-src/guidelines/agent-infra/memory-access.md +121 -0
  81. package/.agent-src/guidelines/agent-infra/naming.md +69 -0
  82. package/.agent-src/guidelines/agent-infra/output-patterns.md +117 -0
  83. package/.agent-src/guidelines/agent-infra/review-routing-data-format.md +144 -0
  84. package/.agent-src/guidelines/agent-infra/role-contracts.md +211 -0
  85. package/.agent-src/guidelines/agent-infra/role-mode-router.md +89 -0
  86. package/.agent-src/guidelines/agent-infra/runtime-layer.md +89 -0
  87. package/.agent-src/guidelines/agent-infra/self-improvement-pipeline.md +135 -0
  88. package/.agent-src/guidelines/agent-infra/size-and-scope.md +189 -0
  89. package/.agent-src/guidelines/agent-infra/tool-integration.md +73 -0
  90. package/.agent-src/guidelines/docs/readme-size-and-splitting.md +153 -0
  91. package/.agent-src/guidelines/e2e/playwright.md +363 -0
  92. package/.agent-src/guidelines/php/api-design.md +115 -0
  93. package/.agent-src/guidelines/php/artisan-commands.md +81 -0
  94. package/.agent-src/guidelines/php/blade-ui.md +78 -0
  95. package/.agent-src/guidelines/php/controllers.md +90 -0
  96. package/.agent-src/guidelines/php/database.md +111 -0
  97. package/.agent-src/guidelines/php/eloquent.md +208 -0
  98. package/.agent-src/guidelines/php/flux.md +80 -0
  99. package/.agent-src/guidelines/php/general.md +191 -0
  100. package/.agent-src/guidelines/php/git.md +96 -0
  101. package/.agent-src/guidelines/php/jobs.md +111 -0
  102. package/.agent-src/guidelines/php/livewire.md +71 -0
  103. package/.agent-src/guidelines/php/logging.md +79 -0
  104. package/.agent-src/guidelines/php/naming.md +89 -0
  105. package/.agent-src/guidelines/php/patterns/dependency-injection.md +57 -0
  106. package/.agent-src/guidelines/php/patterns/dtos.md +199 -0
  107. package/.agent-src/guidelines/php/patterns/events.md +67 -0
  108. package/.agent-src/guidelines/php/patterns/factory.md +53 -0
  109. package/.agent-src/guidelines/php/patterns/pipelines.md +66 -0
  110. package/.agent-src/guidelines/php/patterns/policies.md +66 -0
  111. package/.agent-src/guidelines/php/patterns/repositories.md +122 -0
  112. package/.agent-src/guidelines/php/patterns/service-layer.md +64 -0
  113. package/.agent-src/guidelines/php/patterns/strategy.md +69 -0
  114. package/.agent-src/guidelines/php/patterns.md +28 -0
  115. package/.agent-src/guidelines/php/performance.md +92 -0
  116. package/.agent-src/guidelines/php/resources.md +100 -0
  117. package/.agent-src/guidelines/php/security.md +110 -0
  118. package/.agent-src/guidelines/php/sql.md +97 -0
  119. package/.agent-src/guidelines/php/validations.md +119 -0
  120. package/.agent-src/guidelines/php/websocket.md +100 -0
  121. package/.agent-src/personas/README.md +104 -0
  122. package/.agent-src/personas/ai-agent.md +77 -0
  123. package/.agent-src/personas/critical-challenger.md +73 -0
  124. package/.agent-src/personas/developer.md +73 -0
  125. package/.agent-src/personas/product-owner.md +78 -0
  126. package/.agent-src/personas/qa.md +67 -0
  127. package/.agent-src/personas/senior-engineer.md +77 -0
  128. package/.agent-src/personas/stakeholder.md +78 -0
  129. package/.agent-src/rules/agent-docs.md +61 -0
  130. package/.agent-src/rules/analysis-skill-routing.md +48 -0
  131. package/.agent-src/rules/architecture.md +62 -0
  132. package/.agent-src/rules/artifact-drafting-protocol.md +73 -0
  133. package/.agent-src/rules/ask-when-uncertain.md +52 -0
  134. package/.agent-src/rules/augment-portability.md +38 -0
  135. package/.agent-src/rules/augment-source-of-truth.md +128 -0
  136. package/.agent-src/rules/capture-learnings.md +89 -0
  137. package/.agent-src/rules/cli-output-handling.md +94 -0
  138. package/.agent-src/rules/commit-conventions.md +64 -0
  139. package/.agent-src/rules/context-hygiene.md +90 -0
  140. package/.agent-src/rules/docker-commands.md +55 -0
  141. package/.agent-src/rules/docs-sync.md +79 -0
  142. package/.agent-src/rules/downstream-changes.md +70 -0
  143. package/.agent-src/rules/e2e-testing.md +53 -0
  144. package/.agent-src/rules/guidelines.md +90 -0
  145. package/.agent-src/rules/improve-before-implement.md +94 -0
  146. package/.agent-src/rules/language-and-tone.md +104 -0
  147. package/.agent-src/rules/laravel-translations.md +48 -0
  148. package/.agent-src/rules/markdown-safe-codeblocks.md +18 -0
  149. package/.agent-src/rules/minimal-safe-diff.md +87 -0
  150. package/.agent-src/rules/missing-tool-handling.md +62 -0
  151. package/.agent-src/rules/model-recommendation.md +70 -0
  152. package/.agent-src/rules/package-ci-checks.md +80 -0
  153. package/.agent-src/rules/php-coding.md +63 -0
  154. package/.agent-src/rules/preservation-guard.md +29 -0
  155. package/.agent-src/rules/review-routing-awareness.md +125 -0
  156. package/.agent-src/rules/reviewer-awareness.md +92 -0
  157. package/.agent-src/rules/roadmap-progress-sync.md +56 -0
  158. package/.agent-src/rules/role-mode-adherence.md +54 -0
  159. package/.agent-src/rules/rule-type-governance.md +46 -0
  160. package/.agent-src/rules/runtime-safety.md +42 -0
  161. package/.agent-src/rules/scope-control.md +40 -0
  162. package/.agent-src/rules/security-sensitive-stop.md +77 -0
  163. package/.agent-src/rules/size-enforcement.md +29 -0
  164. package/.agent-src/rules/skill-improvement-trigger.md +58 -0
  165. package/.agent-src/rules/skill-quality.md +110 -0
  166. package/.agent-src/rules/slash-commands.md +30 -0
  167. package/.agent-src/rules/think-before-action.md +91 -0
  168. package/.agent-src/rules/token-efficiency.md +99 -0
  169. package/.agent-src/rules/tool-safety.md +36 -0
  170. package/.agent-src/rules/upstream-proposal.md +76 -0
  171. package/.agent-src/rules/user-interaction.md +79 -0
  172. package/.agent-src/rules/verify-before-complete.md +120 -0
  173. package/.agent-src/scripts/scan-seeder-violations.php +145 -0
  174. package/.agent-src/scripts/update_roadmap_progress.py +244 -0
  175. package/.agent-src/skills/adversarial-review/SKILL.md +149 -0
  176. package/.agent-src/skills/agent-docs-writing/SKILL.md +234 -0
  177. package/.agent-src/skills/analysis-autonomous-mode/SKILL.md +197 -0
  178. package/.agent-src/skills/analysis-skill-router/SKILL.md +134 -0
  179. package/.agent-src/skills/api-design/SKILL.md +104 -0
  180. package/.agent-src/skills/api-endpoint/SKILL.md +185 -0
  181. package/.agent-src/skills/api-testing/SKILL.md +206 -0
  182. package/.agent-src/skills/artisan-commands/SKILL.md +78 -0
  183. package/.agent-src/skills/authz-review/SKILL.md +171 -0
  184. package/.agent-src/skills/aws-infrastructure/SKILL.md +152 -0
  185. package/.agent-src/skills/blade-ui/SKILL.md +75 -0
  186. package/.agent-src/skills/blast-radius-analyzer/SKILL.md +185 -0
  187. package/.agent-src/skills/bug-analyzer/SKILL.md +256 -0
  188. package/.agent-src/skills/check-refs/SKILL.md +72 -0
  189. package/.agent-src/skills/code-refactoring/SKILL.md +200 -0
  190. package/.agent-src/skills/code-review/SKILL.md +214 -0
  191. package/.agent-src/skills/command-routing/SKILL.md +96 -0
  192. package/.agent-src/skills/command-writing/SKILL.md +143 -0
  193. package/.agent-src/skills/composer-packages/SKILL.md +172 -0
  194. package/.agent-src/skills/context-authoring/SKILL.md +157 -0
  195. package/.agent-src/skills/context-document/SKILL.md +153 -0
  196. package/.agent-src/skills/conventional-commits-writing/SKILL.md +70 -0
  197. package/.agent-src/skills/copilot-agents-optimization/SKILL.md +220 -0
  198. package/.agent-src/skills/copilot-config/SKILL.md +203 -0
  199. package/.agent-src/skills/dashboard-design/SKILL.md +116 -0
  200. package/.agent-src/skills/data-flow-mapper/SKILL.md +160 -0
  201. package/.agent-src/skills/database/SKILL.md +91 -0
  202. package/.agent-src/skills/dependency-upgrade/SKILL.md +204 -0
  203. package/.agent-src/skills/description-assist/SKILL.md +169 -0
  204. package/.agent-src/skills/design-review/SKILL.md +228 -0
  205. package/.agent-src/skills/devcontainer/SKILL.md +121 -0
  206. package/.agent-src/skills/developer-like-execution/SKILL.md +276 -0
  207. package/.agent-src/skills/docker/SKILL.md +245 -0
  208. package/.agent-src/skills/dto-creator/SKILL.md +117 -0
  209. package/.agent-src/skills/eloquent/SKILL.md +92 -0
  210. package/.agent-src/skills/eloquent/evals/last-run.json +99 -0
  211. package/.agent-src/skills/eloquent/evals/triggers.json +16 -0
  212. package/.agent-src/skills/estimate-ticket/SKILL.md +186 -0
  213. package/.agent-src/skills/estimate-ticket/evals/output-schema.yml +20 -0
  214. package/.agent-src/skills/estimate-ticket/evals/triggers.json +18 -0
  215. package/.agent-src/skills/fe-design/SKILL.md +223 -0
  216. package/.agent-src/skills/feature-planning/SKILL.md +226 -0
  217. package/.agent-src/skills/file-editor/SKILL.md +129 -0
  218. package/.agent-src/skills/finishing-a-development-branch/SKILL.md +200 -0
  219. package/.agent-src/skills/flux/SKILL.md +64 -0
  220. package/.agent-src/skills/git-workflow/SKILL.md +102 -0
  221. package/.agent-src/skills/github-ci/SKILL.md +122 -0
  222. package/.agent-src/skills/grafana/SKILL.md +168 -0
  223. package/.agent-src/skills/guideline-writing/SKILL.md +147 -0
  224. package/.agent-src/skills/jira-integration/SKILL.md +182 -0
  225. package/.agent-src/skills/jobs-events/SKILL.md +87 -0
  226. package/.agent-src/skills/judge-bug-hunter/SKILL.md +157 -0
  227. package/.agent-src/skills/judge-code-quality/SKILL.md +158 -0
  228. package/.agent-src/skills/judge-security-auditor/SKILL.md +167 -0
  229. package/.agent-src/skills/judge-test-coverage/SKILL.md +154 -0
  230. package/.agent-src/skills/laravel/SKILL.md +195 -0
  231. package/.agent-src/skills/laravel-horizon/SKILL.md +169 -0
  232. package/.agent-src/skills/laravel-mail/SKILL.md +193 -0
  233. package/.agent-src/skills/laravel-middleware/SKILL.md +185 -0
  234. package/.agent-src/skills/laravel-notifications/SKILL.md +168 -0
  235. package/.agent-src/skills/laravel-pennant/SKILL.md +188 -0
  236. package/.agent-src/skills/laravel-pulse/SKILL.md +160 -0
  237. package/.agent-src/skills/laravel-reverb/SKILL.md +205 -0
  238. package/.agent-src/skills/laravel-scheduling/SKILL.md +167 -0
  239. package/.agent-src/skills/laravel-validation/SKILL.md +71 -0
  240. package/.agent-src/skills/learning-to-rule-or-skill/SKILL.md +249 -0
  241. package/.agent-src/skills/lint-skills/SKILL.md +72 -0
  242. package/.agent-src/skills/livewire/SKILL.md +79 -0
  243. package/.agent-src/skills/logging-monitoring/SKILL.md +100 -0
  244. package/.agent-src/skills/mcp/SKILL.md +193 -0
  245. package/.agent-src/skills/merge-conflicts/SKILL.md +158 -0
  246. package/.agent-src/skills/migration-creator/SKILL.md +160 -0
  247. package/.agent-src/skills/module-management/SKILL.md +154 -0
  248. package/.agent-src/skills/multi-tenancy/SKILL.md +129 -0
  249. package/.agent-src/skills/openapi/SKILL.md +154 -0
  250. package/.agent-src/skills/override-management/SKILL.md +186 -0
  251. package/.agent-src/skills/performance/SKILL.md +69 -0
  252. package/.agent-src/skills/performance-analysis/SKILL.md +118 -0
  253. package/.agent-src/skills/pest-testing/SKILL.md +321 -0
  254. package/.agent-src/skills/php-coder/SKILL.md +78 -0
  255. package/.agent-src/skills/php-coder/evals/triggers.json +16 -0
  256. package/.agent-src/skills/php-debugging/SKILL.md +184 -0
  257. package/.agent-src/skills/php-service/SKILL.md +96 -0
  258. package/.agent-src/skills/playwright-testing/SKILL.md +244 -0
  259. package/.agent-src/skills/project-analysis-core/SKILL.md +138 -0
  260. package/.agent-src/skills/project-analysis-hypothesis-driven/SKILL.md +130 -0
  261. package/.agent-src/skills/project-analysis-laravel/SKILL.md +119 -0
  262. package/.agent-src/skills/project-analysis-nextjs/SKILL.md +123 -0
  263. package/.agent-src/skills/project-analysis-node-express/SKILL.md +111 -0
  264. package/.agent-src/skills/project-analysis-react/SKILL.md +119 -0
  265. package/.agent-src/skills/project-analysis-symfony/SKILL.md +111 -0
  266. package/.agent-src/skills/project-analysis-zend-laminas/SKILL.md +108 -0
  267. package/.agent-src/skills/project-analyzer/SKILL.md +341 -0
  268. package/.agent-src/skills/project-docs/SKILL.md +137 -0
  269. package/.agent-src/skills/quality-tools/SKILL.md +411 -0
  270. package/.agent-src/skills/readme-reviewer/SKILL.md +187 -0
  271. package/.agent-src/skills/readme-writing/SKILL.md +142 -0
  272. package/.agent-src/skills/readme-writing-package/SKILL.md +185 -0
  273. package/.agent-src/skills/receiving-code-review/SKILL.md +190 -0
  274. package/.agent-src/skills/refine-ticket/SKILL.md +310 -0
  275. package/.agent-src/skills/refine-ticket/detection-map.yml +124 -0
  276. package/.agent-src/skills/refine-ticket/evals/output-schema.yml +16 -0
  277. package/.agent-src/skills/refine-ticket/evals/triggers.json +16 -0
  278. package/.agent-src/skills/requesting-code-review/SKILL.md +199 -0
  279. package/.agent-src/skills/review-routing/SKILL.md +195 -0
  280. package/.agent-src/skills/roadmap-management/SKILL.md +303 -0
  281. package/.agent-src/skills/rtk-output-filtering/SKILL.md +184 -0
  282. package/.agent-src/skills/rule-writing/SKILL.md +148 -0
  283. package/.agent-src/skills/security/SKILL.md +79 -0
  284. package/.agent-src/skills/security-audit/SKILL.md +123 -0
  285. package/.agent-src/skills/sentry-integration/SKILL.md +170 -0
  286. package/.agent-src/skills/sequential-thinking/SKILL.md +158 -0
  287. package/.agent-src/skills/skill-improvement-pipeline/SKILL.md +155 -0
  288. package/.agent-src/skills/skill-management/SKILL.md +121 -0
  289. package/.agent-src/skills/skill-reviewer/SKILL.md +218 -0
  290. package/.agent-src/skills/skill-writing/SKILL.md +291 -0
  291. package/.agent-src/skills/skill-writing/evals/triggers.json +16 -0
  292. package/.agent-src/skills/sql-writing/SKILL.md +74 -0
  293. package/.agent-src/skills/subagent-orchestration/SKILL.md +190 -0
  294. package/.agent-src/skills/systematic-debugging/SKILL.md +244 -0
  295. package/.agent-src/skills/technical-specification/SKILL.md +185 -0
  296. package/.agent-src/skills/terraform/SKILL.md +137 -0
  297. package/.agent-src/skills/terragrunt/SKILL.md +217 -0
  298. package/.agent-src/skills/test-driven-development/SKILL.md +252 -0
  299. package/.agent-src/skills/test-performance/SKILL.md +172 -0
  300. package/.agent-src/skills/threat-modeling/SKILL.md +189 -0
  301. package/.agent-src/skills/traefik/SKILL.md +319 -0
  302. package/.agent-src/skills/universal-project-analysis/SKILL.md +179 -0
  303. package/.agent-src/skills/upstream-contribute/SKILL.md +255 -0
  304. package/.agent-src/skills/using-git-worktrees/SKILL.md +148 -0
  305. package/.agent-src/skills/validate-feature-fit/SKILL.md +113 -0
  306. package/.agent-src/skills/verify-before-complete/SKILL.md +188 -0
  307. package/.agent-src/skills/websocket/SKILL.md +75 -0
  308. package/.agent-src/templates/AGENTS.md +146 -0
  309. package/.agent-src/templates/agent-settings.md +256 -0
  310. package/.agent-src/templates/agents/.gitattributes.fragment +16 -0
  311. package/.agent-src/templates/agents/agent-project-settings.example.yml +138 -0
  312. package/.agent-src/templates/agents/memory/architecture-decisions.example.yml +95 -0
  313. package/.agent-src/templates/agents/memory/domain-invariants.example.yml +80 -0
  314. package/.agent-src/templates/agents/memory/historical-patterns.example.yml +82 -0
  315. package/.agent-src/templates/agents/memory/incident-learnings.example.yml +113 -0
  316. package/.agent-src/templates/agents/memory/ownership.example.yml +75 -0
  317. package/.agent-src/templates/agents/memory/product-rules.example.yml +87 -0
  318. package/.agent-src/templates/agents/proposal.example.md +143 -0
  319. package/.agent-src/templates/command.md +84 -0
  320. package/.agent-src/templates/contexts/auth-model.md +59 -0
  321. package/.agent-src/templates/contexts/data-sensitivity.md +60 -0
  322. package/.agent-src/templates/contexts/deployment-order.md +72 -0
  323. package/.agent-src/templates/contexts/observability.md +64 -0
  324. package/.agent-src/templates/contexts/tenant-boundaries.md +68 -0
  325. package/.agent-src/templates/contexts.md +116 -0
  326. package/.agent-src/templates/copilot-instructions.md +115 -0
  327. package/.agent-src/templates/features.md +125 -0
  328. package/.agent-src/templates/github-workflows/memory-hygiene.yml +133 -0
  329. package/.agent-src/templates/github-workflows/pr-risk-review.yml +123 -0
  330. package/.agent-src/templates/github-workflows/proposal-drift.yml +118 -0
  331. package/.agent-src/templates/overrides/command.md +24 -0
  332. package/.agent-src/templates/overrides/guideline.md +21 -0
  333. package/.agent-src/templates/overrides/rule.md +19 -0
  334. package/.agent-src/templates/overrides/skill.md +24 -0
  335. package/.agent-src/templates/overrides/template.md +21 -0
  336. package/.agent-src/templates/persona.md +99 -0
  337. package/.agent-src/templates/roadmaps.md +109 -0
  338. package/.agent-src/templates/scripts/README.md +195 -0
  339. package/.agent-src/templates/scripts/check_memory.py +283 -0
  340. package/.agent-src/templates/scripts/check_memory_proposal.py +180 -0
  341. package/.agent-src/templates/scripts/historical-bug-patterns.example.yml +84 -0
  342. package/.agent-src/templates/scripts/implement_ticket/__init__.py +57 -0
  343. package/.agent-src/templates/scripts/implement_ticket/__main__.py +9 -0
  344. package/.agent-src/templates/scripts/implement_ticket/cli.py +171 -0
  345. package/.agent-src/templates/scripts/implement_ticket/delivery_state.py +130 -0
  346. package/.agent-src/templates/scripts/implement_ticket/dispatcher.py +134 -0
  347. package/.agent-src/templates/scripts/implement_ticket/persona_policy.py +85 -0
  348. package/.agent-src/templates/scripts/implement_ticket/steps/__init__.py +49 -0
  349. package/.agent-src/templates/scripts/implement_ticket/steps/analyze.py +98 -0
  350. package/.agent-src/templates/scripts/implement_ticket/steps/implement.py +145 -0
  351. package/.agent-src/templates/scripts/implement_ticket/steps/memory.py +136 -0
  352. package/.agent-src/templates/scripts/implement_ticket/steps/plan.py +175 -0
  353. package/.agent-src/templates/scripts/implement_ticket/steps/refine.py +140 -0
  354. package/.agent-src/templates/scripts/implement_ticket/steps/report.py +195 -0
  355. package/.agent-src/templates/scripts/implement_ticket/steps/test.py +180 -0
  356. package/.agent-src/templates/scripts/implement_ticket/steps/verify.py +170 -0
  357. package/.agent-src/templates/scripts/memory_hash.py +75 -0
  358. package/.agent-src/templates/scripts/memory_lookup.py +216 -0
  359. package/.agent-src/templates/scripts/memory_report.py +184 -0
  360. package/.agent-src/templates/scripts/memory_signal.py +167 -0
  361. package/.agent-src/templates/scripts/memory_status.py +156 -0
  362. package/.agent-src/templates/scripts/ownership-map.example.yml +87 -0
  363. package/.agent-src/templates/scripts/pr-risk-config.example.yml +76 -0
  364. package/.agent-src/templates/scripts/pr_review_routing.py +340 -0
  365. package/.agent-src/templates/scripts/pr_risk_review.py +211 -0
  366. package/.agent-src/templates/skill.md +136 -0
  367. package/.augment-plugin/marketplace.json +32 -0
  368. package/.augment-plugin/plugin.json +21 -0
  369. package/.claude-plugin/marketplace.json +119 -0
  370. package/AGENTS.md +121 -0
  371. package/CHANGELOG.md +279 -0
  372. package/CONTRIBUTING.md +176 -0
  373. package/LICENSE +21 -0
  374. package/README.md +357 -0
  375. package/bin/install.php +38 -0
  376. package/composer.json +29 -0
  377. package/config/agent-settings.template.yml +96 -0
  378. package/config/profiles/balanced.ini +10 -0
  379. package/config/profiles/full.ini +10 -0
  380. package/config/profiles/minimal.ini +10 -0
  381. package/docs/architecture.md +144 -0
  382. package/docs/customization.md +88 -0
  383. package/docs/development.md +171 -0
  384. package/docs/getting-started.md +130 -0
  385. package/docs/github-topics.md +84 -0
  386. package/docs/installation.md +376 -0
  387. package/docs/mcp.md +133 -0
  388. package/docs/quality.md +98 -0
  389. package/docs/skills-catalog.md +136 -0
  390. package/docs/troubleshooting.md +167 -0
  391. package/llms.txt +130 -0
  392. package/package.json +31 -0
  393. package/scripts/audit_skill_descriptions.py +168 -0
  394. package/scripts/check_compression.py +221 -0
  395. package/scripts/check_memory.py +341 -0
  396. package/scripts/check_memory_proposal.py +180 -0
  397. package/scripts/check_portability.py +320 -0
  398. package/scripts/check_proposal.py +269 -0
  399. package/scripts/check_references.py +400 -0
  400. package/scripts/ci_summary.py +131 -0
  401. package/scripts/compress.py +671 -0
  402. package/scripts/compress.sh +18 -0
  403. package/scripts/first-run.sh +109 -0
  404. package/scripts/generate_catalog.py +116 -0
  405. package/scripts/install +151 -0
  406. package/scripts/install-hooks.sh +29 -0
  407. package/scripts/install.py +487 -0
  408. package/scripts/install.sh +637 -0
  409. package/scripts/install_anthropic_key.sh +101 -0
  410. package/scripts/inventory_frontmatter.py +164 -0
  411. package/scripts/lint_marketplace.py +142 -0
  412. package/scripts/lint_regression.py +232 -0
  413. package/scripts/mcp_render.py +159 -0
  414. package/scripts/measure_patterns.py +376 -0
  415. package/scripts/memory_hash.py +75 -0
  416. package/scripts/memory_lookup.py +441 -0
  417. package/scripts/memory_report.py +336 -0
  418. package/scripts/memory_signal.py +210 -0
  419. package/scripts/memory_status.py +195 -0
  420. package/scripts/postinstall.sh +60 -0
  421. package/scripts/readme_linter.py +580 -0
  422. package/scripts/refine_ticket_detect.py +623 -0
  423. package/scripts/requirements-evals.txt +7 -0
  424. package/scripts/runtime_dispatcher.py +265 -0
  425. package/scripts/runtime_handler.py +148 -0
  426. package/scripts/runtime_registry.py +166 -0
  427. package/scripts/schemas/command.schema.json +32 -0
  428. package/scripts/schemas/persona.schema.json +42 -0
  429. package/scripts/schemas/rule.schema.json +28 -0
  430. package/scripts/schemas/skill.schema.json +73 -0
  431. package/scripts/setup.sh +230 -0
  432. package/scripts/setup_eval_venv.sh +58 -0
  433. package/scripts/skill_linter.py +2175 -0
  434. package/scripts/skill_trigger_eval.py +651 -0
  435. package/scripts/tool_registry.py +146 -0
  436. package/scripts/tools/__init__.py +1 -0
  437. package/scripts/tools/adapter_errors.py +63 -0
  438. package/scripts/tools/base_adapter.py +91 -0
  439. package/scripts/tools/github_adapter.py +128 -0
  440. package/scripts/tools/jira_adapter.py +115 -0
  441. package/scripts/update_counts.py +147 -0
  442. package/scripts/validate_frontmatter.py +424 -0
  443. package/templates/consumer-settings/README.md +46 -0
  444. package/templates/consumer-settings/augment-settings.json +12 -0
  445. package/templates/consumer-settings/claude-settings.json +9 -0
  446. package/templates/consumer-settings/copilot-settings.json +14 -0
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env bash
2
+ # Interactive Anthropic-API-key installer for scripts/skill_trigger_eval.py.
3
+ #
4
+ # Reads the key with `read -s` so it never echoes to the terminal and
5
+ # never lands in shell history or scrollback. Writes atomically to
6
+ # ~/.config/agent-config/anthropic.key with mode 0600.
7
+ #
8
+ # Contract — companion to scripts/skill_trigger_eval.py:
9
+ # - File path: $HOME/.config/agent-config/anthropic.key
10
+ # - File mode: 0600 (owner read/write only)
11
+ # - Key format: must start with `sk-ant-`
12
+ # - No --force, no --yes, no env-var bypass. Piped stdin is rejected.
13
+ #
14
+ # The runner re-checks all of the above at every live invocation and
15
+ # refuses to run if the file drifts from this contract.
16
+
17
+ set -euo pipefail
18
+
19
+ TARGET_DIR="${HOME}/.config/agent-config"
20
+ TARGET_FILE="${TARGET_DIR}/anthropic.key"
21
+
22
+ # ── controlling-terminal requirement ─────────────────────────────────────
23
+ # We read from /dev/tty directly (fd 3), not from stdin. This is the
24
+ # stricter and more portable contract:
25
+ # - works under `task`, `script`, `sudo`, anything that reattaches stdin
26
+ # - forces every character to come from the user's real keyboard, so a
27
+ # pipe or redirected file cannot smuggle the key into the process
28
+ # - exits cleanly if there is no controlling terminal at all (e.g. CI,
29
+ # cron, agent automation)
30
+ if ! exec 3</dev/tty 2>/dev/null; then
31
+ echo "❌ install_anthropic_key.sh requires a controlling terminal." >&2
32
+ echo " /dev/tty not available \u2014 refusing to run under automation." >&2
33
+ exit 2
34
+ fi
35
+
36
+ # ── overwrite confirmation ───────────────────────────────────────────────
37
+ if [[ -e "${TARGET_FILE}" ]]; then
38
+ echo "⚠️ ${TARGET_FILE} already exists."
39
+ printf "Overwrite? [type 'yes' to replace, anything else aborts]: "
40
+ read -r -u 3 answer
41
+ if [[ "${answer}" != "yes" ]]; then
42
+ echo "Aborted. Existing key untouched."
43
+ exit 0
44
+ fi
45
+ fi
46
+
47
+ # ── read key (no echo, no history) ───────────────────────────────────────
48
+ echo "Paste your Anthropic API key (input is hidden, no echo)."
49
+ echo "The key should start with 'sk-ant-'."
50
+ printf "Key: "
51
+ # -s = silent (no echo), read from fd 3 = /dev/tty, not stdin.
52
+ read -r -s -u 3 API_KEY
53
+ echo
54
+
55
+ if [[ -z "${API_KEY}" ]]; then
56
+ echo "❌ Empty input — no file written." >&2
57
+ exit 2
58
+ fi
59
+
60
+ if [[ "${API_KEY}" != sk-ant-* ]]; then
61
+ echo "❌ Input does not look like an Anthropic key (missing 'sk-ant-' prefix)." >&2
62
+ echo " No file written." >&2
63
+ exit 2
64
+ fi
65
+
66
+ # ── create config dir with 0700, atomic write with 0600 ──────────────────
67
+ mkdir -p "${TARGET_DIR}"
68
+ chmod 0700 "${TARGET_DIR}"
69
+
70
+ TMP_FILE="$(mktemp "${TARGET_DIR}/.anthropic.key.XXXXXX")"
71
+ cleanup() { rm -f "${TMP_FILE}"; }
72
+ trap cleanup EXIT
73
+
74
+ # chmod the tmpfile BEFORE writing the key, so there is no window where
75
+ # the key sits on disk with group/other-readable permissions.
76
+ chmod 0600 "${TMP_FILE}"
77
+ printf '%s\n' "${API_KEY}" > "${TMP_FILE}"
78
+ mv "${TMP_FILE}" "${TARGET_FILE}"
79
+ trap - EXIT
80
+
81
+ # Clear the variable and the `mv` positional argument — defence in depth
82
+ # against a crash handler that dumps the process environment.
83
+ API_KEY=""
84
+
85
+ # ── verify mode post-write (portable stat: BSD on macOS, GNU on Linux) ───
86
+ if ACTUAL_MODE=$(stat -f '%Lp' "${TARGET_FILE}" 2>/dev/null); then
87
+ : # macOS / BSD
88
+ else
89
+ ACTUAL_MODE=$(stat -c '%a' "${TARGET_FILE}")
90
+ fi
91
+
92
+ if [[ "${ACTUAL_MODE}" != "600" ]]; then
93
+ echo "❌ Permissions verification failed: ${TARGET_FILE} has mode ${ACTUAL_MODE}, expected 600." >&2
94
+ echo " Delete and reinstall: rm ${TARGET_FILE} && $0" >&2
95
+ exit 3
96
+ fi
97
+
98
+ echo "✅ Key installed: ${TARGET_FILE} (mode 0600)."
99
+ echo " Verify: ls -la ${TARGET_FILE}"
100
+ echo " Rotate: rerun this script (you'll be prompted to overwrite)."
101
+ echo " Remove: rm ${TARGET_FILE}"
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Inventory frontmatter keys across all agent artefacts.
4
+
5
+ Reads .agent-src.uncompressed/{skills,rules,commands,personas}, parses the
6
+ YAML frontmatter of every file, and prints per-type:
7
+
8
+ - total file count
9
+ - every key observed, with count and percentage
10
+ - sample values (up to 3) per key
11
+
12
+ Output is Markdown on stdout, intended to be captured into
13
+ `agents/docs/frontmatter-contract.md` as raw material for Phase 1 of the
14
+ frontmatter-schema roadmap.
15
+
16
+ Stdlib-only. No PyYAML — we do a simple line-based parse sufficient for
17
+ our frontmatter shapes (flat keys, inline lists, block lists, one nested
18
+ `execution:` block).
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import re
24
+ import sys
25
+ from collections import Counter, defaultdict
26
+ from pathlib import Path
27
+
28
+ ROOT = Path(__file__).resolve().parent.parent
29
+ SRC = ROOT / ".agent-src.uncompressed"
30
+
31
+ FRONTMATTER_RE = re.compile(r"^---\n(.*?)\n---\n", re.DOTALL)
32
+
33
+
34
+ def extract_frontmatter(text: str) -> str | None:
35
+ match = FRONTMATTER_RE.search(text)
36
+ return match.group(1) if match else None
37
+
38
+
39
+ def parse_frontmatter_keys(fm: str) -> dict[str, str]:
40
+ """Return a flat {key: raw_value_string} for a frontmatter block.
41
+
42
+ For nested blocks (e.g. `execution:`), the nested keys are flattened
43
+ with a dot notation: `execution.type`, `execution.handler`, etc.
44
+ Inline lists (`personas: [a, b]`) and block lists (`- a\n- b`) are
45
+ rendered as their raw value.
46
+ """
47
+ result: dict[str, str] = {}
48
+ lines = fm.splitlines()
49
+ i = 0
50
+ current_nested: str | None = None
51
+ current_list_key: str | None = None
52
+ list_buffer: list[str] = []
53
+
54
+ def flush_list() -> None:
55
+ nonlocal current_list_key, list_buffer
56
+ if current_list_key is not None:
57
+ result[current_list_key] = "[" + ", ".join(list_buffer) + "]"
58
+ current_list_key = None
59
+ list_buffer = []
60
+
61
+ while i < len(lines):
62
+ line = lines[i]
63
+ stripped = line.strip()
64
+
65
+ if not stripped or stripped.startswith("#"):
66
+ i += 1
67
+ continue
68
+
69
+ # Top-level key (no leading whitespace)
70
+ if line and not line[0].isspace():
71
+ flush_list()
72
+ current_nested = None
73
+ m = re.match(r"^([\w-]+):\s*(.*?)\s*$", line)
74
+ if m:
75
+ key, value = m.group(1), m.group(2)
76
+ if value == "" or value == "|":
77
+ # Could start a nested block OR a list. Look ahead.
78
+ nxt = lines[i + 1].strip() if i + 1 < len(lines) else ""
79
+ if nxt.startswith("- "):
80
+ current_list_key = key
81
+ else:
82
+ current_nested = key
83
+ result[key] = "{nested}"
84
+ else:
85
+ result[key] = value
86
+ # Nested (indented) key
87
+ elif current_nested is not None:
88
+ m = re.match(r"^\s+([\w-]+):\s*(.*?)\s*$", line)
89
+ if m:
90
+ key, value = m.group(1), m.group(2)
91
+ result[f"{current_nested}.{key}"] = value or "{nested}"
92
+ # Block list item
93
+ elif current_list_key is not None and stripped.startswith("- "):
94
+ list_buffer.append(stripped[2:].strip())
95
+
96
+ i += 1
97
+
98
+ flush_list()
99
+ return result
100
+
101
+
102
+ def gather_files(artefact_dir: Path, pattern: str) -> list[Path]:
103
+ if not artefact_dir.exists():
104
+ return []
105
+ files = [f for f in artefact_dir.rglob(pattern) if not f.is_symlink()]
106
+ return sorted(files)
107
+
108
+
109
+ def inventory_type(name: str, files: list[Path]) -> None:
110
+ total = len(files)
111
+ print(f"### {name} — {total} files\n")
112
+ if total == 0:
113
+ print("_(no files)_\n")
114
+ return
115
+
116
+ key_counts: Counter[str] = Counter()
117
+ key_value_counts: dict[str, Counter[str]] = defaultdict(Counter)
118
+
119
+ for f in files:
120
+ text = f.read_text(encoding="utf-8")
121
+ fm = extract_frontmatter(text)
122
+ if fm is None:
123
+ continue
124
+ parsed = parse_frontmatter_keys(fm)
125
+ for k, v in parsed.items():
126
+ key_counts[k] += 1
127
+ # Record distinct values for enum detection; truncate to keep
128
+ # the table readable and strip surrounding quotes.
129
+ normalized = v.strip('"').strip("'")[:80] if v else "{empty}"
130
+ key_value_counts[k][normalized] += 1
131
+
132
+ print("| key | count | % | status | distinct values (count) |")
133
+ print("|---|---:|---:|---|---|")
134
+ for key, count in sorted(key_counts.items(), key=lambda kv: (-kv[1], kv[0])):
135
+ pct = count * 100 // total
136
+ status = "required" if pct >= 95 else "optional"
137
+ values = key_value_counts[key]
138
+ if len(values) <= 8:
139
+ rendered = " · ".join(f"`{val}` ({n})" for val, n in values.most_common())
140
+ else:
141
+ top = values.most_common(5)
142
+ rendered = " · ".join(f"`{val}` ({n})" for val, n in top)
143
+ rendered += f" · … +{len(values) - 5} more"
144
+ print(f"| `{key}` | {count} | {pct}% | {status} | {rendered} |")
145
+ print()
146
+
147
+
148
+ def main() -> int:
149
+ print("# Frontmatter inventory (generated)\n")
150
+ print("Generated by `scripts/inventory_frontmatter.py`. Raw material for")
151
+ print("Phase 1 of the frontmatter-schema roadmap. Do not edit by hand.\n")
152
+
153
+ inventory_type("skills", gather_files(SRC / "skills", "SKILL.md"))
154
+ inventory_type("rules", gather_files(SRC / "rules", "*.md"))
155
+ inventory_type("commands", gather_files(SRC / "commands", "*.md"))
156
+ inventory_type("personas", [
157
+ f for f in gather_files(SRC / "personas", "*.md")
158
+ if f.name.lower() != "readme.md"
159
+ ])
160
+ return 0
161
+
162
+
163
+ if __name__ == "__main__":
164
+ sys.exit(main())
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Lint .claude-plugin/marketplace.json for the event4u/agent-config package.
4
+
5
+ Validates the Claude Code Plugin Marketplace manifest against the canonical
6
+ shape used by anthropics/skills:
7
+
8
+ - Required top-level fields: name, owner, metadata, plugins
9
+ - owner must have name + email
10
+ - metadata must have description + version
11
+ - metadata.version must match package.json (single source of truth)
12
+ - every plugins[].skills[] entry must exist on disk and carry a SKILL.md
13
+
14
+ Exit codes: 0 = clean, 1 = problems found, 3 = internal error.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import json
20
+ import sys
21
+ from pathlib import Path
22
+
23
+ ROOT = Path(".")
24
+ MARKETPLACE = ROOT / ".claude-plugin" / "marketplace.json"
25
+ PACKAGE_JSON = ROOT / "package.json"
26
+
27
+
28
+ def fail(errors: list[str]) -> int:
29
+ print("❌ marketplace.json has problems:")
30
+ for e in errors:
31
+ print(f" - {e}")
32
+ return 1
33
+
34
+
35
+ def require_key(obj: dict, key: str, where: str, errors: list[str]) -> bool:
36
+ if key not in obj:
37
+ errors.append(f"missing key `{key}` in {where}")
38
+ return False
39
+ return True
40
+
41
+
42
+ def main() -> int:
43
+ if not MARKETPLACE.exists():
44
+ print(f"❌ {MARKETPLACE} not found")
45
+ return 1
46
+
47
+ try:
48
+ data = json.loads(MARKETPLACE.read_text())
49
+ except json.JSONDecodeError as e:
50
+ print(f"❌ {MARKETPLACE} is not valid JSON: {e}")
51
+ return 1
52
+
53
+ errors: list[str] = []
54
+
55
+ # Top-level required fields
56
+ for k in ("name", "owner", "metadata", "plugins"):
57
+ require_key(data, k, "marketplace root", errors)
58
+
59
+ # Owner
60
+ owner = data.get("owner", {})
61
+ if isinstance(owner, dict):
62
+ for k in ("name", "email"):
63
+ require_key(owner, k, "owner", errors)
64
+ else:
65
+ errors.append("`owner` must be an object")
66
+
67
+ # Metadata + version sync
68
+ metadata = data.get("metadata", {})
69
+ if isinstance(metadata, dict):
70
+ for k in ("description", "version"):
71
+ require_key(metadata, k, "metadata", errors)
72
+ mp_version = metadata.get("version")
73
+ if mp_version and PACKAGE_JSON.exists():
74
+ pkg = json.loads(PACKAGE_JSON.read_text())
75
+ pkg_version = pkg.get("version")
76
+ if pkg_version and mp_version != pkg_version:
77
+ errors.append(
78
+ f"metadata.version `{mp_version}` does not match "
79
+ f"package.json version `{pkg_version}`"
80
+ )
81
+ else:
82
+ errors.append("`metadata` must be an object")
83
+
84
+ # Plugins
85
+ plugins = data.get("plugins", [])
86
+ if not isinstance(plugins, list) or not plugins:
87
+ errors.append("`plugins` must be a non-empty array")
88
+ plugins = []
89
+
90
+ for idx, plugin in enumerate(plugins):
91
+ where = f"plugins[{idx}]"
92
+ if not isinstance(plugin, dict):
93
+ errors.append(f"{where} must be an object")
94
+ continue
95
+ for k in ("name", "description", "source", "skills"):
96
+ require_key(plugin, k, where, errors)
97
+
98
+ skills = plugin.get("skills", [])
99
+ if not isinstance(skills, list):
100
+ errors.append(f"{where}.skills must be an array")
101
+ continue
102
+
103
+ seen: set[str] = set()
104
+ for s_idx, path in enumerate(skills):
105
+ entry = f"{where}.skills[{s_idx}]"
106
+ if not isinstance(path, str):
107
+ errors.append(f"{entry} must be a string")
108
+ continue
109
+ if path in seen:
110
+ errors.append(f"{entry} is a duplicate: `{path}`")
111
+ seen.add(path)
112
+
113
+ # Resolve path relative to repo root (strip leading "./" only,
114
+ # NOT every "." and "/" character)
115
+ rel = path.removeprefix("./")
116
+ skill_dir = ROOT / rel
117
+ if not skill_dir.exists():
118
+ errors.append(f"{entry} path does not exist: `{path}`")
119
+ continue
120
+ skill_md = skill_dir / "SKILL.md"
121
+ if not skill_md.exists():
122
+ errors.append(f"{entry} has no SKILL.md: `{path}`")
123
+
124
+ if errors:
125
+ return fail(errors)
126
+
127
+ plugin_count = len(plugins)
128
+ skill_count = sum(len(p.get("skills", [])) for p in plugins if isinstance(p, dict))
129
+ print(
130
+ f"✅ marketplace.json ({plugin_count} plugin"
131
+ f"{'s' if plugin_count != 1 else ''}, {skill_count} skills total)"
132
+ )
133
+ print(" No issues found.")
134
+ return 0
135
+
136
+
137
+ if __name__ == "__main__":
138
+ try:
139
+ sys.exit(main())
140
+ except Exception as exc: # pragma: no cover
141
+ print(f"❌ internal error: {exc}", file=sys.stderr)
142
+ sys.exit(3)
@@ -0,0 +1,232 @@
1
+ #!/usr/bin/env python3
2
+ """Detect lint regressions between the current branch and a baseline (default: main).
3
+
4
+ Runs skill_linter.py --all --format json on both the baseline and current branch,
5
+ then compares results to find:
6
+ - New failures (file did not fail before, fails now)
7
+ - Status downgrades (pass → warn, warn → fail, pass → fail)
8
+ - New issues (issue codes that appeared on existing files)
9
+ - Status upgrades (fail → warn, warn → pass, fail → pass) — shown as improvements
10
+
11
+ Usage:
12
+ python3 scripts/lint_regression.py [--baseline main] [--format text|json|markdown]
13
+
14
+ Requires: git, python3, scripts/skill_linter.py
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import argparse
20
+ import json
21
+ import subprocess
22
+ import sys
23
+ import tempfile
24
+ from pathlib import Path
25
+
26
+
27
+ def run_linter_json(ref: str | None, repo_root: Path) -> dict:
28
+ """Run the linter and return parsed JSON. If ref is None, run on working tree."""
29
+ env_cmd = []
30
+ if ref:
31
+ # Run linter at a specific git ref using git stash + checkout
32
+ # Instead, use git show to avoid checkout — run on working tree after git stash
33
+ pass
34
+
35
+ cmd = [sys.executable, str(repo_root / "scripts" / "skill_linter.py"), "--all", "--format", "json",
36
+ "--repo-root", str(repo_root)]
37
+
38
+ if ref:
39
+ # Strategy: create a temp worktree at the ref, run linter there, clean up
40
+ with tempfile.TemporaryDirectory(prefix="lint-baseline-") as tmpdir:
41
+ subprocess.run(
42
+ ["git", "-C", str(repo_root), "worktree", "add", "--detach", tmpdir, ref],
43
+ capture_output=True, check=True
44
+ )
45
+ try:
46
+ result = subprocess.run(
47
+ [sys.executable, str(repo_root / "scripts" / "skill_linter.py"),
48
+ "--all", "--format", "json", "--repo-root", tmpdir],
49
+ capture_output=True, text=True, cwd=tmpdir
50
+ )
51
+ return json.loads(result.stdout) if result.stdout.strip() else {"results": [], "summary": {}}
52
+ finally:
53
+ subprocess.run(
54
+ ["git", "-C", str(repo_root), "worktree", "remove", "--force", tmpdir],
55
+ capture_output=True
56
+ )
57
+ else:
58
+ result = subprocess.run(cmd, capture_output=True, text=True, cwd=str(repo_root))
59
+ return json.loads(result.stdout) if result.stdout.strip() else {"results": [], "summary": {}}
60
+
61
+
62
+ def build_status_map(data: dict) -> dict[str, dict]:
63
+ """Build {file: {status, issue_codes}} from linter JSON output."""
64
+ result = {}
65
+ for entry in data.get("results", []):
66
+ f = entry["file"]
67
+ codes = {i["code"] for i in entry.get("issues", [])}
68
+ result[f] = {"status": entry["status"], "codes": codes}
69
+ return result
70
+
71
+
72
+ STATUS_ORDER = {"pass": 0, "pass_with_warnings": 1, "fail": 2}
73
+
74
+
75
+ def compare(baseline: dict[str, dict], current: dict[str, dict]) -> dict:
76
+ """Compare baseline and current lint results."""
77
+ regressions = []
78
+ improvements = []
79
+ new_files = []
80
+
81
+ all_files = sorted(set(baseline.keys()) | set(current.keys()))
82
+
83
+ for f in all_files:
84
+ base = baseline.get(f)
85
+ curr = current.get(f)
86
+
87
+ if curr and not base:
88
+ if curr["status"] != "pass":
89
+ new_files.append({"file": f, "status": curr["status"], "codes": sorted(curr["codes"])})
90
+ continue
91
+
92
+ if base and not curr:
93
+ continue # File removed — not a regression
94
+
95
+ base_order = STATUS_ORDER.get(base["status"], 0)
96
+ curr_order = STATUS_ORDER.get(curr["status"], 0)
97
+
98
+ if curr_order > base_order:
99
+ new_codes = curr["codes"] - base["codes"]
100
+ regressions.append({
101
+ "file": f,
102
+ "was": base["status"],
103
+ "now": curr["status"],
104
+ "new_codes": sorted(new_codes),
105
+ })
106
+ elif curr_order < base_order:
107
+ removed_codes = base["codes"] - curr["codes"]
108
+ improvements.append({
109
+ "file": f,
110
+ "was": base["status"],
111
+ "now": curr["status"],
112
+ "removed_codes": sorted(removed_codes),
113
+ })
114
+
115
+ return {"regressions": regressions, "improvements": improvements, "new_files": new_files}
116
+
117
+
118
+ def format_text(delta: dict) -> str:
119
+ lines = ["=== Lint Regression Report ===", ""]
120
+
121
+ if not delta["regressions"] and not delta["new_files"]:
122
+ lines.append("✅ No regressions detected.")
123
+ else:
124
+ if delta["regressions"]:
125
+ lines.append(f"❌ {len(delta['regressions'])} regression(s):")
126
+ for r in delta["regressions"]:
127
+ codes = ", ".join(r["new_codes"]) if r["new_codes"] else "(same codes, stricter)"
128
+ lines.append(f" {r['file']}: {r['was']} → {r['now']} [{codes}]")
129
+ lines.append("")
130
+
131
+ if delta["new_files"]:
132
+ lines.append(f"⚠️ {len(delta['new_files'])} new file(s) with issues:")
133
+ for nf in delta["new_files"]:
134
+ lines.append(f" {nf['file']}: {nf['status']} [{', '.join(nf['codes'])}]")
135
+ lines.append("")
136
+
137
+ if delta["improvements"]:
138
+ lines.append(f"✅ {len(delta['improvements'])} improvement(s):")
139
+ for imp in delta["improvements"]:
140
+ lines.append(f" {imp['file']}: {imp['was']} → {imp['now']}")
141
+
142
+ return "\n".join(lines)
143
+
144
+
145
+ def format_markdown(delta: dict) -> str:
146
+ lines = ["## 📊 Lint Regression Report", ""]
147
+
148
+ if not delta["regressions"] and not delta["new_files"]:
149
+ lines.append("✅ No regressions detected.")
150
+ else:
151
+ if delta["regressions"]:
152
+ n = len(delta["regressions"])
153
+ lines.extend([
154
+ "<details>",
155
+ f"<summary>❌ {n} Regression{'s' if n != 1 else ''}</summary>",
156
+ "",
157
+ "| File | Was | Now | New Issues |",
158
+ "|---|---|---|---|",
159
+ ])
160
+ for r in delta["regressions"]:
161
+ codes = ", ".join(r["new_codes"]) if r["new_codes"] else "—"
162
+ lines.append(f"| `{r['file']}` | {r['was']} | {r['now']} | {codes} |")
163
+ lines.extend(["", "</details>", ""])
164
+
165
+ if delta["new_files"]:
166
+ n = len(delta["new_files"])
167
+ lines.extend([
168
+ "<details>",
169
+ f"<summary>⚠️ {n} New file{'s' if n != 1 else ''} with issues</summary>",
170
+ "",
171
+ "| File | Status | Issues |",
172
+ "|---|---|---|",
173
+ ])
174
+ for nf in delta["new_files"]:
175
+ lines.append(f"| `{nf['file']}` | {nf['status']} | {', '.join(nf['codes'])} |")
176
+ lines.extend(["", "</details>", ""])
177
+
178
+ if delta["improvements"]:
179
+ n = len(delta["improvements"])
180
+ lines.extend([
181
+ "<details>",
182
+ f"<summary>✅ {n} Improvement{'s' if n != 1 else ''}</summary>",
183
+ "",
184
+ "| File | Was | Now |",
185
+ "|---|---|---|",
186
+ ])
187
+ for imp in delta["improvements"]:
188
+ lines.append(f"| `{imp['file']}` | {imp['was']} | {imp['now']} |")
189
+ lines.extend(["", "</details>"])
190
+
191
+ return "\n".join(lines)
192
+
193
+
194
+ def main() -> int:
195
+ parser = argparse.ArgumentParser(description="Detect lint regressions between branches.")
196
+ parser.add_argument("--baseline", default="main", help="Git ref to compare against (default: main)")
197
+ parser.add_argument("--format", choices=["text", "json", "markdown"], default="text",
198
+ help="Output format (default: text)")
199
+ parser.add_argument("--repo-root", default=".", help="Repository root")
200
+ args = parser.parse_args()
201
+
202
+ root = Path(args.repo_root).resolve()
203
+
204
+ print(f"Collecting baseline ({args.baseline})...", file=sys.stderr)
205
+ try:
206
+ baseline_data = run_linter_json(args.baseline, root)
207
+ except subprocess.CalledProcessError:
208
+ print(f"Error: could not create worktree for '{args.baseline}'. "
209
+ f"Does the ref exist?", file=sys.stderr)
210
+ return 2
211
+
212
+ print("Collecting current branch...", file=sys.stderr)
213
+ current_data = run_linter_json(None, root)
214
+
215
+ baseline_map = build_status_map(baseline_data)
216
+ current_map = build_status_map(current_data)
217
+
218
+ delta = compare(baseline_map, current_map)
219
+
220
+ if args.format == "json":
221
+ print(json.dumps(delta, indent=2))
222
+ elif args.format == "markdown":
223
+ print(format_markdown(delta))
224
+ else:
225
+ print(format_text(delta))
226
+
227
+ # Exit 1 if regressions found
228
+ return 1 if delta["regressions"] or delta["new_files"] else 0
229
+
230
+
231
+ if __name__ == "__main__":
232
+ raise SystemExit(main())