Metadata-Version: 2.4
Name: fixyourdocs
Version: 0.3.0
Summary: Reference Python SDK for the Docs Feedback Protocol
Project-URL: Homepage, https://docsfeedback.org
Project-URL: Repository, https://github.com/fixyourdocs/sdk-python
Project-URL: Specification, https://github.com/fixyourdocs/protocol
Author-email: FixYourDocs <hello@fixyourdocs.io>
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: agents,ai,docs,documentation,feedback,protocol
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Documentation
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Requires-Dist: httpx<1,>=0.27
Requires-Dist: pydantic<3,>=2.6
Provides-Extra: dev
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Description-Content-Type: text/markdown

# fixyourdocs (Python SDK)

Reference Python SDK for the [Docs Feedback Protocol](https://github.com/fixyourdocs/protocol)
v0. The protocol lets AI agents file structured reports against
documentation pages when the docs break agent task flows.

- Full docs: <https://docs.fixyourdocs.io/sdk/python/>.
- Spec: <https://docsfeedback.org>.
- Why this exists: [the FixYourDocs manifesto](https://github.com/fixyourdocs/manifesto/blob/main/MANIFESTO.md).

## Install

```sh
pip install fixyourdocs
```

Requires Python 3.9+.

## CLI

The package ships a `fixyourdocs` console script covering the two
one-liners from the [agents-md-snippet](https://github.com/fixyourdocs/agents-md-snippet)
README:

```sh
# Adds the canonical AGENTS.md block to your repo. Idempotent.
pipx run fixyourdocs init

# Sends a single report to the Hub.
pipx run fixyourdocs report \
  --doc-url https://example.com/docs/install \
  --summary "Install fails on macOS 14" \
  --agent claude-code \
  --kind broken
```

`init` auto-detects `AGENTS.md`, `CLAUDE.md`, `.cursor/rules`, or
`.github/copilot-instructions.md` and appends to whichever exists
(falling back to creating `AGENTS.md`). Pass `--file <path>` to override.

`init --global` instead writes the consumer-side "report stale
third-party docs" block to a global agent-config file (default
`~/.claude/CLAUDE.md`, override with `--file`). Use this when you want an
agent to offer to report broken **third-party** docs it consults across
all your projects, rather than wiring the per-repo block into one repo.
It is idempotent.

`report` accepts `--details`, `--suggested-fix`, `--api-url`, `--token`,
and `--json` for machine-readable output. Exit codes: `0` success,
`2` user error, `1` transport or server error.

## Usage (sync)

```python
from fixyourdocs import Client, Report

report = Report.create(
    doc_url="https://docs.example.com/getting-started",
    summary="The page does not document how to set the API_KEY env var.",
    kind="missing",
    agent_name="claude-code",
)

with Client(api_url="https://hub.fixyourdocs.io") as client:
    result = client.send(report)

print(result.id, result.is_duplicate)
```

## Usage (async)

```python
import asyncio
from fixyourdocs import AsyncClient, Report

async def main() -> None:
    report = Report.create(
        doc_url="https://docs.example.com/getting-started",
        summary="The page does not document how to set the API_KEY env var.",
        kind="missing",
        agent_name="claude-code",
    )
    async with AsyncClient(api_url="https://hub.fixyourdocs.io") as client:
        result = await client.send(report)
    print(result.id, result.is_duplicate)

asyncio.run(main())
```

## API shape

The wire format is a nested object (`agent`, `report`, `task_context`),
so the SDK exposes two ways to build a `Report`:

- **`Report.create(...)`** — ergonomic, flat keyword-argument
  constructor for the common case. Internally builds the nested
  wire-format structure.
- **`Report(agent=AgentInfo(...), report=ReportBody(...), ...)`** — the
  typed nested form, useful when constructing reports programmatically
  from already-typed sub-objects.

Both produce identical wire output.

## Consumer-side mode

When an agent reports against **third-party** docs it consulted (rather
than your own first-party docs), the client defaults guard against
leaking private context and against pestering hosts that have opted out.
All three options are constructor arguments on both `Client` and
`AsyncClient`:

```python
from fixyourdocs import Client, PrivacyError, OptedOutError

with Client(api_url="https://hub.fixyourdocs.io") as client:
    client.send(report)  # guards run before any network call
```

- **`enforce_privacy=True`** (default) — before any network call,
  validates `report.doc_url` and raises `PrivacyError` (no request is
  made) unless it is a public `https://` page. Rejected: non-`https`
  schemes; missing hosts; `localhost` and `.localhost` / `.local` /
  `.internal` names; bare single-label hostnames; and IP literals in
  loopback / private / link-local / reserved ranges. Pass
  `enforce_privacy=False` for first-party / self-hosted / internal docs.
- **`include_transcript=False`** (default) — strips
  `task_context.transcript_excerpt` from the wire body (dropping
  `task_context` entirely if nothing else remains) so transcript
  snippets never leave the machine unless you opt in with
  `include_transcript=True`. Your `Report` object is never mutated.
- **`discover_opt_out=True`** (default) — before posting, fetches
  `https://<doc-host>/.well-known/docs-feedback.json`; if the host
  published `opt_in: false`, `send()` raises `OptedOutError` and does not
  post. The result is cached per host for 24h. Missing/invalid
  well-known files are treated as opted-in.

## Errors

Non-2xx responses raise typed exceptions:

| Status | Exception |
|---|---|
| 400 | `ValidationError` (`.details`) |
| 401 | `AuthError` |
| 404 | `NotFoundError` |
| 410 | `OptedOutError` (`.since`) |
| 413 | `PayloadTooLargeError` (`.max_bytes`) |
| 415 | `UnsupportedMediaTypeError` |
| 422 | `PolicyRejectedError` (`.reason`) |
| 429 | `RateLimitedError` (`.retry_after`) |
| 5xx | `ServerError` (after one automatic retry on 502/503/504) |

All inherit from `FixYourDocsError`. In consumer-side mode, `send()` may
also raise `PrivacyError` (non-public `doc_url`) or `OptedOutError`
(host opted out via its `.well-known` file) *before* any request is
posted.

## Licence

Apache License 2.0 — see [LICENSE](LICENSE).

## Contributing

Contributions require a DCO sign-off and a signed Apache Individual
Contributor License Agreement — see [CONTRIBUTING.md](CONTRIBUTING.md).
