fix(#2644): deduplicate Cursor slash menu (#2812)

* fix(#2644): deduplicate Cursor slash menu

* fix: preserve installer executable mode

* chore: add changeset for PR #2812

* test(#2644): acknowledge Cursor emission changes

* test(#2644): drop spent emitted drift acknowledgments

* fix(#2644): remove retired Cursor command converter

---------

Co-authored-by: clezcoding <clezcoding@users.noreply.github.com>
Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
This commit is contained in:
𝚌𝚕𝚎𝚣𝚌𝚘𝚍𝚒𝚗𝚐
2026-08-03 18:05:45 +02:00
committed by GitHub
parent 067a4d1c6c
commit 88f6d9bd1b
29 changed files with 727 additions and 360 deletions

View File

@@ -65,11 +65,11 @@ describe('resolveRuntimeArtifactLayout — claude global', () => {
});
describe('resolveRuntimeArtifactLayout — cursor', () => {
test('returns correct layout for cursor — skills + commands + agents kinds (#785, ADR-1235)', () => {
test('returns correct layout for cursor — skills + agents only (#2644)', () => {
const layout = resolveRuntimeArtifactLayout('cursor', FAKE_DIR);
assert.strictEqual(layout.runtime, 'cursor');
assert.strictEqual(layout.configDir, FAKE_DIR);
assert.strictEqual(layout.kinds.length, 3);
assert.strictEqual(layout.kinds.length, 2);
const skillsKind = layout.kinds.find(k => k.kind === 'skills');
assert.ok(skillsKind, 'must have a skills kind');
@@ -77,11 +77,8 @@ describe('resolveRuntimeArtifactLayout — cursor', () => {
assert.strictEqual(skillsKind.prefix, 'gsd-');
assert.strictEqual(typeof skillsKind.stage, 'function');
const commandsKind = layout.kinds.find(k => k.kind === 'commands');
assert.ok(commandsKind, 'must have a commands kind (#785 Cursor 1.6 slash commands)');
assert.strictEqual(commandsKind.destSubpath, 'commands');
assert.strictEqual(commandsKind.prefix, 'gsd-');
assert.strictEqual(typeof commandsKind.stage, 'function');
assert.equal(layout.kinds.find(k => k.kind === 'commands'), undefined,
'Cursor skills are the sole slash-menu surface; commands would duplicate them (#2644)');
const agentsKind = layout.kinds.find(k => k.kind === 'agents');
assert.ok(agentsKind, 'must have an agents kind (ADR-1235 §1 descriptor cutover)');
@@ -416,11 +413,11 @@ describe('resolveRuntimeArtifactLayout edge-cases', () => {
assert.ok(kindNames.includes('agents'), 'should have agents kind');
});
test('cursor has both skills and commands kinds (#785)', () => {
test('cursor has a skills kind and no commands kind (#2644)', () => {
const layout = resolveRuntimeArtifactLayout('cursor', '/tmp/x');
const kindNames = layout.kinds.map(k => k.kind);
assert.ok(kindNames.includes('skills'), 'cursor must have skills kind');
assert.ok(kindNames.includes('commands'), 'cursor must have commands kind (#785 Cursor 1.6)');
assert.ok(!kindNames.includes('commands'), 'cursor commands kind would duplicate skill menu entries');
});
test('claude global has only skills kind', () => {
@@ -657,39 +654,11 @@ describe('stage — opencode/kilo skills kind (#784)', () => {
}
});
describe('stage — cursor commands kind (#785)', () => {
test('cursor commands kind stage returns directory with converted .md files', () => {
describe('stage — cursor retired commands kind (#2644)', () => {
test('cursor layout exposes no commands staging surface', () => {
const layout = resolveRuntimeArtifactLayout('cursor', FAKE_STAGE_DIR);
const commandsKind = layout.kinds.find(k => k.kind === 'commands');
assert.ok(commandsKind, 'cursor should have a commands kind (#785)');
const stagedDir = commandsKind.stage(PROFILE_CORE);
assert.ok(fs.existsSync(stagedDir), 'stagedDir must exist');
const entries = fs.readdirSync(stagedDir).filter(f => f.endsWith('.md'));
assert.ok(entries.length >= 1, 'at least one command file should be staged');
// Cursor commands are plain markdown — no YAML frontmatter
for (const entry of entries) {
const content = fs.readFileSync(path.join(stagedDir, entry), 'utf8');
assert.ok(!content.startsWith('---'), `${entry}: cursor commands must not start with YAML frontmatter`);
}
});
test('cursor commands stage applies Cursor-specific content transforms', () => {
const layout = resolveRuntimeArtifactLayout('cursor', FAKE_STAGE_DIR);
const commandsKind = layout.kinds.find(k => k.kind === 'commands');
assert.ok(commandsKind, 'cursor should have a commands kind (#785)');
const stagedDir = commandsKind.stage(PROFILE_FULL);
assert.ok(fs.existsSync(stagedDir), 'stagedDir must exist');
// Verify all staged files are .md only (no subdirectory SKILL.md layout)
const entries = fs.readdirSync(stagedDir, { withFileTypes: true });
for (const entry of entries) {
assert.ok(entry.isFile(), `${entry.name}: cursor commands dir must contain only flat files`);
assert.ok(entry.name.endsWith('.md'), `${entry.name}: must be .md file`);
}
assert.equal(commandsKind, undefined);
});
});