Metadata-Version: 2.4
Name: threadly-sdk
Version: 0.1.3
Summary: Thread-ly SDK — programmatic access to Sprint Translation Reports
License: MIT
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.28
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: responses; extra == "dev"

# threadly-sdk

Python SDK for [Thread-ly](https://thread-ly.com) — the engineering narrative platform.

## Install

```bash
pip install threadly-sdk
```

Or install from source:

```bash
cd sdk/
pip install -e .
```

## Authentication

All SDK endpoints require an API key. **Platform operators:** create keys via the backend CLI and share them securely with design partners (email, 1Password, etc.):

```bash
cd /path/to/threadly_v2
PYTHONPATH=. python -m app.cli create-api-key --name "Design Partner Acme"
```

The key starts with `tly_` and is shown only once. Store it securely. Pass it to the client:

```python
from threadly import ThreadlyClient

client = ThreadlyClient(
    base_url="https://api.thread-ly.com",
    api_key="tly_your_key_here",
)
```

Or set `THREADLY_API_KEY` in your environment.

## Quick Start

```python
from threadly import generate_report

# Connect repo + run full pipeline (ingest + generate sprint report). One call.
result = generate_report("owner", "repo-name", days=14, api_key="tly_...")
print(result["markdown"])
```

`generate_report` runs the full pipeline in one call: it connects the repo, fetches and ingests PRs, then generates the report. No prior ingestion needed. Large repos may take several minutes (default timeout: 5 min).

## Full Client

```python
from threadly import ThreadlyClient

client = ThreadlyClient(
    base_url="https://api.thread-ly.com",
    api_key="tly_your_key_here",
)

# Connect a repo
repo = client.connect_repo("owner", "repo-name")

# Upload sprint context (PRD, sprint goals, etc.)
client.upload_context(repo["id"], "sprint_goals.md")

# Generate the Sprint Translation Report
report = client.sprint_report(repo["id"], days=14)
print(report["markdown"])

# Get the continuity narrative
narrative = client.narrative(repo["id"])
print(narrative["markdown"])
```

## Configuration

| Env Variable | Default | Description |
|---|---|---|
| `THREADLY_URL` | `http://localhost:8000` | Thread-ly API base URL |
| `THREADLY_API_KEY` | (none) | API key (`tly_...`) for authenticated endpoints |

## API Methods

| Method | Description |
|---|---|
| `connect_repo(owner, name)` | Connect a GitHub repository. Returns repo object with `id`. |
| `list_repos()` | List all connected repos. |
| `get_repo(repo_id)` | Get repo details. |
| `trigger_ingest(repo_id, limit=50)` | Start background ingestion. Returns `{status, task_id, message}`. Poll `get_job_status(task_id)` for completion. |
| `get_job_status(task_id)` | Check status of a background job. |
| `get_reports(repo_id)` | Return cached narrative and sprint report in one call. Returns `{narrative, sprint_report, sprint_days}`. Empty if ingest has not run. |
| `sprint_report(repo_id, days=14, preview=False)` | Generate the Sprint Translation Report. Free: `days=14`. Use `preview=True` with `days=90` for a sampled quarterly preview. Returns `{markdown, report}`. |
| `narrative(repo_id)` | Generate the continuity narrative. Returns `{markdown, doc}`. |
| `upload_context(repo_id, file_path, doc_type=None)` | Upload a context document (PRD, sprint goals). Accepts PDF, Markdown (.md, .markdown), plain text (.txt), RST (.rst), JSON, CSV. Max 10 MB. Returns `{id, filename, doc_type, char_count, created_at}`. |
| `list_context(repo_id)` | List uploaded context docs. Returns `[{id, filename, doc_type, char_count, created_at}, ...]`. |
| `delete_context(repo_id, doc_id)` | Deactivate a context document. Returns `{status, id}`. |

## Ingestion Workflows

- **`sprint_report` / `generate_report`** — Run the full pipeline in one call: fetch PRs, ingest, build knowledge, generate report. No prior ingestion needed. Use for on-demand reports.
- **`trigger_ingest` + `get_job_status`** — Use when you want to ingest first (e.g. for `narrative` or repeated reports). Poll `get_job_status(task_id)` until `state` is `SUCCESS` or `FAILURE`.
- **`get_reports`** — After ingest completes, returns cached narrative and sprint report in one call. Fastest way to fetch both reports.
- **`narrative`** — Uses the knowledge base. Best results after ingestion (or after a `sprint_report` run, which builds knowledge).

## Free Tier Limits

| Limit | Per |
|---|---|
| **Reports** | 5 per month (only counted when actually generating; cache hits don't count) |
| **Time window** | Sprint (14 days) only. Use `preview=True` with `days=90` for a sampled quarterly preview |
| **PR coverage** | Up to 150 PRs per report (no silent truncation) |
| General API | 20 requests/hour |
| Ingest | 10/day |

Monthly, quarterly, half-year, and yearly reports require upgrade. When exceeded: `429` or `402` (paywall).

## Report detail & tone

Sprint reports and continuity narratives use **hedged, evidence-grounded language**: merged PRs and heuristics are the source of truth—the copy separates **what changed** from **likely implications** and **what to verify**, and avoids claiming planning success, roadmap intent, or certainty (e.g. “ensured,” “always,” “all capacity met”) unless the input explicitly supports it. **Try Live** and **SDK** share the same PM rewrite prompts; the SDK additionally builds repository knowledge for richer grounding.

| Source | Scope | Notes |
|--------|--------|-------|
| **SDK** (free tier, API key) | **14-day** sprint window; **up to 150 merged PRs** per report (enforced on the server; no silent truncation below that cap) | Full pipeline: **knowledge build + PM rewrite**. Same report shape and tone as the examples on thread-ly.com. |
| **Live demo (Try Live)** | **Up to 5 days**, **up to 10 PRs** (server-clamped for speed) | Same PM rewrite and effort table; **no knowledge build**. Less project context in the prompt. |

## Sprint report structure (`report` dict)

`sprint_report(...)` returns `{"markdown": "...", "report": {...}}`. Notable keys:

- **`change_risk_signals`** — List of deterministic risk flags from merged PRs (sensitive paths, cross-layer changes, interface or migration files, release-only PRs, possible missing consumer wiring). Each item includes `category` (`sensitive_surface`, `integration`, `breadth`, `release`), `severity` (`warning` renders as **[Warning]** in Markdown; `info` renders as **[Review]**), and `message`. These are heuristics, not a call graph.
- **`blast_radius`** — Deprecated alias: same list as `change_risk_signals` for backward compatibility.
- **`maintenance_divergence`**, **`capabilities_merged`**, **`sprint_volume`**, etc. — Unchanged.

For additional field documentation, see `docs/SPRINT_REPORT.md` in the Thread-ly backend source tree (if you self-host or contribute).

## Errors

The SDK raises `requests.HTTPError` on non-2xx responses. Common codes: `401` (invalid or missing API key), `402` (paywall / tier restriction, e.g. full monthly report without upgrade), `404` (repo or document not found), `429` (rate limit exceeded). Use `response.raise_for_status()` or wrap calls in try/except.

## More

Full documentation: [thread-ly.com/docs](https://thread-ly.com/docs)
