feat(cli): update ultracite rules and add autocomplete multiselect in ultracite and ruler

This commit is contained in:
Aman Varshney
2025-08-21 23:28:15 +05:30
parent 869063df7e
commit f13c00d1ba
5 changed files with 101 additions and 46 deletions

View File

@@ -66,7 +66,7 @@
"dependencies": { "dependencies": {
"@biomejs/js-api": "^3.0.0", "@biomejs/js-api": "^3.0.0",
"@biomejs/wasm-nodejs": "^2.2.0", "@biomejs/wasm-nodejs": "^2.2.0",
"@clack/prompts": "^0.11.0", "@clack/prompts": "^1.0.0-alpha.4",
"consola": "^3.4.2", "consola": "^3.4.2",
"execa": "^9.6.0", "execa": "^9.6.0",
"fs-extra": "^11.3.1", "fs-extra": "^11.3.1",

View File

@@ -1,5 +1,10 @@
import path from "node:path"; import path from "node:path";
import { isCancel, log, multiselect, spinner } from "@clack/prompts"; import {
isCancel,
log,
autocompleteMultiselect,
spinner,
} from "@clack/prompts";
import { execa } from "execa"; import { execa } from "execa";
import fs from "fs-extra"; import fs from "fs-extra";
import pc from "picocolors"; import pc from "picocolors";
@@ -61,14 +66,16 @@ export async function setupVibeRules(config: ProjectConfig) {
opencode: { label: "OpenCode" }, opencode: { label: "OpenCode" },
} as const; } as const;
const selectedEditors = await multiselect<keyof typeof EDITORS>({ const selectedEditors = await autocompleteMultiselect<keyof typeof EDITORS>(
{
message: "Select AI assistants for Ruler", message: "Select AI assistants for Ruler",
options: Object.entries(EDITORS).map(([key, v]) => ({ options: Object.entries(EDITORS).map(([key, v]) => ({
value: key as keyof typeof EDITORS, value: key as keyof typeof EDITORS,
label: v.label, label: v.label,
})), })),
required: false, required: false,
}); },
);
if (isCancel(selectedEditors)) return exitCancelled("Operation cancelled"); if (isCancel(selectedEditors)) return exitCancelled("Operation cancelled");

View File

@@ -1,4 +1,9 @@
import { isCancel, log, multiselect } from "@clack/prompts"; import {
autocompleteMultiselect,
group,
log,
multiselect,
} from "@clack/prompts";
import { execa } from "execa"; import { execa } from "execa";
import pc from "picocolors"; import pc from "picocolors";
import type { ProjectConfig } from "../../types"; import type { ProjectConfig } from "../../types";
@@ -14,43 +19,79 @@ type UltraciteRule =
| "windsurf" | "windsurf"
| "zed" | "zed"
| "claude" | "claude"
| "codex"; | "codex"
| "kiro"
| "cline"
| "amp"
| "aider"
| "firebase-studio"
| "open-hands"
| "gemini-cli"
| "junie"
| "augmentcode"
| "kilo-code"
| "goose";
const EDITORS = { const EDITORS = {
vscode: { vscode: {
label: "VSCode / Cursor / Windsurf", label: "VSCode / Cursor / Windsurf",
hint: "Visual Studio Code editor configuration",
}, },
zed: { zed: {
label: "Zed", label: "Zed",
hint: "Zed editor configuration",
}, },
} as const; } as const;
const RULES = { const RULES = {
"vscode-copilot": { "vscode-copilot": {
label: "VS Code Copilot", label: "VS Code Copilot",
hint: "GitHub Copilot integration for VS Code",
}, },
cursor: { cursor: {
label: "Cursor", label: "Cursor",
hint: "Cursor AI editor configuration",
}, },
windsurf: { windsurf: {
label: "Windsurf", label: "Windsurf",
hint: "Windsurf editor configuration",
}, },
zed: { zed: {
label: "Zed", label: "Zed",
hint: "Zed editor rules",
}, },
claude: { claude: {
label: "Claude", label: "Claude",
hint: "Claude AI integration",
}, },
codex: { codex: {
label: "Codex", label: "Codex",
hint: "Codex AI integration", },
kiro: {
label: "Kiro",
},
cline: {
label: "Cline",
},
amp: {
label: "Amp",
},
aider: {
label: "Aider",
},
"firebase-studio": {
label: "Firebase Studio",
},
"open-hands": {
label: "Open Hands",
},
"gemini-cli": {
label: "Gemini CLI",
},
junie: {
label: "Junie",
},
augmentcode: {
label: "AugmentCode",
},
"kilo-code": {
label: "Kilo Code",
},
goose: {
label: "Goose",
}, },
} as const; } as const;
@@ -62,29 +103,36 @@ export async function setupUltracite(config: ProjectConfig, hasHusky: boolean) {
await setupBiome(projectDir); await setupBiome(projectDir);
const editors = await multiselect<UltraciteEditor>({ const result = await group(
{
editors: () =>
multiselect<UltraciteEditor>({
message: "Choose editors", message: "Choose editors",
options: Object.entries(EDITORS).map(([key, editor]) => ({ options: Object.entries(EDITORS).map(([key, editor]) => ({
value: key as UltraciteEditor, value: key as UltraciteEditor,
label: editor.label, label: editor.label,
hint: editor.hint,
})), })),
required: true, required: true,
}); }),
rules: () =>
if (isCancel(editors)) return exitCancelled("Operation cancelled"); autocompleteMultiselect<UltraciteRule>({
const rules = await multiselect<UltraciteRule>({
message: "Choose rules", message: "Choose rules",
options: Object.entries(RULES).map(([key, rule]) => ({ options: Object.entries(RULES).map(([key, rule]) => ({
value: key as UltraciteRule, value: key as UltraciteRule,
label: rule.label, label: rule.label,
hint: rule.hint,
})), })),
required: true, required: true,
}); }),
},
{
onCancel: () => {
exitCancelled("Operation cancelled");
},
},
);
if (isCancel(rules)) return exitCancelled("Operation cancelled"); const editors = result.editors as UltraciteEditor[];
const rules = result.rules as UltraciteRule[];
const ultraciteArgs = ["init", "--pm", packageManager]; const ultraciteArgs = ["init", "--pm", packageManager];

View File

@@ -60,7 +60,7 @@ export async function getProjectName(initialName?: string): Promise<string> {
initialValue: initialName, initialValue: initialName,
defaultValue: defaultName, defaultValue: defaultName,
validate: (value) => { validate: (value) => {
const nameToUse = value.trim() || defaultName; const nameToUse = String(value ?? "").trim() || defaultName;
const finalDirName = path.basename(nameToUse); const finalDirName = path.basename(nameToUse);
const validationError = validateDirectoryName(finalDirName); const validationError = validateDirectoryName(finalDirName);

View File

@@ -22,7 +22,7 @@
"dependencies": { "dependencies": {
"@biomejs/js-api": "^3.0.0", "@biomejs/js-api": "^3.0.0",
"@biomejs/wasm-nodejs": "^2.2.0", "@biomejs/wasm-nodejs": "^2.2.0",
"@clack/prompts": "^0.11.0", "@clack/prompts": "^1.0.0-alpha.4",
"consola": "^3.4.2", "consola": "^3.4.2",
"execa": "^9.6.0", "execa": "^9.6.0",
"fs-extra": "^11.3.1", "fs-extra": "^11.3.1",
@@ -273,9 +273,9 @@
"@biomejs/wasm-nodejs": ["@biomejs/wasm-nodejs@2.2.0", "", {}, "sha512-jTN0IdKqt8EOPAzMGYKzeNykOKljZBRwABO2N6rEaC5CzJEhS3CuApdjqWpK/TMsVfw694gA3qhNNRhyFI2mWg=="], "@biomejs/wasm-nodejs": ["@biomejs/wasm-nodejs@2.2.0", "", {}, "sha512-jTN0IdKqt8EOPAzMGYKzeNykOKljZBRwABO2N6rEaC5CzJEhS3CuApdjqWpK/TMsVfw694gA3qhNNRhyFI2mWg=="],
"@clack/core": ["@clack/core@0.5.0", "", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow=="], "@clack/core": ["@clack/core@1.0.0-alpha.4", "", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-VCtU+vjyKPMSakVrB9q1bOnXN7QW/w4+YQDQCOF59GrzydW+169i0fVx/qzRRXJgt8KGj/pZZ/JxXroFZIDByg=="],
"@clack/prompts": ["@clack/prompts@0.11.0", "", { "dependencies": { "@clack/core": "0.5.0", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw=="], "@clack/prompts": ["@clack/prompts@1.0.0-alpha.4", "", { "dependencies": { "@clack/core": "1.0.0-alpha.4", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-KnmtDF2xQGoI5AlBme9akHtvCRV0RKAARUXHBQO2tMwnY8B08/4zPWigT7uLK25UPrMCEqnyQPkKRjNdhPbf8g=="],
"@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.0", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA=="], "@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.0", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA=="],