Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .agents/skills/usethis-agents/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
name: usethis-agents
description: Maintain AGENTS.md and agent skill configuration
compatibility: usethis, agent skills, markdown
license: MIT
metadata:
version: "1.0"
---

# Agent Configuration

## The One-Source-of-Truth Principle

When providing reference material about code objects (functions, classes, modules) to agents, always prefer automation over manual documentation. Use the source code's own docstrings as the single source of truth and extract them via prek hooks — never maintain hand-written summaries that duplicate what the code already says.

### Why

Manually maintained reference material inevitably drifts out of sync with the code it describes. Docstrings live alongside the code, are reviewed during code changes, and can be mechanically extracted — ensuring the documentation always reflects reality.

### How to apply the principle

When you need to add reference material about code to agent configuration:

1. Ensure the relevant code objects have descriptive docstrings (or YAML frontmatter descriptions for skills).
2. Create or extend a prek hook to extract the information into a generated file under `docs/`.
3. Reference the generated file from `AGENTS.md` or skill files using `<!-- sync:path -->` markers.
4. Never hand-write summaries of code that could be extracted automatically.

## Maintaining AGENTS.md

`AGENTS.md` is the top-level agent configuration file. It uses sync blocks to embed auto-generated content and is validated by prek hooks.

### Sync blocks

Content between `<!-- sync:path/to/file -->` and `<!-- /sync:path/to/file -->` markers is verified by the `check-doc-sync` hook. To update synced content:

1. Modify the source (e.g. add a docstring to a module, or update a skill's description).
2. Run the relevant export hook to regenerate the docs file.
3. Copy the updated content into the sync block, or let prek handle it during commit.

### Skills registry

The skills table in `AGENTS.md` must include every skill directory under `.agents/skills/`. The `check-skills-documented` hook enforces this. When creating a new skill, add it to the table (see the `usethis-skills-create` skill for the full procedure).
8 changes: 8 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ repos:
always_run: true
pass_filenames: false
priority: 0
- id: export-skills-directory
name: export-skills-directory
entry: uv run --frozen --offline hooks/export-skills-directory.py
args: ["--skills-dir=.agents/skills", "--output-file=docs/skills-directory.txt"]
language: system
always_run: true
pass_filenames: false
priority: 0
- id: export-module-tree
name: export-module-tree
entry: uv run --frozen --offline hooks/export-module-tree.py
Expand Down
49 changes: 26 additions & 23 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,29 +207,32 @@ The `.agents/skills` directory contains agent skills.

#### usethis-specific skills

| Skill | Description |
| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `usethis-cli-modify` | Modify the usethis CLI layer (commands, options, help text) and keep documentation in sync |
| `usethis-file-remove` | Remove files from the project (e.g. obsolete configs or generated artifacts) in a safe, consistent way |
| `usethis-github-actions-update` | Update GitHub Actions workflows |
| `usethis-github-issue-create` | Create GitHub issues via the gh CLI to record lessons, track follow-up work, or file bugs discovered during development |
| `usethis-pre-commit` | Guidance on pre-commit hooks — this project uses prek, not pre-commit directly |
| `usethis-prek-add-hook` | Add a prek hook for dev |
| `usethis-prek-hook-bespoke-create` | Write bespoke prek hooks as Python scripts for custom project-specific checks |
| `usethis-python-code` | Guidelines for Python code design decisions such as when to share vs. duplicate code |
| `usethis-python-code-modify` | Modify Python code (e.g. refactor, add new code, or delete code) |
| `usethis-python-enum` | Style and testing conventions for working with Python enums |
| `usethis-python-functions` | Guidelines for Python function design, including return types and signature simplicity |
| `usethis-python-module-layout-modify` | Modify the Python module layout (create, move, rename, or delete modules) |
| `usethis-python-ruff` | Guidelines for complying with Ruff linter rules instead of suppressing them |
| `usethis-python-test-affected-find` | Identify tests that are potentially affected by code changes, to catch regressions before CI |
| `usethis-qa-import-linter` | Use the Import Linter software on the usethis project |
| `usethis-qa-static-checks` | Perform static code checks |
| `usethis-skills-create` | Create new agent skills (SKILL.md files) following best practices for content quality, structure, and discoverability |
| `usethis-skills-external-add` | Add an external (community) skill and document it in AGENTS.md |
| `usethis-skills-external-install` | Install/reinstall already-tracked external skills from skills-lock.json (e.g. after a fresh clone) |
| `usethis-skills-modify` | Modify agent skills (SKILL.md files) |
| `usethis-python-test-full-coverage` | Write tests that achieve full code coverage and verify coverage locally before pushing |
<!-- sync:docs/skills-directory.txt -->

- `usethis-agents`: Maintain AGENTS.md and agent skill configuration
- `usethis-cli-modify`: Modify the usethis CLI layer (commands, options, help text) and keep documentation in sync
- `usethis-file-remove`: Remove files from the project
- `usethis-github-actions-update`: Update GitHub Actions workflows
- `usethis-github-issue-create`: Create GitHub issues via the gh CLI to record lessons, track follow-up work, or file bugs discovered during development
- `usethis-pre-commit`: Guidance on pre-commit hooks — this project uses prek, not pre-commit directly
- `usethis-prek-add-hook`: Add a prek hook for dev
- `usethis-prek-hook-bespoke-create`: Write bespoke prek hooks as Python scripts for custom project-specific checks
- `usethis-python-code`: Guidelines for Python code design decisions such as when to share vs. duplicate code
- `usethis-python-code-modify`: Modify Python code (e.g. refactor, add new code, or delete code)
- `usethis-python-enum`: Style and testing conventions for working with Python enums
- `usethis-python-functions`: Guidelines for Python function design, including return types and signature simplicity
- `usethis-python-module-layout-modify`: Modify the Python module layout (create, move, rename, or delete modules)
- `usethis-python-ruff`: Guidelines for complying with Ruff linter rules instead of suppressing them
- `usethis-python-test-affected-find`: Identify tests that are potentially affected by code changes, to catch regressions before CI
- `usethis-python-test-full-coverage`: Write tests that achieve full code coverage and verify coverage locally before pushing
- `usethis-qa-import-linter`: Use the Import Linter software on the usethis project
- `usethis-qa-static-checks`: Perform static code checks
- `usethis-skills-create`: Create new agent skills (SKILL.md files) following best practices for content quality, structure, and discoverability
- `usethis-skills-external-add`: Add an external (community) skill to the project from a third-party source, including installing it and documenting it in AGENTS.md
- `usethis-skills-external-install`: Reinstall already-tracked external skills from skills-lock.json (e.g. after a fresh clone)
- `usethis-skills-modify`: Modify agent skills (SKILL.md files)

<!-- /sync:docs/skills-directory.txt -->

#### External skills

Expand Down
22 changes: 22 additions & 0 deletions docs/skills-directory.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
- `usethis-agents`: Maintain AGENTS.md and agent skill configuration
- `usethis-cli-modify`: Modify the usethis CLI layer (commands, options, help text) and keep documentation in sync
- `usethis-file-remove`: Remove files from the project
- `usethis-github-actions-update`: Update GitHub Actions workflows
- `usethis-github-issue-create`: Create GitHub issues via the gh CLI to record lessons, track follow-up work, or file bugs discovered during development
- `usethis-pre-commit`: Guidance on pre-commit hooks — this project uses prek, not pre-commit directly
- `usethis-prek-add-hook`: Add a prek hook for dev
- `usethis-prek-hook-bespoke-create`: Write bespoke prek hooks as Python scripts for custom project-specific checks
- `usethis-python-code`: Guidelines for Python code design decisions such as when to share vs. duplicate code
- `usethis-python-code-modify`: Modify Python code (e.g. refactor, add new code, or delete code)
- `usethis-python-enum`: Style and testing conventions for working with Python enums
- `usethis-python-functions`: Guidelines for Python function design, including return types and signature simplicity
- `usethis-python-module-layout-modify`: Modify the Python module layout (create, move, rename, or delete modules)
- `usethis-python-ruff`: Guidelines for complying with Ruff linter rules instead of suppressing them
- `usethis-python-test-affected-find`: Identify tests that are potentially affected by code changes, to catch regressions before CI
- `usethis-python-test-full-coverage`: Write tests that achieve full code coverage and verify coverage locally before pushing
- `usethis-qa-import-linter`: Use the Import Linter software on the usethis project
- `usethis-qa-static-checks`: Perform static code checks
- `usethis-skills-create`: Create new agent skills (SKILL.md files) following best practices for content quality, structure, and discoverability
- `usethis-skills-external-add`: Add an external (community) skill to the project from a third-party source, including installing it and documenting it in AGENTS.md
- `usethis-skills-external-install`: Reinstall already-tracked external skills from skills-lock.json (e.g. after a fresh clone)
- `usethis-skills-modify`: Modify agent skills (SKILL.md files)
110 changes: 110 additions & 0 deletions hooks/export-skills-directory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
"""Export a skills directory from SKILL.md YAML frontmatter.

Scans agent skill directories for SKILL.md files, extracts the name and
description from each file's YAML frontmatter, and writes a formatted
Markdown bullet list to an output file.
"""

from __future__ import annotations

import argparse
import re
import sys
from pathlib import Path


def _parse_frontmatter(path: Path) -> dict[str, str]:
"""Extract top-level string fields from YAML frontmatter."""
text = path.read_text(encoding="utf-8")
match = re.match(r"^---\n(.*?)\n---", text, re.DOTALL)
if not match:
return {}
frontmatter = match.group(1)
fields: dict[str, str] = {}
for line in frontmatter.splitlines():
m = re.match(r"^(\w[\w-]*)\s*:\s*(.+)$", line)
if m:
key = m.group(1).strip()
value = m.group(2).strip().strip('"').strip("'")
fields[key] = value
return fields


def main() -> int:
"""Export a skills directory bullet list from SKILL.md frontmatter."""
parser = argparse.ArgumentParser(
description="Export a skills directory from SKILL.md frontmatter.",
)
parser.add_argument(
"--skills-dir",
required=True,
help="Path to the skills directory to scan.",
)
parser.add_argument(
"--output-file",
required=True,
help="Path to the output file to write the directory to.",
)
parser.add_argument(
"--prefix",
default="usethis-",
help="Only include skills whose directory name starts with this prefix.",
)
args = parser.parse_args()

skills_dir = Path(args.skills_dir)
output_file = Path(args.output_file)
prefix: str = args.prefix

if not skills_dir.is_dir():
print(f"ERROR: {skills_dir} is not a directory.", file=sys.stderr)
return 1

rows: list[tuple[str, str]] = []
missing: list[str] = []

for skill_dir in sorted(skills_dir.iterdir()):
if not skill_dir.is_dir():
continue
if not skill_dir.name.startswith(prefix):
continue
skill_md = skill_dir / "SKILL.md"
if not skill_md.is_file():
missing.append(skill_dir.name)
continue
fields = _parse_frontmatter(skill_md)
name = fields.get("name", skill_dir.name)
description = fields.get("description", "")
if not description:
missing.append(skill_dir.name)
continue
rows.append((name, description))

if not rows:
print("ERROR: No skills found.", file=sys.stderr)
return 1

lines: list[str] = []
for name, desc in rows:
lines.append(f"- `{name}`: {desc}")

content = "\n".join(lines) + "\n"

output_file.parent.mkdir(parents=True, exist_ok=True)
output_file.write_text(content, encoding="utf-8")

print(f"Skills directory written to {output_file}.")

if missing:
print(
f"WARNING: {len(missing)} skill(s) missing SKILL.md or description:",
file=sys.stderr,
)
for name in missing:
print(f" - {name}", file=sys.stderr)

return 0


if __name__ == "__main__":
raise SystemExit(main())
Loading