# refactor: restructure .cursor directory for improved organization and clarity ## Description This PR refactors the `.cursor` directory to enhance organization, clarity, and maintainability. ### Problem The existing `.cursor` directory lacked clear organization, making it difficult to find specific files, understand their purpose, and add new components consistently. ### Solution A comprehensive restructuring: #### New Directory Structure ``` .cursor/ ├── settings.json # Main configuration file ├── docs/ # Documentation │ ├── guides/ # In-depth guides │ ├── references/ # Quick references │ └── practices/ # Best practices ├── rules/ # Rule definitions │ ├── commit/ # Commit-related rules │ ├── quality/ # Code quality rules │ ├── testing/ # Testing rules │ └── verification/ # Verification rules └── hooks/ # Git hooks and scripts ``` #### Key Changes 1. **Logical Categorization**: Organized files into clear categories based on purpose 2. **Improved Documentation**: Added comprehensive README files for each directory 3. **Standardized Naming**: Implemented consistent kebab-case naming convention 4. **Reference Updates**: Updated all internal references to point to new file locations ### Benefits - **Easier Navigation**: Clear categorization makes finding files intuitive - **Improved Understanding**: Comprehensive documentation explains purpose and usage - **Simplified Maintenance**: Logical structure makes updates and additions easier - **Better Onboarding**: New team members can quickly understand the system This refactoring sets a solid foundation for all Cursor AI-related configurations and rules, making it easier for the team to leverage Cursor's capabilities.
744 lines
21 KiB
Plaintext
744 lines
21 KiB
Plaintext
---
|
|
description:
|
|
globs:
|
|
alwaysApply: true
|
|
---
|
|
# Feature Implementation Validator
|
|
|
|
```yaml
|
|
name: Feature Implementation Validator
|
|
description: Validates that new features are completely and correctly implemented
|
|
author: Cursor AI
|
|
version: 1.0.0
|
|
tags:
|
|
- feature
|
|
- implementation
|
|
- quality
|
|
- validation
|
|
activation:
|
|
always: true
|
|
event:
|
|
- pull_request
|
|
- command
|
|
triggers:
|
|
- pull_request.created
|
|
- pull_request.updated
|
|
- pull_request.labeled:feature
|
|
- command: "validate_feature"
|
|
```
|
|
|
|
## Rule Definition
|
|
|
|
This rule ensures that new feature implementations meet quality standards, including proper testing, documentation, and adherence to best practices.
|
|
|
|
## Feature Validation Logic
|
|
|
|
```javascript
|
|
// Main function to validate feature implementation
|
|
function validateFeature(files, codebase, pullRequest) {
|
|
const results = {
|
|
completeness: checkCompleteness(files, codebase),
|
|
tests: checkTestCoverage(files, codebase),
|
|
documentation: checkDocumentation(files, codebase),
|
|
bestPractices: checkBestPractices(files, codebase),
|
|
accessibility: checkAccessibility(files, codebase),
|
|
score: 0,
|
|
issues: [],
|
|
recommendations: []
|
|
};
|
|
|
|
// Calculate overall score
|
|
results.score = calculateScore(results);
|
|
|
|
// Generate issues and recommendations
|
|
results.issues = identifyIssues(results);
|
|
results.recommendations = generateRecommendations(results.issues);
|
|
|
|
return {
|
|
...results,
|
|
summary: generateSummary(results)
|
|
};
|
|
}
|
|
|
|
// Check if the feature implementation is complete
|
|
function checkCompleteness(files, codebase) {
|
|
const results = {
|
|
hasImplementation: false,
|
|
hasTests: false,
|
|
hasDocumentation: false,
|
|
missingComponents: []
|
|
};
|
|
|
|
// Check for implementation files
|
|
const implementationFiles = files.filter(file => {
|
|
return !file.path.includes('.test.') &&
|
|
!file.path.includes('.spec.') &&
|
|
!file.path.includes('docs/') &&
|
|
!file.path.endsWith('.md');
|
|
});
|
|
|
|
results.hasImplementation = implementationFiles.length > 0;
|
|
|
|
// Check for test files
|
|
const testFiles = files.filter(file => {
|
|
return file.path.includes('.test.') || file.path.includes('.spec.');
|
|
});
|
|
|
|
results.hasTests = testFiles.length > 0;
|
|
|
|
// Check for documentation
|
|
const docFiles = files.filter(file => {
|
|
return file.path.includes('docs/') || file.path.endsWith('.md');
|
|
});
|
|
|
|
results.hasDocumentation = docFiles.length > 0;
|
|
|
|
// Identify missing components
|
|
if (!results.hasImplementation) {
|
|
results.missingComponents.push('implementation');
|
|
}
|
|
|
|
if (!results.hasTests) {
|
|
results.missingComponents.push('tests');
|
|
}
|
|
|
|
if (!results.hasDocumentation) {
|
|
results.missingComponents.push('documentation');
|
|
}
|
|
|
|
// Check for missing components based on feature type
|
|
const featureType = identifyFeatureType(files);
|
|
if (featureType === 'ui' && !hasUiComponents(files)) {
|
|
results.missingComponents.push('UI components');
|
|
}
|
|
|
|
if (featureType === 'api' && !hasApiEndpoints(files)) {
|
|
results.missingComponents.push('API endpoints');
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
// Check test coverage of the feature
|
|
function checkTestCoverage(files, codebase) {
|
|
const results = {
|
|
hasFunctionalTests: false,
|
|
hasUnitTests: false,
|
|
hasIntegrationTests: false,
|
|
coverage: 0,
|
|
untested: []
|
|
};
|
|
|
|
// Get all non-test implementation files
|
|
const implFiles = files.filter(file => {
|
|
return !file.path.includes('.test.') &&
|
|
!file.path.includes('.spec.') &&
|
|
!file.path.endsWith('.md');
|
|
});
|
|
|
|
// Check for different test types
|
|
const testFiles = files.filter(file => {
|
|
return file.path.includes('.test.') || file.path.includes('.spec.');
|
|
});
|
|
|
|
results.hasFunctionalTests = testFiles.some(file =>
|
|
file.content.includes('test(') &&
|
|
(file.content.includes('render(') || file.content.includes('fireEvent'))
|
|
);
|
|
|
|
results.hasUnitTests = testFiles.some(file =>
|
|
file.content.includes('test(') &&
|
|
!file.content.includes('render(')
|
|
);
|
|
|
|
results.hasIntegrationTests = testFiles.some(file =>
|
|
file.content.includes('describe(') &&
|
|
file.content.includes('integration')
|
|
);
|
|
|
|
// Calculate rough coverage
|
|
let testedFunctions = 0;
|
|
let totalFunctions = 0;
|
|
|
|
implFiles.forEach(file => {
|
|
const functions = extractFunctions(file.content);
|
|
totalFunctions += functions.length;
|
|
|
|
functions.forEach(func => {
|
|
// Check if function is tested in any test file
|
|
const isTested = testFiles.some(testFile =>
|
|
testFile.content.includes(func.name)
|
|
);
|
|
|
|
if (isTested) {
|
|
testedFunctions++;
|
|
} else {
|
|
results.untested.push(func.name);
|
|
}
|
|
});
|
|
});
|
|
|
|
results.coverage = totalFunctions ? (testedFunctions / totalFunctions) * 100 : 0;
|
|
|
|
return results;
|
|
}
|
|
|
|
// Check if documentation is complete
|
|
function checkDocumentation(files, codebase) {
|
|
const results = {
|
|
hasUserDocs: false,
|
|
hasDeveloperDocs: false,
|
|
hasApiDocs: false,
|
|
missingDocs: []
|
|
};
|
|
|
|
// Check for documentation files
|
|
const docFiles = files.filter(file => {
|
|
return file.path.includes('docs/') || file.path.endsWith('.md');
|
|
});
|
|
|
|
// Check for user documentation
|
|
results.hasUserDocs = docFiles.some(file =>
|
|
file.content.includes('user') ||
|
|
file.content.includes('guide') ||
|
|
file.path.includes('user')
|
|
);
|
|
|
|
// Check for developer documentation
|
|
results.hasDeveloperDocs = docFiles.some(file =>
|
|
file.content.includes('developer') ||
|
|
file.content.includes('implementation') ||
|
|
file.path.includes('dev')
|
|
);
|
|
|
|
// Check for API documentation
|
|
results.hasApiDocs = docFiles.some(file =>
|
|
file.content.includes('API') ||
|
|
file.content.includes('endpoint') ||
|
|
file.path.includes('api')
|
|
);
|
|
|
|
// Identify missing documentation
|
|
if (!results.hasUserDocs) {
|
|
results.missingDocs.push('user documentation');
|
|
}
|
|
|
|
if (!results.hasDeveloperDocs) {
|
|
results.missingDocs.push('developer documentation');
|
|
}
|
|
|
|
const hasApiCode = files.some(file =>
|
|
file.path.includes('api') ||
|
|
file.content.includes('axios') ||
|
|
file.content.includes('fetch')
|
|
);
|
|
|
|
if (hasApiCode && !results.hasApiDocs) {
|
|
results.missingDocs.push('API documentation');
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
// Check adherence to best practices
|
|
function checkBestPractices(files, codebase) {
|
|
const results = {
|
|
followsNamingConventions: true,
|
|
followsArchitecture: true,
|
|
hasCleanCode: true,
|
|
violations: []
|
|
};
|
|
|
|
// Check for naming convention violations
|
|
files.forEach(file => {
|
|
if (file.path.includes('.tsx') || file.path.includes('.jsx')) {
|
|
// React component should use PascalCase
|
|
const filename = file.path.split('/').pop().split('.')[0];
|
|
if (!/^[A-Z][a-zA-Z0-9]*$/.test(filename)) {
|
|
results.followsNamingConventions = false;
|
|
results.violations.push(`React component "${filename}" should use PascalCase`);
|
|
}
|
|
}
|
|
|
|
if (file.path.includes('.java')) {
|
|
// Java classes should use PascalCase
|
|
const filename = file.path.split('/').pop().split('.')[0];
|
|
if (!/^[A-Z][a-zA-Z0-9]*$/.test(filename)) {
|
|
results.followsNamingConventions = false;
|
|
results.violations.push(`Java class "${filename}" should use PascalCase`);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Check for architectural violations
|
|
files.forEach(file => {
|
|
if (file.path.includes('app/client/components') && file.content.includes('fetch(')) {
|
|
results.followsArchitecture = false;
|
|
results.violations.push('Components should not make API calls directly, use services instead');
|
|
}
|
|
|
|
if (file.path.includes('app/server/controllers') && file.content.includes('Repository')) {
|
|
results.followsArchitecture = false;
|
|
results.violations.push('Controllers should not access repositories directly, use services instead');
|
|
}
|
|
});
|
|
|
|
// Check for clean code issues
|
|
files.forEach(file => {
|
|
// Check for long functions (more than 50 lines)
|
|
const functions = extractFunctions(file.content);
|
|
functions.forEach(func => {
|
|
if (func.lines > 50) {
|
|
results.hasCleanCode = false;
|
|
results.violations.push(`Function "${func.name}" is too long (${func.lines} lines)`);
|
|
}
|
|
});
|
|
|
|
// Check for high complexity (nested conditionals)
|
|
if (/if\s*\([^)]*\)\s*\{[^{}]*if\s*\([^)]*\)/g.test(file.content)) {
|
|
results.hasCleanCode = false;
|
|
results.violations.push('Nested conditionals detected, consider refactoring');
|
|
}
|
|
|
|
// Check for commented-out code
|
|
if (/\/\/\s*[a-zA-Z0-9]+.*\(.*\).*\{/g.test(file.content)) {
|
|
results.hasCleanCode = false;
|
|
results.violations.push('Commented-out code detected, remove or refactor');
|
|
}
|
|
});
|
|
|
|
return results;
|
|
}
|
|
|
|
// Check accessibility (for UI features)
|
|
function checkAccessibility(files, codebase) {
|
|
const results = {
|
|
hasA11yAttributes: false,
|
|
hasKeyboardNavigation: false,
|
|
hasSemanticsHtml: false,
|
|
issues: []
|
|
};
|
|
|
|
// Only check UI files
|
|
const uiFiles = files.filter(file => {
|
|
return (file.path.includes('.tsx') || file.path.includes('.jsx')) &&
|
|
file.path.includes('component');
|
|
});
|
|
|
|
if (uiFiles.length === 0) {
|
|
// Not a UI feature, mark as not applicable
|
|
return {
|
|
notApplicable: true
|
|
};
|
|
}
|
|
|
|
// Check for accessibility attributes
|
|
results.hasA11yAttributes = uiFiles.some(file =>
|
|
file.content.includes('aria-') ||
|
|
file.content.includes('role=')
|
|
);
|
|
|
|
if (!results.hasA11yAttributes) {
|
|
results.issues.push('No ARIA attributes found in UI components');
|
|
}
|
|
|
|
// Check for keyboard navigation
|
|
results.hasKeyboardNavigation = uiFiles.some(file =>
|
|
file.content.includes('onKeyDown') ||
|
|
file.content.includes('onKeyPress')
|
|
);
|
|
|
|
if (!results.hasKeyboardNavigation) {
|
|
results.issues.push('No keyboard navigation handlers found');
|
|
}
|
|
|
|
// Check for semantic HTML
|
|
results.hasSemanticsHtml = uiFiles.some(file =>
|
|
file.content.includes('<nav') ||
|
|
file.content.includes('<main') ||
|
|
file.content.includes('<section') ||
|
|
file.content.includes('<article') ||
|
|
file.content.includes('<aside') ||
|
|
file.content.includes('<header') ||
|
|
file.content.includes('<footer')
|
|
);
|
|
|
|
if (!results.hasSemanticsHtml) {
|
|
results.issues.push('No semantic HTML elements found');
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
// Calculate overall score based on all checks
|
|
function calculateScore(results) {
|
|
let score = 100;
|
|
|
|
// Deduct for missing components
|
|
score -= results.completeness.missingComponents.length * 15;
|
|
|
|
// Deduct for low test coverage
|
|
if (results.tests.coverage < 80) {
|
|
score -= (80 - results.tests.coverage) / 4;
|
|
}
|
|
|
|
// Deduct for missing documentation
|
|
score -= results.documentation.missingDocs.length * 10;
|
|
|
|
// Deduct for best practice violations
|
|
score -= results.bestPractices.violations.length * 5;
|
|
|
|
// Deduct for accessibility issues (if applicable)
|
|
if (!results.accessibility.notApplicable) {
|
|
score -= results.accessibility.issues.length * 10;
|
|
}
|
|
|
|
return Math.max(0, Math.round(score));
|
|
}
|
|
|
|
// Identify issues from all validation checks
|
|
function identifyIssues(results) {
|
|
const issues = [];
|
|
|
|
// Add missing components as issues
|
|
results.completeness.missingComponents.forEach(component => {
|
|
issues.push({
|
|
type: 'completeness',
|
|
severity: 'high',
|
|
message: `Missing ${component}`
|
|
});
|
|
});
|
|
|
|
// Add test coverage issues
|
|
if (results.tests.coverage < 80) {
|
|
issues.push({
|
|
type: 'testing',
|
|
severity: 'high',
|
|
message: `Low test coverage (${Math.round(results.tests.coverage)}%)`
|
|
});
|
|
}
|
|
|
|
results.tests.untested.forEach(func => {
|
|
issues.push({
|
|
type: 'testing',
|
|
severity: 'medium',
|
|
message: `Function "${func}" lacks tests`
|
|
});
|
|
});
|
|
|
|
// Add documentation issues
|
|
results.documentation.missingDocs.forEach(doc => {
|
|
issues.push({
|
|
type: 'documentation',
|
|
severity: 'medium',
|
|
message: `Missing ${doc}`
|
|
});
|
|
});
|
|
|
|
// Add best practice violations
|
|
results.bestPractices.violations.forEach(violation => {
|
|
issues.push({
|
|
type: 'best_practice',
|
|
severity: 'medium',
|
|
message: violation
|
|
});
|
|
});
|
|
|
|
// Add accessibility issues
|
|
if (!results.accessibility.notApplicable) {
|
|
results.accessibility.issues.forEach(issue => {
|
|
issues.push({
|
|
type: 'accessibility',
|
|
severity: 'high',
|
|
message: issue
|
|
});
|
|
});
|
|
}
|
|
|
|
return issues;
|
|
}
|
|
|
|
// Generate recommendations based on identified issues
|
|
function generateRecommendations(issues) {
|
|
const recommendations = [];
|
|
|
|
// Group issues by type
|
|
const issuesByType = {};
|
|
issues.forEach(issue => {
|
|
if (!issuesByType[issue.type]) {
|
|
issuesByType[issue.type] = [];
|
|
}
|
|
issuesByType[issue.type].push(issue);
|
|
});
|
|
|
|
// Generate recommendations for completeness issues
|
|
if (issuesByType.completeness) {
|
|
recommendations.push({
|
|
type: 'completeness',
|
|
title: 'Complete the feature implementation',
|
|
steps: issuesByType.completeness.map(issue => issue.message.replace('Missing ', 'Add '))
|
|
});
|
|
}
|
|
|
|
// Generate recommendations for testing issues
|
|
if (issuesByType.testing) {
|
|
recommendations.push({
|
|
type: 'testing',
|
|
title: 'Improve test coverage',
|
|
steps: [
|
|
'Write more unit tests for untested functions',
|
|
'Add integration tests for component interactions',
|
|
'Ensure all edge cases are covered'
|
|
]
|
|
});
|
|
}
|
|
|
|
// Generate recommendations for documentation issues
|
|
if (issuesByType.documentation) {
|
|
recommendations.push({
|
|
type: 'documentation',
|
|
title: 'Complete the documentation',
|
|
steps: issuesByType.documentation.map(issue => issue.message.replace('Missing ', 'Add '))
|
|
});
|
|
}
|
|
|
|
// Generate recommendations for best practice issues
|
|
if (issuesByType.best_practice) {
|
|
recommendations.push({
|
|
type: 'best_practice',
|
|
title: 'Follow best practices',
|
|
steps: issuesByType.best_practice.map(issue => issue.message)
|
|
});
|
|
}
|
|
|
|
// Generate recommendations for accessibility issues
|
|
if (issuesByType.accessibility) {
|
|
recommendations.push({
|
|
type: 'accessibility',
|
|
title: 'Improve accessibility',
|
|
steps: [
|
|
'Add appropriate ARIA attributes to UI components',
|
|
'Implement keyboard navigation for all interactive elements',
|
|
'Use semantic HTML elements to improve screen reader experience'
|
|
]
|
|
});
|
|
}
|
|
|
|
return recommendations;
|
|
}
|
|
|
|
// Generate a summary of the validation results
|
|
function generateSummary(results) {
|
|
const score = results.score;
|
|
let status = '';
|
|
|
|
if (score >= 90) {
|
|
status = 'EXCELLENT';
|
|
} else if (score >= 70) {
|
|
status = 'GOOD';
|
|
} else if (score >= 50) {
|
|
status = 'NEEDS IMPROVEMENT';
|
|
} else {
|
|
status = 'INCOMPLETE';
|
|
}
|
|
|
|
return {
|
|
score,
|
|
status,
|
|
issues: results.issues.length,
|
|
critical: results.issues.filter(issue => issue.severity === 'high').length,
|
|
recommendations: results.recommendations.length,
|
|
message: generateSummaryMessage(score, status, results)
|
|
};
|
|
}
|
|
|
|
// Generate a summary message based on results
|
|
function generateSummaryMessage(score, status, results) {
|
|
if (status === 'EXCELLENT') {
|
|
return 'Feature implementation meets or exceeds all quality standards. Good job!';
|
|
} else if (status === 'GOOD') {
|
|
return `Feature implementation is good overall but has ${results.issues.length} issues to address.`;
|
|
} else if (status === 'NEEDS IMPROVEMENT') {
|
|
const critical = results.issues.filter(issue => issue.severity === 'high').length;
|
|
return `Feature implementation needs significant improvement with ${critical} critical issues.`;
|
|
} else {
|
|
return 'Feature implementation is incomplete and does not meet minimum quality standards.';
|
|
}
|
|
}
|
|
|
|
// Helper function to extract functions from code
|
|
function extractFunctions(content) {
|
|
const functions = [];
|
|
|
|
// JavaScript/TypeScript functions
|
|
const jsMatches = content.match(/function\s+([a-zA-Z0-9_]+)\s*\([^)]*\)\s*\{/g) || [];
|
|
jsMatches.forEach(match => {
|
|
const name = match.match(/function\s+([a-zA-Z0-9_]+)/)[1];
|
|
const startIndex = content.indexOf(match);
|
|
const endIndex = findClosingBrace(content, startIndex + match.indexOf('{'));
|
|
const functionBody = content.substring(startIndex, endIndex + 1);
|
|
const lines = functionBody.split('\n').length;
|
|
|
|
functions.push({ name, lines });
|
|
});
|
|
|
|
// Java methods
|
|
const javaMatches = content.match(/public|private|protected\s+[a-zA-Z0-9_<>]+\s+([a-zA-Z0-9_]+)\s*\([^)]*\)\s*\{/g) || [];
|
|
javaMatches.forEach(match => {
|
|
const nameParts = match.match(/\s+([a-zA-Z0-9_]+)\s*\(/);
|
|
if (nameParts && nameParts[1]) {
|
|
const name = nameParts[1];
|
|
const startIndex = content.indexOf(match);
|
|
const endIndex = findClosingBrace(content, startIndex + match.indexOf('{'));
|
|
const functionBody = content.substring(startIndex, endIndex + 1);
|
|
const lines = functionBody.split('\n').length;
|
|
|
|
functions.push({ name, lines });
|
|
}
|
|
});
|
|
|
|
return functions;
|
|
}
|
|
|
|
// Helper function to find closing brace
|
|
function findClosingBrace(content, openBraceIndex) {
|
|
let braceCount = 1;
|
|
for (let i = openBraceIndex + 1; i < content.length; i++) {
|
|
if (content[i] === '{') {
|
|
braceCount++;
|
|
} else if (content[i] === '}') {
|
|
braceCount--;
|
|
if (braceCount === 0) {
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
return content.length - 1;
|
|
}
|
|
|
|
// Helper function to identify feature type
|
|
function identifyFeatureType(files) {
|
|
const uiFiles = files.filter(file => {
|
|
return (file.path.includes('.tsx') || file.path.includes('.jsx')) &&
|
|
(file.path.includes('component') || file.path.includes('page'));
|
|
});
|
|
|
|
const apiFiles = files.filter(file => {
|
|
return (file.path.includes('controller') || file.path.includes('service')) &&
|
|
(file.path.includes('.java') || file.path.includes('.ts'));
|
|
});
|
|
|
|
if (uiFiles.length > apiFiles.length) {
|
|
return 'ui';
|
|
} else if (apiFiles.length > 0) {
|
|
return 'api';
|
|
} else {
|
|
return 'other';
|
|
}
|
|
}
|
|
|
|
// Helper function to check for UI components
|
|
function hasUiComponents(files) {
|
|
return files.some(file => {
|
|
return (file.path.includes('.tsx') || file.path.includes('.jsx')) &&
|
|
file.path.includes('component');
|
|
});
|
|
}
|
|
|
|
// Helper function to check for API endpoints
|
|
function hasApiEndpoints(files) {
|
|
return files.some(file => {
|
|
return (file.path.includes('controller') || file.path.includes('route')) &&
|
|
(file.path.includes('.java') || file.path.includes('.ts'));
|
|
});
|
|
}
|
|
|
|
// Run on activation
|
|
function activate(context) {
|
|
// Register event handlers
|
|
context.on('pull_request.created', (event) => {
|
|
const files = context.getPullRequestFiles(event.pullRequest.id);
|
|
const codebase = context.getCodebase();
|
|
return validateFeature(files, codebase, event.pullRequest);
|
|
});
|
|
|
|
context.on('pull_request.updated', (event) => {
|
|
const files = context.getPullRequestFiles(event.pullRequest.id);
|
|
const codebase = context.getCodebase();
|
|
return validateFeature(files, codebase, event.pullRequest);
|
|
});
|
|
|
|
context.on('pull_request.labeled:feature', (event) => {
|
|
const files = context.getPullRequestFiles(event.pullRequest.id);
|
|
const codebase = context.getCodebase();
|
|
return validateFeature(files, codebase, event.pullRequest);
|
|
});
|
|
|
|
context.registerCommand('validate_feature', (args) => {
|
|
const prId = args.pullRequest;
|
|
if (!prId) {
|
|
return {
|
|
status: "error",
|
|
message: "No pull request specified"
|
|
};
|
|
}
|
|
|
|
const files = context.getPullRequestFiles(prId);
|
|
const codebase = context.getCodebase();
|
|
const pullRequest = context.getPullRequest(prId);
|
|
return validateFeature(files, codebase, pullRequest);
|
|
});
|
|
}
|
|
|
|
// Export functions
|
|
module.exports = {
|
|
activate,
|
|
validateFeature,
|
|
checkCompleteness,
|
|
checkTestCoverage,
|
|
checkDocumentation,
|
|
checkBestPractices,
|
|
checkAccessibility
|
|
};
|
|
```
|
|
|
|
## When It Runs
|
|
|
|
This rule can be triggered:
|
|
- When a new feature pull request is created
|
|
- When a pull request is updated
|
|
- When a pull request is labeled with 'feature'
|
|
- When a developer runs the `validate_feature` command in Cursor
|
|
- Before merging feature implementation
|
|
|
|
## Usage Example
|
|
|
|
1. Create a pull request for a new feature
|
|
2. Run `validate_feature --pullRequest=123` in Cursor
|
|
3. Review the validation results
|
|
4. Address any identified issues
|
|
5. Re-run validation to confirm all issues are resolved
|
|
|
|
## Feature Implementation Best Practices
|
|
|
|
### Completeness Checklist
|
|
|
|
- [ ] Implementation code
|
|
- [ ] Comprehensive tests
|
|
- [ ] User documentation
|
|
- [ ] Developer documentation
|
|
- [ ] API documentation (if applicable)
|
|
|
|
### Testing Requirements
|
|
|
|
- Unit tests for all functions/methods
|
|
- Integration tests for component interactions
|
|
- Functional tests for UI components
|
|
- Edge case coverage
|
|
- Minimum 80% test coverage
|
|
|
|
### Documentation Guidelines
|
|
|
|
- **User Documentation**: Explain how to use the feature
|
|
- **Developer Documentation**: Explain how the feature is implemented
|
|
- **API Documentation**: Document endpoints, parameters, and responses |