agent-readable 0.1.0__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.
Files changed (26) hide show
  1. {agent_readable-0.1.0 → agent_readable-0.1.2}/.github/workflows/test.yml +17 -0
  2. agent_readable-0.1.2/CHANGELOG.md +73 -0
  3. {agent_readable-0.1.0 → agent_readable-0.1.2}/PKG-INFO +67 -56
  4. {agent_readable-0.1.0 → agent_readable-0.1.2}/README.md +63 -53
  5. agent_readable-0.1.2/docs/agent_help_vs_help.gif +0 -0
  6. {agent_readable-0.1.0 → agent_readable-0.1.2}/examples/any_class.py +7 -6
  7. {agent_readable-0.1.0 → agent_readable-0.1.2}/examples/duck_type.py +2 -2
  8. agent_readable-0.1.0/examples/module_support.py → agent_readable-0.1.2/examples/modules_and_functions.py +23 -5
  9. {agent_readable-0.1.0 → agent_readable-0.1.2}/examples/sqlite_connection.py +2 -2
  10. {agent_readable-0.1.0 → agent_readable-0.1.2}/examples/temperature.py +2 -2
  11. {agent_readable-0.1.0 → agent_readable-0.1.2}/pyproject.toml +16 -4
  12. agent_readable-0.1.2/skills/agent-readable/SKILL.md +183 -0
  13. {agent_readable-0.1.0 → agent_readable-0.1.2}/src/agent_readable/__init__.py +1 -1
  14. {agent_readable-0.1.0 → agent_readable-0.1.2}/src/agent_readable/__main__.py +15 -11
  15. agent_readable-0.1.2/src/agent_readable/_model.py +301 -0
  16. agent_readable-0.1.2/src/agent_readable/_protocol.py +194 -0
  17. agent_readable-0.1.2/src/agent_readable/_render.py +64 -0
  18. agent_readable-0.1.2/tests/__init__.py +0 -0
  19. {agent_readable-0.1.0 → agent_readable-0.1.2}/tests/test_cli.py +21 -4
  20. {agent_readable-0.1.0 → agent_readable-0.1.2}/tests/test_protocol.py +303 -4
  21. agent_readable-0.1.0/AGENT-PROMPT.md +0 -76
  22. agent_readable-0.1.0/src/agent_readable/_protocol.py +0 -363
  23. {agent_readable-0.1.0 → agent_readable-0.1.2}/.github/workflows/publish.yml +0 -0
  24. {agent_readable-0.1.0 → agent_readable-0.1.2}/.gitignore +0 -0
  25. {agent_readable-0.1.0 → agent_readable-0.1.2}/LICENSE +0 -0
  26. /agent_readable-0.1.0/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.0
4
- Summary: A lightweight protocol for exposing agent-oriented documentation from Python classes and modules
3
+ Version: 0.1.2
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,22 +27,22 @@ 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 your library's API. Ship the usage rules — lifecycle order, pre-conditions, anti-patterns right next to your code.
31
+
32
+ <!-- markdownlint-disable MD033 -->
33
+ <p align="center">
34
+ <strong><code>logging.Logger</code> compared with <code>agent_help()</code> and <code>help()</code></strong><br>
35
+ <img src="docs/agent_help_vs_help.gif" alt="agent_help vs help">
36
+ </p>
37
+ <!-- markdownlint-enable MD033 -->
30
38
 
31
39
  ## Problem
32
40
 
33
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."*
34
42
 
35
- 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.
36
-
37
- `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.
38
-
39
- ```text
40
- help(logging.Logger) 217 lines — every inherited method, dunder, and MRO detail.
41
- agent_help(logging.Logger) 56 lines — structured sections + any author notes.
42
- ```
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
- 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.
45
46
 
46
47
  ## Installation
47
48
 
@@ -124,12 +125,15 @@ Reads a value from a hardware sensor.
124
125
 
125
126
  ## Why it matters
126
127
 
127
- `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.
128
+ `agent_help()` covers two failure modes agents hit on real APIs:
128
129
 
