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.
-
Enable Corepack to manage pnpm versions automatically:
corepack enable -
Fork and clone the repository:
git clone https://github.com/<your-username>/airi.git cd airi -
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 inapps/stage-web/vite.config.ts, with routes defined insrc/pages/.apps/stage-tamagotchi/– The Electron desktop application. The main process is inapps/stage-tamagotchi/src/main/index.ts, while the renderer shares Vue components with the web version. IPC communication uses@moeru/eventawith contracts defined inapps/stage-tamagotchi/src/shared/eventa.ts.apps/stage-pocket/– The Capacitor mobile app for iOS/Android. Native code resides inandroid/andios/directories, while the Vue layer reusespackages/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 likeuseAudioanduseDark.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(orpnpm dev) - Desktop:
pnpm dev:tamagotchi - Mobile:
pnpm dev:pocket:iosorpnpm 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 lintlocally. The configuration spans.eslintrc.cjsandeslint.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
injecacontainer (seeapps/stage-tamagotchi/src/main/index.ts) for testability. - Conventional Commits: Use prefixes like
feat:,fix:, ordocs:in your commit messages. Optionally include emojis. - Branch Strategy: Create short-lived branches named
<username>/<feature-name>, keep them rebased onmain, and open PRs targeting themainbranch.
Summary
- moeru-ai/airi is a pnpm workspace monorepo with apps in
apps/and shared logic inpackages/. - Install Node.js ≥23, Rust, and pnpm 10+, then run
pnpm installto begin contributing. - Use
pnpm dev:web,pnpm dev:tamagotchi, orpnpm dev:pocket:iosto run specific applications. - New AI providers require implementation in
packages/stage-ui/src/stores/providers/, registration inprovider-catalog.ts, and Vitest unit tests. - Always run
pnpm lintandpnpm typecheckbefore 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →