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:
| Export | Contents |
|---|---|
. (default) | Re-exports all public API |
./schema | Zod schemas for all domain entities |
./vault | Database client, repositories, migrations |
./quest | Quest engine and state machine |
./xp | XP calculator and level math |
./skill | Skill tree builder and XP routing |
./forge | Forge engine and runtime daemon |
./gateway | HTTP server for webhook ingestion |
./proof | Proof engine and multiplier calculation |
./companion | Trust ladder, AI provider registry, prompts |
./agent | Streaming agent runtime and tool definitions |
@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
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.