AI Development

Accelerate TelemetryOS application development using Claude Code with a project-specific CLAUDE.md configuration file.

AI Development

Accelerate TelemetryOS application development using Claude Code, Anthropic's AI coding assistant, with proper project configuration.

Overview

Claude Code is a terminal-based AI assistant that reads and writes files, executes commands, and generates code. When configured with a CLAUDE.md file in the project root, it understands TelemetryOS application architecture, SDK APIs, and code patterns.

Note that this guide applies equally to other AI agents like OpenAI's Codex or Cursor. Simply rename CLAUDE.md to AGENTS.md or whatever the agent requires.

What Claude Code Does:

  • Reads CLAUDE.md automatically on startup
  • Generates code following project patterns
  • Debugs issues using project context
  • Executes builds, tests, and deployments

Quick Setup

If you scaffolded your project with tos init, run the following command to copy a detailed CLAUDE.md into your project:

tos claude-code

This copies a project-specific CLAUDE.md with SDK-aware configuration. You can customize it further with the template below.

CLAUDE.md Template for TelemetryOS Applications

Copy this entire template into the project root as CLAUDE.md. This is a technical reference for code generation.

# TelemetryOS Application

**Application:** [App Name]
**Purpose:** [What this application does]

## Quick Start

```bash
npm install        # Install dependencies
npm run build      # Build and check for TypeScript errors
tos dev            # Open in Developer App (or: npm run dev)
```

**IMPORTANT:** Always run `npm run build` after making changes to check for TypeScript errors. Do not rely solely on the dev server.

## Architecture

TelemetryOS apps have up to three mount points. Apps that need a browser-accessible interface add a third: the Web mount point.

| Mount | Purpose | Runs On |
|-------|---------|---------|
| `/render` | Content displayed on devices | Physical device (TV, kiosk) |
| `/settings` | Configuration UI | Studio admin portal |
| `/web` (optional) | Browser-accessible interface | Any browser (phone, tablet, desktop) |

**Runtime Environment:**
- Chrome browser (platform-controlled version)
- Iframe sandbox execution
- Client-side only (no SSR, no Node.js APIs)
- Modern web APIs available (Fetch, WebSockets, WebGL, Canvas)

**Communication:**
- Instance store hooks sync Settings ↔ Render in real time
- Application store hooks are accessible from Settings, Render, and Web
- Web views typically use application and shared(namespace) store hooks

## Project Structure

```
project-root/
├── telemetry.config.json       # Platform configuration
├── package.json
├── tsconfig.json
├── vite.config.ts
├── public/
│   └── assets/                 # Static assets copied to dist/ during build
└── src/
    ├── index.tsx               # Entry point (configure SDK here)
    ├── App.tsx                 # Mount point routing
    ├── views/
    │   ├── Settings.tsx        # /settings mount point
    │   ├── Render.tsx          # /render mount point
    │   ├── Render.css          # Render styles
    │   ├── Web.tsx             # /web mount point (if using web)
    │   └── Web.css             # Web view styles (if using web)
    ├── hooks/
    │   └── store.ts            # Store state hooks
    ├── components/             # Reusable components
    └── types/                  # TypeScript interfaces
```

## Configuration Files

### telemetry.config.json (project root)
```json
{
  "name": "app-name",
  "version": "2026.1.0",
  "mountPoints": {
    "render": "/render",
    "settings": "/settings"
  },
  "devServer": {
    "runCommand": "vite --port $PORT",
    "url": "http://localhost:$PORT",
    "readyPattern": "VITE.*ready in"
  }
}
```

## SDK Initialization

```typescript
// src/index.tsx
import { configure } from '@telemetryos/sdk'
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App'

// Configure SDK ONCE before React renders
configure()

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <App />
  </StrictMode>
)
```

## Storage API

Import from `@telemetryos/sdk`.

**Four Scopes:**

| Scope | API | Visibility | Syncs to Cloud |
|-------|-----|-----------|----------------|
| **application** | `store().application` | All instances of this app in the account | Yes |
| **instance** | `store().instance` | This specific app instance only | Yes |
| **device** | `store().device` | This device only | No |
| **shared(namespace)** | `store().shared('ns')` | Any app using the same namespace | Yes |

