toes/CLAUDE.md
2026-01-30 15:26:58 -08:00

4.2 KiB

Toes - Claude Code Guide

What It Is

Personal web appliance that auto-discovers and runs multiple web apps on your home network.

"Plug it in, turn it on, and forget about the cloud."

How It Works

  1. Host server scans /apps directory for valid apps
  2. Valid app = has package.json with scripts.toes entry
  3. Each app spawned as child process with unique port (3001+)
  4. Dashboard UI shows all apps with current status, logs, and links

Key Files

Server (src/server/)

  • apps.ts - The heart: app discovery, process management, health checks, auto-restart
  • api/apps.ts - REST API for app lifecycle (start/stop/restart, logs, icons, rename)
  • api/sync.ts - File sync protocol (manifest, push/pull, watch)
  • index.tsx - Entry point (minimal, initializes Hype)
  • shell.tsx - HTML shell for web UI

Client (src/client/)

  • components/ - Dashboard, Sidebar, AppDetail, Nav
  • modals/ - NewApp, RenameApp, DeleteApp dialogs
  • styles/ - Forge CSS-in-JS (themes, buttons, forms, layout)
  • state.ts - Client state management
  • api.ts - API client

CLI (src/cli/)

  • commands/manage.ts - list, start, stop, restart, info, new, rename, delete, open
  • commands/sync.ts - push, pull, sync
  • commands/logs.ts - log viewing with tail support

Shared (src/shared/)

  • Code shared between frontend (browser) and backend (server)
  • types.ts - App, AppState, Manifest interfaces
  • IMPORTANT: Cannot use filesystem or Node APIs (runs in browser)

Lib (src/lib/)

  • Code shared between CLI and server (server-side only)
  • templates.ts - Template generation for new apps
  • Can use filesystem and Node APIs (never runs in browser)

Other

  • apps/*/package.json - Must have "toes": "bun run --watch index.tsx" script
  • TODO.txt - Task list

Tech Stack

Running

bun run --hot src/server/index.tsx  # Dev mode with hot reload

App Structure

// apps/example/index.tsx
import { Hype } from "@because/hype"
const app = new Hype()
app.get("/", (c) => c.html(<h1>Content</h1>))
export default app.defaults

Conventions

  • Apps get PORT env var from host
  • Each app is isolated process with own dependencies
  • No path-based routing - apps run on separate ports
  • DATA_DIR env controls where apps are discovered
  • Path aliases: $ → server, @ → shared, % → lib

Current State

Infrastructure (Complete)

  • App discovery, spawn, watch, auto-restart with exponential backoff
  • Health checks every 30s (3 failures trigger restart)
  • Port pool (3001-3100), sticky allocation per app
  • SSE streams for real-time app state and log updates
  • File sync protocol with hash-based manifests

CLI

  • Full management: toes list|start|stop|restart|info|new|rename|delete|open
  • File sync: toes push|pull|sync
  • Logs: toes logs [-f] <app>

Check TODO.txt for planned features

Coding Guidelines

TS files should be organized in the following way:

  • imports
  • re-exports
  • const/lets
  • enums
  • interfaces
  • types
  • classes
  • functions
  • module init (top level function calls)

In each section, put the exports first, in alphabetical order.

Then, after the exports (if there were any), put everything else, also in alphabetical order.

For single-line functions, use const fn = () => {} and put them in the "functions" section of the file.

All other functions use the function blah(){} format.

Example:

import { code } from "coders"
import { something } from "somewhere"

export type { SomeType }

const RETRY_TIMES = 5
const WIDTH = 480

enum State {
  Stopped,
  Starting,
  Running,
}

interface Config {
  name: string
  port: number
}

type Handler = (req: Request) => Response

class App {
  config: Config

  constructor(config: Config) {
    this.config = config
  }
}

const isApp = (name: string) =>
  apps.has(name)

function createApp(name: string): App {
  const app = new App({ name, port: 3000 })
  apps.set(name, app)
  return app
}

function start(app: App): void {
  console.log(`Starting ${app.config.name}`)
}