epythet.ai_artifacts¶
Discover a repository’s AI agent artifacts and render the “For AI agents” page.
A repository that ships tooling for coding agents does so by convention, not
registration: skills are folders holding a SKILL.md (the Agent Skills spec),
subagents are Markdown files with a frontmatter, and instruction files carry
fixed names. This module reads those conventions and, when anything is found,
renders one page for the documentation site that says what exists, where it
lives, how to install it, and which machine-readable outputs the site itself
publishes (llms.txt, the .md twins, the flat <package>.md,
objects.inv).
Where epythet looks (relative to the project root; {pkg} is the package
directory):
artifact |
locations, in order of preference |
|---|---|
skills |
|
subagents |
|
instruction files |
|
Symlinks are followed and duplicates removed, so the .claude/skills/ bridge
that points into {pkg}/data/skills/ yields one skill, attributed to its real
location. The page is a PageSpec (ai-agents.md),
produced by ai_artifacts_page() and added to the scaffold by default when
[tool.epythet] ai_artifacts is on (the default) and at least one artifact
exists. The default template is DEFAULT_TEMPLATE; a project can point
ai_artifacts_template at its own file, and a hand-written docsrc/ai-agents.md
without the epythet marker is never overwritten.
>>> import tempfile, pathlib
>>> root = pathlib.Path(tempfile.mkdtemp())
>>> skill = root / "pkg" / "data" / "skills" / "pkg-quickstart"
>>> skill.mkdir(parents=True)
>>> _ = (skill / "SKILL.md").write_text(
... "---\nname: pkg-quickstart\ndescription: Use pkg.\n---\n\n# Body\n"
... )
>>> found = discover_artifacts(root, package_dir=root / "pkg")
>>> [s.name for s in found.skills], found.skills[0].shipped
(['pkg-quickstart'], True)
>>> found.skills[0].install_command("org/pkg")
'gh skill install org/pkg pkg-quickstart --agent claude-code'
Module Attributes
Skill folders relative to the project root; |
|
Subagent definition folders (one Markdown file per agent). |
|
Instruction files and directories agents read, with the audience each serves. |
|
The generated page's filename under |
|
The agent host named in generated |
|
Environment variable that switches the page off for a whole fleet build ( |
|
The machine-readable outputs every epythet site publishes, in display order. |
|
The fields a page template may use. |
|
The default page template; |
|
Descriptions of the machine-readable outputs, keyed by kind. |
Functions
|
The machine-readable outputs a configuration produces, with URLs when known. |
|
The "For AI agents" |
|
|
|
A plain-text listing of |
|
The generated pages a scaffold gets when the caller passes none. |
|
Find the skills, subagents and instruction files of a project by convention. |
False when |
|
|
The YAML frontmatter of a Markdown file as a dict ( |
|
Render the "For AI agents" page for |
|
|
|
The GitHub Pages URL a GitHub repository publishes to ('' when unknown). |
Classes
|
Everything |
|
A machine-readable output of the built site, with its URL when known. |
|
An instruction file or directory ( |
|
One skill folder: its |
|
One subagent definition file ( |
- epythet.ai_artifacts.AGENT_LOCATIONS: tuple[str, ...] = ('{pkg}/data/agents', '.claude/agents')¶
Subagent definition folders (one Markdown file per agent).
- epythet.ai_artifacts.AGENT_OUTPUT_DESCRIPTIONS = {'aggregate_md': 'the whole documentation as one Markdown file', 'aggregate_pdf': 'the whole documentation as one PDF, for reading', 'llms': 'an index of every page with a one-line description ([llms.txt](https://llmstxt.org) format)', 'md_twins': 'a rendered Markdown twin of every page, advertised from each page\'s `<head>` with `<link rel="alternate" type="text/markdown">`', 'objects_inv': 'the Sphinx inventory: a symbol-to-URL index (`sphobjinv convert plain objects.inv -`)'}¶
Descriptions of the machine-readable outputs, keyed by kind.
- epythet.ai_artifacts.AGENT_OUTPUT_KINDS = ('llms', 'aggregate_md', 'aggregate_pdf', 'md_twins', 'objects_inv')¶
The machine-readable outputs every epythet site publishes, in display order.
- class epythet.ai_artifacts.AIArtifacts(project_dir, skills=(), subagents=(), instruction_files=())[source]¶
Bases:
objectEverything
discover_artifacts()found for one project.
- class epythet.ai_artifacts.AgentOutput(kind, filename, description, url='')[source]¶
Bases:
objectA machine-readable output of the built site, with its URL when known.
- epythet.ai_artifacts.DEFAULT_AGENT_HOST = 'claude-code'¶
The agent host named in generated
gh skill installlines.
- epythet.ai_artifacts.DEFAULT_TEMPLATE = '{marker}\n\n# For AI agents\n\n`{name}` ships artifacts for coding agents alongside its code. This page lists\nthem, says where each lives in the repository, and points at the\nmachine-readable copies of this documentation.\n{skills_section}{subagents_section}{instructions_section}{outputs_section}'¶
The default page template;
str.formatfields are the section renders.
- epythet.ai_artifacts.DISABLE_ENV = 'EPYTHET_AI_ARTIFACTS'¶
Environment variable that switches the page off for a whole fleet build (
0/false/no/off) without touching anypyproject.toml.
- epythet.ai_artifacts.INSTRUCTION_LOCATIONS: tuple[tuple[str, str], ...] = (('CLAUDE.md', 'Claude Code'), ('.claude/CLAUDE.md', 'Claude Code'), ('AGENTS.md', 'Codex, Copilot, Cursor and other agents'), ('.github/copilot-instructions.md', 'GitHub Copilot'), ('.cursor/rules', 'Cursor'), ('.codex', 'Codex'))¶
Instruction files and directories agents read, with the audience each serves.
- class epythet.ai_artifacts.InstructionFile(source, audience, is_dir=False)[source]¶
Bases:
objectAn instruction file or directory (
CLAUDE.md,AGENTS.md, …).
- epythet.ai_artifacts.PAGE_FILENAME = 'ai-agents.md'¶
The generated page’s filename under
docsrc.
- epythet.ai_artifacts.SKILL_LOCATIONS: tuple[str, ...] = ('{pkg}/data/skills', 'skills', '.claude/skills')¶
Skill folders relative to the project root;
{pkg}is the package directory.
- class epythet.ai_artifacts.Skill(name, source, description='', audience='', shipped=False, installable=True)[source]¶
Bases:
objectOne skill folder: its
name, description, and where the real files live.sourceis the project-relative POSIX path of the folder that holds the files (a symlink in.claude/skills/is attributed to its target).shippedis true when that folder is under the package directory, so the skill is inside the wheel;installablewhengh skillcan see it (a non-hidden path).- install_command(repo_stub, *, agent='claude-code')[source]¶
The
gh skill installline, orNonewhengh skillcannot see it.>>> Skill("x", "pkg/data/skills/x").install_command("org/repo") 'gh skill install org/repo x --agent claude-code' >>> Skill("x", ".claude/skills/x", installable=False).install_command("o/r")
- class epythet.ai_artifacts.Subagent(name, source, description='', tools='', shipped=False)[source]¶
Bases:
objectOne subagent definition file (
name, description, tools, source path).
- epythet.ai_artifacts.TEMPLATE_FIELDS = frozenset({'display_name', 'instructions_section', 'marker', 'name', 'outputs_section', 'repo_stub', 'site_url', 'skills_section', 'subagents_section'})¶
The fields a page template may use.
- epythet.ai_artifacts.agent_outputs_for(config)[source]¶
The machine-readable outputs a configuration produces, with URLs when known.
- Return type:
>>> from epythet.config import DocsConfig >>> cfg = DocsConfig(project_dir="/tmp/x", name="x", repo_url="https://github.com/o/x") >>> [o.filename for o in agent_outputs_for(cfg)] ['llms.txt', 'x.md', '<page>.html.md', 'objects.inv'] >>> agent_outputs_for(cfg)[0].url 'https://o.github.io/x/llms.txt'
- epythet.ai_artifacts.ai_artifacts_page(config, *, artifacts=None)[source]¶
The “For AI agents”
PageSpecfor a project, orNone.Nonewhenconfig.ai_artifactsis off, when theEPYTHET_AI_ARTIFACTSenvironment variable is0/false(the fleet-wide switch), or when no artifact was found. The template isconfig.ai_artifacts_template(a file, relative to the project root) when set, elseDEFAULT_TEMPLATE.- Raises:
ConfigError – when the template file is missing or has a field the renderer does not provide (literal braces must be doubled:
{{).
- epythet.ai_artifacts.artifacts_json(artifacts)[source]¶
artifactsas indented JSON (the--format jsonCLI output).- Return type:
- epythet.ai_artifacts.artifacts_table(artifacts, *, repo_stub='')[source]¶
A plain-text listing of
artifacts(the default CLI output).- Return type:
- epythet.ai_artifacts.default_pages(config)[source]¶
The generated pages a scaffold gets when the caller passes none.
- Return type:
- epythet.ai_artifacts.discover_artifacts(project_dir, *, package_dir=None)[source]¶
Find the skills, subagents and instruction files of a project by convention.
- epythet.ai_artifacts.enabled_by_environment()[source]¶
False when
EPYTHET_AI_ARTIFACTSis set to0,false,nooroff.- Return type:
>>> os.environ[DISABLE_ENV] = "0"; enabled_by_environment() False >>> del os.environ[DISABLE_ENV]; enabled_by_environment() True
- epythet.ai_artifacts.parse_frontmatter(text)[source]¶
The YAML frontmatter of a Markdown file as a dict (
{}when absent).Uses PyYAML when installed; otherwise, or when PyYAML rejects the block (an unquoted
description: Use when x: yis a common slip), a small reader that understands the subset skills and agents use:key: valuescalars,>/|block scalars, one level of nested mapping,[a, b]flow lists and trailing comments. A malformed frontmatter never raises.- Return type:
>>> parse_frontmatter("---\ndescription: Use when a: b\nname: x\n---\n") {'description': 'Use when a: b', 'name': 'x'}
>>> parse_frontmatter("---\nname: x\nmetadata:\n audience: users\n---\nbody") {'name': 'x', 'metadata': {'audience': 'users'}} >>> parse_frontmatter("no frontmatter") {}
- epythet.ai_artifacts.render_ai_artifacts_page(artifacts, config, *, template='{marker}\\n\\n# For AI agents\\n\\n`{name}` ships artifacts for coding agents alongside its code. This page lists\\nthem, says where each lives in the repository, and points at the\\nmachine-readable copies of this documentation.\\n{skills_section}{subagents_section}{instructions_section}{outputs_section}', agent='claude-code')[source]¶
Render the “For AI agents” page for
artifactsand aDocsConfig.
- epythet.ai_artifacts.repo_stub_for(repo_url)[source]¶
owner/repofrom a GitHub URL (’’ when it is not one).Deeper paths, fragments and queries are dropped, so an
IssuesURL in[project.urls]still names the repository.- Return type:
>>> repo_stub_for("https://github.com/i2mint/epythet.git") 'i2mint/epythet' >>> repo_stub_for("https://github.com/i2mint/epythet/issues#readme") 'i2mint/epythet' >>> repo_stub_for("git@github.com:i2mint/epythet.git") 'i2mint/epythet' >>> repo_stub_for("https://gitlab.com/o/r") ''