129
- `agent_help()` gives them a home next to the code:
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:
130
134
 
131
135
  ```
132
- # help(sqlite3.Connection) — 200+ lines of terminal output:
136
+ # help(sqlite3.Connection) — every inherited dunder, in source order:
133
137
  Help on class Connection in module sqlite3:
134
138
 
135
139
  class Connection(builtins.object)
@@ -143,10 +147,11 @@ class Connection(builtins.object)
143
147
  | ...
144
148
  | backup(self, target, /, *, pages=-1, progress=None, ...)
145
149
  | blobopen(self, table, column, rowid, /, *, readonly=False, ...)
146
- | ... (continues for 200+ more lines)
150
+ | ... (no curation, no behavioral rules, no signal that backup() needs an
151
+ | open target connection before being called)
147
152
  ```
148
153
 
149
- `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.
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.
150
155
 
151
156
  The examples below demonstrate four ways to use `agent_help()`.
152
157
 
@@ -183,14 +188,14 @@ An agent-friendly wrapper around sqlite3.Connection.
183
188
 
184
189
  ## Public API
185
190
 
186
- - `backup(/, target, *, pages=-1, progress=None, name='main', sleep=0.25)` method: Makes a backup of the database.
191
+ - `backup(target, *, pages=-1, progress=None, name='main', sleep=0.25)` method: Makes a backup of the database.
187
192
  - ...
188
- - `close(/)` method: Close the database connection.
189
- - `commit(/)` method: Commit any pending transaction to the database.
193
+ - `close()` method: Close the database connection.
194
+ - `commit()` method: Commit any pending transaction to the database.
190
195
  - ...
191
196
  - `execute(...)` method: Executes an SQL statement.
192
197
  - ...
193
- - `rollback(/)` method: Roll back to the start of any pending transaction.
198
+ - `rollback()` method: Roll back to the start of any pending transaction.
194
199
  - ...
195
200
 
196
201
  ## Agent usage rules
@@ -359,7 +364,7 @@ print(agent_help(RateLimiter))
359
364
 
360
365
  ## Example 4: Any class — no setup required
361
366
 
362
- 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()`.
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.
363
368
  Full example: [`examples/any_class.py`](examples/any_class.py).
364
369
 
365
370
  ```python
@@ -369,7 +374,7 @@ from agent_readable import agent_help
369
374
  print(agent_help(logging.Logger))
370
375
  ```
371
376
 
372
- `help(logging.Logger)` produces **217 lines** of output. `agent_help(logging.Logger)` produces a compact, scannable summary:
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:
373
378
 
374
379
  ````
375
380
  # Logger
@@ -404,9 +409,9 @@ Instances of the Logger class represent a single logging channel. A
404
409
 
405
410
  No mixin, no duck-typing — just pass any class to `agent_help()`.
406
411
 
407
- ## Example 5: Modules
412
+ ## Example 5: Modules, functions, and methods
408
413
 
409
- `agent_help()` also works on modules — it generates a summary with the module docstring, public functions, and classes. Full example: [`examples/module_support.py`](examples/module_support.py).
414
+ `agent_help()` also works on modules — it generates a summary with the module docstring, public functions, and classes. Full example: [`examples/modules_and_functions.py`](examples/modules_and_functions.py).
410
415
 
