Skip to main content
Turborepo + pnpm workspaces. The license boundary follows the package boundary.
3ngram/
├── apps/
│   ├── server/            # express: REST API + MCP — one deployable
│   ├── worker/            # BullMQ consumers: consolidation, embeddings backfill, surfacing
│   └── cli/               # @3ngram/cli — thin SDK client; the coding-agent default transport
├── packages/
│   ├── schema/            # Zod v4: THE single source of truth (enums, constraints, tool I/O, API DTOs)
│   ├── db/                # Drizzle schema (generated CHECKs from packages/schema), withTenant(), migrations
│   ├── core/              # Domain services: memory write path, retrieval, commitments FSM, consolidation
│   ├── llm/               # Gateway client + operation-keyed routing registry + usage tracking
│   ├── sdk/               # Published TS client (@3ngram/sdk) — thin over REST
│   └── config/            # Shared tsconfig, Biome, env parsing (zod-validated)
├── eval/                  # Golden set + LongMemEval/MemoryAgentBench harnesses
├── cmd/3ngram-hook/       # Static Go binary: Claude Code briefing/precheck hook (read-side context feed)
├── docker-compose.yml     # Local dev / self-host: postgres+pgvector, redis
├── scripts/
│   └── provision-roles.sql  # The ONLY place GRANTs/roles live
├── docs/                  # Mintlify docs site (this tree)
├── LICENSING.md           # Apache-2.0 license statement (platform billing = separate private repo)
└── .github/workflows/     # ci.yml, eval-nightly.yml, release-3ngram-hook.yml

Layering rules (enforced, not aspirational)

apps/* → packages/core → packages/db → postgres

        packages/schema (imported by everyone, imports nothing)
  • Transport layers (REST routes, MCP tools) contain zero business logic — they parse (schema), call packages/core, format. Oversized router files are the anti-pattern to avoid.
  • packages/core never imports express/MCP/Next types.
  • 50-line function / 500-line file budget, enforced by lint warning + review. Oversized files in core paths are exactly the failure mode this budget prevents.
  • Dependency direction checked in CI (dependency-cruiser or Turborepo boundaries).

Server topology note

API and MCP ship as one express app / one service in v1 (a shared service layer makes a separate process split unnecessary at this scale). Split into separate deployables only when load profiles demonstrably diverge — the code structure (thin adapters over core) keeps that a deploy-config change, not a refactor.