Automating Drupal dependency management with custom Claude Code commands

Components

As developers, we often face repetitive tasks that are perfect candidates for automation. In Drupal development, one such task is identifying unused modules: packages installed via Composer but never actually enabled in the CMS. This can happen when testing modules, switching solutions, or inheriting legacy projects.

Traditional approaches require manually cross-referencing composer.json with the configuration files or running Drush commands to compare installed versus enabled modules. But what if we could ask an AI assistant to do this analysis for us with a simple command?

Enter Claude Code custom slash commands, a powerful feature that allows you to create reusable automation workflows tailored to your project's specific needs.

The Problem: Dependency Bloat

In a typical Drupal project managed with Composer, it's easy to accumulate unused dependencies over time:

  • You install a module to test a feature but later choose a different solution
  • A module gets disabled but never removed from composer.json
  • You inherit a project with unclear dependency management
  • Development workflows introduce modules that production doesn't need

These unused modules:

  • Increase your vendor directory size
  • Add unnecessary code to security audits
  • Create confusion about what's actually being used
  • Slow down Composer operations

The Solution: A Custom Claude Code Command

Claude Code allows you to create custom slash commands stored in .claude/commands/ as markdown files. These commands provide structured instructions that Claude follows consistently every time you invoke them.

I created /check-unused-modules to automate the comparison between Composer dependencies and enabled Drupal modules.

Complete Command Code

Here's the full implementation (.claude/commands/check-unused-modules.md):

---
description: Check for Drupal modules in composer.json that are not enabled in the CMS
---

# Check Unused Drupal Modules

Compare composer.json with config/sync/core.extension.yml to identify Drupal contributed modules that are installed via Composer but not enabled in the CMS.

## Instructions

