Metadata-Version: 2.4
Name: cntxgraph-client
Version: 0.1.0
Summary: Python client for cntxgraph-core
Author-email: Patrick Dwyer <patrick@patrickdwyer.com>
Requires-Python: >=3.11
Requires-Dist: httpx>=0.28
Requires-Dist: pydantic>=2.9
Provides-Extra: dev
Requires-Dist: pyright>=1.1; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=8.3; extra == 'dev'
Requires-Dist: ruff>=0.8; extra == 'dev'
Description-Content-Type: text/markdown

# cntxgraph-client

Python client for [cntxgraph-core](https://github.com/patrickdwyer33/cntxgraph-core).

## Install

```bash
pip install cntxgraph-client
```

## Quickstart

### Agent loop (without MCP)

```python
from cntxgraph_client import AsyncCntxgraphClient, StackTraceEntry

sg = AsyncCntxgraphClient(url="http://localhost:8000")

# Traverse the knowledge hierarchy
result = await sg.think.query("how do we handle NDA renewals?")
print(result.node_path)           # e.g. ["Sales", "Enterprise"]
print(result.skills_activated)    # list of Skill objects

# Record reference resolution (use_reference equivalent)
await sg.stack_traces.append_entry(
    result.stack_trace_id,
    StackTraceEntry(depth=-1, node="Renewal Script", decision="activate", rationale="Resolved reference"),
)

# Submit new knowledge, linked back to this traversal
await sg.queue.submit(
    summary="NDAs over $500k require legal sign-off",
    processes=[{"name": "NDA Review", "description": "Review process for high-value NDAs"}],
    stack_trace_id=result.stack_trace_id,
    agent_id="my-agent-v1",
)
```

### Approver workflow

```python
from cntxgraph_client import CntxgraphClient, CntxgraphNotFoundError

with CntxgraphClient(url="http://localhost:8000") as sg:
    try:
        plan = sg.plans.pending()
        print(f"Plan {plan.id}: {len(plan.operations)} operations")
        approved = sg.plans.approve(plan.id)
        print(f"Applied: {approved.status}")
    except CntxgraphNotFoundError:
        print("No plan pending review")
```

## Resources

| Resource | Methods |
|----------|---------|
| `client.think` | `query(prompt)` |
| `client.queue` | `submit(summary, processes, raw_transcript?, stack_trace_id?, agent_id?)`, `status()`, `flush()` |
| `client.plans` | `list(status?)`, `get(id)`, `pending()`, `approve(id)`, `defer(id)`, `defer_submission(plan_id, sub_id)`, `rollback(id)`, `delete(id)` |
| `client.nodes` | `get(node_id, include?)`, `subtree(node_id?, depth?)`, `children(node_id)` |
| `client.stack_traces` | `get(trace_id)`, `append_entry(trace_id, entry)` |

Both `CntxgraphClient` (sync) and `AsyncCntxgraphClient` (async) provide identical resources.

## Error handling

```python
from cntxgraph_client import CntxgraphNotFoundError, CntxgraphServerError

try:
    plan = sg.plans.get("bad-id")
except CntxgraphNotFoundError as e:
    print(e.status_code, e.request_url)
except CntxgraphServerError as e:
    print(e.raw_body)
```

## Development

```bash
pip install -e ".[dev]"
python -m pytest
ruff check cntxgraph_client/ tests/
```
