Monorepo for Aesthetic.Computer aesthetic.computer
at main 390 lines 12 kB view raw view rendered
1# CLAUDE.md 2 3This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. 4 5## Project Overview 6 7Aesthetic Computer (AC) is a mobile-first runtime and social network for creative computing. It's designed as a musical instrument-like interface where users discover memorizable paths through commands and published "pieces" (interactive programs). The system supports both JavaScript (.mjs) and KidLisp (.lisp) pieces. 8 9## Agent Memory (Local-First) 10 11When @jeffrey is working, Claude hook events are written to a local encrypted memory store first. 12 13- **Hook**: `.claude/settings.json``UserPromptSubmit``node memory/hook.mjs` 14- **Git hook**: `.githooks/post-commit` → commit log + Codex import + remote flush 15- **Local store**: `~/.ac-agent-memory` (overridable via `AGENT_MEMORY_HOME`) 16- **Encryption**: AES-256-GCM (`AGENT_MEMORY_KEY` optional; local key file otherwise) 17- **Redaction**: metadata and summaries are redacted before indexing/sync 18- **CLI**: `node memory/cli.mjs` (`list`, `remember`, `checkpoint`, `doctor`, `profile`, `flush-remote`) 19- **Codex import**: `node memory/codex-sync.mjs` 20 21Remote writes are optional and disabled by default: 22 23- `AGENT_MEMORY_REMOTE_ENABLED=true` + `AGENT_MEMORY_REMOTE_URL=...` enables queued remote sync 24 25`remember` continuity is represented as lineage (`remembered_from`) instead of taking over a live mutable session. 26 27## AestheticAnts & Score.md 28 29This repository uses an automated maintenance system called "AestheticAnts" (AA). The main score lives at `SCORE.md`, and ant-specific mindset/rules live in `ants/mindset-and-rules.md`. Read both before contributing. 30 31**Important:** Do not modify `ants/mindset-and-rules.md` unless you are the queen (@jeffrey). 32 33## Development Commands 34 35### Running the Development Environment 36 37The system consists of multiple servers that run concurrently: 38 39```bash 40# Run all servers (site, session, edge, stripe) - primary dev command 41npm run aesthetic 42# or shorthand: 43npm run ac 44 45# Run individual servers: 46npm run site # Main dev server (port 8888) 47npm run server:session # Session backend (port 8889) 48npm run stripe # Stripe webhook listener 49``` 50 51### Testing 52 53```bash 54# Run all tests 55npm test 56 57# KidLisp tests (with auto-reload on changes) 58npm run test:kidlisp 59 60# KidLisp tests (direct, no watch) 61npm run test:kidlisp:direct 62 63# Performance tests 64npm run test:perf 65npm run test:perf:chrome 66npm run test:perf:lighthouse 67``` 68 69### Creating New Pieces 70 71```bash 72# Generate a new piece from the blank.mjs template 73npm run new piece-name "Description of the piece" 74``` 75 76### Working with Sessions 77 78```bash 79# List active session backends 80npm run session:alive 81 82# View logs for a specific session 83npm run server:session:logs SESSION_ID 84 85# Reset/terminate a session 86npm run session:reset SESSION_ID 87 88# Deploy session server 89npm run session:publish 90``` 91 92### Assets Management 93 94```bash 95# Sync assets down from Digital Ocean Spaces 96npm run assets:sync:down 97 98# Sync assets up to Digital Ocean Spaces 99npm run assets:sync:up 100``` 101 102### AC Native OS (fedac/native/) 103 104```bash 105# Full build pipeline: binary → initramfs → kernel 106ac-os build 107 108# Build + flash USB 109ac-os flash 110 111# Build + upload OTA release (ALWAYS rebuilds first) 112ac-os upload 113 114# Build + flash + upload 115ac-os flash+upload 116``` 117 118**Critical:** `ac-os upload` always does a full rebuild before uploading. The kernel embeds the git hash and build name at compile time (`AC_GIT_HASH`, `AC_BUILD_NAME` in the Makefile). Uploading without rebuilding would serve a stale kernel with the wrong version string. 119 120### Notation 121 122- **compush** - commit & push 123 124## Architecture 125 126### Core Components 127 1281. **Boot System** (`system/public/aesthetic.computer/boot.mjs`) 129 - Entry point that loads the BIOS and initializes the runtime 130 - Manages service worker registration for module caching 131 - Handles WebSocket module loader for hot reloading 132 - Boot telemetry system logs to `/api/boot-log` 133 1342. **BIOS** (`system/public/aesthetic.computer/bios.mjs`) 135 - Main runtime coordinator 136 - Manages piece lifecycle (boot, act, paint, sim, etc.) 137 - Provides the API surface for pieces 138 - Handles routing and navigation 139 1403. **Disk System** (`system/public/aesthetic.computer/lib/disk.mjs`) 141 - Large module (~572KB) that provides the core API for pieces 142 - Graphics primitives, audio, input handling, UI components 143 - All pieces interact with AC through the Disk API 144 1454. **Module Loader** (`system/public/aesthetic.computer/module-loader.mjs`) 146 - WebSocket-based dynamic module loading 147 - Enables hot reloading during development 148 - Prefetches commonly used modules 149 150### Pieces 151 152Pieces are the fundamental unit of content in AC. Each piece is a single `.mjs` or `.lisp` file located in `system/public/aesthetic.computer/disks/`. 153 154#### Piece Structure (JavaScript) 155 156Pieces export lifecycle functions that receive an API object: 157 158```javascript 159// boot: runs once when the piece loads 160function boot({ wipe, screen, params, colon, api }) { 161 // Initialize state 162} 163 164// paint: runs every frame 165function paint({ wipe, ink, line, circle, screen }) { 166 // Render graphics 167} 168 169// act: handles user input and events 170function act({ event: e }) { 171 if (e.is("keyboard:down:space")) { 172 // Handle spacebar press 173 } 174} 175 176// sim: simulation/game logic that runs every frame 177function sim() { 178 // Update game state 179} 180 181export { boot, paint, act, sim }; 182``` 183 184Common lifecycle functions: 185- `boot` - Initialization, runs once 186- `paint` - Rendering, runs every frame 187- `act` - Event handling (input, network, etc.) 188- `sim` - Simulation/logic, runs every frame 189- `leave` - Cleanup when exiting the piece 190- `preview` - Static preview image generation 191 192#### Piece API Surface 193 194The API is provided through function parameters. Common APIs: 195- **Graphics**: `wipe`, `ink`, `line`, `box`, `circle`, `plot`, `paste`, etc. 196- **Text**: `write`, `type`, `paste`, `help` 197- **Input**: `event`, `pen`, `hand`, `gamepad` 198- **Audio**: `sound`, `speaker`, `microphone` 199- **UI**: `ui.Button`, `ui.TextInput`, `cursor` 200- **System**: `screen`, `params`, `colon`, `store`, `net`, `jump`, `send` 201 202### Servers and Services 203 2041. **System Server** (`system/` + `lith/`) 205 - Production: lith monolith (Express + Caddy on DigitalOcean VPS) 206 - Dev: `npm run site` (port 8888) 207 - Backend functions in `system/netlify/functions/` 208 - Handles piece serving, authentication, storage APIs 209 2102. **Session Server** (`session-server/`) 211 - Per-session backend using Jamsocket for ephemeral deployments 212 - WebSocket server using Geckos.io for real-time communication 213 - Manages chat, multiplayer state, and real-time features 214 - Uses Redis for state synchronization 215 2163. **Feed Server** (`dp1-feed/`) 217 - Cloudflare Worker for activity feeds 218 - Deployed separately to Cloudflare Workers 219 220### KidLisp 221 222KidLisp is a minimal Lisp dialect for generative art, with 118 built-in functions across 12 categories. See `kidlisp/README.md` for comprehensive documentation. 223 224**Key files:** 225- `system/public/aesthetic.computer/lib/kidlisp.mjs` - Main evaluator 226- `system/netlify/functions/store-kidlisp.mjs` - Storage API 227- `kidlisp/tools/` - Analysis and debugging tools 228 229**Running KidLisp tools:** 230```bash 231# Analyze piece structure (requires dev server running) 232./kidlisp/tools/source-tree.mjs $cow 233 234# Get source code 235./kidlisp/tools/get-source.mjs $piece-code 236``` 237 238### Data Storage 239 240- **MongoDB**: User data, handles, chat messages, moods 241- **Redis**: Session state, real-time coordination 242- **Firebase**: Authentication, cloud messaging 243- **Digital Ocean Spaces**: Asset storage (CDN) 244 245### Routing and URLs 246 247- Pieces are URL-addressable: `https://aesthetic.computer/piece-name` 248- Parameters: `piece-name:param1:param2` 249- User pieces: `@handle/piece-name` 250- QR code sharing: `share piece-name` 251 252## Development Workflow 253 254### Local Development in Codespaces 255 256When running in GitHub Codespaces, the server is accessible at: 257``` 258https://{CODESPACE_NAME}-8888.app.github.dev 259``` 260 261Get your Codespace name: `echo $CODESPACE_NAME` 262 263### Hot Reloading 264 265The module loader provides hot reloading during development: 266- Piece changes are reflected immediately when saved 267- WebSocket connection shown in boot canvas 268- Use `channel custom-name` for multi-device testing 269 270### Publishing 271 272```bash 273# In the AC prompt: 274publish # Publish current piece 275publish piece-name # Publish with custom name 276source # Download blank template 277source piece-name # Fork existing piece 278``` 279 280## Important Directories 281 282- `system/public/aesthetic.computer/disks/` - All pieces (.mjs and .lisp files) 283- `system/public/aesthetic.computer/lib/` - Shared libraries and utilities 284- `system/netlify/functions/` - Serverless backend functions 285- `session-server/` - Real-time session backend 286- `shared/` - Code shared between system and session servers 287- `kidlisp/` - KidLisp language documentation and tools 288- `spec/` - Jasmine tests for KidLisp 289- `ants/` - AestheticAnts automated maintenance system 290 291## Key Patterns 292 293### Event Handling in Pieces 294 295Events use a string-based pattern matching system: 296```javascript 297event.is("keyboard:down:a") // 'a' key pressed 298event.is("touch") // Any touch event 299event.is("lift") // Touch/click released 300event.is("draw") // Drag with pen down 301event.is("keyboard:down:arrowup") // Arrow key 302``` 303 304### State Management 305 306Pieces maintain state in module-level variables: 307```javascript 308let score = 0; 309let enemies = []; 310 311function boot() { 312 // Initialize state 313} 314 315function sim() { 316 // Update state 317 score += 1; 318} 319``` 320 321### API Requests from Pieces 322 323Use the `net` API for HTTP requests: 324```javascript 325function boot({ net }) { 326 net.pieces("@user/list").then((data) => { 327 // Handle response 328 }); 329} 330``` 331 332### Multiplayer Networking (Dual-Channel Pattern) 333 334Multiplayer pieces use both WebSocket (reliable) and UDP (low-latency) channels. See `squash.mjs` for the canonical implementation. 335 336```javascript 337function boot({ net: { socket, udp }, handle }) { 338 // UDP for high-frequency position sync (low latency, may drop packets) 339 udpChannel = udp((type, content) => { 340 if (type === "game:move") { 341 const d = typeof content === "string" ? JSON.parse(content) : content; 342 // Update remote player position 343 } 344 }); 345 346 // WebSocket for reliable game events (join/leave, scoring, round control) 347 server = socket((id, type, content) => { 348 if (type.startsWith("connected")) { /* joined session */ } 349 if (type === "left") { /* player disconnected */ } 350 if (type === "game:join") { /* another player joined */ } 351 if (type === "game:score") { /* reliable score event */ } 352 }); 353 354 // Send reliable event 355 server.send("game:join", { handle: handle() }); 356 // Send low-latency position update 357 udpChannel.send("game:move", { x, y, vx, vy }); 358} 359``` 360 361**Session server routing:** 362- UDP handlers: add `channel.on("game:move", ...)` in geckos section of `session-server/session.mjs` 363- WebSocket: position messages use `others()` (relay to all except sender), game events use `everyone()` (catch-all relay) 364- Chat invites: typing `'piece-name'` in chat creates a clickable link to join 365 366**Reference pieces:** `squash.mjs` (2D platformer), `1v1.mjs` (3D FPS), `udp.mjs` (minimal UDP test) 367 368### UI Components 369 370```javascript 371function boot({ ui: { Button, TextInput } }) { 372 const btn = new Button("Click me", { box: [10, 10, 100, 40] }); 373} 374 375function act({ event: e }) { 376 if (btn.trigger(e)) { 377 // Button was clicked 378 } 379} 380``` 381 382## Notes 383 384- The codebase uses `.mjs` extension for ES modules 385- Prefer `const` destructuring for API parameters to minimize imports 386- Graphics operations are immediate-mode (no retained scene graph) 387- All coordinates are in pixels 388- Default color depth is 8-bit RGB (0-255 per channel) 389- The `wipe` function clears the screen and should be called first in `paint` 390- When making changes, consult `ants/mindset-and-rules.md` for the ant operating philosophy