How to Contribute to moeru-ai/airi: A Complete Guide for New Contributors

To contribute to moeru-ai/airi, fork the repository, install Node.js ≥23 and Rust, run pnpm install, create a feature branch, and submit a PR after passing pnpm lint and pnpm typecheck.

The moeru-ai/airi repository is a monorepo implementing an AI-powered virtual character platform using TypeScript, Vue 3, and Rust. Whether you are adding new AI providers, fixing UI components, or improving the Electron desktop experience, understanding the workspace-based architecture is essential before opening your first pull request.

Prerequisites and Initial Setup

Before writing code, ensure your environment matches the project's requirements. The codebase requires Node.js ≥23, pnpm 10+, and Rust for the native plugins in the crates/ directory.

  1. Enable Corepack to manage pnpm versions automatically:

    corepack enable
  2. Fork and clone the repository:

    git clone https://github.com/<your-username>/airi.git
    cd airi
  3. Install dependencies across all workspaces:

    pnpm install

The root package.json defines the workspace layout and orchestrates scripts through TurboRepo.

Understanding the Monorepo Architecture

AIRI uses pnpm workspaces to organize a web front-end, desktop Electron app, mobile Capacitor app, and shared UI packages. Keep shared logic inside packages/ and application-specific code inside apps/.

Web, Desktop, and Mobile Apps

The apps/ directory contains three distinct entry points:

  • apps/stage-web/ – The browser-based UI. Configuration lives in apps/stage-web/vite.config.ts, with routes defined in src/pages/.
  • apps/stage-tamagotchi/ – The Electron desktop application. The main process is in apps/stage-tamagotchi/src/main/index.ts, while the renderer shares Vue components with the web version. IPC communication uses @moeru/eventa with contracts defined in apps/stage-tamagotchi/src/shared/eventa.ts.
  • apps/stage-pocket/ – The Capacitor mobile app for iOS/Android. Native code resides in android/ and ios/ directories, while the Vue layer reuses packages/stage-ui.

Core UI Packages and Provider System

The packages/stage-ui/ directory contains the heart of the application logic:

  • src/stores/providers/ – Implementations for LLM, TTS, and STT providers.
  • src/stores/provider-catalog.ts – The central registry where all providers are exported for use by the UI.
  • src/composables/ – Reusable Vue composables like useAudio and useDark.
  • src/components/ – Business-level UI components including character cards and control panels.

When contributing new AI integrations, you must register them in packages/stage-ui/src/stores/provider-catalog.ts after implementing the required interface in src/stores/providers/.

Development Workflow

Run development servers using the workspace-specific scripts defined in the root package.json:

  • Web: pnpm dev:web (or pnpm dev)
  • Desktop: pnpm dev:tamagotchi
  • Mobile: pnpm dev:pocket:ios or pnpm dev:pocket:android

Before committing, validate your changes against the CI pipeline. The GitHub Actions workflow in .github/workflows/ci.yml enforces these exact commands:

pnpm typecheck
pnpm lint
pnpm test

Vitest handles testing across workspaces. Run tests for a specific package using the filter flag:

pnpm -F @proj-airi/stage-ui exec vitest run

Adding Features: A Practical Example

To illustrate the contribution pattern, here is a complete example of adding a custom text-to-speech (TTS) provider to packages/stage-ui.

Create the provider implementation in packages/stage-ui/src/stores/providers/elevenlabs-custom/index.ts:

import type { TtsProvider } from '../../types'
import { fetchJson } from '@/utils/stream'

export const ElevenlabsCustomProvider: TtsProvider = {
  id: 'elevenlabs-custom',
  name: 'ElevenLabs (Custom)',
  async speak(text) {
    const payload = { text, voice_settings: { stability: 0.75 } }
    const res = await fetchJson('https://api.elevenlabs.io/v1/text-to-speech', {
      method: 'POST',
      body: JSON.stringify(payload),
      headers: { 'xi-api-key': process.env.ELEVENLABS_API_KEY! },
    })
    return res.audio_content // Uint8Array
  },
}

Register the provider in packages/stage-ui/src/stores/provider-catalog.ts:

import { ElevenlabsCustomProvider } from './providers/elevenlabs-custom'

export const ProviderCatalog = [
  // …existing providers
  ElevenlabsCustomProvider,
]

Add a unit test in packages/stage-ui/src/stores/providers/elevenlabs-custom/index.test.ts:

import { ElevenlabsCustomProvider } from './index'
import { vi } from 'vitest'

vi.mock('@/utils/stream', async () => ({
  fetchJson: vi.fn().mockResolvedValue({ audio_content: new Uint8Array([1, 2, 3]) }),
}))

test('ElevenlabsCustomProvider returns audio bytes', async () => {
  const audio = await ElevenlabsCustomProvider.speak('hello')
  expect(audio).toEqual(new Uint8Array([1, 2, 3]))
})

Run the specific test with:

pnpm -F @proj-airi/stage-ui exec vitest run src/stores/providers/elevenlabs-custom/index.test.ts

Code Quality and CI Requirements

The project enforces strict quality gates via GitHub Actions. All PRs must pass linting, type-checking, and builds before merging.

  • Linting: Run pnpm lint locally. The configuration spans .eslintrc.cjs and eslint.config.js.
  • Styling: Use UnoCSS shortcuts defined in uno.config.ts. Avoid inline Tailwind strings; instead use :class="['px-2', 'flex', 'items-center']".
  • Dependency Injection: Services in the Electron main process use the injeca container (see apps/stage-tamagotchi/src/main/index.ts) for testability.
  • Conventional Commits: Use prefixes like feat:, fix:, or docs: in your commit messages. Optionally include emojis.
  • Branch Strategy: Create short-lived branches named <username>/<feature-name>, keep them rebased on main, and open PRs targeting the main branch.

Summary

  • moeru-ai/airi is a pnpm workspace monorepo with apps in apps/ and shared logic in packages/.
  • Install Node.js ≥23, Rust, and pnpm 10+, then run pnpm install to begin contributing.
  • Use pnpm dev:web, pnpm dev:tamagotchi, or pnpm dev:pocket:ios to run specific applications.
  • New AI providers require implementation in packages/stage-ui/src/stores/providers/, registration in provider-catalog.ts, and Vitest unit tests.
  • Always run pnpm lint and pnpm typecheck before submitting a PR to ensure CI compliance.

Frequently Asked Questions

What are the minimum system requirements to contribute to moeru-ai/airi?

You need Node.js version 23 or higher, pnpm 10 or higher (enabled via Corepack), and Rust installed for the native plugins in the crates/ directory. The desktop app requires Electron build tools, while mobile development requires Android Studio or Xcode for Capacitor.

How do I run tests for a specific package in the monorepo?

Use the pnpm filter flag to target specific workspaces. For example, to test the stage-ui package, run pnpm -F @proj-airi/stage-ui exec vitest run. You can also run the full test suite with pnpm test from the repository root.

Where should I place new AI provider integrations?

Implement new providers in packages/stage-ui/src/stores/providers/<provider-name>/ following the interface definitions in src/stores/providers.ts. You must export the implementation from src/stores/provider-catalog.ts to make it available to the UI components.

How does the Electron desktop app handle communication between processes?

The desktop application uses @moeru/eventa for type-safe IPC between the main and renderer processes. Contracts are defined in apps/stage-tamagotchi/src/shared/eventa.ts and implemented separately in the main process (src/main/) and renderer (src/renderer/).

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →