Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 407 lines 13 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**Routine OTA releases are built remotely on the oven.** When fedac/native/ 105changes land on `origin/main`, oven's git poller auto-triggers a build. To 106trigger or observe a build explicitly: 107 108```bash 109ac-os oven # Trigger remote OTA build for HEAD 110ac-os oven status # Show oven queue + recent builds 111ac-os oven watch # Tail active build logs (SSE) 112ac-os oven cancel # Cancel active oven job 113``` 114 115**Use `ac-os oven` for OTA releases — not `ac-os upload`.** `ac-os upload` 116is a local-build-and-push fallback that requires a clean tree and has 117historically auto-stashed uncommitted work in ways that strand changes. 118 119Local-only commands (rarely needed for OTA): 120 121```bash 122# Full build pipeline: binary → initramfs → kernel 123ac-os build 124 125# Build + flash USB 126ac-os flash 127 128# Build + upload OTA release (legacy local path — prefer `ac-os oven`) 129ac-os upload 130 131# Build + flash + upload 132ac-os flash+upload 133``` 134 135**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. 136 137### Notation 138 139- **compush** - commit & push 140 141## Architecture 142 143### Core Components 144 1451. **Boot System** (`system/public/aesthetic.computer/boot.mjs`) 146 - Entry point that loads the BIOS and initializes the runtime 147 - Manages service worker registration for module caching 148 - Handles WebSocket module loader for hot reloading 149 - Boot telemetry system logs to `/api/boot-log` 150 1512. **BIOS** (`system/public/aesthetic.computer/bios.mjs`) 152 - Main runtime coordinator 153 - Manages piece lifecycle (boot, act, paint, sim, etc.) 154 - Provides the API surface for pieces 155 - Handles routing and navigation 156 1573. **Disk System** (`system/public/aesthetic.computer/lib/disk.mjs`) 158 - Large module (~572KB) that provides the core API for pieces 159 - Graphics primitives, audio, input handling, UI components 160 - All pieces interact with AC through the Disk API 161 1624. **Module Loader** (`system/public/aesthetic.computer/module-loader.mjs`) 163 - WebSocket-based dynamic module loading 164 - Enables hot reloading during development 165 - Prefetches commonly used modules 166 167### Pieces 168 169Pieces are the fundamental unit of content in AC. Each piece is a single `.mjs` or `.lisp` file located in `system/public/aesthetic.computer/disks/`. 170 171#### Piece Structure (JavaScript) 172 173Pieces export lifecycle functions that receive an API object: 174 175```javascript 176// boot: runs once when the piece loads 177function boot({ wipe, screen, params, colon, api }) { 178 // Initialize state 179} 180 181// paint: runs every frame 182function paint({ wipe, ink, line, circle, screen }) { 183 // Render graphics 184} 185 186// act: handles user input and events 187function act({ event: e }) { 188 if (e.is("keyboard:down:space")) { 189 // Handle spacebar press 190 } 191} 192 193// sim: simulation/game logic that runs every frame 194function sim() { 195 // Update game state 196} 197 198export { boot, paint, act, sim }; 199``` 200 201Common lifecycle functions: 202- `boot` - Initialization, runs once 203- `paint` - Rendering, runs every frame 204- `act` - Event handling (input, network, etc.) 205- `sim` - Simulation/logic, runs every frame 206- `leave` - Cleanup when exiting the piece 207- `preview` - Static preview image generation 208 209#### Piece API Surface 210 211The API is provided through function parameters. Common APIs: 212- **Graphics**: `wipe`, `ink`, `line`, `box`, `circle`, `plot`, `paste`, etc. 213- **Text**: `write`, `type`, `paste`, `help` 214- **Input**: `event`, `pen`, `hand`, `gamepad` 215- **Audio**: `sound`, `speaker`, `microphone` 216- **UI**: `ui.Button`, `ui.TextInput`, `cursor` 217- **System**: `screen`, `params`, `colon`, `store`, `net`, `jump`, `send` 218 219### Servers and Services 220 2211. **System Server** (`system/` + `lith/`) 222 - Production: **lith** monolith — Express + Caddy on a DigitalOcean VPS (`lith.aesthetic.computer`). Deployed with `fish lith/deploy.fish`, which pulls from the tangled knot `git@knot.aesthetic.computer:aesthetic.computer/core`. Netlify is no longer the host. 223 - Dev: `npm run site` (port 8888) 224 - Backend function handlers live in `system/netlify/functions/` — the path is historical, but lith's Express adapts each file as a route, so keep adding new endpoints there. 225 - Handles piece serving, authentication, storage APIs 226 2272. **Session Server** (`session-server/`) 228 - Per-session backend using Jamsocket for ephemeral deployments 229 - WebSocket server using Geckos.io for real-time communication 230 - Manages chat, multiplayer state, and real-time features 231 - Uses Redis for state synchronization 232 2333. **Feed Server** (`dp1-feed/`) 234 - Cloudflare Worker for activity feeds 235 - Deployed separately to Cloudflare Workers 236 237### KidLisp 238 239KidLisp is a minimal Lisp dialect for generative art, with 118 built-in functions across 12 categories. See `kidlisp/README.md` for comprehensive documentation. 240 241**Key files:** 242- `system/public/aesthetic.computer/lib/kidlisp.mjs` - Main evaluator 243- `system/netlify/functions/store-kidlisp.mjs` - Storage API 244- `kidlisp/tools/` - Analysis and debugging tools 245 246**Running KidLisp tools:** 247```bash 248# Analyze piece structure (requires dev server running) 249./kidlisp/tools/source-tree.mjs $cow 250 251# Get source code 252./kidlisp/tools/get-source.mjs $piece-code 253``` 254 255### Data Storage 256 257- **MongoDB**: User data, handles, chat messages, moods 258- **Redis**: Session state, real-time coordination 259- **Firebase**: Authentication, cloud messaging 260- **Digital Ocean Spaces**: Asset storage (CDN) 261 262### Routing and URLs 263 264- Pieces are URL-addressable: `https://aesthetic.computer/piece-name` 265- Parameters: `piece-name:param1:param2` 266- User pieces: `@handle/piece-name` 267- QR code sharing: `share piece-name` 268 269## Development Workflow 270 271### Local Development in Codespaces 272 273When running in GitHub Codespaces, the server is accessible at: 274``` 275https://{CODESPACE_NAME}-8888.app.github.dev 276``` 277 278Get your Codespace name: `echo $CODESPACE_NAME` 279 280### Hot Reloading 281 282The module loader provides hot reloading during development: 283- Piece changes are reflected immediately when saved 284- WebSocket connection shown in boot canvas 285- Use `channel custom-name` for multi-device testing 286 287### Publishing 288 289```bash 290# In the AC prompt: 291publish # Publish current piece 292publish piece-name # Publish with custom name 293source # Download blank template 294source piece-name # Fork existing piece 295``` 296 297## Important Directories 298 299- `system/public/aesthetic.computer/disks/` - All pieces (.mjs and .lisp files) 300- `system/public/aesthetic.computer/lib/` - Shared libraries and utilities 301- `system/netlify/functions/` - Serverless backend functions 302- `session-server/` - Real-time session backend 303- `shared/` - Code shared between system and session servers 304- `kidlisp/` - KidLisp language documentation and tools 305- `spec/` - Jasmine tests for KidLisp 306- `ants/` - AestheticAnts automated maintenance system 307 308## Key Patterns 309 310### Event Handling in Pieces 311 312Events use a string-based pattern matching system: 313```javascript 314event.is("keyboard:down:a") // 'a' key pressed 315event.is("touch") // Any touch event 316event.is("lift") // Touch/click released 317event.is("draw") // Drag with pen down 318event.is("keyboard:down:arrowup") // Arrow key 319``` 320 321### State Management 322 323Pieces maintain state in module-level variables: 324```javascript 325let score = 0; 326let enemies = []; 327 328function boot() { 329 // Initialize state 330} 331 332function sim() { 333 // Update state 334 score += 1; 335} 336``` 337 338### API Requests from Pieces 339 340Use the `net` API for HTTP requests: 341```javascript 342function boot({ net }) { 343 net.pieces("@user/list").then((data) => { 344 // Handle response 345 }); 346} 347``` 348 349### Multiplayer Networking (Dual-Channel Pattern) 350 351Multiplayer pieces use both WebSocket (reliable) and UDP (low-latency) channels. See `squash.mjs` for the canonical implementation. 352 353```javascript 354function boot({ net: { socket, udp }, handle }) { 355 // UDP for high-frequency position sync (low latency, may drop packets) 356 udpChannel = udp((type, content) => { 357 if (type === "game:move") { 358 const d = typeof content === "string" ? JSON.parse(content) : content; 359 // Update remote player position 360 } 361 }); 362 363 // WebSocket for reliable game events (join/leave, scoring, round control) 364 server = socket((id, type, content) => { 365 if (type.startsWith("connected")) { /* joined session */ } 366 if (type === "left") { /* player disconnected */ } 367 if (type === "game:join") { /* another player joined */ } 368 if (type === "game:score") { /* reliable score event */ } 369 }); 370 371 // Send reliable event 372 server.send("game:join", { handle: handle() }); 373 // Send low-latency position update 374 udpChannel.send("game:move", { x, y, vx, vy }); 375} 376``` 377 378**Session server routing:** 379- UDP handlers: add `channel.on("game:move", ...)` in geckos section of `session-server/session.mjs` 380- WebSocket: position messages use `others()` (relay to all except sender), game events use `everyone()` (catch-all relay) 381- Chat invites: typing `'piece-name'` in chat creates a clickable link to join 382 383**Reference pieces:** `squash.mjs` (2D platformer), `1v1.mjs` (3D FPS), `udp.mjs` (minimal UDP test) 384 385### UI Components 386 387```javascript 388function boot({ ui: { Button, TextInput } }) { 389 const btn = new Button("Click me", { box: [10, 10, 100, 40] }); 390} 391 392function act({ event: e }) { 393 if (btn.trigger(e)) { 394 // Button was clicked 395 } 396} 397``` 398 399## Notes 400 401- The codebase uses `.mjs` extension for ES modules 402- Prefer `const` destructuring for API parameters to minimize imports 403- Graphics operations are immediate-mode (no retained scene graph) 404- All coordinates are in pixels 405- Default color depth is 8-bit RGB (0-255 per channel) 406- The `wipe` function clears the screen and should be called first in `paint` 407- When making changes, consult `ants/mindset-and-rules.md` for the ant operating philosophy