agent-readable 0.1.1__tar.gz → 0.1.2__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.
- {agent_readable-0.1.1 → agent_readable-0.1.2}/.github/workflows/test.yml +17 -0
- agent_readable-0.1.2/CHANGELOG.md +73 -0
- {agent_readable-0.1.1 → agent_readable-0.1.2}/PKG-INFO +41 -50
- {agent_readable-0.1.1 → agent_readable-0.1.2}/README.md +38 -48
- {agent_readable-0.1.1 → agent_readable-0.1.2}/examples/any_class.py +7 -6
- {agent_readable-0.1.1 → agent_readable-0.1.2}/examples/duck_type.py +2 -2
- {agent_readable-0.1.1 → agent_readable-0.1.2}/examples/modules_and_functions.py +1 -1
- {agent_readable-0.1.1 → agent_readable-0.1.2}/examples/sqlite_connection.py +2 -2
- {agent_readable-0.1.1 → agent_readable-0.1.2}/examples/temperature.py +2 -2
- {agent_readable-0.1.1 → agent_readable-0.1.2}/pyproject.toml +15 -3
- agent_readable-0.1.2/skills/agent-readable/SKILL.md +183 -0
- {agent_readable-0.1.1 → agent_readable-0.1.2}/src/agent_readable/__init__.py +1 -1
- agent_readable-0.1.2/src/agent_readable/_model.py +301 -0
- agent_readable-0.1.2/src/agent_readable/_protocol.py +194 -0
- agent_readable-0.1.2/src/agent_readable/_render.py +64 -0
- agent_readable-0.1.2/tests/__init__.py +0 -0
- {agent_readable-0.1.1 → agent_readable-0.1.2}/tests/test_cli.py +1 -1
- {agent_readable-0.1.1 → agent_readable-0.1.2}/tests/test_protocol.py +198 -4
- agent_readable-0.1.1/AGENT-PROMPT.md +0 -82
- agent_readable-0.1.1/src/agent_readable/_protocol.py +0 -417
- {agent_readable-0.1.1 → agent_readable-0.1.2}/.github/workflows/publish.yml +0 -0
- {agent_readable-0.1.1 → agent_readable-0.1.2}/.gitignore +0 -0
- {agent_readable-0.1.1 → agent_readable-0.1.2}/LICENSE +0 -0
- {agent_readable-0.1.1 → agent_readable-0.1.2}/docs/agent_help_vs_help.gif +0 -0
- {agent_readable-0.1.1 → agent_readable-0.1.2}/src/agent_readable/__main__.py +0 -0
- /agent_readable-0.1.1/tests/__init__.py → /agent_readable-0.1.2/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.
|
|
3
|
+
Version: 0.1.2
|
|
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,7 @@ Description-Content-Type: text/markdown
|
|
|
26
27
|
|
|
27
28
|
# agent-readable
|
|
28
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
31
|
|
|
31
32
|
<!-- markdownlint-disable MD033 -->
|
|
32
33
|
<p align="center">
|
|
@@ -39,16 +40,9 @@ A lightweight Python protocol for exposing agent-oriented documentation from cla
|
|
|
39
40
|
|
|
40
41
|
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
42
|
|
|
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()`
|
|
43
|
+
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.
|
|
43
44
|
|
|
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.
|
|
45
|
-
|
|
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.
|
|
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.
|
|
52
46
|
|
|
53
47
|
## Installation
|
|
54
48
|
|
|
@@ -131,12 +125,15 @@ Reads a value from a hardware sensor.
|
|
|
131
125
|
|
|
132
126
|
## Why it matters
|
|
133
127
|
|
|
134
|
-
`
|
|
128
|
+
`agent_help()` covers two failure modes agents hit on real APIs:
|
|
135
129
|
|
|
136
|
-
`
|
|
130
|
+
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.
|
|
131
|
+
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.
|
|
132
|
+
|
|
133
|
+
A concrete contrast:
|
|
137
134
|
|
|
138
135
|
```
|
|
139
|
-
# help(sqlite3.Connection) —
|
|
136
|
+
# help(sqlite3.Connection) — every inherited dunder, in source order:
|
|
140
137
|
Help on class Connection in module sqlite3:
|
|
141
138
|
|
|
142
139
|
class Connection(builtins.object)
|
|
@@ -150,10 +147,11 @@ class Connection(builtins.object)
|
|
|
150
147
|
| ...
|
|
151
148
|
| backup(self, target, /, *, pages=-1, progress=None, ...)
|
|
152
149
|
| blobopen(self, table, column, rowid, /, *, readonly=False, ...)
|
|
153
|
-
| ... (
|
|
150
|
+
| ... (no curation, no behavioral rules, no signal that backup() needs an
|
|
151
|
+
| open target connection before being called)
|
|
154
152
|
```
|
|
155
153
|
|
|
156
|
-
`agent_help(sqlite3.Connection)` produces a
|
|
154
|
+
`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
155
|
|
|
158
156
|
The examples below demonstrate four ways to use `agent_help()`.
|
|
159
157
|
|
|
@@ -190,14 +188,14 @@ An agent-friendly wrapper around sqlite3.Connection.
|
|
|
190
188
|
|
|
191
189
|
## Public API
|
|
192
190
|
|
|
193
|
-
- `backup(
|
|
191
|
+
- `backup(target, *, pages=-1, progress=None, name='main', sleep=0.25)` method: Makes a backup of the database.
|
|
194
192
|
- ...
|
|
195
|
-
- `close(
|
|
196
|
-
- `commit(
|
|
193
|
+
- `close()` method: Close the database connection.
|
|
194
|
+
- `commit()` method: Commit any pending transaction to the database.
|
|
197
195
|
- ...
|
|
198
196
|
- `execute(...)` method: Executes an SQL statement.
|
|
199
197
|
- ...
|
|
200
|
-
- `rollback(
|
|
198
|
+
- `rollback()` method: Roll back to the start of any pending transaction.
|
|
201
199
|
- ...
|
|
202
200
|
|
|
203
201
|
## Agent usage rules
|
|
@@ -366,7 +364,7 @@ print(agent_help(RateLimiter))
|
|
|
366
364
|
|
|
367
365
|
## Example 4: Any class — no setup required
|
|
368
366
|
|
|
369
|
-
Even without the mixin or duck-typing, `agent_help()` still generates
|
|
367
|
+
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
368
|
Full example: [`examples/any_class.py`](examples/any_class.py).
|
|
371
369
|
|
|
372
370
|
```python
|
|
@@ -376,7 +374,7 @@ from agent_readable import agent_help
|
|
|
376
374
|
print(agent_help(logging.Logger))
|
|
377
375
|
```
|
|
378
376
|
|
|
379
|
-
`
|
|
377
|
+
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
378
|
|
|
381
379
|
````
|
|
382
380
|
# Logger
|
|
@@ -429,13 +427,15 @@ Output:
|
|
|
429
427
|
|
|
430
428
|
## Purpose
|
|
431
429
|
|
|
432
|
-
Example: Using agent_help() on modules.
|
|
430
|
+
Example: Using agent_help() on modules, functions, and methods.
|
|
433
431
|
|
|
434
|
-
Demonstrates
|
|
432
|
+
Demonstrates non-class targets:
|
|
435
433
|
1. A custom module (this file itself).
|
|
436
434
|
2. A stdlib module (pathlib).
|
|
435
|
+
3. A function (connect, defined below).
|
|
436
|
+
4. A method (Query.execute, defined below).
|
|
437
437
|
|
|
438
|
-
Run this file to see
|
|
438
|
+
Run this file to see all outputs:
|
|
439
439
|
python examples/modules_and_functions.py
|
|
440
440
|
|
|
441
441
|
## Public API
|
|
@@ -476,20 +476,13 @@ operating systems.
|
|
|
476
476
|
|
|
477
477
|
## Public API
|
|
478
478
|
|
|
479
|
-
- `DirEntryInfo` class: Implementation of pathlib.types.PathInfo that provides status
|
|
480
479
|
- `Path` class: PurePath subclass that can make system calls.
|
|
481
|
-
- `PathInfo` class: Implementation of pathlib.types.PathInfo that provides status
|
|
482
480
|
- `PosixPath` class: Path subclass for non-Windows systems.
|
|
483
481
|
- `PurePath` class: Base class for manipulating paths without I/O.
|
|
484
482
|
- `PurePosixPath` class: PurePath subclass for non-Windows systems.
|
|
485
483
|
- `PureWindowsPath` class: PurePath subclass for Windows systems.
|
|
486
484
|
- `UnsupportedOperation` class: An exception that is raised when an unsupported operation is attempted.
|
|
487
485
|
- `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
486
|
|
|
494
487
|
## Agent usage rules
|
|
495
488
|
|
|
@@ -499,7 +492,7 @@ operating systems.
|
|
|
499
492
|
- If usage is ambiguous, prefer the simplest documented usage pattern.
|
|
500
493
|
````
|
|
501
494
|
|
|
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.
|
|
495
|
+
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
496
|
|
|
504
497
|
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
498
|
|
|
@@ -543,12 +536,9 @@ Outputs agent-oriented documentation for the given class, module, function, or m
|
|
|
543
536
|
|
|
544
537
|
### How does my agent know to call `agent_help()` instead of `help()`?
|
|
545
538
|
|
|
546
|
-
|
|
547
|
-
|
|
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.
|
|
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.
|
|
550
540
|
|
|
551
|
-
|
|
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.
|
|
552
542
|
|
|
553
543
|
### How is this different from `AGENTS.md` / `llms.txt` / Cursor rules?
|
|
554
544
|
|
|
@@ -576,7 +566,7 @@ Yes — `agent_help()` falls back to introspection (Example 4). You get a struct
|
|
|
576
566
|
|
|
577
567
|
## Keeping agent docs up to date
|
|
578
568
|
|
|
579
|
-
Agent docs can go stale when classes change — new methods, changed behavior, removed APIs.
|
|
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.
|
|
580
570
|
|
|
581
571
|
## The `__agent_help__` protocol
|
|
582
572
|
|
|
@@ -594,13 +584,13 @@ Classes that define a `@classmethod` named `__agent_help__` returning a `str` ar
|
|
|
594
584
|
|
|
595
585
|
The two dunders intentionally encode different composition rules:
|
|
596
586
|
|
|
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
|
|
603
|
-
| Mixin required? | No — duck-typed classmethod is enough | No — defining `__agent_notes__` on any class is enough
|
|
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 |
|
|
604
594
|
|
|
605
595
|
## Class docstring hints
|
|
606
596
|
|
|
@@ -632,10 +622,11 @@ If a class inherits from `AgentReadableMixin`, coding agents should call `agent_
|
|
|
632
622
|
|
|
633
623
|
### `agent_help(obj)`
|
|
634
624
|
|
|
635
|
-
Returns a string of agent-oriented documentation for a class, instance, or
|
|
625
|
+
Returns a string of agent-oriented documentation for a class, instance, module, function, or method.
|
|
636
626
|
|
|
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.
|
|
627
|
+
- 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).
|
|
628
|
+
- 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.
|
|
629
|
+
- 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
630
|
|
|
640
631
|
## License
|
|
641
632
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# agent-readable
|
|
2
2
|
|
|
3
|
-
|
|
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.
|
|
4
4
|
|
|
5
5
|
<!-- markdownlint-disable MD033 -->
|
|
6
6
|
<p align="center">
|
|
@@ -13,16 +13,9 @@ A lightweight Python protocol for exposing agent-oriented documentation from cla
|
|
|
13
13
|
|
|
14
14
|
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."*
|
|
15
15
|
|
|
16
|
-
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()`
|
|
16
|
+
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
17
|
|
|
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 the rules.
|
|
19
|
-
|
|
20
|
-
```text
|
|
21
|
-
help(logging.Logger) 217 lines — every inherited method, dunder, and MRO detail.
|
|
22
|
-
agent_help(logging.Logger) 56 lines — structured sections + any author notes.
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
The compactness is a side effect; the structure is the point.
|
|
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.
|
|
26
19
|
|
|
27
20
|
## Installation
|
|
28
21
|
|
|
@@ -105,12 +98,15 @@ Reads a value from a hardware sensor.
|
|
|
105
98
|
|
|
106
99
|
## Why it matters
|
|
107
100
|
|
|
108
|
-
`
|
|
101
|
+
`agent_help()` covers two failure modes agents hit on real APIs:
|
|
109
102
|
|
|
110
|
-
`
|
|
103
|
+
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.
|
|
104
|
+
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.
|
|
105
|
+
|
|
106
|
+
A concrete contrast:
|
|
111
107
|
|
|
112
108
|
```
|
|
113
|
-
# help(sqlite3.Connection) —
|
|
109
|
+
# help(sqlite3.Connection) — every inherited dunder, in source order:
|
|
114
110
|
Help on class Connection in module sqlite3:
|
|
115
111
|
|
|
116
112
|
class Connection(builtins.object)
|
|
@@ -124,10 +120,11 @@ class Connection(builtins.object)
|
|
|
124
120
|
| ...
|
|
125
121
|
| backup(self, target, /, *, pages=-1, progress=None, ...)
|
|
126
122
|
| blobopen(self, table, column, rowid, /, *, readonly=False, ...)
|
|
127
|
-
| ... (
|
|
123
|
+
| ... (no curation, no behavioral rules, no signal that backup() needs an
|
|
124
|
+
| open target connection before being called)
|
|
128
125
|
```
|
|
129
126
|
|
|
130
|
-
`agent_help(sqlite3.Connection)` produces a
|
|
127
|
+
`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.
|
|
131
128
|
|
|
132
129
|
The examples below demonstrate four ways to use `agent_help()`.
|
|
133
130
|
|
|
@@ -164,14 +161,14 @@ An agent-friendly wrapper around sqlite3.Connection.
|
|
|
164
161
|
|
|
165
162
|
## Public API
|
|
166
163
|
|
|
167
|
-
- `backup(
|
|
164
|
+
- `backup(target, *, pages=-1, progress=None, name='main', sleep=0.25)` method: Makes a backup of the database.
|
|
168
165
|
- ...
|
|
169
|
-
- `close(
|
|
170
|
-
- `commit(
|
|
166
|
+
- `close()` method: Close the database connection.
|
|
167
|
+
- `commit()` method: Commit any pending transaction to the database.
|
|
171
168
|
- ...
|
|
172
169
|
- `execute(...)` method: Executes an SQL statement.
|
|
173
170
|
- ...
|
|
174
|
-
- `rollback(
|
|
171
|
+
- `rollback()` method: Roll back to the start of any pending transaction.
|
|
175
172
|
- ...
|
|
176
173
|
|
|
177
174
|
## Agent usage rules
|
|
@@ -340,7 +337,7 @@ print(agent_help(RateLimiter))
|
|
|
340
337
|
|
|
341
338
|
## Example 4: Any class — no setup required
|
|
342
339
|
|
|
343
|
-
Even without the mixin or duck-typing, `agent_help()` still generates
|
|
340
|
+
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.
|
|
344
341
|
Full example: [`examples/any_class.py`](examples/any_class.py).
|
|
345
342
|
|
|
346
343
|
```python
|
|
@@ -350,7 +347,7 @@ from agent_readable import agent_help
|
|
|
350
347
|
print(agent_help(logging.Logger))
|
|
351
348
|
```
|
|
352
349
|
|
|
353
|
-
`
|
|
350
|
+
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:
|
|
354
351
|
|
|
355
352
|
````
|
|
356
353
|
# Logger
|
|
@@ -403,13 +400,15 @@ Output:
|
|
|
403
400
|
|
|
404
401
|
## Purpose
|
|
405
402
|
|
|
406
|
-
Example: Using agent_help() on modules.
|
|
403
|
+
Example: Using agent_help() on modules, functions, and methods.
|
|
407
404
|
|
|
408
|
-
Demonstrates
|
|
405
|
+
Demonstrates non-class targets:
|
|
409
406
|
1. A custom module (this file itself).
|
|
410
407
|
2. A stdlib module (pathlib).
|
|
408
|
+
3. A function (connect, defined below).
|
|
409
|
+
4. A method (Query.execute, defined below).
|
|
411
410
|
|
|
412
|
-
Run this file to see
|
|
411
|
+
Run this file to see all outputs:
|
|
413
412
|
python examples/modules_and_functions.py
|
|
414
413
|
|
|
415
414
|
## Public API
|
|
@@ -450,20 +449,13 @@ operating systems.
|
|
|
450
449
|
|
|
451
450
|
## Public API
|
|
452
451
|
|
|
453
|
-
- `DirEntryInfo` class: Implementation of pathlib.types.PathInfo that provides status
|
|
454
452
|
- `Path` class: PurePath subclass that can make system calls.
|
|
455
|
-
- `PathInfo` class: Implementation of pathlib.types.PathInfo that provides status
|
|
456
453
|
- `PosixPath` class: Path subclass for non-Windows systems.
|
|
457
454
|
- `PurePath` class: Base class for manipulating paths without I/O.
|
|
458
455
|
- `PurePosixPath` class: PurePath subclass for non-Windows systems.
|
|
459
456
|
- `PureWindowsPath` class: PurePath subclass for Windows systems.
|
|
460
457
|
- `UnsupportedOperation` class: An exception that is raised when an unsupported operation is attempted.
|
|
461
458
|
- `WindowsPath` class: Path subclass for Windows systems.
|
|
462
|
-
- `copy_info(info, target, follow_symlinks=True)` function: Copy metadata from the given PathInfo to the given local path.
|
|
463
|
-
- `copyfileobj(source_f, target_f)` function: Copy data from file-like object source_f to file-like object target_f.
|
|
464
|
-
- `ensure_different_files(source, target)` function: Raise OSError(EINVAL) if both paths refer to the same file.
|
|
465
|
-
- `ensure_distinct_paths(source, target)` function: Raise OSError(EINVAL) if the other path is within this path.
|
|
466
|
-
- `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
|
|
467
459
|
|
|
468
460
|
## Agent usage rules
|
|
469
461
|
|
|
@@ -473,7 +465,7 @@ operating systems.
|
|
|
473
465
|
- If usage is ambiguous, prefer the simplest documented usage pattern.
|
|
474
466
|
````
|
|
475
467
|
|
|
476
|
-
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.
|
|
468
|
+
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.
|
|
477
469
|
|
|
478
470
|
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.
|
|
479
471
|
|
|
@@ -517,12 +509,9 @@ Outputs agent-oriented documentation for the given class, module, function, or m
|
|
|
517
509
|
|
|
518
510
|
### How does my agent know to call `agent_help()` instead of `help()`?
|
|
519
511
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
- Paste [`AGENT-PROMPT.md`](AGENT-PROMPT.md) into your conversation, or
|
|
523
|
-
- Add it permanently to your repo's `AGENTS.md` / `CLAUDE.md` / `.cursor/rules` / equivalent instruction file.
|
|
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.
|
|
524
513
|
|
|
525
|
-
|
|
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.
|
|
526
515
|
|
|
527
516
|
### How is this different from `AGENTS.md` / `llms.txt` / Cursor rules?
|
|
528
517
|
|
|
@@ -550,7 +539,7 @@ Yes — `agent_help()` falls back to introspection (Example 4). You get a struct
|
|
|
550
539
|
|
|
551
540
|
## Keeping agent docs up to date
|
|
552
541
|
|
|
553
|
-
Agent docs can go stale when classes change — new methods, changed behavior, removed APIs.
|
|
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.
|
|
554
543
|
|
|
555
544
|
## The `__agent_help__` protocol
|
|
556
545
|
|
|
@@ -568,13 +557,13 @@ Classes that define a `@classmethod` named `__agent_help__` returning a `str` ar
|
|
|
568
557
|
|
|
569
558
|
The two dunders intentionally encode different composition rules:
|
|
570
559
|
|
|
571
|
-
| Aspect | `__agent_help__()` | `__agent_notes__()`
|
|
572
|
-
| --------------- | ----------------------------------------------- |
|
|
573
|
-
| Semantics | **Replacement** — returned string IS the output | **Additive** — appended to auto-generated docs
|
|
574
|
-
| Composition | Single class wins (the one closest in MRO) | Accumulated across the MRO; leaf class wins on conflict (header marks this)
|
|
575
|
-
| When to use | Total control over the rendered text | "Auto-doc + my extra do/don't rules"
|
|
576
|
-
| Skipped when | (always called if defined) | Skipped when a
|
|
577
|
-
| Mixin required? | No — duck-typed classmethod is enough | No — defining `__agent_notes__` on any class is enough
|
|
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
567
|
|
|
579
568
|
## Class docstring hints
|
|
580
569
|
|
|
@@ -606,10 +595,11 @@ If a class inherits from `AgentReadableMixin`, coding agents should call `agent_
|
|
|
606
595
|
|
|
607
596
|
### `agent_help(obj)`
|
|
608
597
|
|
|
609
|
-
Returns a string of agent-oriented documentation for a class, instance, or
|
|
598
|
+
Returns a string of agent-oriented documentation for a class, instance, module, function, or method.
|
|
610
599
|
|
|
611
|
-
- 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).
|
|
612
|
-
- 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.
|
|
600
|
+
- 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).
|
|
601
|
+
- 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.
|
|
602
|
+
- 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.
|
|
613
603
|
|
|
614
604
|
## License
|
|
615
605
|
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
Example: Using agent_help() on any class — no setup required.
|
|
3
3
|
|
|
4
4
|
Even without duck-typing or the mixin, ``agent_help()`` still generates
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
structured Markdown from introspection — a curated public-API list with
|
|
6
|
+
current signatures, free of inherited dunders and MRO clutter. You cannot
|
|
7
|
+
add custom ``__agent_notes__()``, but the output is still more
|
|
8
|
+
agent-friendly than Python's built-in ``help()``.
|
|
8
9
|
|
|
9
10
|
Uses logging.Logger — a stdlib class every Python developer knows.
|
|
10
11
|
|
|
@@ -18,14 +19,14 @@ import os
|
|
|
18
19
|
from agent_readable import agent_help
|
|
19
20
|
|
|
20
21
|
if __name__ == "__main__":
|
|
21
|
-
print("=== help(logging.Logger) —
|
|
22
|
+
print("=== help(logging.Logger) — every inherited dunder and MRO detail ===")
|
|
22
23
|
print()
|
|
23
24
|
os.environ["PAGER"] = "cat"
|
|
24
|
-
help(logging.Logger) #
|
|
25
|
+
help(logging.Logger) # NOSONAR
|
|
25
26
|
|
|
26
27
|
print()
|
|
27
28
|
print("=" * 72)
|
|
28
29
|
print()
|
|
29
|
-
print("=== agent_help(logging.Logger) —
|
|
30
|
+
print("=== agent_help(logging.Logger) — curated, structured public API ===")
|
|
30
31
|
print()
|
|
31
32
|
print(agent_help(logging.Logger))
|
|
@@ -18,7 +18,7 @@ class RateLimiter:
|
|
|
18
18
|
|
|
19
19
|
def __init__(self, max_tokens: int, refill_rate: float): ...
|
|
20
20
|
|
|
21
|
-
def acquire(self,
|
|
21
|
+
def acquire(self, _tokens: int = 1) -> bool:
|
|
22
22
|
"""Try to acquire tokens. Returns False if rate-limited."""
|
|
23
23
|
return True
|
|
24
24
|
|
|
@@ -53,7 +53,7 @@ if __name__ == "__main__":
|
|
|
53
53
|
print("=== help(RateLimiter) — no usage guidance ===")
|
|
54
54
|
print()
|
|
55
55
|
os.environ["PAGER"] = "cat"
|
|
56
|
-
help(RateLimiter) #
|
|
56
|
+
help(RateLimiter) # NOSONAR
|
|
57
57
|
|
|
58
58
|
print()
|
|
59
59
|
print("=" * 72)
|
|
@@ -34,10 +34,10 @@ class Connection(sqlite3.Connection, AgentReadableMixin):
|
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
if __name__ == "__main__":
|
|
37
|
-
print("=== help(sqlite3.Connection) —
|
|
37
|
+
print("=== help(sqlite3.Connection) — every inherited dunder and MRO detail ===")
|
|
38
38
|
print()
|
|
39
39
|
os.environ["PAGER"] = "cat"
|
|
40
|
-
help(sqlite3.Connection) #
|
|
40
|
+
help(sqlite3.Connection) # NOSONAR
|
|
41
41
|
|
|
42
42
|
print()
|
|
43
43
|
print("=" * 72)
|
|
@@ -73,10 +73,10 @@ class CalibratedSensor(Sensor):
|
|
|
73
73
|
|
|
74
74
|
|
|
75
75
|
if __name__ == "__main__":
|
|
76
|
-
print("=== help(CalibratedSensor) —
|
|
76
|
+
print("=== help(CalibratedSensor) — every inherited dunder and MRO detail ===")
|
|
77
77
|
print()
|
|
78
78
|
os.environ["PAGER"] = "cat"
|
|
79
|
-
help(CalibratedSensor) #
|
|
79
|
+
help(CalibratedSensor) # NOSONAR
|
|
80
80
|
|
|
81
81
|
print()
|
|
82
82
|
print("=" * 72)
|