agent-readable 0.1.2__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 (24) hide show
  1. {agent_readable-0.1.2 → agent_readable-0.2.0}/PKG-INFO +79 -59
  2. {agent_readable-0.1.2 → agent_readable-0.2.0}/README.md +78 -58
  3. {agent_readable-0.1.2 → agent_readable-0.2.0}/docs/agent_help_vs_help.gif +0 -0
  4. {agent_readable-0.1.2 → agent_readable-0.2.0}/pyproject.toml +1 -1
  5. agent_readable-0.1.2/skills/agent-readable/SKILL.md +0 -183
  6. {agent_readable-0.1.2 → agent_readable-0.2.0}/.github/workflows/publish.yml +0 -0
  7. {agent_readable-0.1.2 → agent_readable-0.2.0}/.github/workflows/test.yml +0 -0
  8. {agent_readable-0.1.2 → agent_readable-0.2.0}/.gitignore +0 -0
  9. {agent_readable-0.1.2 → agent_readable-0.2.0}/CHANGELOG.md +0 -0
  10. {agent_readable-0.1.2 → agent_readable-0.2.0}/LICENSE +0 -0
  11. {agent_readable-0.1.2 → agent_readable-0.2.0}/examples/any_class.py +0 -0
  12. {agent_readable-0.1.2 → agent_readable-0.2.0}/examples/duck_type.py +0 -0
  13. {agent_readable-0.1.2 → agent_readable-0.2.0}/examples/modules_and_functions.py +0 -0
  14. {agent_readable-0.1.2 → agent_readable-0.2.0}/examples/sqlite_connection.py +0 -0
  15. {agent_readable-0.1.2 → agent_readable-0.2.0}/examples/temperature.py +0 -0
  16. {agent_readable-0.1.2 → agent_readable-0.2.0}/src/agent_readable/__init__.py +0 -0
  17. {agent_readable-0.1.2 → agent_readable-0.2.0}/src/agent_readable/__main__.py +0 -0
  18. {agent_readable-0.1.2 → agent_readable-0.2.0}/src/agent_readable/_model.py +0 -0
  19. {agent_readable-0.1.2 → agent_readable-0.2.0}/src/agent_readable/_protocol.py +0 -0
  20. {agent_readable-0.1.2 → agent_readable-0.2.0}/src/agent_readable/_render.py +0 -0
  21. {agent_readable-0.1.2 → agent_readable-0.2.0}/src/agent_readable/py.typed +0 -0
  22. {agent_readable-0.1.2 → agent_readable-0.2.0}/tests/__init__.py +0 -0
  23. {agent_readable-0.1.2 → agent_readable-0.2.0}/tests/test_cli.py +0 -0
  24. {agent_readable-0.1.2 → agent_readable-0.2.0}/tests/test_protocol.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-readable
3
- Version: 0.1.2
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
6
  Project-URL: Issues, https://github.com/zydo/agent-readable/issues
@@ -27,7 +27,13 @@ Description-Content-Type: text/markdown
27
27
 
28
28
  # agent-readable
29
29
 
30
- Stop coding agents from hallucinating your library's API. Ship the usage rules lifecycle order, pre-conditions, anti-patterns right next to your code.
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
+ ```
31
37
 
32
38
  <!-- markdownlint-disable MD033 -->
33
39
  <p align="center">
@@ -42,7 +48,7 @@ AI coding agents recognize established libraries from their training data, but t
42
48
 
43
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.
44
50
 
45
- `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 a curated public-API list with current signatures, the class's behavioral rules attached, and no inherited-dunder or MRO noise to wade through.
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.
46
52
 
47
53
  ## Installation
48
54
 
@@ -54,11 +60,51 @@ Requires Python 3.10+. No runtime dependencies.
54
60
 
55
61
  ## Quickstart
56
62
 
63
+ `agent_help()` works on **any Python class, module, function, or method — no opt-in required**. Point it at a stdlib class:
64
+
57
65
  ```python
58
- from agent_readable import AgentReadableMixin, agent_help
66
+ from agent_readable import agent_help
67
+ import logging
59
68
 
69
+ print(agent_help(logging.Logger))
70
+ ```
60
71
 
61
- 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:
62
108
  """Reads a value from a hardware sensor."""
63
109
 
64
110
  def __init__(self, pin: int, *, unit: str = "C"): ...
@@ -85,43 +131,7 @@ class Sensor(AgentReadableMixin):
85
131
  print(agent_help(Sensor))
