Best Practices for Developing with holaOS: A Complete Guide to the Monorepo Architecture
TLDR: holaOS development follows strict conventions across its Electron desktop app, TypeScript runtime, state store, and plugin SDK—use wrapper patterns for UI components, ordered migrations for database changes, skill-based architecture for agent capabilities, and provided npm scripts for reproducible builds.
holaOS is a monorepo that combines a desktop Electron front-end, a TypeScript runtime, a plug-in SDK, and agent-invokable skills. Following established best practices for developing with holaOS ensures your contributions remain type-safe, testable, and compatible with the automated CI pipeline. This guide covers the architectural conventions, critical file paths, and practical patterns used throughout the codebase according to the holaboss-ai/holaOS source.
Desktop App Development Practices
The desktop application lives in apps/desktop/ and serves as the Electron wrapper that loads the runtime bundle and renders HolaApps.
Use Provided npm Scripts for Reproducible Workflows
Never run Electron directly. The repository provides standardized scripts:
# Initial setup
npm run desktop:install
# Prepare runtime with local state store
npm run desktop:prepare-runtime:local
# Start development server
npm run desktop:dev
These scripts handle bundling, environment validation, and runtime initialization in the correct order.
Environment Configuration
Copy the template before first run:
cp apps/desktop/.env.example apps/desktop/.env
Keep all environment-specific variables in apps/desktop/.env. The .env.example file documents required keys for new developers.
Icon Wrapper Pattern (Critical)
Never import icons directly from lucide-react or @hugeicons/core-free-icons. All UI icons must route through the centralized wrapper:
// apps/desktop/src/components/ui/icons.tsx
import { makeIcon } from '@hugeicons/core-free-icons';
import { IconType } from '@hugeicons/core-free-icons';
export const MyNewIcon: IconType = makeIcon(
<svg viewBox="0 0 24 24"><path d="M12 2l7 7h-4v7h-6V9H5z"/></svg>
);
// Correct usage in components
import { MyNewIcon } from '@/components/ui/icons';
export default function Sidebar() {
return <MyNewIcon className="w-5 h-5" />;
}
Direct imports trigger CI lint errors as documented in AGENTS.md. The wrapper enforces consistent sizing, styling, and tree-shaking behavior.
Runtime State Store Management
The state store at runtime/state-store/src/store.ts provides durable, file-based storage for shared memory, sessions, and agent capabilities.
Migration-Driven Schema Changes
Always use numbered migration files:
// runtime/state-store/src/migrations/034-add-notifications.ts
import { Migration } from '../migrations';
export const migration034: Migration = {
id: 34,
up: async (db) => {
await db.exec(`
CREATE TABLE notifications (
id TEXT PRIMARY KEY,
message TEXT NOT NULL,
created_at INTEGER NOT NULL
);
`);
},
};
Key practices:
- Keep migrations ordered numerically (001-, 002-, etc.)
- Run migrations before launch:
npm run desktop:prepare-runtime - Write unit tests: See
src/migrations.test.tsfor the test pattern
Harness and Skill Development
The harness system in runtime/harnesses/src/ exposes tools, model routing, and MCP (Model Context Protocol) to agents.
Skill-Based Architecture
Implement new capabilities as skills under runtime/harnesses/src/embedded-skills/:
// runtime/harnesses/src/embedded-skills/my-api-client/SKILL.ts
import { createSkill } from '../skill-base';
import { fetch } from 'node-fetch';
export const myApiSkill = createSkill('myApiClient', async (input) => {
const resp = await fetch(`https://api.example.com/v1/${input.id}`);
const data = await resp.json();
return data;
});
Required steps:
- Follow the template in
runtime/harnesses/src/embedded-skills/SKILL.md - Register the skill in
runtime/harnesses/src/index.ts - Add unit tests at
runtime/harnesses/src/embedded-skills/[skill-name]/SKILL.test.ts - Update
interface-designdocumentation
Tool Usage Tracking
Use the tool-replay-budget-ledger to monitor and enforce tool usage limits for agents.
Plugin SDK Integration
The app-builder-sdk (packages/app-builder-sdk) provides type-safe wrappers for HolaApps running inside the workspace.
SDK Generation and Synchronization
Generate SDK updates from the OpenAPI specification:
npm run generate:sdk
This uses Kubb to produce type-safe clients. Always:
- Keep SDK version synchronized with server contract
- Verify against
packages/app-builder-sdk/README.mdbefore releasing
Build and Bundle Configuration
holaOS uses tsup, vite, and tsdown for bundling. Follow existing patterns:
// runtime/state-store/tsup.config.ts - reference implementation
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm'],
dts: true,
splitting: false,
sourcemap: true,
clean: true,
});
Always run npm run build in each package before committing to ensure clean output and type generation.
Testing and CI Requirements
- Run
npm testlocally before pushing—this executes Jest unit tests and Playwright integration tests - Add tests for any public API change
- Mirror patterns from
runtime/state-store/src/store.test.ts
Documentation and Versioning
Keep Docs Synchronized
Documentation lives in docs/ and contribute/. Update relevant files when changing code:
plugin-sdk.mdfor SDK method additionsSKILL.mdwhen modifying skill interfacesAGENTS.mdfor agent behavior changes
Conventional Commits
Drive changelogs with structured commits:
feat: add notification polling to runtime
- Implements webhook fallback for missed events
- Adds retry logic with exponential backoff
- Updates SKILL.md with new error handling pattern
Prefixes: feat:, fix:, chore:, docs:, test:
Summary
- Desktop workflow: Use provided npm scripts,
.envconfiguration, and the icon wrapper pattern inapps/desktop/src/components/ui/icons.tsx - State store: Create ordered migrations, run
desktop:prepare-runtime, and test each migration - Skills: Build agent capabilities using the
SKILL.mdtemplate inruntime/harnesses/src/embedded-skills/ - SDK: Generate from OpenAPI with
generate:sdkand keep versions synchronized - Build: Follow existing
tsup.config.tspatterns and build before commit - Quality: Test locally with
npm test, use conventional commits, and synchronize documentation
Frequently Asked Questions
What is the holaOS monorepo structure?
holaOS combines four main components: an Electron desktop app (apps/desktop/), a TypeScript runtime with state store (runtime/state-store/), agent harnesses with MCP support (runtime/harnesses/), and a plugin SDK (packages/app-builder-sdk). These share a unified build system and testing infrastructure.
Why can't I import icons directly from icon libraries?
Direct imports from lucide-react or @hugeicons/core-free-icons bypass the centralized makeIcon wrapper in apps/desktop/src/components/ui/icons.tsx. This breaks styling consistency, tree-shaking optimization, and CI lint rules. The wrapper enforces uniform behavior across all UI components.
How do I add a new database table to holaOS?
Create a numbered migration file in runtime/state-store/src/migrations/, implement the Migration interface with up and down methods, add a corresponding test file, then run npm run desktop:prepare-runtime:local to apply it. Never modify existing migration files once committed.
What is MCP in holaOS and where is it implemented?
MCP (Model Context Protocol) enables plugging in any LLM provider or custom tool server to agent harnesses. The core client implementation lives in runtime/harnesses/src/mcp.ts, allowing agents to route requests to external models or specialized tool servers through a standardized interface.
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 →