--- name: agent-customization description: LLM-powered injection of project context into installed agent templates via `aspens customize agents` triggers: files: - src/commands/customize.js - src/prompts/customize-agents.md keywords: - customize - agents - subagent - agent customization --- You are working on **agent customization** — the feature that reads a project's skills and AGENTS.md, then uses Claude CLI to inject project-specific context into generic agent files in `.claude/agents/`. ## Domain purpose `aspens customize agents` makes generic, bundled agent templates project-aware. It pulls the repo's skills + AGENTS.md as ground truth and asks Claude to add a tech-stack line, 3-5 project conventions, and real commands into each agent — without touching the agent's core logic. ## Business rules / invariants - **Claude-only feature.** Throws `CliError` for Codex-only repos (`config.targets === ['codex']`). Codex CLI has no agent concept. - **Base skill is required.** Pre-flight throws `CliError("Run 'aspens doc init' first — base skill is required for agent context.")` if `.agents/skills/base/SKILL.md` is missing. - **Skills (`.claude/skills/**`) are the single source of truth** for project context. The prompt must not invent other context directories. - **Read-only tools only.** Claude is invoked with `allowedTools: ['Read', 'Glob', 'Grep']` — no edits/writes from the LLM itself. - **Output paths restricted to `.claude/`.** `parseFileOutput()` rejects anything else; `writeSkillFiles(..., { force: true })` does the actual write. ## Non-obvious behaviors - **Frontmatter preservation is split across LLM + code.** The prompt instructs Claude to preserve YAML frontmatter verbatim (including NOT adding a `skills:` line). Then `maybeInjectBaseSkill()` post-processes each returned file to add `skills: [base]` into the frontmatter — this keeps agents valid even when installed via `aspens add agent` without a prior `doc init`. - **`--reset` semantics:** without `--reset`, agents that already declare `skills:` are left alone; with `--reset`, any existing `skills:` line is overwritten to `skills: [base]`. Used to roll out v0.8 upgrades to previously-customized agents. - **`## Project context` block is verbatim-preserved** by the prompt — it carries conditional read instructions for code-map / domain skills. - **AGENTS.md is truncated at 3000 chars** in `gatherProjectContext()`; skills are passed in full. - **Agent discovery:** `findAgents()` recursively walks `.claude/agents/`, extracts `name:` via regex, falls back to filename if missing. - **Default timeout 300s** via `resolveTimeout(options.timeout, 300)`; `ASPENS_TIMEOUT` env var honored with warning on invalid value. ## Critical files (purpose, not inventory) - `src/commands/customize.js` — orchestrator: preflight, agent discovery, context gathering, per-agent Claude calls, post-LLM `skills: [base]` injection, write. - `src/prompts/customize-agents.md` — system prompt; enforces frontmatter + `## Project context` preservation and bans file-inventory / hub-ranking output. ## Critical Rules - **Never let the LLM emit a `skills:` line** — the prompt forbids it and the code adds it. If you change one, change both. - **Never weaken path sanitization** — only `.claude/` paths may be written. - **Never duplicate file-inventory or hub-ranking output** in customized agents — the graph hook supplies that dynamically. - **Do not bypass the base-skill preflight** — agents without base context regress to generic behavior. --- **Last Updated:** 2026-05-11