**Method Signatures (same for all scopes):**
```typescript
store().instance.set<T>(key: string, value: T): Promise<boolean>
store().instance.get<T>(key: string): Promise<T | undefined>
store().instance.subscribe<T>(key: string, handler: (value: T | undefined) => void): Promise<boolean>
store().instance.unsubscribe<T>(key: string, handler?: (value: T | undefined) => void): Promise<boolean>
store().instance.delete(key: string): Promise<boolean>
```

**Scope Access by Mount Point:**

| Scope | Settings | Render | Web |
|-------|----------|--------|-----|
| application | Yes | Yes | Yes |
| instance | Yes | Yes | No |
| device | No | Yes | No |
| shared(namespace) | Yes | Yes | Yes |

### React Store Hooks (Recommended)

The SDK provides React hooks that handle subscription, loading state, and cleanup automatically. Import from `@telemetryos/sdk/react`.

```typescript
// hooks/store.ts
import { createUseInstanceStoreState } from '@telemetryos/sdk/react'

export const useCityState = createUseInstanceStoreState<string>('city', '')
export const useUnitsState = createUseInstanceStoreState<'imperial' | 'metric'>('units', 'imperial')
```

```typescript
// In any component (Settings or Render)
import { useCityState } from '../hooks/store'

const [isLoading, city, setCity] = useCityState()      // 0ms debounce (default)
const [isLoading, city, setCity] = useCityState(250)    // 250ms debounce for text inputs
```

**Available factory functions:**
- `createUseInstanceStoreState<T>(key, default)` — Settings ↔ Render sync (most common)
- `createUseApplicationStoreState<T>(key, default)` — Account-wide shared data
- `createUseDeviceStoreState<T>(key, default)` — Device-local only (Render only)
- `createUseSharedStoreState<T>(key, default, namespace)` — Cross-app communication
- `createUseDynamicNamespaceStoreState<T>(key, default)` — Shared with runtime namespace

All return: `[isLoading: boolean, value: T, setValue: Dispatch<SetStateAction<T>>]`

### Settings Components

The SDK provides styled Settings components. Import from `@telemetryos/sdk/react`.

```typescript
import {
  SettingsContainer,
  SettingsField,
  SettingsLabel,
  SettingsInputFrame,
  SettingsSelectFrame,
  SettingsSwitchFrame,
  SettingsSwitchLabel,
} from '@telemetryos/sdk/react'
```

## Complete File Implementations

### src/views/Settings.tsx
```typescript
import {
  SettingsContainer,
  SettingsField,
  SettingsLabel,
  SettingsInputFrame,
  SettingsSelectFrame,
} from '@telemetryos/sdk/react'
import { useCityState, useUnitsState } from '../hooks/store'

export default function Settings() {
  const [isLoadingCity, city, setCity] = useCityState(250)
  const [isLoadingUnits, units, setUnits] = useUnitsState()

  return (
    <SettingsContainer>
      <SettingsField>
        <SettingsLabel>City</SettingsLabel>
        <SettingsInputFrame>
          <input
            type="text"
            placeholder="Enter city name"
            disabled={isLoadingCity}
            value={city}
            onChange={(e) => setCity(e.target.value)}
          />
        </SettingsInputFrame>
      </SettingsField>

      <SettingsField>
        <SettingsLabel>Units</SettingsLabel>
        <SettingsSelectFrame>
          <select
            disabled={isLoadingUnits}
            value={units}
            onChange={(e) => setUnits(e.target.value as 'imperial' | 'metric')}
          >
            <option value="imperial">Fahrenheit</option>
            <option value="metric">Celsius</option>
          </select>
        </SettingsSelectFrame>
      </SettingsField>
    </SettingsContainer>
  )
}
```

