From a8de1ac05e1281caa2db911a19efa7cc2ddc8516 Mon Sep 17 00:00:00 2001 From: martinstadelmann Date: Tue, 14 Jul 2026 08:16:02 +0200 Subject: [PATCH 1/4] PF-531: Added AI integration for coding agents --- README.md | 6 ++ assets/merge/.claude/settings.json.twig | 15 ++++ assets/merge/.gitignore.twig | 3 + .../actions/install-local/action.yml.twig | 2 +- .../workflows/copilot-setup-steps.yml.twig | 72 ++++++++++++++++++ assets/replace/AGENTS.md.twig | 74 +++++++++++++++++++ composer.json | 5 ++ docs/ai.md | 46 ++++++++++++ docs/automation.md | 14 +++- docs/configuration.md | 4 + 10 files changed, 239 insertions(+), 2 deletions(-) create mode 100644 assets/merge/.claude/settings.json.twig create mode 100644 assets/replace/.github/workflows/copilot-setup-steps.yml.twig create mode 100644 assets/replace/AGENTS.md.twig create mode 100644 docs/ai.md diff --git a/README.md b/README.md index 0b66824..48b4217 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ The bundled assets are for the iqual internal developer platform's Drupal integr * Workflows for Drupal automation using **GitHub Actions** * Integration for deployment to **Upsun** (formerly Platform.sh) * `Makefile` **commands** for project and app tasks +* **AI integration** for coding agents ## Quick Start @@ -118,6 +119,7 @@ assets/replace/ │ │ └── upgrader.sh │ └── workflows │ ├── config-pull.yml.twig +│ ├── copilot-setup-steps.yml.twig │ ├── phpcs.yml.twig │ ├── phpunit-functional-testing.yml.twig │ ├── phpunit-unit-testing.yml.twig @@ -125,6 +127,7 @@ assets/replace/ │ ├── update.yml.twig │ ├── upgrade.yml.twig │ └── visual-regression-testing.yml.twig +├── AGENTS.md.twig ├── Makefile ├── README.md.twig ├── solr @@ -158,6 +161,8 @@ Assets that will be merged into existing destination files or added if inexisten ``` assets/merge/ +├── .claude +│ └── settings.json.twig ├── .dockerignore ├── .env.twig ├── .env.visreg.twig @@ -185,6 +190,7 @@ assets/merge/ * [Service Deployment](./docs/deployment.md) * [App Installation](./docs/installation.md) * [Automation (CI/CD)](./docs/automation.md) + * [AI Integration](./docs/ai.md) * DDEV * [DDEV Usage](https://docs.ddev.com/en/stable/users/usage/) * [DDEV Configuration](https://docs.ddev.com/en/stable/users/configuration/config/) diff --git a/assets/merge/.claude/settings.json.twig b/assets/merge/.claude/settings.json.twig new file mode 100644 index 0000000..7f1fc4d --- /dev/null +++ b/assets/merge/.claude/settings.json.twig @@ -0,0 +1,15 @@ +{% if ai.claude %} +{ + "extraKnownMarketplaces": { + "iqual": { + "source": { + "source": "github", + "repo": "iqual-ch/claude-plugins" + } + } + }, + "enabledPlugins": { + "iqual-developer@iqual": true + } +} +{% endif %} diff --git a/assets/merge/.gitignore.twig b/assets/merge/.gitignore.twig index 824547b..a7616df 100644 --- a/assets/merge/.gitignore.twig +++ b/assets/merge/.gitignore.twig @@ -109,3 +109,6 @@ maintenance_*.html playwright-report/ playwright-snapshots/ playwright-tmp/ + +# Ignore local AI agent settings +.claude/settings.local.json diff --git a/assets/replace/.github/actions/install-local/action.yml.twig b/assets/replace/.github/actions/install-local/action.yml.twig index e2e6a28..6040afe 100644 --- a/assets/replace/.github/actions/install-local/action.yml.twig +++ b/assets/replace/.github/actions/install-local/action.yml.twig @@ -1,4 +1,4 @@ -{% if workflows.update or workflows.vrt %} +{% if workflows.update or workflows.vrt or workflows.phpunit or ai.copilot %} {%- verbatim -%} name: Install Drupal locally description: Checkout, configure, deploy and install a local drupal project diff --git a/assets/replace/.github/workflows/copilot-setup-steps.yml.twig b/assets/replace/.github/workflows/copilot-setup-steps.yml.twig new file mode 100644 index 0000000..bc9ad53 --- /dev/null +++ b/assets/replace/.github/workflows/copilot-setup-steps.yml.twig @@ -0,0 +1,72 @@ +{% if ai.copilot %} +name: Copilot setup steps + +{% if deployment == "platform.sh" %} +{% verbatim -%} +env: + DDEV_VERSION: ${{ vars.DDEV_VERSION || 'latest' }} + +on: + workflow_dispatch: + +jobs: + # Must be named exactly "copilot-setup-steps" to be picked up by Copilot. + copilot-setup-steps: + runs-on: {% endverbatim %}{{ workflows.runner | default('ubuntu-latest') }}{% verbatim %} + + timeout-minutes: 45 + + permissions: + contents: read + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Install agent skills from the iqual-developer plugin + uses: iqual-ch/claude-plugins/.github/actions/install-skills@main + + - name: Install Drupal locally + uses: ./.github/actions/install-local + with: + ssh_key: ${{ secrets.SSH_KEY }} + + # The coding agent gets a fully installed local site but must not be able + # to reach remote environments: remove the SSH key before handing over. + - name: Remove SSH access to remote environments + shell: bash + run: | + rm -f .ddev/homeadditions/.ssh/id_rsa + ddev exec 'rm -f ~/.ssh/id_rsa' || true + ddev restart + + - name: Verify remote environment access is gone + shell: bash + run: | + if timeout 30 ddev drush @spot status 2>/dev/null; then + echo "::error::drush can still reach @spot over SSH after key removal." + exit 1 + fi +{% endverbatim -%} +{% else %} +on: + workflow_dispatch: + +jobs: + # Must be named exactly "copilot-setup-steps" to be picked up by Copilot. + copilot-setup-steps: + runs-on: {{ workflows.runner | default('ubuntu-latest') }} + + timeout-minutes: 15 + + permissions: + contents: read + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Install agent skills from the iqual-developer plugin + uses: iqual-ch/claude-plugins/.github/actions/install-skills@main +{% endif %} +{% endif %} diff --git a/assets/replace/AGENTS.md.twig b/assets/replace/AGENTS.md.twig new file mode 100644 index 0000000..3ad2271 --- /dev/null +++ b/assets/replace/AGENTS.md.twig @@ -0,0 +1,74 @@ +{% if ai.agents %} +# AGENTS.md + +{{ title|default(name) }} — a Drupal website built on the iqual Drupal Platform stack. + +> This file is generated by [`iqual/drupal-platform`](https://github.com/iqual-ch/drupal-platform) and will be overwritten on updates — do not edit it directly. Add project-specific knowledge to `docs/` and project-specific skills to `.agents/skills/` instead (both are picked up automatically). + +## Facts + +- **Stack:** Drupal, PHP {{ runtime.php_version }}, MariaDB {{ runtime.db_version }}, nginx-fpm — via [DDEV](https://ddev.readthedocs.io/) locally{% if deployment == "platform.sh" %}, [Upsun](https://docs.upsun.com/) (PaaS) in remote environments{% endif %}. +{% if runtime.solr_version %} +- **Search:** Apache Solr {{ runtime.solr_version }} via Search API. +{% endif %} +- **Docroot:** `app/{{ locations['web-root'] }}`. Composer root: `app` (`app/composer.json` — `ddev composer` operates there automatically). Custom code: `app/{{ locations['web-root'] }}/modules/custom/`, `app/{{ locations['web-root'] }}/themes/custom/`. +- **URLs:** local `https://{{ name }}.ddev.site/`{% if deployment == "platform.sh" %}, live {{ url }}/{% endif %}. +- **Runtime is already up** in this environment — don't run `make install` unless explicitly asked to (re)provision. Use `ddev drush status` to check site state first. +- **Workflow:** GitHub Flow — branch → develop → PR → review → merge{% if deployment == "platform.sh" %} → deploy. Only `main` deploys to production{% endif %}. Branch format: `issue/XXX-YYY_description`, commit format: `XXX-YYY: Description`, referencing a Jira issue. +- **Never push to `main`, and never merge a PR yourself.** Merging always requires an explicit, human-approved review — even if all checks pass. +- **Dependencies:** Composer (PHP) + npm/Bun (JS), locked in lock files. Config changes go through Drupal's config export/import (`drush cex`/`drush cim`), never hand-edited in the target environment. +- **CI (GitHub Actions), on every PR:** PHPCS linting{% if workflows.phpunit %}, PHPUnit (unit/kernel/functional/browser){% endif %}{% if workflows.vrt %}, visual regression testing{% endif %}. Must pass before merge. + +## Commands + +Run `make help` for the full list; `ddev drush ` for anything Drupal-specific once the environment is up. + +| Task | Command | +|---|---| +| Check site status | `ddev drush status` | +| Start local runtime | `make runtime` | +| Build & deploy Drupal locally | `make drupal` | +| Lint (PHPCS + PHPStan, custom code only) | `make lint` | +| Auto-fix lint issues | `make beauty` | +| Run tests | `make test`{% if workflows.phpunit %} (see the `drupal-testing` skill for granular targets){% endif %} | +{% if workflows.phpunit %} +| List available Drupal tests | `make drupal-test-list` | +{% endif %} +{% if deployment == "platform.sh" %} +| Pull config from remote | `make config-pull` | +{% endif %} +| Open project in browser (for user) | `make launch` | +| Open project logged in as admin (for user) | `make login` | + +## Skills + +Reusable Drupal engineering knowledge is distributed as Agent Skills through the `iqual-developer` Claude plugin in [iqual-ch/claude-plugins](https://github.com/iqual-ch/claude-plugins). + +Don't guess at Drupal-specific implementation details — check the installed skills and read the relevant one first. Core skills include: + +| Skill | Use for | +|---|---| +| `drupal-expert` | Modules, themes, hooks, services, plugins, config schema, caching, security, Drush codegen, version compatibility. | +| `drupal-testing` | Writing/placing/running PHPUnit + DTT tests, deciding what belongs in this project vs. a shared test suite vs. upstream OSS. | +| `drupal-contrib` | Changing a Composer dependency (contrib module/theme, iqual package): source checkouts, GitHub vs. Drupal.org issue-fork workflows, safe Composer handling. | + +The plugin is the canonical source — it may ship more (or newer) skills than listed here. Project-specific skills, if any, live in `.agents/skills/` in this repository. + +## Where code belongs + +Code specific to this project lives in this repository (custom modules/themes, config, project-level tests). If a change is actually about fixing or extending a dependency's own behavior — a contributed package or an internal/shared package — don't work around it here: contribute the fix upstream and consume the update instead of patching it locally. Decision rules live in the `drupal-expert` skill; the hands-on workflow for changing a dependency lives in the `drupal-contrib` skill — read it **before** editing any composer-installed code. +{% if (locations['project-root'] ~ '/docs') is existing_file %} + +## Project documentation + +Project-specific documentation lives in [`docs/`](./docs). Read it before making architectural decisions — it takes precedence over the generic guidance in this file. +{% endif %} + +## Definition of done + +- Implementation matches the described scope. +- `make lint` passes{% if workflows.phpunit %}; relevant tests exist and pass (see the `drupal-testing` skill){% endif %}. +- PR is linked to a Jira issue, reviewed, and approved before merging. +- Config exported (`drush cex`) if site configuration changed. +- Documentation updated if behavior or setup changed. +{% endif %} diff --git a/composer.json b/composer.json index 117f155..2023128 100644 --- a/composer.json +++ b/composer.json @@ -35,6 +35,11 @@ "vrt": true, "phpunit": false }, + "ai": { + "agents": true, + "claude": true, + "copilot": true + }, "deployment": "platform.sh", "platformsh_config": { "region": "ch-1", diff --git a/docs/ai.md b/docs/ai.md new file mode 100644 index 0000000..4497eb4 --- /dev/null +++ b/docs/ai.md @@ -0,0 +1,46 @@ +# AI Integration + +The Drupal Platform scaffolds an AI/agent integration into every project so that coding agents (Claude Code, GitHub Copilot coding agent, and other AGENTS.md-compatible tools) get project context, shared skills and a working environment out of the box. + +The integration consists of three managed assets, each of which can be disabled individually with a [package variable](./configuration.md#drupal-platform-package-variables): + +| Asset | Variable | Purpose | +|---|---|---| +| `AGENTS.md` | `ai.agents` | Generated agent instruction file with project facts, commands and conventions. | +| `.claude/settings.json` | `ai.claude` | Recommends the `iqual-developer` Claude Code plugin (marketplace: [iqual-ch/claude-plugins](https://github.com/iqual-ch/claude-plugins)). | +| `.github/workflows/copilot-setup-steps.yml` | `ai.copilot` | Prepares the GitHub Copilot coding agent's cloud environment. | + +## AGENTS.md + +`AGENTS.md` is a **replaced** (fully managed) asset: it is templated from the package variables (PHP/DB versions, deployment type, enabled workflows, URLs, …) and overwritten on every scaffold run. Do not edit it in the project. + +Project-specific knowledge belongs next to it instead: + +* **`docs/`** — project-specific documentation. If the directory exists, `AGENTS.md` automatically points agents to it (re-run `composer project:scaffold` after creating it). +* **`.agents/skills/`** — project-specific [Agent Skills](https://agentskills.io) committed to the repository. + +## Shared skills + +Reusable Drupal engineering knowledge (skills such as `drupal-expert`, `drupal-testing`, `drupal-contrib`) is **not** committed to projects. It is maintained centrally in [iqual-ch/claude-plugins](https://github.com/iqual-ch/claude-plugins) and distributed per agent: + +* **Claude Code**: the scaffolded `.claude/settings.json` registers the `iqual` marketplace and enables the `iqual-developer` plugin — developers are prompted once to install it when they open the repository. +* **GitHub Copilot coding agent**: `copilot-setup-steps.yml` installs the plugin's skills into `.agents/skills/` (excluded from git via `.git/info/exclude`) before the agent starts. +* **Other agents/CI**: use the reusable [`install-skills` action](https://github.com/iqual-ch/claude-plugins#readme). + +This keeps skills versioned and updated in one place, instead of drifting copies in every project repository. + +## Copilot coding agent environment + +The Copilot cloud agent runs `.github/workflows/copilot-setup-steps.yml` before it starts working (the job must be named exactly `copilot-setup-steps`). The scaffolded workflow: + +1. Installs the shared agent skills. +2. Installs the project locally (DDEV runtime + Drupal, using the `SSH_KEY` secret to sync from the SPOT) — `platform.sh` deployments only. +3. **Removes the SSH key** and verifies the remote environment is unreachable, so the agent has a full local site and toolchain but no access to remote environments. + +For `local-only` deployments only the skills are installed. + +The workflow respects the `workflows.runner` variable for custom runner labels and can be disabled entirely with `ai.copilot: false`. + +## Claude Code settings + +`.claude/settings.json` is a **merged** asset: the plugin recommendation keys are merged into the file, while any additional project-specific settings (e.g. `permissions`) are preserved. Local per-developer overrides belong in `.claude/settings.local.json` (git-ignored by the scaffolded `.gitignore`). diff --git a/docs/automation.md b/docs/automation.md index 8c29678..d7c7dc4 100644 --- a/docs/automation.md +++ b/docs/automation.md @@ -10,6 +10,7 @@ There are multiple GitHub Action workflows for running common automation tasks. * [PHPUnit: Unit Testing](#phpunit-unit-testing) * [PHPUnit: Functional Testing](#phpunit-functional-testing) * [Visual Regression Testing: Comparing reference website to local test deployment](#visual-regression-testing) +* [Copilot: Set up the GitHub Copilot coding agent environment](#copilot-setup-steps) ## Update Drupal Project @@ -286,4 +287,15 @@ This workflow requires a full build of Drupal. This workflow will install the project in a GitHub Actions runner environment and run a visual regression test using the node package [`iqual/playwright-vrt`](https://github.com/iqual-ch/playwright-vrt). The workflow will look for the website's sitemap, and fall back to crawling to gather relevant links. If the test fails (when there are visual differences between the local and live/reference version), then it will upload a Playwright report as a workflow artifact. -The VRT configuration can be customized in the `playwright-vrt.config.json` file in the root of the project. \ No newline at end of file +The VRT configuration can be customized in the `playwright-vrt.config.json` file in the root of the project. +## Copilot Setup Steps + +* Workflow: `copilot-setup-steps.yml` +* Config variable: `ai.copilot` +* Runs on: + * Manual dispatch + * Automatically by the GitHub Copilot coding agent before it starts working + +This workflow prepares the ephemeral GitHub Actions environment for the [GitHub Copilot coding agent](https://docs.github.com/en/copilot/using-github-copilot/coding-agent). It installs the shared agent skills from [`iqual-ch/claude-plugins`](https://github.com/iqual-ch/claude-plugins) and — for `platform.sh` deployments — a full local Drupal installation. Afterwards the SSH key is removed and verified to be gone, so the agent can build, lint and test the project but cannot reach remote environments. + +See [AI Integration](./ai.md) for the full concept. diff --git a/docs/configuration.md b/docs/configuration.md index b7b91f0..f32ee3a 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -27,6 +27,10 @@ Assets and configuration managed by the Drupal Platform have to be customized us * `workflows.phpunit`: Enable/Add the Drupal testing workflow * `workflows.vrt`: Enable/Add the visual regression testing workflow * `workflows.runner`: Custom GitHub Actions runner label for DDEV-based workflows (e.g. `ubuntu-latest-m`, default: `ubuntu-latest`) +* AI integration settings (see [AI Integration](./ai.md)) + * `ai.agents`: Enable/Add the generated `AGENTS.md` agent instruction file (default: `true`) + * `ai.claude`: Enable/Add the Claude Code plugin recommendation in `.claude/settings.json` (default: `true`) + * `ai.copilot`: Enable/Add the GitHub Copilot coding agent setup workflow (default: `true`) * `deployment`: Deployment integration type, see [available remote deployment options](./deployment.md#remote-deployment) * Platform.sh config * `platformsh_config.region`: Deployment region (e.g. `ch-1`) From 57cb0d8f28b1883d9c0290243ac1743698a6aea4 Mon Sep 17 00:00:00 2001 From: Martin Stadelmann Date: Tue, 14 Jul 2026 10:00:23 +0200 Subject: [PATCH 2/4] PF-531: Added `CLAUDE.md` for improved Claude Code support --- assets/replace/CLAUDE.md.twig | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 assets/replace/CLAUDE.md.twig diff --git a/assets/replace/CLAUDE.md.twig b/assets/replace/CLAUDE.md.twig new file mode 100644 index 0000000..2504ea3 --- /dev/null +++ b/assets/replace/CLAUDE.md.twig @@ -0,0 +1,4 @@ +{% if ai.claude %} +@AGENTS.md + +{% endif %} From 295ecaebd2e3e53e276f59c93d300747f3c4c6d2 Mon Sep 17 00:00:00 2001 From: martinstadelmann Date: Tue, 14 Jul 2026 10:07:48 +0200 Subject: [PATCH 3/4] PF-531: Documented the `CLAUDE.md` asset --- README.md | 1 + docs/ai.md | 3 +++ docs/configuration.md | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 48b4217..977dcdb 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,7 @@ assets/replace/ │ ├── upgrade.yml.twig │ └── visual-regression-testing.yml.twig ├── AGENTS.md.twig +├── CLAUDE.md.twig ├── Makefile ├── README.md.twig ├── solr diff --git a/docs/ai.md b/docs/ai.md index 4497eb4..020254d 100644 --- a/docs/ai.md +++ b/docs/ai.md @@ -8,6 +8,7 @@ The integration consists of three managed assets, each of which can be disabled |---|---|---| | `AGENTS.md` | `ai.agents` | Generated agent instruction file with project facts, commands and conventions. | | `.claude/settings.json` | `ai.claude` | Recommends the `iqual-developer` Claude Code plugin (marketplace: [iqual-ch/claude-plugins](https://github.com/iqual-ch/claude-plugins)). | +| `CLAUDE.md` | `ai.claude` | Claude Code entry point that imports `AGENTS.md`. | | `.github/workflows/copilot-setup-steps.yml` | `ai.copilot` | Prepares the GitHub Copilot coding agent's cloud environment. | ## AGENTS.md @@ -44,3 +45,5 @@ The workflow respects the `workflows.runner` variable for custom runner labels a ## Claude Code settings `.claude/settings.json` is a **merged** asset: the plugin recommendation keys are merged into the file, while any additional project-specific settings (e.g. `permissions`) are preserved. Local per-developer overrides belong in `.claude/settings.local.json` (git-ignored by the scaffolded `.gitignore`). + +`CLAUDE.md` is a **replaced** (fully managed) asset that imports `AGENTS.md` (`@AGENTS.md`), so Claude Code picks up the same instructions as other agents — do not add project-specific content to it. diff --git a/docs/configuration.md b/docs/configuration.md index f32ee3a..218cee9 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -29,7 +29,7 @@ Assets and configuration managed by the Drupal Platform have to be customized us * `workflows.runner`: Custom GitHub Actions runner label for DDEV-based workflows (e.g. `ubuntu-latest-m`, default: `ubuntu-latest`) * AI integration settings (see [AI Integration](./ai.md)) * `ai.agents`: Enable/Add the generated `AGENTS.md` agent instruction file (default: `true`) - * `ai.claude`: Enable/Add the Claude Code plugin recommendation in `.claude/settings.json` (default: `true`) + * `ai.claude`: Enable/Add the Claude Code integration — plugin recommendation in `.claude/settings.json` and `CLAUDE.md` importing `AGENTS.md` (default: `true`) * `ai.copilot`: Enable/Add the GitHub Copilot coding agent setup workflow (default: `true`) * `deployment`: Deployment integration type, see [available remote deployment options](./deployment.md#remote-deployment) * Platform.sh config From 3d28785ea088c91a331fbe71740a9dbf8fe861a7 Mon Sep 17 00:00:00 2001 From: martinstadelmann Date: Tue, 14 Jul 2026 10:35:05 +0200 Subject: [PATCH 4/4] PF-531: Nudged agents towards human review before publishing --- assets/replace/AGENTS.md.twig | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/replace/AGENTS.md.twig b/assets/replace/AGENTS.md.twig index 3ad2271..5c009f2 100644 --- a/assets/replace/AGENTS.md.twig +++ b/assets/replace/AGENTS.md.twig @@ -16,6 +16,7 @@ - **Runtime is already up** in this environment — don't run `make install` unless explicitly asked to (re)provision. Use `ddev drush status` to check site state first. - **Workflow:** GitHub Flow — branch → develop → PR → review → merge{% if deployment == "platform.sh" %} → deploy. Only `main` deploys to production{% endif %}. Branch format: `issue/XXX-YYY_description`, commit format: `XXX-YYY: Description`, referencing a Jira issue. - **Never push to `main`, and never merge a PR yourself.** Merging always requires an explicit, human-approved review — even if all checks pass. +- **Prefer review over publishing:** when a user is present, offer to push or open a PR rather than doing it unasked — committing and pushing to your own working branch is always fine when that's how you deliver your work. - **Dependencies:** Composer (PHP) + npm/Bun (JS), locked in lock files. Config changes go through Drupal's config export/import (`drush cex`/`drush cim`), never hand-edited in the target environment. - **CI (GitHub Actions), on every PR:** PHPCS linting{% if workflows.phpunit %}, PHPUnit (unit/kernel/functional/browser){% endif %}{% if workflows.vrt %}, visual regression testing{% endif %}. Must pass before merge.