1. Read composer.json and extract all drupal/* packages from the require section (excluding drupal/core-* packages)

2. Read config/sync/core.extension.yml and extract all enabled modules from the module: section

3. Compare the lists and identify modules that are:
   - Present in composer.json as drupal/module_name
   - NOT enabled in core.extension.yml

4. Present findings in this format:

   ## Unused Drupal Modules

   The following modules are installed via Composer but NOT enabled in Drupal:

   - drupal/example_module
   - drupal/another_module

   These modules can be safely removed with:
   docker compose exec php composer remove drupal/example_module drupal/another_module

   Note: If all modules are enabled, report: "✅ All Drupal modules in composer.json are enabled. No unused modules found."

5. Important considerations:
   - Ignore themes (they can be installed but not set as default)
   - Ignore submodules (like metatag_facebook - these are part of parent modules)
   - Only report top-level contributed modules that are completely unused
   - Remember that some modules are dependencies of other modules and may not appear in core.extension.yml but are still needed

## Output Format

Provide a clear, actionable report with:
- List of unused modules (if any)
- Ready-to-run composer remove command
- Clear indication if no cleanup is needed

How It Works

The command follows a simple but effective workflow:

  1. Read composer.json: Extracts all drupal/* packages from the require section
  2. Read core.extension.yml: Gets the list of enabled modules from Drupal's configuration
  3. Compare: Identifies packages in Composer that aren't enabled in Drupal
  4. Report: Provides a clear, actionable output with ready-to-run commands

The command includes important edge case handling:

  • Excludes drupal/core-* packages (these are framework components, not modules)
  • Ignores themes (can be installed but not active)
  • Accounts for submodules (e.g., metatag_facebook is part of the metatag module)
  • Recognizes that some modules are dependencies and won't appear in core.extension.yml

Real-World Example Output

When I run /check-unused-modules on my production site (gorannikolovski.com), here's what I get:

## Unused Drupal Modules

✅ All Drupal modules in composer.json are enabled. No unused modules found.

All 43 Drupal contributed modules installed via Composer are properly enabled in the CMS:

Enabled modules from composer.json:
- admin_toolbar ✓
- antibot ✓
- block_class ✓
- block_field ✓
- captcha ✓
- classy ✓ (theme)
- coffee ✓
- colorbox ✓
- config_readonly ✓
- diff ✓
- editor_advanced_link ✓
- email_registration ✓
- flood_control ✓
- gin ✓ (theme)
- gin_login ✓
- gin_toolbar ✓
- gtm ✓
- highlightjs_input_filter ✓
- ip2country ✓
- linkit ✓
- login_emailusername ✓
- login_switch ✓
- mailchimp ✓
- markdown_easy ✓
- menu_link_attributes ✓
- metatag ✓ (including submodules)
- metatag_async_widget ✓
- node_edit_protection ✓
- paragraphs ✓
- pathauto ✓
- queue_ui ✓
- recaptcha ✓
- recaptcha_v3 ✓
- redirect ✓
- schema_metatag ✓ (including submodules)
- search_api ✓
- simple_sitemap ✓
- something_went_wrong ✓
- symfony_mailer_lite ✓
- views_bulk_operations ✓
- webform ✓
- webform_mailchimp ✓

No cleanup needed - your Composer dependencies are optimized and all installed modules are actively being used.

Perfect! My dependencies are clean. But if there were unused modules, the output would look like this:

## Unused Drupal Modules

The following modules are installed via Composer but NOT enabled in Drupal:

- drupal/facets
- drupal/perimeter

These modules can be safely removed with:

docker compose exec php composer remove drupal/facets drupal/perimeter

Notice how the command provides a ready-to-execute removal command with no need to manually construct it.

Why This Approach Works Well

1. Consistency

Every time you run /check-unused-modules, you get the same thorough analysis. No forgetting to check certain edge cases or making manual comparison errors.

2. Documentation as Code

The command itself documents the process. New team members can read the .md file to understand exactly what analysis is being performed.

3. Project-Specific Context

The command knows your project structure. It knows where your composer.json is, where your configuration exports live (config/sync/), and that you're using Docker for local development.

4. Actionable Output

Rather than just listing problems, it provides ready-to-run solutions. Copy, paste, execute. Done.

5. Reusability

Write once, run anytime. Whether you're doing monthly maintenance or cleaning up after a major refactor, the command is always there.

Extending the Concept

This pattern can be applied to many other development tasks:

  • Security audits: Check for outdated dependencies with known vulnerabilities
  • Code quality: Analyze custom modules for deprecated API usage
  • Performance: Identify large unused libraries or assets
  • Configuration drift: Compare configuration between environments
  • Testing coverage: Find untested code paths or missing test cases

The key is identifying repetitive analytical tasks that involve:

  1. Reading multiple files
  2. Comparing or analyzing data
  3. Producing actionable insights

Implementation Tips

If you want to create your own custom commands:

1. Be Specific with Instructions

Don't assume Claude knows your exact workflow. Spell out each step clearly:

  • "Read file X"
  • "Extract pattern Y"
  • "Compare with Z"
  • "Present as format Q"

2. Define Output Format

Include examples of the exact output you want. This ensures consistency across invocations.

3. Handle Edge Cases

Think through special scenarios (themes vs. modules, submodules, dependencies) and document how to handle them.

4. Provide Context

If your project uses Docker, mention it. If you have special file locations, specify them. Context helps Claude give better results.

5. Make It Actionable

Don't just identify problems. Provide solutions. Generate ready-to-run commands, link to relevant documentation, suggest next steps.

Project Integration

I've integrated this command into my CLAUDE.md project instructions, which provides context about my Drupal 11 site:

  • Docker-based local development
  • GitLab CI/CD pipeline
  • Configuration management workflow
  • Custom module structure
  • QA tools and testing strategy

Claude Code reads this file and understands my project context, making commands like /check-unused-modules even more powerful because they're aware of the broader development environment.

You can see this in action in my repository where I also document common development commands, architecture decisions, and important notes for AI-assisted development.

Conclusion

Custom Claude Code commands represent a powerful shift in how we approach development automation. Instead of writing scripts in Bash, Python, or Node.js, we can write instructions in natural language that an AI assistant executes consistently.

The /check-unused-modules command demonstrates this perfectly:

  • Simple to write: Plain markdown, no programming required
  • Easy to maintain: Update the instructions when your needs change
  • Contextually aware: Understands your project structure and conventions
  • Immediately useful: Solves a real problem that occurs in Drupal projects

This is just one example of how AI-powered automation can streamline your workflow. The pattern is extensible to countless other development tasks. Anywhere you find yourself doing repetitive analysis, there's likely an opportunity for a custom command.

What repetitive tasks in your projects could benefit from this approach? Start simple, iterate, and build a library of custom commands that make your development workflow more efficient.

Try it yourself: Create a .claude/commands/ directory in your project, add a markdown file with clear instructions, and invoke it with a slash command. You'll be surprised at how quickly these custom automations become indispensable parts of your toolkit.

About the Author

Goran Nikolovski is a senior developer with over 10 years of experience turning ideas into scalable digital products—from web platforms and mobile apps to AI-powered tools. He is passionate about clean code, creative problem-solving, and shaping the future of digital development.

AI Assistant