411
416
  ```python
412
417
  import sys
@@ -422,14 +427,16 @@ Output:
422
427
 
423
428
  ## Purpose
424
429
 
425
- Example: Using agent_help() on modules.
430
+ Example: Using agent_help() on modules, functions, and methods.
426
431
 
427
- Demonstrates both shapes of module support:
432
+ Demonstrates non-class targets:
428
433
  1. A custom module (this file itself).
429
434
  2. A stdlib module (pathlib).
435
+ 3. A function (connect, defined below).
436
+ 4. A method (Query.execute, defined below).
430
437
 
431
- Run this file to see both outputs:
432
- python examples/module_support.py
438
+ Run this file to see all outputs:
439
+ python examples/modules_and_functions.py
433
440
 
434
441
  ## Public API
435
442
 
@@ -445,7 +452,7 @@ Run this file to see both outputs:
445
452
  - If usage is ambiguous, prefer the simplest documented usage pattern.
446
453
  ````
447
454
 
448
- You can also pass any stdlib or third-party module — same `module_support.py` shows it.
455
+ You can also pass any stdlib or third-party module — same `modules_and_functions.py` shows it.
449
456
 
450
457
  ```python
451
458
  import pathlib
@@ -469,20 +476,13 @@ operating systems.
469
476
 
470
477
  ## Public API
471
478
 
472
- - `DirEntryInfo` class: Implementation of pathlib.types.PathInfo that provides status
473
479
  - `Path` class: PurePath subclass that can make system calls.
474
- - `PathInfo` class: Implementation of pathlib.types.PathInfo that provides status
475
480
  - `PosixPath` class: Path subclass for non-Windows systems.
476
481
  - `PurePath` class: Base class for manipulating paths without I/O.
477
482
  - `PurePosixPath` class: PurePath subclass for non-Windows systems.
478
483
  - `PureWindowsPath` class: PurePath subclass for Windows systems.
479
484
  - `UnsupportedOperation` class: An exception that is raised when an unsupported operation is attempted.
480
485
  - `WindowsPath` class: Path subclass for Windows systems.
481
- - `copy_info(info, target, follow_symlinks=True)` function: Copy metadata from the given PathInfo to the given local path.
482
- - `copyfileobj(source_f, target_f)` function: Copy data from file-like object source_f to file-like object target_f.
483
- - `ensure_different_files(source, target)` function: Raise OSError(EINVAL) if both paths refer to the same file.
484
- - `ensure_distinct_paths(source, target)` function: Raise OSError(EINVAL) if the other path is within this path.
485
- - `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
486
486
 
487
487
  ## Agent usage rules
488
488
 
@@ -492,7 +492,16 @@ operating systems.
492
492
  - If usage is ambiguous, prefer the simplest documented usage pattern.
493
493
  ````
494
494
 
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.
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.
496
+
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.
498
+
499
+ ```python
500
+ import pathlib
501
+ from agent_readable import agent_help
502
+
503
+ print(agent_help(pathlib.Path.read_text))
504
+ ```
496
505
 
497
506
  ```python
498
507
  import sys
@@ -515,20 +524,21 @@ python -m agent_readable agent_readable:AgentReadableMixin
515
524
 
516
525
  # Any module
517
526
  python -m agent_readable pathlib
527
+
528
+ # A function or method
529
+ python -m agent_readable json:dumps
530
+ python -m agent_readable pathlib:Path.read_text
518
531
  ```
519
532
 
520
- Outputs agent-oriented documentation for the given class or module to stdout.
533
+ Outputs agent-oriented documentation for the given class, module, function, or method to stdout.
521
534
 
522
535
  ## FAQ
523
536
 
524
537
  ### How does my agent know to call `agent_help()` instead of `help()`?
525
538
 
526
- Today, you tell it. Either:
527
-
528
- - Paste [`AGENT-PROMPT.md`](AGENT-PROMPT.md) into your conversation, or
529
- - 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.
530
540
 
531
- "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.
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.
532
542
 
533
543
  ### How is this different from `AGENTS.md` / `llms.txt` / Cursor rules?
534
544
 
@@ -556,7 +566,7 @@ Yes — `agent_help()` falls back to introspection (Example 4). You get a struct
556
566
 
557
567
  ## Keeping agent docs up to date
558
568
 
559
- 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.
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.
560
570
 
561
571
  ## The `__agent_help__` protocol
562
572
 
@@ -574,13 +584,13 @@ Classes that define a `@classmethod` named `__agent_help__` returning a `str` ar
574
584
 
575
585
  The two dunders intentionally encode different composition rules:
576
586
 
577
- | Aspect | `__agent_help__()` | `__agent_notes__()` |
578
- |-----------------|-------------------------------------------------|-----------------------------------------------------------------------------|
579
- | Semantics | **Replacement** — returned string IS the output | **Additive** — appended to auto-generated docs |
580
- | Composition | Single class wins (the one closest in MRO) | Accumulated across the MRO; leaf class wins on conflict (header marks this) |
581
- | When to use | Total control over the rendered text | "Auto-doc + my extra do/don't rules" |
582
- | Skipped when | (always called if defined) | Skipped when a duck-typed `__agent_help__` is present (it owns the output) |
583
- | 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 |
584
594
 
585
595
  ## Class docstring hints
586
596
 
@@ -612,10 +622,11 @@ If a class inherits from `AgentReadableMixin`, coding agents should call `agent_
612
622
 
