@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.
- package/.agent-src/README.md +64 -0
- package/.agent-src/commands/agent-handoff.md +64 -0
- package/.agent-src/commands/agent-status.md +83 -0
- package/.agent-src/commands/agents-audit.md +243 -0
- package/.agent-src/commands/agents-cleanup.md +169 -0
- package/.agent-src/commands/agents-prepare.md +137 -0
- package/.agent-src/commands/analyze-reference-repo.md +191 -0
- package/.agent-src/commands/bug-fix.md +181 -0
- package/.agent-src/commands/bug-investigate.md +175 -0
- package/.agent-src/commands/commit.md +121 -0
- package/.agent-src/commands/compress.md +177 -0
- package/.agent-src/commands/config-agent-settings.md +126 -0
- package/.agent-src/commands/context-create.md +167 -0
- package/.agent-src/commands/context-refactor.md +170 -0
- package/.agent-src/commands/copilot-agents-init.md +150 -0
- package/.agent-src/commands/copilot-agents-optimize.md +251 -0
- package/.agent-src/commands/create-pr-description.md +112 -0
- package/.agent-src/commands/create-pr.md +76 -0
- package/.agent-src/commands/do-and-judge.md +114 -0
- package/.agent-src/commands/do-in-steps.md +84 -0
- package/.agent-src/commands/e2e-heal.md +98 -0
- package/.agent-src/commands/e2e-plan.md +85 -0
- package/.agent-src/commands/estimate-ticket.md +80 -0
- package/.agent-src/commands/feature-dev.md +111 -0
- package/.agent-src/commands/feature-explore.md +180 -0
- package/.agent-src/commands/feature-plan.md +288 -0
- package/.agent-src/commands/feature-refactor.md +181 -0
- package/.agent-src/commands/feature-roadmap.md +184 -0
- package/.agent-src/commands/fix-ci.md +48 -0
- package/.agent-src/commands/fix-portability.md +97 -0
- package/.agent-src/commands/fix-pr-bot-comments.md +146 -0
- package/.agent-src/commands/fix-pr-comments.md +58 -0
- package/.agent-src/commands/fix-pr-developer-comments.md +152 -0
- package/.agent-src/commands/fix-references.md +94 -0
- package/.agent-src/commands/fix-seeder.md +146 -0
- package/.agent-src/commands/implement-ticket.md +133 -0
- package/.agent-src/commands/jira-ticket.md +71 -0
- package/.agent-src/commands/judge.md +86 -0
- package/.agent-src/commands/memory-add.md +130 -0
- package/.agent-src/commands/memory-full.md +97 -0
- package/.agent-src/commands/memory-promote.md +144 -0
- package/.agent-src/commands/mode.md +121 -0
- package/.agent-src/commands/module-create.md +132 -0
- package/.agent-src/commands/module-explore.md +157 -0
- package/.agent-src/commands/optimize-agents.md +139 -0
- package/.agent-src/commands/optimize-augmentignore.md +262 -0
- package/.agent-src/commands/optimize-rtk-filters.md +120 -0
- package/.agent-src/commands/optimize-skills.md +121 -0
- package/.agent-src/commands/override-create.md +97 -0
- package/.agent-src/commands/override-manage.md +96 -0
- package/.agent-src/commands/package-reset.md +154 -0
- package/.agent-src/commands/package-test.md +154 -0
- package/.agent-src/commands/prepare-for-review.md +91 -0
- package/.agent-src/commands/project-analyze.md +300 -0
- package/.agent-src/commands/project-health.md +95 -0
- package/.agent-src/commands/propose-memory.md +108 -0
- package/.agent-src/commands/quality-fix.md +106 -0
- package/.agent-src/commands/refine-ticket.md +81 -0
- package/.agent-src/commands/review-changes.md +130 -0
- package/.agent-src/commands/review-routing.md +111 -0
- package/.agent-src/commands/roadmap-create.md +110 -0
- package/.agent-src/commands/roadmap-execute.md +68 -0
- package/.agent-src/commands/rule-compliance-audit.md +139 -0
- package/.agent-src/commands/tests-create.md +73 -0
- package/.agent-src/commands/tests-execute.md +58 -0
- package/.agent-src/commands/threat-model.md +115 -0
- package/.agent-src/commands/update-form-request-messages.md +189 -0
- package/.agent-src/commands/upstream-contribute.md +171 -0
- package/.agent-src/contexts/augment-infrastructure.md +181 -0
- package/.agent-src/contexts/documentation-hierarchy.md +142 -0
- package/.agent-src/contexts/model-recommendations.md +142 -0
- package/.agent-src/contexts/override-system.md +187 -0
- package/.agent-src/contexts/skills-and-commands.md +154 -0
- package/.agent-src/contexts/subagent-configuration.md +62 -0
- package/.agent-src/guidelines/agent-infra/agent-interaction-and-decision-quality.md +110 -0
- package/.agent-src/guidelines/agent-infra/break-glass-usage.md +113 -0
- package/.agent-src/guidelines/agent-infra/developer-judgment.md +82 -0
- package/.agent-src/guidelines/agent-infra/engineering-memory-data-format.md +117 -0
- package/.agent-src/guidelines/agent-infra/layered-settings.md +158 -0
- package/.agent-src/guidelines/agent-infra/memory-access.md +121 -0
- package/.agent-src/guidelines/agent-infra/naming.md +69 -0
- package/.agent-src/guidelines/agent-infra/output-patterns.md +117 -0
- package/.agent-src/guidelines/agent-infra/review-routing-data-format.md +144 -0
- package/.agent-src/guidelines/agent-infra/role-contracts.md +211 -0
- package/.agent-src/guidelines/agent-infra/role-mode-router.md +89 -0
- package/.agent-src/guidelines/agent-infra/runtime-layer.md +89 -0
- package/.agent-src/guidelines/agent-infra/self-improvement-pipeline.md +135 -0
- package/.agent-src/guidelines/agent-infra/size-and-scope.md +189 -0
- package/.agent-src/guidelines/agent-infra/tool-integration.md +73 -0
- package/.agent-src/guidelines/docs/readme-size-and-splitting.md +153 -0
- package/.agent-src/guidelines/e2e/playwright.md +363 -0
- package/.agent-src/guidelines/php/api-design.md +115 -0
- package/.agent-src/guidelines/php/artisan-commands.md +81 -0
- package/.agent-src/guidelines/php/blade-ui.md +78 -0
- package/.agent-src/guidelines/php/controllers.md +90 -0
- package/.agent-src/guidelines/php/database.md +111 -0
- package/.agent-src/guidelines/php/eloquent.md +208 -0
- package/.agent-src/guidelines/php/flux.md +80 -0
- package/.agent-src/guidelines/php/general.md +191 -0
- package/.agent-src/guidelines/php/git.md +96 -0
- package/.agent-src/guidelines/php/jobs.md +111 -0
- package/.agent-src/guidelines/php/livewire.md +71 -0
- package/.agent-src/guidelines/php/logging.md +79 -0
- package/.agent-src/guidelines/php/naming.md +89 -0
- package/.agent-src/guidelines/php/patterns/dependency-injection.md +57 -0
- package/.agent-src/guidelines/php/patterns/dtos.md +199 -0
- package/.agent-src/guidelines/php/patterns/events.md +67 -0
- package/.agent-src/guidelines/php/patterns/factory.md +53 -0
- package/.agent-src/guidelines/php/patterns/pipelines.md +66 -0
- package/.agent-src/guidelines/php/patterns/policies.md +66 -0
- package/.agent-src/guidelines/php/patterns/repositories.md +122 -0
- package/.agent-src/guidelines/php/patterns/service-layer.md +64 -0
- package/.agent-src/guidelines/php/patterns/strategy.md +69 -0
- package/.agent-src/guidelines/php/patterns.md +28 -0
- package/.agent-src/guidelines/php/performance.md +92 -0
- package/.agent-src/guidelines/php/resources.md +100 -0
- package/.agent-src/guidelines/php/security.md +110 -0
- package/.agent-src/guidelines/php/sql.md +97 -0
- package/.agent-src/guidelines/php/validations.md +119 -0
- package/.agent-src/guidelines/php/websocket.md +100 -0
- package/.agent-src/personas/README.md +104 -0
- package/.agent-src/personas/ai-agent.md +77 -0
- package/.agent-src/personas/critical-challenger.md +73 -0
- package/.agent-src/personas/developer.md +73 -0
- package/.agent-src/personas/product-owner.md +78 -0
- package/.agent-src/personas/qa.md +67 -0
- package/.agent-src/personas/senior-engineer.md +77 -0
- package/.agent-src/personas/stakeholder.md +78 -0
- package/.agent-src/rules/agent-docs.md +61 -0
- package/.agent-src/rules/analysis-skill-routing.md +48 -0
- package/.agent-src/rules/architecture.md +62 -0
- package/.agent-src/rules/artifact-drafting-protocol.md +73 -0
- package/.agent-src/rules/ask-when-uncertain.md +52 -0
- package/.agent-src/rules/augment-portability.md +38 -0
- package/.agent-src/rules/augment-source-of-truth.md +128 -0
- package/.agent-src/rules/capture-learnings.md +89 -0
- package/.agent-src/rules/cli-output-handling.md +94 -0
- package/.agent-src/rules/commit-conventions.md +64 -0
- package/.agent-src/rules/context-hygiene.md +90 -0
- package/.agent-src/rules/docker-commands.md +55 -0
- package/.agent-src/rules/docs-sync.md +79 -0
- package/.agent-src/rules/downstream-changes.md +70 -0
- package/.agent-src/rules/e2e-testing.md +53 -0
- package/.agent-src/rules/guidelines.md +90 -0
- package/.agent-src/rules/improve-before-implement.md +94 -0
- package/.agent-src/rules/language-and-tone.md +104 -0
- package/.agent-src/rules/laravel-translations.md +48 -0
- package/.agent-src/rules/markdown-safe-codeblocks.md +18 -0
- package/.agent-src/rules/minimal-safe-diff.md +87 -0
- package/.agent-src/rules/missing-tool-handling.md +62 -0
- package/.agent-src/rules/model-recommendation.md +70 -0
- package/.agent-src/rules/package-ci-checks.md +80 -0
- package/.agent-src/rules/php-coding.md +63 -0
- package/.agent-src/rules/preservation-guard.md +29 -0
- package/.agent-src/rules/review-routing-awareness.md +125 -0
- package/.agent-src/rules/reviewer-awareness.md +92 -0
- package/.agent-src/rules/roadmap-progress-sync.md +56 -0
- package/.agent-src/rules/role-mode-adherence.md +54 -0
- package/.agent-src/rules/rule-type-governance.md +46 -0
- package/.agent-src/rules/runtime-safety.md +42 -0
- package/.agent-src/rules/scope-control.md +40 -0
- package/.agent-src/rules/security-sensitive-stop.md +77 -0
- package/.agent-src/rules/size-enforcement.md +29 -0
- package/.agent-src/rules/skill-improvement-trigger.md +58 -0
- package/.agent-src/rules/skill-quality.md +110 -0
- package/.agent-src/rules/slash-commands.md +30 -0
- package/.agent-src/rules/think-before-action.md +91 -0
- package/.agent-src/rules/token-efficiency.md +99 -0
- package/.agent-src/rules/tool-safety.md +36 -0
- package/.agent-src/rules/upstream-proposal.md +76 -0
- package/.agent-src/rules/user-interaction.md +79 -0
- package/.agent-src/rules/verify-before-complete.md +120 -0
- package/.agent-src/scripts/scan-seeder-violations.php +145 -0
- package/.agent-src/scripts/update_roadmap_progress.py +244 -0
- package/.agent-src/skills/adversarial-review/SKILL.md +149 -0
- package/.agent-src/skills/agent-docs-writing/SKILL.md +234 -0
- package/.agent-src/skills/analysis-autonomous-mode/SKILL.md +197 -0
- package/.agent-src/skills/analysis-skill-router/SKILL.md +134 -0
- package/.agent-src/skills/api-design/SKILL.md +104 -0
- package/.agent-src/skills/api-endpoint/SKILL.md +185 -0
- package/.agent-src/skills/api-testing/SKILL.md +206 -0
- package/.agent-src/skills/artisan-commands/SKILL.md +78 -0
- package/.agent-src/skills/authz-review/SKILL.md +171 -0
- package/.agent-src/skills/aws-infrastructure/SKILL.md +152 -0
- package/.agent-src/skills/blade-ui/SKILL.md +75 -0
- package/.agent-src/skills/blast-radius-analyzer/SKILL.md +185 -0
- package/.agent-src/skills/bug-analyzer/SKILL.md +256 -0
- package/.agent-src/skills/check-refs/SKILL.md +72 -0
- package/.agent-src/skills/code-refactoring/SKILL.md +200 -0
- package/.agent-src/skills/code-review/SKILL.md +214 -0
- package/.agent-src/skills/command-routing/SKILL.md +96 -0
- package/.agent-src/skills/command-writing/SKILL.md +143 -0
- package/.agent-src/skills/composer-packages/SKILL.md +172 -0
- package/.agent-src/skills/context-authoring/SKILL.md +157 -0
- package/.agent-src/skills/context-document/SKILL.md +153 -0
- package/.agent-src/skills/conventional-commits-writing/SKILL.md +70 -0
- package/.agent-src/skills/copilot-agents-optimization/SKILL.md +220 -0
- package/.agent-src/skills/copilot-config/SKILL.md +203 -0
- package/.agent-src/skills/dashboard-design/SKILL.md +116 -0
- package/.agent-src/skills/data-flow-mapper/SKILL.md +160 -0
- package/.agent-src/skills/database/SKILL.md +91 -0
- package/.agent-src/skills/dependency-upgrade/SKILL.md +204 -0
- package/.agent-src/skills/description-assist/SKILL.md +169 -0
- package/.agent-src/skills/design-review/SKILL.md +228 -0
- package/.agent-src/skills/devcontainer/SKILL.md +121 -0
- package/.agent-src/skills/developer-like-execution/SKILL.md +276 -0
- package/.agent-src/skills/docker/SKILL.md +245 -0
- package/.agent-src/skills/dto-creator/SKILL.md +117 -0
- package/.agent-src/skills/eloquent/SKILL.md +92 -0
- package/.agent-src/skills/eloquent/evals/last-run.json +99 -0
- package/.agent-src/skills/eloquent/evals/triggers.json +16 -0
- package/.agent-src/skills/estimate-ticket/SKILL.md +186 -0
- package/.agent-src/skills/estimate-ticket/evals/output-schema.yml +20 -0
- package/.agent-src/skills/estimate-ticket/evals/triggers.json +18 -0
- package/.agent-src/skills/fe-design/SKILL.md +223 -0
- package/.agent-src/skills/feature-planning/SKILL.md +226 -0
- package/.agent-src/skills/file-editor/SKILL.md +129 -0
- package/.agent-src/skills/finishing-a-development-branch/SKILL.md +200 -0
- package/.agent-src/skills/flux/SKILL.md +64 -0
- package/.agent-src/skills/git-workflow/SKILL.md +102 -0
- package/.agent-src/skills/github-ci/SKILL.md +122 -0
- package/.agent-src/skills/grafana/SKILL.md +168 -0
- package/.agent-src/skills/guideline-writing/SKILL.md +147 -0
- package/.agent-src/skills/jira-integration/SKILL.md +182 -0
- package/.agent-src/skills/jobs-events/SKILL.md +87 -0
- package/.agent-src/skills/judge-bug-hunter/SKILL.md +157 -0
- package/.agent-src/skills/judge-code-quality/SKILL.md +158 -0
- package/.agent-src/skills/judge-security-auditor/SKILL.md +167 -0
- package/.agent-src/skills/judge-test-coverage/SKILL.md +154 -0
- package/.agent-src/skills/laravel/SKILL.md +195 -0
- package/.agent-src/skills/laravel-horizon/SKILL.md +169 -0
- package/.agent-src/skills/laravel-mail/SKILL.md +193 -0
- package/.agent-src/skills/laravel-middleware/SKILL.md +185 -0
- package/.agent-src/skills/laravel-notifications/SKILL.md +168 -0
- package/.agent-src/skills/laravel-pennant/SKILL.md +188 -0
- package/.agent-src/skills/laravel-pulse/SKILL.md +160 -0
- package/.agent-src/skills/laravel-reverb/SKILL.md +205 -0
- package/.agent-src/skills/laravel-scheduling/SKILL.md +167 -0
- package/.agent-src/skills/laravel-validation/SKILL.md +71 -0
- package/.agent-src/skills/learning-to-rule-or-skill/SKILL.md +249 -0
- package/.agent-src/skills/lint-skills/SKILL.md +72 -0
- package/.agent-src/skills/livewire/SKILL.md +79 -0
- package/.agent-src/skills/logging-monitoring/SKILL.md +100 -0
- package/.agent-src/skills/mcp/SKILL.md +193 -0
- package/.agent-src/skills/merge-conflicts/SKILL.md +158 -0
- package/.agent-src/skills/migration-creator/SKILL.md +160 -0
- package/.agent-src/skills/module-management/SKILL.md +154 -0
- package/.agent-src/skills/multi-tenancy/SKILL.md +129 -0
- package/.agent-src/skills/openapi/SKILL.md +154 -0
- package/.agent-src/skills/override-management/SKILL.md +186 -0
- package/.agent-src/skills/performance/SKILL.md +69 -0
- package/.agent-src/skills/performance-analysis/SKILL.md +118 -0
- package/.agent-src/skills/pest-testing/SKILL.md +321 -0
- package/.agent-src/skills/php-coder/SKILL.md +78 -0
- package/.agent-src/skills/php-coder/evals/triggers.json +16 -0
- package/.agent-src/skills/php-debugging/SKILL.md +184 -0
- package/.agent-src/skills/php-service/SKILL.md +96 -0
- package/.agent-src/skills/playwright-testing/SKILL.md +244 -0
- package/.agent-src/skills/project-analysis-core/SKILL.md +138 -0
- package/.agent-src/skills/project-analysis-hypothesis-driven/SKILL.md +130 -0
- package/.agent-src/skills/project-analysis-laravel/SKILL.md +119 -0
- package/.agent-src/skills/project-analysis-nextjs/SKILL.md +123 -0
- package/.agent-src/skills/project-analysis-node-express/SKILL.md +111 -0
- package/.agent-src/skills/project-analysis-react/SKILL.md +119 -0
- package/.agent-src/skills/project-analysis-symfony/SKILL.md +111 -0
- package/.agent-src/skills/project-analysis-zend-laminas/SKILL.md +108 -0
- package/.agent-src/skills/project-analyzer/SKILL.md +341 -0
- package/.agent-src/skills/project-docs/SKILL.md +137 -0
- package/.agent-src/skills/quality-tools/SKILL.md +411 -0
- package/.agent-src/skills/readme-reviewer/SKILL.md +187 -0
- package/.agent-src/skills/readme-writing/SKILL.md +142 -0
- package/.agent-src/skills/readme-writing-package/SKILL.md +185 -0
- package/.agent-src/skills/receiving-code-review/SKILL.md +190 -0
- package/.agent-src/skills/refine-ticket/SKILL.md +310 -0
- package/.agent-src/skills/refine-ticket/detection-map.yml +124 -0
- package/.agent-src/skills/refine-ticket/evals/output-schema.yml +16 -0
- package/.agent-src/skills/refine-ticket/evals/triggers.json +16 -0
- package/.agent-src/skills/requesting-code-review/SKILL.md +199 -0
- package/.agent-src/skills/review-routing/SKILL.md +195 -0
- package/.agent-src/skills/roadmap-management/SKILL.md +303 -0
- package/.agent-src/skills/rtk-output-filtering/SKILL.md +184 -0
- package/.agent-src/skills/rule-writing/SKILL.md +148 -0
- package/.agent-src/skills/security/SKILL.md +79 -0
- package/.agent-src/skills/security-audit/SKILL.md +123 -0
- package/.agent-src/skills/sentry-integration/SKILL.md +170 -0
- package/.agent-src/skills/sequential-thinking/SKILL.md +158 -0
- package/.agent-src/skills/skill-improvement-pipeline/SKILL.md +155 -0
- package/.agent-src/skills/skill-management/SKILL.md +121 -0
- package/.agent-src/skills/skill-reviewer/SKILL.md +218 -0
- package/.agent-src/skills/skill-writing/SKILL.md +291 -0
- package/.agent-src/skills/skill-writing/evals/triggers.json +16 -0
- package/.agent-src/skills/sql-writing/SKILL.md +74 -0
- package/.agent-src/skills/subagent-orchestration/SKILL.md +190 -0
- package/.agent-src/skills/systematic-debugging/SKILL.md +244 -0
- package/.agent-src/skills/technical-specification/SKILL.md +185 -0
- package/.agent-src/skills/terraform/SKILL.md +137 -0
- package/.agent-src/skills/terragrunt/SKILL.md +217 -0
- package/.agent-src/skills/test-driven-development/SKILL.md +252 -0
- package/.agent-src/skills/test-performance/SKILL.md +172 -0
- package/.agent-src/skills/threat-modeling/SKILL.md +189 -0
- package/.agent-src/skills/traefik/SKILL.md +319 -0
- package/.agent-src/skills/universal-project-analysis/SKILL.md +179 -0
- package/.agent-src/skills/upstream-contribute/SKILL.md +255 -0
- package/.agent-src/skills/using-git-worktrees/SKILL.md +148 -0
- package/.agent-src/skills/validate-feature-fit/SKILL.md +113 -0
- package/.agent-src/skills/verify-before-complete/SKILL.md +188 -0
- package/.agent-src/skills/websocket/SKILL.md +75 -0
- package/.agent-src/templates/AGENTS.md +146 -0
- package/.agent-src/templates/agent-settings.md +256 -0
- package/.agent-src/templates/agents/.gitattributes.fragment +16 -0
- package/.agent-src/templates/agents/agent-project-settings.example.yml +138 -0
- package/.agent-src/templates/agents/memory/architecture-decisions.example.yml +95 -0
- package/.agent-src/templates/agents/memory/domain-invariants.example.yml +80 -0
- package/.agent-src/templates/agents/memory/historical-patterns.example.yml +82 -0
- package/.agent-src/templates/agents/memory/incident-learnings.example.yml +113 -0
- package/.agent-src/templates/agents/memory/ownership.example.yml +75 -0
- package/.agent-src/templates/agents/memory/product-rules.example.yml +87 -0
- package/.agent-src/templates/agents/proposal.example.md +143 -0
- package/.agent-src/templates/command.md +84 -0
- package/.agent-src/templates/contexts/auth-model.md +59 -0
- package/.agent-src/templates/contexts/data-sensitivity.md +60 -0
- package/.agent-src/templates/contexts/deployment-order.md +72 -0
- package/.agent-src/templates/contexts/observability.md +64 -0
- package/.agent-src/templates/contexts/tenant-boundaries.md +68 -0
- package/.agent-src/templates/contexts.md +116 -0
- package/.agent-src/templates/copilot-instructions.md +115 -0
- package/.agent-src/templates/features.md +125 -0
- package/.agent-src/templates/github-workflows/memory-hygiene.yml +133 -0
- package/.agent-src/templates/github-workflows/pr-risk-review.yml +123 -0
- package/.agent-src/templates/github-workflows/proposal-drift.yml +118 -0
- package/.agent-src/templates/overrides/command.md +24 -0
- package/.agent-src/templates/overrides/guideline.md +21 -0
- package/.agent-src/templates/overrides/rule.md +19 -0
- package/.agent-src/templates/overrides/skill.md +24 -0
- package/.agent-src/templates/overrides/template.md +21 -0
- package/.agent-src/templates/persona.md +99 -0
- package/.agent-src/templates/roadmaps.md +109 -0
- package/.agent-src/templates/scripts/README.md +195 -0
- package/.agent-src/templates/scripts/check_memory.py +283 -0
- package/.agent-src/templates/scripts/check_memory_proposal.py +180 -0
- package/.agent-src/templates/scripts/historical-bug-patterns.example.yml +84 -0
- package/.agent-src/templates/scripts/implement_ticket/__init__.py +57 -0
- package/.agent-src/templates/scripts/implement_ticket/__main__.py +9 -0
- package/.agent-src/templates/scripts/implement_ticket/cli.py +171 -0
- package/.agent-src/templates/scripts/implement_ticket/delivery_state.py +130 -0
- package/.agent-src/templates/scripts/implement_ticket/dispatcher.py +134 -0
- package/.agent-src/templates/scripts/implement_ticket/persona_policy.py +85 -0
- package/.agent-src/templates/scripts/implement_ticket/steps/__init__.py +49 -0
- package/.agent-src/templates/scripts/implement_ticket/steps/analyze.py +98 -0
- package/.agent-src/templates/scripts/implement_ticket/steps/implement.py +145 -0
- package/.agent-src/templates/scripts/implement_ticket/steps/memory.py +136 -0
- package/.agent-src/templates/scripts/implement_ticket/steps/plan.py +175 -0
- package/.agent-src/templates/scripts/implement_ticket/steps/refine.py +140 -0
- package/.agent-src/templates/scripts/implement_ticket/steps/report.py +195 -0
- package/.agent-src/templates/scripts/implement_ticket/steps/test.py +180 -0
- package/.agent-src/templates/scripts/implement_ticket/steps/verify.py +170 -0
- package/.agent-src/templates/scripts/memory_hash.py +75 -0
- package/.agent-src/templates/scripts/memory_lookup.py +216 -0
- package/.agent-src/templates/scripts/memory_report.py +184 -0
- package/.agent-src/templates/scripts/memory_signal.py +167 -0
- package/.agent-src/templates/scripts/memory_status.py +156 -0
- package/.agent-src/templates/scripts/ownership-map.example.yml +87 -0
- package/.agent-src/templates/scripts/pr-risk-config.example.yml +76 -0
- package/.agent-src/templates/scripts/pr_review_routing.py +340 -0
- package/.agent-src/templates/scripts/pr_risk_review.py +211 -0
- package/.agent-src/templates/skill.md +136 -0
- package/.augment-plugin/marketplace.json +32 -0
- package/.augment-plugin/plugin.json +21 -0
- package/.claude-plugin/marketplace.json +119 -0
- package/AGENTS.md +121 -0
- package/CHANGELOG.md +279 -0
- package/CONTRIBUTING.md +176 -0
- package/LICENSE +21 -0
- package/README.md +357 -0
- package/bin/install.php +38 -0
- package/composer.json +29 -0
- package/config/agent-settings.template.yml +96 -0
- package/config/profiles/balanced.ini +10 -0
- package/config/profiles/full.ini +10 -0
- package/config/profiles/minimal.ini +10 -0
- package/docs/architecture.md +144 -0
- package/docs/customization.md +88 -0
- package/docs/development.md +171 -0
- package/docs/getting-started.md +130 -0
- package/docs/github-topics.md +84 -0
- package/docs/installation.md +376 -0
- package/docs/mcp.md +133 -0
- package/docs/quality.md +98 -0
- package/docs/skills-catalog.md +136 -0
- package/docs/troubleshooting.md +167 -0
- package/llms.txt +130 -0
- package/package.json +31 -0
- package/scripts/audit_skill_descriptions.py +168 -0
- package/scripts/check_compression.py +221 -0
- package/scripts/check_memory.py +341 -0
- package/scripts/check_memory_proposal.py +180 -0
- package/scripts/check_portability.py +320 -0
- package/scripts/check_proposal.py +269 -0
- package/scripts/check_references.py +400 -0
- package/scripts/ci_summary.py +131 -0
- package/scripts/compress.py +671 -0
- package/scripts/compress.sh +18 -0
- package/scripts/first-run.sh +109 -0
- package/scripts/generate_catalog.py +116 -0
- package/scripts/install +151 -0
- package/scripts/install-hooks.sh +29 -0
- package/scripts/install.py +487 -0
- package/scripts/install.sh +637 -0
- package/scripts/install_anthropic_key.sh +101 -0
- package/scripts/inventory_frontmatter.py +164 -0
- package/scripts/lint_marketplace.py +142 -0
- package/scripts/lint_regression.py +232 -0
- package/scripts/mcp_render.py +159 -0
- package/scripts/measure_patterns.py +376 -0
- package/scripts/memory_hash.py +75 -0
- package/scripts/memory_lookup.py +441 -0
- package/scripts/memory_report.py +336 -0
- package/scripts/memory_signal.py +210 -0
- package/scripts/memory_status.py +195 -0
- package/scripts/postinstall.sh +60 -0
- package/scripts/readme_linter.py +580 -0
- package/scripts/refine_ticket_detect.py +623 -0
- package/scripts/requirements-evals.txt +7 -0
- package/scripts/runtime_dispatcher.py +265 -0
- package/scripts/runtime_handler.py +148 -0
- package/scripts/runtime_registry.py +166 -0
- package/scripts/schemas/command.schema.json +32 -0
- package/scripts/schemas/persona.schema.json +42 -0
- package/scripts/schemas/rule.schema.json +28 -0
- package/scripts/schemas/skill.schema.json +73 -0
- package/scripts/setup.sh +230 -0
- package/scripts/setup_eval_venv.sh +58 -0
- package/scripts/skill_linter.py +2175 -0
- package/scripts/skill_trigger_eval.py +651 -0
- package/scripts/tool_registry.py +146 -0
- package/scripts/tools/__init__.py +1 -0
- package/scripts/tools/adapter_errors.py +63 -0
- package/scripts/tools/base_adapter.py +91 -0
- package/scripts/tools/github_adapter.py +128 -0
- package/scripts/tools/jira_adapter.py +115 -0
- package/scripts/update_counts.py +147 -0
- package/scripts/validate_frontmatter.py +424 -0
- package/templates/consumer-settings/README.md +46 -0
- package/templates/consumer-settings/augment-settings.json +12 -0
- package/templates/consumer-settings/claude-settings.json +9 -0
- 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())
|