@usrrname/cursorrules
Version:
A wicked npx-able lib of cursor rules with Otaku AI agents
103 lines (83 loc) ⢠3.61 kB
JavaScript
import { copyFile, cp, mkdir } from 'node:fs/promises';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { help } from '../commands.mjs';
import { detectNpxSandbox } from './detect-npx.mjs';
import { findFolderUp } from './find-folder-up.mjs';
import { findPackageRoot } from './find-package-root.mjs';
import { validateDirname } from './validate-dirname.mjs';
const detection = detectNpxSandbox();
const __dirname = dirname(fileURLToPath(import.meta.url));
/** @type {string|undefined} */
let sourceRulesBasePath = '';
if (detection.isNpxSandbox || !sourceRulesBasePath) {
const packageRoot = findPackageRoot(__dirname, '@usrrname/cursorrules');
sourceRulesBasePath = resolve(packageRoot, '.cursor', 'rules');
}
// only for local development and testing
if (process.env.CI || ['development', 'test'].includes(process.env.NODE_ENV ?? '')) {
// running inside repo / globally installed copy ā locate nearest .cursor
const found = await findFolderUp('.cursor', process.cwd())
?? await findFolderUp('.cursor', __dirname);
if (!found) throw new Error("'.cursor' folder not found");
sourceRulesBasePath = resolve(found, 'rules');
}
/**
* @param {string} dirname - output folder relative path
*/
export const downloadFiles = async (dirname) => {
if (!dirname) throw new Error('Output directory is required');
console.info('š„ Downloading all rules...');
const outputDir = await validateDirname(dirname);
if (!sourceRulesBasePath) {
console.error(`ā Error: sourceRulesBasePath is not defined`);
process.exit(1);
}
try {
// copy whole folder
await cp(
sourceRulesBasePath,
outputDir,
{ recursive: true },
)
console.log(`ā
Success! All rules saved to ${outputDir}`);
} catch (err) {
console.error(`ā Error: ${err.message}`, err);
process.exit(1);
}
}
/**
* Download selected rules only
* @param {string} folderName - output folder relative path
* @param {Array<{category: string, displayName: string, selected: boolean, name: string, path: string, fullPath: string}>} selectedRules - Array of selected rule objects
*/
export const downloadSelectedFiles = async (folderName, selectedRules) => {
if (!folderName) throw new Error('Output directory is required');
if (!selectedRules || selectedRules.length === 0) {
console.log('āļø No rules selected, skipping download');
await help();
return;
}
console.info('š„ Downloading selected rules...');
const outputDir = await validateDirname(folderName)
try {
// Create output directory structure
await mkdir(outputDir, { recursive: true });
await mkdir(join(outputDir, '.cursor'), { recursive: true });
// Copy selected rules
for (const rule of selectedRules) {
const sourcePath = join(sourceRulesBasePath, rule.path);
const destPath = join(outputDir, '.cursor', 'rules', rule.path);
const destDir = dirname(destPath);
// Ensure destination directory exists
await mkdir(destDir, { recursive: true });
// Copy the rule file
await copyFile(sourcePath, destPath);
console.log(` š Copied: ${rule.displayName}`);
}
console.log(`\nš Success! ${selectedRules.length} selected rules saved to ${outputDir}`);
} catch (err) {
console.error(`ā Error: ${err.message}`, err);
process.exit(1);
}
}