613
623
  ### `agent_help(obj)`
614
624
 
615
- Returns a string of agent-oriented documentation for a class, instance, or module.
625
+ Returns a string of agent-oriented documentation for a class, instance, module, function, or method.
616
626
 
617
- - 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).
618
- - 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.
619
630
 
620
631
  ## License
621
632
 
@@ -1,21 +1,21 @@
1
1
  # agent-readable
2
2
 
3
- A lightweight Python protocol for exposing agent-oriented documentation from classes and modules.
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
+
5
+ <!-- markdownlint-disable MD033 -->
6
+ <p align="center">
7
+ <strong><code>logging.Logger</code> compared with <code>agent_help()</code> and <code>help()</code></strong><br>
8
+ <img src="docs/agent_help_vs_help.gif" alt="agent_help vs help">
9
+ </p>
10
+ <!-- markdownlint-enable MD033 -->
4
11
 
5
12
  ## Problem
6
13
 
7
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."*
8
15
 
9
- 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.
10
-
11
- `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.
12
-
13
- ```text
14
- help(logging.Logger) 217 lines — every inherited method, dunder, and MRO detail.
15
- agent_help(logging.Logger) 56 lines — structured sections + any author notes.
16
- ```
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
- 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.
19
19
 
20
20
  ## Installation
21
21
 
@@ -98,12 +98,15 @@ Reads a value from a hardware sensor.
98
98
 
99
99
  ## Why it matters
100
100
 
101
- `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.
101
+ `agent_help()` covers two failure modes agents hit on real APIs:
102
102
 
103
- `agent_help()` gives them a home next to the code:
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:
104
107
 
105
108
  ```
106
- # help(sqlite3.Connection) — 200+ lines of terminal output:
109
+ # help(sqlite3.Connection) — every inherited dunder, in source order:
107
110
  Help on class Connection in module sqlite3:
108
111
 
109
112
  class Connection(builtins.object)
@@ -117,10 +120,11 @@ class Connection(builtins.object)
117
120
  | ...
118
121
  | backup(self, target, /, *, pages=-1, progress=None, ...)
119
122
  | blobopen(self, table, column, rowid, /, *, readonly=False, ...)
120
- | ... (continues for 200+ more lines)
123
+ | ... (no curation, no behavioral rules, no signal that backup() needs an
124
+ | open target connection before being called)
121
125
  ```
122
126
 
