Install
mkdir -p .claude/skills/drush-webmaster && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/16732" && unzip -o skill.zip -d .claude/skills/drush-webmaster && rm skill.zipInstalls to .claude/skills/drush-webmaster
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Manage Drupal content, entities, views, menus, fields, and site structure using drush webmaster (wm:*) commands. Use when asked to "list content types", "find articles by author", "create a new page", "edit node 42", "add a field", "build a view", "add a menu link", "translate this content", "publish this node", "what content types exist", "show me the site schema", "search for content", "clone this entity", "moderate content", or any Drupal entity/content management task. Proactively invoke this skill (do NOT attempt raw SQL, drush eval, or direct Drupal API calls) when the user wants to query, create, edit, inspect, or manage Drupal content, entities, views, menus, blocks, vocabularies, media, translations, or site configuration.About this skill
Preamble (run first)
# Site context discovery — runs automatically when skill is invoked
echo "=== SITE CONTEXT ==="
drush wm:site:info 2>/dev/null || \
echo "SITE_INFO: unavailable (no drush/bootstrap)"
echo ""
echo "=== CONTENT TYPES ==="
drush wm:content-type:list 2>/dev/null || echo "CONTENT_TYPES: unavailable"
echo ""
echo "=== OPTIONAL MODULES ==="
_PM="drush pm:list --status=enabled"
_PM="$_PM --type=module --format=list"
_HAS_LANG=$($_PM 2>/dev/null \
| grep -c "^language$" || true)
_HAS_MOD=$($_PM 2>/dev/null \
| grep -c "^content_moderation$" || true)
_HAS_SEARCH=$($_PM 2>/dev/null \
| grep -c "^search$" || true)
echo "TRANSLATION_ENABLED: $([ "$_HAS_LANG" -gt 0 ] && echo 'yes' || echo 'no')"
echo "MODERATION_ENABLED: $([ "$_HAS_MOD" -gt 0 ] && echo 'yes' || echo 'no')"
echo "SEARCH_ENABLED: $([ "$_HAS_SEARCH" -gt 0 ] && echo 'yes' || echo 'no')"
# Show configured languages if translation is enabled
if [ "$_HAS_LANG" -gt 0 ]; then
echo ""
echo "=== LANGUAGES ==="
drush wm:translation:languages 2>/dev/null || true
fi
# Site learnings — persistent knowledge across sessions
_SITE_UUID=$(drush config:get system.site uuid \
--format=string 2>/dev/null || echo "unknown")
_SITE_HASH=$(echo -n "$_SITE_UUID" \
| (shasum -a 256 2>/dev/null || sha256sum) \
| cut -c1-12)
_LEARN_DIR="$HOME/.drush-wm/sites/$_SITE_HASH"
_LEARN_FILE="$_LEARN_DIR/learnings.jsonl"
echo ""
echo "=== LEARNINGS ==="
echo "SITE_HASH: $_SITE_HASH"
if [ -f "$_LEARN_FILE" ]; then
_LEARN_COUNT=$(wc -l < "$_LEARN_FILE" | tr -d ' ')
echo "LEARNINGS_COUNT: $_LEARN_COUNT"
tail -50 "$_LEARN_FILE"
else
echo "LEARNINGS_COUNT: 0"
fi
After reading preamble output:
- If
TRANSLATION_ENABLEDisno: do not suggest translation commands (wm:translation:*,wm:entity:translation:*) - If
MODERATION_ENABLEDisno: do not suggest moderation commands (wm:entity:transitions,wm:entity:moderate) - If
SEARCH_ENABLEDisno: do not suggestwm:search, usewm:entity:queryinstead - If
SITE_INFOisunavailable: alert the user that drush is not available or the site cannot be bootstrapped - Use the content type list to validate bundle names before running entity queries or create operations
Drush Webmaster Commands
The wm:* command suite provides YAML-based Drupal
site management. All output is structured YAML. Run
drush wm:<command> --help for full documentation
on any command before using it.
Discovery (Full Schema)
wm:schema:dump — Full site schema for AI context
Use for deeper exploration beyond what the preamble provides.
# Full schema (all sections)
drush wm:schema:dump
# Single section (faster, focused)
drush wm:schema:dump --section=content-types
drush wm:schema:dump --section=views
drush wm:schema:dump --section=vocabularies
# Multiple sections
drush wm:schema:dump --section=content-types --section=vocabularies
Available sections: content-types,
vocabularies, views, menus, blocks,
media-types, entity-types, site
Use drush wm:schema:sections to list all available sections.
wm:site:info — Site name, slogan, email, paths
drush wm:site:info
Before Modifying (Discover First)
Before running any create, edit, or delete operation, run the appropriate discovery command if you haven't already in this conversation. This prevents validation errors and wasted retries.
| Operation | Run first |
|---|---|
| Create entity | wm:content-type:get — fields |
| Edit fields | wm:entity:get — current values |
| Delete entity | wm:entity:get — confirm target |
| Add field | wm:field:list + wm:field:types |
| Create/edit view | wm:view:tables + available:fields |
| Translate | wm:translation:languages |
| Moderate | wm:entity:transitions — states |
| Bulk create | wm:content-type:get — structure |
| Bulk delete | wm:entity:query — confirm scope |
Skip discovery when the context is already known from earlier in the conversation or the user explicitly asks to proceed directly.
Querying & Inspecting Entities
wm:entity:query — Find entities by field conditions
Powerful query with operators, sorting, pagination. No search index needed.
# Published articles by author
drush wm:entity:query node article \
--where="status=1" --where="uid=5" \
--sort=created:DESC
# Count drafts
drush wm:entity:query node --where="status=0" --count
# Content without images
drush wm:entity:query node article --where="field_image=NULL" --fields=id,title
# Paginate
drush wm:entity:query node blog --limit=50 --offset=50
Where operators: =, !=, >, <, >=, <=,
=NULL, !=NULL, =a,b,c (IN list)
Options: --where, --sort=field:ASC|DESC,
--limit, --offset, --fields, --count
wm:entity:get — Get single entity with field values
drush wm:entity:get node 1
drush wm:entity:get node 1 --fields=title,body
drush wm:entity:get node 1 --exclude=revision_*,created
drush wm:entity:get user 1
wm:entity:field:get — Get a single field value
drush wm:entity:field:get node 1 title
drush wm:entity:field:get node 1 body
drush wm:entity:field:get user 1 mail
wm:entity:field:set — Set a single field value
drush wm:entity:field:set node 1 title "New Title"
drush wm:entity:field:set node 1 body \
'{"value":"<p>HTML</p>","format":"full_html"}'
drush wm:entity:field:set node 1 status 1
drush wm:entity:field:set node 1 status 1 --dry-run
Value is a plain string for simple fields, or JSON for structured fields (body, entity references, links, images).
wm:entity:list — Simple entity listing
drush wm:entity:list node article
wm:search — Full-text search (requires Search module + index)
drush wm:search "contact form"
drush wm:search "DXPR" --limit=50
drush wm:search "admin" --type=user_search
Returns: nid, bundle, title, url, snippet. Use
wm:entity:get to get full content after finding
results.
Creating & Editing Entities (YAML Workflow)
The edit/apply workflow: export to YAML, modify, apply with validation.
wm:entity:edit + wm:entity:apply — Edit existing entity
# Step 1: Export to versioned YAML file
drush wm:entity:edit node 5089
# Step 2: Edit the YAML file at ~/.drush-wm/entities/
# Step 3: Validate and save
drush wm:entity:apply node 5089
# Preview changes without saving
drush wm:entity:apply node 5089 --dry-run
wm:entity:new + wm:entity:apply — Create new entity
# Step 1: Create template with all fields
drush wm:entity:new node article
# Step 2: Edit the template (fill required fields)
# Step 3: Create the entity
drush wm:entity:apply node new
Bulk Operations
drush wm:entity:bulk-create # YAML input
drush wm:entity:bulk-update # YAML input
drush wm:entity:bulk-delete # YAML input
Other Entity Operations
drush wm:entity:clone node 42 # Clone entity
drush wm:entity:deep-clone node 42 # Clone with references
drush wm:entity:diff node 42 43 # Compare entities
drush wm:entity:history node 42 # YAML version history
drush wm:entity:revert node 42 # Revert to previous version
drush wm:entity:fields node article # List fields for a bundle
drush wm:entity:types # List entity types
Content Types & Fields
drush wm:content-type:list # List all content types
drush wm:content-type:get article # Full details + fields
drush wm:content-type:stats # Content counts
drush wm:content-type:create # Create content type
drush wm:content-type:update article # Update settings
drush wm:content-type:delete article # Delete (must be empty)
drush wm:field:list node article # List fields on bundle
drush wm:field:get node article title # Field details
drush wm:field:add # Add field to bundle
drush wm:field:update # Update field settings
drush wm:field:delete # Delete field + data
drush wm:field:types # Available field types
Views
Same YAML edit/apply workflow as entities.
drush wm:view:list # List all views
drush wm:view:get content # Full view config
drush wm:view:create # Create view
drush wm:view:clone content # Clone existing view
drush wm:view:edit blog # Export to YAML
drush wm:view:apply blog # Apply changes
drush wm:view:apply blog --dry-run # Validate only
drush wm:view:preview content # Render view output
drush wm:view:delete content # Delete view
drush wm:view:enable/disable content # Toggle view
# Discovery helpers for building views
drush wm:view:tables # Available base tables
drush wm:view:available:fields node_field_data
drush wm:view:available:filters node_field_data
drush wm:view:available:sorts node_field_data
drush wm:view:available:arguments node_field_data
drush wm:view:available:relationships node_field_data
drush wm:view:available:areas node_field_data
Menus
drush wm:menu:list # All menus with link counts
drush wm:menu:get main # Menu details + all links
drush wm:menu:create # Create menu
drush wm:menu:delete main # Delete (must be empty)
drush wm:menu:link:list main # Links with hierarchy
drush wm:menu:link:add # Add link
drush wm:menu:link:update # Update link
drush wm:menu:link:delete # Delete link
Vocabularies & Taxonomy
drush wm:vocabulary:list # All vocabularies + counts
drush wm:vocabulary:get tags # Vocabulary details + fields
drush wm:vocabulary:create # Create vocabulary
drush wm:vocabulary:update tags # Update settings
drush wm:vocabulary:delete tags # Delete (must be empty)
Use `wm:entity:query
Content truncated.