const fs = require('fs'); const path = require('path'); const { createFlatRuleOperations, createInstallTargetAdapter, createManagedOperation, isForeignPlatformPath, } = require('./helpers'); function toCursorRuleFileName(fileName, sourceRelativeFile) { if (path.basename(sourceRelativeFile).toLowerCase() === 'readme.md') { return null; } return fileName.endsWith('.md') ? `${fileName.slice(0, -3)}.mdc` : fileName; } module.exports = createInstallTargetAdapter({ id: 'cursor-project', target: 'cursor', kind: 'project', rootSegments: ['.cursor'], installStatePathSegments: ['ecc-install-state.json'], nativeRootRelativePath: '.cursor', planOperations(input, adapter) { const modules = Array.isArray(input.modules) ? input.modules : (input.module ? [input.module] : []); const seenDestinationPaths = new Set(); const { repoRoot, projectRoot, homeDir, } = input; const planningInput = { repoRoot, projectRoot, homeDir, }; const targetRoot = adapter.resolveRoot(planningInput); const entries = modules.flatMap((module, moduleIndex) => { const paths = Array.isArray(module.paths) ? module.paths : []; return paths .filter(p => !isForeignPlatformPath(p, adapter.target)) .map((sourceRelativePath, pathIndex) => ({ module, sourceRelativePath, moduleIndex, pathIndex, })); }).sort((left, right) => { const getPriority = value => { if (value === 'rules') { return 0; } if (value === '.cursor') { return 1; } return 2; }; const leftPriority = getPriority(left.sourceRelativePath); const rightPriority = getPriority(right.sourceRelativePath); if (leftPriority !== rightPriority) { return leftPriority - rightPriority; } if (left.moduleIndex !== right.moduleIndex) { return left.moduleIndex - right.moduleIndex; } return left.pathIndex - right.pathIndex; }); function takeUniqueOperations(operations) { return operations.filter(operation => { if (!operation || !operation.destinationPath) { return false; } if (seenDestinationPaths.has(operation.destinationPath)) { return false; } seenDestinationPaths.add(operation.destinationPath); return true; }); } return entries.flatMap(({ module, sourceRelativePath }) => { if (sourceRelativePath === 'rules') { return takeUniqueOperations(createFlatRuleOperations({ moduleId: module.id, repoRoot, sourceRelativePath, destinationDir: path.join(targetRoot, 'rules'), destinationNameTransform: toCursorRuleFileName, })); } if (sourceRelativePath === '.cursor') { const cursorRoot = path.join(repoRoot, '.cursor'); if (!fs.existsSync(cursorRoot) || !fs.statSync(cursorRoot).isDirectory()) { return []; } const childOperations = fs.readdirSync(cursorRoot, { withFileTypes: true }) .sort((left, right) => left.name.localeCompare(right.name)) .filter(entry => entry.name !== 'rules') .map(entry => createManagedOperation({ moduleId: module.id, sourceRelativePath: path.join('.cursor', entry.name), destinationPath: path.join(targetRoot, entry.name), strategy: 'preserve-relative-path', })); const ruleOperations = createFlatRuleOperations({ moduleId: module.id, repoRoot, sourceRelativePath: '.cursor/rules', destinationDir: path.join(targetRoot, 'rules'), destinationNameTransform: toCursorRuleFileName, }); return takeUniqueOperations([...childOperations, ...ruleOperations]); } return takeUniqueOperations([ adapter.createScaffoldOperation(module.id, sourceRelativePath, planningInput), ]); }); }, });