Skip to content

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}

FolderPackage 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 each package.json)
  • .js extensions are required in all relative imports (even for .ts files)
  • 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 build

Otherwise 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')
})

Released under the MIT License.