agent-readable 0.1.1__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. {agent_readable-0.1.1 → agent_readable-0.2.0}/.github/workflows/test.yml +17 -0
  2. agent_readable-0.2.0/CHANGELOG.md +73 -0
  3. {agent_readable-0.1.1 → agent_readable-0.2.0}/PKG-INFO +106 -95
  4. {agent_readable-0.1.1 → agent_readable-0.2.0}/README.md +103 -93
  5. {agent_readable-0.1.1 → agent_readable-0.2.0}/docs/agent_help_vs_help.gif +0 -0
  6. {agent_readable-0.1.1 → agent_readable-0.2.0}/examples/any_class.py +7 -6
  7. {agent_readable-0.1.1 → agent_readable-0.2.0}/examples/duck_type.py +2 -2
  8. {agent_readable-0.1.1 → agent_readable-0.2.0}/examples/modules_and_functions.py +1 -1
  9. {agent_readable-0.1.1 → agent_readable-0.2.0}/examples/sqlite_connection.py +2 -2
  10. {agent_readable-0.1.1 → agent_readable-0.2.0}/examples/temperature.py +2 -2
  11. {agent_readable-0.1.1 → agent_readable-0.2.0}/pyproject.toml +15 -3
  12. {agent_readable-0.1.1 → agent_readable-0.2.0}/src/agent_readable/__init__.py +1 -1
  13. agent_readable-0.2.0/src/agent_readable/_model.py +301 -0
  14. agent_readable-0.2.0/src/agent_readable/_protocol.py +194 -0
  15. agent_readable-0.2.0/src/agent_readable/_render.py +64 -0
  16. agent_readable-0.2.0/tests/__init__.py +0 -0
  17. {agent_readable-0.1.1 → agent_readable-0.2.0}/tests/test_cli.py +1 -1
  18. {agent_readable-0.1.1 → agent_readable-0.2.0}/tests/test_protocol.py +198 -4
  19. agent_readable-0.1.1/AGENT-PROMPT.md +0 -82
  20. agent_readable-0.1.1/src/agent_readable/_protocol.py +0 -417
  21. {agent_readable-0.1.1 → agent_readable-0.2.0}/.github/workflows/publish.yml +0 -0
  22. {agent_readable-0.1.1 → agent_readable-0.2.0}/.gitignore +0 -0
  23. {agent_readable-0.1.1 → agent_readable-0.2.0}/LICENSE +0 -0
  24. {agent_readable-0.1.1 → agent_readable-0.2.0}/src/agent_readable/__main__.py +0 -0
  25. /agent_readable-0.1.1/tests/__init__.py → /agent_readable-0.2.0/src/agent_readable/py.typed +0 -0
@@ -7,6 +7,23 @@ on:
7
7
  workflow_dispatch:
8
8
 
9
9
  jobs:
10
+ lint:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+
15
+ - name: Install uv
16
+ uses: astral-sh/setup-uv@v3
17
+
18
+ - name: Install dev deps
19
+ run: uv sync --group dev
20
+
21
+ - name: Ruff check
22
+ run: uv run ruff check
23
+
24
+ - name: Ruff format check
25
+ run: uv run ruff format --check
26
+
10
27
  test:
11
28
  runs-on: ubuntu-latest
12
29
  strategy:
