fix formatting

This commit is contained in:
Joe Danziger
2025-05-11 13:32:42 -04:00
parent d28170ee03
commit 7169296c24
12 changed files with 279 additions and 133 deletions

View File

@@ -446,9 +446,15 @@ async function initializeProject(options = {}) {
try {
const mcpResult = await options.mcpServer.call('rules', mcpArgs);
if (mcpResult && mcpResult.success) {
log('success', `Brand rules added via MCP: ${selectedBrandRules.join(', ')}`);
log(
'success',
`Brand rules added via MCP: ${selectedBrandRules.join(', ')}`
);
} else {
log('error', `MCP rules add failed: ${mcpResult?.error?.message || 'Unknown error'}`);
log(
'error',
`MCP rules add failed: ${mcpResult?.error?.message || 'Unknown error'}`
);
}
} catch (err) {
log('error', `MCP server error: ${err.message}`);

View File

@@ -2,32 +2,37 @@ import fs from 'fs';
import path from 'path';
describe('Cursor Profile Initialization Functionality', () => {
let cursorProfileContent;
let cursorProfileContent;
beforeAll(() => {
const cursorJsPath = path.join(process.cwd(), 'scripts', 'profiles', 'cursor.js');
cursorProfileContent = fs.readFileSync(cursorJsPath, 'utf8');
});
beforeAll(() => {
const cursorJsPath = path.join(
process.cwd(),
'scripts',
'profiles',
'cursor.js'
);
cursorProfileContent = fs.readFileSync(cursorJsPath, 'utf8');
});
test('cursor.js exports correct brandName and rulesDir', () => {
expect(cursorProfileContent).toContain("const brandName = 'Cursor'");
expect(cursorProfileContent).toContain("const rulesDir = '.cursor/rules'");
});
test('cursor.js exports correct brandName and rulesDir', () => {
expect(cursorProfileContent).toContain("const brandName = 'Cursor'");
expect(cursorProfileContent).toContain("const rulesDir = '.cursor/rules'");
});
test('cursor.js preserves .mdc filenames in fileMap', () => {
expect(cursorProfileContent).toContain('fileMap = {');
// Should NOT contain any .md mapping
expect(cursorProfileContent).not.toMatch(/\.md'/);
});
test('cursor.js preserves .mdc filenames in fileMap', () => {
expect(cursorProfileContent).toContain('fileMap = {');
// Should NOT contain any .md mapping
expect(cursorProfileContent).not.toMatch(/\.md'/);
});
test('cursor.js contains tool naming logic and global replacements', () => {
expect(cursorProfileContent).toContain('edit_file');
expect(cursorProfileContent).toContain('search tool');
expect(cursorProfileContent).not.toContain('apply_diff');
expect(cursorProfileContent).not.toContain('search_files tool');
});
test('cursor.js contains tool naming logic and global replacements', () => {
expect(cursorProfileContent).toContain('edit_file');
expect(cursorProfileContent).toContain('search tool');
expect(cursorProfileContent).not.toContain('apply_diff');
expect(cursorProfileContent).not.toContain('search_files tool');
});
test('cursor.js contains correct documentation URL logic', () => {
expect(cursorProfileContent).toContain('docs.cursor.com');
});
test('cursor.js contains correct documentation URL logic', () => {
expect(cursorProfileContent).toContain('docs.cursor.com');
});
});

View File

@@ -0,0 +1,76 @@
// Integration tests for rules transformer functions in MCP server context
import { jest } from '@jest/globals';
import {
convertAllRulesToBrandRules,
removeBrandRules
} from '../../../scripts/modules/rule-transformer.js';
import * as windsurfProfile from '../../../scripts/profiles/windsurf.js';
// Mock fs functions as in direct-functions.test.js
const mockExistsSync = jest.fn();
const mockWriteFileSync = jest.fn();
const mockReadFileSync = jest.fn();
const mockUnlinkSync = jest.fn();
const mockMkdirSync = jest.fn();
const mockRmSync = jest.fn();
const mockReaddirSync = jest.fn();
jest.mock('fs', () => ({
existsSync: mockExistsSync,
writeFileSync: mockWriteFileSync,
readFileSync: mockReadFileSync,
unlinkSync: mockUnlinkSync,
mkdirSync: mockMkdirSync,
rmSync: mockRmSync,
readdirSync: mockReaddirSync
}));
describe('rules transformer', () => {
const mockProjectDir = '/mock/project/root';
beforeEach(() => {
jest.clearAllMocks();
});
test('should convert all Cursor rules to windsurf brand rules', () => {
// Arrange
mockExistsSync.mockImplementation((p) => {
// Simulate that the cursor rules directory exists
if (p === '/mock/project/root/assets/rules') return true;
// Simulate that the brand rules directory does not exist initially
if (p === '/mock/project/root/.windsurf/rules') return false;
return false;
});
mockReadFileSync.mockImplementation((p) => 'mock rule content');
mockReaddirSync.mockImplementation((dir) => ['sample-rule.mdc']);
mockWriteFileSync.mockImplementation(() => {});
mockMkdirSync.mockImplementation(() => {});
// Act
const result = convertAllRulesToBrandRules(mockProjectDir, windsurfProfile);
// Assert
expect(result.success).toBeGreaterThanOrEqual(0);
expect(mockWriteFileSync).toHaveBeenCalled();
expect(mockMkdirSync).toHaveBeenCalled();
});
test('should remove windsurf brand rules', () => {
// Arrange
mockExistsSync.mockImplementation((p) => {
// Simulate that the brand rules directory exists
if (p === '/mock/project/root/.windsurf/rules') return true;
return false;
});
mockRmSync.mockImplementation(() => {});
mockUnlinkSync.mockImplementation(() => {});
mockReaddirSync.mockImplementation((dir) => []);
// Act
const removed = removeBrandRules(mockProjectDir, windsurfProfile);
// Assert
expect(removed).toBe(true);
expect(mockRmSync).toHaveBeenCalled();
});
});

View File

@@ -22,20 +22,34 @@ describe('Roo Files Inclusion in Package', () => {
const rooJsContent = fs.readFileSync(rooJsPath, 'utf8');
// Check for the main handler function
expect(rooJsContent.includes("onAddBrandRules(targetDir)")).toBe(true);
expect(rooJsContent.includes('onAddBrandRules(targetDir)')).toBe(true);
// Check for general recursive copy of assets/roocode
expect(rooJsContent.includes("copyRecursiveSync(sourceDir, targetDir)")).toBe(true);
expect(
rooJsContent.includes('copyRecursiveSync(sourceDir, targetDir)')
).toBe(true);
// Check for .roomodes file copying logic (source and destination paths)
expect(rooJsContent.includes("path.join(sourceDir, '.roomodes')")).toBe(true);
expect(rooJsContent.includes("path.join(targetDir, '.roomodes')")).toBe(true);
expect(rooJsContent.includes("path.join(sourceDir, '.roomodes')")).toBe(
true
);
expect(rooJsContent.includes("path.join(targetDir, '.roomodes')")).toBe(
true
);
// Check for mode-specific rule file copying logic
expect(rooJsContent.includes("for (const mode of rooModes)")).toBe(true);
expect(rooJsContent.includes("path.join(rooModesDir, `rules-${mode}`, `${mode}-rules`)")).toBe(true);
expect(rooJsContent.includes("path.join(targetDir, '.roo', `rules-${mode}`, `${mode}-rules`)")).toBe(true);
expect(rooJsContent.includes('for (const mode of rooModes)')).toBe(true);
expect(
rooJsContent.includes(
'path.join(rooModesDir, `rules-${mode}`, `${mode}-rules`)'
)
).toBe(true);
expect(
rooJsContent.includes(
"path.join(targetDir, '.roo', `rules-${mode}`, `${mode}-rules`)"
)
).toBe(true);
// Check for definition of rooModes array and all modes
const rooModesArrayRegex = /const rooModes\s*=\s*\[([^\]]+)\]\s*;?/;
const rooModesMatch = rooJsContent.match(rooModesArrayRegex);

View File

@@ -19,26 +19,37 @@ describe('Roo Profile Initialization Functionality', () => {
expect(rooProfileContent).toContain(
'copyRecursiveSync(sourceDir, targetDir)'
);
expect(rooProfileContent).toContain("path.resolve(__dirname, '../../assets/roocode')"); // Verifies sourceDir definition
expect(rooProfileContent).toContain(
"path.resolve(__dirname, '../../assets/roocode')"
); // Verifies sourceDir definition
// Check for the loop that processes rooModes
expect(rooProfileContent).toContain('for (const mode of rooModes)');
// Check for creation of mode-specific rule directories (e.g., .roo/rules-architect)
// This is the line: if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
expect(rooProfileContent).toContain("fs.mkdirSync(destDir, { recursive: true });");
expect(rooProfileContent).toContain("const destDir = path.dirname(dest);"); // part of the same logic block
expect(rooProfileContent).toContain(
'fs.mkdirSync(destDir, { recursive: true });'
);
expect(rooProfileContent).toContain('const destDir = path.dirname(dest);'); // part of the same logic block
});
test('roo.js profile copies .roomodes file via onAddBrandRules', () => {
expect(rooProfileContent).toContain('onAddBrandRules(targetDir)');
// Check for the specific .roomodes copy logic
expect(rooProfileContent).toContain('fs.copyFileSync(roomodesSrc, roomodesDest);');
expect(rooProfileContent).toContain("const roomodesSrc = path.join(sourceDir, '.roomodes');");
expect(rooProfileContent).toContain("const roomodesDest = path.join(targetDir, '.roomodes');");
expect(rooProfileContent).toContain("path.resolve(__dirname, '../../assets/roocode')"); // sourceDir for roomodesSrc
expect(rooProfileContent).toContain(
'fs.copyFileSync(roomodesSrc, roomodesDest);'
);
expect(rooProfileContent).toContain(
"const roomodesSrc = path.join(sourceDir, '.roomodes');"
);
expect(rooProfileContent).toContain(
"const roomodesDest = path.join(targetDir, '.roomodes');"
);
expect(rooProfileContent).toContain(
"path.resolve(__dirname, '../../assets/roocode')"
); // sourceDir for roomodesSrc
});
test('roo.js profile copies mode-specific rule files via onAddBrandRules', () => {
@@ -47,11 +58,17 @@ describe('Roo Profile Initialization Functionality', () => {
// Check for the specific mode rule file copy logic
expect(rooProfileContent).toContain('fs.copyFileSync(src, dest);');
// Check source path construction for mode rules
expect(rooProfileContent).toContain("const src = path.join(rooModesDir, `rules-${mode}`, `${mode}-rules`);");
expect(rooProfileContent).toContain(
'const src = path.join(rooModesDir, `rules-${mode}`, `${mode}-rules`);'
);
// Check destination path construction for mode rules
expect(rooProfileContent).toContain("const dest = path.join(targetDir, '.roo', `rules-${mode}`, `${mode}-rules`);");
expect(rooProfileContent).toContain("const rooModesDir = path.join(sourceDir, '.roo');"); // part of src path
expect(rooProfileContent).toContain(
"const dest = path.join(targetDir, '.roo', `rules-${mode}`, `${mode}-rules`);"
);
expect(rooProfileContent).toContain(
"const rooModesDir = path.join(sourceDir, '.roo');"
); // part of src path
});
});

View File

@@ -2,41 +2,41 @@ import fs from 'fs';
import path from 'path';
describe('Rules Files Inclusion in Package', () => {
test('package.json includes assets/** in the "files" array for rules files', () => {
const packageJsonPath = path.join(process.cwd(), 'package.json');
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
expect(packageJson.files).toContain('assets/**');
});
test('package.json includes assets/** in the "files" array for rules files', () => {
const packageJsonPath = path.join(process.cwd(), 'package.json');
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
expect(packageJson.files).toContain('assets/**');
});
test('all rules files exist in assets/rules directory', () => {
const rulesDir = path.join(process.cwd(), 'assets', 'rules');
const expectedFiles = [
'ai_providers.mdc',
'ai_services.mdc',
'architecture.mdc',
'changeset.mdc',
'commands.mdc',
'cursor_rules.mdc',
'dependencies.mdc',
'dev_workflow.mdc',
'glossary.mdc',
'mcp.mdc',
'new_features.mdc',
'self_improve.mdc',
'taskmaster.mdc',
'tasks.mdc',
'tests.mdc',
'ui.mdc',
'utilities.mdc',
];
for (const file of expectedFiles) {
expect(fs.existsSync(path.join(rulesDir, file))).toBe(true);
}
});
test('all rules files exist in assets/rules directory', () => {
const rulesDir = path.join(process.cwd(), 'assets', 'rules');
const expectedFiles = [
'ai_providers.mdc',
'ai_services.mdc',
'architecture.mdc',
'changeset.mdc',
'commands.mdc',
'cursor_rules.mdc',
'dependencies.mdc',
'dev_workflow.mdc',
'glossary.mdc',
'mcp.mdc',
'new_features.mdc',
'self_improve.mdc',
'taskmaster.mdc',
'tasks.mdc',
'tests.mdc',
'ui.mdc',
'utilities.mdc'
];
for (const file of expectedFiles) {
expect(fs.existsSync(path.join(rulesDir, file))).toBe(true);
}
});
test('assets/rules directory is not empty', () => {
const rulesDir = path.join(process.cwd(), 'assets', 'rules');
const files = fs.readdirSync(rulesDir).filter(f => !f.startsWith('.'));
expect(files.length).toBeGreaterThan(0);
});
test('assets/rules directory is not empty', () => {
const rulesDir = path.join(process.cwd(), 'assets', 'rules');
const files = fs.readdirSync(rulesDir).filter((f) => !f.startsWith('.'));
expect(files.length).toBeGreaterThan(0);
});
});

View File

@@ -2,35 +2,42 @@ import fs from 'fs';
import path from 'path';
describe('Windsurf Profile Initialization Functionality', () => {
let windsurfProfileContent;
let windsurfProfileContent;
beforeAll(() => {
const windsurfJsPath = path.join(process.cwd(), 'scripts', 'profiles', 'windsurf.js');
windsurfProfileContent = fs.readFileSync(windsurfJsPath, 'utf8');
});
beforeAll(() => {
const windsurfJsPath = path.join(
process.cwd(),
'scripts',
'profiles',
'windsurf.js'
);
windsurfProfileContent = fs.readFileSync(windsurfJsPath, 'utf8');
});
test('windsurf.js exports correct brandName and rulesDir', () => {
expect(windsurfProfileContent).toContain("const brandName = 'Windsurf'");
expect(windsurfProfileContent).toContain("const rulesDir = '.windsurf/rules'");
});
test('windsurf.js exports correct brandName and rulesDir', () => {
expect(windsurfProfileContent).toContain("const brandName = 'Windsurf'");
expect(windsurfProfileContent).toContain(
"const rulesDir = '.windsurf/rules'"
);
});
test('windsurf.js contains fileMap for .mdc to .md mapping', () => {
expect(windsurfProfileContent).toContain("fileMap = {");
expect(windsurfProfileContent).toContain(".mdc'");
expect(windsurfProfileContent).toContain(".md'");
});
test('windsurf.js contains fileMap for .mdc to .md mapping', () => {
expect(windsurfProfileContent).toContain('fileMap = {');
expect(windsurfProfileContent).toContain(".mdc'");
expect(windsurfProfileContent).toContain(".md'");
});
test('windsurf.js contains tool renaming and extension logic', () => {
expect(windsurfProfileContent).toContain("edit_file");
expect(windsurfProfileContent).toContain("apply_diff");
expect(windsurfProfileContent).toContain("search tool");
expect(windsurfProfileContent).toContain("search_files tool");
expect(windsurfProfileContent).toContain(".mdc");
expect(windsurfProfileContent).toContain(".md");
});
test('windsurf.js contains tool renaming and extension logic', () => {
expect(windsurfProfileContent).toContain('edit_file');
expect(windsurfProfileContent).toContain('apply_diff');
expect(windsurfProfileContent).toContain('search tool');
expect(windsurfProfileContent).toContain('search_files tool');
expect(windsurfProfileContent).toContain('.mdc');
expect(windsurfProfileContent).toContain('.md');
});
test('windsurf.js contains correct documentation URL transformation', () => {
expect(windsurfProfileContent).toContain('docs.cursor.com');
expect(windsurfProfileContent).toContain('docs.windsurf.com');
});
test('windsurf.js contains correct documentation URL transformation', () => {
expect(windsurfProfileContent).toContain('docs.cursor.com');
expect(windsurfProfileContent).toContain('docs.windsurf.com');
});
});

View File

@@ -59,7 +59,14 @@ describe('Cursor Integration', () => {
fs.mkdirSync(path.join(tempDir, '.cursor', 'rules'), { recursive: true });
// Create mode-specific rule directories
const cursorModes = ['architect', 'ask', 'boomerang', 'code', 'debug', 'test'];
const cursorModes = [
'architect',
'ask',
'boomerang',
'code',
'debug',
'test'
];
for (const mode of cursorModes) {
fs.mkdirSync(path.join(tempDir, '.cursor', `rules-${mode}`), {
recursive: true
@@ -71,7 +78,10 @@ describe('Cursor Integration', () => {
}
// Copy .cursormodes file
fs.writeFileSync(path.join(tempDir, '.cursormodes'), 'Cursormodes file content');
fs.writeFileSync(
path.join(tempDir, '.cursormodes'),
'Cursormodes file content'
);
}
test('creates all required .cursor directories', () => {

View File

@@ -2,7 +2,10 @@ import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
import { convertRuleToBrandRule, convertAllRulesToBrandRules } from '../../scripts/modules/rule-transformer.js';
import {
convertRuleToBrandRule,
convertAllRulesToBrandRules
} from '../../scripts/modules/rule-transformer.js';
import * as cursorProfile from '../../scripts/profiles/cursor.js';
const __filename = fileURLToPath(import.meta.url);
@@ -113,7 +116,4 @@ This references [dev_workflow.mdc](mdc:.cursor/rules/dev_workflow.mdc) and
expect(convertedContent).not.toContain('(mdc:.roo/rules/');
expect(convertedContent).not.toContain('(mdc:.windsurf/rules/');
});
});

View File

@@ -2,7 +2,10 @@ import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
import { convertRuleToBrandRule, convertAllRulesToBrandRules } from '../../scripts/modules/rule-transformer.js';
import {
convertRuleToBrandRule,
convertAllRulesToBrandRules
} from '../../scripts/modules/rule-transformer.js';
import * as rooProfile from '../../scripts/profiles/roo.js';
const __filename = fileURLToPath(import.meta.url);
@@ -111,17 +114,17 @@ This references [dev_workflow.mdc](mdc:.cursor/rules/dev_workflow.mdc) and
expect(convertedContent).not.toContain('(mdc:.cursor/rules/');
});
it('should run post-processing when converting all rules for Roo', () => {
// Simulate a rules directory with a .mdc file
const assetsRulesDir = path.join(testDir, 'assets', 'rules');
fs.mkdirSync(assetsRulesDir, { recursive: true });
const assetRule = path.join(assetsRulesDir, 'dev_workflow.mdc');
fs.writeFileSync(assetRule, 'dummy');
// Should create .roo/rules and call post-processing
convertAllRulesToBrandRules(testDir, rooProfile);
// Check for post-processing artifacts, e.g., rules-* folders or extra files
const rooDir = path.join(testDir, '.roo');
const found = fs.readdirSync(rooDir).some(f => f.startsWith('rules-'));
expect(found).toBe(true); // There should be at least one rules-* folder
});
it('should run post-processing when converting all rules for Roo', () => {
// Simulate a rules directory with a .mdc file
const assetsRulesDir = path.join(testDir, 'assets', 'rules');
fs.mkdirSync(assetsRulesDir, { recursive: true });
const assetRule = path.join(assetsRulesDir, 'dev_workflow.mdc');
fs.writeFileSync(assetRule, 'dummy');
// Should create .roo/rules and call post-processing
convertAllRulesToBrandRules(testDir, rooProfile);
// Check for post-processing artifacts, e.g., rules-* folders or extra files
const rooDir = path.join(testDir, '.roo');
const found = fs.readdirSync(rooDir).some((f) => f.startsWith('rules-'));
expect(found).toBe(true); // There should be at least one rules-* folder
});
});

View File

@@ -110,6 +110,4 @@ This references [dev_workflow.mdc](mdc:.cursor/rules/dev_workflow.mdc) and
expect(convertedContent).toContain('(mdc:.windsurf/rules/taskmaster.md)');
expect(convertedContent).not.toContain('(mdc:.cursor/rules/');
});
});

View File

@@ -59,7 +59,14 @@ describe('Windsurf Integration', () => {
fs.mkdirSync(path.join(tempDir, '.windsurf', 'rules'), { recursive: true });
// Create mode-specific rule directories
const windsurfModes = ['architect', 'ask', 'boomerang', 'code', 'debug', 'test'];
const windsurfModes = [
'architect',
'ask',
'boomerang',
'code',
'debug',
'test'
];
for (const mode of windsurfModes) {
fs.mkdirSync(path.join(tempDir, '.windsurf', `rules-${mode}`), {
recursive: true
@@ -71,7 +78,10 @@ describe('Windsurf Integration', () => {
}
// Copy .windsurfmodes file
fs.writeFileSync(path.join(tempDir, '.windsurfmodes'), 'Windsurfmodes file content');
fs.writeFileSync(
path.join(tempDir, '.windsurfmodes'),
'Windsurfmodes file content'
);
}
test('creates all required .windsurf directories', () => {