86
132
  ```
87
133
 
88
- Output:
89
-
90
- ````
91
- # Sensor
92
-
93
- ## Constructor
94
-
95
- ```python
96
- Sensor(pin: int, *, unit: str = 'C')
97
- ```
98
-
99
- ## Purpose
100
-
101
- Reads a value from a hardware sensor.
102
-
103
- ## Public API
104
-
105
- - `calibrate(offset: float)` method: Apply a calibration offset.
106
- - `read() -> float` method: Read the current sensor value.
107
-
108
- ## Agent usage rules
109
-
110
- - Prefer the public API listed above.
111
- - Do not use private methods or attributes starting with `_`.
112
- - Do not invent unsupported behavior.
113
- - If usage is ambiguous, prefer the simplest documented usage pattern.
114
-
115
- ## Notes from class Sensor
116
-
117
- ## Do
118
-
119
- - Call `calibrate()` once during setup, before `read()`.
120
-
121
- ## Do not
122
-
123
- - Do not call `read()` before `calibrate()` on first use.
124
- ````
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.
125
135
 
126
136
  ## Why it matters
127
137
 
@@ -153,7 +163,7 @@ class Connection(builtins.object)
153
163
 
154
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.
155
165
 
156
- 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).
157
167
 
158
168
  ## Example 1: Wrapping an existing class
159
169
 
@@ -536,9 +546,13 @@ Outputs agent-oriented documentation for the given class, module, function, or m
536
546
 
537
547
  ### How does my agent know to call `agent_help()` instead of `help()`?
538
548
 
539
- Install the agent **skill** that ships in this repo at [`skills/agent-readable/`](skills/agent-readable/). It follows the [Agent Skills open standard](https://agentskills.io) — adopted by Claude Code, Codex CLI (OpenAI), Gemini CLI (Google), GitHub Copilot, Cursor, JetBrains Junie, Goose, OpenCode, and 40+ other tools — so dropping the folder into your agent's skills directory (`~/.claude/skills/`, `~/.codex/skills/`, your editor's equivalent) is enough for the agent to discover it. 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.
549
+ Install the companion agent skill:
550
+
551
+ ```bash
552
+ npx skills add zydo/skills --skill agent-readable
553
+ ```
540
554
 
541
- The folder lives next to the source code for now; a hub-published version (so you can install it through your harness's marketplace) is the next step 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.)
542
556
 
543
557
  ### How is this different from `AGENTS.md` / `llms.txt` / Cursor rules?
544
558
 
@@ -564,19 +578,23 @@ Yes. Two ways:
564
578
 
565
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.
566
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
+
567
585
  ## Keeping agent docs up to date
568
586
 
569
- Agent docs can go stale when classes change — new methods, changed behavior, removed APIs. Install the skill at [`skills/agent-readable/`](skills/agent-readable/) into your agent (see the FAQ above for the install). It teaches your agent to run `agent_help()` before modifying a class, prefer docstrings over `__agent_notes__()`, and verify that the output stays 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.
570
588
 
571
589
  ## The `__agent_help__` protocol
572
590
 
573
- `__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:
574
592
 
575
- - `__str__` (str) — string representation
576
- - `__rich_repr__` (Rich) — custom console representation
577
- - `__html__` (Django, Jinja2) — HTML rendering
578
- - `__array__` (NumPy) — array conversion
579
- - `__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.
580
598
 
581
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.
582
600
 
@@ -584,13 +602,15 @@ Classes that define a `@classmethod` named `__agent_help__` returning a `str` ar
584
602
 
585
603
  The two dunders intentionally encode different composition rules:
586
604
 
587
- | Aspect | `__agent_help__()` | `__agent_notes__()` |
588
- | --------------- | ----------------------------------------------- | --------------------------------------------------------------------------------------------- |
589
- | Semantics | **Replacement** — returned string IS the output | **Additive** — appended to auto-generated docs |
590
- | Composition | Single class wins (the one closest in MRO) | Accumulated across the MRO; leaf class wins on conflict (header marks this) |
591
- | When to use | Total control over the rendered text | "Auto-doc + my extra do/don't rules" |
592
- | Skipped when | (always called if defined) | Skipped (with a `UserWarning`) when a custom `__agent_help__` is present (it owns the output) |
593
- | 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.
594
614
 
595
615
  ## Class docstring hints
596
616
 
@@ -1,6 +1,12 @@
1
1
  # agent-readable
2
2
 
3
- Stop coding agents from hallucinating your library's API. Ship the usage rules lifecycle order, pre-conditions, anti-patterns right next to your code.
3
+ 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.
4
+
5
+ To let your coding agent automatically call `agent_help()` before using an unfamiliar API, install the skill:
6
+
7
+ ```bash
8
+ npx skills add zydo/skills --skill agent-readable
9
+ ```
4
10
 
5
11
  <!-- markdownlint-disable MD033 -->
6
12
  <p align="center">
@@ -15,7 +21,7 @@ AI coding agents recognize established libraries from their training data, but t
15
21
 
16
22
  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.
17
23
 
18
- `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 a curated public-API list with current signatures, the class's behavioral rules attached, and no inherited-dunder or MRO noise to wade through.
24
+ `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.
19
25
 
20
26
  ## Installation
21
27
 
@@ -27,11 +33,51 @@ Requires Python 3.10+. No runtime dependencies.
27
33
 
28
34
  ## Quickstart
29
35
 
36
+ `agent_help()` works on **any Python class, module, function, or method — no opt-in required**. Point it at a stdlib class:
37
+
30
38
  ```python
