From d3a3f573e333f6afe1fe2fd983a4db8976d4411e Mon Sep 17 00:00:00 2001 From: Hotragn Pettugani <103170876+Hotragn@users.noreply.github.com> Date: Sun, 20 Sep 2026 19:35:10 -0400 Subject: [PATCH] fix(aider): CONVENTIONS.md is a roster index, not 3.8 million characters of agents (#871) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aider loads a conventions file into context and keeps it there for the whole session — that is what the file is for, and the docs say to load it with --read so prompt caching can hold it. This integration concatenated every agent body into it. At 279 agents that is 3,816,372 characters, roughly a million tokens. No model takes that. Anyone who ran ./scripts/install.sh --tool aider got a CONVENTIONS.md that either blows the context window on the first turn or bills for a million tokens trying. CONVENTIONS.md is now the roster index it was described as: one entry per agent with the name, the description, the division, and the path to the agent file. 96,823 characters, down from 3.8 million. The header explains how to pull a single agent's full instructions into the session: /read-only /path/to/agency-agents/engineering/engineering-frontend-developer.md Naming an agent in a prompt still works the way it did — the description is what the model needed for that, and it is still there. test-convert-outputs.sh now holds the index to being an index: it fails if CONVENTIONS.md grows past 250,000 characters, if it does not list exactly one path per roster agent, or if any path it prints does not resolve. The existing round-trip check on the accumulated file still covers the names and descriptions. --- README.md | 12 ++++++++++-- integrations/README.md | 8 +++++--- integrations/aider/README.md | 31 +++++++++++++++++++++++-------- scripts/convert.sh | 31 +++++++++++++++++++++++-------- scripts/install.sh | 4 +++- scripts/test-convert-outputs.sh | 25 +++++++++++++++++++++++++ 6 files changed, 89 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index ef21203a..685810cb 100644 --- a/README.md +++ b/README.md @@ -733,7 +733,7 @@ The Agency works natively with Claude Code, and ships conversion + install scrip - **[Gemini CLI](https://github.com/google-gemini/gemini-cli)** -- `.md` agent files -> `~/.gemini/agents/` - **[OpenCode](https://opencode.ai)** — `.md` agent files → `.opencode/agents/` - **[Cursor](https://cursor.sh)** — `.mdc` rule files → `.cursor/rules/` -- **[Aider](https://aider.chat)** — single `CONVENTIONS.md` → `./CONVENTIONS.md` +- **[Aider](https://aider.chat)** — `CONVENTIONS.md` roster index → `./CONVENTIONS.md` - **[Windsurf](https://codeium.com/windsurf)** — single `.windsurfrules` → `./.windsurfrules` - **[OpenClaw](https://github.com/openclaw/openclaw)** — `SOUL.md` + `AGENTS.md` + `IDENTITY.md` per agent - **[Qwen Code](https://github.com/QwenLM/qwen-code)** — `.md` SubAgent files → `~/.qwen/agents/` @@ -926,7 +926,10 @@ See [integrations/cursor/README.md](integrations/cursor/README.md) for details.
Aider -All agents are compiled into a single `CONVENTIONS.md` file that Aider reads automatically. +`CONVENTIONS.md` is the roster index — every agent's name, description, and the +path to its full instructions. Aider keeps a conventions file in context for the +whole session, and the 279 bodies together are about a million tokens, so the +file lists the agents rather than inlining them. ```bash cd /your/project @@ -938,6 +941,11 @@ Then reference agents in your Aider session: Use the Frontend Developer agent to refactor this component. ``` +When you want an agent's full instructions, read its file in: +``` +/read-only /path/to/agency-agents/engineering/engineering-frontend-developer.md +``` + See [integrations/aider/README.md](integrations/aider/README.md) for details.
diff --git a/integrations/README.md b/integrations/README.md index 14b12781..0eb1df87 100644 --- a/integrations/README.md +++ b/integrations/README.md @@ -12,7 +12,7 @@ supported agentic coding tools. - **[OpenCode](#opencode)** — `.md` agent files in `opencode/` - **[OpenClaw](#openclaw)** — `SOUL.md` + `AGENTS.md` + `IDENTITY.md` workspaces - **[Cursor](#cursor)** — `.mdc` rule files in `cursor/` -- **[Aider](#aider)** — `CONVENTIONS.md` in `aider/` +- **[Aider](#aider)** — `CONVENTIONS.md` roster index in `aider/` - **[Windsurf](#windsurf)** — `.windsurfrules` in `windsurf/` - **[Kimi Code](#kimi-code)** — YAML agent specs in `kimi/` - **[Qwen Code](#qwen-code)** — project-scoped `.md` SubAgents in `.qwen/agents/` @@ -171,8 +171,10 @@ See [cursor/README.md](cursor/README.md) for details. ## Aider -All agents are consolidated into a single `CONVENTIONS.md` file that Aider -reads automatically when present in your project root. +`CONVENTIONS.md` is the roster index — name, description, and the path to each +agent's full instructions. Aider keeps a conventions file in context for the +whole session, so the file lists the agents instead of inlining a million +tokens of them. ```bash cd /your/project && /path/to/agency-agents/scripts/install.sh --tool aider diff --git a/integrations/aider/README.md b/integrations/aider/README.md index d8158d13..95296003 100644 --- a/integrations/aider/README.md +++ b/integrations/aider/README.md @@ -1,7 +1,18 @@ # Aider Integration -The full Agency roster is consolidated into a single `CONVENTIONS.md` file. -Aider reads this file automatically when it's present in your project root. +`CONVENTIONS.md` is the roster index: every agent's name, what it is for, its +division, and the path to its full instructions. + +## Why an index and not the agents + +Aider keeps a conventions file in context for the whole session — that is the +point of the file. The 279 agent bodies together are about 3.8 million +characters, roughly a million tokens, so a conventions file holding all of them +does not fit in any model and costs a fortune in the attempt. + +The index is about 97,000 characters (~24k tokens). Load it read-only so aider +marks it cacheable, and pull in a single agent's full instructions when you +actually need them. ## Install @@ -11,26 +22,30 @@ cd /your/project /path/to/agency-agents/scripts/install.sh --tool aider ``` -## Activate an Agent +## Use an agent -In your Aider session, reference the agent by name: +Naming the agent is usually enough — its description is already in context: ``` Use the Frontend Developer agent to refactor this component. ``` +When you want the agent's full instructions, read its file into the session. +The index gives you the path: + ``` -Apply the Reality Checker agent to verify this is production-ready. +/read-only /path/to/agency-agents/engineering/engineering-frontend-developer.md ``` -## Manual Usage - -You can also pass the conventions file directly: +## Manual usage ```bash aider --read CONVENTIONS.md ``` +`--read` marks the file read-only and lets aider cache it when prompt caching +is enabled, so the index is not re-sent on every turn. + ## Regenerate ```bash diff --git a/scripts/convert.sh b/scripts/convert.sh index e379d858..2be6de20 100755 --- a/scripts/convert.sh +++ b/scripts/convert.sh @@ -14,7 +14,7 @@ # gemini-cli — Gemini CLI subagent files (~/.gemini/agents/*.md) # opencode — OpenCode agent files (.opencode/agents/*.md) # cursor — Cursor rule files (.cursor/rules/*.mdc) -# aider — Single CONVENTIONS.md for Aider +# aider — Single CONVENTIONS.md roster index for Aider # windsurf — Single .windsurfrules for Windsurf # openclaw — OpenClaw workspaces (integrations/openclaw//SOUL.md) # qwen — Qwen Code SubAgent files (~/.qwen/agents/*.md) @@ -568,11 +568,21 @@ trap 'rm -f "$AIDER_TMP" "$WINDSURF_TMP"' EXIT cat > "$AIDER_TMP" <<'HEREDOC' # The Agency — AI Agent Conventions # -# This file provides Aider with the full roster of specialized AI agents from -# The Agency (https://github.com/msitarzewski/agency-agents). +# The roster of specialized AI agents from The Agency +# (https://github.com/msitarzewski/agency-agents): each one's name, what it is +# for, and where its full instructions live. # -# To activate an agent, reference it by name in your Aider session prompt, e.g.: -# "Use the Frontend Developer agent to review this component." +# Aider keeps a conventions file in context for the whole session, so this is an +# index and not the agents themselves. Inlining every body would make this file +# about 3.8 million characters, which no model will take. +# +# To use an agent: +# 1. Name it in your prompt — "Use the Frontend Developer agent to review +# this component." The description below is usually enough for that. +# 2. For its full instructions, pull the agent file into the session: +# /read-only /path/to/agency-agents/engineering/engineering-frontend-developer.md +# +# Paths below are relative to an agency-agents checkout. # # Generated by scripts/convert.sh — do not edit manually. @@ -590,12 +600,16 @@ HEREDOC accumulate_aider() { local file="$1" - local name description body + local name description source division name="$(get_field "name" "$file")" description="$(get_field "description" "$file")" - body="$(get_body "$file")" + source="${file#"$REPO_ROOT"/}" + division="${source%%/*}" + # One index entry per agent, not the agent. A conventions file is read into + # every request; the bodies together are 3.8 million characters and this + # index is about 90,000. cat >> "$AIDER_TMP" < ${description} -${body} +Division: ${division} +Full instructions: ${source} HEREDOC } diff --git a/scripts/install.sh b/scripts/install.sh index fcd99d71..78cf1122 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -18,7 +18,7 @@ # gemini-cli -- Install agents to ~/.gemini/agents/ # opencode -- Copy agents to .opencode/agents/ in current directory # cursor -- Copy rules to .cursor/rules/ in current directory -# aider -- Copy CONVENTIONS.md to current directory +# aider -- Copy the CONVENTIONS.md roster index to current directory # windsurf -- Copy .windsurfrules to current directory # openclaw -- Copy workspaces to ~/.openclaw/agency-agents/ # qwen -- Copy SubAgents to ~/.qwen/agents/ (user-wide) or .qwen/agents/ (project) @@ -926,6 +926,8 @@ install_aider() { fi install_file "$src" "$dest" ok "Aider: installed -> $dest" + dim " CONVENTIONS.md is the roster index. Load one agent's full instructions with" + dim " /read-only $REPO_ROOT/" $SELECTION_ACTIVE && warn "Aider: single-file format — team/agent filtering N/A (installs the full roster)." warn "Aider: project-scoped. Run from your project root to install there." } diff --git a/scripts/test-convert-outputs.sh b/scripts/test-convert-outputs.sh index 4b5676d4..d3a22e99 100755 --- a/scripts/test-convert-outputs.sh +++ b/scripts/test-convert-outputs.sh @@ -353,6 +353,31 @@ if split_bad: else: ok(f"openclaw: all {N} agents keep every source fenced block whole in one output file") +# --- Layer A (context budget): the Aider index has to stay an index ---------- +# Aider keeps a conventions file in context for the whole session. Inlining the +# agent bodies made CONVENTIONS.md 3.8 million characters, which no model will +# take, so it carries one index entry per agent instead: description plus the +# path to the real file. Two things have to hold for that to be worth anything — +# the file stays small enough to load, and every path it prints resolves. +AIDER_INDEX_CEILING = 250_000 +aider_index = os.path.join(OUT, "aider", "CONVENTIONS.md") +if os.path.isfile(aider_index): + text = open(aider_index, encoding="utf-8").read() + if len(text) > AIDER_INDEX_CEILING: + bad(f"aider: CONVENTIONS.md is {len(text):,} characters — it is loaded into " + f"every request, so it has to stay an index, not the agents themselves") + paths = re.findall(r"^Full instructions: (.+)$", text, re.M) + dangling = sorted({p for p in paths if not os.path.isfile(os.path.join(R, p))}) + if len(paths) != N: + bad(f"aider: CONVENTIONS.md points at {len(paths)} agent files, roster has {N}") + elif dangling: + for d in dangling[:3]: + bad(f"aider: CONVENTIONS.md points at a file that does not exist: {d}") + if len(dangling) > 3: + bad(f"aider: ...and {len(dangling)-3} more dangling paths") + elif len(text) <= AIDER_INDEX_CEILING: + ok(f"aider: index is {len(text):,} characters and all {N} agent paths resolve") + # --- Layer A (app-facing): every SOURCE frontmatter strict-parsed above ------- for m in src_bad[:5]: bad(m) if len(src_bad) > 5: bad(f"...and {len(src_bad)-5} more source frontmatter problems")