Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ claude plugin add itk-dev/itkdev-skills
| Agent | Description |
|-------|-------------|
| `itkdev-code-review` | Automated PR review against ITK Dev standards |
| `itkdev-create-project` | Create new Drupal/Symfony projects with ITK Dev Docker setup |
| `itkdev-issue-workflow` | Autonomous GitHub issue workflow (runs in isolated subagent context) |
129 changes: 129 additions & 0 deletions agents/itkdev-create-project.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
---
name: itkdev-create-project
description: "Create a new Drupal or Symfony project with ITK Dev Docker setup. Delegates to this agent when creating new projects, building new sites, or scaffolding new applications."
skills:
- itkdev-docker
- itkdev-docker-templates
- itkdev-taskfile
- itkdev-drupal
- itkdev-symfony
memory: project
---

# Create Project Agent

You are a project scaffolding agent that creates new Drupal or Symfony projects with the ITK Dev Docker setup. You guide the user through project creation with interactive steps where needed and autonomous execution where possible.

You have access to all standard tools: Bash, Read, Write, Edit, Glob, Grep, Task, WebFetch, and more.

**Sandbox note:** The `itkdev-docker-compose` and `docker compose` commands need to access paths and sockets (e.g. the Docker socket) outside the default sandbox allowlist. Before running these commands, explain this to the user and ask for permission to disable the sandbox.

