Skip to content

Commit 69004ba

Browse files
committed
added cursor rules new format
1 parent 5b2a964 commit 69004ba

12 files changed

Lines changed: 300 additions & 45 deletions

File tree

app/existing/[repoUrl]/repo-scan-client.tsx

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -112,26 +112,26 @@ export default function RepoScanClient({ initialRepoUrl }: RepoScanClientProps)
112112
}))
113113
}, [scanResult])
114114

115-
const handleStartScan = () => {
116-
if (!repoUrlForScan) {
117-
return
118-
}
119-
120-
setHasConfirmed(true)
121-
setScanToken((token) => token + 1)
115+
const handleStartScan = () => {
116+
if (!repoUrlForScan) {
117+
return
118+
}
122119

123-
track(ANALYTICS_EVENTS.REPO_SCAN_START, { repo: repoUrlForScan })
124-
}
120+
setHasConfirmed(true)
121+
setScanToken((token) => token + 1)
125122

126-
const handleRetryScan = () => {
127-
if (!repoUrlForScan) {
128-
return
123+
track(ANALYTICS_EVENTS.REPO_SCAN_START, { repo: repoUrlForScan })
129124
}
130125

131-
setScanToken((token) => token + 1)
126+
const handleRetryScan = () => {
127+
if (!repoUrlForScan) {
128+
return
129+
}
132130

133-
track(ANALYTICS_EVENTS.REPO_SCAN_RETRY, { repo: repoUrlForScan })
134-
}
131+
setScanToken((token) => token + 1)
132+
133+
track(ANALYTICS_EVENTS.REPO_SCAN_RETRY, { repo: repoUrlForScan })
134+
}
135135

136136
const warnings = scanResult?.warnings ?? []
137137
const stackMeta = scanResult?.conventions ?? null
@@ -372,7 +372,16 @@ export default function RepoScanClient({ initialRepoUrl }: RepoScanClientProps)
372372
disabled={busy}
373373
className="flex h-[36px] items-center rounded-full px-4 py-0 text-sm"
374374
>
375-
{busy ? `Generating ${file.filename}…` : `Generate ${file.filename}`}
375+
<div className="flex items-center gap-2">
376+
<span>
377+
{busy ? `Generating ${file.filename}…` : `Generate ${file.filename}`}
378+
</span>
379+
{file.isLegacy && (
380+
<span className="rounded-full bg-amber-950/30 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider text-white">
381+
Legacy
382+
</span>
383+
)}
384+
</div>
376385
</Button>
377386
)
378387
})}

app/new/stack/stack-summary-page.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -388,9 +388,16 @@ export function StackSummaryPage({ stackId, mode }: StackSummaryPageProps) {
388388
disabled={isGenerating}
389389
className="flex h-[38px] min-w-[190px] items-center justify-center rounded-full px-6 py-0 text-base leading-none shadow-lg shadow-primary/20"
390390
>
391-
<span className="text-sm font-semibold text-foreground">
392-
{isGenerating ? `Generating ${file.filename}…` : `Generate ${file.filename}`}
393-
</span>
391+
<div className="flex items-center gap-2">
392+
<span className="text-sm font-semibold text-foreground">
393+
{isGenerating ? `Generating ${file.filename}…` : `Generate ${file.filename}`}
394+
</span>
395+
{file.isLegacy && (
396+
<span className="rounded-full bg-amber-950/30 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider text-white">
397+
Legacy
398+
</span>
399+
)}
400+
</div>
394401
</Button>
395402
)
396403
})}

components/final-output-view.tsx

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,51 @@ export default function FinalOutputView({ fileName, fileContent, mimeType, onClo
9898
}, COPY_RESET_DELAY)
9999
}, [currentContent])
100100

