{
  "markdown": "# 🏞️ TrailGraph\n\n**An AI trip planner for the U.S. National Parks — and a reference app for giving a [Vercel Eve](https://vercel.com/) agent a real, graph-native memory with the [Neo4j Agent Memory Service (NAMS)](https://memory.neo4jlabs.com).**\n\n[![TrailGraph.app](img/trail-graph.png)](https://trailgraph.app)\n\n[TrailGraph](https://trailgraph.app) turns the National Park Service's open data into a connected graph you can explore, plan\ntrips on, and chat with — through an AI \"ranger\" that actually remembers what you love. The interesting\npart isn't the chatbot; it's *how the agent remembers*: **Eve** runs the agent, **NAMS** gives it\nmemory, and both sit on a **single Neo4j** so the agent can traverse straight from *\"this user loves\ndark skies and quiet trails\"* to the specific parks, campgrounds, and routes that fit.\n\n> If you're here to learn the pattern, jump to **[How Eve + NAMS fit together](#-how-eve--nams-fit-together)**.\n\n---\n\n## The big idea: a context graph\n\n![Context graphs and agent memory](img/3-memory-types.png)\n\nMost \"agent memory\" is a pile of embedded text snippets. TrailGraph uses the more complete idea — a\n**context graph**: a connected, queryable memory of the user *and* the world, in the same database as\nyour domain data.\n\n- **One Neo4j, two graphs.** The NPS **domain graph** (parks ↔ activities ↔ topics ↔ campgrounds ↔\n  alerts ↔ places ↔ people ↔ tours ↔ amenities ↔ passport stamps ↔ events) and each user's **context\n  graph** (preferences, considered parks, trips, accessibility constraints, passes, stamps, and the\n  agent's own reasoning) live in the **same instance**. NAMS is pointed at that same Neo4j as its\n  workspace store, which is the decision that unlocks everything else.\n- **Memory that's a graph, not a transcript.** NAMS captures three memory types — **short-term**\n  (conversation), **long-term** (entities + preferences, extracted automatically), and **reasoning**\n  (the agent's decision/tool-call traces, the rare one that powers honest \"why did you suggest this?\").\n- **Cross-graph traversal.** Because both graphs are co-resident, a recommendation is a single Cypher\n  hop — `(:User)-[:PREFERS]->(:Activity)<-[:OFFERS]-(:Park)` — not a join across two systems. That's\n  something a chatbot-over-an-API simply can't do.\n\n---\n\n\n## ✨ How Eve + NAMS fit together\n\n![Eve + NAMS fit together](img/5-memory-gateway.png)\n\nThis is the part worth copying into your own Eve app. Each pattern is one small, real file.\n\n### 1. One boundary around NAMS — [`lib/memory.ts`](lib/memory.ts)\nEvery memory call goes through a `MemoryGateway` interface; the only file that imports the\n`@neo4j-labs/agent-memory` SDK is the adapter behind it. Per-user isolation is enforced by constructing\n**one `MemoryClient` per user** with `namespace = userId`.\n\n### 2. Persist every turn the right way — [`agent/hooks/persist-turn.ts`](agent/hooks/persist-turn.ts)\nPersistence is an **Eve hook** on `message.received` / `message.completed` / `reasoning.completed`, not\na tool the model has to remember to call. Memory becomes a runtime guarantee instead of a hope, and a\nslow memory write never blocks the user's turn.\n\n![Persist every turn the right way](img/4-eve-turn.png)\n\n### 3. Server-bound identity — [`lib/eve-auth.ts`](lib/eve-auth.ts) · [`agent/channels/eve.ts`](agent/channels/eve.ts) · [`lib/agent-ctx.ts`](lib/agent-ctx.ts)\nThe signed-in [Better Auth](https://www.better-auth.com/) user flows from the request cookie → the Eve\nchannel's auth function → `ctx.session.auth.current.principalId`. **No tool accepts a `userId`**, so the\nmodel can't spoof whose memory it touches.\n\n### 4. Cross-graph bridges — [`lib/bridges.ts`](lib/bridges.ts) + [`lib/canonicalize.ts`](lib/canonicalize.ts)\nWhen a user states a preference, we write the raw fact to NAMS **and** a deterministic\n`(:User)-[:PREFERS]->(:Activity|:Topic)` edge into the graph — canonicalizing free text (\"dark skies\")\nto a real domain node (`:Activity {name:\"Astronomy\"}`). The edge keeps the user's original words for\nhonest explanations and makes personalization show up instantly. Every new domain node type becomes a\nnew bridge target: the context graph now also captures **how you travel** (`TRAVELS_WITH` a\n`:Constraint`, `REQUIRES` an `:Amenity`), passes you hold (`HOLDS`→`:EntrancePass`), stamps you've\ncollected (`COLLECTED`→`:PassportStamp`), and your travel window (`AVAILABLE`→`:Season`) — so a single\ntraversal can satisfy *and explain* an accessibility- and pass-aware recommendation.\n\n### 5. Memory beyond the chat box — [`lib/recommend.ts`](lib/recommend.ts) · [`lib/explain.ts`](lib/explain.ts)\nBecause the context graph is just data in Neo4j, the homepage \"For you,\" the map defaults, and the\n\"because you liked…\" rationale all read the same preferences the ranger does — not only the agent.\n\n![Persist every turn the right way](img/trail-graph-memory.png)\n\n### 6. The graph, made visible — [`components/graph/NvlGraph.tsx`](components/graph/NvlGraph.tsx)\nThe `/graph` constellation and an interactive one-hop graph on every park page are rendered with the\n**Neo4j Visualization Library (NVL)** — the engine behind Neo4j Bloom — so the product *looks* like the\ngraph it is.\n\nA companion deep-dive on this integration is written up as a blog post [here](https://lyonwj.com/blog/agent-memory-with-eve-and-nams).\n\n---\n\n## What you can do\n\n![What you can do](img/trail-graph-plan.png)\n\n- **Explore** 470+ NPS sites with full-text + faceted search (activity, topic, **amenity**, state,\n  dark-sky), and a personalized **\"For you\"** rail.\n- **Search** (`/search`) — one query box, **semantic** results across parks, **places** (17k POIs), and\n  **people** (historical figures), ranked by meaning via per-node vector embeddings.\n- **Map** every site on a clustered MapLibre map with layer toggles (campgrounds, visitor centers,\n  things-to-do, active alerts) loaded by viewport, plus real **park boundary** overlays on detail maps.\n- **Read the map, don't just look at it** — switch the labeled vector basemap between **Topo** and\n  **Dark**, then recolor every park by a **data lens** (dark sky, crowds, entry fee, or accessibility)\n  or by **live conditions** (weather + road events), so the map answers a question instead of plotting\n  dots.\n- **See the graph on the map** — toggle **park-to-park edges** (the materialized `NEAR` proximity graph\n  plus shared-topic / shared-activity links) to trace journeys across the country, and flip on\n  **\"your map\"** to light up the parks you've considered and the passport stamps you've collected.\n- **Ask the map** — a **ranger command bar** turns \"dark-sky parks near Moab\" into a focused, filtered\n  view, and you can **build a trip right on the canvas**: click parks to add stops, watch live\n  drive-time / mileage metrics, and drag to reorder.\n- **Take it into the field** — generate an **offline pack** (boundaries + POIs zipped for the area), a\n  printable **field sheet**, or a **share-a-view** deep link that reopens the exact map someone else was\n  looking at.\n- **Fly the parks in 3D** — with optional terrain enabled, trip routes and the `/journeys` story tour\n  become cinematic **3D fly-throughs** (gracefully flat, pitched 2D when no elevation source is set).\n- **Plan** multi-park, multi-day trips with drive segments, day-by-day pacing, graph-aware route\n  optimization, per-trip alert checks, **date-aware open/closed validation** (`check_open` flags a road\n  or facility that's closed on your travel dates), a **real fees/passes budget** (per-vehicle/person/\n  motorcycle entrance fees summed from NPS data, with the America-the-Beautiful break-even and fee-free-day\n  nudges), shareable read-only links, and `.ics` export — or **seed a trip from an official NPS tour**.\n- **Trails** (`/trails`) — real, hikeable **trails** with an elevation profile, a route map, and trailhead\n  logistics, searchable by length, elevation gain, difficulty, route type, dogs, accessibility, season, and\n  permit (NPS Public Trails GIS geometry; difficulty + time are labeled estimates, never a safety guarantee).\n  **Add a hike to a trip day** (nested under a park stop, with an over-packed-day warning and real per-hike GPX\n  tracks), **stitch connected trails into loops** (\"link Bright Angel + South Kaibab for a rim-to-rim\"), search\n  by **vibe** (\"a quiet alpine-lake hike with wildflowers under 5 mi\") or hit **surprise me**, see what\n  **hikers like you** also did, and follow **Trail ↔ Learn ↔ Journeys** cross-links. The ranger remembers your\n  trail preferences and saved / bucket-list / hiked trails.\n- **Journeys** (`/journeys`) — cross-park **thematic journeys** connected by a historical figure or a\n  shared topic, highlighted on the graph constellation, with a scrollytelling 3D tour. (This is the original\n  \"Trails\" theme feature, rebranded so `/trails` could mean real trails.)\n- **Chat** with the **ranger**, which recalls your preferences, recommends parks with reasons, builds\n  trips, finds places/people semantically (`find_place`/`find_person`), finds real trails (`find_trails`,\n  `trail_vibe`, `build_loop`), and respects how you travel — remembering what you like across sessions.\n- **Plan for how *you* travel** — tell the ranger you use a wheelchair, travel in a 30-ft RV, need a\n  specific amenity, hold an annual pass, or are going in September, and every later recommendation,\n  itinerary, and cost honors it — with provenance (\"has a wheelchair-accessible campground\").\n- **Collect** passport stamps and see events that land during your travel window, per park.\n- **Your memory** (`/me`): see, tune (boost/down-rank), and delete everything the app remembers —\n  preferences, considered parks, trips, travel constraints, passes, stamps, and dates — with durable\n  deletes (tombstones) so extraction won't resurrect them.\n- **Conditions** on each park: dark-sky/Bortle rating, best months + a monthly-visitation chart, trail\n  difficulty/length, current weather, timed-entry, **operating hours + seasonal closures**, an\n  **accessibility scorecard** (reported features across places/campgrounds/trails/parking), **parking +\n  EV charging**, the **latest NPS news releases**, and live **webcams + road events** — graph-native\n  or on-demand behind swappable adapters.\n- **Nearby & regional** discovery: a materialized `NEAR` proximity graph and curated geographic\n  `:Region`s seed tighter multi-park trips (\"what else is within range of Mesa Verde?\").\n- **Dark mode** (system-aware, with a toggle in the nav) across the whole app, including the map basemap.\n\n![TrailGraph trails](img/trail-graph-trails.png)\n\n---\n\n## Tech stack\n\n![Architecture diagram](img/architecture.png)\n\n| | |\n|---|---|\n| **Framework** | Next.js (App Router, RSC) · React · TypeScript |\n| **Agent** | [Eve](https://vercel.com/) (durable agent runtime) · AI Gateway |\n| **Memory** | [NAMS](https://neo4j.com/labs/) — `@neo4j-labs/agent-memory`, hosted, on an external Neo4j |\n| **Database** | Neo4j (domain graph + context graph + app data) |\n| **Search** | Neo4j full-text + faceted, and semantic vector search (parks/places/people) via AI Gateway embeddings |\n| **Auth** | Better Auth (passwordless magic link) |\n| **UI** | Chakra UI v3 — custom *\"Topographic Adventure\"* theme (`theme/`: pine/trail/sand tokens, light-first dark mode, recipes) · Bricolage Grotesque + Inter (`next/font`) · `react-icons` · MapLibre GL + Protomaps · Neo4j NVL · Recharts |\n| **Routing** | OpenRouteService (drive segments) |\n\n---\n\n## Getting started\n\n**Prerequisites:** Node 20+, `pnpm`, a Neo4j 5.x instance, an\n[NPS API key](https://www.nps.gov/subjects/developer/get-started.htm), a NAMS workspace + API key\n(pointed at your Neo4j), and an AI Gateway key.\n\n```bash\ncp .env.example .env.local        # NPS, Neo4j, NAMS, Eve/AI Gateway, auth, routing keys\npnpm install\npnpm db:migrate                   # constraints + point / full-text / vector indexes\npnpm nams:spike                   # ✅ proves NAMS writes land in YOUR Neo4j (the core bet)\npnpm dev                          # Next + the Eve ranger together — open http://localhost:3000/plan\n```\n\nThen populate the domain graph from the NPS API: `curl \"http://localhost:3000/api/sync?tier=all\"`\n(and `pnpm datasources:sync` for the dark-sky / crowds / trail-difficulty conditions). The sync is\n**resumable and rate-limit-tolerant** — large resources page-and-checkpoint, so a `429` pauses (saving\na cursor) and the next run continues; the response reports `{paused:[…]}`. It also embeds `:Place`/\n`:Person` for semantic search (content-hash gated); add `EMBED_ARTICLES=1` to also embed the ~19k\narticles. The sync also promotes the rich NPS payloads it already downloads into queryable nodes —\noperating hours + seasonal closures, structured entrance fees, campground inventory, event recurrence,\naccessibility, news releases, and a `NEAR`/region graph (see [`docs/DECISIONS.md`](docs/DECISIONS.md)\nADR-059). Self-guided audio + multimedia is opt-in behind `SYNC_MULTIMEDIA=1` (large, off by default).\n`pnpm sync:reset <resource>…` clears specific checkpoints to force a re-sync.\n\n**Real trails** are a separate ingest (ADR-066–073): `pnpm trails:sync` — or the slow sync with\n`SYNC_TRAILS=1` — pulls NPS Public Trails GIS into `:Trail` nodes, simplifies the geometry to **Vercel Blob**\n(`BLOB_READ_WRITE_TOKEN`; local `public/trails/` in dev), joins curated NPS hikes, and derives the\nloop-builder `CONNECTS` network. Add `SYNC_TRAIL_ELEVATION=1` (+ `ELEVATION_API_URL`, opentopodata-compatible)\nfor elevation profiles — it throttles to the public ~1 req/s (`TRAIL_ELEV_THROTTLE_MS=0` for a self-hosted host)\nand on the daily-quota `429` stops + resumes next run — `EMBED_TRAILS=1` for trail vibe-search, and\n`ENRICH_OSM_TRAILS=1` for OSM-fill of NPS-empty parks. On Vercel\nthe Blob token is **required** for trails (the local-file fallback is dev-only) — see\n[`docs/DEPLOY-MAP-DATA.md`](docs/DEPLOY-MAP-DATA.md).\n\n> `pnpm dev` auto-starts the Eve agent behind the app via Eve's `withEve`. To run the app **without** the\n> agent (just Explore / Map / Plan UI): `DISABLE_EVE=1 pnpm dev`.\n\n**Maps:** with no setup, maps fall back to MapLibre demo tiles. For a real terrain basemap, build a\nself-hosted Protomaps extract:\n\n```bash\nbrew install pmtiles   # go-pmtiles CLI\nPMTILES_SOURCE=https://build.protomaps.com/<YYYYMMDD>.pmtiles pnpm build:basemap\n```\n\nThis writes `public/basemap/us.pmtiles` (gitignored; host it on a CDN for production — see below). Any\nMapLibre `style.json` URL works in `NEXT_PUBLIC_MAP_TILES_URL` too.\n\n**Labels work with zero setup.** The map's label-font glyphs are **self-hosted and committed** (the Noto\nSans PBFs under `public/basemap/fonts/`, regenerated with `pnpm build:glyphs`) and served same-origin, so\npark / city / road names render even before you build a basemap — there's no third-party glyph host to\n404 and silently drop every label. To serve glyphs from your own CDN instead, point\n`NEXT_PUBLIC_MAP_GLYPHS_URL` at a `{fontstack}/{range}.pbf` template.\n\n**3D terrain is optional (off → flat).** Maps render flat by default. Set `NEXT_PUBLIC_MAP_TERRAIN_URL`\nto a raster-DEM tile template (`…/{z}/{x}/{y}.png`) or a TileJSON URL to enable 3D terrain and the trip /\n`/trails` fly-throughs — AWS's open **Terrarium** elevation tiles\n(`https://elevation-tiles-prod.s3.amazonaws.com/terrarium/{z}/{x}/{y}.png`) are a good free default. The\nencoding defaults to `terrarium` (override with `NEXT_PUBLIC_MAP_TERRAIN_ENCODING`); set\n`NEXT_PUBLIC_MAP_TERRAIN_ATTRIBUTION` for the credit line. Whatever DEM host you choose, **add it to both\n`img-src` and `connect-src`** in the `next.config.ts` CSP (the AWS host is allowed out of the box). With\nthe env unset every terrain hook is a no-op, so fly-throughs degrade to flat, pitched 2D camera moves.\n\n---\n\n## Deploy to Vercel\n\nBecause the ranger is wired in with **`withEve(nextConfig)`** (`next.config.ts`), the agent and the app\n**compile and deploy together as one ordinary Vercel app** — there's no separate `eve build`/`eve deploy`\nstep (those are for standalone agent projects). The build command stays `next build`.\n\n1. **Push to GitHub and import the repo in Vercel** (or `vercel --prod` from the CLI). Vercel\n   auto-detects Next.js; leave the build command as the default.\n2. **Set environment variables** (Production) — everything in `.env.example`:\n   - `NEO4J_URI`/`USERNAME`/`PASSWORD`/`DATABASE` — reachable from Vercel (e.g. **Neo4j Aura**,\n     `neo4j+s://…`).\n   - `NAMS_API_KEY` + `NAMS_WORKSPACE_ID` — the NAMS workspace must point at **that same Neo4j**\n     (the context-graph bet). Leave `NAMS_BASE_URL` blank.\n   - `NPS_API_KEY`; `BETTER_AUTH_SECRET` + `BETTER_AUTH_URL=https://<your-domain>`; `RESEND_API_KEY` +\n     `EMAIL_FROM`; `ORS_API_KEY`.\n   - `AGENT_MODEL` / `EMBEDDING_MODEL`. On Vercel, **models resolve through AI Gateway via the project's\n     OIDC token**, so `AI_GATEWAY_API_KEY` is only needed locally. The Eve channel admits Vercel\n     deployments through `vercelOidc()` (`agent/channels/eve.ts`). **Do not set `EVE_BASE_URL`.**\n   - `CRON_SECRET` — Vercel sends it as the `Authorization: Bearer` on cron calls; `/api/sync` checks it.\n   - `NEXT_PUBLIC_MAP_TILES_URL` — the Blob URL from the next section.\n   - `BLOB_READ_WRITE_TOKEN` — a **Vercel Blob** store, required for the basemap **and** trail geometry\n     (both are too large for / written outside the deploy bundle; the local-file fallback can't run on\n     Vercel's read-only FS). Trail flags for the cron sync: `SYNC_TRAILS=1` (+ optional\n     `SYNC_TRAIL_ELEVATION=1`/`ELEVATION_API_URL`, `EMBED_TRAILS=1`, `ENRICH_OSM_TRAILS=1`). See\n     [`docs/DEPLOY-MAP-DATA.md`](docs/DEPLOY-MAP-DATA.md).\n3. **Deploy.** The scheduled jobs in [`vercel.json`](vercel.json) start automatically: a once-daily\n   full sync (`/api/sync?tier=all` — corpus + alerts/events + data sources) and a once-daily memory\n   reconcile. This fits **Vercel Hobby** (≤2 cron jobs, daily). On Pro you can split into more frequent\n   `tier=slow`/`tier=fast` schedules. `/api/sync`'s long runtime comes from its route-segment\n   `export const maxDuration` (Fluid Compute / Pro).\n4. **One-time data setup** against the production Neo4j (run locally with prod `NEO4J_*` in\n   `.env.local`): `pnpm db:migrate` · `pnpm ontology:setup` · `pnpm nams:spike`, then seed the graph\n   (`curl -H \"Authorization: Bearer $CRON_SECRET\" \"https://<your-domain>/api/sync?tier=all\"` and\n   `pnpm datasources:sync`). For **real trails**, run `SYNC_TRAIL_ELEVATION=1 EMBED_TRAILS=1 pnpm trails:sync`\n   with `BLOB_READ_WRITE_TOKEN` set so `:Park.trailsGeoUrl` stores Blob URLs (not local paths) — full\n   handling of the trail GeoJSON + DEM on Vercel is in [`docs/DEPLOY-MAP-DATA.md`](docs/DEPLOY-MAP-DATA.md).\n\n### Host the basemap on Vercel Blob (CDN)\n\nThe `.pmtiles` file is too large for a deploy bundle (and is gitignored). Put it on **Vercel Blob**,\nwhich serves it from Vercel's CDN with the HTTP range support PMTiles needs:\n\n```bash\n# 1) Create a Blob store: Vercel dashboard → Storage → Blob, then expose its token locally:\nvercel env pull .env.local            # provides BLOB_READ_WRITE_TOKEN  (or export it manually)\n# 2) Build + upload (streamed multipart; stable URL):\npnpm build:basemap                    # → public/basemap/us.pmtiles\npnpm basemap:upload                   # → prints https://<store>.public.blob.vercel-storage.com/basemap/us.pmtiles\n# 3) Set NEXT_PUBLIC_MAP_TILES_URL to that URL in the Vercel project (Production) and redeploy.\n```\n\n`basemap:upload` verifies the uploaded URL answers a `Range` request with `206` before you wire it up.\n\n> **Use the public URL printed above, exactly — `*.public.blob.vercel-storage.com/…` with no query\n> string.** Do **not** paste a `*.private.blob…` host or a signed `?vercel-blob-delegation=…` download\n> URL (e.g. from the Blob dashboard): those are served through Blob's auth proxy, which **ignores HTTP\n> range requests** — so every client downloads the *entire* hundreds-of-MB `.pmtiles` file — and the\n> signed token **expires ~12h** after each deploy, dropping all users to demo tiles.\n\nRe-run `build:basemap` + `basemap:upload` to refresh tiles (the object name is stable, so the URL\ndoesn't change). The map falls back to demo tiles automatically if the URL is unset or unreachable.\n\n---\n\n## Project structure\n\n```\napp/         Next.js App Router — pages + Route Handlers (/api/auth, /api/sync, /api/trips, …)\nagent/       Eve agent — instructions.md, agent.ts, tools/, channels/eve.ts, hooks/persist-turn.ts\nlib/         adapters + domain logic — memory (NAMS), neo4j, bridges, recommend, queries, datasources/\ntheme/       Chakra design system — tokens, semantic tokens, recipes, textures (brand: pine/trail/sand)\ncomponents/  UI — chat, plan, graph (NVL), map, park, memory, ui/ (primitives)\ndb/          Cypher migrations + migrate/verify runners\nscripts/     seed, ontology setup, basemap build + Blob upload, data-source + trail sync, the NAMS spike\nevals/       Eve eval suite\ntests/       integration (real Neo4j, gated) + e2e (Playwright)\n```\n\n---\n\n## Testing\n\n```bash\npnpm typecheck\npnpm test:unit                          # pure logic, mocked I/O — runs anywhere\nRUN_INTEGRATION=1 pnpm test:integration # real Neo4j (CI uses an ephemeral container) — never prod\npnpm test:e2e                           # Playwright — builds + serves a prod build; needs a seeded Neo4j: pnpm seed:test\n```\n\nUnit tests cover the pure logic (recommendation ranking, canonicalization, route ordering, ICS, the\ndata-source derivations, **the NPS data-feature parsers** — operating-hours/open-closed, fee units,\ncampsite inventory, event-date expansion, accessibility/region derivation, contacts, trail metrics —\nNVL data mapping, brand-color resolution, server-bound identity). Integration tests exercise the real\ngraph (domain queries, the trip service, cross-graph recommendations, the Better Auth adapter, memory\ndelete + tombstones, sharing, and **the data features end-to-end** in\n`tests/integration/nps-data-features.itest.ts`: `check_open`, the fee budget, the fixed campground\n`HAS_AMENITY` edge, the accessibility scorecard, news/article search, regions + `NEAR`). E2E covers the\npublic surface (incl. the new park-page hours/accessibility/news/parking blocks in\n`tests/e2e/nps-data-features.spec.ts`) and an authenticated trip-building flow — run against a\n**production build** (`pnpm build && pnpm start`), because Chakra's Emotion SSR only yields a trustworthy\nhydration signal in prod (dev emits class-hash false positives).\n\n---\n\"\n\n\n  \n![](img/trail-graph-search.png)\n\n> ⚠️ TrailGraph is a demo, **not** an official NPS safety source — always defer to NPS.gov and rangers\n> for life-safety decisions.\n\nBuilt with Neo4j · Eve · NAMS. National Park data courtesy of the [NPS Data API](https://www.nps.gov/subjects/developer/index.htm).\n",
  "bytes": 22859,
  "sha": "7f1e449fb05b1d49a18279d43f787c647b83c96adcf82be627c50097ede04f4b",
  "repo_slug": "johnymontana/trailgraph",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/okf_johnymontana_trailgraph_openwiki_index_m_fafd2270/readme"
}