Skip to main content

Monorepo Structure

Package Responsibilities

@grindxp/core

The domain engine. All business logic lives here. The other packages depend on it; it depends on nothing else in the monorepo. Key modules:

@grindxp/cli

Twenty-plus commands powered by Clack. Each command is a module in src/commands/. The entry point (src/index.ts) routes to the appropriate command based on process.argv. Integrations (Telegram polling, WhatsApp Web via Baileys) are also implemented here since they require long-running processes managed by the CLI.

@grindxp/tui

A 30fps React terminal UI built on OpenTUI. Uses the same OpenTUI React renderer as a browser React app uses the DOM; the JSX maps to terminal primitives. The TUI renders four screens (Dashboard, Quests, Skills, Chat) navigated by keyboard shortcuts. The Chat screen is itself a full application (ChatApp.tsx) that runs the agent streaming loop.

@grindxp/web

A TanStack Start SSR web app. Currently ships a polished landing page and an app shell with stubbed pages. App pages are under active development.

Data Flow

For AI interactions:
For automation:

Key Design Decisions

Bun-only

Grind uses Bun APIs directly (Bun.serve, import.meta.dir, bun:sqlite). It is not designed to run on Node.js. This provides fast startup, native SQLite, and native .env loading.

exactOptionalPropertyTypes: true

TypeScript is configured with exactOptionalPropertyTypes. This means you cannot assign undefined to an optional property. Use spread conditionals instead:

Local-first

The vault is a local SQLite file at ~/.grind/vault.db. Encryption is applied via libsql’s encryption extension. Turso cloud sync is optional and additive; the local file is always the primary.

AI is optional

Zero AI calls are made by the core quest/XP/skill system. The companion and agent are independent modules that the user opts into by configuring an API key.

Single domain engine

All four packages (core, cli, tui, web) share the same @grindxp/core domain engine. There is no separate backend API. The CLI and TUI open the SQLite vault directly. The web app will eventually call @grindxp/core server functions via TanStack Start’s server functions.