# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview Aesthetic 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. ## Agent Memory (Local-First) When @jeffrey is working, Claude hook events are written to a local encrypted memory store first. - **Hook**: `.claude/settings.json` → `UserPromptSubmit` → `node memory/hook.mjs` - **Git hook**: `.githooks/post-commit` → commit log + Codex import + remote flush - **Local store**: `~/.ac-agent-memory` (overridable via `AGENT_MEMORY_HOME`) - **Encryption**: AES-256-GCM (`AGENT_MEMORY_KEY` optional; local key file otherwise) - **Redaction**: metadata and summaries are redacted before indexing/sync - **CLI**: `node memory/cli.mjs` (`list`, `remember`, `checkpoint`, `doctor`, `profile`, `flush-remote`) - **Codex import**: `node memory/codex-sync.mjs` Remote writes are optional and disabled by default: - `AGENT_MEMORY_REMOTE_ENABLED=true` + `AGENT_MEMORY_REMOTE_URL=...` enables queued remote sync `remember` continuity is represented as lineage (`remembered_from`) instead of taking over a live mutable session. ## AestheticAnts & Score.md This 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. **Important:** Do not modify `ants/mindset-and-rules.md` unless you are the queen (@jeffrey). ## Development Commands ### Running the Development Environment The system consists of multiple servers that run concurrently: ```bash # Run all servers (site, session, edge, stripe) - primary dev command npm run aesthetic # or shorthand: npm run ac # Run individual servers: npm run site # Main dev server (port 8888) npm run server:session # Session backend (port 8889) npm run stripe # Stripe webhook listener ``` ### Testing ```bash # Run all tests npm test # KidLisp tests (with auto-reload on changes) npm run test:kidlisp # KidLisp tests (direct, no watch) npm run test:kidlisp:direct # Performance tests npm run test:perf npm run test:perf:chrome npm run test:perf:lighthouse ``` ### Creating New Pieces ```bash # Generate a new piece from the blank.mjs template npm run new piece-name "Description of the piece" ``` ### Working with Sessions ```bash # List active session backends npm run session:alive # View logs for a specific session npm run server:session:logs SESSION_ID # Reset/terminate a session npm run session:reset SESSION_ID # Deploy session server npm run session:publish ``` ### Assets Management ```bash # Sync assets down from Digital Ocean Spaces npm run assets:sync:down # Sync assets up to Digital Ocean Spaces npm run assets:sync:up ``` ### AC Native OS (fedac/native/) ```bash # Full build pipeline: binary → initramfs → kernel ac-os build # Build + flash USB ac-os flash # Build + upload OTA release (ALWAYS rebuilds first) ac-os upload # Build + flash + upload ac-os flash+upload ``` **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. ### Notation - **compush** - commit & push ## Architecture ### Core Components 1. **Boot System** (`system/public/aesthetic.computer/boot.mjs`) - Entry point that loads the BIOS and initializes the runtime - Manages service worker registration for module caching - Handles WebSocket module loader for hot reloading - Boot telemetry system logs to `/api/boot-log` 2. **BIOS** (`system/public/aesthetic.computer/bios.mjs`) - Main runtime coordinator - Manages piece lifecycle (boot, act, paint, sim, etc.) - Provides the API surface for pieces - Handles routing and navigation 3. **Disk System** (`system/public/aesthetic.computer/lib/disk.mjs`) - Large module (~572KB) that provides the core API for pieces - Graphics primitives, audio, input handling, UI components - All pieces interact with AC through the Disk API 4. **Module Loader** (`system/public/aesthetic.computer/module-loader.mjs`) - WebSocket-based dynamic module loading - Enables hot reloading during development - Prefetches commonly used modules ### Pieces Pieces are the fundamental unit of content in AC. Each piece is a single `.mjs` or `.lisp` file located in `system/public/aesthetic.computer/disks/`. #### Piece Structure (JavaScript) Pieces export lifecycle functions that receive an API object: ```javascript // boot: runs once when the piece loads function boot({ wipe, screen, params, colon, api }) { // Initialize state } // paint: runs every frame function paint({ wipe, ink, line, circle, screen }) { // Render graphics } // act: handles user input and events function act({ event: e }) { if (e.is("keyboard:down:space")) { // Handle spacebar press } } // sim: simulation/game logic that runs every frame function sim() { // Update game state } export { boot, paint, act, sim }; ``` Common lifecycle functions: - `boot` - Initialization, runs once - `paint` - Rendering, runs every frame - `act` - Event handling (input, network, etc.) - `sim` - Simulation/logic, runs every frame - `leave` - Cleanup when exiting the piece - `preview` - Static preview image generation #### Piece API Surface The API is provided through function parameters. Common APIs: - **Graphics**: `wipe`, `ink`, `line`, `box`, `circle`, `plot`, `paste`, etc. - **Text**: `write`, `type`, `paste`, `help` - **Input**: `event`, `pen`, `hand`, `gamepad` - **Audio**: `sound`, `speaker`, `microphone` - **UI**: `ui.Button`, `ui.TextInput`, `cursor` - **System**: `screen`, `params`, `colon`, `store`, `net`, `jump`, `send` ### Servers and Services 1. **System Server** (`system/` + `lith/`) - Production: lith monolith (Express + Caddy on DigitalOcean VPS) - Dev: `npm run site` (port 8888) - Backend functions in `system/netlify/functions/` - Handles piece serving, authentication, storage APIs 2. **Session Server** (`session-server/`) - Per-session backend using Jamsocket for ephemeral deployments - WebSocket server using Geckos.io for real-time communication - Manages chat, multiplayer state, and real-time features - Uses Redis for state synchronization 3. **Feed Server** (`dp1-feed/`) - Cloudflare Worker for activity feeds - Deployed separately to Cloudflare Workers ### KidLisp KidLisp is a minimal Lisp dialect for generative art, with 118 built-in functions across 12 categories. See `kidlisp/README.md` for comprehensive documentation. **Key files:** - `system/public/aesthetic.computer/lib/kidlisp.mjs` - Main evaluator - `system/netlify/functions/store-kidlisp.mjs` - Storage API - `kidlisp/tools/` - Analysis and debugging tools **Running KidLisp tools:** ```bash # Analyze piece structure (requires dev server running) ./kidlisp/tools/source-tree.mjs $cow # Get source code ./kidlisp/tools/get-source.mjs $piece-code ``` ### Data Storage - **MongoDB**: User data, handles, chat messages, moods - **Redis**: Session state, real-time coordination - **Firebase**: Authentication, cloud messaging - **Digital Ocean Spaces**: Asset storage (CDN) ### Routing and URLs - Pieces are URL-addressable: `https://aesthetic.computer/piece-name` - Parameters: `piece-name:param1:param2` - User pieces: `@handle/piece-name` - QR code sharing: `share piece-name` ## Development Workflow ### Local Development in Codespaces When running in GitHub Codespaces, the server is accessible at: ``` https://{CODESPACE_NAME}-8888.app.github.dev ``` Get your Codespace name: `echo $CODESPACE_NAME` ### Hot Reloading The module loader provides hot reloading during development: - Piece changes are reflected immediately when saved - WebSocket connection shown in boot canvas - Use `channel custom-name` for multi-device testing ### Publishing ```bash # In the AC prompt: publish # Publish current piece publish piece-name # Publish with custom name source # Download blank template source piece-name # Fork existing piece ``` ## Important Directories - `system/public/aesthetic.computer/disks/` - All pieces (.mjs and .lisp files) - `system/public/aesthetic.computer/lib/` - Shared libraries and utilities - `system/netlify/functions/` - Serverless backend functions - `session-server/` - Real-time session backend - `shared/` - Code shared between system and session servers - `kidlisp/` - KidLisp language documentation and tools - `spec/` - Jasmine tests for KidLisp - `ants/` - AestheticAnts automated maintenance system ## Key Patterns ### Event Handling in Pieces Events use a string-based pattern matching system: ```javascript event.is("keyboard:down:a") // 'a' key pressed event.is("touch") // Any touch event event.is("lift") // Touch/click released event.is("draw") // Drag with pen down event.is("keyboard:down:arrowup") // Arrow key ``` ### State Management Pieces maintain state in module-level variables: ```javascript let score = 0; let enemies = []; function boot() { // Initialize state } function sim() { // Update state score += 1; } ``` ### API Requests from Pieces Use the `net` API for HTTP requests: ```javascript function boot({ net }) { net.pieces("@user/list").then((data) => { // Handle response }); } ``` ### Multiplayer Networking (Dual-Channel Pattern) Multiplayer pieces use both WebSocket (reliable) and UDP (low-latency) channels. See `squash.mjs` for the canonical implementation. ```javascript function boot({ net: { socket, udp }, handle }) { // UDP for high-frequency position sync (low latency, may drop packets) udpChannel = udp((type, content) => { if (type === "game:move") { const d = typeof content === "string" ? JSON.parse(content) : content; // Update remote player position } }); // WebSocket for reliable game events (join/leave, scoring, round control) server = socket((id, type, content) => { if (type.startsWith("connected")) { /* joined session */ } if (type === "left") { /* player disconnected */ } if (type === "game:join") { /* another player joined */ } if (type === "game:score") { /* reliable score event */ } }); // Send reliable event server.send("game:join", { handle: handle() }); // Send low-latency position update udpChannel.send("game:move", { x, y, vx, vy }); } ``` **Session server routing:** - UDP handlers: add `channel.on("game:move", ...)` in geckos section of `session-server/session.mjs` - WebSocket: position messages use `others()` (relay to all except sender), game events use `everyone()` (catch-all relay) - Chat invites: typing `'piece-name'` in chat creates a clickable link to join **Reference pieces:** `squash.mjs` (2D platformer), `1v1.mjs` (3D FPS), `udp.mjs` (minimal UDP test) ### UI Components ```javascript function boot({ ui: { Button, TextInput } }) { const btn = new Button("Click me", { box: [10, 10, 100, 40] }); } function act({ event: e }) { if (btn.trigger(e)) { // Button was clicked } } ``` ## Notes - The codebase uses `.mjs` extension for ES modules - Prefer `const` destructuring for API parameters to minimize imports - Graphics operations are immediate-mode (no retained scene graph) - All coordinates are in pixels - Default color depth is 8-bit RGB (0-255 per channel) - The `wipe` function clears the screen and should be called first in `paint` - When making changes, consult `ants/mindset-and-rules.md` for the ant operating philosophy