This commit rebrands the project from "Happy" to "HAPI" throughout the codebase, including documentation, comments, logs, and tool references. It also adds comprehensive README files for the server and web components, clarifies the monorepo structure in AGENTS.md and root README.md, and removes the outdated roadmap.md file. Changes include: - Rebrand references from Happy to HAPI in CLI, server, and web components - MCP tool names updated from mcp__happy__ to mcp__hapi__ - Process/service names updated consistently - New server/README.md with deployment and configuration guide - New web/README.md with stack and development instructions - Updated root README.md with quickstart guide - Updated AGENTS.md with cleaner structure documentation - Removed cli/roadmap.md (now superseded by documentation)
8.1 KiB
HAPI CLI Codebase Overview
Project Overview
HAPI CLI (hapi) is a command-line tool that wraps Claude Code to enable remote control and session sharing via hapi-server (Telegram Bot + Mini App). It's part of a two-component system:
- hapi (this project) - CLI wrapper for Claude Code
- hapi-server - Public server (Socket.IO + REST + SQLite) + Telegram Mini App
Code Style Preferences
TypeScript Conventions
- Strict typing: No untyped code ("I despise untyped code")
- Clean function signatures: Explicit parameter and return types
- As little as possible classes
- Comprehensive JSDoc comments: Each file includes header comments explaining responsibilities.
- Import style: Uses
@/alias for src imports, e.g.,import { logger } from '@/ui/logger' - File extensions: Uses
.tsfor TypeScript files - Export style: Named exports preferred, with occasional default exports for main functions
DO NOT
- Create stupid small functions / getters / setters
- Excessive use of
ifstatements - especially if you can avoid control flow changes with a better design - NEVER import modules mid-code - ALL imports must be at the top of the file
Error Handling
- Graceful error handling with proper error messages
- Use of
try-catchblocks with specific error logging - Abort controllers for cancellable operations
- Careful handling of process lifecycle and cleanup
Testing
- Unit tests using Vitest
- No mocking - tests make real API calls
- Test files colocated with source files (
.test.ts) - Descriptive test names and proper async handling
Logging
- All debugging through file logs to avoid disturbing Claude sessions
- Console output only for user-facing messages
- Special handling for large JSON objects with truncation
Architecture & Key Components
1. API Module (/src/api/)
Handles bot communication (direct connect; no end-to-end encryption).
api.ts: Main API client class for session managementapiSession.ts: WebSocket-based real-time session client with RPC supportauth.ts: LoadsCLI_API_TOKENfrom envencryption.ts: Base64 helpers (no encryption)types.ts: Zod schemas for type-safe API communication
Key Features:
- Socket.IO for real-time messaging
- Optimistic concurrency control for state updates
- RPC handler registration for remote procedure calls
2. Claude Integration (/src/claude/)
Core Claude Code integration layer.
-
loop.ts: Main control loop managing interactive/remote modes -
types.ts: Claude message type definitions with parsers -
claudeSdk.ts: Direct SDK integration using@anthropic-ai/claude-code -
interactive.ts: LIKELY WILL BE DEPRECATED in favor of running through SDK PTY-based interactive Claude sessions -
watcher.ts: File system watcher for Claude session files (for interactive mode snooping) -
mcp/startPermissionServer.ts: MCP (Model Context Protocol) permission server
Key Features:
- Dual mode operation: interactive (terminal) and remote (mobile control)
- Session persistence and resumption
- Real-time message streaming
- Permission intercepting via MCP [Permission checking not implemented yet]
3. UI Module (/src/ui/)
User interface components.
logger.ts: Centralized logging system with file outputqrcode.ts: QR code generation for mobile authenticationstart.ts: Main application startup and orchestration
Key Features:
- Clean console UI with chalk styling
- QR code display for easy mobile connection
- Graceful mode switching between interactive and remote
4. Core Files
index.ts: CLI entry point with argument parsingpersistence.ts: Local storage for settings and keysutils/time.ts: Exponential backoff utilities
Data Flow
-
Authentication:
- Use
CLI_API_TOKENto authenticate tohapi-server(REST + Socket.IO)
- Use
-
Session Creation:
- Create/load session via
POST /cli/sessions→ Establish Socket.IO/cliconnection
- Create/load session via
-
Message Flow:
- Local mode: terminal/SDK → hapi CLI → hapi-server → Telegram Mini App
-
Permission Handling:
- Claude requests permission → hapi CLI exposes RPC handlers → Mini App calls REST → hapi-server relays RPC to hapi CLI
Key Design Decisions
- File-based logging: Prevents interference with Claude's terminal UI
- Dual Claude integration: Process spawning for interactive, SDK for remote
- No E2E encryption: Use HTTPS/TLS for
hapi-serverdeployments - Session persistence: Allows resuming sessions across restarts
- Optimistic concurrency: Handles distributed state updates gracefully
Security Considerations
CLI_API_TOKENis a shared secret; treat it like a password.- No end-to-end encryption: use HTTPS/TLS for
hapi-serverdeployments. - Session isolation through unique session IDs.
Dependencies
- Core: Node.js, TypeScript
- Claude:
@anthropic-ai/claude-codeSDK - Networking: Socket.IO client, Axios
- Terminal: node-pty, chalk, qrcode-terminal
- Validation: Zod
- Testing: Vitest
Running the Daemon
Starting the Daemon
# From the hapi CLI directory:
hapi daemon start
# With custom bot URL (for local development):
HAPI_BOT_URL=http://localhost:3006 CLI_API_TOKEN=your_token hapi daemon start
# Stop the daemon:
hapi daemon stop
# Check daemon status:
hapi daemon status
Daemon Logs
- Daemon logs are stored in
~/.hapi-dev/logs/(or$HAPI_HOME_DIR/logs/) - Named with format:
YYYY-MM-DD-HH-MM-SS-daemon.log
Session Forking claude and sdk behavior
Commands Run
Initial Session
claude --print --output-format stream-json --verbose 'list files in this directory'
- Original Session ID:
aada10c6-9299-4c45-abc4-91db9c0f935d - Created file:
~/.claude/projects/.../aada10c6-9299-4c45-abc4-91db9c0f935d.jsonl
Resume with --resume flag
claude --print --output-format stream-json --verbose --resume aada10c6-9299-4c45-abc4-91db9c0f935d 'what file did we just see?'
- New Session ID:
1433467f-ff14-4292-b5b2-2aac77a808f0 - Created file:
~/.claude/projects/.../1433467f-ff14-4292-b5b2-2aac77a808f0.jsonl
Key Findings for --resume
1. Session File Behavior
- Creates a NEW session file with NEW session ID
- Original session file remains unchanged
- Two separate files exist after resumption
2. History Preservation
- The new session file contains the COMPLETE history from the original session
- History is prefixed at the beginning of the new file
- Includes a summary line at the very top
3. Session ID Rewriting
- CRITICAL FINDING: All historical messages have their sessionId field UPDATED to the new session ID
- Original messages from session
aada10c6-9299-4c45-abc4-91db9c0f935dnow showsessionId: "1433467f-ff14-4292-b5b2-2aac77a808f0" - This creates a unified session history under the new ID
4. Message Structure in New File
Line 1: Summary of previous conversation
Lines 2-6: Complete history from original session (with updated session IDs)
Lines 7-8: New messages from current interaction
5. Context Preservation
- Claude successfully maintains full context
- Can answer questions about previous interactions
- Behaves as if it's a continuous conversation
Technical Details
Original Session File Structure
- Contains only messages from the original session
- All messages have original session ID
- Remains untouched after resume
New Session File Structure After Resume
{"type":"summary","summary":"Listing directory files in current location","leafUuid":"..."}
{"parentUuid":null,"sessionId":"1433467f-ff14-4292-b5b2-2aac77a808f0","message":{"role":"user","content":[{"type":"text","text":"list files in this directory"}]},...}
// ... all historical messages with NEW session ID ...
{"parentUuid":"...","sessionId":"1433467f-ff14-4292-b5b2-2aac77a808f0","message":{"role":"user","content":"what file did we just see?"},...}
Implications for handy-cli
When using --resume:
- Must handle new session ID in responses
- Original session remains as historical record
- All context preserved but under new session identity
- Session ID in stream-json output will be the new one, not the resumed one