create-veap 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +3 -0
  2. package/bin/veap.js +7 -0
  3. package/dist/commands/docker.d.ts +1 -0
  4. package/dist/commands/docker.js +55 -0
  5. package/dist/commands/init.d.ts +5 -0
  6. package/dist/commands/init.js +111 -0
  7. package/dist/index.d.ts +3 -0
  8. package/dist/index.js +16 -0
  9. package/dist/main.d.ts +2 -0
  10. package/dist/main.js +1 -0
  11. package/dist/manifests.d.ts +22 -0
  12. package/dist/manifests.js +32 -0
  13. package/dist/mutate.d.ts +46 -0
  14. package/dist/mutate.js +127 -0
  15. package/dist/package-manager.d.ts +56 -0
  16. package/dist/package-manager.js +222 -0
  17. package/dist/project-name.d.ts +22 -0
  18. package/dist/project-name.js +107 -0
  19. package/dist/prompts.d.ts +20 -0
  20. package/dist/prompts.js +93 -0
  21. package/dist/scaffold.d.ts +26 -0
  22. package/dist/scaffold.js +62 -0
  23. package/dist/utils.d.ts +1 -0
  24. package/dist/utils.js +29 -0
  25. package/package.json +45 -0
  26. package/stubs/docker/.dockerignore.stub +39 -0
  27. package/stubs/docker/Dockerfile.bun.stub +42 -0
  28. package/stubs/docker/Dockerfile.npm.stub +42 -0
  29. package/stubs/docker/Dockerfile.stub +49 -0
  30. package/stubs/docker/Dockerfile.yarn.stub +44 -0
  31. package/stubs/docker/compose.yml.stub +58 -0
  32. package/stubs/overlay-full/AGENTS.md.stub +63 -0
  33. package/stubs/overlay-full/app/[[...catchAll]]/page.tsx.stub +47 -0
  34. package/stubs/overlay-full/app/api/[...catchAll]/route.ts.stub +111 -0
  35. package/stubs/overlay-full/app/error.tsx.stub +34 -0
  36. package/stubs/overlay-full/app/globals.css.stub +124 -0
  37. package/stubs/overlay-full/app/layout.tsx.stub +71 -0
  38. package/stubs/overlay-full/app/not-found.tsx.stub +29 -0
  39. package/stubs/overlay-full/app/storage/[...path]/route.ts.stub +61 -0
  40. package/stubs/overlay-full/lib/plugins.gen.ts.stub +6 -0
  41. package/stubs/overlay-full/lib/veap.ts.stub +26 -0
  42. package/stubs/overlay-full/migrations/index.ts.stub +2 -0
  43. package/stubs/overlay-full/next.config.ts.stub +34 -0
  44. package/stubs/overlay-full/proxy.ts.stub +27 -0
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # create-veap
2
+
3
+ Initializer for Veap Framework applications. Scaffolds a full plugin-enabled Veap project on top of the official create-next-app.
package/bin/veap.js ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { createRequire } from "module";
4
+ const require = createRequire(import.meta.url);
5
+
6
+ // We run the compiled CLI from dist
7
+ require("../dist/index.js");
@@ -0,0 +1 @@
1
+ export declare function initDockerConfig(targetDir?: string, pm?: "pnpm" | "npm" | "yarn" | "bun"): Promise<void>;
@@ -0,0 +1,55 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import ora from "ora";
5
+ export async function initDockerConfig(targetDir, pm) {
6
+ const rootDir = targetDir || process.cwd();
7
+ console.log(`\n🐳 Initializing Docker configuration in ${rootDir}...`);
8
+ // Read project name for compose.yml placeholders (like @veap/cli does)
9
+ let projectName = "veap-app";
10
+ const pkgPath = path.join(rootDir, "package.json");
11
+ if (fs.existsSync(pkgPath)) {
12
+ try {
13
+ projectName =
14
+ JSON.parse(fs.readFileSync(pkgPath, "utf-8")).name || projectName;
15
+ }
16
+ catch (_) { }
17
+ }
18
+ const stubsDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../stubs/docker");
19
+ if (!fs.existsSync(stubsDir)) {
20
+ console.error(`Error: Missing Docker stubs directory at ${stubsDir}`);
21
+ return;
22
+ }
23
+ const files = [
24
+ { src: "Dockerfile.stub", dest: "Dockerfile" },
25
+ { src: ".dockerignore.stub", dest: ".dockerignore" },
26
+ { src: "compose.yml.stub", dest: "compose.yml" },
27
+ ];
28
+ const spin = ora("Generating Docker files...").start();
29
+ try {
30
+ for (const f of files) {
31
+ let content = fs.readFileSync(path.join(stubsDir, f.src), "utf-8");
32
+ // Replace project name in compose.yml
33
+ if (f.dest === "compose.yml") {
34
+ content = content.replace(/\{\{name\}\}/g, projectName);
35
+ }
36
+ fs.writeFileSync(path.join(rootDir, f.dest), content, "utf-8");
37
+ }
38
+ // Copy the Dockerfile variant for the chosen package manager over the
39
+ // pnpm baseline shipped in the stubs.
40
+ if (pm && pm !== "pnpm") {
41
+ const variantPath = path.join(stubsDir, `Dockerfile.${pm}.stub`);
42
+ if (fs.existsSync(variantPath)) {
43
+ fs.writeFileSync(path.join(rootDir, "Dockerfile"), fs.readFileSync(variantPath, "utf-8"), "utf-8");
44
+ }
45
+ }
46
+ spin.succeed("Docker configuration files initialized.");
47
+ console.log(" - Dockerfile");
48
+ console.log(" - compose.yml");
49
+ console.log(" - .dockerignore\n");
50
+ }
51
+ catch (err) {
52
+ spin.fail("Failed to generate Docker files.");
53
+ console.error(err);
54
+ }
55
+ }
@@ -0,0 +1,5 @@
1
+ export declare function initProject(name?: string, options?: {
2
+ docker?: boolean;
3
+ skipInstall?: boolean;
4
+ pm?: string;
5
+ }): Promise<void>;
@@ -0,0 +1,111 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import ora from "ora";
5
+ import { processStubs } from "../utils.js";
6
+ import { applyPackageManager, pmInstall, pmRunCommand, resolvePackageManager, } from "../package-manager.js";
7
+ import { scaffoldNextApp } from "../scaffold.js";
8
+ import { promptConfirm, promptProjectName } from "../prompts.js";
9
+ import { parseProjectName, projectNameError } from "../project-name.js";
10
+ import { appendGitignore, clearAppDirectory, injectVeapDependencies, writeEnvFile, } from "../mutate.js";
11
+ export async function initProject(name, options) {
12
+ // ---- Resolve the project name: from the CLI argument or the interactive
13
+ // prompt (which resolves to a default on non-TTY, so scripts never hang).
14
+ let nameInput = (name ?? "").trim();
15
+ if (!nameInput) {
16
+ nameInput = await promptProjectName("my-veap-app", process.cwd());
17
+ }
18
+ else {
19
+ const error = projectNameError(nameInput, process.cwd());
20
+ if (error) {
21
+ console.error(`Error: ${error}`);
22
+ process.exit(1);
23
+ }
24
+ }
25
+ // npm name rules: `@scope/name` keeps the full name in package.json while
26
+ // the project folder receives the base name.
27
+ const parsed = parseProjectName(nameInput);
28
+ const projectName = parsed.folder;
29
+ const destDir = path.resolve(process.cwd(), projectName);
30
+ // ---- Docker is opt-in; ask when the flag was not given (TTY only).
31
+ let withDocker = options?.docker;
32
+ if (withDocker === undefined) {
33
+ withDocker = await promptConfirm("Include Docker configuration?", false);
34
+ }
35
+ // Resolve the package manager up front so the prompt runs before any
36
+ // scaffolding starts (cleaner UX than asking mid-scaffold).
37
+ const pm = await resolvePackageManager({ pm: options?.pm });
38
+ const overlayFolder = "../../stubs/overlay-full";
39
+ console.log(`šŸš€ Initializing new Veap project (Full plugin-enabled): ${projectName} in ${destDir}...`);
40
+ try {
41
+ // ---- 1. Scaffold the Next.js shell with the official create-next-app.
42
+ // CNA owns the boilerplate (Next version, tsconfig, postcss, fonts,
43
+ // README, git init); it runs with explicit flags so it never prompts.
44
+ const spin = ora("Scaffolding Next.js app (create-next-app)...").start();
45
+ try {
46
+ scaffoldNextApp(projectName, { cwd: process.cwd(), pm });
47
+ spin.succeed("Next.js app scaffolded.");
48
+ }
49
+ catch (err) {
50
+ spin.fail("create-next-app failed.");
51
+ throw err;
52
+ }
53
+ const variables = { name: projectName };
54
+ // ---- 2. The overlay owns the whole app/ tree: a leftover CNA page would
55
+ // collide with the optional catch-all route (same specificity).
56
+ clearAppDirectory(destDir);
57
+ // ---- 3. Apply the veap overlay: only the files CNA cannot know about
58
+ // (veap layout, router catch-all, api pipeline, storage route, bootstrap,
59
+ // veap next.config...). Overlay files overwrite CNA counterparts.
60
+ const overlaySpin = ora("Applying Veap overlay files...").start();
61
+ const stubsDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), overlayFolder);
62
+ processStubs(stubsDir, destDir, variables);
63
+ overlaySpin.succeed("Veap overlay applied.");
64
+ // ---- 4. Mutate the generated project: veap dependencies, starter .env
65
+ // (with a fresh random ENCRYPTION_KEY) and .gitignore additions. Scoped
66
+ // names are restored into package.json here (CNA derives the name from
67
+ // the folder and drops the scope).
68
+ const mutateSpin = ora("Configuring Veap (deps, .env)...").start();
69
+ injectVeapDependencies(destDir, parsed.packageName);
70
+ writeEnvFile(destDir);
71
+ appendGitignore(destDir);
72
+ mutateSpin.succeed("Veap configuration applied.");
73
+ // ---- 5. Workspace and storage folders.
74
+ for (const dir of [
75
+ path.join(destDir, "public"),
76
+ path.join(destDir, "storage"),
77
+ path.join(destDir, "plugins"),
78
+ path.join(destDir, "templates"),
79
+ ]) {
80
+ if (!fs.existsSync(dir)) {
81
+ fs.mkdirSync(dir, { recursive: true });
82
+ }
83
+ }
84
+ // ---- 6. Package manager: packageManager field, workspaces config,
85
+ // .yarnrc.yml for yarn berry.
86
+ applyPackageManager(destDir, pm);
87
+ // ---- 7. Docker configuration (optional).
88
+ if (withDocker) {
89
+ const { initDockerConfig } = await import("./docker.js");
90
+ await initDockerConfig(destDir, pm);
91
+ }
92
+ // ---- 8. Single dependency install, after all mutations.
93
+ if (!options?.skipInstall) {
94
+ const spinner = ora(`šŸ“¦ Installing dependencies (${pm} install)...`).start();
95
+ try {
96
+ pmInstall(pm, destDir);
97
+ spinner.succeed("Dependencies installed.");
98
+ }
99
+ catch (_err) {
100
+ spinner.fail(`Failed to install dependencies automatically. Run ${pm} install manually.`);
101
+ }
102
+ }
103
+ console.log(`\n✨ Veap project "${projectName}" initialized successfully!`);
104
+ console.log(`šŸ’” Go to project directory: cd ${projectName}`);
105
+ console.log(`šŸ’” Start dev server: ${pmRunCommand(pm, "dev")}`);
106
+ }
107
+ catch (err) {
108
+ console.error("Error initializing project:", err);
109
+ process.exit(1);
110
+ }
111
+ }
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import { initProject } from "./commands/init.js";
3
+ export { initProject };
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import cac from "cac";
3
+ import { initProject } from "./commands/init.js";
4
+ const cli = cac("create-veap");
5
+ cli
6
+ .command("[name]", "Initialize a new Veap project (asks for details when omitted)")
7
+ .option("--docker", "Initialize Docker configuration")
8
+ .option("--skip-install", "Skip dependencies installation")
9
+ .option("--pm <manager>", "Package manager to use: pnpm, npm, yarn or bun (auto-detected when omitted)")
10
+ .action(async (name, options) => {
11
+ await initProject(name, options);
12
+ });
13
+ cli.help();
14
+ cli.version("0.4.0");
15
+ cli.parse();
16
+ export { initProject };
package/dist/main.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { initProject } from "./commands/init.js";
2
+ export type { PackageManager } from "./package-manager.js";
package/dist/main.js ADDED
@@ -0,0 +1 @@
1
+ export { initProject } from "./commands/init.js";
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Veap-specific dependencies layered on top of the freshly generated
3
+ * `create-next-app` project. The CNA-provided Next/React/TypeScript/Tailwind
4
+ * versions are intentionally NOT pinned here - we take whatever the official
5
+ * scaffolder ships, so generated apps never lag behind the framework.
6
+ *
7
+ * Only `@types/node` is overridden: the engine requirement is Node >= 22.
8
+ * The linter comes from CNA (`--eslint`); no Biome, no changesets.
9
+ */
10
+ export interface VeapDependencyManifest {
11
+ dependencies: Record<string, string>;
12
+ devDependencies: Record<string, string>;
13
+ scripts?: Record<string, string>;
14
+ }
15
+ /**
16
+ * Versions deliberately pinned OVER the CNA-provided ones: the engine
17
+ * requirement is Node >= 22. Applied after the spread merge so CNA cannot
18
+ * win these keys.
19
+ */
20
+ export declare const PINNED_DEV_DEPENDENCIES: Record<string, string>;
21
+ /** Full plugin-enabled project. */
22
+ export declare const FULL_MANIFEST: VeapDependencyManifest;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Veap-specific dependencies layered on top of the freshly generated
3
+ * `create-next-app` project. The CNA-provided Next/React/TypeScript/Tailwind
4
+ * versions are intentionally NOT pinned here - we take whatever the official
5
+ * scaffolder ships, so generated apps never lag behind the framework.
6
+ *
7
+ * Only `@types/node` is overridden: the engine requirement is Node >= 22.
8
+ * The linter comes from CNA (`--eslint`); no Biome, no changesets.
9
+ */
10
+ /**
11
+ * Versions deliberately pinned OVER the CNA-provided ones: the engine
12
+ * requirement is Node >= 22. Applied after the spread merge so CNA cannot
13
+ * win these keys.
14
+ */
15
+ export const PINNED_DEV_DEPENDENCIES = {
16
+ "@types/node": "^22.20.2",
17
+ };
18
+ /** Full plugin-enabled project. */
19
+ export const FULL_MANIFEST = {
20
+ dependencies: {
21
+ dotenv: "^17.2.3",
22
+ pg: "^8.16.3",
23
+ "@veap/framework": "latest",
24
+ "@veap/ui": "latest",
25
+ },
26
+ devDependencies: {
27
+ ...PINNED_DEV_DEPENDENCIES,
28
+ "@types/pg": "^8.16.0",
29
+ rimraf: "^6.1.3",
30
+ "tw-animate-css": "^1.4.0",
31
+ },
32
+ };
@@ -0,0 +1,46 @@
1
+ import { type VeapDependencyManifest } from "./manifests.js";
2
+ /**
3
+ * Post-scaffold mutations applied to the freshly generated
4
+ * `create-next-app` project. Everything CNA generated is kept as-is - these
5
+ * functions only inject what Veap needs on top of it.
6
+ */
7
+ /** The dependency manifest applied to every generated project. */
8
+ export declare function getManifest(): VeapDependencyManifest;
9
+ /**
10
+ * Removes the CNA-generated `app/` directory before the overlay is applied.
11
+ * The overlay owns the whole `app/` tree: Next.js forbids a static page route
12
+ * at the same specificity as an optional catch-all, so a leftover
13
+ * `app/page.tsx` would conflict with the Veap `app/[[...catchAll]]/page.tsx`
14
+ * ("You cannot define a route with the same specificity as an optional
15
+ * catch-all route").
16
+ */
17
+ export declare function clearAppDirectory(projectDir: string): void;
18
+ /**
19
+ * Merges the veap dependency manifest into the CNA-generated package.json.
20
+ * CNA-provided entries (next, react, typescript, tailwind, eslint setup)
21
+ * win for keys they already define; the manifest only fills the gaps and
22
+ * overrides `@types/node` (Node >= 22 engine) plus adds `engines`.
23
+ *
24
+ * `packageName` overrides the generated package name (CNA derives it from
25
+ * the folder, which loses the scope of names like `@scope/name`).
26
+ */
27
+ export declare function injectVeapDependencies(projectDir: string, packageName?: string): void;
28
+ /**
29
+ * Builds the starter `.env` content.
30
+ *
31
+ * ENCRYPTION_KEY is generated randomly per project (16 bytes, base64 - the
32
+ * exact contract validated at boot by @veap/framework, which has no fallback
33
+ * since the fail-fast fix). DATABASE_URL defaults to a local SQLite file;
34
+ * mail values are left as documented placeholders.
35
+ */
36
+ export declare function buildEnvFile(): string;
37
+ /**
38
+ * Writes the starter `.env`. CNA's generated `.gitignore` ignores `.env*`,
39
+ * so the file stays local to the new project.
40
+ */
41
+ export declare function writeEnvFile(projectDir: string): void;
42
+ /**
43
+ * Appends the missing veap entries (storage dirs, plugin dist) to the
44
+ * CNA-generated `.gitignore`. Idempotent: no-op when everything is present.
45
+ */
46
+ export declare function appendGitignore(projectDir: string): void;
package/dist/mutate.js ADDED
@@ -0,0 +1,127 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { FULL_MANIFEST, PINNED_DEV_DEPENDENCIES, } from "./manifests.js";
5
+ /**
6
+ * Post-scaffold mutations applied to the freshly generated
7
+ * `create-next-app` project. Everything CNA generated is kept as-is - these
8
+ * functions only inject what Veap needs on top of it.
9
+ */
10
+ /** The dependency manifest applied to every generated project. */
11
+ export function getManifest() {
12
+ return FULL_MANIFEST;
13
+ }
14
+ /**
15
+ * Removes the CNA-generated `app/` directory before the overlay is applied.
16
+ * The overlay owns the whole `app/` tree: Next.js forbids a static page route
17
+ * at the same specificity as an optional catch-all, so a leftover
18
+ * `app/page.tsx` would conflict with the Veap `app/[[...catchAll]]/page.tsx`
19
+ * ("You cannot define a route with the same specificity as an optional
20
+ * catch-all route").
21
+ */
22
+ export function clearAppDirectory(projectDir) {
23
+ const appDir = path.join(projectDir, "app");
24
+ if (fs.existsSync(appDir)) {
25
+ fs.rmSync(appDir, { recursive: true, force: true });
26
+ }
27
+ }
28
+ /**
29
+ * Merges the veap dependency manifest into the CNA-generated package.json.
30
+ * CNA-provided entries (next, react, typescript, tailwind, eslint setup)
31
+ * win for keys they already define; the manifest only fills the gaps and
32
+ * overrides `@types/node` (Node >= 22 engine) plus adds `engines`.
33
+ *
34
+ * `packageName` overrides the generated package name (CNA derives it from
35
+ * the folder, which loses the scope of names like `@scope/name`).
36
+ */
37
+ export function injectVeapDependencies(projectDir, packageName) {
38
+ const pkgPath = path.join(projectDir, "package.json");
39
+ if (!fs.existsSync(pkgPath)) {
40
+ throw new Error(`package.json not found in ${projectDir}`);
41
+ }
42
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
43
+ const manifest = getManifest();
44
+ if (packageName) {
45
+ pkg.name = packageName;
46
+ }
47
+ pkg.dependencies = {
48
+ ...manifest.dependencies,
49
+ ...pkg.dependencies,
50
+ };
51
+ pkg.devDependencies = {
52
+ ...manifest.devDependencies,
53
+ ...pkg.devDependencies,
54
+ // Versions we own (node engine) win over CNA.
55
+ ...PINNED_DEV_DEPENDENCIES,
56
+ };
57
+ if (manifest.scripts) {
58
+ pkg.scripts = { ...pkg.scripts, ...manifest.scripts };
59
+ }
60
+ pkg.engines = { ...pkg.engines, node: ">=22" };
61
+ fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`, "utf-8");
62
+ }
63
+ /**
64
+ * Builds the starter `.env` content.
65
+ *
66
+ * ENCRYPTION_KEY is generated randomly per project (16 bytes, base64 - the
67
+ * exact contract validated at boot by @veap/framework, which has no fallback
68
+ * since the fail-fast fix). DATABASE_URL defaults to a local SQLite file;
69
+ * mail values are left as documented placeholders.
70
+ */
71
+ export function buildEnvFile() {
72
+ const encryptionKey = randomBytes(16).toString("base64");
73
+ return [
74
+ "# Veap application configuration (generated by create-veap)",
75
+ "",
76
+ "# 16-byte base64 key used for AES encryption at rest (recovery codes,",
77
+ "# TOTP secrets). Regenerate with: openssl rand -base64 16",
78
+ `ENCRYPTION_KEY="${encryptionKey}"`,
79
+ "",
80
+ "# Local SQLite database (swap for a PostgreSQL URL in production)",
81
+ 'DATABASE_URL="sqlite:./storage/veap.sqlite"',
82
+ "",
83
+ "# Mail transport: smtp (default) | console | custom",
84
+ "MAIL_TRANSPORT=smtp",
85
+ "# MAIL_USERNAME=",
86
+ "# MAIL_PASSWORD=",
87
+ "# MAIL_FROM_ADDRESS=",
88
+ "",
89
+ ].join("\n");
90
+ }
91
+ /**
92
+ * Writes the starter `.env`. CNA's generated `.gitignore` ignores `.env*`,
93
+ * so the file stays local to the new project.
94
+ */
95
+ export function writeEnvFile(projectDir) {
96
+ const envPath = path.join(projectDir, ".env");
97
+ if (fs.existsSync(envPath)) {
98
+ // Never clobber: CNA does not generate .env, so this only triggers on
99
+ // re-runs over an existing directory.
100
+ return;
101
+ }
102
+ fs.writeFileSync(envPath, buildEnvFile(), "utf-8");
103
+ }
104
+ /** Entries veap requires in `.gitignore` on top of CNA's output. */
105
+ const GITIGNORE_ENTRIES = ["public/storage/", "storage/", "**/*/dist"];
106
+ /**
107
+ * Appends the missing veap entries (storage dirs, plugin dist) to the
108
+ * CNA-generated `.gitignore`. Idempotent: no-op when everything is present.
109
+ */
110
+ export function appendGitignore(projectDir) {
111
+ const gitignorePath = path.join(projectDir, ".gitignore");
112
+ const existing = fs.existsSync(gitignorePath)
113
+ ? fs.readFileSync(gitignorePath, "utf-8")
114
+ : "";
115
+ const present = new Set(existing.split("\n").map((line) => line.trim()));
116
+ const missing = GITIGNORE_ENTRIES.filter((entry) => !present.has(entry));
117
+ if (missing.length === 0)
118
+ return;
119
+ const block = [
120
+ "",
121
+ "# Veap additions (appended by create-veap)",
122
+ ...missing,
123
+ "",
124
+ ].join("\n");
125
+ const separator = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
126
+ fs.writeFileSync(gitignorePath, `${existing}${separator}${block}`, "utf-8");
127
+ }
@@ -0,0 +1,56 @@
1
+ export type PackageManager = "pnpm" | "npm" | "yarn" | "bun";
2
+ export declare const PACKAGE_MANAGERS: PackageManager[];
3
+ export interface PackageManagerDetection {
4
+ pm: PackageManager;
5
+ /** Where the default came from: package.json field, a lockfile, or the framework default. */
6
+ source: "field" | "lockfile" | "default";
7
+ /** Human-readable detail, e.g. "pnpm@11.9.0" or "pnpm-lock.yaml". */
8
+ detail?: string;
9
+ }
10
+ /**
11
+ * Detects the package manager to default to by inspecting `dir` (usually the
12
+ * directory the scaffolding command was launched from).
13
+ *
14
+ * Priority: `packageManager` field in package.json > lockfiles > default (pnpm).
15
+ */
16
+ export declare function detectPackageManager(dir: string): PackageManagerDetection;
17
+ /**
18
+ * Interactive numbered-choice prompt rendered on readline (no external
19
+ * prompt library, matching the rest of this CLI).
20
+ *
21
+ * The detected package manager is listed first as the default; pressing
22
+ * Enter accepts it. Accepts a number (1-4) or a name (`pnpm`, `npm`,
23
+ * `yarn`, `bun`). On EOF / closed stdin (piped input, CI) resolves to the
24
+ * detected default instead of hanging.
25
+ */
26
+ export declare function promptPackageManager(detection: PackageManagerDetection): Promise<PackageManager>;
27
+ /**
28
+ * Resolves the package manager to use for a new project.
29
+ *
30
+ * Order: explicit `--pm <name>` flag (validated hard - scripts must not hang
31
+ * on a prompt) → interactive prompt (TTY only) → auto-detected default
32
+ * (non-TTY, CI).
33
+ */
34
+ export declare function resolvePackageManager(options?: {
35
+ pm?: string;
36
+ }): Promise<PackageManager>;
37
+ /**
38
+ * Applies the chosen package manager to the freshly scaffolded project:
39
+ *
40
+ * - pnpm: pins `packageManager` (corepack), writes `pnpm-workspace.yaml`
41
+ * only when missing (an existing one is the source of truth)
42
+ * - yarn: pins `packageManager`, adds `.yarnrc.yml` with `nodeLinker:
43
+ * node-modules` (berry's default PnP breaks Next.js)
44
+ * - npm/bun: removes `packageManager` (not managed by corepack), switches
45
+ * workspace definition to the `workspaces` field in package.json
46
+ */
47
+ export declare function applyPackageManager(projectDir: string, pm: PackageManager): void;
48
+ /** Command prefix for a package-manager-agnostic npm-script invocation. */
49
+ export declare function pmRunCommand(pm: PackageManager, script: string): string;
50
+ /**
51
+ * Installs dependencies with the chosen package manager (all four use the
52
+ * same `install` verb).
53
+ */
54
+ export declare function pmInstall(pm: PackageManager, cwd: string, opts?: {
55
+ stdio?: "inherit" | "ignore" | "pipe";
56
+ }): void;