Module Development Guide
This guide covers developing external modules that live as separate git repos under the MoLOS monorepo.
Quick Start with CLI
The fastest way to create a new module is using the built-in CLI:
# From MoLOS root directory
npm run module:create my-module --name "My Module" --author "Your Name" --description "My module description"
# Or with all defaults
npm run module:create my-module
This will create a complete module scaffold at modules/my-module with all necessary files and folders.
Manual Workflow
If you prefer to start from a sample module manually:
- Create a new git repo
- Clone MoLOS main repo
- Clone
MoLOS-Sample-Moduleor another working module as boilerplate - Copy boilerplate into
modules/<ModuleId> - Run
npm run module:syncto create symlinks and run migrations - Run
npm run devto start development
Module Structure
modules/<ModuleId>/
├── package.json # @molos/module-<module-id>
├── drizzle.config.ts # Database migration config (if has DB)
├── drizzle/ # Migration files (if has DB)
│ ├── 0000_initial.sql
│ ├── 0000_initial.down.sql
│ └── meta/
└── src/
├── index.ts # Main exports
├── config.ts # Module configuration (REQUIRED)
├── models/ # TypeScript types and enums
├── lib/ # Library code
│ ├── components/ # Svelte components
│ └── utils/ # Utility functions
├── server/
│ ├── ai/ # AI tools (optional)
│ │ └── ai-tools.ts
│ ├── database/ # Database schema (if has DB)
│ │ ├── schema/
│ │ │ ├── index.ts
│ │ │ └── tables.ts
│ └── repositories/ # Data access layer
│ ├── base-repository.ts
│ └── *.ts
├── routes/
│ ├── ui/ # SvelteKit UI routes
│ │ ├── +layout.svelte
│ │ ├── +page.svelte
│ │ └── */ # Sub-routes
│ └── api/ # API endpoints
│ └── +server.ts
└── stores/ # Svelte stores (Svelte 5 runes)
Development Map
- Database Schema - Define tables and migrations
- TypeScript Models - Create type-safe interfaces and inputs
- Repository Layer - Implement data access logic
- API Endpoints - Build SvelteKit server routes
- UI Development - Create routes, components, and stores
- Module Configuration - Manifest + navigation config
- AI Integration - Optional AI tools for Architect Agent
Import Standards (CRITICAL)
Module-Internal Code
CRITICAL: When importing from within a module's own code, ALWAYS use the $module alias. This ensures imports work in both development and production builds.
// ✅ CORRECT: Use $module alias for module-internal imports
import { TaskRepository } from '$module/server/repositories/task-repository.js';
import { TaskStatus } from '$module/models/index.js';
import { myValidator } from '$module/server/utils/validator.js';
// ❌ WRONG: Fragile relative paths (break in production builds)
import { TaskRepository } from '../../../../../server/repositories/task-repository.js';
import { TaskStatus } from '../../../../models/index.js';
Main App Code
Always use $lib alias for main app imports:
// ✅ ALWAYS use $lib alias
import { db } from '$lib/server/db';
import { Button } from '$lib/components/ui/button';
import { auth } from '$lib/server/auth';
// ❌ NEVER use relative paths (breaks in node_modules)
import { db } from '../../../../../src/lib/server/db';
import { Button } from '../../../../../src/lib/components/ui/button';
Cross-Module Imports
Modules can import from other modules using two patterns:
// Pattern 1: $lib/modules/{ModuleName} - imports from module's lib directory
import { goalsStore } from '$lib/modules/MoLOS-Goals/stores';
// Pattern 2: $lib/models/external_modules/{ModuleName} - imports specific subdirectory
import { Goal } from '$lib/models/external_modules/MoLOS-Goals';
Database Migrations
Migration Command Changes
CRITICAL SAFETY RULE: NEVER RUN drizzle-kit generate
FORBIDDEN: Never run drizzle-kit generate or npm run db:generate. These commands are explicitly removed for safety.
Why:
- Creates journal/SQL desync (migrations in journal but no SQL files)
- Generates random names (
0003_dizzy_jane_foster.sql) - zero context - Cannot enforce table naming conventions (
MoLOS-{Name}_{table}) - Overwrites manual migrations without warning
Correct Approach:
# Always create migrations manually with descriptive names
npm run db:migration:create --name add_new_feature --module <ModuleId> --reversible
# Validate after creating
npm run db:validate
# Apply (safe with auto-backup)
npm run db:migrate
Migration File Structure
Forward Migration (0001_description.sql):
-- Migration: add_feature
-- Module: <ModuleId>
-- Created: 2026-03-17
CREATE TABLE <ModuleId>_items (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
name TEXT NOT NULL
);
--> statement-breakpoint
CREATE INDEX idx_<module>_items_user_id ON <ModuleId>_items(user_id);
Rollback Migration (0001_description.down.sql):
-- Rollback: add_feature
-- Module: <ModuleId>
-- Created: 2026-03-17
DROP INDEX IF EXISTS idx_<module>_items_user_id;
--> statement-breakpoint
DROP TABLE IF EXISTS <ModuleId>_items;
Configuration
package.json
{
"name": "@molos/module-<module-id>",
"version": "1.0.0",
"type": "module",
"exports": {
".": "./src/index.ts",
"./config": "./src/config.ts",
"./models": "./src/models/index.js",
"./database": "./src/server/database/schema.js"
},
"peerDependencies": {
"svelte": "^5.45.0"
},
"dependencies": {
"lucide-svelte": "^0.561.0"
}
}
config.ts (REQUIRED)
import { SquareCheck, ListTodo } from 'lucide-svelte';
import type { ModuleConfig } from '$lib/config/types';
export const myModuleConfig: ModuleConfig = {
id: 'MoLOS-MyModule', // REQUIRED: Must match module ID convention
name: 'My Module',
href: '/ui/MoLOS-MyModule',
icon: SquareCheck,
description: 'Module description',
navigation: [
{
name: 'Dashboard',
icon: ListTodo,
href: '/ui/MoLOS-MyModule/dashboard'
}
]
};
export default myModuleConfig;
Svelte 5 Requirements
State Management
- ✅ USE: Svelte 5 runes (
$state,$derived,$props,$effect,$bindable) - ❌ AVOID: Svelte 4 stores (
writable,readable,derivedfrom 'svelte/store')
Exception: Existing modules may use Svelte 4 stores but migration is recommended.
Component Props
- ✅ USE:
$props()for declaring component props in Svelte 5 - ❌ AVOID:
export letprop declarations (Svelte 4 legacy)
Exception: export let data is valid for passing data from +page.server.ts/+layout.server.ts load functions.
Shared State Files
- ✅ USE:
.svelte.tsor.svelte.jsfiles for shared reactive logic - ❌ AVOID:
.tsfiles with exports of$statevariables
// src/stores/entities.svelte.ts - CORRECT
let count = $state(0);
export const store = { get value() { return count; } };
Testing
Run tests before pushing:
# Run all tests
npm run test
# Run tests in watch mode
npm run test:unit
Publishing
Publish to npm
# Build module
npm run build
# Publish
npm publish --access public
Submit to Modules Directory
To be included in the official MoLOS Docker image, submit your module to the MoLOS modules directory.
Common Issues
Import errors ($module/server/... not found)
Ensure module code uses $module alias for internal imports, not relative paths.
Module not in sidebar
Run npm run module:sync and verify config.ts exists.
Database table not found
Create migration: npm run db:migration:create --name add_table --module <ModuleId> and then npm run db:migrate.
Related Documentation
- Configuration - Module config details
- Database Schema - Database and migrations
- TypeScript Models - Type definitions
- Repository Layer - Data access patterns
- API Endpoints - SvelteKit routes
- UI Development - Components and pages
- AI Integration - AI tools and MCP
Last Updated: March 17, 2026