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
- DDEV + drupalorg-cli Setup (Recommended)
- 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 local .diff review artifacts in diffs/. |
| Maintainer Burnout: Spammy, low-quality issues. | Maintainer-friendly: Warns on risky patterns and keeps human review in the loop. |
Lost Fixes: Local work vanishes on composer update. |
Preserved Work: Artifacts are saved for future MR follow-up. |
| 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 artifacts only (comment + .diff) |
| 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 local .diff artifact 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 MR ARTIFACTS + DIFF]
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
β βββ diffs/
β βββ metatag-fix-3345678.diff # Local review artifact
βββ unfiled-update-module-check/ # New issue needed
βββ REPORT.md
βββ ISSUE_COMMENT.md
βββ diffs/
βββ project-fix-new.diff
Directory naming: {nid}-{slug}/ for existing issues, unfiled-{slug}/ for new issues.
- Python 3.8+
- Git
- Internet access to drupal.org API
- DDEV (recommended for running
drupalorg-cli) - PHP 8.1+ in the runtime where
drupalorg-cliexecutes
# Clone from your preferred location
git clone <repository-url>
cd drupal-contribute-fixThis skill now recommends drupalorg-cli for issue-fork/MR/pipeline execution.
If your host PHP is older, run drupalorg-cli inside DDEV.
Install a global DDEV command:
mkdir -p ~/.ddev/commands/web
cat > ~/.ddev/commands/web/drupalorg <<'EOF'
#!/usr/bin/env bash
## Description: Run drupalorg-cli inside the web container
## Usage: drupalorg [args]
## ProjectTypes: drupal,drupal11,drupal10,drupal9,drupal8,drupal7,backdrop,php
## ExecRaw: true
set -euo pipefail
PHAR="/mnt/ddev-global-cache/drupalorg-cli/drupalorg.phar"
URL="https://github.com/mglaman/drupalorg-cli/releases/latest/download/drupalorg.phar"
mkdir -p "$(dirname "$PHAR")"
if [ ! -x "$PHAR" ]; then
curl -fsSL "$URL" -o "$PHAR"
chmod +x "$PHAR"
fi
exec php "$PHAR" "$@"
EOF
chmod +x ~/.ddev/commands/web/drupalorgOptional shell alias:
alias drupalorg='ddev drupalorg'Verify in a DDEV project directory:
ddev restart
ddev drupalorg --versionSearch only (preflight):
python3 scripts/contribute_fix.py preflight \
--project metatag \
--keywords "TypeError MetatagManager" \
--out .drupal-contribute-fixTip: Drupal.org's api-d7 endpoint does not support a full-text text= filter (it returns HTTP 412). For manual keyword searching, use the Drupal.org UI search:
https://www.drupal.org/project/issues/search/<project>?text=<keywords>
For deeper triage (filters by status/priority/category/version/component/tag) and issue/thread summaries via api-d7, use the companion tool drupal-issue-queue (GitHub repo: scottfalconer/drupal-issue-queue).
Common examples (run from the drupal-issue-queue directory):
# Summarize an issue (Markdown)
python scripts/dorg.py issue <nid-or-url> --format md
# Filter/search a project's issues
python scripts/dorg.py search --project <machine_name> --status "needs review" --limit 20 --format jsonIf drupal-contribute-fix canβt auto-detect drupal-issue-queue on your machine, set:
export DRUPAL_ISSUE_QUEUE_DIR=/path/to/drupal-issue-queueSearch + generate MR artifacts + local diff:
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 fix: TypeError in MetatagManager" \
"After fix: Page renders without error" \
--out .drupal-contribute-fixPreferred handoff to drupalorg-cli (run in your Drupal project directory):
drupalorg issue:show <nid> --format=llm
drupalorg issue:get-fork <nid> --format=llm
drupalorg issue:setup-remote <nid>
drupalorg issue:checkout <nid> <branch>
drupalorg mr:list <nid> --format=llm
drupalorg mr:status <nid> <mr-iid> --format=llmTest an existing MR/diff artifact (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 (legacy fallback):
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 via drupalorg mr:* commands |
| Historical patches | Issue has patch attachments but no MR | Use MR workflow for new work |
| New | No existing fixes | File issue, then use drupalorg issue-fork + MR flow |
| 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 local diff artifact 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 local artifacts when existing upstream fixes are found:
| Outcome | Action | Exit |
|---|---|---|
| β PROCEED | MR artifacts + local diff generated. | 0 |
| π REDIRECT | Existing fix found. Use the upstream MR workflow. | 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 convert local contrib/core changes into upstream MR work
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; reworking duplicate code takes far longer
- 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 providedrupalorg-clinext commands
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 completed triage/fix prep. Here's how to continue upstream:
π .drupal-contribute-fix/<nid>-<slug>/
- ISSUE_COMMENT.md - Copy/paste this to drupal.org
- diffs/<file>.diff - Local review artifact (do not upload unless maintainers ask)
Recommended commands:
drupalorg issue:show <nid> --format=llm
drupalorg issue:get-fork <nid> --format=llm
drupalorg issue:setup-remote <nid>
drupalorg issue:checkout <nid> <branch>
drupalorg mr:list <nid> --format=llm
For unfiled issues (unfiled-<slug>/):
- Create a new issue at https://www.drupal.org/project/issues/
- Use ISSUE_COMMENT.md as the description template
- Continue with
drupalorgcommands using the new issue NID
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 # Local diff 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.