epythet.agentic_readme¶
Check that a README documents a project’s agentic aspects; render and place the section.
A project that ships skills, subagents or instruction files, and whose site
publishes agent-readable documentation (llms.txt, <package>.md), should
say so in its README: that is where an agent arriving at the repository looks
first. check_readme() reuses epythet.ai_artifacts.discover_artifacts()
to learn what exists and reads the README to see whether each kind is mentioned
(a gh skill install line, a skill or agent name, CLAUDE.md, llms.txt,
a heading about agents). The result is a ReadmeReport: one
KindCheck per kind, each ok, warn or n/a. The check is a
heuristic: a mention counts whatever the sentence around it says.
render_section() produces the README section from the effective snippets
(epythet.userconfig: the user’s agentic-readme-section.md and
agentic-readme-humor.md over the packaged defaults) and the effective
ReadmePolicy (the user’s config.toml, with a
project’s [tool.epythet.readme] keys on top so a committed README does not
depend on who ran the tool). place_section() puts it between two marker
comments so a later run updates it in place: before the first heading after the
title when agentic_first is on, at the end otherwise. The “for humans” line
links to the heading that follows the section.
>>> readme = "# pkg\n\nTagline.\n\n# Install\n\npip install pkg\n"
>>> start, end, heading, level = place_section(readme, agentic_first=True)
>>> readme[start:end], heading.title, level
('', 'Install', 1)
>>> humans_link_for(heading)
'[Install](#install)'
Module Attributes
The comments that delimit the generated section in a README (each on its own line). |
|
README filenames, in order of preference. |
|
The kinds a check reports on, in display order. |
|
The snippet names the section is rendered from. |
|
The opener used when |
|
Longest blurb (first sentence of a description) shown per skill or agent. |
|
Skill name suffixes that make a skill the one named in the install line. |
|
The fields a section snippet may use. |
Functions
|
Report whether the README documents the project's agentic aspects; draft or write the section. |
|
The first sentence of a skill or agent description, short enough for a table cell. |
|
Which agentic aspects the project has, and whether its README mentions each. |
|
Render the section for a project as it would be placed: |
|
The project's README, by the usual names ( |
|
GitHub's anchor for a heading title. |
|
Every ATX heading outside fenced code blocks. |
|
The skill named in the install line: a |
|
|
|
The instruction the skill hands an agent when the policy is |
|
Resolve a project once; each argument given is used instead of being loaded. |
|
|
|
The character span of the marked section ( |
|
Where the section goes in |
|
|
|
The README section for |
|
The name of the section snippet |
|
Whether the project ships anything an agent installs or reads as instructions. |
|
|
|
Add or update the marked section in the project's README; returns |
Classes
|
A Markdown ATX heading: its line index, level and title text. |
|
One artifact kind: whether the project has it and whether the README covers it. |
|
What every entry point needs once: config, artifacts, README, effective policy. |
|
The outcome of |
Exceptions
The README or a snippet is in a state the tool will not write over. |
- epythet.agentic_readme.HEADLINE_SUFFIXES = ('-setup', '-quickstart', '-start')¶
Skill name suffixes that make a skill the one named in the install line.
- class epythet.agentic_readme.Heading(line, level, title)[source]¶
Bases:
objectA Markdown ATX heading: its line index, level and title text.
- epythet.agentic_readme.KINDS = ('skills', 'subagents', 'instruction_files', 'agent_docs', 'section')¶
The kinds a check reports on, in display order.
- class epythet.agentic_readme.KindCheck(kind, present, documented, items=(), evidence='')[source]¶
Bases:
objectOne artifact kind: whether the project has it and whether the README covers it.
- epythet.agentic_readme.MARKER_START = '<!-- epythet:agentic-readme:start -->'¶
The comments that delimit the generated section in a README (each on its own line).
- epythet.agentic_readme.MAX_BLURB = 140¶
Longest blurb (first sentence of a description) shown per skill or agent.
- epythet.agentic_readme.NEUTRAL_INTRO = 'If you are a human'¶
The opener used when
humoris off.
- class epythet.agentic_readme.Project(root, config, artifacts, readme, user_config, policy, project_overrides)[source]¶
Bases:
objectWhat every entry point needs once: config, artifacts, README, effective policy.
- epythet.agentic_readme.README_NAMES = ('README.md', 'readme.md', 'README.markdown', 'README.rst', 'README.txt', 'README')¶
README filenames, in order of preference.
- class epythet.agentic_readme.ReadmeReport(project_dir, readme, checks, policy, user_config, project_overrides)[source]¶
Bases:
objectThe outcome of
check_readme()for one project.policyis the effectiveReadmePolicy;user_configandproject_overridesare where it came from.
- epythet.agentic_readme.SECTION_FIELDS = frozenset({'docs_block', 'for_humans_intro', 'heading', 'humans_link', 'instructions_block', 'marker_end', 'marker_start', 'name', 'repo_stub', 'site_url', 'skills_block', 'subagents_block'})¶
The fields a section snippet may use.
- epythet.agentic_readme.SECTION_SNIPPET = 'agentic-readme-section'¶
The snippet names the section is rendered from. A project whose only agentic aspect is its agent-readable documentation gets the shorter docs-only variant: it ships no tooling, so the section must not say it does.
- exception epythet.agentic_readme.SectionError[source]¶
Bases:
ValueErrorThe README or a snippet is in a state the tool will not write over.
Raised for unpaired or repeated markers, a README that is not UTF-8 or not Markdown, and a section snippet that fails to format or drops the markers.
- epythet.agentic_readme.ai_readme_check(project_dir, *, format='table', fail_on='', draft=False, write=False)[source]¶
Report whether the README documents the project’s agentic aspects; draft or write the section.
Reuses
epythet ai-artifactsdiscovery (skills, subagents, instruction files) plus the agent-readable outputs the site publishes, and looks for each in the README: agh skill installline or skill name, a subagent name,CLAUDE.md/AGENTS.md,llms.txt/<package>.md, and a heading about agents (or epythet’s own section markers). The effective policy (~/.config/epythet/config.toml[readme], overridden by the project’s[tool.epythet.readme]) is part of the output so a skill can read it.--writeis explicit: it writes whatever the policy says.- Parameters:
project_dir – the project root
format (
str) – table (human) or jsonfail_on (
str) –warnto exit 1 when anything present is undocumented (default: exit 0)draft (
bool) – print the README section rendered from the effective snippets and policy, without writingwrite (
bool) – add or update the section inREADME.mdbetween epythet’s markers
- epythet.agentic_readme.blurb(description, *, max_length=140)[source]¶
The first sentence of a skill or agent description, short enough for a table cell.
- Return type:
>>> blurb("Find and fix things. Use when asked to fix.") 'find and fix things' >>> blurb("Do the thing and then some: a, b, c, " + "and more " * 30) 'do the thing and then some'
- epythet.agentic_readme.check_readme(project_dir, *, config=None, artifacts=None, user_config=None)[source]¶
Which agentic aspects the project has, and whether its README mentions each.
- Parameters:
config – the
DocsConfig(loaded when omitted)artifacts (
AIArtifacts|None) – discovery result (computed when omitted)user_config (
UserConfig|None) – the user’s policy (read from the config dir when omitted)
- Return type:
- epythet.agentic_readme.draft_section(project_dir, *, user_config=None, config=None, artifacts=None)[source]¶
Render the section for a project as it would be placed:
(section, readme_text, start, end).readme_textis the current README (""when none exists);startandenddelimit the span the section replaces.
- epythet.agentic_readme.find_readme(project_dir)[source]¶
The project’s README, by the usual names (
Nonewhen there is none).
- epythet.agentic_readme.github_anchor(title)[source]¶
GitHub’s anchor for a heading title.
- Return type:
>>> github_anchor("For AI agents"), github_anchor("What it *fixes*: `x`") ('for-ai-agents', 'what-it-fixes-x') >>> github_anchor("my_function and [links](https://x)") 'my_function-and-links'
- epythet.agentic_readme.headings_of(text)[source]¶
Every ATX heading outside fenced code blocks.
>>> [h.title for h in headings_of("# A\n```\n# not one\n```\n## B\n")] ['A', 'B']
- epythet.agentic_readme.headline_skill(skills)[source]¶
The skill named in the install line: a
*-setup-like one if any, else the first installable.
- epythet.agentic_readme.humans_link_for(heading)[source]¶
[Title](#anchor)for the heading after the section, or a plain fallback.- Return type:
- epythet.agentic_readme.instruction_text(*, snippets=<function snippet_text>)[source]¶
The instruction the skill hands an agent when the policy is
add.- Return type:
- epythet.agentic_readme.load(project_dir, *, config=None, artifacts=None, user_config=None)[source]¶
Resolve a project once; each argument given is used instead of being loaded.
- Return type:
- epythet.agentic_readme.load_project(project_dir)[source]¶
(config, artifacts)for a project;configisNonefor a non-Python tree.A tree without
pyproject.tomlorsetup.cfgis inspected without a config. A tree that has one but cannot be loaded raisesConfigError: a broken[tool.epythet]must not silently change what gets written.
- epythet.agentic_readme.marker_span(text, *, strict=True)[source]¶
The character span of the marked section (
Nonewhen there is none).Markers count only on their own line outside fenced code, so a README that shows them in an example is not mistaken for one that has the section.
- epythet.agentic_readme.place_section(text, *, agentic_first)[source]¶
Where the section goes in
text:(start, end, next_heading, level).text[start:end]is the span to replace: the existing marked section when there is one (its position is kept, wherever the author moved it), else an empty span right before the first heading after the title (agentic_first) or at the end of the file.next_headingis the heading that follows the span (the “for humans” target) andlevelthe heading level the section should use to sit among its siblings. Headings inside the existing section are ignored, so rewriting never changes the level.
- epythet.agentic_readme.read_readme(path)[source]¶
(text, newline): the README with\nline ends, and the style to write back.- Raises:
SectionError – when the file is not UTF-8
- Return type:
- epythet.agentic_readme.render_section(artifacts, config, *, policy, level=1, humans_link='the top of the page', snippets=<function snippet_text>, agent='claude-code')[source]¶
The README section for
artifacts, from the effective snippets andpolicy.- Parameters:
- Raises:
SectionError – when the section snippet fails to format or drops a marker
- Return type:
- epythet.agentic_readme.section_snippet_for(artifacts)[source]¶
The name of the section snippet
artifactscalls for.SECTION_SNIPPETwhen the project ships tooling, else the shorterDOCS_ONLY_SECTION_SNIPPET, which makes no “ships tooling” claim.- Return type:
- epythet.agentic_readme.ships_tooling(artifacts)[source]¶
Whether the project ships anything an agent installs or reads as instructions.
Skills, subagents and instruction files count; published agent-readable documentation (
llms.txt,<package>.md) does not, because it is a view of the docs rather than tooling.- Return type:
- epythet.agentic_readme.splice_section(text, section, *, start, end)[source]¶
textwithsectionin place oftext[start:end], blank lines kept sane.- Return type:
- epythet.agentic_readme.write_section(project_dir, *, user_config=None)[source]¶
Add or update the marked section in the project’s README; returns
(path, outcome).outcomeisadded,updatedorunchanged. The README must be Markdown (README.md); a missing README is created with the section alone. Line endings are kept as found (CRLF stays CRLF).- Raises:
SectionError – when there is nothing agentic to document, the README is not Markdown or not UTF-8, it has unpaired markers, or the snippet is broken
- Return type: