Introduction
Monorepo allows managing all packages in a repository — shared types, build order, and atomic changes. This article guides you through setup from scratch.
1. Initialize Monorepo
1.1 Original Package.json
{
"name": "xclaw",
"private": true,
"type": "module",
"workspaces": [
"packages/*",
"packages/channels/*"
],
"scripts": {
"build": "npm run build --workspaces --if-present",
"dev": "concurrently \"npm run dev:server\" \"npm run dev:web\"",
"dev:server": "npm run dev -w @xclaw-ai/server",
"dev:web": "npm run dev -w @xclaw-ai/web",
"test": "vitest",
"lint": "eslint packages/*/src"
},
"engines": {
"node": ">=20.0.0",
"npm": ">=10.0.0"
}
}
1.2 Create packages structure
mkdir -p packages/{shared,core,db,gateway,server,integrations,domains,skills,ml,web}/src
2. Shared Package — Foundation Types
2.1 packages/shared/package.json
{
"name": "@xclaw-ai/shared",
"version": "1.0.0",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc -b",
"dev": "tsc -b --watch"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
}
}
2.2 Shared Types
// packages/shared/src/types/agent.ts
export interface AgentConfig {
id: string;
name: string;
persona: string;
systemPrompt?: string;
llm: LLMConfig;
maxToolIterations: number;
}
export interface LLMConfig {
provider: string;
model?: string;
apiKey?: string;
baseUrl?: string;
temperature?: number;
maxTokens?: number;
}
export interface LLMMessage {
role: 'system' | 'user' | 'assistant' | 'tool';
content: string;
images?: string[];
toolCalls?: ToolCall[];
toolCallId?: string;
}
export interface LLMResponse {
content: string;
toolCalls?: ToolCall[];
usage?: { promptTokens: number; completionTokens: number; totalTokens: number };
finishReason?: 'stop' | 'tool_calls' | 'length';
}
// packages/shared/src/types/tools.ts
export interface ToolDefinition {
name: string;
description: string;
parameters: {
type: 'object';
properties: Record<string, {
type: string;
description: string;
enum?: string[];
}>;
required?: string[];
};
sandbox?: { required: boolean };
}
export interface ToolCall {
id: string;
name: string;
arguments: Record<string, unknown>;
}
export interface ToolResult {
toolCallId: string;
success: boolean;
result: unknown;
error?: string;
duration: number;
}
// packages/shared/src/types/streaming.ts
export type StreamEvent =
| { type: 'text-delta'; delta: string }
| { type: 'tool-call-start'; toolCallId: string; toolName: string }
| { type: 'tool-call-args'; toolCallId: string; args: string }
| { type: 'tool-call-end'; toolCallId: string }
| { type: 'tool-result'; toolCallId: string; result: ToolResult }
| { type: 'finish'; usage?: LLMResponse['usage'] }
| { type: 'error'; error: string };
3. TypeScript Project References
3.1 Root tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true
}
}
3.2 Package-level tsconfig
// packages/core/tsconfig.json
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"composite": true
},
"include": ["src"],
"references": [
{ "path": "../shared" },
{ "path": "../db" }
]
}
3.3 Build Order
shared (0 deps)
↓
db (depends on shared)
↓
core (depends on shared, db)
↓
integrations (depends on shared, core)
domains (depends on shared, core)
ml (depends on shared, core)
↓
skills (depends on shared, core)
skill-hub (depends on shared, core, skills)
↓
gateway (depends on shared, db, core, integrations, domains, skills)
↓
server (depends on all)
4. npm Workspaces Commands
# Install dependencies cho tất cả packages
npm install
# Build tất cả packages theo dependency order
npm run build
# Chạy script trong package cụ thể
npm run dev -w @xclaw-ai/server
# Thêm dependency vào package
npm install hono -w @xclaw-ai/gateway
# Thêm internal dependency
# (npm workspaces tự link qua symlinks)
npm install @xclaw-ai/shared -w @xclaw-ai/core
5. ESM Configuration
xClaw uses ESM (ECMAScript Modules) entirely:
// ✅ ESM imports — phải có .js extension
import { Agent } from './agent/agent.js';
import { LLMRouter } from '../llm/llm-router.js';
import type { AgentConfig } from '@xclaw-ai/shared';
// ❌ CommonJS — KHÔNG dùng
// const { Agent } = require('./agent');
Important note: Import path must be present .js extension even if the source file is .ts. TypeScript compiler will resolve correctly.
6. Summary
You have learned:
- Setup npm workspaces monorepo
- Create shared types package
- TypeScript project references for build order
- ESM module configuration
Next article: Designing Dual-Database with PostgreSQL (Drizzle ORM) + MongoDB.