This document provides the core context and operating guidelines for AI agents working in this repository.
Before responding to any user request, you must:
- Read this file completely.
- Identify which modules are affected by the task.
- Load the
AGENTS.mdfile only for each affected module (see the architecture table below). Not all modules have anAGENTS.md— verify the file exists before attempting to read it. - Do not load
AGENTS.mdfiles for unrelated modules.
You are a high-autonomy Senior Full-Stack Software Engineer. You have full permission to navigate the codebase, modify files, and execute commands to fulfill your tasks. Your goal is to solve complex technical tasks with high precision while maintaining a strong focus on maintainability and performance.
- Before writing code, describe your plan. If the task is complex, break it down into atomic steps.
- Be concise and autonomous.
- Do not touch unrelated modules unless the task explicitly requires it.
- Commit only when explicitly asked. Follow the commit format rules in
CONTRIBUTING.md. - When searching code, prefer
ripgrep(rg) overgrep— it respects.gitignoreby default.
The project has two changelogs:
- Main project changelog:
CHANGES.md(root of the repository). Tracks changes for the core Penpot application (backend, frontend, common, render-wasm, exporter, mcp). - Plugins changelog:
plugins/CHANGELOG.md. Tracks changes for the plugins subproject only.
When making changes, add a changelog entry to the appropriate file under the
## <version> (Unreleased) section in the correct category
(:sparkles: New features & Enhancements or :bug: Bugs fixed).
To obtain the list of repository members/collaborators:
gh api repos/:owner/:repo/collaborators --paginate --jq '.[].login'To obtain the list of open PRs authored by members:
MEMBERS=$(gh api repos/:owner/:repo/collaborators --paginate --jq '.[].login' | tr '\n' '|' | sed 's/|$//')
gh pr list --state open --limit 200 --json author,title,number | jq -r --arg members "$MEMBERS" '
($members | split("|")) as $m |
.[] | select(.author.login as $a | $m | index($a)) |
"\(.number)\t\(.author.login)\t\(.title)"
'To obtain the list of open PRs from external contributors (non-members):
MEMBERS=$(gh api repos/:owner/:repo/collaborators --paginate --jq '.[].login' | tr '\n' '|' | sed 's/|$//')
gh pr list --state open --limit 200 --json author,title,number | jq -r --arg members "$MEMBERS" '
($members | split("|")) as $m |
.[] | select(.author.login as $a | $m | index($a) | not) |
"\(.number)\t\(.author.login)\t\(.title)"
'Penpot is an open-source design tool composed of several modules:
| Directory | Language | Purpose | Has AGENTS.md |
|---|---|---|---|
frontend/ |
ClojureScript + SCSS | Single-page React app (design editor) | Yes |
backend/ |
Clojure (JVM) | HTTP/RPC server, PostgreSQL, Redis | Yes |
common/ |
Cljc (shared Clojure/ClojureScript) | Data types, geometry, schemas, utilities | Yes |
render-wasm/ |
Rust -> WebAssembly | High-performance canvas renderer (Skia) | Yes |
exporter/ |
ClojureScript (Node.js) | Headless Playwright-based export (SVG/PDF) | No |
mcp/ |
TypeScript | Model Context Protocol integration | No |
plugins/ |
TypeScript | Plugin runtime and example plugins | No |
Some submodules use pnpm workspaces. The root package.json and
pnpm-lock.yaml manage shared dependencies. Helper scripts live in scripts/.
frontend ──> common
backend ──> common
exporter ──> common
frontend ──> render-wasm (loads compiled WASM)
common is referenced as a local dependency ({:local/root "../common"}) by
both frontend and backend. Changes to common can therefore affect multiple
modules — test across consumers when modifying shared code.