feat(cli): add vibe rules addon (#481)

This commit is contained in:
Aman Varshney
2025-08-09 12:06:23 +05:30
committed by GitHub
parent 9005a432cf
commit 6cf476a21e
42 changed files with 429 additions and 302 deletions

View File

@@ -1,11 +1,11 @@
import { cancel, groupMultiselect, isCancel } from "@clack/prompts";
import pc from "picocolors";
import { groupMultiselect, isCancel } from "@clack/prompts";
import { DEFAULT_CONFIG } from "../constants";
import { type Addons, AddonsSchema, type Frontend } from "../types";
import {
getCompatibleAddons,
validateAddonCompatibility,
} from "../utils/addon-compatibility";
import { exitCancelled } from "../utils/errors";
type AddonOption = {
value: Addons;
@@ -42,6 +42,10 @@ function getAddonDisplay(addon: Addons): { label: string; hint: string } {
label = "Ultracite";
hint = "Zero-config Biome preset with AI integration";
break;
case "vibe-rules":
label = "vibe-rules";
hint = "Install and apply BTS rules to editors";
break;
case "husky":
label = "Husky";
hint = "Modern native Git hooks made easy";
@@ -65,7 +69,7 @@ function getAddonDisplay(addon: Addons): { label: string; hint: string } {
const ADDON_GROUPS = {
Documentation: ["starlight", "fumadocs"],
Linting: ["biome", "oxlint", "ultracite"],
Other: ["turborepo", "pwa", "tauri", "husky"],
Other: ["vibe-rules", "turborepo", "pwa", "tauri", "husky"],
};
export async function getAddonsChoice(
@@ -119,10 +123,7 @@ export async function getAddonsChoice(
selectableGroups: false,
});
if (isCancel(response)) {
cancel(pc.red("Operation cancelled"));
process.exit(0);
}
if (isCancel(response)) return exitCancelled("Operation cancelled");
return response;
}
@@ -175,10 +176,7 @@ export async function getAddonsToAdd(
selectableGroups: false,
});
if (isCancel(response)) {
cancel(pc.red("Operation cancelled"));
process.exit(0);
}
if (isCancel(response)) return exitCancelled("Operation cancelled");
return response;
}

View File

@@ -1,7 +1,7 @@
import { cancel, isCancel, select } from "@clack/prompts";
import pc from "picocolors";
import { isCancel, select } from "@clack/prompts";
import type { API, Backend, Frontend } from "../types";
import { allowedApisForFrontends } from "../utils/compatibility-rules";
import { exitCancelled } from "../utils/errors";
export async function getApiChoice(
Api?: API | undefined,
@@ -43,10 +43,7 @@ export async function getApiChoice(
initialValue: apiOptions[0].value,
});
if (isCancel(apiType)) {
cancel(pc.red("Operation cancelled"));
process.exit(0);
}
if (isCancel(apiType)) return exitCancelled("Operation cancelled");
return apiType;
}

View File

@@ -1,7 +1,7 @@
import { cancel, confirm, isCancel } from "@clack/prompts";
import pc from "picocolors";
import { confirm, isCancel } from "@clack/prompts";
import { DEFAULT_CONFIG } from "../constants";
import type { Backend } from "../types";
import { exitCancelled } from "../utils/errors";
export async function getAuthChoice(
auth: boolean | undefined,
@@ -21,10 +21,7 @@ export async function getAuthChoice(
initialValue: DEFAULT_CONFIG.auth,
});
if (isCancel(response)) {
cancel(pc.red("Operation cancelled"));
process.exit(0);
}
if (isCancel(response)) return exitCancelled("Operation cancelled");
return response;
}

View File

@@ -1,7 +1,7 @@
import { cancel, isCancel, select } from "@clack/prompts";
import pc from "picocolors";
import { isCancel, select } from "@clack/prompts";
import { DEFAULT_CONFIG } from "../constants";
import type { Backend, Frontend } from "../types";
import { exitCancelled } from "../utils/errors";
export async function getBackendFrameworkChoice(
backendFramework?: Backend,
@@ -63,10 +63,7 @@ export async function getBackendFrameworkChoice(
initialValue: DEFAULT_CONFIG.backend,
});
if (isCancel(response)) {
cancel(pc.red("Operation cancelled"));
process.exit(0);
}
if (isCancel(response)) return exitCancelled("Operation cancelled");
return response;
}

View File

@@ -1,5 +1,4 @@
import { cancel, group } from "@clack/prompts";
import pc from "picocolors";
import { group } from "@clack/prompts";
import type {
Addons,
API,
@@ -14,6 +13,7 @@ import type {
Runtime,
WebDeploy,
} from "../types";
import { exitCancelled } from "../utils/errors";
import { getAddonsChoice } from "./addons";
import { getApiChoice } from "./api";
import { getAuthChoice } from "./auth";
@@ -102,10 +102,7 @@ export async function gatherConfig(
install: () => getinstallChoice(flags.install),
},
{
onCancel: () => {
cancel(pc.red("Operation cancelled"));
process.exit(0);
},
onCancel: () => exitCancelled("Operation cancelled"),
},
);

