agent-rules-kit
Version:
Bootstrap of **Cursor** rules (`.mdc`) and mirror documentation (`.md`) for AI agent-guided projects.
318 lines (258 loc) • 12.8 kB
JavaScript
/**
* Angular Service for Agent Rules Kit
* Handles operations specific to the Angular stack
*/
import chalk from 'chalk';
import fs from 'fs-extra';
import path from 'path';
import { BaseService } from '../base-service.js';
/**
* Service specific to the Angular stack
*/
export class AngularService extends BaseService {
constructor(options = {}) {
super(options);
this.fileService = options.fileService;
this.configService = options.configService;
this.templatesDir = options.templatesDir;
}
/**
* Detect version of Angular from package.json
* @param {string} projectPath - Path to the project root
* @returns {string|null} - Detected version or null if not found
*/
detectAngularVersion(projectPath) {
try {
const packagePath = path.join(projectPath, 'package.json');
this.debugLog(`Looking for package.json at: ${packagePath}`);
if (!fs.existsSync(packagePath)) {
this.debugLog('package.json not found');
return null;
}
const packageContent = fs.readFileSync(packagePath, 'utf8');
const pkg = JSON.parse(packageContent);
this.debugLog(`Found package.json with content length: ${packageContent.length}`);
if (!pkg.dependencies || !pkg.dependencies['@angular/core']) {
this.debugLog('Angular not found in package.json dependencies');
return null;
}
const versionStr = pkg.dependencies['@angular/core'];
this.debugLog(`Found Angular version: ${versionStr}`);
// Extract major version number
const match = versionStr.match(/\d+/);
if (match) {
const version = parseInt(match[0], 10);
this.debugLog(`Detected Angular version: ${version}`);
return version.toString();
}
} catch (error) {
this.debugLog(`Error detecting Angular version: ${error.message}`);
}
return null;
}
/**
* Copies specific architecture rules for Angular
* @param {string} architecture - Architecture name (standard, modular, etc.)
* @param {string} targetRules - Target rules directory
* @param {Object} options - Additional options
*/
copyArchitectureRules(architecture, targetRules, options = {}) {
if (!architecture) return;
const templatesDir = options.templatesDir || this.templatesDir;
const archDir = path.join(templatesDir, 'stacks/angular/architectures', architecture);
this.debugLog(`Looking for Angular architecture rules: ${architecture}`);
if (!this.fileService.directoryExists(archDir)) {
this.debugLog(`Angular architecture directory not found: ${archDir}`);
return;
}
const files = this.fileService.getFilesInDirectory(archDir);
this.debugLog(`Found ${files.length} architecture files for Angular ${architecture}`);
// Create stack directory if it doesn't exist
const stackFolder = path.join(targetRules, 'angular');
this.fileService.ensureDirectoryExists(stackFolder);
// Get kit configuration for rule metadata
const kitConfig = this.configService.loadKitConfig(templatesDir);
files.forEach(f => {
const srcFile = path.join(archDir, f);
// Use prefix to indicate architecture
const fileName = `architecture-${architecture}-${f}`.replace(/\.md$/, '.mdc');
const destFile = path.join(stackFolder, fileName);
// Custom metadata for each file
const meta = {
stack: 'angular',
architecture: architecture,
projectPath: options.projectPath || '.',
detectedVersion: options.detectedVersion,
versionRange: options.versionRange,
debug: options.debug || this.debug
};
this.fileService.wrapMdToMdc(srcFile, destFile, meta, kitConfig);
this.debugLog(`Processed Angular architecture file: ${f}`);
});
console.log(`${chalk.green('✅')} Applied ${chalk.magenta(architecture)} architecture rules for Angular`);
}
/**
* Copies testing rules for Angular
* @param {string} targetRules - Target rules directory
* @param {Object} options - Additional options
*/
copyTestingRules(targetRules, options = {}) {
const templatesDir = options.templatesDir || this.templatesDir;
const testingDir = path.join(templatesDir, 'stacks/angular/testing');
this.debugLog(`Looking for testing rules for Angular`);
if (!this.fileService.directoryExists(testingDir)) {
this.debugLog(`Angular testing directory not found: ${testingDir}`);
return;
}
const files = this.fileService.getFilesInDirectory(testingDir);
this.debugLog(`Found ${files.length} testing files for Angular`);
// Create stack directory if it doesn't exist
const stackFolder = path.join(targetRules, 'angular');
this.fileService.ensureDirectoryExists(stackFolder);
// Get kit configuration for rule metadata
const kitConfig = this.configService.loadKitConfig(templatesDir);
files.forEach(f => {
const srcFile = path.join(testingDir, f);
// Use prefix to indicate that it's a testing rule
const fileName = `testing-${f}`.replace(/\.md$/, '.mdc');
const destFile = path.join(stackFolder, fileName);
// Custom metadata for each file
const meta = {
stack: 'angular',
testing: true,
projectPath: options.projectPath || '.',
detectedVersion: options.detectedVersion,
versionRange: options.versionRange,
debug: options.debug || this.debug
};
this.fileService.wrapMdToMdc(srcFile, destFile, meta, kitConfig);
this.debugLog(`Processed Angular testing file: ${f}`);
});
console.log(`${chalk.green('✅')} Applied testing rules for Angular`);
}
/**
* Copies Angular signals rules
* @param {string} targetRules - Target rules directory
* @param {Object} options - Additional options
*/
copySignalsRules(targetRules, options = {}) {
const templatesDir = options.templatesDir || this.templatesDir;
const signalsDir = path.join(templatesDir, 'stacks/angular/signals');
this.debugLog(`Looking for signals rules for Angular`);
if (!this.fileService.directoryExists(signalsDir)) {
this.debugLog(`Angular signals directory not found: ${signalsDir}`);
return;
}
const files = this.fileService.getFilesInDirectory(signalsDir);
this.debugLog(`Found ${files.length} signals files for Angular`);
// Create stack directory if it doesn't exist
const stackFolder = path.join(targetRules, 'angular');
this.fileService.ensureDirectoryExists(stackFolder);
// Get kit configuration for rule metadata
const kitConfig = this.configService.loadKitConfig(templatesDir);
files.forEach(f => {
const srcFile = path.join(signalsDir, f);
// Use prefix to indicate that it's a signals rule
const fileName = `signals-${f}`.replace(/\.md$/, '.mdc');
const destFile = path.join(stackFolder, fileName);
// Custom metadata for each file
const meta = {
stack: 'angular',
signals: true,
projectPath: options.projectPath || '.',
detectedVersion: options.detectedVersion,
versionRange: options.versionRange,
debug: options.debug || this.debug
};
this.fileService.wrapMdToMdc(srcFile, destFile, meta, kitConfig);
this.debugLog(`Processed Angular signals file: ${f}`);
});
console.log(`${chalk.green('✅')} Applied signals rules for Angular`);
}
/**
* Copies version-specific overlay rules for Angular
* @param {string} targetRules - Target rules directory
* @param {Object} versionMeta - Version metadata
* @param {Object} options - Additional options
*/
copyVersionOverlay(targetRules, versionMeta, options = {}) {
if (!versionMeta || !versionMeta.versionRange) {
this.debugLog('No version range specified for Angular, skipping version overlay');
return false;
}
const templatesDir = options.templatesDir || this.templatesDir;
const versionFolder = path.join(templatesDir, 'stacks/angular', versionMeta.versionRange);
this.debugLog(`Looking for Angular version-specific rules: ${versionMeta.versionRange}`);
if (!this.fileService.directoryExists(versionFolder)) {
this.debugLog(`Angular version directory not found: ${versionFolder}`);
return false;
}
const files = this.fileService.getFilesInDirectory(versionFolder);
this.debugLog(`Found ${files.length} version-specific files for Angular ${versionMeta.versionRange}`);
// Create stack directory if it doesn't exist
const stackFolder = path.join(targetRules, 'angular');
this.fileService.ensureDirectoryExists(stackFolder);
// Get kit configuration for rule metadata
const kitConfig = this.configService.loadKitConfig(templatesDir);
files.forEach(f => {
const srcFile = path.join(versionFolder, f);
// Use prefix to indicate version
const fileName = `version-${versionMeta.detectedVersion}-${f}`.replace(/\.md$/, '.mdc');
const destFile = path.join(stackFolder, fileName);
// Custom metadata for each file
const meta = {
stack: 'angular',
versionSpecific: true,
version: versionMeta.detectedVersion,
versionRange: versionMeta.versionRange,
projectPath: options.projectPath || '.',
debug: options.debug || this.debug
};
this.fileService.wrapMdToMdc(srcFile, destFile, meta, kitConfig);
this.debugLog(`Processed Angular version-specific file: ${f}`);
});
console.log(`${chalk.green('✅')} Applied version-specific rules for ${chalk.cyan('Angular')} ${chalk.yellow(versionMeta.versionRange)}`);
return true;
}
/**
* Copies Angular base rules
* @param {string} targetRules - Target rules directory
* @param {Object} versionMeta - Version metadata
* @param {Object} options - Additional options
*/
copyBaseRules(targetRules, versionMeta = {}, options = {}) {
const templatesDir = options.templatesDir || this.templatesDir;
const baseDir = path.join(templatesDir, 'stacks/angular/base');
this.debugLog(`Looking for base rules in: ${baseDir}`);
if (!this.fileService.directoryExists(baseDir)) {
console.error(chalk.red(`❌ Base directory not found: ${baseDir}`));
return false;
}
this.debugLog(`Found base directory with ${this.fileService.getFilesInDirectory(baseDir).length} files`);
// Get kit configuration for rule metadata
const kitConfig = this.configService.loadKitConfig(templatesDir);
// Iterate over base files and add project path and version info
const baseFiles = this.fileService.getFilesInDirectory(baseDir);
this.debugLog(`Processing ${baseFiles.length} base files for angular`);
// Create stack directory if it doesn't exist
const stackFolder = path.join(targetRules, 'angular');
this.fileService.ensureDirectoryExists(stackFolder);
baseFiles.forEach(f => {
const srcFile = path.join(baseDir, f);
// Store in stack folder with original name
const fileName = `${f}`.replace(/\.md$/, '.mdc');
const destFile = path.join(stackFolder, fileName);
this.debugLog(`Copying ${f} to ${fileName}`);
// Custom metadata for each file
const fileMeta = {
...versionMeta,
stack: 'angular',
projectPath: options.projectPath || '.',
debug: options.debug || this.debug
};
this.fileService.wrapMdToMdc(srcFile, destFile, fileMeta, kitConfig);
});
console.log(`${chalk.green('✅')} Copied base rules for ${chalk.cyan('angular')}`);
return true;
}
}