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

> Learn how to contribute to moeru-ai/airi with this comprehensive guide. Follow simple steps to fork, set up, code, and submit your pull request for a successful contribution.

- Repository: [Moeru AI/airi](https://github.com/moeru-ai/airi)
- Tags: getting-started
- Published: 2026-03-08

---

**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:

   ```bash
   corepack enable
   ```

2. **Fork and clone** the repository:

   ```bash
   git clone https://github.com/<your-username>/airi.git
   cd airi
   ```

3. **Install dependencies** across all workspaces:

   ```bash
   pnpm install
   ```

The root [`package.json`](https://github.com/moeru-ai/airi/blob/main/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`](https://github.com/moeru-ai/airi/blob/main/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`](https://github.com/moeru-ai/airi/blob/main/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`](https://github.com/moeru-ai/airi/blob/main/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`](https://github.com/moeru-ai/airi/blob/main/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`](https://github.com/moeru-ai/airi/blob/main/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`](https://github.com/moeru-ai/airi/blob/main/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`](https://github.com/moeru-ai/airi/blob/main/.github/workflows/ci.yml) enforces these exact commands:

```bash
pnpm typecheck
pnpm lint
pnpm test

```

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

```bash
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`](https://github.com/moeru-ai/airi/blob/main/packages/stage-ui/src/stores/providers/elevenlabs-custom/index.ts):

```typescript
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`](https://github.com/moeru-ai/airi/blob/main/packages/stage-ui/src/stores/provider-catalog.ts):

```typescript
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`](https://github.com/moeru-ai/airi/blob/main/packages/stage-ui/src/stores/providers/elevenlabs-custom/index.test.ts):

```typescript
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:

```bash
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`](https://github.com/moeru-ai/airi/blob/main/eslint.config.js).
- **Styling**: Use **UnoCSS** shortcuts defined in [`uno.config.ts`](https://github.com/moeru-ai/airi/blob/main/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`](https://github.com/moeru-ai/airi/blob/main/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`](https://github.com/moeru-ai/airi/blob/main/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`](https://github.com/moeru-ai/airi/blob/main/src/stores/providers.ts). You must export the implementation from [`src/stores/provider-catalog.ts`](https://github.com/moeru-ai/airi/blob/main/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`](https://github.com/moeru-ai/airi/blob/main/apps/stage-tamagotchi/src/shared/eventa.ts) and implemented separately in the main process (`src/main/`) and renderer (`src/renderer/`).