diff --git a/.changeset/nasty-nails-repair.md b/.changeset/nasty-nails-repair.md new file mode 100644 index 000000000..4278f0992 --- /dev/null +++ b/.changeset/nasty-nails-repair.md @@ -0,0 +1,5 @@ +--- +"lingo.dev": minor +--- + +feat: add init cursor command for .cursorrules setup diff --git a/packages/cli/src/cli/cmd/assets/agents.md b/packages/cli/src/cli/cmd/assets/agents.md new file mode 100644 index 000000000..d66203797 --- /dev/null +++ b/packages/cli/src/cli/cmd/assets/agents.md @@ -0,0 +1,10 @@ +# Cursor AI i18n Rules +The following rules and guidelines should be followed to ensure proper internationalization (i18n) support in Cursor AI agents: +1. **Use translation keys**: All user-facing strings must use translation keys instead of hardcoded text. Reference the appropriate key from your locale files. +2. **Locale files**: Store translations in locale-specific files (e.g., `en.json`, `fr.json`). Ensure all supported languages are kept in sync. +3. **Fallback language**: Always provide a fallback language (usually English) for missing translations. +4. **Pluralization and formatting**: Use i18n libraries that support pluralization, date, and number formatting according to the user's locale. +5. **No concatenation**: Avoid string concatenation for translatable text. Use interpolation features provided by your i18n library. +6. **Contextual translations**: Provide context for translators where necessary, especially for ambiguous terms. +7. **Testing**: Test agents in multiple locales to ensure all strings are translated and formatting is correct. +_For more details, refer to the Cursor AI i18n documentation or contact the localization team._ diff --git a/packages/cli/src/cli/cmd/init.ts b/packages/cli/src/cli/cmd/init.ts index fed7b41d0..40f7522f3 100644 --- a/packages/cli/src/cli/cmd/init.ts +++ b/packages/cli/src/cli/cmd/init.ts @@ -19,6 +19,7 @@ import { ensurePatterns } from "../utils/ensure-patterns"; import updateGitignore from "../utils/update-gitignore"; import initCICD from "../utils/init-ci-cd"; import open from "open"; +import cursorInitCmd from "./init/cursor"; const openUrl = (path: string) => { const settings = getSettings(undefined); @@ -116,7 +117,6 @@ export default new InteractiveCommand() throw new Error(`Invalid path: ${p}`); } } - return values; }) .prompt(undefined) // make non-interactive @@ -258,4 +258,5 @@ export default new InteractiveCommand() if (!isInteractive) { Ora().info("Please see https://lingo.dev/cli"); } - }); + }) + .addCommand(cursorInitCmd); diff --git a/packages/cli/src/cli/cmd/init/cursor.ts b/packages/cli/src/cli/cmd/init/cursor.ts new file mode 100644 index 000000000..bfb369e15 --- /dev/null +++ b/packages/cli/src/cli/cmd/init/cursor.ts @@ -0,0 +1,54 @@ +import { InteractiveCommand, InteractiveOption } from "interactive-commander"; +import Ora from "ora"; +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; +import { confirm } from "@inquirer/prompts"; + +// Get the directory of this file (works in both dev and production) +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +// Access agents.md from assets directory (bundled with published package) +const AGENTS_MD = path.resolve(__dirname, "../assets/agents.md"); +// Create .cursorrules in user's current working directory (their project) +const CURSORRULES = path.resolve(process.cwd(), ".cursorrules"); + +export default new InteractiveCommand() + .command("cursor") + .description("Initialize .cursorrules with i18n-specific instructions for Cursor AI.") + .addOption( + new InteractiveOption("-f, --force", "Overwrite .cursorrules without prompt.") + .default(false) + ) + .action(async (options) => { + const spinner = Ora(); + // Read agents.md + let template: string; + try { + template = fs.readFileSync(AGENTS_MD, "utf-8"); + } catch (err) { + spinner.fail("Template file agents.md not found. Please reinstall the package."); + return process.exit(1); + } + // Check for existing .cursorrules + const exists = fs.existsSync(CURSORRULES); + let shouldWrite; + if (exists && !options.force) { + shouldWrite = await confirm({ + message: ".cursorrules already exists. Overwrite?", + }); + if (!shouldWrite) { + spinner.info("Skipped: .cursorrules left unchanged."); + return; + } + } + try { + fs.writeFileSync(CURSORRULES, template); + spinner.succeed("Created .cursorrules"); + spinner.info( + ".cursorrules has been created with i18n-specific instructions for Cursor AI.", + ); + } catch (err) { + spinner.fail(`Failed to write .cursorrules: ${err}`); + process.exit(1); + } + });