fix: install Cursor rules as mdc files

This commit is contained in:
Affaan Mustafa
2026-04-12 23:32:39 -07:00
parent 9da8e5f6ac
commit 133e881ce0
4 changed files with 33 additions and 10 deletions

View File

@@ -40,6 +40,11 @@ module.exports = createInstallTargetAdapter({
repoRoot,
sourceRelativePath,
destinationDir: path.join(targetRoot, 'rules'),
destinationNameTransform(fileName) {
return fileName.endsWith('.md')
? `${fileName.slice(0, -3)}.mdc`
: fileName;
},
});
}

View File

@@ -181,7 +181,13 @@ function createNamespacedFlatRuleOperations(adapter, moduleId, sourceRelativePat
return operations;
}
function createFlatRuleOperations({ moduleId, repoRoot, sourceRelativePath, destinationDir }) {
function createFlatRuleOperations({
moduleId,
repoRoot,
sourceRelativePath,
destinationDir,
destinationNameTransform,
}) {
const normalizedSourcePath = normalizeRelativePath(sourceRelativePath);
const sourceRoot = path.join(repoRoot || '', normalizedSourcePath);
@@ -201,7 +207,10 @@ function createFlatRuleOperations({ moduleId, repoRoot, sourceRelativePath, dest
if (entry.isDirectory()) {
const relativeFiles = listRelativeFiles(entryPath);
for (const relativeFile of relativeFiles) {
const flattenedFileName = `${namespace}-${normalizeRelativePath(relativeFile).replace(/\//g, '-')}`;
const defaultFileName = `${namespace}-${normalizeRelativePath(relativeFile).replace(/\//g, '-')}`;
const flattenedFileName = typeof destinationNameTransform === 'function'
? destinationNameTransform(defaultFileName)
: defaultFileName;
operations.push(createManagedOperation({
moduleId,
sourceRelativePath: path.join(normalizedSourcePath, namespace, relativeFile),
@@ -210,10 +219,13 @@ function createFlatRuleOperations({ moduleId, repoRoot, sourceRelativePath, dest
}));
}
} else if (entry.isFile()) {
const destinationFileName = typeof destinationNameTransform === 'function'
? destinationNameTransform(entry.name)
: entry.name;
operations.push(createManagedOperation({
moduleId,
sourceRelativePath: path.join(normalizedSourcePath, entry.name),
destinationPath: path.join(destinationDir, entry.name),
destinationPath: path.join(destinationDir, destinationFileName),
strategy: 'flatten-copy',
}));
}

View File

@@ -103,7 +103,7 @@ function runTests() {
assert.strictEqual(preserved.strategy, 'flatten-copy');
assert.strictEqual(
preserved.destinationPath,
path.join(projectRoot, '.cursor', 'rules', 'common-coding-style.md')
path.join(projectRoot, '.cursor', 'rules', 'common-coding-style.mdc')
);
})) passed++; else failed++;
@@ -126,16 +126,16 @@ function runTests() {
assert.ok(
plan.operations.some(operation => (
normalizedRelativePath(operation.sourceRelativePath) === 'rules/common/coding-style.md'
&& operation.destinationPath === path.join(projectRoot, '.cursor', 'rules', 'common-coding-style.md')
&& operation.destinationPath === path.join(projectRoot, '.cursor', 'rules', 'common-coding-style.mdc')
)),
'Should flatten common rules into namespaced files'
'Should flatten common rules into namespaced .mdc files'
);
assert.ok(
plan.operations.some(operation => (
normalizedRelativePath(operation.sourceRelativePath) === 'rules/typescript/testing.md'
&& operation.destinationPath === path.join(projectRoot, '.cursor', 'rules', 'typescript-testing.md')
&& operation.destinationPath === path.join(projectRoot, '.cursor', 'rules', 'typescript-testing.mdc')
)),
'Should flatten language rules into namespaced files'
'Should flatten language rules into namespaced .mdc files'
);
assert.ok(
!plan.operations.some(operation => (
@@ -143,6 +143,12 @@ function runTests() {
)),
'Should not preserve nested rule directories for cursor installs'
);
assert.ok(
!plan.operations.some(operation => (
operation.destinationPath === path.join(projectRoot, '.cursor', 'rules', 'common-coding-style.md')
)),
'Should not emit .md Cursor rule files'
);
})) passed++; else failed++;
if (test('plans antigravity remaps for workflows, skills, and flat rules', () => {

View File

@@ -130,8 +130,8 @@ function runTests() {
const result = run(['--target', 'cursor', 'typescript'], { cwd: projectDir, homeDir });
assert.strictEqual(result.code, 0, result.stderr);
assert.ok(fs.existsSync(path.join(projectDir, '.cursor', 'rules', 'common-coding-style.md')));
assert.ok(fs.existsSync(path.join(projectDir, '.cursor', 'rules', 'typescript-testing.md')));
assert.ok(fs.existsSync(path.join(projectDir, '.cursor', 'rules', 'common-coding-style.mdc')));
assert.ok(fs.existsSync(path.join(projectDir, '.cursor', 'rules', 'typescript-testing.mdc')));
assert.ok(fs.existsSync(path.join(projectDir, '.cursor', 'agents', 'architect.md')));
assert.ok(fs.existsSync(path.join(projectDir, '.cursor', 'commands', 'plan.md')));
assert.ok(fs.existsSync(path.join(projectDir, '.cursor', 'hooks.json')));