123
- `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.
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.
124
128
 
125
129
  The examples below demonstrate four ways to use `agent_help()`.
126
130
 
@@ -157,14 +161,14 @@ An agent-friendly wrapper around sqlite3.Connection.
157
161
 
158
162
  ## Public API
159
163
 
160
- - `backup(/, target, *, pages=-1, progress=None, name='main', sleep=0.25)` method: Makes a backup of the database.
164
+ - `backup(target, *, pages=-1, progress=None, name='main', sleep=0.25)` method: Makes a backup of the database.
161
165
  - ...
162
- - `close(/)` method: Close the database connection.
163
- - `commit(/)` method: Commit any pending transaction to the database.
166
+ - `close()` method: Close the database connection.
167
+ - `commit()` method: Commit any pending transaction to the database.
164
168
  - ...
165
169
  - `execute(...)` method: Executes an SQL statement.
166
170
  - ...
167
- - `rollback(/)` method: Roll back to the start of any pending transaction.
171
+ - `rollback()` method: Roll back to the start of any pending transaction.
168
172
  - ...
169
173
 
170
174
  ## Agent usage rules
@@ -333,7 +337,7 @@ print(agent_help(RateLimiter))
333
337
 
334
338
  ## Example 4: Any class — no setup required
335
339
 
336
- 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()`.
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.
337
341
  Full example: [`examples/any_class.py`](examples/any_class.py).
338
342
 
339
343
  ```python
@@ -343,7 +347,7 @@ from agent_readable import agent_help
343
347
  print(agent_help(logging.Logger))
344
348
  ```
345
349
 
346
- `help(logging.Logger)` produces **217 lines** of output. `agent_help(logging.Logger)` produces a compact, scannable summary:
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:
347
351
 
348
352
  ````
349
353
  # Logger
@@ -378,9 +382,9 @@ Instances of the Logger class represent a single logging channel. A
378
382
 
379
383
  No mixin, no duck-typing — just pass any class to `agent_help()`.
380
384
 
381
- ## Example 5: Modules
385
+ ## Example 5: Modules, functions, and methods
382
386
 
383
- `agent_help()` also works on modules — it generates a summary with the module docstring, public functions, and classes. Full example: [`examples/module_support.py`](examples/module_support.py).
387
+ `agent_help()` also works on modules — it generates a summary with the module docstring, public functions, and classes. Full example: [`examples/modules_and_functions.py`](examples/modules_and_functions.py).
384
388
 
385
389
  ```python
386
390
  import sys
@@ -396,14 +400,16 @@ Output:
396
400
 
397
401
  ## Purpose
398
402
 
399
- Example: Using agent_help() on modules.
403
+ Example: Using agent_help() on modules, functions, and methods.
400
404
 
401
- Demonstrates both shapes of module support:
405
+ Demonstrates non-class targets:
402
406
  1. A custom module (this file itself).
403
407
  2. A stdlib module (pathlib).
408
+ 3. A function (connect, defined below).
409
+ 4. A method (Query.execute, defined below).
404
410
 
405
- Run this file to see both outputs:
406
- python examples/module_support.py
411
+ Run this file to see all outputs:
412
+ python examples/modules_and_functions.py
407
413
 
408
414
  ## Public API
409
415
 
@@ -419,7 +425,7 @@ Run this file to see both outputs:
419
425
  - If usage is ambiguous, prefer the simplest documented usage pattern.
420
426
  ````
421
427
 
422
- You can also pass any stdlib or third-party module — same `module_support.py` shows it.
428
+ You can also pass any stdlib or third-party module — same `modules_and_functions.py` shows it.
423
429
 
424
430
  ```python
425
431
  import pathlib
@@ -443,20 +449,13 @@ operating systems.
443
449
 
444
450
  ## Public API
445
451
 
446
- - `DirEntryInfo` class: Implementation of pathlib.types.PathInfo that provides status
447
452
  - `Path` class: PurePath subclass that can make system calls.
448
- - `PathInfo` class: Implementation of pathlib.types.PathInfo that provides status
449
453
  - `PosixPath` class: Path subclass for non-Windows systems.
450
454
  - `PurePath` class: Base class for manipulating paths without I/O.
451
455
  - `PurePosixPath` class: PurePath subclass for non-Windows systems.
452
456
  - `PureWindowsPath` class: PurePath subclass for Windows systems.
