From f13c00d1ba52f40f9151230c6f6f2093a9b681fb Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Thu, 21 Aug 2025 23:28:15 +0530 Subject: [PATCH] feat(cli): update ultracite rules and add autocomplete multiselect in ultracite and ruler --- apps/cli/package.json | 2 +- apps/cli/src/helpers/addons/ruler-setup.ts | 25 ++-- .../cli/src/helpers/addons/ultracite-setup.ts | 112 +++++++++++++----- apps/cli/src/prompts/project-name.ts | 2 +- bun.lock | 6 +- 5 files changed, 101 insertions(+), 46 deletions(-) diff --git a/apps/cli/package.json b/apps/cli/package.json index 3185c2c..b54e534 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -66,7 +66,7 @@ "dependencies": { "@biomejs/js-api": "^3.0.0", "@biomejs/wasm-nodejs": "^2.2.0", - "@clack/prompts": "^0.11.0", + "@clack/prompts": "^1.0.0-alpha.4", "consola": "^3.4.2", "execa": "^9.6.0", "fs-extra": "^11.3.1", diff --git a/apps/cli/src/helpers/addons/ruler-setup.ts b/apps/cli/src/helpers/addons/ruler-setup.ts index c22b8d1..f63aab4 100644 --- a/apps/cli/src/helpers/addons/ruler-setup.ts +++ b/apps/cli/src/helpers/addons/ruler-setup.ts @@ -1,5 +1,10 @@ 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 fs from "fs-extra"; import pc from "picocolors"; @@ -61,14 +66,16 @@ export async function setupVibeRules(config: ProjectConfig) { opencode: { label: "OpenCode" }, } as const; - const selectedEditors = await multiselect({ - message: "Select AI assistants for Ruler", - options: Object.entries(EDITORS).map(([key, v]) => ({ - value: key as keyof typeof EDITORS, - label: v.label, - })), - required: false, - }); + const selectedEditors = await autocompleteMultiselect( + { + message: "Select AI assistants for Ruler", + options: Object.entries(EDITORS).map(([key, v]) => ({ + value: key as keyof typeof EDITORS, + label: v.label, + })), + required: false, + }, + ); if (isCancel(selectedEditors)) return exitCancelled("Operation cancelled"); diff --git a/apps/cli/src/helpers/addons/ultracite-setup.ts b/apps/cli/src/helpers/addons/ultracite-setup.ts index fa108d0..182de58 100644 --- a/apps/cli/src/helpers/addons/ultracite-setup.ts +++ b/apps/cli/src/helpers/addons/ultracite-setup.ts @@ -1,4 +1,9 @@ -import { isCancel, log, multiselect } from "@clack/prompts"; +import { + autocompleteMultiselect, + group, + log, + multiselect, +} from "@clack/prompts"; import { execa } from "execa"; import pc from "picocolors"; import type { ProjectConfig } from "../../types"; @@ -14,43 +19,79 @@ type UltraciteRule = | "windsurf" | "zed" | "claude" - | "codex"; + | "codex" + | "kiro" + | "cline" + | "amp" + | "aider" + | "firebase-studio" + | "open-hands" + | "gemini-cli" + | "junie" + | "augmentcode" + | "kilo-code" + | "goose"; const EDITORS = { vscode: { label: "VSCode / Cursor / Windsurf", - hint: "Visual Studio Code editor configuration", }, zed: { label: "Zed", - hint: "Zed editor configuration", }, } as const; const RULES = { "vscode-copilot": { label: "VS Code Copilot", - hint: "GitHub Copilot integration for VS Code", }, cursor: { label: "Cursor", - hint: "Cursor AI editor configuration", }, windsurf: { label: "Windsurf", - hint: "Windsurf editor configuration", }, zed: { label: "Zed", - hint: "Zed editor rules", }, claude: { label: "Claude", - hint: "Claude AI integration", }, 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; @@ -62,29 +103,36 @@ export async function setupUltracite(config: ProjectConfig, hasHusky: boolean) { await setupBiome(projectDir); - const editors = await multiselect({ - message: "Choose editors", - options: Object.entries(EDITORS).map(([key, editor]) => ({ - value: key as UltraciteEditor, - label: editor.label, - hint: editor.hint, - })), - required: true, - }); + const result = await group( + { + editors: () => + multiselect({ + message: "Choose editors", + options: Object.entries(EDITORS).map(([key, editor]) => ({ + value: key as UltraciteEditor, + label: editor.label, + })), + required: true, + }), + rules: () => + autocompleteMultiselect({ + message: "Choose rules", + options: Object.entries(RULES).map(([key, rule]) => ({ + value: key as UltraciteRule, + label: rule.label, + })), + required: true, + }), + }, + { + onCancel: () => { + exitCancelled("Operation cancelled"); + }, + }, + ); - if (isCancel(editors)) return exitCancelled("Operation cancelled"); - - const rules = await multiselect({ - message: "Choose rules", - options: Object.entries(RULES).map(([key, rule]) => ({ - value: key as UltraciteRule, - label: rule.label, - hint: rule.hint, - })), - required: true, - }); - - if (isCancel(rules)) return exitCancelled("Operation cancelled"); + const editors = result.editors as UltraciteEditor[]; + const rules = result.rules as UltraciteRule[]; const ultraciteArgs = ["init", "--pm", packageManager]; diff --git a/apps/cli/src/prompts/project-name.ts b/apps/cli/src/prompts/project-name.ts index a8319a2..8173ce2 100644 --- a/apps/cli/src/prompts/project-name.ts +++ b/apps/cli/src/prompts/project-name.ts @@ -60,7 +60,7 @@ export async function getProjectName(initialName?: string): Promise { initialValue: initialName, defaultValue: defaultName, validate: (value) => { - const nameToUse = value.trim() || defaultName; + const nameToUse = String(value ?? "").trim() || defaultName; const finalDirName = path.basename(nameToUse); const validationError = validateDirectoryName(finalDirName); diff --git a/bun.lock b/bun.lock index c6b5d69..21d4aff 100644 --- a/bun.lock +++ b/bun.lock @@ -22,7 +22,7 @@ "dependencies": { "@biomejs/js-api": "^3.0.0", "@biomejs/wasm-nodejs": "^2.2.0", - "@clack/prompts": "^0.11.0", + "@clack/prompts": "^1.0.0-alpha.4", "consola": "^3.4.2", "execa": "^9.6.0", "fs-extra": "^11.3.1", @@ -273,9 +273,9 @@ "@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=="],