### src/views/Render.tsx
```typescript
import { useEffect, useState } from 'react'
import { proxy, store } from '@telemetryos/sdk'
import { useCityState, useUnitsState } from '../hooks/store'

interface WeatherData {
  temperature: number
  conditions: string
}

export default function Render() {
  const [isLoadingCity, city] = useCityState()
  const [isLoadingUnits, units] = useUnitsState()
  const [weather, setWeather] = useState<WeatherData | null>(null)
  const [error, setError] = useState<string | null>(null)

  useEffect(() => {
    if (isLoadingCity || isLoadingUnits || !city) return

    const fetchWeather = async () => {
      try {
        const response = await proxy().fetch(
          `https://api.openweathermap.org/data/2.5/weather?q=${city}&units=${units}&appid=YOUR_API_KEY`
        )
        if (!response.ok) throw new Error(`API error: ${response.status}`)

        const data = await response.json()
        setWeather({ temperature: data.main.temp, conditions: data.weather[0].main })

        // Cache for offline use
        await store().device.set('cachedWeather', { data: { temperature: data.main.temp, conditions: data.weather[0].main }, timestamp: Date.now() })
      } catch (err) {
        setError(err instanceof Error ? err.message : 'Unknown error')

        // Try cached data
        const cached = await store().device.get<{ data: WeatherData }>('cachedWeather')
        if (cached) setWeather(cached.data)
      }
    }

    fetchWeather()
  }, [city, units, isLoadingCity, isLoadingUnits])

  if (isLoadingCity || isLoadingUnits) return <div>Loading...</div>
  if (!city) return <div>Configure a city in Settings</div>
  if (error && !weather) return <div>Error: {error}</div>

  return (
    <div>
      <h1>{city}</h1>
      <div>{weather?.temperature}°{units === 'metric' ? 'C' : 'F'}</div>
      <div>{weather?.conditions}</div>
      {error && <div style={{ color: 'orange' }}>Showing cached data</div>}
    </div>
  )
}
```

### src/hooks/store.ts
```typescript
import { createUseInstanceStoreState } from '@telemetryos/sdk/react'

export const useCityState = createUseInstanceStoreState<string>('city', '')
export const useUnitsState = createUseInstanceStoreState<'imperial' | 'metric'>('units', 'imperial')
```

## Proxy API

```typescript
import { proxy } from '@telemetryos/sdk'

// Use proxy for ALL external API calls to avoid CORS errors
const response = await proxy().fetch('https://api.example.com/data')
const json = await response.json()
```

- Same interface as standard `fetch()`
- Handles CORS server-side
- Returns standard `Response` object

## Hard Constraints

**These cause runtime errors:**

1. **Device storage in Settings** — Settings runs in Studio browser, not on devices. `store().device` is not available in Settings or Web mount points. Use `store().instance` or `store().application` instead.

2. **External API without proxy** — Direct `fetch()` to external domains fails with CORS error. Use `proxy().fetch()` for all external requests.

3. **Missing configure()** — SDK methods throw "SDK not configured" Error. Call `configure()` once in index.tsx before React renders.

4. **Instance/device storage in Web** — Web mount points have no instance or device context. Use `store().application` and `store().shared(namespace)` only.

5. **Timeout errors** — All SDK operations timeout after 30 seconds. Handle with try/catch.

## Development Commands

```bash
npm install        # Install dependencies
tos dev            # Open in Developer App
npm run dev        # Alternative: start Vite directly
npm run build      # Build for production (includes type check)
```

**Local testing:**
- Settings: visible in right sidebar of dev host
- Render: visible in resizable main pane of dev host
- Web: visible as a tab in dev host (if configured)

**Deployment:**
```bash
git add .
git commit -m "Description"
git push origin main
# GitHub integration auto-deploys
```

## Common Errors

| Error | Cause | Fix |
|-------|-------|-----|
| "SDK not configured" | `configure()` not called | Call `configure()` in index.tsx before React renders |
| "device storage not available" | `store().device` used in Settings/Web | Use `store().instance` or `store().application` |
| CORS error | Direct `fetch()` to external domain | Use `proxy().fetch()` |
| "Request timeout" | SDK operation exceeded 30s | Handle with try/catch |
| Render not updating | No subscription to store changes | Use store hooks or `store().instance.subscribe()` |

## Project-Specific Context

[Add project-specific details here:]

**Application Name:** [name from telemetry.config.json]

**External APIs:**
- [API name]: [endpoint]
  - Authentication: [method]
  - Rate limits: [limits]

**Custom Store Keys:**
- [key]: [type] — [purpose]

## Documentation

- [Storage Methods](../sdk-reference/storage-methods) — Complete storage scope reference
- [Store Hooks](../sdk-reference/react-hooks) — React hook patterns and factory functions
- [Mount Points](../core-concepts/mount-points) — Render, Settings, Web, Workers, Containers
- [Web Mount Point](../core-concepts/mount-points/web-mount-point) — Browser-accessible interfaces
- [Proxy Methods](../sdk-reference/proxy-methods) — External API integration with CORS handling
- [Configuration](../sdk-reference/configuration) — telemetry.config.json schema
- [Code Examples](./code-examples) — Complete working applications
- [LLMS.txt](https://docs.telemetryos.com/llms.txt) — Complete documentation index

What’s Next