@@ -0,0 +1,73 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.2] - 2026-05-30
11
+
12
+ ### Added
13
+
14
+ - `py.typed` marker so inline type annotations are visible to downstream type
15
+ checkers (PEP 561).
16
+ - A module's `__all__` is now honored as the authoritative public API when
17
+ present, including symbols re-exported from other modules.
18
+ - `agent_help()` now emits a `UserWarning` when a class defines both a custom
19
+ `__agent_help__()` and `__agent_notes__()`, since the notes are silently
20
+ dropped in that case.
21
+ - Agent Skill at `skills/agent-readable/SKILL.md` following the
22
+ [Agent Skills open standard](https://agentskills.io). Installable via
23
+ `npx skills add zydo/agent-readable --skill agent-readable` and portable
24
+ across Claude Code, Codex CLI (OpenAI), Gemini CLI (Google), GitHub Copilot,
25
+ Cursor, JetBrains Junie, Goose, OpenCode, and 40+ other adopters. The skill
26
+ teaches the agent to call `agent_help()` before writing Python against a
27
+ library and to author new APIs with `__agent_notes__()`. Supersedes
28
+ `AGENT-PROMPT.md` as the recommended way to wire up coding agents.
29
+
30
+ ### Fixed
31
+
32
+ - Method signatures with a positional-only `self` no longer render a dangling
33
+ `/` (e.g. `backup(/, target)` now renders as `backup(target)`).
34
+
35
+ ### Documentation
36
+
37
+ - README and examples reframed around curation, not compactness. The "Why it
38
+ matters" section now leads with two failure modes — what-exists (curated
39
+ Public API list curbs hallucinated methods and stale signatures) and
40
+ how-to-use (lifecycle rules via `__agent_notes__()`) — instead of a
41
+ comparative empirical claim about which is more common. Removed the
42
+ "217 vs 56 lines" framing in favor of structure-and-rules language.
43
+ - Tagline sharpened to lead with the hallucination-stopping outcome rather
44
+ than the protocol mechanics.
45
+
46
+ ### Removed
47
+
48
+ - `AGENT-PROMPT.md` (superseded by the agent skill at
49
+ `skills/agent-readable/SKILL.md`).
50
+
51
+ ## [0.1.1] - 2026-05-11
52
+
53
+ ### Added
54
+
55
+ - `agent_help()` support for functions and methods.
56
+
57
+ ### Documentation
58
+
59
+ - Demo GIF comparing `agent_help()` and `help()`, plus additional README
60
+ examples.
61
+
62
+ ## [0.1.0] - 2026-05-10
63
+
64
+ ### Added
65
+
66
+ - Initial release: the `agent_help()` function, the `AgentReadable` protocol,
67
+ `AgentReadableMixin`, `__agent_notes__` accumulation across the MRO, module
68
+ support, and the `python -m agent_readable` CLI.
69
+
70
+ [Unreleased]: https://github.com/zydo/agent-readable/compare/v0.1.2...HEAD
71
+ [0.1.2]: https://github.com/zydo/agent-readable/compare/v0.1.1...v0.1.2
72
+ [0.1.1]: https://github.com/zydo/agent-readable/compare/v0.1.0...v0.1.1
73
+ [0.1.0]: https://github.com/zydo/agent-readable/releases/tag/v0.1.0
@@ -1,15 +1,16 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-readable
3
- Version: 0.1.1
3
+ Version: 0.2.0
4
4
  Summary: A lightweight Python protocol for agent-oriented documentation
5
5
  Project-URL: Repository, https://github.com/zydo/agent-readable
6
+ Project-URL: Issues, https://github.com/zydo/agent-readable/issues
7
+ Project-URL: Changelog, https://github.com/zydo/agent-readable/blob/main/CHANGELOG.md
6
8
  Author: zydo and agent-readable contributors
7
9
  License-Expression: MIT
8
10
  License-File: LICENSE
9
11
  Keywords: agent,agent-help,ai,ai-agent,coding-agent,context-engineering,docstring,documentation,llm,mixin,prompt-engineering,protocol,vibe-coding
10
12
  Classifier: Development Status :: 3 - Alpha
11
13
  Classifier: Intended Audience :: Developers
12
- Classifier: License :: OSI Approved :: MIT License
13
14
  Classifier: Programming Language :: Python :: 3
14
15
  Classifier: Programming Language :: Python :: 3.10
15
16
  Classifier: Programming Language :: Python :: 3.11
@@ -26,7 +27,13 @@ Description-Content-Type: text/markdown
26
27
 
27
28
  # agent-readable
28
29
 
29
- A lightweight Python protocol for exposing agent-oriented documentation from classes and modules.
30
+ Stop coding agents from hallucinating Python APIs. `agent_help(target)` reads the live public-API surface off any class, module, function, or method — no author opt-in needed — and optionally returns class-level usage rules (lifecycle order, pre-conditions, anti-patterns) when the library ships them.
31
+
32
+ To let your coding agent automatically call `agent_help()` before using an unfamiliar API, install the skill:
33
+
34
+ ```bash
35
+ npx skills add zydo/skills --skill agent-readable
36
+ ```
30
37
 
