Monorepo Structure
Organization
runeya/
├── apps/
│ ├── agent/ # Process executor
│ ├── cli/ # runeya binary (entry point)
│ ├── docs/ # This documentation site
│ ├── server/ # Local server
│ └── web/ # Vue 3 interface
├── packages/
│ ├── shared/ # Zod schemas, shared types
│ ├── mcp-server/ # MCP Server (Model Context Protocol)
│ ├── runner-native/ # Native runner — execution via execa
│ └── runner-docker/ # Docker runner — execution via Docker
├── scripts/
│ ├── check-monorepo/ # Monorepo convention lint
│ ├── generate-ci/ # GitHub Actions generation
│ ├── tsup-config/ # Shared tsup config
│ └── retrigger-all-build/ # Cascade rebuild trigger
├── docs/ # Markdown source documentation
├── turbo.json # Turborepo pipeline
└── package.json # Root (workspaces)Naming Convention
Required format: @runeya/{folder}-{name}
| Folder | Package name |
|---|---|
apps/agent | @runeya/apps-agent |
apps/cli | @runeya/apps-cli |
apps/server | @runeya/apps-server |
apps/web | @runeya/apps-web |
packages/shared | @runeya/packages-shared |
packages/mcp-server | @runeya/packages-mcp-server |
packages/runner-native | @runeya/packages-runner-native |
packages/runner-docker | @runeya/packages-runner-docker |
scripts/check-monorepo | @runeya/scripts-check-monorepo |
scripts/generate-ci | @runeya/scripts-generate-ci |
scripts/tsup-config | @runeya/scripts-tsup-config |
scripts/retrigger-all-build | @runeya/scripts-retrigger-all-build |
Validated by yarn check-monorepo — blocked in CI.
Module System
- All packages are ESM (
"type": "module"in eachpackage.json) .jsextensions are required in all relative imports (even for.tsfiles)- Node.js 22+ required
typescript
// ✅ Correct
import { serviceStore } from './service-store.js'
// ❌ Incorrect
import { serviceStore } from './service-store'Building with tsup
Each package uses tsup with the shared config:
typescript
// apps/server/tsup.config.ts
import { getAppConfig } from '@runeya/scripts-tsup-config'
export default getAppConfig({
entry: ['src/index.ts'],
// noExternal bundles @runeya/* (dist, not src)
})Rebuild shared after any change
After any change to packages/shared, you must rebuild before testing:
sh
yarn workspace @runeya/packages-shared buildOtherwise the server in watch mode runs with the old version of Zod and silently strips new fields.
Turborepo Pipeline
json
// turbo.json
{
"pipeline": {
"build": {
"dependsOn": ["^build"], // build deps first
"outputs": ["dist/**"]
},
"test": {
"dependsOn": ["build"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}Yarn 4 Berry
nodeLinker: node-modules(maximum compatibility)- Workspaces:
apps/*,packages/*,scripts/* - Workspace dependencies:
"@runeya/packages-shared": "workspace:*"
Tests
- Framework: Vitest
- Each package has its own
vitest.config.ts - Workspace aliases configured for cross-package imports
- Singleton pattern:
typescript
// Always reset modules for singletons
beforeEach(async () => {
vi.resetModules()
vi.doMock('./my-dep.js', () => ({ ... }))
const { myService } = await import('./my-service.js')
})