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

{pkg}/data/skills/*/SKILL.md (shipped in the wheel, gh skill-installable), skills/*/SKILL.md (gh skill-installable), .claude/skills/*/SKILL.md

subagents

{pkg}/data/agents/*.md, .claude/agents/*.md

instruction files

CLAUDE.md, .claude/CLAUDE.md, AGENTS.md, .github/copilot-instructions.md, .cursor/rules, .codex/

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_LOCATIONS

Skill folders relative to the project root; {pkg} is the package directory.

AGENT_LOCATIONS

Subagent definition folders (one Markdown file per agent).

INSTRUCTION_LOCATIONS

Instruction files and directories agents read, with the audience each serves.

PAGE_FILENAME

The generated page's filename under docsrc.

DEFAULT_AGENT_HOST

The agent host named in generated gh skill install lines.

DISABLE_ENV

Environment variable that switches the page off for a whole fleet build (0 / false / no / off) without touching any pyproject.toml.

AGENT_OUTPUT_KINDS

The machine-readable outputs every epythet site publishes, in display order.

TEMPLATE_FIELDS

The fields a page template may use.

DEFAULT_TEMPLATE

The default page template; str.format fields are the section renders.

AGENT_OUTPUT_DESCRIPTIONS

Descriptions of the machine-readable outputs, keyed by kind.

Functions

agent_outputs_for(config)

The machine-readable outputs a configuration produces, with URLs when known.

ai_artifacts_page(config, *[, artifacts])

The "For AI agents" PageSpec for a project, or None.

artifacts_json(artifacts)

artifacts as indented JSON (the --format json CLI output).

artifacts_table(artifacts, *[, repo_stub])

A plain-text listing of artifacts (the default CLI output).

default_pages(config)

The generated pages a scaffold gets when the caller passes none.

discover_artifacts(project_dir, *[, package_dir])

Find the skills, subagents and instruction files of a project by convention.

enabled_by_environment()

False when EPYTHET_AI_ARTIFACTS is set to 0, false, no or off.

parse_frontmatter(text)

The YAML frontmatter of a Markdown file as a dict ({} when absent).

render_ai_artifacts_page(artifacts, config, *)

Render the "For AI agents" page for artifacts and a DocsConfig.

repo_stub_for(repo_url)

owner/repo from a GitHub URL ('' when it is not one).

site_url_for(repo_url)

The GitHub Pages URL a GitHub repository publishes to ('' when unknown).

Classes

AIArtifacts(project_dir[, skills, ...])

Everything discover_artifacts() found for one project.

AgentOutput(kind, filename, description[, url])

A machine-readable output of the built site, with its URL when known.

InstructionFile(source, audience[, is_dir])

An instruction file or directory (CLAUDE.md, AGENTS.md, ...).

Skill(name, source[, description, audience, ...])

One skill folder: its name, description, and where the real files live.

Subagent(name, source[, description, tools, ...])

One subagent definition file (name, description, tools, source path).

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: object

Everything discover_artifacts() found for one project.

to_dict()[source]

A JSON-ready view (paths relative to the project root).

Return type:

dict

class epythet.ai_artifacts.AgentOutput(kind, filename, description, url='')[source]

Bases: object

A 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 install lines.

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.format fields 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 any pyproject.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: object

An 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: object

One skill folder: its name, description, and where the real files live.

source is the project-relative POSIX path of the folder that holds the files (a symlink in .claude/skills/ is attributed to its target). shipped is true when that folder is under the package directory, so the skill is inside the wheel; installable when gh skill can see it (a non-hidden path).

install_command(repo_stub, *, agent='claude-code')[source]

The gh skill install line, or None when gh skill cannot 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: object

One 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:

list[AgentOutput]

>>> 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” PageSpec for a project, or None.

None when config.ai_artifacts is off, when the EPYTHET_AI_ARTIFACTS environment variable is 0/false (the fleet-wide switch), or when no artifact was found. The template is config.ai_artifacts_template (a file, relative to the project root) when set, else DEFAULT_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]

artifacts as indented JSON (the --format json CLI output).

Return type:

str

epythet.ai_artifacts.artifacts_table(artifacts, *, repo_stub='')[source]

A plain-text listing of artifacts (the default CLI output).

Return type:

str

epythet.ai_artifacts.default_pages(config)[source]

The generated pages a scaffold gets when the caller passes none.

Return type:

list

epythet.ai_artifacts.discover_artifacts(project_dir, *, package_dir=None)[source]

Find the skills, subagents and instruction files of a project by convention.

Parameters:
  • project_dir (str | Path) – the repository root

  • package_dir (str | Path | None) – the importable package directory, for {pkg}/data/... (skipped when None)

Return type:

AIArtifacts

epythet.ai_artifacts.enabled_by_environment()[source]

False when EPYTHET_AI_ARTIFACTS is set to 0, false, no or off.

Return type:

bool

>>> 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: y is a common slip), a small reader that understands the subset skills and agents use: key: value scalars, >/| block scalars, one level of nested mapping, [a, b] flow lists and trailing comments. A malformed frontmatter never raises.

Return type:

dict

>>> 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 artifacts and a DocsConfig.

Parameters:
  • template (str) – a str.format template with the fields marker, name, display_name, repo_stub, site_url, skills_section, subagents_section, instructions_section, outputs_section

  • agent (str) – the host named in the gh skill install lines

Return type:

str

epythet.ai_artifacts.repo_stub_for(repo_url)[source]

owner/repo from a GitHub URL (’’ when it is not one).

Deeper paths, fragments and queries are dropped, so an Issues URL in [project.urls] still names the repository.

Return type:

str

>>> 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")
''
epythet.ai_artifacts.site_url_for(repo_url)[source]

The GitHub Pages URL a GitHub repository publishes to (’’ when unknown).

Return type:

str

>>> site_url_for("https://github.com/i2mint/epythet")
'https://i2mint.github.io/epythet/'
>>> site_url_for("")
''