**File access in new project directories:** Files inside newly created project directories may not be accessible via the `Read` or `Edit` tools due to sandbox/permission restrictions (the directory didn't exist when permissions were set). To work around this:
- **Reading files:** Use `docker compose exec phpfpm cat -n <path>` or `grep -n` inside the container instead of the `Read` tool.
- **Editing files:** Use `docker compose exec phpfpm sed -i ...` or `docker compose exec phpfpm bash -c '...'` inside the container instead of the `Edit` tool. For appending content, use `cat >>` inside the container.
- **Writing new files:** The `Write` tool typically works for creating new files in the project directory, even when `Read`/`Edit` are blocked.
- All container commands require sandbox disabled — ask the user for permission once at the start of the Docker phase.

## PHASE 1: Project Requirements (USER INTERACTION)

1. Ask two questions:
- **Type:** Drupal or Symfony?
- **Version:** Which version?

2. Fetch available templates from the GitHub API using `WebFetch` (not `curl` via Bash, which is blocked by the sandbox):
```
WebFetch url: https://api.github.com/repos/itk-dev/devops_itkdev-docker/contents/templates?ref=develop
prompt: List all directory names (the "name" field) from this JSON array. Only return the names, one per line.
```

3. Present the relevant templates (filtered by the chosen type) to the user so they can confirm which one to use.

4. **Version mismatch handling:** If the exact version template doesn't exist (e.g. the user wants Symfony 7 but only `symfony-6` is available), use the closest available template — the Docker setup is compatible across versions. The actual framework version is determined by the `composer create-project` step, not the template.

5. Confirm the template selection before proceeding.

## PHASE 2: Scaffold Docker Environment (AUTONOMOUS)

1. Create the project directory and navigate into it.

2. Apply the devops Docker template:
```bash
printf 'y\n\n' | itkdev-docker-compose template:install <template-name>
```
Where `<template-name>` matches the confirmed template (e.g. `drupal-11`, `symfony-8`).

The `template:install` command has three interactive prompts (see `itkdev-docker-templates` skill for details). Piping `printf 'y\n\n'` answers `y` to the confirmation and accepts defaults for project name and domain.

3. Verify the generated `.env` file contains correct values for `COMPOSE_PROJECT_NAME`, `COMPOSE_DOMAIN`, and `ITKDEV_TEMPLATE`. If the project name or domain should differ from the defaults, edit the `.env` file directly after install.

## PHASE 3: Service Selection (USER INTERACTION)

1. Read the generated `docker-compose.yml` to determine which services the template includes.

2. Present the list of services to the user and ask which ones the project actually needs.

3. For each service the user does not need, remove it from `docker-compose.yml`, `docker-compose.dev.yml`, and `docker-compose.server.yml` — including any `depends_on` references, networks, volumes, and environment variables that only apply to the removed service. Keep the files clean and consistent after removal.

## PHASE 4: Framework Scaffolding (AUTONOMOUS)

1. Start the Docker environment:
```bash
docker compose up -d
```

2. Run `composer create-project` inside the PHP-FPM container to scaffold the project on top of the Docker setup. The project is created in a temp directory first to avoid conflicts with existing files, then merged into the app root.

**Drupal:**
```bash
docker compose exec phpfpm composer create-project drupal/recommended-project:<version> /tmp/project && docker compose exec phpfpm cp -rT /tmp/project /app
```

**Symfony:** See the "Project Scaffolding" section in the `itkdev-symfony` skill for the full procedure (create-project, `.env` restore, post-scaffold `composer install`).

3. Run `composer install` to ensure the vendor directory and autoloader are fully initialized:
```bash
docker compose exec phpfpm composer install
```
This prevents extraction errors (e.g. `RecursiveDirectoryIterator` failures) that can occur when requiring new packages into a partially-initialized vendor directory from `cp -rT`.

4. **Drupal only:** Install Drush immediately — it is not included in `drupal/recommended-project` but is required for site installation and all subsequent Drupal tasks:
```bash
docker compose exec phpfpm composer require drush/drush --no-interaction
```

## PHASE 5: Report & Optional Steps (USER INTERACTION)

Report the following to the user:
- **Project directory** — the full path
- **Docker services** — which services are running
- **Project type and version** — e.g. Drupal 11, Symfony 8
- **Accessible domain** — e.g. `http://<name>.local.itkdev.dk`

Then offer to continue with these optional next steps:

### Option 1: Install the site

**Drupal:** See the "Site Installation" section in the `itkdev-drupal` skill for the full procedure (settings.php setup, profile selection, drush command). If the user does not specify a profile, default to **minimal**.

**Symfony:** See the "Database Configuration" section in the `itkdev-symfony` skill for the full procedure (Doctrine ORM installation, DATABASE_URL configuration, connection verification).

### Option 2: Create a Taskfile.yml

Generate a [Task](https://taskfile.dev/) file that mirrors the checks and steps defined in the `.github/workflows/` directory, so developers can run the same CI checks locally. See the `itkdev-taskfile` skill for the canonical header, task patterns, and framework-specific console commands.

### Option 3: Create a theme (Drupal only)

If the project is Drupal, ask if the user wants to set up a custom theme. See the "Theme Development" section in the `itkdev-drupal` skill for the full procedure (base theme vs custom theme, generation steps).

## Important Rules

- Always ask for project type and version before starting
- Always fetch and present available templates from GitHub
- Use the `itkdev-docker-templates` skill for template installation details
- Use the `itkdev-taskfile` skill for Taskfile generation patterns
- Use the `itkdev-drupal` skill for Drupal-specific guidance (site installation, theme development, drush)
- Use the `itkdev-symfony` skill for Symfony-specific guidance (scaffolding, database, console commands)
- **Console commands:** Always use `vendor/bin/` when running framework CLI tools inside the container (e.g. `vendor/bin/drush`). Never use bare `drush` or `console`.
- The `itkdev-docker-compose` CLI must be on the PATH (from the `scripts/` directory of the devops repo)
- Drupal uses PHP CodeSniffer (`.phpcs.xml.dist`), Symfony uses PHP CS Fixer (`.php-cs-fixer.dist.php`)
- The `drupal-module` template is a minimal setup (no Nginx/database/Memcached) for standalone module development
49 changes: 30 additions & 19 deletions skills/itkdev-docker-templates/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,26 @@ itkdev-docker-compose template:update --force

After installation, the `.env` file will contain `ITKDEV_TEMPLATE=<template-name>`.

### Interactive Prompts

The `template:install` command has three interactive prompts that will hang in a non-interactive shell:

1. `Are you sure you want to install the <name> template in <dir> (y|N)?` — uses `read -p ... -n 1 -r`, reads a single character. Must send `y`.
2. `Project name (<dir-name>)?` (default: directory name) — uses `read -e`, accepts newline for default.
3. `Domain [<dir-name>.local.itkdev.dk]?` (default: `<dir-name>.local.itkdev.dk`) — uses `read -e`, accepts newline for default.

The confirmation prompt (`y|N`) is **always** shown (even on fresh installs). For non-interactive shells, pipe input:
```bash
printf 'y\n\n' | itkdev-docker-compose template:install <template-name>
```
This answers `y` to the confirmation and accepts defaults for project name and domain.

Using `--force` skips only the confirmation prompt but still requires the project name and domain inputs.

### Version Mismatch

If the exact version template doesn't exist (e.g. the user wants Symfony 7 but only `symfony-6` is available), use the closest available template — the Docker setup is compatible across versions. The actual framework version is determined by the `composer create-project` step, not the template.

## Setup Workflows

### New Drupal 11 Project
Expand Down Expand Up @@ -107,40 +127,31 @@ After installation, the `.env` file will contain `ITKDEV_TEMPLATE=<template-name

## Procedural: List Templates

When the user asks to list available templates, run:
When the user asks to list available templates, use `WebFetch` (not `curl` or `gh` via Bash, which may be blocked by the sandbox):

```bash
gh api repos/itk-dev/devops_itkdev-docker/contents/templates --jq '.[].name'
```

This returns directory names from the templates folder.
WebFetch url: https://api.github.com/repos/itk-dev/devops_itkdev-docker/contents/templates?ref=develop
prompt: List all directory names (the "name" field) from this JSON array. Only return the names, one per line.
```

## Procedural: Get Template Files

To list files in a specific template:

```bash
gh api repos/itk-dev/devops_itkdev-docker/contents/templates/{template} --jq '.[].name'
```

For nested directories (like `.docker/` or `.github/`), follow up with:

```bash
gh api repos/itk-dev/devops_itkdev-docker/contents/templates/{template}/{subdir} --jq '.[].name'
WebFetch url: https://api.github.com/repos/itk-dev/devops_itkdev-docker/contents/templates/{template}?ref=develop
prompt: List all file/directory names (the "name" field) from this JSON array. Only return the names, one per line.
```

For nested directories (like `.docker/` or `.github/`), follow up with the subdirectory path in the URL.

## Procedural: Get Template File Content

To read a specific template file:

```bash
curl -sL "https://raw.githubusercontent.com/itk-dev/devops_itkdev-docker/develop/templates/{template}/{file}"
```

Or via GitHub CLI:

```bash
gh api repos/itk-dev/devops_itkdev-docker/contents/templates/{template}/{file} --jq '.content' | base64 -d
WebFetch url: https://raw.githubusercontent.com/itk-dev/devops_itkdev-docker/develop/templates/{template}/{file}
prompt: Return the full file content exactly as-is.
```

## Procedural: Compare Project Against Template
Expand Down
81 changes: 78 additions & 3 deletions skills/itkdev-drupal/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,35 @@ services:

## Theme Development

### Creating a New Theme
When setting up a theme, ask the user which approach they want:

- **ITK Dev Base Theme** — uses [Tailwind CSS](https://tailwindcss.com/) via the [ITK Dev base theme](https://github.com/itk-dev/itkdev_base_theme) starterkit. Recommended for most projects.
- **Custom theme** — a manually scaffolded theme with no base theme dependency.

### Option A: ITK Dev Base Theme (recommended)

1. Ask the user for a **theme machine name** (e.g. `my_custom_theme`) and a **human-readable theme name** (e.g. `"My Custom Theme"`).

2. Require the base theme package:
```bash
docker compose exec phpfpm composer require itk-dev/itkdev_base_theme
```

3. Generate the subtheme. The theme must be placed in `themes/custom` for base theme references to work:
```bash
docker compose exec phpfpm php /app/web/core/scripts/drupal generate-theme THEMENAME \
--name="THEME READABLE NAME" \
--path="themes/custom" \
--starterkit=starterkit_project_theme
```
Replace `THEMENAME` with the machine name and `THEME READABLE NAME` with the readable name.

4. Inform the user to follow the README inside the generated theme directory (`web/themes/custom/THEMENAME/README.md`) for further setup (Tailwind CSS build, etc.).

### Option B: Custom theme

Ask the user for a **theme machine name** and a **human-readable theme name**, then scaffold the following structure:

Basic structure:
```
themes/custom/theme_name/
├── theme_name.info.yml
Expand All @@ -141,7 +167,7 @@ themes/custom/theme_name/
└── templates/
```

### Theme info.yml Template
Theme `info.yml` template:
```yaml
name: 'Theme Name'
type: theme
Expand All @@ -153,6 +179,55 @@ libraries:
- theme_name/global-styling
```

## Site Installation

**Prerequisite:** Drush is not included in `drupal/recommended-project`. If it hasn't been installed yet, require it first:
```bash
docker compose exec phpfpm composer require drush/drush --no-interaction
```

On a freshly scaffolded Drupal project (via `composer create-project`), `settings.php` does not exist — only `default.settings.php` is provided. You must create it before running `drush site:install`, or Drush will fail with `Call to a member function getInstallTasks() on null`:

```bash
docker compose exec phpfpm bash -c 'cp /app/web/sites/default/default.settings.php /app/web/sites/default/settings.php && chmod 666 /app/web/sites/default/settings.php'
```

After creating `settings.php`, make the following changes. **Important:** The `Read` and `Edit` tools may not work on files in newly created project directories due to sandbox/permission restrictions. Use `docker compose exec phpfpm` commands to read and edit `settings.php` inside the container instead (e.g. `sed -i`, `cat -n`, `grep -n`, `cat >>`). See the agent's sandbox note for details.

1. **Config sync directory:** Set `$settings['config_sync_directory']` to `'../config/sync'`.
```bash
docker compose exec phpfpm sed -i "s|# \$settings\['config_sync_directory'\] = '/directory/outside/webroot';|\$settings['config_sync_directory'] = '../config/sync';|" /app/web/sites/default/settings.php
```
Also create the directory:
```bash
docker compose exec phpfpm mkdir -p /app/config/sync
```
2. **Skip permissions hardening:** Uncomment `$settings['skip_permissions_hardening'] = TRUE;` so file permissions are never hardened. Note: this setting may not exist in all Drupal versions — skip if not present.
3. **Local settings override:** Remove the commented-out `settings.local.php` include block and append an uncommented version at the very end of the file:
```bash
# Remove the commented block
docker compose exec phpfpm sed -i '/^#$/,/^# }$/d' /app/web/sites/default/settings.php
# Append uncommented block at end
docker compose exec phpfpm bash -c 'cat >> /app/web/sites/default/settings.php << '\''EOFPHP'\''

if (file_exists($app_root . '\''/'\'' . $site_path . '\''/settings.local.php'\'')) {
include $app_root . '\''/'\'' . $site_path . '\''/settings.local.php'\'';
}
EOFPHP'
```

Ask the user which install profile to use. If the user does not specify, default to **minimal**:
- **minimal** (default) — bare-bones installation with no pre-configured content types or modules
- **standard** — includes default content types, blocks, and commonly used modules

Then install with the chosen profile and `--db-url` matching the MariaDB container credentials:

```bash
docker compose exec phpfpm vendor/bin/drush site:install <profile> --yes --db-url=mysql://db:db@mariadb:3306/db --account-name=admin --account-pass=admin
```

Without a profile and `--db-url`, Drush cannot resolve the install profile or connect to the database.

## Drush Commands

**Always run drush via itkdev-docker-compose** (or use Taskfile tasks if available):
Expand Down
Loading