453
457
  - `UnsupportedOperation` class: An exception that is raised when an unsupported operation is attempted.
454
458
  - `WindowsPath` class: Path subclass for Windows systems.
455
- - `copy_info(info, target, follow_symlinks=True)` function: Copy metadata from the given PathInfo to the given local path.
456
- - `copyfileobj(source_f, target_f)` function: Copy data from file-like object source_f to file-like object target_f.
457
- - `ensure_different_files(source, target)` function: Raise OSError(EINVAL) if both paths refer to the same file.
458
- - `ensure_distinct_paths(source, target)` function: Raise OSError(EINVAL) if the other path is within this path.
459
- - `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
460
459
 
461
460
  ## Agent usage rules
462
461
 
@@ -466,7 +465,16 @@ operating systems.
466
465
  - If usage is ambiguous, prefer the simplest documented usage pattern.
467
466
  ````
468
467
 
469
- 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.
469
+
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.
471
+
472
+ ```python
473
+ import pathlib
474
+ from agent_readable import agent_help
475
+
476
+ print(agent_help(pathlib.Path.read_text))
477
+ ```
470
478
 
471
479
  ```python
472
480
  import sys
@@ -489,20 +497,21 @@ python -m agent_readable agent_readable:AgentReadableMixin
489
497
 
490
498
  # Any module
491
499
  python -m agent_readable pathlib
500
+
501
+ # A function or method
502
+ python -m agent_readable json:dumps
503
+ python -m agent_readable pathlib:Path.read_text
492
504
  ```
493
505
 
494
- Outputs agent-oriented documentation for the given class or module to stdout.
506
+ Outputs agent-oriented documentation for the given class, module, function, or method to stdout.
495
507
 
496
508
  ## FAQ
497
509
 
498
510
  ### How does my agent know to call `agent_help()` instead of `help()`?
499
511
 
500
- Today, you tell it. Either:
501
-
502
- - Paste [`AGENT-PROMPT.md`](AGENT-PROMPT.md) into your conversation, or
503
- - 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.
504
513
 
505
- "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.
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.
506
515
 
507
516
  ### How is this different from `AGENTS.md` / `llms.txt` / Cursor rules?
508
517
 
@@ -530,7 +539,7 @@ Yes — `agent_help()` falls back to introspection (Example 4). You get a struct
530
539
 
531
540
  ## Keeping agent docs up to date
532
541
 
533
- 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.
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.
534
543
 
535
544
  ## The `__agent_help__` protocol
536
545
 
@@ -548,13 +557,13 @@ Classes that define a `@classmethod` named `__agent_help__` returning a `str` ar
548
557
 
549
558
  The two dunders intentionally encode different composition rules:
550
559
 
551
- | Aspect | `__agent_help__()` | `__agent_notes__()` |
552
- |-----------------|-------------------------------------------------|-----------------------------------------------------------------------------|
553
- | Semantics | **Replacement** — returned string IS the output | **Additive** — appended to auto-generated docs |
554
- | Composition | Single class wins (the one closest in MRO) | Accumulated across the MRO; leaf class wins on conflict (header marks this) |
555
- | When to use | Total control over the rendered text | "Auto-doc + my extra do/don't rules" |
556
- | Skipped when | (always called if defined) | Skipped when a duck-typed `__agent_help__` is present (it owns the output) |
557
- | 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 |
558
567
 
559
568
  ## Class docstring hints
560
569
 
@@ -586,10 +595,11 @@ If a class inherits from `AgentReadableMixin`, coding agents should call `agent_
586
595
 
587
596
  ### `agent_help(obj)`
588
597
 
589
- Returns a string of agent-oriented documentation for a class, instance, or module.
598
+ Returns a string of agent-oriented documentation for a class, instance, module, function, or method.
590
599
 
591
- - 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).
592
- - 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.
593
603
 
594
604
  ## License
595
605