3cf3694ee71abd001ff50d4638bc2610f7bc2905
Introduces a two-LLM-call pipeline: the first call classifies the user's message intent as "edit", "info", or "help". Edit messages proceed through the existing routing → edit → propose flow. Info messages get a generated response about site content from the manifest. Help messages get a templated capabilities overview. This handles open-ended questions like "What can I do?" or "What does my site have on it?" which previously had no path through the system. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Dynamic Sites
An LLM-powered website editing framework. Edit your site via SMS, a web API, or a visual editor — all driven by natural language.
Architecture
┌─────────────────────────────────────────────────┐
│ Channels │
│ SMS (Telnyx) │ POST /api/edit │ /editor │
└───────┬─────────┴────────┬─────────┴─────┬──────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────┐
│ Orchestrator (Express, port 3001) │
│ │
│ Webhook ──► Idempotency ──► Rate Limit ──► │
│ │
│ ┌──────────────────────────────────────┐ │
│ │ In-Process FIFO Queue (concurrency 1) │
│ │ │ │
│ │ propose: route ► LLM ► proposal │ │
│ │ apply: validate ► writeContentFile│ │
│ └──────────────────────────────────────┘ │
│ │
│ SQLite: idempotency, proposals, rate limits, │
│ audit log │
└───────────────────────┬─────────────────────────┘
│ writes canonical JSON
▼
┌──────────────────┐
│ content/ (JSON) │ ◄── shared volume
│ site-context.json│
└────────┬─────────┘
│ reads with TTL cache
▼
┌──────────────────┐
│ Astro SSR │
│ (port 4321) │
│ Homepage + Editor│
└──────────────────┘
Quick Start
Prerequisites
- Node.js 22+
- npm
Local Development
# Clone and install
npm install
# Start the Astro dev server (port 4321)
npm run dev
# In another terminal, start the orchestrator (port 3001)
npm run dev:server
# Visit http://localhost:4321 — the demo site renders from fixtures
# Visit http://localhost:4321/editor — log in with API_EDIT_SECRET
Environment Variables
Copy .env.example to .env and set at minimum:
API_EDIT_SECRET— shared secret for API auth and editor loginOLLAMA_API_KEY— required for LLM-powered edits
See .env.example for all options.
Docker
docker compose build
docker compose up -d
# Site: http://localhost:4321
# Orchestrator: http://localhost:3001/health
Project Structure
├── content/ # Canonical JSON content (the "database")
│ ├── sections/ # One JSON file per site section
│ ├── events.json # Upcoming events
│ └── .backups/ # Pre-apply backups (auto-managed)
├── config/
│ └── sms-sites.json # SMS routing allowlist
├── site-context.json # Brand tone, style, LLM prompt context
├── shared/ # Zod schemas + canonical JSON (workspace pkg)
│ └── src/
│ ├── schemas/index.ts # All Zod schemas (the contract)
│ ├── canonical-json.ts # Sorted-key JSON serialization
│ └── repo-validation.ts# Path → schema mapping
├── server/ # Orchestrator (workspace pkg)
│ └── src/
│ ├── index.ts # Entrypoint + graceful shutdown
│ ├── app.ts # Express app factory
│ ├── db.ts # SQLite (idempotency, proposals, audit)
│ ├── logger.ts # Structured logging (pino)
│ ├── queue/ # FIFO queue + job processor
│ ├── routes/ # API edit, SMS webhook, health
│ ├── llm/ # Ollama client with retry/validation
│ ├── sms/ # Telnyx parse, reply, templates
│ └── io/ # Filesystem writer (atomic, with backup)
├── src/ # Astro SSR site
│ ├── pages/
│ │ ├── index.astro # Homepage (renders from content/)
│ │ └── editor.astro # Editor (auth-gated React island)
│ ├── lib/
│ │ ├── site-bundle.ts # Content parser + validator
│ │ └── site-data.ts # Disk reader with TTL cache
│ ├── layouts/
│ │ └── BaseLayout.astro
│ └── components/
│ ├── sections/ # Astro section components
│ └── editor/ # React editor island
├── scripts/ # CLI tools
├── docker-compose.yml # Full stack (web + orchestrator)
├── Dockerfile # SSR site image
└── server/Dockerfile # Orchestrator image
Edit Flow
- User sends a natural language message (SMS, HTTP, or editor)
- Route: LLM determines which content file to edit
- Propose: LLM generates new JSON + plain-language summary
- Confirm: User replies YES/NO (SMS) or clicks confirm (editor/HTTP)
- Apply: Validated JSON is written to disk via atomic write
- Live: Astro SSR picks up the change on next request (TTL cache)
Key Design Decisions
- No Redis, no BullMQ: Simple in-process FIFO queue with concurrency 1
- No git: Content persistence is filesystem-only
- SQLite for everything: Idempotency, proposals, rate limits, audit log
- Zod is the contract: Schemas drive validation at every boundary
- Atomic writes: temp file + rename prevents partial writes
- Pre-write backups: Last 20 versions per file under
content/.backups/
Description
Languages
TypeScript
57.5%
Astro
34.8%
JavaScript
2.8%
CSS
2.7%
Dockerfile
1.2%
Other
1%