101-
const handleDownloadClick = useCallback(() => {
101+
const handleDownloadClick = useCallback(async () => {
102102
if (!currentContent) {
103103
return
104104
}
105105

106+
if (normalizedFileName.endsWith('.zip')) {
107+
try {
108+
const { default: JSZip } = await import('jszip')
109+
const zip = new JSZip()
110+
111+
// Split combined content by the FILE delimiter
112+
const parts = currentContent.split('--- FILE: ')
113+
let foundFiles = false
114+
115+
for (const part of parts) {
116+
if (!part.trim()) continue
117+
118+
const lines = part.split('\n')
119+
const firstLine = lines[0].trim()
120+
if (firstLine.endsWith(' ---')) {
121+
const fileName = firstLine.replace(' ---', '').trim()
122+
const content = lines.slice(1).join('\n').trim()
123+
zip.file(fileName, content)
124+
foundFiles = true
125+
}
126+
}
127+
128+
if (foundFiles) {
129+
const blob = await zip.generateAsync({ type: 'blob' })
130+
const url = URL.createObjectURL(blob)
131+
const link = document.createElement("a")
132+
link.href = url
133+
link.download = normalizedFileName
134+
link.style.display = "none"
135+
document.body.appendChild(link)
136+
link.click()
137+
document.body.removeChild(link)
138+
URL.revokeObjectURL(url)
139+
return
140+
}
141+
} catch (error) {
142+
console.error('Failed to generate ZIP:', error)
143+
}
144+
}
145+
106146
const downloadMimeType = mimeType ?? "text/plain;charset=utf-8"
107147
const blob = new Blob([currentContent], { type: downloadMimeType })
108148
const url = URL.createObjectURL(blob)

data/files.json

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,22 @@
2424
"enabled": true
2525
},
2626
{
27-
"value": "cursor-rules",
28-
"label": "Cursor Rules",
27+
"value": "cursor-rules-legacy",
28+
"label": "Cursor Rules (JSON)",
2929
"filename": ".cursor/rules",
3030
"format": "json",
31-
"docs": "https://docs.cursor.com/workflows/rules",
31+
"docs": "https://docs.cursor.com/context/rules",
32+
"enabled": true,
33+
"isLegacy": true
34+
},
35+
{
36+
"value": "cursor-rules",
37+
"label": "Cursor Rules (.mdc + ZIP)",
38+
"filename": "cursor-rules.zip",
39+
"format": "zip",
40+
"docs": "https://cursor.com/docs/context/rules",
3241
"enabled": true
3342
}
3443
]
3544
}
36-
]
45+
]
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
--- FILE: .cursor/rules/tech-stack.mdc ---
2+
---
3+
description: Application core tech stack and framework guidance
4+
globs: ["**/*"]
5+
alwaysApply: true
6+
---
7+
8+
# Tech Stack: {{stackSelection}}
9+
10+
- **Language**: {{language}}
11+
- **Framework**: {{stackSelection}}
12+
- **Tooling**: {{tooling}}
13+
14+
## Core Guidance
15+
{{stackGuidance}}
16+
17+
--- FILE: .cursor/rules/naming.mdc ---
18+
---
19+
description: Naming conventions for variables, files, and components
20+
globs: ["**/*.{ts,tsx,js,jsx,py,go,rs}"]
21+
alwaysApply: false
22+
---
23+
24+
# Naming Conventions
25+
26+
- **Variables & Functions**: {{variableNaming}}
27+
- **Files & Modules**: {{fileNaming}}
28+
- **Components & Types**: {{componentNaming}}
29+
- **Exports**: {{exports}}
30+
31+
--- FILE: .cursor/rules/testing.mdc ---
32+
---
33+
description: Testing strategy and quality assurance
34+
globs: ["**/*.test.{ts,tsx,js,jsx}", "**/*_test.go", "**/test_*.py", "**/__tests__/**/*"]
35+
alwaysApply: false
36+
---
37+
38+
# Testing Strategy
39+
40+
- **Unit Testing**: {{testingUT}}
41+
- **E2E Testing**: {{testingE2E}}
42+
43+
## Requirements
44+
- Use descriptive test names.
45+
- Cover both success and failure cases.
46+
- Place tests in appropriate directories based on project structure.
47+
48+
--- FILE: .cursor/rules/security.mdc ---
49+
---
50+
description: Security, validation, and logging rules
51+
globs: ["**/*"]
52+
alwaysApply: false
53+
---
54+
55+
# Security & Quality
56+
57+
- **Auth & Secrets**: {{auth}}
58+
- **Validation**: {{validation}}
59+
- **Logging**: {{logging}}
60+
61+
## Rules
62+
- Never commit secrets to version control.
63+
- Validate all external data inputs.
64+
- Use structured logging; avoid logging sensitive information.
65+
66+
--- FILE: .cursor/rules/collaboration.mdc ---
67+
---
68+
description: Commit messages and PR conventions
69+
globs: [".git/**/*", "**/*"]
70+
alwaysApply: false
71+
---
72+
73+
# Collaboration
74+
75+
- **Commit Style**: {{commitStyle}}
76+
- **PR Rules**: {{prRules}}
77+
- **Collaboration Style**: {{collaboration}}
78+
79+
Follow project-specific conventions for small, focused changes.

