Skip to content
MatthewFerozPublic

About

A LangGraph agent that turns GitHub issues and merged PRs into grounded, reviewable documentation pull requests. FastAPI + HTMX, fully traced.

Topics

Resources

Stars

5 stars

Watchers

0 watching

Forks

Repository files navigation

DocsHound

DocsHound

DocsHound turns open issues and merged pull requests into grounded, reviewable documentation updates.

The project contains two independently deployable applications:

  • frontend/ — React, TypeScript, and Vite static application
  • backend/ — FastAPI JSON/SSE API and agent runtime

Credentials submitted through the local connection panels are sent directly to the backend, held only in process memory, and never returned by the API.

Product flow

Repository activity
        ↓
Open gaps + shipped changes
        ↓
Search relevant first-party documentation
        ↓
Assess coverage: missing, partial, or documented
        ↓
Grounded Markdown draft for missing or partial coverage
        ↓
Human review and approval
        ↓
New-page or existing-page patch preview
        ↓
Documentation pull request

Run locally

Requires Python 3.14 and Bun 1.3+. The original root setup commands remain supported:

python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
test -f .env || cp .env.example .env
# Add credentials to .env, then start the app:
./run.sh

Open http://127.0.0.1:8000. run.sh builds the React frontend and serves it with the API from one backend process. HOST and PORT can override the default bind address and port. Keep an existing .env when upgrading; replacing it would discard its credentials and settings.

For a frozen backend installation, use uv 0.12+ instead of the pip commands:

cd backend
uv sync --locked
cd ..
./run.sh

Backend dependencies are declared in backend/pyproject.toml and reproducibly resolved by backend/uv.lock. Refresh them deliberately with cd backend && uv lock --upgrade && uv sync.

For frontend hot reload, run each development server in its own terminal:

./backend/run.sh
bun run --cwd frontend dev

The frontend development server runs at http://127.0.0.1:5173 and proxies /api to the backend at http://127.0.0.1:8000.

OpenCode stage demo

The repeatable stage setup, pinned live GitHub sources, read-only preflight, and LangSmith trace configuration live in demo/. Start the complete demo environment with:

./demo/run.sh opencode

Backend configuration

Add credentials to backend/.env (or a root .env for compatibility with existing local installations):

APP_ENV=development   # set to production to disable browser key entry
GITHUB_TOKEN=          # optional server token for scans and documentation PRs
MERGE_GATEWAY_API_KEY= # recommended: model-based analysis through Gateway
MERGE_GATEWAY_PRIMARY_MODEL=google/gemini-3.7-flash
MERGE_GATEWAY_FALLBACK_MODEL=openai/gpt-5.6-luna
OPENAI_API_KEY=        # optional legacy direct-provider fallback
OPENAI_MODEL=gpt-4o-mini
LANGSMITH_API_KEY=     # default OTLP trace destination
LANGSMITH_PROJECT=docshound
ALLOWED_ORIGINS=http://localhost:5173
DOCSHOUND_DB_PATH=     # optional: explicit shared SQLite path

For automatic forks of arbitrary public repositories, use a classic GitHub token with the public_repo scope. GitHub requires Administration write access to create a fork with a fine-grained token, so fine-grained tokens are best for direct writes or reusing a fork you already created; give that fork Contents and Pull requests read/write access.

Frontend configuration

VITE_API_BASE_URL is the only frontend environment variable. Set it to the public backend origin for independent deployments:

VITE_API_BASE_URL=https://api.example.com

Values prefixed with VITE_ are public and embedded in the browser bundle. Never place Merge Gateway, model-provider, or GitHub credentials in the frontend environment.

In local development, a server-managed GITHUB_TOKEN is automatically verified for the repository entered on the homepage without being sent to or displayed in the browser. This is the recommended setup for repeatable demos. When no server token is configured, the readiness checklist can accept one GitHub token for repository research and documentation pull requests. The model connection menu can similarly send a Merge Gateway key to the backend. Browser-provided overrides are held only in backend process memory and cleared on restart. DocsHound automatically resolves the official documentation repository and folder from repository structure, README links, the GitHub homepage, and “Edit this page on GitHub” links. The detected source is shown before each run and can be overridden.

Pull-request previews always use that upstream documentation repository. Only after approval does DocsHound resolve a write destination: it writes directly when permitted, otherwise it reuses the connected account's fork or creates one, then opens the pull request against upstream. If GitHub permits the fork and commit but requires browser confirmation for the cross-fork pull request, DocsHound links directly to the prefilled upstream comparison page.

For a confirmed documentation root containing at most 100 Markdown or MDX pages, an authenticated run reads the complete corpus before ranking the best eight pages per finding. Larger roots use a bounded search of up to 100 pages. When documentation lives in a separate repository, its issues and merged pull requests can also be included as documentation-specific evidence. Browser credential entry is disabled when APP_ENV is production; production deployments should inject GITHUB_TOKEN and MERGE_GATEWAY_API_KEY through the server's secret manager.

Public website fetches check URLs and DNS answers before each request, including redirect targets. They request uncompressed responses and treat replies that still advertise compression as unavailable. Documentation pages retain a prefix of up to 500,000 bytes before text extraction. Robots.txt, sitemap, and documentation-source homepage responses above 2,000,000 bytes are unavailable. The HTTP transport resolves hostnames separately from the DNS preflight, so deployments that require DNS rebinding protection should also enforce network egress restrictions.

