feat(skills): agent-skills (SKILL.md) format support for review/improve/describe#2385
feat(skills): agent-skills (SKILL.md) format support for review/improve/describe#2385IsmaelMartinez wants to merge 6 commits into
Conversation
Pointer file for Claude Code: high-level prompt-building hot path, settings/Dynaconf flow, git-provider abstraction, and the unit-test command. Defers to AGENTS.md for the full repo guidelines.
Discover SKILL.md files from configured filesystem paths, parse YAML frontmatter, and inject the combined name/description/body block into the review, improve, and describe prompts as a separate skills_context variable (rendered above extra_instructions so user-supplied instructions still take precedence). Activation is description-based: every discovered skill is included with its "Use when..." description, and the model decides which guidance to apply. A token budget (skills.max_skills_tokens, default 4000) caps the injected block, dropping skills from the end if exceeded. Disabled by default; enable via [skills] in configuration. No new dependencies (uses stdlib yaml). Refs The-PR-Agent#2384.
Apply post-review fixes to the agent-skills loader:
* Inline non-SKILL.md resources. Every *.md file in the skill directory
tree (including references/ subdirectories) is now gathered and
appended to the skill body in the prompt. This is the closest analogue
to the standard's progressive-disclosure model under PR-Agent's
single-shot prompt architecture, where the model has no opportunity
to Read files mid-turn. scripts/ and assets/ subdirectories remain
excluded -- the implementation supports text-only skills, and skills
that depend on script execution will not work here. Documented in
the loader docstring and configuration.toml.
* Treat nested SKILL.md files as independent skills: their subtree is
not absorbed into the outer skill's resources.
* Switch get_skills_context to attribute-style settings access for
consistency with the rest of the codebase, narrow the catch to only
the int() coercion (programmer errors now surface), and expand
$ENV_VAR alongside ~ in configured paths.
* Bump default max_skills_tokens from 4000 to 8000 (a typical multi-skill
setup needed lifting; still user-overridable per repo).
* Clean up format_skills_context truncation (drop dead variable, slice
cleanly against the budget).
* Add tests for: Jinja2 syntax in skill bodies (confirms substitution is
single-pass and {% raw %} wrapping is unnecessary), env-var and ~
path expansion, sibling .md and references/ resource gathering,
scripts/ + assets/ exclusion, nested SKILL.md isolation, resource
rendering, and resource-aware budget enforcement.
Review Summary by QodoAdd agent-skills (SKILL.md) format support for review/improve/describe tools
WalkthroughsDescription• Implements agent-skills (SKILL.md) format support for /review, /improve, and /describe tools • Discovers and parses SKILL.md files from configured filesystem paths with YAML frontmatter • Inlines sibling *.md resources (including references/ subdirectories) into skill context • Injects formatted skills into prompts via skills_context variable, positioned above extra_instructions • Disabled by default; gated behind [skills] enabled = true configuration with token budget control Diagramflowchart LR
A["Configured filesystem paths"] -->|discover_skills| B["SKILL.md files"]
B -->|parse frontmatter| C["Skill metadata<br/>name + description"]
B -->|gather resources| D["Sibling *.md files<br/>references/"]
C --> E["Format skills context"]
D --> E
E -->|inject via skills_context| F["Review/Improve/Describe<br/>prompts"]
F -->|render with Jinja2| G["Model receives<br/>formatted skills"]
File Changes1. pr_agent/algo/skills_loader.py
|
Code Review by Qodo
Context used 1. No skills.installed support
|
* Cache get_skills_context() via starlette_context so the three tools
(review/improve/describe) share one discovery + parse + format per
request, mirroring the apply_repo_settings cache pattern.
* Replace the 4-chars-per-token heuristic with TokenEncoder and
clip_tokens (already in pr_agent.algo) so the budget reflects actual
model tokens.
* Log when remaining skills are dropped after the first-skill
truncation path (operational visibility).
* Remove unused Skill.path field; pass file_path directly to
_gather_resources at construction.
* Drop dead inner sorted(files) in _gather_resources -- the final
resources.sort() is authoritative.
* Fix asymmetric Jinja whitespace strip in pr_reviewer_prompts.toml
and pr_description_prompts.toml ({% endif %} -> {%- endif %}).
* Trim WHAT-narrating comments per code-review pass.
Address Qodo review findings on PR The-PR-Agent#2385: * Security: apply_repo_settings now refuses to apply the [skills] section from a repo's .pr_agent.toml. skills.paths walks the host filesystem and inlines file contents into the LLM prompt; without this gate, a malicious repo could set paths = ["/etc"] or ["~/.ssh"] in its repo settings and exfiltrate sensitive host files. The skills section is now host-level configuration only (global settings, env vars, CLI). * Reliability: per-resource file size cap (256 KB). _gather_resources now stats each candidate before opening; oversized files are skipped with a warning log rather than read into memory. Defends against pathological skill directories or accidental inclusion of generated docs that would spike memory during tool init. * Tests for both behaviours. The third Qodo finding — that get_skills_context injects every skill unconditionally rather than selecting a relevant subset based on PR context — is the documented progressive-disclosure limitation called out in the PR description's Limitations section and detailed in the out-of-band design note. A two-pass selector is the architecturally correct follow-up and is intentionally out of scope here.
|
Persistent review updated to latest commit 10f3591 |
UnicodeDecodeError is not a subclass of OSError, so a single non-UTF-8 SKILL.md or sibling resource would propagate out of get_skills_context() and break /review, /improve, /describe whenever skills were enabled. Both reads now catch (OSError, UnicodeDecodeError); the offending file is logged and skipped. Tests cover both SKILL.md and resource paths. Addresses Qodo finding The-PR-Agent#3 on PR The-PR-Agent#2385.
|
Persistent review updated to latest commit 33f6e27 |
|
It would be great to see support for this |
|
I'd love to see this in pr-agent! Would definitely make use of it. |
Summary
Implements agent-skills (SKILL.md) format support for
/review,/improve, and/describe, addressing #2384. Skills are discovered from configured filesystem paths, parsed for YAML frontmatter (name+description), and injected into prompts alongsideextra_instructions. Sibling*.mdfiles in the skill directory tree (includingreferences/subdirectories) are inlined as part of the same skill;scripts/andassets/subdirectories are skipped because PR-Agent's single-shot prompt architecture cannot execute scripts or load binary assets on demand. Disabled by default; gated behind[skills] enabled = true.The value proposition is org-wide curated skill libraries distributed at the PR-Agent host level — install one set of skills, reuse across many repos. This is distinct from per-repo guidance checked into version control, which is the focus of related work in flight (see "Relationship to in-flight work" below).
Scope
Parsing, discovery, formatting, and injection of text-only agent skills from filesystem paths. Every discovered skill's body and inlined
*.mdresources are added to every prompt when the feature is enabled. Thedescriptionfield is preserved and rendered as a section header in the injected context but does not currently drive selective loading.Limitations
PR-Agent dispatches single-shot model calls — there is no tool-use loop in which the model could
Reada file mid-turn — so the agent-skills standard's progressive-disclosure model (model seesname + descriptionat startup, reads SKILL.md only on selection, readsreferences/*.mdonly on demand) is not implementable on the current architecture. Every text byte of every enabled skill is loaded on every PR. Themax_skills_tokensbudget (default 8000, user-configurable) caps the combined block; skills past the cap are dropped from the end with a warning. Skills that depend on script execution or binary assets will not work — those subdirectories are excluded from inlining and PR-Agent has no mechanism to surface them.A draft design for a two-pass selector — a cheap weak-model call that picks relevant skills before the main tool prompt — is in
AGENT_SKILLS_DESIGN.mdon this branch. That is the architecturally correct way to honor progressive disclosure within the current single-shot flow. Long-term, the in-flight MCP PRs (#2348, #2356) would offer a cleaner path: with MCP tool calling, the model could invoke aread_skill_bodytool on demand, removing the need for a separate selector pass entirely. Either follow-up is out of scope for this PR.Relationship to in-flight work
Several adjacent PRs and issues touch the broader "let OSS users supply richer custom guidance to PR-Agent" gap. This PR is intended to complement, not replace, them.
#2382(load repobest_practices.mdin OSS) addresses #2377 by reading a single file from the PR's own repository via a newGitProvider.get_pr_agent_repo_custom_file()method. Its scope is per-repo guidance, checked into version control, scoped to/improve. This PR's scope is host-level skill libraries, configured outside any one repo, available to/review,/improve, and/describe. The two are complementary: a team could use #2382 for repo-specific rules and this for shared org standards in the same deployment. There is some file-level overlap (pr_code_suggestions.py,pr_reviewer.py,configuration.toml, three prompt TOMLs add different blocks in different sections); whichever lands second will need a small rebase but not a redesign.#2304(asbach —add_repo_metadatafor AGENTS.md / QODO.md / claude.md) takes a similar shape to #2382: read named files from the PR's repo and inject. If it lands, a follow-up to this PR could optionally use the sameget_repo_file()plumbing to discover skills inside the PR's repo (for example, a.pr_agent_skills/directory checked into version control), in addition to host-level paths. This branch deliberately did not depend on #2304 to keep review surface independent.#2348and#2356(MCP tool orchestration / registry) introduce tool-use capability to PR-Agent. As noted under Limitations, this is the architecturally cleanest substrate for the progressive-disclosure half of the agent-skills standard, and is the natural home for any future "selectively load skill content" work.Issue references for context: #2384 (this proposal), #2311 (the original "skills support" inquiry), #1766 (the long-running "custom rules" thread, where the workaround pattern is to stuff guidance into
extra_instructions).Configuration
Repos can override these in their own
.pr_agent.tomlper the existing Dynaconf merge logic.Test plan
PYTHONPATH=. ./.venv/bin/pytest tests/unittest -v(363 passed locally; 31 new tests intests/unittest/test_skills_loader.pycovering parsing, discovery, formatting, budget enforcement, env-var path expansion, Jinja-syntax safety, sibling-md gathering, references/ inlining, scripts/+assets/ exclusion, and nested-SKILL.md isolation)[skills], pointpathsat a directory containing one or more SKILL.md files, runpython -m pr_agent.cli --pr_url <url> reviewagainst a real PR, confirmskills_contextreaches the rendered promptenabled = false, prompts are byte-identical to pre-feature output (the{%- if skills_context %}block produces nothing)[truncated]marker without raisingRefs: #2384, #2311, #1766. Complements: #2382, #2304, #2348, #2356.