31
- from agent_readable import AgentReadableMixin, agent_help
39
+ from agent_readable import agent_help
40
+ import logging
32
41
 
42
+ print(agent_help(logging.Logger))
43
+ ```
33
44
 
34
- class Sensor(AgentReadableMixin):
45
+ Output (excerpted):
46
+
47
+ ````
48
+ # Logger
49
+
50
+ ## Constructor
51
+
52
+ ```python
53
+ Logger(name, level=0)
54
+ ```
55
+
56
+ ## Purpose
57
+
58
+ Instances of the Logger class represent a single logging channel. ...
59
+
60
+ ## Public API
61
+
62
+ - `addHandler(hdlr)` method: Add the specified handler to this logger.
63
+ - `debug(msg, *args, **kwargs)` method: Log 'msg % args' with severity 'DEBUG'.
64
+ - `info(msg, *args, **kwargs)` method: Log 'msg % args' with severity 'INFO'.
65
+ - `setLevel(level)` method: Set the logging level of this logger.
66
+ - `warning(msg, *args, **kwargs)` method: Log 'msg % args' with severity 'WARNING'.
67
+ - ...
68
+ ````
69
+
70
+ 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.
71
+
72
+ ### Library authors: ship usage rules next to the code (optional)
73
+
74
+ 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:
75
+
76
+ ```python
77
+ from agent_readable import agent_help
78
+
79
+
80
+ class Sensor:
35
81
  """Reads a value from a hardware sensor."""
36
82
 
37
83
  def __init__(self, pin: int, *, unit: str = "C"): ...
@@ -58,43 +104,7 @@ class Sensor(AgentReadableMixin):
58
104
  print(agent_help(Sensor))
59
105
  ```
60
106
 
61
- Output:
62
-
63
- ````
64
- # Sensor
65
-
66
- ## Constructor
67
-
68
- ```python
69
- Sensor(pin: int, *, unit: str = 'C')
70
- ```
71
-
72
- ## Purpose
73
-
74
- Reads a value from a hardware sensor.
75
-
76
- ## Public API
77
-
78
- - `calibrate(offset: float)` method: Apply a calibration offset.
79
- - `read() -> float` method: Read the current sensor value.
80
-
81
- ## Agent usage rules
82
-
83
- - Prefer the public API listed above.
84
- - Do not use private methods or attributes starting with `_`.
85
- - Do not invent unsupported behavior.
86
- - If usage is ambiguous, prefer the simplest documented usage pattern.
87
-
88
- ## Notes from class Sensor
89
-
90
- ## Do
91
-
92
- - Call `calibrate()` once during setup, before `read()`.
93
-
94
- ## Do not
95
-
96
- - Do not call `read()` before `calibrate()` on first use.
97
- ````
107
+ `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.
98
108
 
99
109
  ## Why it matters
100
110
 
@@ -126,7 +136,7 @@ class Connection(builtins.object)
126
136
 