lib/scan-generate.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { ANALYTICS_EVENTS } from "@/lib/analytics-events"
99

1010
const fileOptions = getFileOptions()
1111

12-
export type OutputFileId = "instructions-md" | "agents-md" | "cursor-rules"
12+
export type OutputFileId = "instructions-md" | "agents-md" | "cursor-rules" | "cursor-rules-legacy"
1313

1414
export async function generateFromRepoScan(
1515
scan: RepoScanSummary,

lib/template-config.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,16 @@ export const templateCombinations: Record<string, TemplateConfig> = {
105105
template: 'agents-template.md',
106106
outputFileName: 'agents.md',
107107
},
108-
// Cursor rules
109-
'cursor-rules': {
108+
// Cursor rules (Legacy)
109+
'cursor-rules-legacy': {
110110
template: 'cursor-rules-template.json',
111111
outputFileName: '.cursor/rules',
112112
},
113+
// New Cursor rules (MDC + ZIP)
114+
'cursor-rules': {
115+
template: 'cursor-rules-template-mdc.md',
116+
outputFileName: 'cursor-rules.zip',
117+
},
113118
// Generic JSON rules (placeholder)
114119
'json-rules': {
115120
template: 'copilot-instructions-template.md',

lib/template-render.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,14 +97,13 @@ export async function renderTemplate({
9797
}
9898

9999
const replaceVariable = (key: keyof WizardResponses, fallback = 'Not specified') => {
100-
const placeholder = `{{${String(key)}}}`
100+
const placeholder = new RegExp(`{{${String(key)}}}`, 'g')
101101

102-
if (!generatedContent.includes(placeholder)) {
102+
if (!template.includes(`{{${String(key)}}}`)) {
103103
return
104104
}
105105

106106
const value = responses[key]
107-
108107
const defaultMeta = defaultedResponses?.[key]
109108

110109
if (value === null || value === undefined || value === '') {
@@ -149,8 +148,8 @@ export async function renderTemplate({
149148
replaceVariable('outputFile')
150149

151150
const replaceStaticPlaceholder = (placeholderKey: string, value: string) => {
152-
const placeholder = `{{${placeholderKey}}}`
153-
if (!generatedContent.includes(placeholder)) {
151+
const placeholder = new RegExp(`{{${placeholderKey}}}`, 'g')
152+
if (!template.includes(`{{${placeholderKey}}}`)) {
154153
return
155154
}
156155
const replacement = isJsonTemplate ? escapeForJson(value) : value

lib/wizard-utils.ts

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -65,18 +65,18 @@ export const buildStepFromQuestionSet = (
6565
title: string,
6666
questions: DataQuestionSource[]
6767
): WizardStep => ({
68-
id,
69-
title,
70-
questions: questions.map((question) => ({
71-
id: question.id,
72-
question: question.question,
73-
allowMultiple: question.allowMultiple,
74-
responseKey: question.responseKey,
75-
isReadOnlyOnSummary: question.isReadOnlyOnSummary,
76-
enableFilter: question.enableFilter,
77-
answers: question.answers.map(mapAnswerSourceToWizard),
78-
freeText: question.freeText,
79-
})),
68+
id,
69+
title,
70+
questions: questions.map((question) => ({
71+
id: question.id,
72+
question: question.question,
73+
allowMultiple: question.allowMultiple,
74+
responseKey: question.responseKey,
75+
isReadOnlyOnSummary: question.isReadOnlyOnSummary,
76+
enableFilter: question.enableFilter,
77+
answers: question.answers.map(mapAnswerSourceToWizard),
78+
freeText: question.freeText,
79+
})),
8080
})
8181

8282
export const buildFileOptionsFromQuestion = (
@@ -98,19 +98,22 @@ export const buildFileOptionsFromQuestion = (
9898
icon: answer.icon,
9999
docs: answer.docs,
100100
isDefault: answer.isDefault,
101+
isLegacy: answer.isLegacy,
101102
}))
102103
}
103104

104105
const formatLabelMap: Record<string, string> = {
105106
markdown: "Markdown",
106107
json: "JSON",
107108
"cursor-rules-json": "JSON",
109+
zip: "ZIP Archive",
108110
}
109111

110112
const formatMimeTypeMap: Record<string, string> = {
111113
markdown: "text/markdown",
112114
json: "application/json",
113115
"cursor-rules-json": "application/json",
116+
zip: "application/zip",
114117
}
115118

116119
/**

0 commit comments

Comments
 (0)