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
5 changes: 1 addition & 4 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,11 @@
],
"service": "php",
"workspaceFolder": "/app",
"features": {
"ghcr.io/devcontainers/features/node:1": {}
},
"remoteUser": "nonroot",
"postCreateCommand": "npm install -g intelephense",
"customizations": {
"vscode": {
"extensions": [
"symfony.language-tools",
"bmewburn.vscode-intelephense-client",
"xdebug.php-debug"
],
Expand Down
23 changes: 22 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ CMD [ "frankenphp", "run", "--config", "/etc/frankenphp/Caddyfile" ]
# Dev FrankenPHP image
FROM frankenphp_base AS frankenphp_dev

# Repeated because hadolint doesn't inherit the SHELL of the parent stage
SHELL ["/bin/bash", "-euxo", "pipefail", "-c"]

ENV APP_ENV=dev
ENV XDEBUG_MODE=off
ENV FRANKENPHP_WORKER_CONFIG=watch
Expand All @@ -63,13 +66,31 @@ RUN <<-EOF
git config --system --add safe.directory /app
EOF

# Symfony Language Server, for editors and AI agents that don't bundle it themselves
# https://github.com/symfony/language-tools
ARG TARGETARCH
RUN <<-EOF
case "$TARGETARCH" in
amd64) lsp_arch=x64 ;;
arm64) lsp_arch=arm64 ;;
*) echo "unsupported architecture: $TARGETARCH"; exit 1 ;;
esac
# The release URL redirects to the latest tag, no API token nor rate limit involved
lsp_version=$(basename "$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/symfony/language-tools/releases/latest)")
curl -fsSL "https://github.com/symfony/language-tools/releases/download/$lsp_version/symfony-lsp-$lsp_version-linux-$lsp_arch.tar.gz" \
| tar -xz -C /usr/local/bin --strip-components=1 --wildcards '*/symfony-lsp'
EOF

COPY --link frankenphp/conf.d/20-app.dev.ini $PHP_INI_DIR/app.conf.d/

CMD [ "frankenphp", "run", "--config", "/etc/frankenphp/Caddyfile", "--watch" ]

# Builder for the prod FrankenPHP image
FROM frankenphp_base AS frankenphp_prod_builder

# Repeated because hadolint doesn't inherit the SHELL of the parent stage
SHELL ["/bin/bash", "-euxo", "pipefail", "-c"]

ENV APP_ENV=prod

RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
Expand Down Expand Up @@ -97,7 +118,7 @@ RUN <<-EOF
EOF

# Collect shared libraries needed by FrankenPHP and PHP extensions
# hadolint ignore=DL3008,SC3054,DL4006
# hadolint ignore=DL3008,SC3054
RUN <<-'EOF'
apt-get update
apt-get install -y --no-install-recommends libtree
Expand Down
48 changes: 46 additions & 2 deletions docs/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@ There is no official Dev Container feature for OpenCode yet, so install the CLI

```jsonc
{
// Install the CLI on container creation (alongside the existing intelephense install)
"postCreateCommand": "npm install -g intelephense && curl -fsSL https://opencode.ai/install | bash",
// Install the CLI on container creation
"postCreateCommand": "curl -fsSL https://opencode.ai/install | bash",
"customizations": {
"vscode": {
"extensions": [
"sst-dev.opencode",
"symfony.language-tools",
"bmewburn.vscode-intelephense-client",
"xdebug.php-debug",
],
Expand Down Expand Up @@ -78,6 +79,7 @@ Visual Studio Code extension. Edit `.devcontainer/devcontainer.json`:
"vscode": {
"extensions": [
"anthropic.claude-code",
"symfony.language-tools",
"bmewburn.vscode-intelephense-client",
"xdebug.php-debug",
],
Expand All @@ -92,6 +94,48 @@ Without the firewall, this is all you need. To let Claude Code run autonomously,
[network sandbox](#optional-network-sandbox) first and add `anthropic.com`, `sentry.io`, and
`statsig.com` to the allowlist.

## Symfony Language Server

The dev image ships the [Symfony Language Server](https://github.com/symfony/language-tools) as
`/usr/local/bin/symfony-lsp`. It knows about routes, services, Twig, translations, environment
variables, Messenger, Security, forms, Doctrine and the rest of the framework, so an agent gets
real definitions and diagnostics instead of guesses. It complements a general PHP language server
such as Intelephense, it does not replace it.

The `symfony.language-tools` Visual Studio Code extension bundles its own copy of the server, so
there is nothing to configure there. The binary in the image is for agents and editors that expect
to launch a server themselves.

### With OpenCode

Declare the server in `opencode.json` at the root of the project:

```json
{
"$schema": "https://opencode.ai/config.json",
"lsp": {
"symfony": {
"command": ["symfony-lsp"],
"extensions": [".php", ".twig", ".yaml", ".yml", ".xml"]
}
}
}
```

### From the command line

`symfony-lsp check` reports the same diagnostics without an editor, which is useful to let an agent
verify its own changes:

```console
symfony-lsp check --format=json src/ templates/
```

`--format=github` is also available for CI annotations. The check runs the application to index the
container, so pass `--source-only` when the code cannot be trusted. See
[Introducing symfony-lsp check](https://symfony.com/blog/introducing-symfony-lsp-check-symfony-aware-diagnostics-in-your-ci)
for the full picture.

## Optional: network sandbox

Letting an agent edit files and run commands without confirmation (autonomous, or "YOLO", mode)
Expand Down