View File

@@ -1,6 +1,6 @@
import { cancel, isCancel, select } from "@clack/prompts";
import pc from "picocolors";
import { isCancel, select } from "@clack/prompts";
import type { Backend, DatabaseSetup, ORM, Runtime } from "../types";
import { exitCancelled } from "../utils/errors";
export async function getDBSetupChoice(
databaseType: string,
@@ -101,10 +101,7 @@ export async function getDBSetupChoice(
initialValue: "none",
});
if (isCancel(response)) {
cancel(pc.red("Operation cancelled"));
process.exit(0);
}
if (isCancel(response)) return exitCancelled("Operation cancelled");
return response;
}

View File

@@ -1,7 +1,7 @@
import { cancel, isCancel, select } from "@clack/prompts";
import pc from "picocolors";
import { isCancel, select } from "@clack/prompts";
import { DEFAULT_CONFIG } from "../constants";
import type { Backend, Database, Runtime } from "../types";
import { exitCancelled } from "../utils/errors";
export async function getDatabaseChoice(
database?: Database,
@@ -55,10 +55,7 @@ export async function getDatabaseChoice(
initialValue: DEFAULT_CONFIG.database,
});
if (isCancel(response)) {
cancel(pc.red("Operation cancelled"));
process.exit(0);
}
if (isCancel(response)) return exitCancelled("Operation cancelled");
return response;
}

View File

@@ -1,11 +1,11 @@
import { cancel, isCancel, multiselect } from "@clack/prompts";
import pc from "picocolors";
import { isCancel, multiselect } from "@clack/prompts";
import { DEFAULT_CONFIG } from "../constants";
import type { API, Backend, Database, Examples, Frontend } from "../types";
import {
isExampleAIAllowed,
isExampleTodoAllowed,
} from "../utils/compatibility-rules";
import { exitCancelled } from "../utils/errors";
export async function getExamplesChoice(
examples?: Examples[],
@@ -63,10 +63,7 @@ export async function getExamplesChoice(
),
});
if (isCancel(response)) {
cancel(pc.red("Operation cancelled"));
process.exit(0);
}
if (isCancel(response)) return exitCancelled("Operation cancelled");
return response;
}

View File

@@ -1,8 +1,8 @@
import { cancel, isCancel, multiselect, select } from "@clack/prompts";
import pc from "picocolors";
import { isCancel, multiselect, select } from "@clack/prompts";
import { DEFAULT_CONFIG } from "../constants";
import type { Backend, Frontend } from "../types";
import { isFrontendAllowedWithBackend } from "../utils/compatibility-rules";
import { exitCancelled } from "../utils/errors";
export async function getFrontendChoice(
frontendOptions?: Frontend[],
@@ -28,10 +28,7 @@ export async function getFrontendChoice(
initialValues: ["web"],
});
if (isCancel(frontendTypes)) {
cancel(pc.red("Operation cancelled"));
process.exit(0);
}
if (isCancel(frontendTypes)) return exitCancelled("Operation cancelled");
const result: Frontend[] = [];
@@ -69,7 +66,7 @@ export async function getFrontendChoice(
},
{
value: "tanstack-start" as const,
label: "TanStack Start (vite)",
label: "TanStack Start",
hint: "SSR, Server Functions, API Routes and more with TanStack Router",
},
];
@@ -84,10 +81,7 @@ export async function getFrontendChoice(
initialValue: DEFAULT_CONFIG.frontend[0],
});
if (isCancel(webFramework)) {
cancel(pc.red("Operation cancelled"));
process.exit(0);
}
if (isCancel(webFramework)) return exitCancelled("Operation cancelled");
result.push(webFramework);
}
@@ -110,10 +104,7 @@ export async function getFrontendChoice(
initialValue: "native-nativewind",
});
if (isCancel(nativeFramework)) {
cancel(pc.red("Operation cancelled"));
process.exit(0);
}
if (isCancel(nativeFramework)) return exitCancelled("Operation cancelled");
result.push(nativeFramework);
}

View File

@@ -1,6 +1,6 @@
import { cancel, confirm, isCancel } from "@clack/prompts";
import pc from "picocolors";
import { confirm, isCancel } from "@clack/prompts";
import { DEFAULT_CONFIG } from "../constants";
import { exitCancelled } from "../utils/errors";
export async function getGitChoice(git?: boolean) {
if (git !== undefined) return git;
@@ -10,10 +10,7 @@ export async function getGitChoice(git?: boolean) {
initialValue: DEFAULT_CONFIG.git,
});
if (isCancel(response)) {
cancel(pc.red("Operation cancelled"));
process.exit(0);
}
if (isCancel(response)) return exitCancelled("Operation cancelled");
return response;
}

View File