127
137
  `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.
128
138
 
129
- The examples below demonstrate four ways to use `agent_help()`.
139
+ 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).
130
140
 
131
141
  ## Example 1: Wrapping an existing class
132
142
 
@@ -509,9 +519,13 @@ Outputs agent-oriented documentation for the given class, module, function, or m
509
519
 
510
520
  ### How does my agent know to call `agent_help()` instead of `help()`?
511
521
 
512
- Install the agent **skill** that ships in this repo at [`skills/agent-readable/`](skills/agent-readable/). It follows the [Agent Skills open standard](https://agentskills.io) — adopted by Claude Code, Codex CLI (OpenAI), Gemini CLI (Google), GitHub Copilot, Cursor, JetBrains Junie, Goose, OpenCode, and 40+ other tools — so dropping the folder into your agent's skills directory (`~/.claude/skills/`, `~/.codex/skills/`, your editor's equivalent) is enough for the agent to discover it. 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.
522
+ Install the companion agent skill:
523
+
524
+ ```bash
525
+ npx skills add zydo/skills --skill agent-readable
526
+ ```
513
527
 
514
- The folder lives next to the source code for now; a hub-published version (so you can install it through your harness's marketplace) is the next step on the roadmap.
528
+ 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.)
515
529
 
516
530
  ### How is this different from `AGENTS.md` / `llms.txt` / Cursor rules?
517
531
 
@@ -537,19 +551,23 @@ Yes. Two ways:
537
551
 
538
552
  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.
539
553
 
554
+ ### Is `agent_help()` the strongest fix for API hallucination?
555
+
556
+ 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.
557
+
540
558
  ## Keeping agent docs up to date
541
559
 
542
- Agent docs can go stale when classes change — new methods, changed behavior, removed APIs. Install the skill at [`skills/agent-readable/`](skills/agent-readable/) into your agent (see the FAQ above for the install). It teaches your agent to run `agent_help()` before modifying a class, prefer docstrings over `__agent_notes__()`, and verify that the output stays accurate after changes.
560
+ 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.
543
561
 
544
562
  ## The `__agent_help__` protocol
545
563
 
546
- `__agent_help__()` is a dunder protocol, similar in spirit to ecosystem protocols such as:
564
+ `__agent_help__()` is a dunder protocol for tool-specific documentation, similar in spirit to:
547
565
 
548
- - `__str__` (str) — string representation
549
- - `__rich_repr__` (Rich) — custom console representation
550
- - `__html__` (Django, Jinja2) — HTML rendering
551
- - `__array__` (NumPy) — array conversion
552
- - `__fspath__` (os.fspath)filesystem path conversion
566
+ - `__doc__` (Python `help()`, Sphinx, IDEs, REPLs) the canonical "this is my human-readable documentation" slot, read by inspection tools and surfaced through `help()`
567
+ - `__rich_repr__` (Rich) — Rich-specific console representation read only when Rich renders the object
568
+ - `__html__` (Django, Jinja2, MarkupSafe) — HTML-renderer-specific representation read only when those template engines render the object
569
+
570
+ 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.
553
571
 
554
572
  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.
555
573
 
@@ -557,13 +575,15 @@ Classes that define a `@classmethod` named `__agent_help__` returning a `str` ar
557
575
 
558
576
  The two dunders intentionally encode different composition rules:
559
577
 
560
- | Aspect | `__agent_help__()` | `__agent_notes__()` |
561
- | --------------- | ----------------------------------------------- | --------------------------------------------------------------------------------------------- |
562
- | Semantics | **Replacement** — returned string IS the output | **Additive** — appended to auto-generated docs |
563
- | Composition | Single class wins (the one closest in MRO) | Accumulated across the MRO; leaf class wins on conflict (header marks this) |
564
- | When to use | Total control over the rendered text | "Auto-doc + my extra do/don't rules" |
565
- | Skipped when | (always called if defined) | Skipped (with a `UserWarning`) when a custom `__agent_help__` is present (it owns the output) |
566
- | Mixin required? | No — duck-typed classmethod is enough | No — defining `__agent_notes__` on any class is enough |
578
+ | Aspect | `__agent_help__()` | `__agent_notes__()` |
579
+ | --------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
580
+ | Semantics | **Replacement** — returned string IS the output | **Additive** — appended to auto-generated docs |
581
+ | Composition | Single class wins (the one closest in MRO) | Accumulated across the MRO; leaf class wins on conflict (header marks this) |
582
+ | When to use | Total control over the rendered text | "Auto-doc + my extra do/don't rules" |
583
+ | Skipped when | (always called if defined) | Silently dropped (with `UserWarning`) when a custom `__agent_help__` is present (it owns the output) |
584
+ | Mixin required? | No — duck-typed classmethod is enough | No — defining `__agent_notes__` on any class is enough |
585
+
586
+ **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.
567
587
 
568
588
  ## Class docstring hints
569
589
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "agent-readable"
7
- version = "0.1.2"
7
+ version = "0.2.0"
8
8
  description = "A lightweight Python protocol for agent-oriented documentation"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -1,183 +0,0 @@
1
- ---
2
- name: agent-readable
3
- description: Use agent_readable to get authoritative usage guidance for Python classes, modules, functions, and methods before writing code against them, and to make new Python APIs agent-readable. Activate when writing or modifying Python code that calls into a class, module, function, or method from any library, OR when adding/changing a public Python API. Covers calling `agent_help(obj)` for structured docs + behavioral rules, and authoring docstrings + `__agent_notes__()` so usage rules travel with the code.
4
- ---
5
-
6
- # agent-readable
7
-
8
- `agent-readable` is a tiny Python library (`pip install agent-readable`, zero
9
- runtime deps, Python 3.10+) that gives any class, module, function, or method a
10
- structured, agent-oriented "usage guide" — the canonical answer to *how do I
11
- correctly use this?* — produced by one call: `agent_help(obj)`.
12
-
13
- This skill teaches you two jobs:
14
-
15
- 1. **Consume** — before writing Python code that uses a class, module, function,
16
- or method, call `agent_help(target)` and use the output as the source of truth.
17
- 2. **Author** — when adding or modifying a public Python API, make it
18
- agent-readable so future agents get correct usage on the first try.
19
-
20
- **Current language support:** Python 3.10+. Counterparts in other languages are
21
- on the roadmap; when they ship, this skill will gain sections for them and the
22
- same trigger will fan out across languages.
23
-
24
- ## When to activate this skill
25
-
26
- * The user asks you to write, modify, or refactor Python code that calls into a
27
- class, module, function, or method (from a third-party library or from this
28
- project).
29
- * The user asks you to add or change a public Python class, module, or function
30
- (especially one other code or other agents will use).
31
- * The user mentions `agent_help`, `__agent_notes__`, `AgentReadableMixin`, the
32
- `AgentReadable` protocol, or the `agent-readable` library by name.
33
-
34
- ## Install
35
-
36
- ```bash
37
- pip install agent-readable # or: uv add agent-readable
38
- ```
39
-
40
- Python 3.10+. No runtime dependencies. The library exposes one top-level function
41
- (`agent_help`), one protocol (`AgentReadable`), and one optional mixin
42
- (`AgentReadableMixin`).
43
-
44
- ## Job 1 — Consume: call `agent_help(target)` first
45
-
46
- **Before writing code against a Python target, always call `agent_help(target)`.**
47
- Even if the target does *not* opt in: `agent_help()` falls back to introspection
48
- and returns a structured, current-signature public-API listing — strictly better
49
- than guessing from training data.
50
-
51
- ```python
52
- from agent_readable import agent_help
53
-
54
- print(agent_help(SomeClass)) # class — constructor, public API, notes
55
- print(agent_help(some_instance)) # instance — dispatches to its class
56
- print(agent_help(some_module)) # module — docstring + public functions/classes
57
- print(agent_help(some_func)) # function or method — signature + docstring
58
- ```
59
-
60
- From a coding-agent shell:
61
-
62
- ```bash
63
- python -c "from agent_readable import agent_help; import target_lib; print(agent_help(target_lib.SomeClass))"
64
- ```
65
-
66
- ### How to read the output
67
-
68
- `agent_help()` returns Markdown with these sections (a subset appears depending
69
- on the target):
70
-
71
- * `# <name>` and `## Purpose` — the docstring summary.
72
- * `## Constructor` / `## Signature` — the **current** signature (the source of
73
- truth, beats anything in training data).
74
- * `## Public API` — every public member with current signatures. Treat this as
75
- the authoritative list; do not call methods not listed here.
76
- * `## Agent usage rules` — generic rules (prefer the public API, no private
77
- names, etc.).
78
- * `## Notes from class <X>` — **load-bearing** class-specific behavioral rules:
79
- lifecycle order, pre-conditions, anti-patterns. If a note says *"call
80
- `calibrate()` once before `read()`"*, honor it. If multiple `Notes from class`
81
- sections appear, the leaf class wins on conflict (the header marks this).
82
-
83
- If you are unsure how to use a target, run `agent_help()` again — it is cheap and
84
- the docstrings/notes are the canonical source.
85
-
86
- ## Job 2 — Author: make new Python APIs agent-readable
87
-
88
- Default to **better docstrings** first; only reach for the dunders when
89
- docstrings cannot compose the rule.
90
-
91
- ### 2a. Docstrings are the primary surface
92
-
93
- `agent_help()` already extracts docstrings: the class docstring becomes
94
- `## Purpose`, and each method's first paragraph becomes its `## Public API`
95
- summary. So the bar for a good docstring here is the same as for any
96
- well-documented library: concise summary line, then params/returns/raises if
97
- nontrivial. Keep per-method behavior in the method's docstring — that is where it
98
- stays attached through refactors.
99
-
100
- ### 2b. Add `__agent_notes__()` only for cross-method behavioral rules
101
-
102
- When a rule does not fit in any single method's docstring — lifecycle order
103
- across methods, pre-conditions, anti-patterns ("use `call()` for non-streaming,
104
- `stream()` for streaming"), do/don't lists — put it on the **class** as a
105
- `classmethod` named `__agent_notes__()`:
106
-
107
- ```python
108
- class Sensor:
109
- """Reads a value from a hardware sensor."""
110
-
111
- def __init__(self, pin: int, *, unit: str = "C"): ...
112
- def calibrate(self, offset: float): ...
113
- def read(self) -> float: ...
114
-
115
- @classmethod
116
- def __agent_notes__(cls) -> str:
117
- return """
118
- ## Do
119
-
120
- - Call `calibrate()` once during setup, before `read()`.
121
-
122
- ## Do not
123
-
124
- - Do not call `read()` before `calibrate()` on first use.
125
- """
126
- ```
127
-
128
- Facts about `__agent_notes__()` worth knowing:
129
-
130
- * **Accumulates across the MRO.** Every class in the inheritance chain that
131
- defines its own `__agent_notes__()` contributes a section to `agent_help()`'s
132
- output. The leaf class is marked as taking precedence on conflict.
133
- * **Do not call `super().__agent_notes__()`** — collection is automatic.
134
- * **No mixin required.** Defining `__agent_notes__` on any class is enough.
135
- Monkey-patching also works for classes you do not own:
136
- ```python
137
- ThirdPartyClass.__agent_notes__ = classmethod(lambda cls: "...")
138
- ```
139
- * **Optional `AgentReadableMixin`.** Inheriting from
140
- `agent_readable.AgentReadableMixin` is purely a convenience for type-checking
141
- and IDE hints — `agent_help()` works on plain classes too.
142
-
143
- ### 2c. Almost never define a custom `__agent_help__()`
144
-
145
- `__agent_help__()` **replaces** the entire auto-generated output. Use it only
146
- when you have a hand-formatted help string you genuinely want to ship verbatim.
147
- In every other case, let the auto-doc generate from your class + docstrings +
148
- `__agent_notes__()`.
149
-
150
- ### 2d. Verify after annotating
151
-
152
- After adding or changing notes, run:
153
-
154
- ```bash
155
- python -c "from agent_readable import agent_help; from your_module import YourClass; print(agent_help(YourClass))"
156
- ```
157
-
158
- Check that signatures are correct (you may need to fix type hints), notes appear
159
- in MRO order with the leaf class marked precedence-winning, and no private
160
- members leaked into `## Public API`.
161
-
162
- ## Footguns
163
-
164
- * **Do not define both a custom `__agent_help__()` and `__agent_notes__()` on
165
- the same class.** A custom `__agent_help__()` owns the full output, so the
166
- notes are silently dropped (a `UserWarning` is emitted). Fix it one of two
167
- ways: fold the notes into your custom `__agent_help__()`, or remove the
168
- custom `__agent_help__()` and let the auto-doc + notes path run.
169
- * **Do not put per-method instructions in `__agent_notes__()`.** Single-method
170
- behavior belongs in that method's docstring, where it stays attached through
171
- refactors. `__agent_notes__()` is for rules that span methods.
172
- * **Do not mix `agent_help()` with `help()`.** `help()` returns hundreds of
173
- lines of inherited dunders and MRO detail; `agent_help()` returns a curated,
174
- structured guide with behavioral rules attached. Use `agent_help()`.
175
-
176
- ## The one rule
177
-
178
- > Before writing or modifying Python code that uses an external class, module,
179
- > function, or method, call `agent_help(target)` and use its output as the
180
- > canonical usage guide.
181
-
182
- That call is cheap, runs in any coding-agent shell, and works on any Python
183
- object — annotated or not.
File without changes