From d8fddf99426dbfe1cce0a88ba6880e3b222e827a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Tejero-Cantero?= <807608+alvorithm@users.noreply.github.com> Date: Tue, 22 Sep 2026 08:43:25 +0200 Subject: [PATCH] :recycle: Keep agent content on standard paths, drop client shims (#11719) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :recycle: Keep agent content on standard paths, drop client shims Agent guidance stays where the ecosystem reads it: AGENTS.md at the root and the skills in .agents/skills. Codex scans .agents/skills and follows symlinks, opencode lists it among its discovery locations, and Cursor, Zed, Warp, Cline and Amp use the same project path. Claude Code reads neither, so it needed CLAUDE.md and .claude/skills. Committing those two paths is not a neutral default. Both are gitignored, git treats ignored files as expendable, and a checkout that needs the path deletes whatever a contributor keeps there without a warning. Neither sparse-checkout nor skip-worktree offers a way out, because both make the next pull abort. The two links are now created by the contributor, once per clone, and documented in .agents/README.md. AGENTS.local.md and .agents/local/ give every client one personal layer that composes on top of the shared one, instead of one file per client inside the repository. sync-workspace seeds the two links for ws1 and above, the way it already seeds the gitignored config.js, so the devenv workflow does not change. AI-assisted-by: claude-opus-5 Signed-off-by: Álvaro Tejero Cantero * :recycle: Lean on Claude Code reading AGENTS.md, drop the pointer Claude Code 2.1.277 reads AGENTS.md through the built-in agents-md plugin, whose default mode loads it in a project that has no instruction file of its own. Deleting the committed CLAUDE.md is what turns that on here, so the pointer a Claude user had to create is no longer part of the setup, and only the .claude/skills link remains: the plugin covers instruction files, and skills still load from that path alone. The instruction that replaces the pointer is a removal. A CLAUDE.md, .claude/CLAUDE.md or CLAUDE.local.md anywhere from the root down to the working directory hands the project back to CLAUDE.md and hides this repository's AGENTS.md, while .claude/rules/*.md and the person's own ~/.claude/CLAUDE.md do not count and stay usable for personal steering. CLAUDE.local.md joins the ignore list, and a hard rule in AGENTS.md forbids all three. The docs name the version, say the fallback has not reached Bedrock, Vertex or Foundry, and say the stable auto-update channel runs about a week behind the default one, so the symlink is documented as the stopgap for those cases and as something to drop afterwards. sync-workspace seeds the skills link alone for the same reason. AI-assisted-by: claude-opus-5 Signed-off-by: Álvaro Tejero Cantero * :paperclip: Remove skills directory mention on AGENTS.md Signed-off-by: Andrey Antukh --------- Signed-off-by: Álvaro Tejero Cantero Co-authored-by: Andrey Antukh --- .agents/README.md | 33 ++++++++++++++++++++++++ .claude/skills | 1 - .gitignore | 3 +++ AGENTS.md | 6 ++--- CLAUDE.md | 3 --- CONTRIBUTING.md | 16 ++++++++++++ docs/technical-guide/developer/devenv.md | 6 +++++ manage.sh | 11 ++++++++ 8 files changed, 72 insertions(+), 7 deletions(-) delete mode 120000 .claude/skills delete mode 100644 CLAUDE.md diff --git a/.agents/README.md b/.agents/README.md index 7a8631170e..1f4e2364c5 100644 --- a/.agents/README.md +++ b/.agents/README.md @@ -34,6 +34,39 @@ the "agentic devenv" (`--agentic`) from the technical guide, which runs the client outside devenv and wires it in over MCP — here the client lives inside the sandboxed devenv docker. +The content lives at the paths the ecosystem agreed on: `AGENTS.md` at the +repository root and the skills in this folder, `.agents/skills/`. Codex, +opencode, Cursor, Zed, Amp, omp and pi read both without any setup. + +Claude Code reads `AGENTS.md` from 2.1.277, through the built-in +`agents-md` plugin, but only in a project that has no instruction file of +its own. A `CLAUDE.md`, `.claude/CLAUDE.md` or `CLAUDE.local.md` anywhere +from the repository root down to your working directory hands the whole +project back to `CLAUDE.md`, and this file's guidance stops arriving. Keep +none of them here. Personal steering goes in `AGENTS.local.md`, or in +`.claude/rules/*.md` and `~/.claude/CLAUDE.md`, which the plugin does not +count and which therefore leave the project guidance in place. + +Skills are untouched by that plugin and still load from `.claude/skills` +alone, which is not committed, so link it once per clone: + +```bash +mkdir -p .claude && ln -s ../.agents/skills .claude/skills +# or, with Node: +npx skills add ./.agents/skills --agent claude-code +``` + +Below 2.1.277, and on Bedrock, Vertex and Foundry where the fallback has +not arrived, add `ln -s AGENTS.md CLAUDE.md` and drop it once your client +has the feature. The auto-update channel decides when that is: `latest`, +the default, has it; `stable` runs about a week behind; Homebrew and the +Linux packages update by hand. Personal skills go in +`.agents/local/skills//`. + +Every one of those paths is gitignored, so your own files keep working and +no checkout overwrites them. Inside the devenv, `ws0` sees the links from +the live checkout, and `ws1` and above are seeded on their first sync. + Unlike the agentic devenv, running the client inside the devenv docker gives it full access to the live environment: every dependency already resolved by the image, so the agent can write diff --git a/.claude/skills b/.claude/skills deleted file mode 120000 index 2b7a412b8f..0000000000 --- a/.claude/skills +++ /dev/null @@ -1 +0,0 @@ -../.agents/skills \ No newline at end of file diff --git a/.gitignore b/.gitignore index 28e38db575..50c8d19494 100644 --- a/.gitignore +++ b/.gitignore @@ -98,10 +98,13 @@ opencode.json *.iml /.claude /CLAUDE.md +/CLAUDE.local.md /.playwright-mcp /.devenv/mcp/ /opencode.json /.agents/plans +/.agents/local/ +/AGENTS.local.md /.opencode/reports /.opencode/prompts /.ci-logs diff --git a/AGENTS.md b/AGENTS.md index 1a9db621d8..fa95b4c045 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,9 +14,9 @@ - **Never pipe test output directly to filters** (`| head`, `| tail`, `| grep`, etc.). Always redirect to a file first: `command > /tmp/output.txt 2>&1`, then read/grep the file. This prevents hiding test failures. See `mem:testing` for details. -- **`.claude/skills` is a symlink to `.agents/skills`.** - Edit skills only in their canonical location (`.agents/skills`); never edit - through `.claude/skills`. +- **Read `AGENTS.local.md` after this file whenever it exists.** It carries + the developer's steering for this project and overrides this file on prose style and + workflow, but not on project policy, where instructions here prevail. - **Commit message body lines MUST wrap at ≤76 chars** (subject ≤70 chars) and the commit MUST pass `./scripts/check-commit` with exit code 0 before you consider it done. This is mechanically checked — do not eyeball it. diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 70bf134a48..0000000000 --- a/CLAUDE.md +++ /dev/null @@ -1,3 +0,0 @@ -Read and follow the instructions in `AGENTS.md`. - -Treat `AGENTS.md` as the canonical project instruction file. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a7aeac3f3f..b4e2618f4e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,6 +38,22 @@ Center](https://help.penpot.app/). - **Issue tracker**: We use [GitHub Issues](https://github.com/penpot/penpot/issues) for public bugs and [Taiga](https://tree.taiga.io/project/penpot/) for internal project management. Changelog entries reference both. +- **AI coding agents**: guidance lives in `AGENTS.md` and the skills in + `.agents/skills/`, which Codex, opencode, Cursor, Zed, Amp, omp and pi read + without setup. Claude Code reads `AGENTS.md` from 2.1.277, but only in a + project that has no instruction file of its own, so keep no `CLAUDE.md`, + `.claude/CLAUDE.md` or `CLAUDE.local.md` in your checkout: any one of them + hides this repository's guidance from your session. Skills still load from + `.claude/skills` alone, so link it once per clone with + `mkdir -p .claude && ln -s ../.agents/skills .claude/skills`, or with + `npx skills add ./.agents/skills --agent claude-code`. Below 2.1.277, and + on Bedrock, Vertex and Foundry where the fallback has not arrived, add + `ln -s AGENTS.md CLAUDE.md` and remove it once your client has the + feature. Personal instructions belong in `AGENTS.local.md`, personal + skills in `.agents/local/skills/`, and personal Claude steering in + `.claude/rules/*.md`, which Claude loads beside the project instructions + without switching the fallback off. Every one of these paths is + gitignored. ## Reporting Bugs diff --git a/docs/technical-guide/developer/devenv.md b/docs/technical-guide/developer/devenv.md index d6cc65e89a..12a7e53ee8 100644 --- a/docs/technical-guide/developer/devenv.md +++ b/docs/technical-guide/developer/devenv.md @@ -90,6 +90,12 @@ the frontend's MCP flag) is copied into each workspace on its initial sync only. After that the developer maintains it in each workspace; subsequent `--sync` runs leave the workspace copy alone. +The Claude Code skills link, `.claude/skills`, is seeded the same way: it is +gitignored, so the sync does not carry it, and the first sync creates it as a +link to `.agents/skills`. A workspace that already has that path keeps what it +has. No `CLAUDE.md` is created, because Claude Code reads `AGENTS.md` itself +in a project that has none. + Stopping is equally flexible — each workspace is independent. Shared infra stops only when no instances remain running: diff --git a/manage.sh b/manage.sh index efa4da7601..c3fb41d07c 100755 --- a/manage.sh +++ b/manage.sh @@ -511,6 +511,17 @@ function sync-workspace { install -D "$PWD/$cfg" "$workspace/$cfg" fi + # Initial seed of the Claude Code skills link. It is gitignored, so git + # ls-files does not list it, yet Claude Code reads skills from + # .claude/skills alone. Seeded only when absent: afterwards the + # workspace copy belongs to the user, who may keep their own. No + # CLAUDE.md is seeded: Claude Code reads AGENTS.md itself in a project + # that has none, and a CLAUDE.md here would switch that off. + if [[ ! -e "$workspace/.claude/skills" && ! -L "$workspace/.claude/skills" ]]; then + mkdir -p "$workspace/.claude" + ln -s ../.agents/skills "$workspace/.claude/skills" + fi + ( cd "$workspace" git switch -C "${instance}/${CURRENT_BRANCH}" >/dev/null