Service Architecture in Agent Rules Kit
May 29, 2025 ยท View on GitHub
Introduction
Version 1.0.0 of Agent Rules Kit introduces a service-oriented architecture that improves maintainability, extensibility, and code organization. This documentation explains the structure and how to extend it.
Service Structure
BaseService
The base class that provides shared functionality across all services:
- debugLog: Centralized logging for debugging
- Basic file operations:
- directoryExists / directoryExistsAsync
- ensureDirectoryExists / ensureDirectoryExistsAsync
- getFilesInDirectory / getFilesInDirectoryAsync
- readFile / readFileAsync / readFileOptimized
- writeFile / writeFileAsync
- copyFile / copyFileAsync
- processBatch: Process items in batches to optimize memory usage
FileService
Manages all operations related to files and rule processing:
- addFrontMatter: Adds metadata to markdown content
- processTemplateVariables: Processes template variables in content
- wrapMdToMdc: Converts markdown files to .mdc format with frontmatter
- copyRuleGroup: Copies rule groups maintaining organization
- clearCache: Clears template cache to free memory
- createBackup: Creates backups of existing rules
ConfigService
Handles kit configuration:
- loadKitConfig: Loads configuration from kit-config.json
- getDefaultConfig: Provides default configuration
- saveKitConfig: Saves configuration to kit-config.json
- getConfig: Retrieves cached configuration
- validateOptions: Validates user configuration
- processTemplateVariables: Processes variables in templates
CliService
- Standardized user interface management:
- showBanner: Displays the application banner
- startProgress / updateProgress / completeProgress: Manages progress bars
- info / success / warning / error: Standardized message output
- User input functions:
- askStack: Prompts for stack selection
- askArchitecture: Prompts for architecture selection
- askVersion: Prompts for version selection
- askProjectPath: Prompts for project location
- askDirectoryAction: Asks what to do with existing rules
StackService
Handles stack-agnostic operations:
- detectStackVersion: Detects framework version from project files
- getAvailableStacks: Lists available stacks from configuration
- getAvailableArchitectures: Lists architectures for a stack
- getAvailableVersions: Lists versions for a stack
- mapVersionToRange: Maps specific versions to version ranges
- generateRulesAsync: Orchestrates rule generation
- countTotalRules: Counts total rules for progress tracking
- createBackupAsync: Creates backup of existing rules
Stack-specific Services
LaravelService
- copyArchitectureRules: Copies specific architecture rules (Standard, DDD, Hexagonal)
- copyVersionOverlay: Applies version-specific rules (v8-9, v10-11, v12)
- copyBaseRules: Copies base rules
NextjsService
- copyArchitectureRules: Manages App/Pages router architecture rules
- copyVersionOverlay: Applies version-specific rules (v12, v13, v14)
- copyBaseRules: Copies base rules
ReactService
- copyArchitectureRules: Copies architecture rules (Standard, Atomic, Feature-Sliced)
- copyStateManagementRules: Copies state management rules (Redux, Context, Zustand, etc.)
- copyBaseRules: Copies base rules
AngularService
- copyBaseRules: Copies Angular base rules
- copySignalsRules: Copies rules for Angular Signals (optional)
- copyVersionOverlay: Applies version-specific rules (v14-15, v16-17)
McpService (New in v2.0)
Manages Model Context Protocol (MCP) tools integration:
- getAvailableMcpTools: Retrieves available MCP tools from configuration
- copyMcpToolsRules: Copies selected MCP tool rules with batch processing
- countMcpToolsRules: Counts files to be generated for selected tools
- validateMcpTools: Validates selected tools against available options
- pathExistsAsync: Utility for asynchronous path existence checking
- ensureDirectoryExistsAsync: Utility for asynchronous directory creation
MCP Tools Integration
The McpService enables the new MCP tools functionality:
// Multi-tool selection and processing
const selectedMcpTools = ['pampa', 'github', 'memory'];
const mcpCount = await mcpService.copyMcpToolsRules(
rulesDir,
selectedMcpTools,
meta,
config
);
Key features:
- Multi-select interface: Users can choose multiple MCP tools
- Batch processing: Efficient handling of multiple tool rule sets
- Validation: Ensures selected tools are available and valid
- Separation of concerns: MCP rules are independent from stack rules
- Asynchronous operations: Non-blocking file operations for better performance
Architecture Implementation
Dynamic Service Loading
Services are loaded on-demand to improve startup time and memory usage:
async function loadStackService(stack) {
// Return from cache if already loaded
if (stackServices.has(stack)) {
return stackServices.get(stack);
}
// Dynamically import the required service
const servicePath = `./services/${stack}-service.js`;
const serviceModule = await import(servicePath);
// Get the service class
const ServiceClass =
serviceModule[
`${stack.charAt(0).toUpperCase() + stack.slice(1)}Service`
];
// Instantiate the service
const serviceInstance = new ServiceClass({
debug: debugMode,
fileService,
configService,
templatesDir,
stackService,
});
// Cache the instance
stackServices.set(stack, serviceInstance);
return serviceInstance;
}
Batched Processing
For memory efficiency, file processing is done in batches:
async processBatch(items, processFn, batchSize = 10) {
const results = [];
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
const batchResults = await Promise.all(
batch.map(item => processFn(item))
);
results.push(...batchResults);
// Allow event loop to handle other tasks
if (i + batchSize < items.length) {
await new Promise(resolve => setTimeout(resolve, 0));
}
}
return results;
}
Template Caching
Templates are cached to avoid repeated file reads:
class TemplateCache {
constructor(options = {}) {
this.cache = new Map();
this.maxSize = options.maxSize || 100;
this.ttl = options.ttl || 300000; // 5 minutes
}
get(key) {
const item = this.cache.get(key);
if (item && Date.now() < item.expiry) {
return item.value;
}
return null;
}
set(key, value) {
// Implement cache eviction if needed
if (this.cache.size >= this.maxSize) {
this.evictOldest();
}
this.cache.set(key, {
value,
expiry: Date.now() + this.ttl,
});
}
// ...other cache methods
}
How to extend the architecture
Adding a new stack
- Create a new service class that extends BaseService
- Implement specific methods like copyBaseRules
- Register the new service in the main code
Example:
export class SvelteService extends BaseService {
constructor(options = {}) {
super(options);
this.fileService = options.fileService;
this.configService = options.configService;
}
copyBaseRules(targetRules, versionMeta, options) {
// Svelte-specific implementation
}
// Other specific methods...
}
Adding new functionality to an existing stack
To extend an existing service, simply add new methods to the corresponding class:
// In ReactService:
copyPerformanceRules(targetRules, options = {}) {
// Implementation for performance rules
}
Adding new architectures to a stack
- Add the architecture to kit-config.json
- Create architecture-specific rules in templates/stacks/[stack]/architectures/[new-architecture]/
- Update the stack's service to handle the new architecture
Design patterns used
- Composite Pattern: Stack services compose FileService functionality
- Singleton: ConfigService maintains a single configuration instance
- Strategy: Different stack-specific implementations
- Factory: Centralized service creation
- Adapter: Stack services adapt common patterns to framework-specific implementations
- Observer: Progress reporting through callbacks
Typical execution flow
- User starts the application with
npx agent-rules-kit - Essential services are initialized (BaseService, ConfigService, FileService, CliService, StackService)
- CliService collects user inputs (stack, project path, architecture, etc.)
- Appropriate stack service is dynamically loaded
- Additional stack-specific options are collected
- Template processing begins with progress tracking
- Generated rules are written to the .cursor/rules directory
- Success message is displayed with summary
Advantages of the new architecture
- Higher cohesion: Each service has well-defined responsibilities
- Lower coupling: Services communicate through clear interfaces
- Extensibility: Easy addition of new stacks or functionalities
- Testability: Service classes are easier to test in isolation
- Maintainability: More organized and predictable code
- Performance: Dynamic loading, caching, and batch processing
- Reliability: Better error handling and backup systems