mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-14 05:43:29 +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)
|
const modules = Array.isArray(input.modules)
|
||||||
? input.modules
|
? input.modules
|
||||||
: (input.module ? [input.module] : []);
|
: (input.module ? [input.module] : []);
|
||||||
|
const seenDestinationPaths = new Set();
|
||||||
const {
|
const {
|
||||||
repoRoot,
|
repoRoot,
|
||||||
projectRoot,
|
projectRoot,
|
||||||
@@ -40,20 +41,66 @@ module.exports = createInstallTargetAdapter({
|
|||||||
homeDir,
|
homeDir,
|
||||||
};
|
};
|
||||||
const targetRoot = adapter.resolveRoot(planningInput);
|
const targetRoot = adapter.resolveRoot(planningInput);
|
||||||
|
const entries = modules.flatMap((module, moduleIndex) => {
|
||||||
return modules.flatMap(module => {
|
|
||||||
const paths = Array.isArray(module.paths) ? module.paths : [];
|
const paths = Array.isArray(module.paths) ? module.paths : [];
|
||||||
return paths
|
return paths
|
||||||
.filter(p => !isForeignPlatformPath(p, adapter.target))
|
.filter(p => !isForeignPlatformPath(p, adapter.target))
|
||||||
.flatMap(sourceRelativePath => {
|
.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') {
|
if (sourceRelativePath === 'rules') {
|
||||||
return createFlatRuleOperations({
|
return takeUniqueOperations(createFlatRuleOperations({
|
||||||
moduleId: module.id,
|
moduleId: module.id,
|
||||||
repoRoot,
|
repoRoot,
|
||||||
sourceRelativePath,
|
sourceRelativePath,
|
||||||
destinationDir: path.join(targetRoot, 'rules'),
|
destinationDir: path.join(targetRoot, 'rules'),
|
||||||
destinationNameTransform: toCursorRuleFileName,
|
destinationNameTransform: toCursorRuleFileName,
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sourceRelativePath === '.cursor') {
|
if (sourceRelativePath === '.cursor') {
|
||||||
@@ -80,11 +127,12 @@ module.exports = createInstallTargetAdapter({
|
|||||||
destinationNameTransform: toCursorRuleFileName,
|
destinationNameTransform: toCursorRuleFileName,
|
||||||
});
|
});
|
||||||
|
|
||||||
return [...childOperations, ...ruleOperations];
|
return takeUniqueOperations([...childOperations, ...ruleOperations]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return [adapter.createScaffoldOperation(module.id, sourceRelativePath, planningInput)];
|
return takeUniqueOperations([
|
||||||
});
|
adapter.createScaffoldOperation(module.id, sourceRelativePath, planningInput),
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -124,11 +124,11 @@ function runTests() {
|
|||||||
);
|
);
|
||||||
assert.ok(
|
assert.ok(
|
||||||
plan.operations.some(operation => (
|
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.destinationPath === path.join(projectRoot, '.cursor', 'rules', 'common-agents.mdc')
|
||||||
&& operation.strategy === 'flatten-copy'
|
&& 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++;
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
|||||||
@@ -209,6 +209,70 @@ function runTests() {
|
|||||||
);
|
);
|
||||||
})) passed++; else failed++;
|
})) 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', () => {
|
if (test('plans antigravity remaps for workflows, skills, and flat rules', () => {
|
||||||
const repoRoot = path.join(__dirname, '..', '..');
|
const repoRoot = path.join(__dirname, '..', '..');
|
||||||
const projectRoot = '/workspace/app';
|
const projectRoot = '/workspace/app';
|
||||||
|
|||||||
Reference in New Issue
Block a user