clarc/CLAUDE.md

4.4 KiB

claude-remote

Self-hosted remote control for Claude Code. Wrap CLI in PTY, stream output to server, approve prompts from phone.

Project

Pure Bun stack: no frameworks, minimal dependencies. Target ~1000-1500 lines total.

Core tech:

  • bun-pty for PTY wrapper (FFI to Rust, pre-built binaries)
  • bun:sqlite for persistence
  • Bun.serve() for HTTP + WebSocket + SSE
  • plain text output (no xterm.js) for mobile-friendly display

References

Inspiration and patterns to steal from:

Source Path What to look at
crabigator ../crabigator/ Architecture, WebSocket protocol, cloud client, session management
bun-pty ../bun-pty/ PTY API, spawn patterns, event handling

Key files in crabigator:

  • src/cloud/client.rs - WebSocket connection, reconnection logic
  • src/cloud/events.rs - event types for streaming
  • src/capture.rs - output capture patterns
  • workers/crabigator-api/src/session-do.ts - session state management

Key files in bun-pty:

  • src/index.ts - spawn API, IPty interface
  • src/terminal.ts - FFI bindings (for understanding internals)

Contribution policy

If bun-pty needs changes, fork and contribute upstream. Don't vendor or patch locally.

Workflow

After making changes:

just check  # lint + typecheck + test

Commit after each logical change. One change = one commit. Keep commits atomic.

Code review

Run code review after completing a phase or significant chunk of work:

  • Use superpowers:code-reviewer agent
  • Provide: what was implemented, git range (base SHA to head SHA), requirements
  • Fix critical/important issues before moving on
  • Minor issues can be noted and deferred

Review loop: implement → just check → code review → fix issues → re-review if needed → pause for user.

Bun defaults

  • Use bun <file> instead of node <file> or ts-node <file>
  • Use bun test instead of jest or vitest
  • Use bun build <file.html|file.ts|file.css> instead of webpack or esbuild
  • Use bun install instead of npm install or yarn install or pnpm install
  • Use bun run <script> instead of npm run <script> or yarn run <script> or pnpm run <script>
  • Use bunx <package> <command> instead of npx <package> <command>
  • Bun automatically loads .env, so don't use dotenv.

APIs

  • Bun.serve() supports WebSockets, HTTPS, and routes. Don't use express.
  • bun:sqlite for SQLite. Don't use better-sqlite3.
  • Bun.redis for Redis. Don't use ioredis.
  • Bun.sql for Postgres. Don't use pg or postgres.js.
  • WebSocket is built-in. Don't use ws.
  • Prefer Bun.file over node:fs's readFile/writeFile
  • Bun.$ls instead of execa.

Testing

Use bun test to run tests.

import { test, expect } from "bun:test";

test("hello world", () => {
  expect(1).toBe(1);
});

Frontend

Use HTML imports with Bun.serve(). Don't use vite. HTML imports fully support React, CSS, Tailwind.

Server:

import index from "./index.html"

Bun.serve({
  routes: {
    "/": index,
    "/api/users/:id": {
      GET: (req) => {
        return new Response(JSON.stringify({ id: req.params.id }));
      },
    },
  },
  // optional websocket support
  websocket: {
    open: (ws) => {
      ws.send("Hello, world!");
    },
    message: (ws, message) => {
      ws.send(message);
    },
    close: (ws) => {
      // handle close
    }
  },
  development: {
    hmr: true,
    console: true,
  }
})

HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. <link> tags can point to stylesheets and Bun's CSS bundler will bundle.

<html>
  <body>
    <h1>Hello, world!</h1>
    <script type="module" src="./frontend.tsx"></script>
  </body>
</html>

With the following frontend.tsx:

import React from "react";
import { createRoot } from "react-dom/client";

// import .css files directly and it works
import './index.css';

const root = createRoot(document.body);

export default function Frontend() {
  return <h1>Hello, world!</h1>;
}

root.render(<Frontend />);

Then, run index.ts

bun --hot ./index.ts

For more information, read the Bun API docs in node_modules/bun-types/docs/**.mdx.