31
38
  <!-- markdownlint-disable MD033 -->
32
39
  <p align="center">
@@ -39,16 +46,9 @@ A lightweight Python protocol for exposing agent-oriented documentation from cla
39
46
 
40
47
  AI coding agents recognize established libraries from their training data, but they hallucinate when APIs change, when libraries are new, or when correct usage depends on rules that aren't visible from the API surface — pre-conditions, lifecycle order, anti-patterns, *"use `call()` for non-streaming, `stream()` for streaming."*
41
48
 
42
- Today there's nowhere to put those rules where an agent will reliably find them. Docstrings document the API surface, not behavioral rules. `AGENTS.md` and `llms.txt` work at project granularity, drift fast, and don't travel with refactors. `help()` is verbose and only describes interfaces.
43
-
44
- `agent-readable` adds two dunders — `__agent_help__` (full custom output) and `__agent_notes__` (additive guidance that accumulates across inheritance) — that live next to the code. Library authors annotate once; any agent that calls `agent_help(cls)` gets the rules.
49
+ Today there's nowhere to put those rules where an agent will reliably find them. Docstrings document the API surface, not behavioral rules. `AGENTS.md` and `llms.txt` work at project granularity, drift fast, and don't travel with refactors. `help()` only describes interfaces — and mixes inherited dunders and MRO detail in with the methods you actually want to use.
45
50
 
46
- ```text
47
- help(logging.Logger) 217 lines — every inherited method, dunder, and MRO detail.
48
- agent_help(logging.Logger) 56 lines — structured sections + any author notes.
49
- ```
50
-
51
- The compactness is a side effect; the structure is the point.
51
+ `agent-readable` has two halves, and the lighter one stands alone. The **consume side** is one function — `agent_help(target)` — that works on every Python library today, annotated or not. It introspects the live, installed class/module/function and returns a curated public-API list with current signatures, no inherited-dunder or MRO noise to wade through. That alone closes the *what exists* hallucination — agents stop inventing methods that aren't there and stop calling real ones with stale signatures. The **author side** adds two dunders: `__agent_help__` for full custom output and `__agent_notes__` for additive behavioral rules that accumulate across inheritance. When a library opts in, those rules show up next to the public-API list in the same `agent_help()` output — so the rules travel with refactors instead of rotting in a sidecar file.
52
52
 
53
53
  ## Installation
54
54
 
@@ -60,11 +60,51 @@ Requires Python 3.10+. No runtime dependencies.
60
60
 
61
61
  ## Quickstart
62
62
 
63
+ `agent_help()` works on **any Python class, module, function, or method — no opt-in required**. Point it at a stdlib class:
64
+
63
65
  ```python
64
- from agent_readable import AgentReadableMixin, agent_help
66
+ from agent_readable import agent_help
67
+ import logging
65
68
 
69
+ print(agent_help(logging.Logger))
70
+ ```
66
71
 
