Turn local Drupal fixes into effortless, maintainer-friendly contributions.
AI scales code generation, but it shouldn't scale noise.
We want to empower AI Agents to fix Drupal bugs, while protecting maintainers from a flood of duplicate or low-quality contributions. This tool bridges the gap, transforming the Agent from a "local hacker" into a responsible open-source contributor.
To ensure contributions are helpful rather than overwhelming, this skill enforces three strict rules:
- Targeted: Searches Drupal.org first. If a fix exists, we stop and recommend using it. No duplicate effort.
- High-Quality: Runs PHP lint by default; runs PHPCS if available; flags hack patterns.
- Maintainer-First: Never auto-posts. Generates clean artifacts for you to review.
- Why Use This?
- What This Will NOT Do
- How It Works
- What You Get
- Quick Start
- Workflow Modes
- Options
- Exit Codes (Gatekeeper Behavior)
- For AI Agent Developers
Stop your AI from "hacking" core.
| β Without this Skill | β
With drupal-contribute-fix |
|---|---|
| Duplicate Work: Agent ignores existing MRs/patches. | Upstream-aware: Searches Drupal.org and surfaces existing fixes. |
Tech Debt: Fixes are buried in vendor/ or core/. |
Standardized: Generates patches in patches/. |
| Maintainer Burnout: Spammy, low-quality issues. | Maintainer-friendly: Warns on risky patterns and keeps human review in the loop. |
Lost Fixes: Local patches vanish on composer update. |
Preserved Work: Artifacts are saved for future rerolls. |
| No Validation: Errors slip through. | Quality Gates: Runs PHP lint and PHPCS (if available). |
This skill is designed to be helpful without creating churn.
| π« Never | β Instead |
|---|---|
| Auto-post to Drupal.org | Generates files for you to review and paste |
| Create issues automatically | Searches existing issues; you decide what to file |
| Push to git.drupalcode.org | Outputs local patch files only |
| Bypass your review | Every artifact requires human approval before submission |
| Spam maintainers | Stops when existing MR/patch found; encourages testing over duplicating |
This tool never auto-posts to Drupal.org or pushes code automatically. It only generates local artifacts for you to review and submit.
The --force flag should be rare. Use it only when:
- You've reviewed the existing MR/patch and confirmed your fix is meaningfully different
- You're providing a reroll for a different version
- The existing fix doesn't apply to your Drupal version
When using --force, always explain in your issue comment why a new patch was needed.
graph TD
A[π Local Fix Detected] --> B{Search Drupal.org}
B -- MR/Patch Exists --> C[π STOP & DOWNLOAD]
C --> D[Test Existing Fix]
B -- No Fix Found --> E{Check Dev Branch}
E -- Fixed in Dev --> F[π STOP & UPGRADE]
E -- Bug Exists in Dev --> H{Hack Detection}
H -- Clean Fix --> I[β
GENERATE PATCH]
H -- Hacky Fix --> K[β οΈ WARN USER]
Note: Dev-branch checks are currently a manual step; the tool does not auto-verify them yet.
.drupal-contribute-fix/
βββ UPSTREAM_CANDIDATES.json # Search results cache (shared)
βββ 3345678-fix-metatag-build/ # Known issue with slug
β βββ REPORT.md # Analysis & next steps
β βββ ISSUE_COMMENT.md # Copy/paste this to drupal.org
β βββ patches/
β βββ metatag-fix-3345678.patch # Upload this to the issue
βββ unfiled-update-module-check/ # New issue needed
βββ REPORT.md
βββ ISSUE_COMMENT.md
βββ patches/
βββ project-fix-new.patch
Directory naming: {nid}-{slug}/ for existing issues, unfiled-{slug}/ for new issues.
- Python 3.8+
- Git
- Internet access to drupal.org API
# Clone from your preferred location
git clone <repository-url>
cd drupal-contribute-fixSearch only (preflight):
python3 scripts/contribute_fix.py preflight \
--project metatag \
--keywords "TypeError MetatagManager" \
--out .drupal-contribute-fixSearch + generate patch:
python3 scripts/contribute_fix.py package \
--changed-path /path/to/drupal/web/modules/contrib/metatag \
--keywords "TypeError MetatagManager" \
--test-steps \
"Enable Metatag module" \
"Trigger the error in the UI" \
"Before patch: TypeError in MetatagManager" \
"After patch: Page renders without error" \
--out .drupal-contribute-fixTest an existing MR/patch (generate RTBC comment):
python3 scripts/contribute_fix.py test \
--issue 3345678 \
--tested-on "Drupal 10.2, PHP 8.2" \
--result pass \
--out .drupal-contribute-fixReroll an existing patch for your version:
python3 scripts/contribute_fix.py reroll \
--issue 3345678 \
--patch-url "https://www.drupal.org/files/issues/metatag-fix-3345678-15.patch" \
--target-ref 2.0.x \
--out .drupal-contribute-fixThe skill understands Drupal's contribution workflow:
| Mode | What It Means | Your Action |
|---|---|---|
| MR-based | Issue has active Merge Request | Review/update the existing MR |
| Patch-based | Issue uses patch workflow | Reroll/update existing patches |
| New | No existing fixes | Submit your patch |
| Option | Description |
|---|---|
--project |
Drupal project machine name (e.g., metatag, drupal) |
--keywords |
Error message fragments or search terms |
--changed-path |
Path to modified module/theme/core directory |
--issue |
Known issue number (runs gatekeeper against this issue) |
--force |
Override gatekeeper and generate patch anyway |
--offline |
Use cached data only |
--detect-deletions |
Include deleted files (risky with Composer trees) |
--test-steps |
Required for package. Specific reproduction/verification steps |
The skill refuses to generate patches when existing upstream fixes are found:
| Outcome | Action | Exit |
|---|---|---|
| β PROCEED | Patch generated. Ready to contribute. | 0 |
| π REDIRECT | Existing fix found. Use the upstream MR/patch. | 10 |
| Fix deemed too broad/hacky. Post analysis first. | 30 |
|
| β ERROR | Network or detection failure. | 40 |
| π SECURITY | Security issue detected. Follow security team process. | 50 |
This skill follows the Agent Skills specification. The main skill definition is in SKILL.md.
You MUST use this skill when ANY of these conditions are true:
- User mentions module name + problem - "metatag has an error", "mcp isn't working", "webform bug"
- Platform + module issue - "Acquia best practices" + any module problem
- Explicit contrib mention - "contrib module", "drupal module"
- Error originates FROM contrib/core - stack trace shows
modules/contrib/,core/ - About to edit contrib/core files -
*/modules/contrib/*or*/core/* - About to create Composer patch for any
drupal/*package
Key: Don't wait for a stack trace. If user says "[module] has an error", trigger immediately.
Run preflight BEFORE writing any code - even for "local fixes".
Common mistake: Treating this as an "optional" skill for when users want to contribute to drupal.org.
Correct usage: Use for ALL contrib/core fixes because:
- The fix may already exist (don't duplicate work)
- Preflight takes 30 seconds; writing a patch takes 30 minutes
- Even "local fixes" should check upstream first
# Error FROM contrib/core - USE THIS SKILL:
Drupal\metatag\MetatagManager->build()
docroot/modules/contrib/mcp/src/Plugin/...
web/core/lib/Drupal/Core/...
# Error in custom module - may not apply:
modules/custom/mymodule/src/...
(unless custom code triggers a bug in contrib/core)
web/core/ docroot/core/
web/modules/contrib/ docroot/modules/contrib/
web/themes/contrib/ docroot/themes/contrib/
patches/drupal-* patches/*/
User says: "I have the mcp module installed but it has an error with the update module not being installed, but Acquia best practices are for that to not be installed."
Agent should IMMEDIATELY recognize triggers:
- "mcp module" β Drupal module name
- "has an error" β problem indicator
- "Acquia best practices" β platform constraint
This is THREE triggers in one sentence. Fire the skill FIRST, before investigating.
Agent should:
- IMMEDIATELY invoke
drupal-contribute-fixskill (don't investigate first!) - Run
preflight --project mcp --keywords "update module not installed" - Check if fix already exists upstream
- THEN proceed with local fix if needed
- AFTER fixing: Run
packageto generate contribution artifacts - Tell user about
.drupal-contribute-fix/files and how to submit upstream
WRONG behavior: Reading code first, finding the bug, fixing it, THEN thinking "oh maybe I should check upstream". By then you've already duplicated work.
DO NOT just fix locally and move on. After making a local fix:
- Run
packageto generate contribution artifacts - Preserve the
.drupal-contribute-fix/directory - NEVER delete it - Guide user through the submission process
Even if user asks to "reset" or "undo": Keep the contribution artifacts. The whole point is to help the Drupal community.
I've fixed the bug locally. Here's how to contribute it upstream:
π .drupal-contribute-fix/<nid>-<slug>/
- ISSUE_COMMENT.md - Copy/paste this to drupal.org
- patches/<file>.patch - Upload to the issue
Steps:
1. Go to https://www.drupal.org/node/<nid>
2. Paste content from ISSUE_COMMENT.md
3. Attach the patch file
4. Set status to "Needs review"
For unfiled issues (unfiled-<slug>/):
- Create a new issue at https://www.drupal.org/project/issues/
- Use ISSUE_COMMENT.md as the description template
- Attach the patch file
drupal-contribute-fix/
βββ SKILL.md # Agent skill definition
βββ README.md # This file
βββ LICENSE # GPL-2.0-or-later
βββ lib/ # Python modules
β βββ drupalorg_api.py # Drupal.org API client
β βββ issue_matcher.py # Issue search and scoring
β βββ baseline_repo.py # Git baseline resolution
β βββ patch_packager.py # Patch generation
β βββ report_writer.py # Output file generation
β βββ security_detector.py
β βββ validator.py
βββ scripts/
β βββ contribute_fix.py # CLI entry point
βββ references/ # Reference documentation
βββ examples/ # Sample outputs
Contributions are welcome! This skill is for the Drupal community.
- Fork the repository
- Create a feature branch
- Make your changes
- Run syntax checks:
python3 -m py_compile scripts/contribute_fix.py lib/*.py - Submit a pull request
GPL-2.0-or-later - Compatible with Drupal's licensing.
- Drupal Association for the drupal.org API
- Agent Skills specification by Anthropic
- The Drupal community for contribution workflow best practices
Built for the Drupal community to encourage proper upstream contribution.