Skip to main content

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:

  1. Create a new git repo
  2. Clone MoLOS main repo
  3. Clone MoLOS-Sample-Module or another working module as boilerplate
  4. Copy boilerplate into modules/<ModuleId>
  5. Run npm run module:sync to create symlinks and run migrations
  6. Run npm run dev to 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

  1. Database Schema - Define tables and migrations
  2. TypeScript Models - Create type-safe interfaces and inputs
  3. Repository Layer - Implement data access logic
  4. API Endpoints - Build SvelteKit server routes
  5. UI Development - Create routes, components, and stores
  6. Module Configuration - Manifest + navigation config
  7. 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, derived from '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 let prop 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.ts or .svelte.js files for shared reactive logic
  • AVOID: .ts files with exports of $state variables
// 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.


Last Updated: March 17, 2026