OpenTelemetry and OpenInference tracing

DocsHound produces one vendor-neutral OpenTelemetry trace stream enriched with OpenInference semantics. Each run is an OpenInference AGENT span, repository operations are TOOL spans, and the LangChain, LangGraph, and OpenAI-compatible Gateway calls beneath them are instrumented automatically. Model spans include the requested provider/model, the model returned by Gateway, and whether DocsHound used its fallback route.

Set a LangSmith API key to use LangSmith as the default OTLP destination:

LANGSMITH_API_KEY=lsv2_...
LANGSMITH_PROJECT=docshound
OTEL_SERVICE_NAME=docshound

The LangSmith SDK's native tracing switch is intentionally unnecessary: spans go directly to LangSmith's OTLP endpoint, avoiding a duplicate trace tree. Explicit OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_TRACES_ENDPOINT configuration takes precedence, making it possible to route the same spans to an OpenTelemetry Collector or a future Google exporter without changing agent code. Standard options including OTEL_EXPORTER_OTLP_HEADERS, OTEL_RESOURCE_ATTRIBUTES, and OTEL_SDK_DISABLED are supported. Set OPENINFERENCE_HIDE_INPUTS=true and/or OPENINFERENCE_HIDE_OUTPUTS=true when model content must not be captured by automatic instrumentation. DocsHound's custom spans record repository identity, the selected documentation repository and root, source references, and result summaries—not credentials or issue, pull-request, and document bodies.

Docker

Build and run both applications locally:

docker compose up --build

Open http://localhost:8080. The backend is available at http://localhost:8000, with persistent SQLite data in a named Docker volume.

Each directory also has its own Dockerfile, so the frontend and backend can be built, deployed, scaled, and rolled back separately. Configure the backend's ALLOWED_ORIGINS with the deployed frontend origin.

API

The versioned backend API is under /api/v1. Interactive OpenAPI documentation is available at http://127.0.0.1:8000/docs.

Start a run:

curl -sS -X POST http://127.0.0.1:8000/api/v1/runs \
  -H 'Content-Type: application/json' \
  -d '{"repo":"GoogleCloudPlatform/knowledge-catalog","limit":50}'

Resolve and confirm documentation sources before a run:

curl -sS -X POST http://127.0.0.1:8000/api/v1/sources/resolve \
  -H 'Content-Type: application/json' \
  -d '{"repo":"kubernetes/kubernetes"}'

Then fetch its state or subscribe to JSON server-sent events:

curl -sS http://127.0.0.1:8000/api/v1/runs/<RUN_ID>
curl -N http://127.0.0.1:8000/api/v1/runs/<RUN_ID>/events

Each observer receives its own live event stream. New observers of an active run also receive up to its last 2,000 events. Slow observers retain a bounded recent window, so use the run state endpoint for a complete current snapshot. Completed streams are retained for at most 50 runs in process memory; persisted run state remains available after that event history expires.

Completed run responses and run_completed events include a user-facing outcome and summary. Outcomes distinguish recommendations from no_activity, no_recommendations, partial_failure, and failed, so clients never have to infer an empty result from counters or mistake a recoverable tool error for a successful run.

The API also exposes findings, approval/rejection, approved documents, repository patch previews, patch downloads, and pull-request creation. Each patch preview records the approved revision it was built from. Approving a changed document discards its unpublished preview, and publishing a preview for an earlier revision returns HTTP 409. To update an existing documentation pull request after editing, refresh the preview and publish again; DocsHound pushes the new revision to the same branch and pull request.

For compatibility with existing integrations, POST /runs, GET /runs/{run_id}, and GET /runs/{run_id}/events.json remain available as aliases for the original DocsHound API contract. New integrations should use the versioned /api/v1 routes.

Persistence

The backend stores new installations in backend/data/docshound.db. If that file is absent, an existing root data/docshound.db is reused so an upgrade keeps saved runs and documents visible. If both exist, the backend database takes precedence. Set DOCSHOUND_DB_PATH when a deployment needs an explicit shared location.

The database includes completed runs, findings, approved document revisions, prepared patches, and created pull-request metadata.

SQLite and the in-process event stream are appropriate for a single backend replica. A multi-replica deployment should use shared persistence and event delivery before scaling horizontally.

Tests

Run backend tests:

cd backend
uv lock --check
uv run ruff check app tests ../demo
PYTHONWARNINGS='error::ResourceWarning' \
  uv run --locked python -m unittest discover -s tests -v

Run frontend checks:

cd frontend
bun run format:check
bun run typecheck
bun run test
bun run build

GitHub Actions runs the same locked backend and frontend checks, then builds both Docker images with docker compose build.

Project structure

backend/
  app/                    FastAPI API, agent, persistence, and integrations
  tests/                  API and workflow tests
  Dockerfile
frontend/
  src/                    React application and typed API client
  public/                 DocsHound assets
  Dockerfile
docker-compose.yml        Local production-style deployment
run.sh                    Starts both development servers

About

A LangGraph agent that turns GitHub issues and merged PRs into grounded, reviewable documentation pull requests. FastAPI + HTMX, fully traced.

Topics

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages