@a16njs/plugin-cursor 0.0.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 +113 -0
- package/dist/discover.d.ts +6 -0
- package/dist/discover.d.ts.map +1 -0
- package/dist/discover.js +157 -0
- package/dist/discover.js.map +1 -0
- package/dist/emit.d.ts +9 -0
- package/dist/emit.d.ts.map +1 -0
- package/dist/emit.js +189 -0
- package/dist/emit.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/mdc.d.ts +20 -0
- package/dist/mdc.d.ts.map +1 -0
- package/dist/mdc.js +65 -0
- package/dist/mdc.js.map +1 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# @a16njs/plugin-cursor
|
|
2
|
+
|
|
3
|
+
Cursor IDE plugin for a16n. Discovers and emits Cursor rules.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
This plugin is bundled with the `a16n` CLI. For programmatic use:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @a16njs/plugin-cursor
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Supported Types
|
|
14
|
+
|
|
15
|
+
This plugin supports four customization types:
|
|
16
|
+
|
|
17
|
+
| Type | Format | Description |
|
|
18
|
+
|------|--------|-------------|
|
|
19
|
+
| **GlobalPrompt** | `alwaysApply: true` frontmatter | Always-active rules |
|
|
20
|
+
| **FileRule** | `globs: **/*.ts` frontmatter | Triggered by file patterns |
|
|
21
|
+
| **AgentSkill** | `description: "..."` frontmatter | Triggered by context matching |
|
|
22
|
+
| **AgentIgnore** | `.cursorignore` file | Files/patterns to exclude |
|
|
23
|
+
|
|
24
|
+
## Supported Files
|
|
25
|
+
|
|
26
|
+
### Discovery
|
|
27
|
+
|
|
28
|
+
- `.cursor/rules/**/*.mdc` - MDC format rules with frontmatter (recursive)
|
|
29
|
+
- `.cursorignore` - Gitignore-style patterns for files to exclude
|
|
30
|
+
|
|
31
|
+
> **Note:** Legacy `.cursorrules` files are not supported. Use `.cursor/rules/*.mdc` instead.
|
|
32
|
+
|
|
33
|
+
### Classification Priority
|
|
34
|
+
|
|
35
|
+
Rules are classified based on frontmatter (first match wins):
|
|
36
|
+
|
|
37
|
+
1. `alwaysApply: true` → GlobalPrompt
|
|
38
|
+
2. `globs:` present → FileRule
|
|
39
|
+
3. `description:` present → AgentSkill
|
|
40
|
+
4. No frontmatter → GlobalPrompt (fallback)
|
|
41
|
+
|
|
42
|
+
### Emission
|
|
43
|
+
|
|
44
|
+
- Creates `.cursor/rules/<name>.mdc` files with appropriate frontmatter
|
|
45
|
+
- Creates `.cursorignore` from AgentIgnore patterns
|
|
46
|
+
|
|
47
|
+
## .cursorignore Format
|
|
48
|
+
|
|
49
|
+
The `.cursorignore` file uses gitignore-style patterns:
|
|
50
|
+
|
|
51
|
+
```text
|
|
52
|
+
# Build output
|
|
53
|
+
dist/
|
|
54
|
+
build/
|
|
55
|
+
|
|
56
|
+
# Environment
|
|
57
|
+
.env
|
|
58
|
+
.env.local
|
|
59
|
+
|
|
60
|
+
# Logs
|
|
61
|
+
*.log
|
|
62
|
+
|
|
63
|
+
# Secrets
|
|
64
|
+
secrets/
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## MDC Format
|
|
68
|
+
|
|
69
|
+
Cursor uses MDC (Markdown Configuration) format with YAML frontmatter:
|
|
70
|
+
|
|
71
|
+
```markdown
|
|
72
|
+
---
|
|
73
|
+
alwaysApply: true
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
Always-applied rule content.
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
```markdown
|
|
80
|
+
---
|
|
81
|
+
globs: **/*.tsx,**/*.jsx
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
React-specific guidelines.
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```markdown
|
|
88
|
+
---
|
|
89
|
+
description: Authentication and authorization patterns
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
Auth-related guidelines.
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Usage
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import cursorPlugin from '@a16njs/plugin-cursor';
|
|
99
|
+
import { A16nEngine } from '@a16njs/engine';
|
|
100
|
+
|
|
101
|
+
const engine = new A16nEngine([cursorPlugin]);
|
|
102
|
+
|
|
103
|
+
// Discover Cursor rules
|
|
104
|
+
const result = await cursorPlugin.discover('./my-project');
|
|
105
|
+
console.log(`Found ${result.items.length} rules`);
|
|
106
|
+
|
|
107
|
+
// Emit to Cursor format
|
|
108
|
+
await cursorPlugin.emit(result.items, './my-project');
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## License
|
|
112
|
+
|
|
113
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,eAAe,EAOrB,MAAM,gBAAgB,CAAC;AAgJxB;;GAEG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CA0BrE"}
|
package/dist/discover.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { CustomizationType, createId, } from '@a16njs/models';
|
|
4
|
+
import { parseMdc } from './mdc.js';
|
|
5
|
+
/**
|
|
6
|
+
* Recursively find all .mdc files in a directory and its subdirectories.
|
|
7
|
+
* Returns paths relative to the rulesDir (e.g., "shared/core.mdc").
|
|
8
|
+
*
|
|
9
|
+
* NOTE: This only searches within the given rulesDir. Finding nested
|
|
10
|
+
* .cursor/rules/ directories elsewhere in the project is a future enhancement.
|
|
11
|
+
*/
|
|
12
|
+
async function findMdcFiles(rulesDir, relativePath = '') {
|
|
13
|
+
const results = [];
|
|
14
|
+
try {
|
|
15
|
+
const entries = await fs.readdir(path.join(rulesDir, relativePath), { withFileTypes: true });
|
|
16
|
+
for (const entry of entries) {
|
|
17
|
+
const entryRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
|
|
18
|
+
if (entry.isFile() && entry.name.endsWith('.mdc')) {
|
|
19
|
+
results.push(entryRelativePath);
|
|
20
|
+
}
|
|
21
|
+
else if (entry.isDirectory()) {
|
|
22
|
+
// Recurse into subdirectories
|
|
23
|
+
const subFiles = await findMdcFiles(rulesDir, entryRelativePath);
|
|
24
|
+
results.push(...subFiles);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// Directory doesn't exist or can't be read
|
|
30
|
+
}
|
|
31
|
+
return results;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Parse comma-separated glob patterns into an array.
|
|
35
|
+
* Handles various formats: "*.ts,*.tsx" or "*.ts, *.tsx"
|
|
36
|
+
*/
|
|
37
|
+
function parseGlobs(globsString) {
|
|
38
|
+
return globsString
|
|
39
|
+
.split(',')
|
|
40
|
+
.map(g => g.trim())
|
|
41
|
+
.filter(g => g.length > 0);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Classify a Cursor rule based on its frontmatter.
|
|
45
|
+
*
|
|
46
|
+
* Classification priority:
|
|
47
|
+
* 1. alwaysApply: true → GlobalPrompt
|
|
48
|
+
* 2. globs: present → FileRule
|
|
49
|
+
* 3. description: present → AgentSkill
|
|
50
|
+
* 4. None of above → GlobalPrompt (fallback)
|
|
51
|
+
*/
|
|
52
|
+
function classifyRule(frontmatter, body, sourcePath) {
|
|
53
|
+
// Priority 1: alwaysApply: true → GlobalPrompt
|
|
54
|
+
if (frontmatter.alwaysApply === true) {
|
|
55
|
+
return {
|
|
56
|
+
id: createId(CustomizationType.GlobalPrompt, sourcePath),
|
|
57
|
+
type: CustomizationType.GlobalPrompt,
|
|
58
|
+
sourcePath,
|
|
59
|
+
content: body,
|
|
60
|
+
metadata: { ...frontmatter },
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
// Priority 2: globs present → FileRule
|
|
64
|
+
if (frontmatter.globs) {
|
|
65
|
+
const globs = parseGlobs(frontmatter.globs);
|
|
66
|
+
return {
|
|
67
|
+
id: createId(CustomizationType.FileRule, sourcePath),
|
|
68
|
+
type: CustomizationType.FileRule,
|
|
69
|
+
sourcePath,
|
|
70
|
+
content: body,
|
|
71
|
+
globs,
|
|
72
|
+
metadata: { ...frontmatter },
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// Priority 3: description present → AgentSkill
|
|
76
|
+
if (frontmatter.description) {
|
|
77
|
+
return {
|
|
78
|
+
id: createId(CustomizationType.AgentSkill, sourcePath),
|
|
79
|
+
type: CustomizationType.AgentSkill,
|
|
80
|
+
sourcePath,
|
|
81
|
+
content: body,
|
|
82
|
+
description: frontmatter.description,
|
|
83
|
+
metadata: { ...frontmatter },
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
// Priority 4: Fallback → GlobalPrompt
|
|
87
|
+
return {
|
|
88
|
+
id: createId(CustomizationType.GlobalPrompt, sourcePath),
|
|
89
|
+
type: CustomizationType.GlobalPrompt,
|
|
90
|
+
sourcePath,
|
|
91
|
+
content: body,
|
|
92
|
+
metadata: { ...frontmatter },
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Discover .cursorignore file and parse its patterns.
|
|
97
|
+
* Returns null if file doesn't exist or has no valid patterns.
|
|
98
|
+
*/
|
|
99
|
+
async function discoverCursorIgnore(root) {
|
|
100
|
+
const ignorePath = path.join(root, '.cursorignore');
|
|
101
|
+
try {
|
|
102
|
+
const content = await fs.readFile(ignorePath, 'utf-8');
|
|
103
|
+
const patterns = content
|
|
104
|
+
.split(/\r?\n/)
|
|
105
|
+
.map(line => line.trimEnd())
|
|
106
|
+
.filter(line => {
|
|
107
|
+
const trimmedStart = line.trimStart();
|
|
108
|
+
return trimmedStart.length > 0 && !trimmedStart.startsWith('#');
|
|
109
|
+
})
|
|
110
|
+
.map(line => {
|
|
111
|
+
// Strip inline comments (unescaped ' #')
|
|
112
|
+
const inlineCommentIndex = line.indexOf(' #');
|
|
113
|
+
const cleaned = inlineCommentIndex >= 0 ? line.slice(0, inlineCommentIndex) : line;
|
|
114
|
+
return cleaned.trimEnd();
|
|
115
|
+
})
|
|
116
|
+
.filter(line => line.length > 0);
|
|
117
|
+
if (patterns.length === 0)
|
|
118
|
+
return null;
|
|
119
|
+
return {
|
|
120
|
+
id: createId(CustomizationType.AgentIgnore, '.cursorignore'),
|
|
121
|
+
type: CustomizationType.AgentIgnore,
|
|
122
|
+
sourcePath: '.cursorignore',
|
|
123
|
+
content,
|
|
124
|
+
patterns,
|
|
125
|
+
metadata: {},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Discover all Cursor rules in a project directory.
|
|
134
|
+
*/
|
|
135
|
+
export async function discover(root) {
|
|
136
|
+
const items = [];
|
|
137
|
+
const warnings = [];
|
|
138
|
+
// Find .cursor/rules/*.mdc files
|
|
139
|
+
const rulesDir = path.join(root, '.cursor', 'rules');
|
|
140
|
+
const mdcFiles = await findMdcFiles(rulesDir);
|
|
141
|
+
for (const file of mdcFiles) {
|
|
142
|
+
const filePath = path.join(rulesDir, file);
|
|
143
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
144
|
+
const { frontmatter, body } = parseMdc(content);
|
|
145
|
+
// Classify and add all rules (Phase 2: supports GlobalPrompt, FileRule, AgentSkill)
|
|
146
|
+
const sourcePath = `.cursor/rules/${file}`;
|
|
147
|
+
const item = classifyRule(frontmatter, body, sourcePath);
|
|
148
|
+
items.push(item);
|
|
149
|
+
}
|
|
150
|
+
// Discover .cursorignore (Phase 3)
|
|
151
|
+
const agentIgnore = await discoverCursorIgnore(root);
|
|
152
|
+
if (agentIgnore) {
|
|
153
|
+
items.push(agentIgnore);
|
|
154
|
+
}
|
|
155
|
+
return { items, warnings };
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=discover.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discover.js","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"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"}
|
package/dist/emit.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type AgentCustomization, type EmitResult } from '@a16njs/models';
|
|
2
|
+
/**
|
|
3
|
+
* Emit agent customizations to Cursor format.
|
|
4
|
+
* - GlobalPrompt → .mdc with alwaysApply: true
|
|
5
|
+
* - FileRule → .mdc with globs:
|
|
6
|
+
* - AgentSkill → .mdc with description:
|
|
7
|
+
*/
|
|
8
|
+
export declare function emit(models: AgentCustomization[], root: string): Promise<EmitResult>;
|
|
9
|
+
//# sourceMappingURL=emit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emit.d.ts","sourceRoot":"","sources":["../src/emit.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,UAAU,EAWhB,MAAM,gBAAgB,CAAC;AAwFxB;;;;;GAKG;AACH,wBAAsB,IAAI,CACxB,MAAM,EAAE,kBAAkB,EAAE,EAC5B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,UAAU,CAAC,CAmIrB"}
|
package/dist/emit.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { CustomizationType, WarningCode, isGlobalPrompt, isFileRule, isAgentSkill, isAgentIgnore, } from '@a16njs/models';
|
|
4
|
+
/**
|
|
5
|
+
* Sanitize a filename to be safe for the filesystem.
|
|
6
|
+
* Converts to lowercase, replaces spaces and special chars with hyphens.
|
|
7
|
+
* Returns 'rule' if sanitization produces an empty string.
|
|
8
|
+
*/
|
|
9
|
+
function sanitizeFilename(sourcePath) {
|
|
10
|
+
// Get just the filename without directory
|
|
11
|
+
const basename = path.basename(sourcePath);
|
|
12
|
+
// Remove extension
|
|
13
|
+
const nameWithoutExt = basename.replace(/\.[^.]+$/, '');
|
|
14
|
+
// Convert to lowercase and replace unsafe characters
|
|
15
|
+
const sanitized = nameWithoutExt
|
|
16
|
+
.toLowerCase()
|
|
17
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
18
|
+
.replace(/^-+|-+$/g, ''); // Trim leading/trailing hyphens
|
|
19
|
+
// Return fallback if empty
|
|
20
|
+
return sanitized || 'rule';
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Generate a unique filename by appending a counter if needed.
|
|
24
|
+
* Returns the unique filename and whether a collision occurred.
|
|
25
|
+
*/
|
|
26
|
+
function getUniqueFilename(baseName, usedNames) {
|
|
27
|
+
if (!usedNames.has(baseName)) {
|
|
28
|
+
usedNames.add(baseName);
|
|
29
|
+
return { filename: baseName, collision: false };
|
|
30
|
+
}
|
|
31
|
+
// Collision detected - find unique name
|
|
32
|
+
let counter = 2;
|
|
33
|
+
let uniqueName = `${baseName.replace(/\.mdc$/, '')}-${counter}.mdc`;
|
|
34
|
+
while (usedNames.has(uniqueName)) {
|
|
35
|
+
counter++;
|
|
36
|
+
uniqueName = `${baseName.replace(/\.mdc$/, '')}-${counter}.mdc`;
|
|
37
|
+
}
|
|
38
|
+
usedNames.add(uniqueName);
|
|
39
|
+
return { filename: uniqueName, collision: true };
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Format content as MDC with GlobalPrompt frontmatter.
|
|
43
|
+
*/
|
|
44
|
+
function formatGlobalPromptMdc(content) {
|
|
45
|
+
return `---
|
|
46
|
+
alwaysApply: true
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
${content}
|
|
50
|
+
`;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Format content as MDC with FileRule frontmatter (globs).
|
|
54
|
+
*/
|
|
55
|
+
function formatFileRuleMdc(content, globs) {
|
|
56
|
+
const globsLine = globs.join(',');
|
|
57
|
+
return `---
|
|
58
|
+
globs: ${globsLine}
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
${content}
|
|
62
|
+
`;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Format content as MDC with AgentSkill frontmatter (description).
|
|
66
|
+
*/
|
|
67
|
+
function formatAgentSkillMdc(content, description) {
|
|
68
|
+
// Quote description if it contains special YAML characters
|
|
69
|
+
const needsQuotes = /[:&*#?|\-<>=!%@`]/.test(description);
|
|
70
|
+
const quotedDesc = needsQuotes ? `"${description.replace(/"/g, '\\"')}"` : description;
|
|
71
|
+
return `---
|
|
72
|
+
description: ${quotedDesc}
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
${content}
|
|
76
|
+
`;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Emit agent customizations to Cursor format.
|
|
80
|
+
* - GlobalPrompt → .mdc with alwaysApply: true
|
|
81
|
+
* - FileRule → .mdc with globs:
|
|
82
|
+
* - AgentSkill → .mdc with description:
|
|
83
|
+
*/
|
|
84
|
+
export async function emit(models, root) {
|
|
85
|
+
const written = [];
|
|
86
|
+
const warnings = [];
|
|
87
|
+
const unsupported = [];
|
|
88
|
+
const usedFilenames = new Set();
|
|
89
|
+
// Separate by type
|
|
90
|
+
const globalPrompts = models.filter(isGlobalPrompt);
|
|
91
|
+
const fileRules = models.filter(isFileRule);
|
|
92
|
+
const agentSkills = models.filter(isAgentSkill);
|
|
93
|
+
const agentIgnores = models.filter(isAgentIgnore);
|
|
94
|
+
// Track unsupported types (future types)
|
|
95
|
+
for (const model of models) {
|
|
96
|
+
if (!isGlobalPrompt(model) && !isFileRule(model) && !isAgentSkill(model) && !isAgentIgnore(model)) {
|
|
97
|
+
unsupported.push(model);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const allItems = [...globalPrompts, ...fileRules, ...agentSkills];
|
|
101
|
+
// Early return only if no items at all (including agentIgnores)
|
|
102
|
+
if (allItems.length === 0 && agentIgnores.length === 0) {
|
|
103
|
+
return { written, warnings, unsupported };
|
|
104
|
+
}
|
|
105
|
+
// Track sources that had collisions for warning
|
|
106
|
+
const collisionSources = [];
|
|
107
|
+
// Ensure .cursor/rules directory exists (only if we have mdc items)
|
|
108
|
+
const rulesDir = path.join(root, '.cursor', 'rules');
|
|
109
|
+
if (allItems.length > 0) {
|
|
110
|
+
await fs.mkdir(rulesDir, { recursive: true });
|
|
111
|
+
}
|
|
112
|
+
// Emit each GlobalPrompt as a separate .mdc file
|
|
113
|
+
for (const gp of globalPrompts) {
|
|
114
|
+
const baseName = sanitizeFilename(gp.sourcePath) + '.mdc';
|
|
115
|
+
const { filename, collision } = getUniqueFilename(baseName, usedFilenames);
|
|
116
|
+
if (collision) {
|
|
117
|
+
collisionSources.push(gp.sourcePath);
|
|
118
|
+
}
|
|
119
|
+
const filepath = path.join(rulesDir, filename);
|
|
120
|
+
const content = formatGlobalPromptMdc(gp.content);
|
|
121
|
+
await fs.writeFile(filepath, content, 'utf-8');
|
|
122
|
+
written.push({
|
|
123
|
+
path: filepath,
|
|
124
|
+
type: CustomizationType.GlobalPrompt,
|
|
125
|
+
itemCount: 1,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
// Emit each FileRule as a separate .mdc file with globs
|
|
129
|
+
for (const fr of fileRules) {
|
|
130
|
+
const baseName = sanitizeFilename(fr.sourcePath) + '.mdc';
|
|
131
|
+
const { filename, collision } = getUniqueFilename(baseName, usedFilenames);
|
|
132
|
+
if (collision) {
|
|
133
|
+
collisionSources.push(fr.sourcePath);
|
|
134
|
+
}
|
|
135
|
+
const filepath = path.join(rulesDir, filename);
|
|
136
|
+
const content = formatFileRuleMdc(fr.content, fr.globs);
|
|
137
|
+
await fs.writeFile(filepath, content, 'utf-8');
|
|
138
|
+
written.push({
|
|
139
|
+
path: filepath,
|
|
140
|
+
type: CustomizationType.FileRule,
|
|
141
|
+
itemCount: 1,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
// Emit each AgentSkill as a separate .mdc file with description
|
|
145
|
+
for (const skill of agentSkills) {
|
|
146
|
+
const baseName = sanitizeFilename(skill.sourcePath) + '.mdc';
|
|
147
|
+
const { filename, collision } = getUniqueFilename(baseName, usedFilenames);
|
|
148
|
+
if (collision) {
|
|
149
|
+
collisionSources.push(skill.sourcePath);
|
|
150
|
+
}
|
|
151
|
+
const filepath = path.join(rulesDir, filename);
|
|
152
|
+
const content = formatAgentSkillMdc(skill.content, skill.description);
|
|
153
|
+
await fs.writeFile(filepath, content, 'utf-8');
|
|
154
|
+
written.push({
|
|
155
|
+
path: filepath,
|
|
156
|
+
type: CustomizationType.AgentSkill,
|
|
157
|
+
itemCount: 1,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
// Emit warning if any collisions occurred
|
|
161
|
+
if (collisionSources.length > 0) {
|
|
162
|
+
warnings.push({
|
|
163
|
+
code: WarningCode.FileRenamed,
|
|
164
|
+
message: `Filename collision: ${collisionSources.length} file(s) renamed to avoid overwrite`,
|
|
165
|
+
sources: collisionSources,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
// === Emit AgentIgnores as .cursorignore ===
|
|
169
|
+
if (agentIgnores.length > 0) {
|
|
170
|
+
const allPatterns = agentIgnores.flatMap(ai => ai.patterns);
|
|
171
|
+
const uniquePatterns = [...new Set(allPatterns)];
|
|
172
|
+
const filepath = path.join(root, '.cursorignore');
|
|
173
|
+
await fs.writeFile(filepath, uniquePatterns.join('\n') + '\n', 'utf-8');
|
|
174
|
+
written.push({
|
|
175
|
+
path: filepath,
|
|
176
|
+
type: CustomizationType.AgentIgnore,
|
|
177
|
+
itemCount: agentIgnores.length,
|
|
178
|
+
});
|
|
179
|
+
if (agentIgnores.length > 1) {
|
|
180
|
+
warnings.push({
|
|
181
|
+
code: WarningCode.Merged,
|
|
182
|
+
message: `Merged ${agentIgnores.length} ignore sources into .cursorignore`,
|
|
183
|
+
sources: agentIgnores.map(ai => ai.sourcePath),
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return { written, warnings, unsupported };
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=emit.js.map
|
package/dist/emit.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emit.js","sourceRoot":"","sources":["../src/emit.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAqB,MAAM,gBAAgB,CAAC;AAIpE;;;GAGG;AACH,QAAA,MAAM,YAAY,EAAE,UAWnB,CAAC;AAEF,eAAe,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { CustomizationType } from '@a16njs/models';
|
|
2
|
+
import { discover } from './discover.js';
|
|
3
|
+
import { emit } from './emit.js';
|
|
4
|
+
/**
|
|
5
|
+
* Cursor IDE plugin for a16n.
|
|
6
|
+
* Supports discovery and emission of Cursor rules.
|
|
7
|
+
*/
|
|
8
|
+
const cursorPlugin = {
|
|
9
|
+
id: 'cursor',
|
|
10
|
+
name: 'Cursor IDE',
|
|
11
|
+
supports: [
|
|
12
|
+
CustomizationType.GlobalPrompt,
|
|
13
|
+
CustomizationType.FileRule,
|
|
14
|
+
CustomizationType.AgentSkill,
|
|
15
|
+
],
|
|
16
|
+
discover,
|
|
17
|
+
emit,
|
|
18
|
+
};
|
|
19
|
+
export default cursorPlugin;
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;GAGG;AACH,MAAM,YAAY,GAAe;IAC/B,EAAE,EAAE,QAAQ;IACZ,IAAI,EAAE,YAAY;IAClB,QAAQ,EAAE;QACR,iBAAiB,CAAC,YAAY;QAC9B,iBAAiB,CAAC,QAAQ;QAC1B,iBAAiB,CAAC,UAAU;KAC7B;IAED,QAAQ;IACR,IAAI;CACL,CAAC;AAEF,eAAe,YAAY,CAAC"}
|
package/dist/mdc.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MDC (Markdown Configuration) parsing utilities.
|
|
3
|
+
* Uses regex-based line parsing instead of YAML to avoid parsing issues
|
|
4
|
+
* with Cursor's non-standard frontmatter format.
|
|
5
|
+
*/
|
|
6
|
+
export interface MdcFrontmatter {
|
|
7
|
+
alwaysApply?: boolean;
|
|
8
|
+
description?: string;
|
|
9
|
+
globs?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ParsedMdc {
|
|
12
|
+
frontmatter: MdcFrontmatter;
|
|
13
|
+
body: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Parse MDC file content into frontmatter and body.
|
|
17
|
+
* Uses line-by-line regex parsing for safety with Cursor's format.
|
|
18
|
+
*/
|
|
19
|
+
export declare function parseMdc(content: string): ParsedMdc;
|
|
20
|
+
//# sourceMappingURL=mdc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mdc.d.ts","sourceRoot":"","sources":["../src/mdc.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,cAAc;IAC7B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,cAAc,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CA8DnD"}
|
package/dist/mdc.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MDC (Markdown Configuration) parsing utilities.
|
|
3
|
+
* Uses regex-based line parsing instead of YAML to avoid parsing issues
|
|
4
|
+
* with Cursor's non-standard frontmatter format.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Parse MDC file content into frontmatter and body.
|
|
8
|
+
* Uses line-by-line regex parsing for safety with Cursor's format.
|
|
9
|
+
*/
|
|
10
|
+
export function parseMdc(content) {
|
|
11
|
+
const frontmatter = {};
|
|
12
|
+
// Check for frontmatter delimiters
|
|
13
|
+
const lines = content.split('\n');
|
|
14
|
+
// Find frontmatter boundaries
|
|
15
|
+
let frontmatterStart = -1;
|
|
16
|
+
let frontmatterEnd = -1;
|
|
17
|
+
for (let i = 0; i < lines.length; i++) {
|
|
18
|
+
const line = lines[i]?.trim();
|
|
19
|
+
if (line === '---') {
|
|
20
|
+
if (frontmatterStart === -1) {
|
|
21
|
+
frontmatterStart = i;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
frontmatterEnd = i;
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// No frontmatter found
|
|
30
|
+
if (frontmatterStart === -1 || frontmatterEnd === -1) {
|
|
31
|
+
return {
|
|
32
|
+
frontmatter: {},
|
|
33
|
+
body: content.trim(),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
// Parse frontmatter lines with regex
|
|
37
|
+
for (let i = frontmatterStart + 1; i < frontmatterEnd; i++) {
|
|
38
|
+
const line = lines[i];
|
|
39
|
+
if (!line)
|
|
40
|
+
continue;
|
|
41
|
+
// Parse alwaysApply: true/false
|
|
42
|
+
const alwaysApplyMatch = line.match(/^alwaysApply:\s*(true|false)\s*$/);
|
|
43
|
+
if (alwaysApplyMatch) {
|
|
44
|
+
frontmatter.alwaysApply = alwaysApplyMatch[1] === 'true';
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
// Parse description: "..."
|
|
48
|
+
const descriptionMatch = line.match(/^description:\s*["']?(.+?)["']?\s*$/);
|
|
49
|
+
if (descriptionMatch) {
|
|
50
|
+
frontmatter.description = descriptionMatch[1];
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
// Parse globs: <pattern> (Cursor uses comma-separated string, not YAML array)
|
|
54
|
+
const globsMatch = line.match(/^globs:\s*(.+)\s*$/);
|
|
55
|
+
if (globsMatch) {
|
|
56
|
+
frontmatter.globs = globsMatch[1];
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Extract body (everything after second ---)
|
|
61
|
+
const bodyLines = lines.slice(frontmatterEnd + 1);
|
|
62
|
+
const body = bodyLines.join('\n').trim();
|
|
63
|
+
return { frontmatter, body };
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=mdc.js.map
|
package/dist/mdc.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mdc.js","sourceRoot":"","sources":["../src/mdc.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAaH;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,MAAM,WAAW,GAAmB,EAAE,CAAC;IAEvC,mCAAmC;IACnC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAElC,8BAA8B;IAC9B,IAAI,gBAAgB,GAAG,CAAC,CAAC,CAAC;IAC1B,IAAI,cAAc,GAAG,CAAC,CAAC,CAAC;IAExB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;QAC9B,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;YACnB,IAAI,gBAAgB,KAAK,CAAC,CAAC,EAAE,CAAC;gBAC5B,gBAAgB,GAAG,CAAC,CAAC;YACvB,CAAC;iBAAM,CAAC;gBACN,cAAc,GAAG,CAAC,CAAC;gBACnB,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,uBAAuB;IACvB,IAAI,gBAAgB,KAAK,CAAC,CAAC,IAAI,cAAc,KAAK,CAAC,CAAC,EAAE,CAAC;QACrD,OAAO;YACL,WAAW,EAAE,EAAE;YACf,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE;SACrB,CAAC;IACJ,CAAC;IAED,qCAAqC;IACrC,KAAK,IAAI,CAAC,GAAG,gBAAgB,GAAG,CAAC,EAAE,CAAC,GAAG,cAAc,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3D,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,CAAC,IAAI;YAAE,SAAS;QAEpB,gCAAgC;QAChC,MAAM,gBAAgB,GAAG,IAAI,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACxE,IAAI,gBAAgB,EAAE,CAAC;YACrB,WAAW,CAAC,WAAW,GAAG,gBAAgB,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC;YACzD,SAAS;QACX,CAAC;QAED,2BAA2B;QAC3B,MAAM,gBAAgB,GAAG,IAAI,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;QAC3E,IAAI,gBAAgB,EAAE,CAAC;YACrB,WAAW,CAAC,WAAW,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC;YAC9C,SAAS;QACX,CAAC;QAED,8EAA8E;QAC9E,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;QACpD,IAAI,UAAU,EAAE,CAAC;YACf,WAAW,CAAC,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;YAClC,SAAS;QACX,CAAC;IACH,CAAC;IAED,6CAA6C;IAC7C,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC;IAClD,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IAEzC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;AAC/B,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@a16njs/plugin-cursor",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Cursor IDE plugin for a16n",
|
|
5
|
+
"license": "AGPL-3.0",
|
|
6
|
+
"author": "Texarkanine",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/Texarkanine/a16n.git",
|
|
10
|
+
"directory": "packages/plugin-cursor"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/Texarkanine/a16n#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/Texarkanine/a16n/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"a16n",
|
|
18
|
+
"plugin",
|
|
19
|
+
"cursor",
|
|
20
|
+
"ide",
|
|
21
|
+
"rules"
|
|
22
|
+
],
|
|
23
|
+
"type": "module",
|
|
24
|
+
"main": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"import": "./dist/index.js"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"files": ["dist"],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsc",
|
|
35
|
+
"clean": "rimraf dist *.tsbuildinfo",
|
|
36
|
+
"typecheck": "tsc --noEmit",
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"test:watch": "vitest"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@a16njs/models": "workspace:*"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/node": "^20.0.0",
|
|
45
|
+
"typescript": "^5.4.0",
|
|
46
|
+
"vitest": "^2.0.0"
|
|
47
|
+
}
|
|
48
|
+
}
|