cursor-lint 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -81,10 +81,14 @@ cursor-lint --version Show version
81
81
 
82
82
  Every check in cursor-lint comes from [actual experiments](https://dev.to/nedcodes) testing what Cursor does and doesn't follow. Not guesswork — data.
83
83
 
84
+ ## Need a deeper review?
85
+
86
+ cursor-lint catches structural issues. For a full review of your rules, project structure, and model settings, I offer [$50 async setup audits](https://cursorrulespacks.gumroad.com/l/cursor-setup-audit). You get a written report with specific fixes, not generic advice.
87
+
84
88
  ## License
85
89
 
86
90
  MIT
87
91
 
88
92
  ---
89
93
 
90
- Made by [nedcodes](https://dev.to/nedcodes)
94
+ Made by [nedcodes](https://dev.to/nedcodes) · [Free rules collection](https://github.com/cursorrulespacks/cursorrules-collection) · [Setup audits](https://cursorrulespacks.gumroad.com/l/cursor-setup-audit)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cursor-lint",
3
- "version": "0.2.0",
4
- "description": "Lint your Cursor rules \u2014 catch common mistakes before they break your workflow",
3
+ "version": "0.3.1",
4
+ "description": "Lint your Cursor rules catch common mistakes before they break your workflow",
5
5
  "main": "src/index.js",
6
6
  "bin": {
7
7
  "cursor-lint": "src/cli.js"
package/src/cli.js CHANGED
@@ -3,8 +3,9 @@
3
3
  const path = require('path');
4
4
  const { lintProject } = require('./index');
5
5
  const { verifyProject } = require('./verify');
6
+ const { initProject } = require('./init');
6
7
 
7
- const VERSION = '0.2.0';
8
+ const VERSION = '0.3.1';
8
9
 
9
10
  const RED = '\x1b[31m';
10
11
  const YELLOW = '\x1b[33m';
@@ -26,6 +27,7 @@ ${YELLOW}Options:${RESET}
26
27
  --help, -h Show this help message
27
28
  --version, -v Show version number
28
29
  --verify Check if code follows rules with verify: blocks
30
+ --init Generate starter .mdc rules (auto-detects your stack)
29
31
 
30
32
  ${YELLOW}What it checks (default):${RESET}
31
33
  • .cursorrules files (warns about agent mode compatibility)
@@ -56,6 +58,7 @@ ${YELLOW}verify: block syntax in .mdc frontmatter:${RESET}
56
58
  ${YELLOW}Examples:${RESET}
57
59
  npx cursor-lint # Lint rule files
58
60
  npx cursor-lint --verify # Check code against rules
61
+ npx cursor-lint --init # Generate starter rules for your project
59
62
 
60
63
  ${YELLOW}More info:${RESET}
61
64
  https://github.com/cursorrulespacks/cursor-lint
@@ -77,8 +80,44 @@ async function main() {
77
80
 
78
81
  const cwd = process.cwd();
79
82
  const isVerify = args.includes('--verify');
83
+ const isInit = args.includes('--init');
80
84
 
81
- if (isVerify) {
85
+ if (isInit) {
86
+ console.log(`\n🔍 cursor-lint v${VERSION} --init\n`);
87
+ console.log(`Detecting stack in ${cwd}...\n`);
88
+
89
+ const results = await initProject(cwd);
90
+
91
+ const stacks = Object.entries(results.detected)
92
+ .filter(([_, v]) => v)
93
+ .map(([k]) => k.charAt(0).toUpperCase() + k.slice(1));
94
+
95
+ if (stacks.length > 0) {
96
+ console.log(`Detected: ${stacks.join(', ')}\n`);
97
+ }
98
+
99
+ if (results.created.length > 0) {
100
+ console.log(`${GREEN}Created:${RESET}`);
101
+ for (const f of results.created) {
102
+ console.log(` ${GREEN}✓${RESET} .cursor/rules/${f}`);
103
+ }
104
+ }
105
+
106
+ if (results.skipped.length > 0) {
107
+ console.log(`\n${YELLOW}Skipped (already exist):${RESET}`);
108
+ for (const f of results.skipped) {
109
+ console.log(` ${YELLOW}⚠${RESET} .cursor/rules/${f}`);
110
+ }
111
+ }
112
+
113
+ if (results.created.length > 0) {
114
+ console.log(`\n${DIM}Run cursor-lint to check these rules${RESET}`);
115
+ console.log(`${DIM}Run cursor-lint --verify to check code against them${RESET}\n`);
116
+ }
117
+
118
+ process.exit(0);
119
+
120
+ } else if (isVerify) {
82
121
  console.log(`\n🔍 cursor-lint v${VERSION} --verify\n`);
83
122
  console.log(`Scanning ${cwd} for rule violations...\n`);
84
123
 
@@ -167,6 +206,11 @@ async function main() {
167
206
  if (totalPassed > 0) parts.push(`${GREEN}${totalPassed} passed${RESET}`);
168
207
  console.log(parts.join(', ') + '\n');
169
208
 
209
+ if (totalErrors > 0) {
210
+ console.log(`${DIM}Need help fixing these? Get a full setup review:${RESET}`);
211
+ console.log(`${CYAN}https://cursorrulespacks.gumroad.com/l/cursor-setup-audit${RESET}\n`);
212
+ }
213
+
170
214
  process.exit(totalErrors > 0 ? 1 : 0);
171
215
  }
172
216
  }
package/src/init.js ADDED
@@ -0,0 +1,227 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ async function initProject(projectPath) {
5
+ const detected = detectStack(projectPath);
6
+ const created = [];
7
+ const skipped = [];
8
+
9
+ const rulesDir = path.join(projectPath, '.cursor', 'rules');
10
+
11
+ if (!fs.existsSync(rulesDir)) {
12
+ fs.mkdirSync(rulesDir, { recursive: true });
13
+ }
14
+
15
+ const generalResult = writeRule(rulesDir, 'general.mdc', generateGeneral());
16
+ if (generalResult.created) created.push(generalResult.file);
17
+ else skipped.push(generalResult.file);
18
+
19
+ if (detected.typescript) {
20
+ const result = writeRule(rulesDir, 'typescript.mdc', generateTypeScript());
21
+ if (result.created) created.push(result.file);
22
+ else skipped.push(result.file);
23
+ }
24
+
25
+ if (detected.react && !detected.nextjs) {
26
+ const result = writeRule(rulesDir, 'react.mdc', generateReact());
27
+ if (result.created) created.push(result.file);
28
+ else skipped.push(result.file);
29
+ }
30
+
31
+ if (detected.nextjs) {
32
+ const result = writeRule(rulesDir, 'nextjs.mdc', generateNextJs());
33
+ if (result.created) created.push(result.file);
34
+ else skipped.push(result.file);
35
+ }
36
+
37
+ if (detected.express) {
38
+ const result = writeRule(rulesDir, 'express.mdc', generateExpress());
39
+ if (result.created) created.push(result.file);
40
+ else skipped.push(result.file);
41
+ }
42
+
43
+ if (detected.python) {
44
+ const result = writeRule(rulesDir, 'python.mdc', generatePython());
45
+ if (result.created) created.push(result.file);
46
+ else skipped.push(result.file);
47
+ }
48
+
49
+ return { created, skipped, detected };
50
+ }
51
+
52
+ function detectStack(projectPath) {
53
+ const detected = {
54
+ typescript: false,
55
+ react: false,
56
+ nextjs: false,
57
+ express: false,
58
+ python: false,
59
+ node: false
60
+ };
61
+
62
+ if (fs.existsSync(path.join(projectPath, 'tsconfig.json'))) {
63
+ detected.typescript = true;
64
+ }
65
+
66
+ const pkgPath = path.join(projectPath, 'package.json');
67
+ if (fs.existsSync(pkgPath)) {
68
+ detected.node = true;
69
+ try {
70
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
71
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
72
+
73
+ if (allDeps.react || allDeps['react-dom']) detected.react = true;
74
+ if (allDeps.next) { detected.nextjs = true; detected.react = true; }
75
+ if (allDeps.express) detected.express = true;
76
+ if (allDeps.typescript || allDeps['@types/node']) detected.typescript = true;
77
+ } catch (e) {}
78
+ }
79
+
80
+ try {
81
+ const files = fs.readdirSync(projectPath);
82
+ if (files.some(f => f.endsWith('.py')) ||
83
+ fs.existsSync(path.join(projectPath, 'requirements.txt')) ||
84
+ fs.existsSync(path.join(projectPath, 'pyproject.toml'))) {
85
+ detected.python = true;
86
+ }
87
+ } catch (e) {}
88
+
89
+ return detected;
90
+ }
91
+
92
+ function writeRule(rulesDir, filename, content) {
93
+ const filePath = path.join(rulesDir, filename);
94
+ if (fs.existsSync(filePath)) return { file: filename, created: false };
95
+ fs.writeFileSync(filePath, content);
96
+ return { file: filename, created: true };
97
+ }
98
+
99
+ function generateGeneral() {
100
+ return `---
101
+ description: General code quality rules
102
+ alwaysApply: true
103
+ globs: ["*"]
104
+ verify:
105
+ - antipattern: "TODO"
106
+ message: "Resolve TODO comments before committing"
107
+ - antipattern: "FIXME"
108
+ message: "Resolve FIXME comments before committing"
109
+ - antipattern: "console\\\\.log"
110
+ message: "Remove console.log statements"
111
+ ---
112
+
113
+ # General Guidelines
114
+
115
+ - Write clear, self-documenting code
116
+ - Use meaningful variable and function names
117
+ - Keep functions small and focused
118
+ - Remove all TODOs and FIXMEs before committing
119
+ - No console.log in production code
120
+ `;
121
+ }
122
+
123
+ function generateTypeScript() {
124
+ return `---
125
+ description: TypeScript best practices
126
+ alwaysApply: true
127
+ globs: ["*.ts", "*.tsx"]
128
+ verify:
129
+ - antipattern: ": any"
130
+ message: "Avoid using 'any' type - use proper typing"
131
+ - antipattern: "@ts-ignore"
132
+ message: "Remove @ts-ignore - fix the type error instead"
133
+ ---
134
+
135
+ # TypeScript Rules
136
+
137
+ - Use strict TypeScript configuration
138
+ - Avoid \`any\` type - use \`unknown\` if type is truly unknown
139
+ - Use type inference where possible, explicit types where helpful
140
+ - Prefer interfaces for object shapes, types for unions/intersections
141
+ `;
142
+ }
143
+
144
+ function generateReact() {
145
+ return `---
146
+ description: React best practices
147
+ alwaysApply: true
148
+ globs: ["*.tsx", "*.jsx"]
149
+ verify:
150
+ - antipattern: "dangerouslySetInnerHTML"
151
+ message: "Avoid dangerouslySetInnerHTML - use proper sanitization if needed"
152
+ ---
153
+
154
+ # React Rules
155
+
156
+ - Use functional components with hooks
157
+ - Before writing a useEffect, ask: can this be computed during render?
158
+ - Keep components small and focused
159
+ - Use proper key props in lists (never use array index as key for dynamic lists)
160
+ `;
161
+ }
162
+
163
+ function generateNextJs() {
164
+ return `---
165
+ description: Next.js App Router best practices
166
+ alwaysApply: true
167
+ globs: ["*.ts", "*.tsx"]
168
+ verify:
169
+ - antipattern: "getServerSideProps"
170
+ message: "Use App Router patterns instead of getServerSideProps"
171
+ - antipattern: "getStaticProps"
172
+ message: "Use App Router patterns instead of getStaticProps"
173
+ ---
174
+
175
+ # Next.js Rules
176
+
177
+ - Use App Router (app directory), not Pages Router
178
+ - Mark components as 'use client' only when they need client-side interactivity
179
+ - Default to Server Components
180
+ - Use Server Actions for mutations instead of API routes
181
+ - Use the @/ path alias for imports
182
+ `;
183
+ }
184
+
185
+ function generateExpress() {
186
+ return `---
187
+ description: Express/Node.js best practices
188
+ alwaysApply: true
189
+ globs: ["*.js", "*.ts"]
190
+ verify:
191
+ - antipattern: "app\\\\.use\\\\(express\\\\.json\\\\(\\\\)\\\\)"
192
+ message: "Consider adding body size limits to express.json()"
193
+ ---
194
+
195
+ # Express Rules
196
+
197
+ - Use async/await with proper error handling
198
+ - Always validate and sanitize user input
199
+ - Use middleware for cross-cutting concerns
200
+ - Add rate limiting for public endpoints
201
+ `;
202
+ }
203
+
204
+ function generatePython() {
205
+ return `---
206
+ description: Python best practices
207
+ alwaysApply: true
208
+ globs: ["*.py"]
209
+ verify:
210
+ - antipattern: "print\\\\("
211
+ message: "Use logging instead of print statements"
212
+ - antipattern: "import \\\\*"
213
+ message: "Avoid wildcard imports - import specific names"
214
+ - antipattern: "except:"
215
+ message: "Avoid bare except - catch specific exceptions"
216
+ ---
217
+
218
+ # Python Rules
219
+
220
+ - Follow PEP 8 style guidelines
221
+ - Use type hints for function signatures
222
+ - Use logging instead of print statements
223
+ - Handle exceptions specifically, never use bare except
224
+ `;
225
+ }
226
+
227
+ module.exports = { initProject, detectStack };