Skip to content

Latest commit

 

History

History
77 lines (52 loc) · 6.47 KB

File metadata and controls

77 lines (52 loc) · 6.47 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Commands

Everything runs through bun (never npm/npx/yarn):

bun install
bun run typecheck      # tsc --noEmit for root AND packages/agui — ALWAYS use this script;
                       # a bare `tsc` may hit an older PATH TypeScript that rejects this
                       # repo's ignoreDeprecations setting (TS5103)
bun run lint           # biome check . — fix violations with: bunx biome check --write .
bun run test           # vitest run (happy-dom), all suites
bun run test -- packages/agui/src/events.test.ts   # single test file
bun run build          # design tokens → tsup (core dist/) → styles → packages/agui dist/
bun run playground:full   # vite :5173 + bun API server :3030 (LLM + mock AG-UI agent)
bun run playground:build  # vite build — this is what Vercel deploys (vercel.json)

Gate order before declaring work done: typecheck → lint → test → build. CI (.github/workflows/ci.yml) runs lint → typecheck → test → build on push/PR to main. Pushing main deploys to Vercel — never push without being asked.

Conventions

  • TDD is the workflow here: write the failing test first, watch it fail, then implement. Tests live next to sources (*.test.ts), vitest + happy-dom, no mocks unless unavoidable.
  • Pure functions over classes; the registry is the only global state in the core.
  • Conventional-commit messages; no backticks inside commit messages.
  • DESIGN.md is the design-token theme file (YAML consumed by bun run tokenssrc/design-tokens.css, and injected into the LLM system prompt) — not an architecture doc.

Architecture

Three layers, one direction of data flow: agents emit ComponentSpec JSON → registry dispatches to pure renderers → DOM. User interactions flow back as ActionEvent { action, payload? }.

Core renderer — src/ (@baruch-eric/stream-ui, zero runtime deps)

  • types.ts — the ComponentSpec discriminated union (~23 kinds) + BUILTIN_KINDS runtime list.
  • schemas.ts — hand-authored plain-JSON-Schema per builtin kind. Must stay in sync with types.ts — adapters derive the agent-facing tool schema from these. Child-spec arrays reference #/$defs/componentSpec; the tool-schema assembler supplies that def.
  • registry.tsregister(kind, renderer, schema?) / getSchema / listKinds; createElement never throws (unknown kinds render a fallback node).
  • components.ts — every builtin is a pure (spec, onAction?) => HTMLElement, callable without the registry.
  • index.ts — auto-registers builtins on import; render() (replaces + preserves focus/selection), append(), clear().

AG-UI adapter — packages/agui/ (@baruch-eric/stream-ui-agui)

Connects any AG-UI protocol agent (https://docs.ag-ui.com) to the renderer. Deps: @ag-ui/core + fast-json-patch only (deliberately NOT @ag-ui/client — rationale in docs/agui-integration-design.md).

  • events.ts — dispatcher: AG-UI events → DOM. Streamed text → progressive paragraphs; render_ui/append_ui tool calls → render()/append(); CHUNK expansion; MESSAGES_SNAPSHOT may carry tool calls some servers never stream. Philosophy: warn-and-skip, never throw on agent output.
  • agent.ts — run loop owning transcript, threadId, abort, follow-up runs. Key protocol fact: AG-UI has no client→server event channel — everything client-initiated (user messages, tool results, readables→context, state, interrupt resume entries) rides the next run's RunAgentInput.
  • http.ts — reference POST + SSE connector, events validated via @ag-ui/core EventSchemas. Transport-agnostic: anything (input, signal) => AsyncIterable<event> works as source.
  • hitl.ts / readables.ts / state.tsrenderAndWait (first ActionEvent resolves; abort rejects RunCancelledError), context readables, shared-state store (RFC 6902 deltas, skip-on-failure). HITL supports both classic tool-result and first-class interrupt/resume flows.

Backends — api/ + playground/

api/*-core.ts modules are runtime-agnostic and shared by both the Vercel functions (api/agent.ts, api/agui.ts) and the local bun server (playground/server.ts, port 3030):

  • agent-core.ts — LLM agent (Vercel AI SDK via AI Gateway) emitting the ad-hoc {thinking|render|append|done|error} SSE protocol for the original playground page.
  • agui-core.ts — keyword-routed mock agent speaking schema-valid AG-UI events; needs no API key; drives /agui.html.

Playground is a vite multi-page app (index.html = LLM demo, agui.html = AG-UI demo); vite proxies /api and /agui to :3030.

Workspace & module resolution (important, non-obvious)

The repo is a bun workspace where the core package stays at the root and packages/agui depends on it by published name. Because bun can't workspace:-link the root package and file: deps are stale copies, dev-time resolution of @baruch-eric/stream-ui is handled by:

  • resolve.alias in both vitest.config.ts and vite.config.tssrc/index.ts
  • a paths mapping in packages/agui/tsconfig.json (safe: no composite project references in this repo; that tsconfig is noEmit-only — tsup owns the build via packages/agui/tsup.config.ts, which marks core/@ag-ui/core/fast-json-patch external)

Don't "simplify" this without re-testing all four consumers (tsc, vitest, vite dev, vite build).

@baruch-eric/stream-ui is not yet published to npm; the adapter's peer dep is temporarily peerDependenciesMeta.optional: true so installs don't 404 — remove that flag when the core is published.

Environment / gotchas

  • The playground server walks parent dirs loading .env files (e.g. ~/Arik/dev/.env). The AI SDK gateway reads AI_GATEWAY_API_KEY; VERCEL_AI_GATEWAY_API_KEY is aliased by applyGatewayKeyAlias() — it must be re-applied after env loading (module-load ordering bug happened once already).
  • The AG-UI protocol is pre-1.0 and moving (SDK 0.0.57, 33 event types, TypeScript SDK lives at sdks/typescript/packages/* in their repo). Unknown event types are deliberately warn-and-skip at both transport and dispatcher level.
  • Playground themes (settings ⚙️, localStorage) override design tokens — the Heritage theme paints primary red; don't mistake it for a variant bug.
  • docs/agui-listing.md holds prepared ag-ui Clients-table material; their CONTRIBUTING requires an issue-first flow and npm-published packages before submitting.