Node.js SDK for the Unified Module System (UMS) v2.0. Provides file system operations, TypeScript module loading, and high-level orchestration for building AI persona instructions from modular components.
- Overview
- Installation
- Architecture
- Quick Start
- Core Components
- Usage Examples
- TypeScript Support
- Configuration
- API Reference
- Development
- Relationship to Other Packages
- License
The UMS SDK is the Node.js implementation layer for UMS v2.0, providing:
- File System Operations: Load
.module.tsand.persona.tsfiles from disk - TypeScript Module Loading: Dynamically import
.tsmodules (run undertsx/ts-nodeloader or precompile to JavaScript) - Module Discovery: Automatically find and load modules from configured directories
- Build Orchestration: Complete workflow for building personas from modular components
- Configuration Management: Load and validate
modules.config.ymlfiles - Standard Library Support: Integrated standard module library
The SDK sits between the pure domain logic in ums-lib and the CLI/UI layer, providing the I/O and orchestration needed for real-world applications.
npm install ums-sdkThe SDK requires Node.js 22.0.0 or higher and includes ums-lib as a dependency.
- tsx: Recommended for loading TypeScript modules (
.module.ts,.persona.ts) at runtime - TypeScript: Peer dependency for type-checking; optional at runtime if you precompile
If you need to execute .ts files directly, install tsx and run your scripts with the loader:
npm install tsx --save-dev
node --loader tsx ./scripts/run-build.tsAlternatively, compile source files to JavaScript (tsc --outDir dist) and import the emitted .js files with the SDK.
The UMS ecosystem follows a three-tier architecture:
┌─────────────────────────────────────────────┐
│ CLI / UI Layer │
│ (ums-cli, ums-mcp) │
│ - User interface │
│ - Command handling │
│ - Output formatting │
└─────────────────┬───────────────────────────┘
│
│ uses
▼
┌─────────────────────────────────────────────┐
│ UMS SDK (this package) │
│ - File system operations │
│ - TypeScript module loading │
│ - Module discovery │
│ - Build orchestration │
│ - Configuration management │
└─────────────────┬───────────────────────────┘
│
│ uses
▼
┌─────────────────────────────────────────────┐
│ UMS Library │
│ - Pure domain logic │
│ - Module/persona parsing │
│ - Validation │
│ - Module registry │
│ - Markdown rendering │
└─────────────────────────────────────────────┘
Separation of Concerns:
- ums-lib: Platform-agnostic domain logic, no I/O operations
- ums-sdk: Node.js-specific I/O, file loading, and orchestration
- Re-exports types from ums-lib for convenience
- For domain functions, import from ums-lib or use SDK's high-level API
- CLI/UI: User-facing interfaces consuming the SDK
import { buildPersona, type Module } from 'ums-sdk';
// Build a persona from a TypeScript configuration file
const result = await buildPersona('./personas/my-persona.persona.ts');
console.log(result.markdown); // Rendered Markdown content
console.log(result.buildReport); // Build metadata and statisticsThe SDK re-exports types from ums-lib for convenience:
// Import workflows from SDK
import { buildPersona, validateAll, listModules } from 'ums-sdk';
// Import types from SDK (re-exported from ums-lib)
import type { Module, Persona, BuildReport } from 'ums-sdk';
// For domain functions (validation, rendering), import from ums-lib:
import { validateModule, renderMarkdown } from 'ums-lib';
// Or use SDK's high-level API which handles everything:
const result = await buildPersona('./persona.persona.ts'); // Already validated and rendered!Runtime note: When consuming
.persona.ts/.module.tsfiles directly, run the script with a TypeScript loader (e.g.,node --loader tsx) or precompile the files to.jsbefore calling the SDK.
The SDK provides three main convenience functions for common workflows:
Complete workflow for building a persona:
import { buildPersona } from 'ums-sdk';
const result = await buildPersona('./personas/my-persona.persona.ts', {
configPath: './modules.config.yml',
conflictStrategy: 'warn',
includeStandard: true,
});
// result contains:
// - markdown: Rendered output
// - persona: Loaded persona object
// - modules: Resolved modules in composition order
// - buildReport: Build metadata (SHA-256 hash, module list, etc.)
// - warnings: Any warnings generated during buildValidate all discovered modules and personas:
import { validateAll } from 'ums-sdk';
const report = await validateAll({
configPath: './modules.config.yml',
includeStandard: true,
includePersonas: true,
});
console.log(`Valid modules: ${report.validModules}/${report.totalModules}`);
console.log(`Valid personas: ${report.validPersonas}/${report.totalPersonas}`);
// Check for errors
if (report.errors.size > 0) {
for (const [id, errors] of report.errors) {
console.error(`${id}:`, errors);
}
}Persona discovery:
validateAll({ includePersonas: true })only scans directories listed inmodules.config.yml. Ensure persona files live inside (or beneath) one of the configuredlocalModulePaths.
List all available modules with metadata:
import { listModules } from 'ums-sdk';
const modules = await listModules({
level: 0, // Optional: filter by cognitive level (0-6)
capability: 'reasoning', // Optional: filter by capability
domain: 'typescript', // Optional: filter by domain
tag: 'best-practices', // Optional: filter by tag
});
modules.forEach(module => {
console.log(`${module.id}: ${module.name}`);
console.log(` Description: ${module.description}`);
console.log(` Cognitive Level: ${module.cognitiveLevel}`);
console.log(` Source: ${module.source}`);
console.log(` Capabilities: ${module.capabilities.join(', ')}`);
});
// filePath is only defined for modules discovered from local directories and may be omitted.Loaders handle file I/O and TypeScript execution:
Loads and validates .module.ts files:
import { ModuleLoader } from 'ums-sdk';
const loader = new ModuleLoader();
// Load a single module
const module = await loader.loadModule(
'/path/to/error-handling.module.ts',
'error-handling'
);
// Load raw file content (for hashing, etc.)
const content = await loader.loadRawContent('/path/to/module.ts');Loads and validates .persona.ts files:
import { PersonaLoader } from 'ums-sdk';
const loader = new PersonaLoader();
// Load a persona (supports default or named exports)
const persona = await loader.loadPersona(
'./personas/systems-architect.persona.ts'
);
console.log(persona.name);
console.log(persona.modules); // Module IDs to composeLoads and validates modules.config.yml:
import { ConfigManager } from 'ums-sdk';
const configManager = new ConfigManager();
// Load configuration
const config = await configManager.load('./modules.config.yml');
// config contains:
// - localModulePaths: Array of { path }
// Validate configuration structure
const validation = configManager.validate(configObject);
if (!validation.valid) {
console.error('Config errors:', validation.errors);
}Discovery components find and load modules from directories:
Discovers all .module.ts files in configured paths:
import { ModuleDiscovery } from 'ums-sdk';
const discovery = new ModuleDiscovery();
// Discover modules from configuration
const modules = await discovery.discover(config);
// Or discover from specific paths
const modules = await discovery.discoverInPaths([
'./instruct-modules-v2',
'./custom-modules',
]);Manages standard library modules:
import { StandardLibrary } from 'ums-sdk';
const standardLib = new StandardLibrary();
// Discover all standard library modules
const modules = await standardLib.discoverStandard();
// Check if a module is from standard library
const isStandard = standardLib.isStandardModule('foundation/ethics/do-no-harm');
// Get standard library path
const path = standardLib.getStandardLibraryPath();Complete example building a persona from a TypeScript file:
import { buildPersona } from 'ums-sdk';
import { writeFile } from 'node:fs/promises';
async function buildMyPersona() {
try {
// Build persona
const result = await buildPersona(
'./personas/systems-architect.persona.ts',
{
configPath: './modules.config.yml',
conflictStrategy: 'warn',
includeStandard: true,
}
);
// Write output to file
await writeFile('./dist/systems-architect.md', result.markdown);
// Log build information
console.log(`Built persona: ${result.persona.name}`);
console.log(`Version: ${result.persona.version}`);
console.log(`Modules: ${result.modules.length}`);
console.log(`Build ID: ${result.buildReport.buildId}`);
// Handle warnings
if (result.warnings.length > 0) {
console.warn('Warnings:');
result.warnings.forEach(warning => console.warn(` - ${warning}`));
}
} catch (error) {
console.error('Build failed:', error);
process.exit(1);
}
}
buildMyPersona();Validate all modules and personas in your project:
import { validateAll } from 'ums-sdk';
async function validateProject() {
const report = await validateAll({
includeStandard: true,
includePersonas: true,
});
console.log('\n=== Validation Report ===');
console.log(`Modules: ${report.validModules}/${report.totalModules} valid`);
if (report.totalPersonas !== undefined) {
console.log(
`Personas: ${report.validPersonas}/${report.totalPersonas} valid`
);
}
// Show errors
if (report.errors.size > 0) {
console.error('\nErrors:');
for (const [id, errors] of report.errors) {
console.error(`\n${id}:`);
errors.forEach(error => {
console.error(` - ${error.path || ''}: ${error.message}`);
});
}
process.exit(1);
}
console.log('\nAll validations passed!');
}
validateProject();Query available modules with filtering:
import { listModules } from 'ums-sdk';
async function listAxiomModules() {
// List all axiom-level modules (cognitive level 0)
import { CognitiveLevel } from 'ums-sdk';
const modules = await listModules({
level: CognitiveLevel.AXIOMS_AND_ETHICS,
});
console.log(`Found ${modules.length} axiom-level modules:\n`);
modules.forEach(module => {
console.log(`${module.id}`);
console.log(` Name: ${module.name}`);
console.log(` Description: ${module.description}`);
console.log(` Version: ${module.version}`);
console.log(` Cognitive Level: ${module.cognitiveLevel}`);
console.log(` Capabilities: ${module.capabilities.join(', ')}`);
console.log(` Source: ${module.source}`);
console.log();
});
}
// List modules with a specific capability
async function listReasoningModules() {
const modules = await listModules({
capability: 'reasoning',
});
console.log(`Modules with reasoning capability: ${modules.length}`);
modules.forEach(m => console.log(` - ${m.id} (Level ${m.cognitiveLevel})`));
}
// List TypeScript modules at levels 3-4
async function listTypescriptProcedures() {
const modules = await listModules({
domain: 'typescript',
level: [3, 4], // Domain-specific guidance and procedures
});
console.log(`TypeScript procedural modules: ${modules.length}`);
modules.forEach(m => console.log(` - ${m.id}`));
}
listAxiomModules();
listReasoningModules();
listTypescriptProcedures();The SDK uses tsx to load TypeScript files on-the-fly, allowing you to write modules and personas in TypeScript without a separate compilation step.
The SDK uses path-based module ID extraction. Module IDs are automatically extracted from the file path relative to the configured base path:
- Example:
./modules/foundation/ethics/do-no-harm.module.tswith base./modules→ Module ID:foundation/ethics/do-no-harm - The SDK validates that the module's declared
idfield matches the expected ID from the file path - This ensures consistency between file organization and module identifiers
The SDK performs the following validations:
- Module ID Matching: Validates that the declared module ID matches the file path
- Export Naming: Validates that exported module names follow the camelCase convention
- File Loading: Wraps file system errors with contextual information
The SDK delegates module structure validation to ums-lib, which validates:
- UMS v2.0 Compliance: Module structure, required fields, schema version
- Module Content: Instruction format, metadata completeness
- Registry Operations: Conflict detection, dependency resolution
import type { Module } from 'ums-lib';
import { CognitiveLevel } from 'ums-lib';
export const errorHandling: Module = {
id: 'error-handling',
version: '1.0.0',
schemaVersion: '2.0',
capabilities: ['error-handling', 'debugging'],
cognitiveLevel: CognitiveLevel.DOMAIN_SPECIFIC_GUIDANCE,
metadata: {
name: 'Error Handling',
description: 'Best practices for error handling',
semantic:
'exception error handling debugging recovery best-practices patterns',
},
instruction: {
purpose: 'Guide error handling implementation',
process: [
'Identify error boundaries',
'Implement error handlers',
'Log errors appropriately',
],
},
};import type { Persona } from 'ums-lib';
export default {
name: 'Systems Architect',
version: '1.0.0',
schemaVersion: '2.0',
description: 'Expert in system design and architecture',
semantic: 'architecture design systems scalability patterns',
modules: [
'foundation/reasoning/systems-thinking',
'principle/architecture/separation-of-concerns',
'technology/typescript/best-practices',
],
} satisfies Persona;Modules:
- Must use named exports
- Export name is camelCase transformation of the full module ID path
- Example:
foundation/ethics/do-no-harm→export const foundationEthicsDoNoHarm - The entire path (including tier and category) is converted: slashes removed, segments capitalized
Personas:
- Can use default export (preferred) or named export
- SDK will find any valid Persona object in exports
The SDK uses modules.config.yml for configuration:
# Optional: Global conflict resolution strategy (default: 'error')
conflictStrategy: warn # 'error' | 'warn' | 'replace'
localModulePaths:
- path: ./instruct-modules-v2
- path: ./custom-modules-
conflictStrategy(optional): Global conflict resolution strategyerror: Fail on duplicate module IDs (default)warn: Log warning and skip duplicatereplace: Replace existing module with new one
-
localModulePaths(required): Array of module search pathspath: Directory containing modules
Conflict resolution is controlled globally and follows this priority order:
- Runtime override:
BuildOptions.conflictStrategy(if provided) - Config file default:
conflictStrategyinmodules.config.yml(if specified) - System default:
'error'
Example with config file default:
# modules.config.yml
conflictStrategy: warn # Project-wide default
localModulePaths:
- path: ./modules// Uses 'warn' from config file
const result1 = await buildPersona('./personas/my-persona.persona.ts');
// Overrides config file with 'replace'
const result2 = await buildPersona('./personas/my-persona.persona.ts', {
conflictStrategy: 'replace',
});Note: Per-path conflict resolution (the onConflict field) was removed in v1.0 to simplify configuration. This feature is reserved for potential inclusion in v2.x based on user feedback.
INSTRUCTIONS_MODULES_PATH: Override standard library location (default:./instructions-modules)
| Function | Parameters | Returns | Description |
|---|---|---|---|
buildPersona() |
personaPath: string, options?: BuildOptions |
Promise<BuildResult> |
Build a persona from file |
validateAll() |
options?: ValidateOptions |
Promise<ValidationReport> |
Validate all modules and personas |
listModules() |
options?: ListOptions |
Promise<ModuleInfo[]> |
List all available modules |
| Class | Methods | Description |
|---|---|---|
ModuleLoader |
loadModule(), loadRawContent() |
Load .module.ts files |
PersonaLoader |
loadPersona() |
Load .persona.ts files |
ConfigManager |
load(), validate() |
Load and validate config |
| Class | Methods | Description |
|---|---|---|
ModuleDiscovery |
discover(), discoverInPaths() |
Find and load modules |
StandardLibrary |
discoverStandard(), isStandardModule() |
Manage standard library |
| Error | Extends | Description |
|---|---|---|
SDKError |
Error |
Base SDK error |
ModuleNotFoundError |
SDKError |
File not found |
InvalidExportError |
SDKError |
Invalid module export |
ModuleLoadError |
SDKError |
Module loading failed |
ConfigError |
SDKError |
Configuration error |
DiscoveryError |
SDKError |
Module discovery failed |
interface BuildOptions {
configPath?: string;
conflictStrategy?: 'error' | 'warn' | 'replace';
attribution?: boolean;
includeStandard?: boolean;
}
interface BuildResult {
markdown: string;
persona: Persona;
modules: Module[];
buildReport: BuildReport;
warnings: string[];
}
interface ValidateOptions {
configPath?: string;
includeStandard?: boolean;
includePersonas?: boolean;
}
interface ValidationReport {
totalModules: number;
validModules: number;
errors: Map<string, ValidationError[]>;
warnings: Map<string, SDKValidationWarning[]>;
totalPersonas?: number;
validPersonas?: number;
}
interface ListOptions {
configPath?: string;
includeStandard?: boolean;
level?: number | number[] | CognitiveLevel | CognitiveLevel[]; // Accepts enum or number
capability?: string;
domain?: string;
tag?: string;
}
interface ModuleInfo {
id: string;
name: string;
description: string;
version: string;
capabilities: string[];
source: 'standard' | 'local';
filePath?: string;
}# Install dependencies
npm install
# Build the SDK
npm run build
# Run tests
npm test
# Run tests with coverage
npm run test:coverage
# Type checking
npm run typecheck
# Lint
npm run lint
npm run lint:fix
# Format
npm run format
npm run format:check
# Quality check (all validations)
npm run quality-checkpackages/ums-sdk/
├── src/
│ ├── api/
│ │ └── high-level-api.ts # Convenience functions
│ ├── loaders/
│ │ ├── module-loader.ts # Load .module.ts files
│ │ ├── persona-loader.ts # Load .persona.ts files
│ │ └── config-loader.ts # Load modules.config.yml
│ ├── discovery/
│ │ ├── module-discovery.ts # Find modules in directories
│ │ └── standard-library.ts # Manage standard library
│ ├── orchestration/
│ │ └── build-orchestrator.ts # Build workflow coordination
│ ├── errors/
│ │ └── index.ts # SDK-specific errors
│ ├── types/
│ │ └── index.ts # TypeScript type definitions
│ └── index.ts # Main exports
├── dist/ # Compiled output
├── package.json
├── tsconfig.json
└── README.md
The SDK includes comprehensive unit tests using Vitest:
# Run all tests
npm test
# Run tests in watch mode
npm run test -- --watch
# Run specific test file
npx vitest run src/loaders/module-loader.test.ts
# Coverage report
npm run test:coverage- Follow the project's TypeScript configuration
- Write tests for new features
- Maintain 80% code coverage
- Use ESLint and Prettier for code style
- Update documentation for API changes
- ums-lib: Pure domain logic (validation, rendering, registry)
- yaml: YAML parsing for configuration files
- glob: File pattern matching for module discovery
- tsx (optional): TypeScript execution for
.module.tsand.persona.tsfiles
- ums-cli: Command-line interface using the SDK
- ums-mcp: MCP server for AI assistant integration
The SDK follows these architectural principles:
- Separation of Concerns: I/O operations are isolated from domain logic
- Composition: Uses ums-lib for all domain logic
- Node.js Specific: Leverages Node.js APIs for file system operations
- Type Safety: Full TypeScript support with exported type definitions
- Error Handling: Comprehensive error types for different failure modes
GPL-3.0-or-later
Copyright (c) 2025 synthable
This package is part of the Instructions Composer monorepo.
For questions, issues, or contributions, please visit the GitHub repository.