67
- class Sensor(AgentReadableMixin):
72
+ Output (excerpted):
73
+
74
+ ````
75
+ # Logger
76
+
77
+ ## Constructor
78
+
79
+ ```python
80
+ Logger(name, level=0)
81
+ ```
82
+
83
+ ## Purpose
84
+
85
+ Instances of the Logger class represent a single logging channel. ...
86
+
87
+ ## Public API
88
+
89
+ - `addHandler(hdlr)` method: Add the specified handler to this logger.
90
+ - `debug(msg, *args, **kwargs)` method: Log 'msg % args' with severity 'DEBUG'.
91
+ - `info(msg, *args, **kwargs)` method: Log 'msg % args' with severity 'INFO'.
92
+ - `setLevel(level)` method: Set the logging level of this logger.
93
+ - `warning(msg, *args, **kwargs)` method: Log 'msg % args' with severity 'WARNING'.
94
+ - ...
95
+ ````
96
+
97
+ The `## Public API` list comes from **runtime introspection of the live, installed version** — current signatures, no inherited-dunder noise, no stale training-data guesses. Substitute any class, module, function, or method (yours, a third party's, or a stdlib one); zero setup on the target side.
98
+
99
+ ### Library authors: ship usage rules next to the code (optional)
100
+
101
+ When you own the class, you can also ship class-level usage rules — lifecycle order, pre-conditions, anti-patterns — that `agent_help()` returns alongside the auto-generated public-API list. Define a `__agent_notes__()` classmethod (no mixin, no inheritance required); notes accumulate across the MRO so subclasses don't lose parent rules:
102
+
103
+ ```python
104
+ from agent_readable import agent_help
105
+
106
+
107
+ class Sensor:
68
108
  """Reads a value from a hardware sensor."""
69
109
 
70
110
  def __init__(self, pin: int, *, unit: str = "C"): ...
@@ -91,52 +131,19 @@ class Sensor(AgentReadableMixin):
91
131
  print(agent_help(Sensor))
92
132
  ```
93
133
 
94
- Output:
95
-
96
- ````
97
- # Sensor
98
-
99
- ## Constructor
100
-
101
- ```python
102
- Sensor(pin: int, *, unit: str = 'C')
103
- ```
104
-
105
- ## Purpose
106
-
107
- Reads a value from a hardware sensor.
108
-
109
- ## Public API
110
-
111
- - `calibrate(offset: float)` method: Apply a calibration offset.
112
- - `read() -> float` method: Read the current sensor value.
113
-
114
- ## Agent usage rules
115
-
116
- - Prefer the public API listed above.
117
- - Do not use private methods or attributes starting with `_`.
118
- - Do not invent unsupported behavior.
119
- - If usage is ambiguous, prefer the simplest documented usage pattern.
120
-
121
- ## Notes from class Sensor
122
-
123
- ## Do
124
-
125
- - Call `calibrate()` once during setup, before `read()`.
126
-
127
- ## Do not
128
-
129
- - Do not call `read()` before `calibrate()` on first use.
130
- ````
134
+ `agent_help(Sensor)` now returns the same auto-generated public-API list, plus a `## Notes from class Sensor` section carrying the rules above. Full output and inheritance-with-notes behavior in [Example 2](#example-2-inheritance-with-accumulated-notes) below.
131
135
 
132
136
  ## Why it matters
133
137
 
134
- `help()` documents the **API surface** — what each method does. But agents fail less often on *what methods exist* than on *how to use them*: lifecycle order, pre-conditions, anti-patterns, "this method is for X, that one for Y." Those rules don't fit in docstrings (which describe single methods) and don't belong in a project-level `AGENTS.md` (which describes whole repos). They're class-level, and they need to travel with refactors.
138
+ `agent_help()` covers two failure modes agents hit on real APIs:
135
139
 
136
- `agent_help()` gives them a home next to the code:
140
+ 1. **What exists.** The `## Public API` section is a curated list of current signatures pulled from runtime introspection — no hallucinated methods, no stale signatures from training data, no private members leaking in. If the agent reads this list first, it stops inventing methods that don't exist and stops calling real ones with the wrong arguments.
141
+ 2. **How to use it correctly.** Lifecycle order, pre-conditions, anti-patterns, *"this method is for X, that one for Y."* These don't fit in any single method's docstring and don't belong in a project-level `AGENTS.md` (which describes whole repos). They're class-level, and they need to travel with refactors. `__agent_notes__()` gives them a home next to the code.
142
+
143
+ A concrete contrast:
137
144
 
138
145
  ```
139
- # help(sqlite3.Connection) — 200+ lines of terminal output:
146
+ # help(sqlite3.Connection) — every inherited dunder, in source order:
140
147
  Help on class Connection in module sqlite3:
141
148
 
142
149
  class Connection(builtins.object)
@@ -150,12 +157,13 @@ class Connection(builtins.object)
150
157
  | ...
151
158
  | backup(self, target, /, *, pages=-1, progress=None, ...)
152
159
  | blobopen(self, table, column, rowid, /, *, readonly=False, ...)
153
- | ... (continues for 200+ more lines)
160
+ | ... (no curation, no behavioral rules, no signal that backup() needs an
161
+ | open target connection before being called)
154
162
  ```
155
163
 
156
- `agent_help(sqlite3.Connection)` produces a scannable summary instead — see Example 1 below. Notes from `__agent_notes__()` accumulate across inheritance. Class docs travel with the code in commits, reviews, and refactors. Drift gets caught in code review, not weeks later in a sidecar file.
164
+ `agent_help(sqlite3.Connection)` produces a curated public-API list with current signatures and any class-level rules attached — see Example 1 below. Notes from `__agent_notes__()` accumulate across inheritance. Class docs travel with the code in commits, reviews, and refactors. Drift gets caught in code review, not weeks later in a sidecar file.
157
165
 
158
- The examples below demonstrate four ways to use `agent_help()`.
166
+ The examples below demonstrate five ways to use `agent_help()`, three with library-author opt-in (Examples 1–3) and two on plain classes/modules with no setup (Examples 4–5).
159
167
 
160
168
  ## Example 1: Wrapping an existing class
161
169
 
@@ -190,14 +198,14 @@ An agent-friendly wrapper around sqlite3.Connection.
190
198
 
191
199
  ## Public API
192
200
 
193
- - `backup(/, target, *, pages=-1, progress=None, name='main', sleep=0.25)` method: Makes a backup of the database.
201
+ - `backup(target, *, pages=-1, progress=None, name='main', sleep=0.25)` method: Makes a backup of the database.
194
202
  - ...
195
- - `close(/)` method: Close the database connection.
196
- - `commit(/)` method: Commit any pending transaction to the database.
203
+ - `close()` method: Close the database connection.
204
+ - `commit()` method: Commit any pending transaction to the database.
197
205
  - ...
198
206
  - `execute(...)` method: Executes an SQL statement.
199
207
  - ...
200
- - `rollback(/)` method: Roll back to the start of any pending transaction.
208
+ - `rollback()` method: Roll back to the start of any pending transaction.
201
209
  - ...
202
210
 
203
211
  ## Agent usage rules
@@ -366,7 +374,7 @@ print(agent_help(RateLimiter))
366
374
 
367
375
  ## Example 4: Any class — no setup required
368
376
 
369
- Even without the mixin or duck-typing, `agent_help()` still generates compact, structured Markdown from introspection. If the class (or any class in its MRO) defines `__agent_notes__()`, those notes are auto-appended too — no mixin required. The output is still more agent-friendly than `help()`.
377
+ Even without the mixin or duck-typing, `agent_help()` still generates structured Markdown from introspection — a curated public-API list with current signatures, free of inherited dunders and MRO clutter. If the class (or any class in its MRO) defines `__agent_notes__()`, those notes are auto-appended too — no mixin required.
370
378
  Full example: [`examples/any_class.py`](examples/any_class.py).
371
379
 
372
380
  ```python
@@ -376,7 +384,7 @@ from agent_readable import agent_help
376
384
  print(agent_help(logging.Logger))
377
385
  ```
378
386
 
379
- `help(logging.Logger)` produces **217 lines** of output. `agent_help(logging.Logger)` produces a compact, scannable summary:
387
+ Compare what `agent_help(logging.Logger)` returns to what `help(logging.Logger)` does the former lists only the public methods you'd actually call, with their current signatures and docstring summaries:
380
388
 
381
389
  ````
382
390
  # Logger
@@ -429,13 +437,15 @@ Output:
429
437
 
430
438
  ## Purpose
431
439
 
432
- Example: Using agent_help() on modules.
440
+ Example: Using agent_help() on modules, functions, and methods.
433
441
 
434
- Demonstrates both shapes of module support:
442
+ Demonstrates non-class targets:
435
443
  1. A custom module (this file itself).
436
444
  2. A stdlib module (pathlib).
445
+ 3. A function (connect, defined below).
446
+ 4. A method (Query.execute, defined below).
437
447
 
438
- Run this file to see both outputs:
448
+ Run this file to see all outputs:
439
449
  python examples/modules_and_functions.py
440
450
 
441
451
  ## Public API
@@ -476,20 +486,13 @@ operating systems.
476
486
 
477
487
  ## Public API
478
488
 
479
- - `DirEntryInfo` class: Implementation of pathlib.types.PathInfo that provides status
480
489
  - `Path` class: PurePath subclass that can make system calls.
481
- - `PathInfo` class: Implementation of pathlib.types.PathInfo that provides status
482
490
  - `PosixPath` class: Path subclass for non-Windows systems.
483
491
  - `PurePath` class: Base class for manipulating paths without I/O.
484
492
  - `PurePosixPath` class: PurePath subclass for non-Windows systems.
485
493
  - `PureWindowsPath` class: PurePath subclass for Windows systems.
486
494
  - `UnsupportedOperation` class: An exception that is raised when an unsupported operation is attempted.
487
495
  - `WindowsPath` class: Path subclass for Windows systems.
488
- - `copy_info(info, target, follow_symlinks=True)` function: Copy metadata from the given PathInfo to the given local path.
489
- - `copyfileobj(source_f, target_f)` function: Copy data from file-like object source_f to file-like object target_f.
490
- - `ensure_different_files(source, target)` function: Raise OSError(EINVAL) if both paths refer to the same file.
491
- - `ensure_distinct_paths(source, target)` function: Raise OSError(EINVAL) if the other path is within this path.
492
- - `magic_open(path, mode='r', buffering=-1, encoding=None, errors=None, newline=None)` function: Open the file pointed to by this path and return a file object, as
493
496
 
494
497
  ## Agent usage rules
495
498
 
@@ -499,7 +502,7 @@ operating systems.
499
502
  - If usage is ambiguous, prefer the simplest documented usage pattern.
500
503
  ````
501
504
 
502
- Modules support less customization than classes — there is no mixin inheritance or `__agent_notes__()`. You can override the auto-generated output entirely by setting a module-level `__agent_help__` attribute (callable or string), but this is discouraged since it replaces the auto-generated summary — signatures, purpose, and public API listing are all lost. Prefer clear docstrings on the module and its functions/classes instead.
505
+ Modules support less customization than classes — there is no mixin inheritance or `__agent_notes__()`. You can override the auto-generated output entirely by setting a module-level `__agent_help__` attribute (callable or string), but this is discouraged since it replaces the auto-generated summary — signatures, purpose, and public API listing are all lost. Prefer clear docstrings on the module and its functions/classes instead. If the module defines `__all__`, that list is honored as the authoritative public API — including symbols re-exported from other modules.
503
506
 
504
507
  You can also pass a function or method directly — `agent_help()` renders the signature, full docstring, and usage rules. The output is close to `help()` for a single callable; the bigger wins are still on classes and modules.
505
508
 
@@ -543,12 +546,13 @@ Outputs agent-oriented documentation for the given class, module, function, or m
543
546
 
544
547
  ### How does my agent know to call `agent_help()` instead of `help()`?
545
548
 
546
- Today, you tell it. Either:
549
+ Install the companion agent skill:
547
550
 
548
- - Paste [`AGENT-PROMPT.md`](AGENT-PROMPT.md) into your conversation, or
549
- - Add it permanently to your repo's `AGENTS.md` / `CLAUDE.md` / `.cursor/rules` / equivalent instruction file.
551
+ ```bash
552
+ npx skills add zydo/skills --skill agent-readable
553
+ ```
550
554
 
551
- "You tell it" is the hard part of any new agent protocol. An MCP server (so MCP-aware clients auto-discover the tool) is on the roadmap.
555
+ The skill teaches the agent to install `agent-readable`, call `agent_help(target)` before writing code against a class, module, function, or method, and add `__agent_notes__()` (or improve docstrings) when authoring new public APIs. (Cursor is among the adopters too, but its skill integration is manual — you invoke the skill explicitly there rather than relying on description-match auto-activation.)
552
556
 
553
557
  ### How is this different from `AGENTS.md` / `llms.txt` / Cursor rules?
554
558
 
@@ -574,19 +578,23 @@ Yes. Two ways:
574
578
 
575
579
  Yes — `agent_help()` falls back to introspection (Example 4). You get a structured summary of every plain class, mixin or not. Notes are added on top *if* the class defines them; otherwise the auto-doc is what you see.
576
580
 
581
+ ### Is `agent_help()` the strongest fix for API hallucination?
582
+
583
+ No, and worth being upfront about it. The strongest known mitigation is **constrained decoding** at the harness or decoder layer — masking illegal API tokens before generation, so the model can't produce a nonexistent method at all. `agent_help()` takes the lighter, library-side path: an authoritative usage guide injected into the agent's context, leaving the agent to read and follow it. Recent research on Python API misuse shows the in-context route leaves a meaningful slice of hallucinations on the table even when verified API references are provided. The two approaches compose. Pick `agent_help()` when you have leverage on the library side and want a fix that works on every agent and every library today, without harness changes. Reach for constrained decoding when you control the harness or the decoder. Either way, verify generated code before merging.
584
+
577
585
  ## Keeping agent docs up to date
578
586
 
579
- Agent docs can go stale when classes change — new methods, changed behavior, removed APIs. Copy [`AGENT-PROMPT.md`](AGENT-PROMPT.md) into your coding agent's conversation, or permanently add it to your repo's `AGENTS.md`, `CLAUDE.md`, `.cursor/rules`, `.trae/rules`, `.github/copilot-instructions.md`, or equivalent instruction file. It tells your coding agent to run `agent_help()` before modifying a class, prefer docstrings over `__agent_notes__()`, and verify that docs stay accurate after changes.
587
+ Agent docs can go stale when classes change — new methods, changed behavior, removed APIs. Install the companion skill (`npx skills add zydo/skills --skill agent-readable`) to teach your agent to run `agent_help()` before modifying a class, prefer docstrings over `__agent_notes__()`, and verify that the output stays accurate after changes.
580
588
 
581
589
  ## The `__agent_help__` protocol
582
590
 
583
- `__agent_help__()` is a dunder protocol, similar in spirit to ecosystem protocols such as:
591
+ `__agent_help__()` is a dunder protocol for tool-specific documentation, similar in spirit to:
584
592
 
585
- - `__str__` (str) — string representation
586
- - `__rich_repr__` (Rich) — custom console representation
587
- - `__html__` (Django, Jinja2) — HTML rendering
588
- - `__array__` (NumPy) — array conversion
589
- - `__fspath__` (os.fspath)filesystem path conversion
593
+ - `__doc__` (Python `help()`, Sphinx, IDEs, REPLs) the canonical "this is my human-readable documentation" slot, read by inspection tools and surfaced through `help()`
594
+ - `__rich_repr__` (Rich) — Rich-specific console representation read only when Rich renders the object
595
+ - `__html__` (Django, Jinja2, MarkupSafe) — HTML-renderer-specific representation read only when those template engines render the object
596
+
597
+ Unlike `__str__` or `__fspath__`, these dunders don't change Python runtime behavior they're metadata slots a specific tool reads when it wants a representation. `__agent_help__` follows the same pattern for the agent-docs case.
590
598
 
591
599
  Classes that define a `@classmethod` named `__agent_help__` returning a `str` are considered agent-readable. Modules can define a top-level `__agent_help__` attribute (callable or string). Call the top-level `agent_help(obj)` function to get the docs — just like `str()` calls `__str__()`. The `AgentReadable` `typing.Protocol` and `AgentReadableMixin` are provided for convenience and type-checking, but neither is required.
592
600
 
@@ -594,13 +602,15 @@ Classes that define a `@classmethod` named `__agent_help__` returning a `str` ar
594
602
 
595
603
  The two dunders intentionally encode different composition rules:
596
604
 
597
- | Aspect | `__agent_help__()` | `__agent_notes__()` |
598
- | --------------- | ----------------------------------------------- | --------------------------------------------------------------------------- |
599
- | Semantics | **Replacement** — returned string IS the output | **Additive** — appended to auto-generated docs |
600
- | Composition | Single class wins (the one closest in MRO) | Accumulated across the MRO; leaf class wins on conflict (header marks this) |
601
- | When to use | Total control over the rendered text | "Auto-doc + my extra do/don't rules" |
602
- | Skipped when | (always called if defined) | Skipped when a duck-typed `__agent_help__` is present (it owns the output) |
603
- | Mixin required? | No — duck-typed classmethod is enough | No — defining `__agent_notes__` on any class is enough |
605
+ | Aspect | `__agent_help__()` | `__agent_notes__()` |
606
+ | --------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
607
+ | Semantics | **Replacement** — returned string IS the output | **Additive** — appended to auto-generated docs |
608
+ | Composition | Single class wins (the one closest in MRO) | Accumulated across the MRO; leaf class wins on conflict (header marks this) |
609
+ | When to use | Total control over the rendered text | "Auto-doc + my extra do/don't rules" |
610
+ | Skipped when | (always called if defined) | Silently dropped (with `UserWarning`) when a custom `__agent_help__` is present (it owns the output) |
611
+ | Mixin required? | No — duck-typed classmethod is enough | No — defining `__agent_notes__` on any class is enough |
612
+
613
+ **Heads up on the both-defined case.** When a class defines both `__agent_help__()` and `__agent_notes__()`, the notes are silently dropped — `__agent_help__()` owns the output, and the auto-doc + notes path never runs. A `UserWarning` is emitted, but warnings are easy to miss in agent shells, CI logs, and notebooks — **treat "both defined" as a hard review error** rather than something the warning will reliably catch. Fix it by folding the notes into `__agent_help__()`, or by dropping the custom `__agent_help__()` and letting the auto-doc + notes path run.
604
614
 
605
615
  ## Class docstring hints
606
616
 
@@ -632,10 +642,11 @@ If a class inherits from `AgentReadableMixin`, coding agents should call `agent_
632
642
 
633
643
  ### `agent_help(obj)`
634
644
 
635
- Returns a string of agent-oriented documentation for a class, instance, or module.
645
+ Returns a string of agent-oriented documentation for a class, instance, module, function, or method.
636
646
 
637
- - For classes and instances: if `__agent_help__()` is defined (via mixin or duck-typing), it is called and its return value is used verbatim — duck-typed implementations are responsible for their own formatting and notes are NOT auto-appended. Otherwise, auto-generated docs are produced from introspection, with `__agent_notes__()` from every class in the MRO appended automatically. If `__agent_help__()` raises, falls back to the auto-generated path (which does include notes).
638
- - For modules: if the module defines a `__agent_help__` attribute (callable or string), it is used. Otherwise, auto-generated docs are produced from the module docstring and its public functions and classes.
647
+ - For classes and instances: if `__agent_help__()` is defined (via mixin or duck-typing), it is called and its return value is used verbatim — duck-typed implementations are responsible for their own formatting and notes are NOT auto-appended. If such a class also defines `__agent_notes__()`, a `UserWarning` is emitted because those notes are silently dropped (fold them into `__agent_help__()`, or drop the custom `__agent_help__()` to use the auto-doc path). Otherwise, auto-generated docs are produced from introspection, with `__agent_notes__()` from every class in the MRO appended automatically. If `__agent_help__()` raises, falls back to the auto-generated path (which does include notes).
648
+ - For modules: if the module defines a `__agent_help__` attribute (callable or string), it is used. Otherwise, auto-generated docs are produced from the module docstring and its public functions and classes. When the module defines `__all__`, that list is the authoritative public API (so re-exported symbols are included); otherwise public members are discovered by introspection, skipping private names and anything defined outside the module.
649
+ - For functions and methods (anything `inspect.isroutine` accepts): if the routine defines an `__agent_help__` attribute (callable or string), it is used. Otherwise, auto-generated docs render the signature, full docstring, and usage rules. A bound method's signature drops `self` and a classmethod's drops `cls`. If a callable `__agent_help__` raises, falls back to the auto-generated path.
639
650
 
640
651
  ## License
641
652