Drupal Code Provenance
Overview
In a Drupal project the code that runs on the server splits into two very different piles.
Drupal core and every contrib module, theme, and profile are community code: reviewed, released,
and maintained by thousands of people. The code under modules/custom, themes/custom, and
profiles/custom is what you and the agent wrote for this one site, and it has had none of that
scrutiny. Server-side PHP is where the real risk lives (access checks, queries, form handlers), so
custom PHP is the "dangerous" code worth measuring on its own.
This skill runs a script that counts SLOC (source lines, skipping blank and comment-only lines) and
answers one question: how much of the running code did the agent write, versus the community. It
reports server-side PHP and frontend separately, because a large custom frontend is far less
dangerous than a large custom backend.
What counts as what
| Bucket |
Paths |
Extensions |
| Custom (you) |
modules/custom, themes/custom, profiles/custom |
see below |
| Community |
core/, everything else under modules/, themes/, profiles/ |
see below |
| Server-side PHP ("dangerous") |
either bucket |
.php .module .inc .install .theme .profile .engine |
| Frontend (counted on its own) |
either bucket |
.js .jsx .ts .tsx .mjs .cjs .vue, .css .scss .sass .less, .twig |
.theme files are PHP (theme preprocess and hook logic run on the server), so they count as
server-side, not frontend. Contrib placed directly under modules/ (not in a contrib/
subfolder) still counts as community: anything under modules/ that is not in custom/ is
community.
Workflow
Run the script from the skill folder against the project root. The docroot (web/,
docroot/, or the root itself) is detected automatically by looking for core/.
./count-code.sh /path/to/drupal-project
With no argument it uses the current directory. No external tools are required (bash, find, awk).
Read the two headline numbers. "Dangerous code you wrote" is the custom share of all
server-side PHP SLOC. "Frontend code you wrote" is the custom share of all frontend SLOC. The
table above them shows the raw custom-vs-community SLOC for each language bucket.
Report the result plainly. Give the percentages and the raw SLOC. Do not editorialize about
whether a number is good or bad unless the user asks; a high custom-PHP share is expected on a
heavily bespoke site and low on a mostly-assembled one.
Output
The script prints a table of custom-vs-community SLOC per language, then the two headline ratios:
Dangerous code you wrote (server-side PHP):
10 SLOC of 20 total = 50.0%
Frontend code you wrote:
5 SLOC of 7 total = 71.4%
Counting method
SLOC skips blank lines and comment-only lines (sloc.awk). Comment syntax is applied per language:
// and /* */ for PHP and JS, /* */ only for CSS, {# #} for Twig. # is deliberately not
treated as a PHP line comment, so PHP 8 attributes (#[Route(...)], plugin attributes) count as
code and CSS id selectors (#header) are never mistaken for comments. The counter is a heuristic,
not a parser: a comment token inside a string can truncate that line, but since such a line already
counts as code the effect on the total is negligible.
Common Mistakes
| Mistake |
Fix |
Pointing the script at web/ instead of the project root |
Either works; it detects the docroot. Give the project root and let it find core/. |
Expecting vendor/ (Composer libraries) to be counted |
Out of scope: this measures Drupal core, contrib, and custom, not raw Composer deps. |
Treating .theme/.twig as frontend |
.theme is server-side PHP; only .twig is frontend among theme files. |
Counting contrib under modules/ as custom |
Only modules/custom (and themes/custom, profiles/custom) is custom; the rest is community. |
Reading raw wc -l numbers |
These are SLOC (blank and comment lines removed), so they are lower than wc -l and more honest. |
No core/ found, community shows 0 |
The path is not a Drupal docroot, or core is not installed. Run composer install first, or point at the right root. |
1---2name: drupal-code-provenance3description: Use when measuring how much of a Drupal codebase is custom code (written by you or the agent) versus community code (Drupal core and contrib modules, themes, and profiles), how much server-side "dangerous" PHP was hand-written, the ratio of custom to community code, or a code provenance/authorship breakdown of a Drupal site.4---56# Drupal Code Provenance78## Overview910In a Drupal project the code that runs on the server splits into two very different piles.11Drupal core and every contrib module, theme, and profile are community code: reviewed, released,12and maintained by thousands of people. The code under `modules/custom`, `themes/custom`, and13`profiles/custom` is what you and the agent wrote for this one site, and it has had none of that14scrutiny. Server-side PHP is where the real risk lives (access checks, queries, form handlers), so15custom PHP is the "dangerous" code worth measuring on its own.1617This skill runs a script that counts SLOC (source lines, skipping blank and comment-only lines) and18answers one question: **how much of the running code did the agent write, versus the community.** It19reports server-side PHP and frontend separately, because a large custom frontend is far less20dangerous than a large custom backend.2122## What counts as what2324| Bucket | Paths | Extensions |25|---|---|---|26| Custom (you) | `modules/custom`, `themes/custom`, `profiles/custom` | see below |27| Community | `core/`, everything else under `modules/`, `themes/`, `profiles/` | see below |28| Server-side PHP ("dangerous") | either bucket | `.php .module .inc .install .theme .profile .engine` |29| Frontend (counted on its own) | either bucket | `.js .jsx .ts .tsx .mjs .cjs .vue`, `.css .scss .sass .less`, `.twig` |3031`.theme` files are PHP (theme preprocess and hook logic run on the server), so they count as32server-side, not frontend. Contrib placed directly under `modules/` (not in a `contrib/`33subfolder) still counts as community: anything under `modules/` that is not in `custom/` is34community.3536## Workflow37381. **Run the script** from the skill folder against the project root. The docroot (`web/`,39 `docroot/`, or the root itself) is detected automatically by looking for `core/`.40 ```bash41 ./count-code.sh /path/to/drupal-project42 ```43 With no argument it uses the current directory. No external tools are required (bash, find, awk).44452. **Read the two headline numbers.** "Dangerous code you wrote" is the custom share of all46 server-side PHP SLOC. "Frontend code you wrote" is the custom share of all frontend SLOC. The47 table above them shows the raw custom-vs-community SLOC for each language bucket.48493. **Report the result plainly.** Give the percentages and the raw SLOC. Do not editorialize about50 whether a number is good or bad unless the user asks; a high custom-PHP share is expected on a51 heavily bespoke site and low on a mostly-assembled one.5253## Output5455The script prints a table of custom-vs-community SLOC per language, then the two headline ratios:5657```58Dangerous code you wrote (server-side PHP):59 10 SLOC of 20 total = 50.0%60Frontend code you wrote:61 5 SLOC of 7 total = 71.4%62```6364## Counting method6566SLOC skips blank lines and comment-only lines (`sloc.awk`). Comment syntax is applied per language:67`//` and `/* */` for PHP and JS, `/* */` only for CSS, `{# #}` for Twig. `#` is deliberately **not**68treated as a PHP line comment, so PHP 8 attributes (`#[Route(...)]`, plugin attributes) count as69code and CSS id selectors (`#header`) are never mistaken for comments. The counter is a heuristic,70not a parser: a comment token inside a string can truncate that line, but since such a line already71counts as code the effect on the total is negligible.7273## Common Mistakes7475| Mistake | Fix |76|---|---|77| Pointing the script at `web/` instead of the project root | Either works; it detects the docroot. Give the project root and let it find `core/`. |78| Expecting `vendor/` (Composer libraries) to be counted | Out of scope: this measures Drupal core, contrib, and custom, not raw Composer deps. |79| Treating `.theme`/`.twig` as frontend | `.theme` is server-side PHP; only `.twig` is frontend among theme files. |80| Counting contrib under `modules/` as custom | Only `modules/custom` (and `themes/custom`, `profiles/custom`) is custom; the rest is community. |81| Reading raw `wc -l` numbers | These are SLOC (blank and comment lines removed), so they are lower than `wc -l` and more honest. |82| No `core/` found, community shows 0 | The path is not a Drupal docroot, or core is not installed. Run `composer install` first, or point at the right root. |