mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-13 21:33:32 +08:00
fix: prefer cursor native hooks during install
This commit is contained in:
@@ -29,6 +29,7 @@ module.exports = createInstallTargetAdapter({
|
||||
const modules = Array.isArray(input.modules)
|
||||
? input.modules
|
||||
: (input.module ? [input.module] : []);
|
||||
const seenDestinationPaths = new Set();
|
||||
const {
|
||||
repoRoot,
|
||||
projectRoot,
|
||||
@@ -40,51 +41,98 @@ module.exports = createInstallTargetAdapter({
|
||||
homeDir,
|
||||
};
|
||||
const targetRoot = adapter.resolveRoot(planningInput);
|
||||
|
||||
return modules.flatMap(module => {
|
||||
const entries = modules.flatMap((module, moduleIndex) => {
|
||||
const paths = Array.isArray(module.paths) ? module.paths : [];
|
||||
return paths
|
||||
.filter(p => !isForeignPlatformPath(p, adapter.target))
|
||||
.flatMap(sourceRelativePath => {
|
||||
if (sourceRelativePath === 'rules') {
|
||||
return createFlatRuleOperations({
|
||||
moduleId: module.id,
|
||||
repoRoot,
|
||||
sourceRelativePath,
|
||||
destinationDir: path.join(targetRoot, 'rules'),
|
||||
destinationNameTransform: toCursorRuleFileName,
|
||||
});
|
||||
}
|
||||
.map((sourceRelativePath, pathIndex) => ({
|
||||
module,
|
||||
sourceRelativePath,
|
||||
moduleIndex,
|
||||
pathIndex,
|
||||
}));
|
||||
}).sort((left, right) => {
|
||||
const getPriority = value => {
|
||||
if (value === 'rules') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sourceRelativePath === '.cursor') {
|
||||
const cursorRoot = path.join(repoRoot, '.cursor');
|
||||
if (!fs.existsSync(cursorRoot) || !fs.statSync(cursorRoot).isDirectory()) {
|
||||
return [];
|
||||
}
|
||||
if (value === '.cursor') {
|
||||
return 1;
|
||||
}
|
||||
|
||||
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',
|
||||
}));
|
||||
return 2;
|
||||
};
|
||||
|
||||
const ruleOperations = createFlatRuleOperations({
|
||||
moduleId: module.id,
|
||||
repoRoot,
|
||||
sourceRelativePath: '.cursor/rules',
|
||||
destinationDir: path.join(targetRoot, 'rules'),
|
||||
destinationNameTransform: toCursorRuleFileName,
|
||||
});
|
||||
const leftPriority = getPriority(left.sourceRelativePath);
|
||||
const rightPriority = getPriority(right.sourceRelativePath);
|
||||
if (leftPriority !== rightPriority) {
|
||||
return leftPriority - rightPriority;
|
||||
}
|
||||
|
||||
return [...childOperations, ...ruleOperations];
|
||||
}
|
||||
if (left.moduleIndex !== right.moduleIndex) {
|
||||
return left.moduleIndex - right.moduleIndex;
|
||||
}
|
||||
|
||||
return [adapter.createScaffoldOperation(module.id, sourceRelativePath, planningInput)];
|
||||
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),
|
||||
]);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -124,11 +124,11 @@ function runTests() {
|
||||
);
|
||||
assert.ok(
|
||||
plan.operations.some(operation => (
|
||||
operation.sourceRelativePath === '.cursor/rules/common-agents.md'
|
||||
operation.sourceRelativePath === 'rules/common/agents.md'
|
||||
&& operation.destinationPath === path.join(projectRoot, '.cursor', 'rules', 'common-agents.mdc')
|
||||
&& operation.strategy === 'flatten-copy'
|
||||
)),
|
||||
'Should flatten Cursor platform rules into .mdc files'
|
||||
'Should produce Cursor .mdc rules while preferring rules-core over duplicate platform copies'
|
||||
);
|
||||
})) passed++; else failed++;
|
||||
|
||||
|
||||
@@ -209,6 +209,70 @@ function runTests() {
|
||||
);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('deduplicates cursor rule destinations when rules-core and platform-configs overlap', () => {
|
||||
const repoRoot = path.join(__dirname, '..', '..');
|
||||
const projectRoot = '/workspace/app';
|
||||
|
||||
const plan = planInstallTargetScaffold({
|
||||
target: 'cursor',
|
||||
repoRoot,
|
||||
projectRoot,
|
||||
modules: [
|
||||
{
|
||||
id: 'rules-core',
|
||||
paths: ['rules'],
|
||||
},
|
||||
{
|
||||
id: 'platform-configs',
|
||||
paths: ['.cursor'],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const commonAgentsDestinations = plan.operations.filter(operation => (
|
||||
operation.destinationPath === path.join(projectRoot, '.cursor', 'rules', 'common-agents.mdc')
|
||||
));
|
||||
|
||||
assert.strictEqual(commonAgentsDestinations.length, 1, 'Should keep only one common-agents.mdc operation');
|
||||
assert.strictEqual(
|
||||
normalizedRelativePath(commonAgentsDestinations[0].sourceRelativePath),
|
||||
'rules/common/agents.md',
|
||||
'Should prefer rules-core when cursor platform rules would collide'
|
||||
);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('prefers native cursor hooks when hooks-runtime and platform-configs overlap', () => {
|
||||
const repoRoot = path.join(__dirname, '..', '..');
|
||||
const projectRoot = '/workspace/app';
|
||||
|
||||
const plan = planInstallTargetScaffold({
|
||||
target: 'cursor',
|
||||
repoRoot,
|
||||
projectRoot,
|
||||
modules: [
|
||||
{
|
||||
id: 'hooks-runtime',
|
||||
paths: ['hooks', 'scripts/hooks', 'scripts/lib'],
|
||||
},
|
||||
{
|
||||
id: 'platform-configs',
|
||||
paths: ['.cursor'],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const hooksDestinations = plan.operations.filter(operation => (
|
||||
operation.destinationPath === path.join(projectRoot, '.cursor', 'hooks')
|
||||
));
|
||||
|
||||
assert.strictEqual(hooksDestinations.length, 1, 'Should keep only one .cursor/hooks scaffold operation');
|
||||
assert.strictEqual(
|
||||
normalizedRelativePath(hooksDestinations[0].sourceRelativePath),
|
||||
'.cursor/hooks',
|
||||
'Should prefer native Cursor hooks over generic hooks-runtime hooks'
|
||||
);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('plans antigravity remaps for workflows, skills, and flat rules', () => {
|
||||
const repoRoot = path.join(__dirname, '..', '..');
|
||||
const projectRoot = '/workspace/app';
|
||||
|
||||
Reference in New Issue
Block a user