@@ -1,6 +1,6 @@
import { cancel, confirm, isCancel } from "@clack/prompts";
import pc from "picocolors";
import { confirm, isCancel } from "@clack/prompts";
import { DEFAULT_CONFIG } from "../constants";
import { exitCancelled } from "../utils/errors";
export async function getinstallChoice(install?: boolean) {
if (install !== undefined) return install;
@@ -10,10 +10,7 @@ export async function getinstallChoice(install?: boolean) {
initialValue: DEFAULT_CONFIG.install,
});
if (isCancel(response)) {
cancel(pc.red("Operation cancelled"));
process.exit(0);
}
if (isCancel(response)) return exitCancelled("Operation cancelled");
return response;
}

View File

@@ -1,7 +1,7 @@
import { cancel, isCancel, select } from "@clack/prompts";
import pc from "picocolors";
import { isCancel, select } from "@clack/prompts";
import { DEFAULT_CONFIG } from "../constants";
import type { Backend, Database, ORM, Runtime } from "../types";
import { exitCancelled } from "../utils/errors";
const ormOptions = {
prisma: {
@@ -51,10 +51,7 @@ export async function getORMChoice(
initialValue: database === "mongodb" ? "prisma" : DEFAULT_CONFIG.orm,
});
if (isCancel(response)) {
cancel(pc.red("Operation cancelled"));
process.exit(0);
}
if (isCancel(response)) return exitCancelled("Operation cancelled");
return response;
}

View File

@@ -1,6 +1,6 @@
import { cancel, isCancel, select } from "@clack/prompts";
import pc from "picocolors";
import { isCancel, select } from "@clack/prompts";
import type { PackageManager } from "../types";
import { exitCancelled } from "../utils/errors";
import { getUserPkgManager } from "../utils/get-package-manager";
export async function getPackageManagerChoice(
@@ -28,10 +28,7 @@ export async function getPackageManagerChoice(
initialValue: detectedPackageManager,
});
if (isCancel(response)) {
cancel(pc.red("Operation cancelled"));
process.exit(0);
}
if (isCancel(response)) return exitCancelled("Operation cancelled");
return response;
}

View File

@@ -1,10 +1,11 @@
import path from "node:path";
import { cancel, isCancel, text } from "@clack/prompts";
import { isCancel, text } from "@clack/prompts";
import consola from "consola";
import fs from "fs-extra";
import pc from "picocolors";
import { DEFAULT_CONFIG } from "../constants";
import { ProjectNameSchema } from "../types";
import { exitCancelled } from "../utils/errors";
function isPathWithinCwd(targetPath: string): boolean {
const resolved = path.resolve(targetPath);
@@ -76,10 +77,7 @@ export async function getProjectName(initialName?: string): Promise<string> {
},
});
if (isCancel(response)) {
cancel(pc.red("Operation cancelled."));
process.exit(0);
}
if (isCancel(response)) return exitCancelled("Operation cancelled.");
projectPath = response || defaultName;
isValid = true;

View File

@@ -1,7 +1,7 @@
import { cancel, isCancel, select } from "@clack/prompts";
import pc from "picocolors";
import { isCancel, select } from "@clack/prompts";
import { DEFAULT_CONFIG } from "../constants";
import type { Backend, Runtime } from "../types";
import { exitCancelled } from "../utils/errors";
export async function getRuntimeChoice(
runtime?: Runtime,
@@ -48,10 +48,7 @@ export async function getRuntimeChoice(
initialValue: DEFAULT_CONFIG.runtime,
});
if (isCancel(response)) {
cancel(pc.red("Operation cancelled"));
process.exit(0);
}
if (isCancel(response)) return exitCancelled("Operation cancelled");
return response;
}

View File

@@ -1,8 +1,8 @@
import { cancel, isCancel, select } from "@clack/prompts";
import pc from "picocolors";
import { isCancel, select } from "@clack/prompts";
import { DEFAULT_CONFIG } from "../constants";
import type { Backend, Frontend, Runtime, WebDeploy } from "../types";
import { WEB_FRAMEWORKS } from "../utils/compatibility";
import { exitCancelled } from "../utils/errors";
function hasWebFrontend(frontends: Frontend[]): boolean {
return frontends.some((f) => WEB_FRAMEWORKS.includes(f));
@@ -56,10 +56,7 @@ export async function getDeploymentChoice(
initialValue: DEFAULT_CONFIG.webDeploy,
});
if (isCancel(response)) {
cancel(pc.red("Operation cancelled"));
process.exit(0);
}
if (isCancel(response)) return exitCancelled("Operation cancelled");
return response;
}
@@ -105,10 +102,7 @@ export async function getDeploymentToAdd(
initialValue: DEFAULT_CONFIG.webDeploy,
});
if (isCancel(response)) {
cancel(pc.red("Operation cancelled"));
process.exit(0);
}
if (isCancel(response)) return exitCancelled("Operation cancelled");
return response;
}