This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
A Python client library (hiro_graph_client, published to PyPI as hiro-graph-client) for the REST and WebSocket APIs of the HIRO 7 Graph (arago / Almato AI). Requires Python >= 3.7. Pure library — there is no application entrypoint.
This is a src/-layout package, but the setup.py lives in src/ (not the repo root). The package itself is src/hiro_graph_client/. Most commands operate from src/ or with PYTHONPATH=src.
All workflows go through the Makefile:
make install # install package + runtime deps (use PIPARGS=--user for single-user)
make test # install, then run pytest with PYTHONPATH=src, writes tests/test-results.xml
make pythondoc # build Sphinx HTML docs into docs/_build/html
make dist # build sdist + wheel
make clean / distclean # remove build artifacts / also uninstallRun tests directly (faster iteration):
PYTHONPATH=src python -m pytest tests/unit # all tests
PYTHONPATH=src python -m pytest tests/unit/test_client.py # one file
PYTHONPATH=src python -m pytest tests/unit/test_client.py::test_name # one testNote: make test shells out to make install first, which runs pip install. For a quick local test loop, prefer the direct pytest invocation above against an editable/installed package.
Versions are derived from git tags, not hardcoded. src/version_by_git.py runs git describe --tags --long during setup.py/make and writes src/hiro_graph_client/VERSION (read at runtime by version.py). Tags must look like vX.Y.Z (release) or tX.Y.Z (dev → .devN). No matching tag → fallback 0.0.0.dev0. Do not edit VERSION by hand; bump by tagging. Publishing to PyPI only works once per version, so a new release requires a new tag.
Everything central lives in clientlib.py (~1500 lines). Two parallel class hierarchies, both rooted at AbstractAPI (the HTTP layer: get/post/put/patch/delete, binary streaming, header/proxy/SSL handling, retry via backoff, response/error parsing).
Connection + auth side (one per logical connection, shared across clients):
AbstractAPI→GraphConnectionHandler— owns therequests.Session/connection pool, discovers and caches API endpoints and version info from the backend (get_version,get_api_endpoint_of,get_websocket_config).GraphConnectionHandler→AbstractTokenApiHandler— adds token lifecycle (token,decode_token,refresh_token,revoke_token).- Concrete handlers:
FixedTokenApiHandler(preset token),EnvironmentTokenApiHandler(readsHIRO_TOKENenv var),PasswordAuthTokenApiHandler(logs in with credentials; the only one that auto-renews on expiry, usingTokenInfo+apscheduler).
API client side (one per HIRO API):
AbstractAPI→AuthenticatedAPIHandler— a client that holds a reference to anAbstractTokenApiHandlerand injects its token/endpoint into each request.- Concrete clients, one file each, each public method ≈ one REST endpoint:
HiroGraph(client.py),HiroApp(appclient.py),HiroAuth(authclient.py),HiroAuthz(authzclient.py),HiroIam(iamclient.py),HiroKi(kiclient.py),HiroVariables(variablesclient.py).
Key design point — handler sharing: a single TokenApiHandler (or GraphConnectionHandler) is meant to be constructed once and passed to multiple Hiro* clients via api_handler=. This shares the session, token, and version/endpoint discovery so they aren't repeated per client. When adding/changing behavior, respect this split: connection/token concerns belong on the handler hierarchy, per-API calls on the client hierarchy.
WebSockets (websocketlib.py, eventswebsocket.py, actionwebsocket.py):
AbstractAuthenticatedWebSocketHandler(websocketlib.py) wrapswebsocket-client'sWebSocketAppwith reconnect/backoff, token-aware auth, thread-safe start/stop, and signal handling. It also takes anapi_handlerfor auth/endpoint resolution.- Subclass and override the
on_*callbacks:AbstractEventsWebSocketHandler(on_create/on_update/on_delete, plusEventsFilter) forevent-ws;AbstractActionWebSocketHandler(on_submit_action,on_config_changed, withActionStore/ActionItemtracking andsend_action_result) foraction-ws. - Shutdown rules matter and differ by caller thread:
stop()from another thread;signal_stop()from a signal handler (same thread asrun_forever(), or it deadlocks);restart()likewise must not be called from therun_forever()thread. See the "Closing WebSockets" section ofsrc/README.md.
SSLConfig (in clientlib.py) translates cert/verify settings into the cert/verify args of requests; it is passed to a handler and inherited by all attached clients.
src/README.md is the canonical user-facing documentation with runnable examples for every handler/client and the WebSocket shutdown semantics — read it before changing public API surface. The repo-root README.md only covers installation.