@a5gard/bifrost 1.0.1 → 1.0.3

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.
package/dist/index.js CHANGED
@@ -1,7 +1,5 @@
1
- #!/usr/bin/env bun
2
-
3
- // src/cli.ts
4
- import { Command } from "commander";
1
+ // src/index.ts
2
+ import inquirer3 from "inquirer";
5
3
  import chalk5 from "chalk";
6
4
 
7
5
  // src/constants.ts
@@ -125,211 +123,6 @@ var PLATFORMS = {
125
123
  var PACKAGE_MANAGERS = ["npm", "pnpm", "yarn", "bun"];
126
124
  var TEMP_DIR_PREFIX = "bifrost-temp-";
127
125
 
128
- // src/prompts.ts
129
- import prompts from "prompts";
130
-
131
- // src/utilts.ts
132
- import fs from "fs-extra";
133
- import validateNpmPackageName from "validate-npm-package-name";
134
- function toValidPackageName(name) {
135
- return name.trim().toLowerCase().replace(/\s+/g, "-").replace(/^[._]/, "").replace(/[^a-z0-9-~]+/g, "-");
136
- }
137
- function parseStackReference(template) {
138
- const parts = template.split("/");
139
- if (parts.length !== 2) {
140
- throw new Error("Stack must be in format: owner/repo");
141
- }
142
- return { owner: parts[0], repo: parts[1] };
143
- }
144
- async function directoryExists(dir) {
145
- try {
146
- const stats = await fs.stat(dir);
147
- return stats.isDirectory();
148
- } catch {
149
- return false;
150
- }
151
- }
152
- async function isDirectoryEmpty(dir) {
153
- const files = await fs.readdir(dir);
154
- return files.length === 0;
155
- }
156
- function getPackageManagerCommand(pm, command) {
157
- const commands = {
158
- npm: { install: "npm install", run: "npm run" },
159
- pnpm: { install: "pnpm install", run: "pnpm" },
160
- yarn: { install: "yarn", run: "yarn" },
161
- bun: { install: "bun install", run: "bun" }
162
- };
163
- return commands[pm][command];
164
- }
165
- async function detectPackageManager() {
166
- const userAgent = process.env.npm_config_user_agent;
167
- if (!userAgent) return "bun";
168
- if (userAgent.startsWith("pnpm")) return "pnpm";
169
- if (userAgent.startsWith("yarn")) return "yarn";
170
- if (userAgent.startsWith("bun")) return "bun";
171
- if (userAgent.startsWith("npm")) return "npm";
172
- return "bun";
173
- }
174
- function detectPlatformFromStack(template) {
175
- const lowerStack = template.toLowerCase();
176
- if (lowerStack.includes("remix")) return "remix";
177
- if (lowerStack.includes("next")) return "nextjs";
178
- if (lowerStack.includes("vite")) return "vite";
179
- if (lowerStack.includes("vue")) return "vue";
180
- if (lowerStack.includes("svelte")) return "svelte";
181
- if (lowerStack.includes("astro")) return "astro";
182
- if (lowerStack.includes("solid")) return "solid";
183
- if (lowerStack.includes("qwik")) return "qwik";
184
- if (lowerStack.includes("react") || lowerStack.includes("cra")) return "react";
185
- return void 0;
186
- }
187
- function detectTagsFromStack(template) {
188
- const tags = [];
189
- const lowerStack = template.toLowerCase();
190
- if (lowerStack.includes("typescript") || lowerStack.includes("-ts")) tags.push("typescript");
191
- if (lowerStack.includes("javascript") || lowerStack.includes("-js")) tags.push("javascript");
192
- if (lowerStack.includes("tailwind")) tags.push("tailwind");
193
- if (lowerStack.includes("prisma")) tags.push("prisma");
194
- if (lowerStack.includes("postgres")) tags.push("postgresql");
195
- if (lowerStack.includes("sqlite")) tags.push("sqlite");
196
- if (lowerStack.includes("mongo")) tags.push("mongodb");
197
- if (lowerStack.includes("aws")) tags.push("aws");
198
- if (lowerStack.includes("cloudflare")) tags.push("cloudflare");
199
- if (lowerStack.includes("vercel")) tags.push("vercel");
200
- if (lowerStack.includes("react")) tags.push("react");
201
- return tags;
202
- }
203
-
204
- // src/prompts.ts
205
- async function promptForMissingOptions(projectName, template, packageManager, install) {
206
- const detectedPM = await detectPackageManager();
207
- const questions = [];
208
- if (!projectName) {
209
- questions.push({
210
- type: "text",
211
- name: "projectName",
212
- message: "What would you like to name your new project?",
213
- initial: "my-bifrost-app",
214
- validate: (value) => {
215
- if (!value) return "Project name is required";
216
- return true;
217
- }
218
- });
219
- }
220
- if (!template) {
221
- questions.push({
222
- type: "select",
223
- name: "platform",
224
- message: "Which platform would you like to use?",
225
- choices: Object.entries(PLATFORMS).map(([key, platform]) => ({
226
- title: platform.name,
227
- value: key,
228
- description: platform.description || ""
229
- })),
230
- initial: 0
231
- });
232
- questions.push({
233
- type: (prev) => {
234
- const platform = PLATFORMS[prev];
235
- return platform.templates ? "select" : null;
236
- },
237
- name: "template",
238
- message: "Select a template:",
239
- choices: (prev) => {
240
- const platform = PLATFORMS[prev];
241
- if (!platform.templates) return [];
242
- return Object.entries(platform.templates).map(([key, template2]) => ({
243
- title: template2.name,
244
- value: template2.repo,
245
- description: template2.description
246
- }));
247
- }
248
- });
249
- questions.push({
250
- type: (prev, values) => {
251
- const platformKey = values.platform;
252
- return platformKey === "custom" ? "text" : null;
253
- },
254
- name: "customStack",
255
- message: "Enter template (owner/repo):",
256
- validate: (value) => {
257
- if (!value || !value.includes("/")) {
258
- return "Stack must be in format: owner/repo";
259
- }
260
- return true;
261
- }
262
- });
263
- }
264
- if (!packageManager) {
265
- questions.push({
266
- type: "select",
267
- name: "packageManager",
268
- message: "Which package manager do you prefer?",
269
- choices: PACKAGE_MANAGERS.map((pm) => ({
270
- title: pm,
271
- value: pm,
272
- selected: pm === detectedPM
273
- })),
274
- initial: PACKAGE_MANAGERS.indexOf(detectedPM)
275
- });
276
- }
277
- if (install === void 0) {
278
- questions.push({
279
- type: "confirm",
280
- name: "install",
281
- message: "Would you like to have the install command run once the project has initialized?",
282
- initial: true
283
- });
284
- }
285
- questions.push({
286
- type: "confirm",
287
- name: "gitPush",
288
- message: "Would you like to auto create and push the first commit to GitHub?",
289
- initial: false
290
- });
291
- questions.push({
292
- type: "confirm",
293
- name: "runWizard",
294
- message: "Would you like to run the config.bifrost wizard?",
295
- initial: false
296
- });
297
- questions.push({
298
- type: "confirm",
299
- name: "submitToRegistry",
300
- message: "Would you like to submit your template to the bifrost registry?",
301
- initial: false
302
- });
303
- const answers = await prompts(questions, {
304
- onCancel: () => {
305
- console.log("\nOperation cancelled");
306
- process.exit(0);
307
- }
308
- });
309
- let finalStack = template;
310
- if (!template) {
311
- if (answers.platform === "custom") {
312
- finalStack = answers.customStack;
313
- } else if (answers.template) {
314
- finalStack = answers.template;
315
- } else {
316
- const platform = PLATFORMS[answers.platform];
317
- if (platform.repo) {
318
- finalStack = platform.repo;
319
- }
320
- }
321
- }
322
- return {
323
- projectName: projectName || answers.projectName,
324
- template: finalStack,
325
- packageManager: packageManager || answers.packageManager,
326
- install: install !== void 0 ? install : answers.install,
327
- gitPush: answers.gitPush,
328
- runWizard: answers.runWizard,
329
- submitToRegistry: answers.submitToRegistry
330
- };
331
- }
332
-
333
126
  // src/creator.ts
334
127
  import fs5 from "fs-extra";
335
128
  import path4 from "path";
@@ -338,7 +131,7 @@ import ora2 from "ora";
338
131
 
339
132
  // src/git.ts
340
133
  import { execa } from "execa";
341
- import fs2 from "fs-extra";
134
+ import fs from "fs-extra";
342
135
  import path from "path";
343
136
  import os from "os";
344
137
  async function isGitInstalled() {
@@ -358,11 +151,11 @@ async function cloneRepository(owner, repo, targetDir) {
358
151
  const tempDir = path.join(os.tmpdir(), `${TEMP_DIR_PREFIX}${Date.now()}`);
359
152
  try {
360
153
  await execa("git", ["clone", "--depth", "1", repoUrl, tempDir]);
361
- await fs2.remove(path.join(tempDir, ".git"));
362
- await fs2.copy(tempDir, targetDir, { overwrite: true });
363
- await fs2.remove(tempDir);
154
+ await fs.remove(path.join(tempDir, ".git"));
155
+ await fs.copy(tempDir, targetDir, { overwrite: true });
156
+ await fs.remove(tempDir);
364
157
  } catch (error) {
365
- await fs2.remove(tempDir).catch(() => {
158
+ await fs.remove(tempDir).catch(() => {
366
159
  });
367
160
  if (error instanceof Error) {
368
161
  if (error.message.includes("not found") || error.message.includes("not exist")) {
@@ -402,6 +195,72 @@ async function pushToGitHub(projectDir) {
402
195
 
403
196
  // src/install.ts
404
197
  import { execa as execa2 } from "execa";
198
+
199
+ // src/utilts.ts
200
+ import fs2 from "fs-extra";
201
+ import validateNpmPackageName from "validate-npm-package-name";
202
+ function toValidPackageName(name) {
203
+ return name.trim().toLowerCase().replace(/\s+/g, "-").replace(/^[._]/, "").replace(/[^a-z0-9-~]+/g, "-");
204
+ }
205
+ function parseStackReference(template) {
206
+ const parts = template.split("/");
207
+ if (parts.length !== 2) {
208
+ throw new Error("Stack must be in format: owner/repo");
209
+ }
210
+ return { owner: parts[0], repo: parts[1] };
211
+ }
212
+ async function directoryExists(dir) {
213
+ try {
214
+ const stats = await fs2.stat(dir);
215
+ return stats.isDirectory();
216
+ } catch {
217
+ return false;
218
+ }
219
+ }
220
+ async function isDirectoryEmpty(dir) {
221
+ const files = await fs2.readdir(dir);
222
+ return files.length === 0;
223
+ }
224
+ function getPackageManagerCommand(pm, command) {
225
+ const commands = {
226
+ npm: { install: "npm install", run: "npm run" },
227
+ pnpm: { install: "pnpm install", run: "pnpm" },
228
+ yarn: { install: "yarn", run: "yarn" },
229
+ bun: { install: "bun install", run: "bun" }
230
+ };
231
+ return commands[pm][command];
232
+ }
233
+ function detectPlatformFromStack(template) {
234
+ const lowerStack = template.toLowerCase();
235
+ if (lowerStack.includes("remix")) return "remix";
236
+ if (lowerStack.includes("next")) return "nextjs";
237
+ if (lowerStack.includes("vite")) return "vite";
238
+ if (lowerStack.includes("vue")) return "vue";
239
+ if (lowerStack.includes("svelte")) return "svelte";
240
+ if (lowerStack.includes("astro")) return "astro";
241
+ if (lowerStack.includes("solid")) return "solid";
242
+ if (lowerStack.includes("qwik")) return "qwik";
243
+ if (lowerStack.includes("react") || lowerStack.includes("cra")) return "react";
244
+ return void 0;
245
+ }
246
+ function detectTagsFromStack(template) {
247
+ const tags = [];
248
+ const lowerStack = template.toLowerCase();
249
+ if (lowerStack.includes("typescript") || lowerStack.includes("-ts")) tags.push("typescript");
250
+ if (lowerStack.includes("javascript") || lowerStack.includes("-js")) tags.push("javascript");
251
+ if (lowerStack.includes("tailwind")) tags.push("tailwind");
252
+ if (lowerStack.includes("prisma")) tags.push("prisma");
253
+ if (lowerStack.includes("postgres")) tags.push("postgresql");
254
+ if (lowerStack.includes("sqlite")) tags.push("sqlite");
255
+ if (lowerStack.includes("mongo")) tags.push("mongodb");
256
+ if (lowerStack.includes("aws")) tags.push("aws");
257
+ if (lowerStack.includes("cloudflare")) tags.push("cloudflare");
258
+ if (lowerStack.includes("vercel")) tags.push("vercel");
259
+ if (lowerStack.includes("react")) tags.push("react");
260
+ return tags;
261
+ }
262
+
263
+ // src/install.ts
405
264
  async function installDependencies(projectDir, packageManager) {
406
265
  const installCommand = getPackageManagerCommand(packageManager, "install");
407
266
  const [cmd, ...args] = installCommand.split(" ");
@@ -430,7 +289,7 @@ async function runPostInstallScripts(projectDir, packageManager, scripts) {
430
289
  import fs3 from "fs-extra";
431
290
  import path2 from "path";
432
291
  import os2 from "os";
433
- import prompts2 from "prompts";
292
+ import inquirer from "inquirer";
434
293
  import chalk from "chalk";
435
294
  import ora from "ora";
436
295
  import { execa as execa3 } from "execa";
@@ -462,20 +321,22 @@ async function installPluginLibraries(projectDir, packageManager, libraries) {
462
321
  });
463
322
  }
464
323
  async function promptForFileLocation(fileName, suggestedLocation) {
465
- const response = await prompts2({
466
- type: "text",
467
- name: "location",
468
- message: `Location for ${chalk.cyan(fileName)}:`,
469
- initial: suggestedLocation,
470
- validate: (value) => {
471
- if (!value) return "Location is required";
472
- return true;
324
+ const { location } = await inquirer.prompt([
325
+ {
326
+ type: "input",
327
+ name: "location",
328
+ message: `Location for ${chalk.cyan(fileName)}:`,
329
+ default: suggestedLocation,
330
+ validate: (value) => {
331
+ if (!value) return "Location is required";
332
+ return true;
333
+ }
473
334
  }
474
- });
475
- if (!response.location) {
335
+ ]);
336
+ if (!location) {
476
337
  throw new Error("Operation cancelled");
477
338
  }
478
- return response.location;
339
+ return location;
479
340
  }
480
341
  async function copyPluginFiles(projectDir, pluginTempDir, files) {
481
342
  for (const file of files) {
@@ -595,8 +456,79 @@ async function createBifrostConfig(projectDir, projectName, template, platform,
595
456
  }
596
457
 
597
458
  // src/creator.ts
459
+ import { execSync } from "child_process";
460
+ async function installTailwind(absolutePath, packageManager, useNgin) {
461
+ const spinner = ora2(`Installing Tailwind CSS with ${useNgin ? "preset ngin" : "base config"}...`).start();
462
+ try {
463
+ const installCmd = packageManager === "npm" ? "npm install -D tailwindcss postcss autoprefixer" : `${packageManager} add -D tailwindcss postcss autoprefixer`;
464
+ execSync(installCmd, { cwd: absolutePath, stdio: "ignore" });
465
+ execSync("npx tailwindcss init -p", { cwd: absolutePath, stdio: "ignore" });
466
+ if (useNgin) {
467
+ const tailwindConfig = `import type { Config } from 'tailwindcss';
468
+ import ngin from '@a5gard/ngin';
469
+
470
+ export default {
471
+ presets: [ngin],
472
+ content: ['./app/**/*.{js,jsx,ts,tsx}'],
473
+ } satisfies Config;`;
474
+ await fs5.writeFile(path4.join(absolutePath, "tailwind.config.ts"), tailwindConfig);
475
+ }
476
+ const appCssPath = path4.join(absolutePath, "app", "tailwind.css");
477
+ const rootCssPath = path4.join(absolutePath, "app", "root.css");
478
+ const cssPath = await fs5.pathExists(appCssPath) ? appCssPath : rootCssPath;
479
+ const tailwindDirectives = `@tailwind base;
480
+ @tailwind components;
481
+ @tailwind utilities;
482
+ `;
483
+ if (await fs5.pathExists(cssPath)) {
484
+ const existingCss = await fs5.readFile(cssPath, "utf-8");
485
+ if (!existingCss.includes("@tailwind")) {
486
+ await fs5.writeFile(cssPath, tailwindDirectives + existingCss);
487
+ }
488
+ } else {
489
+ await fs5.ensureDir(path4.dirname(cssPath));
490
+ await fs5.writeFile(cssPath, tailwindDirectives);
491
+ }
492
+ spinner.succeed(`Installed Tailwind CSS with ${useNgin ? "preset ngin" : "base config"}`);
493
+ } catch (error) {
494
+ spinner.fail("Failed to install Tailwind CSS");
495
+ throw error;
496
+ }
497
+ }
498
+ async function installMidgardr(absolutePath, packageManager, withNgin) {
499
+ const spinner = ora2("Installing MI\xD0GAR\xD0R UI components...").start();
500
+ try {
501
+ const command = withNgin ? "full-w-ngin" : "full-install";
502
+ execSync(`bunx @a5gard/midgardr ${command}`, { cwd: absolutePath, stdio: "inherit" });
503
+ spinner.succeed("Installed MI\xD0GAR\xD0R UI components");
504
+ } catch (error) {
505
+ spinner.fail("Failed to install MI\xD0GAR\xD0R UI components");
506
+ throw error;
507
+ }
508
+ }
509
+ async function installBaldr(absolutePath, packageManager) {
510
+ const spinner = ora2("Installing @a5gard/baldr icons...").start();
511
+ try {
512
+ const installCmd = packageManager === "npm" ? "npm install @a5gard/baldr" : `${packageManager} add @a5gard/baldr`;
513
+ execSync(installCmd, { cwd: absolutePath, stdio: "ignore" });
514
+ spinner.succeed("Installed @a5gard/baldr icons");
515
+ } catch (error) {
516
+ spinner.fail("Failed to install @a5gard/baldr icons");
517
+ throw error;
518
+ }
519
+ }
598
520
  async function createProject(context) {
599
- const { projectName, template, packageManager, install, gitPush } = context;
521
+ const {
522
+ projectName,
523
+ template,
524
+ packageManager,
525
+ install,
526
+ gitPush,
527
+ tailwindBase,
528
+ tailwindNgin,
529
+ midgardr,
530
+ baldr
531
+ } = context;
600
532
  const absolutePath = path4.resolve(projectName);
601
533
  console.log();
602
534
  console.log(chalk2.bold("Creating your Bifrost project..."));
@@ -636,6 +568,19 @@ async function createProject(context) {
636
568
  installSpinner.fail("Failed to install dependencies");
637
569
  throw error;
638
570
  }
571
+ if (midgardr) {
572
+ await installMidgardr(absolutePath, packageManager, tailwindNgin || false);
573
+ } else {
574
+ if (tailwindBase) {
575
+ await installTailwind(absolutePath, packageManager, false);
576
+ }
577
+ if (tailwindNgin) {
578
+ await installTailwind(absolutePath, packageManager, true);
579
+ }
580
+ }
581
+ if (baldr) {
582
+ await installBaldr(absolutePath, packageManager);
583
+ }
639
584
  if (stackConfig?.postInstall && Array.isArray(stackConfig.postInstall)) {
640
585
  const postInstallSpinner = ora2("Running post-install scripts...").start();
641
586
  try {
@@ -681,13 +626,17 @@ async function createProject(context) {
681
626
  console.log();
682
627
  console.log(chalk2.bold.green("\u2713 Project created successfully!"));
683
628
  console.log();
684
- console.log(chalk2.bold("Next steps:"));
685
629
  console.log();
686
- console.log(` ${chalk2.cyan("cd")} ${projectName}`);
687
630
  if (!install) {
631
+ console.log(chalk2.bold("Next steps:"));
632
+ console.log(` ${chalk2.cyan("cd")} ${projectName}`);
688
633
  console.log(` ${chalk2.cyan(`${packageManager} install`)}`);
634
+ console.log(` ${chalk2.cyan(`${packageManager} ${packageManager === "npm" ? "run " : ""}dev`)}`);
635
+ } else {
636
+ console.log(chalk2.bold.green("changing directories and starting the first dev server..."));
637
+ execSync(`cd ${projectName}`, { cwd: absolutePath, stdio: "inherit" });
638
+ execSync(`${packageManager} ${packageManager === "npm" ? "run " : ""}dev`, { cwd: absolutePath, stdio: "inherit" });
689
639
  }
690
- console.log(` ${chalk2.cyan(`${packageManager} ${packageManager === "npm" ? "run " : ""}dev`)}`);
691
640
  console.log();
692
641
  }
693
642
 
@@ -695,11 +644,26 @@ async function createProject(context) {
695
644
  import fs6 from "fs-extra";
696
645
  import path5 from "path";
697
646
  import chalk3 from "chalk";
698
- import prompts3 from "prompts";
699
- import { execSync } from "child_process";
647
+ import inquirer2 from "inquirer";
648
+ import { execSync as execSync2 } from "child_process";
649
+ function drawBox(title, content, footer) {
650
+ const width = 117;
651
+ const horizontalLine = "\u2500".repeat(width - 2);
652
+ console.log(`\u256D${horizontalLine}\u256E`);
653
+ console.log(`\u2502${title.padStart(Math.floor((width - 2 + title.length) / 2)).padEnd(width - 2)}\u2502`);
654
+ console.log(`\u251C${horizontalLine}\u2524`);
655
+ content.forEach((line) => {
656
+ console.log(`\u2502 ${line.padEnd(width - 4)} \u2502`);
657
+ });
658
+ if (footer) {
659
+ console.log(`\u251C${horizontalLine}\u2524`);
660
+ console.log(`\u2502${footer.padStart(Math.floor((width - 2 + footer.length) / 2)).padEnd(width - 2)}\u2502`);
661
+ }
662
+ console.log(`\u2570${horizontalLine}\u256F`);
663
+ }
700
664
  async function detectGitHubRepo() {
701
665
  try {
702
- const remote = execSync("git config --get remote.origin.url", { encoding: "utf-8" }).trim();
666
+ const remote = execSync2("git config --get remote.origin.url", { encoding: "utf-8" }).trim();
703
667
  const match = remote.match(/github\.com[:/](.+?)(?:\.git)?$/);
704
668
  if (match) {
705
669
  return match[1];
@@ -723,27 +687,37 @@ async function detectGitHubRepo() {
723
687
  return null;
724
688
  }
725
689
  async function promptForGitHubRepo() {
726
- console.log(chalk3.yellow("\n\u26A0 No GitHub repository detected"));
727
- console.log(chalk3.gray("Please push your project and create a public repository\n"));
728
- const { hasRepo } = await prompts3({
729
- type: "confirm",
730
- name: "hasRepo",
731
- message: "Have you created a public GitHub repository?",
732
- initial: false
733
- });
690
+ drawBox(
691
+ "GITHUB REPOSITORY REQUIRED",
692
+ [
693
+ chalk3.yellow("\u26A0 No GitHub repository detected"),
694
+ "",
695
+ "Please push your project and create a public repository before continuing."
696
+ ]
697
+ );
698
+ const { hasRepo } = await inquirer2.prompt([
699
+ {
700
+ type: "confirm",
701
+ name: "hasRepo",
702
+ message: "Have you created a public GitHub repository?",
703
+ default: false
704
+ }
705
+ ]);
734
706
  if (!hasRepo) {
735
707
  console.log(chalk3.red("\nPlease create a public GitHub repository first"));
736
708
  process.exit(1);
737
709
  }
738
- const { repo } = await prompts3({
739
- type: "text",
740
- name: "repo",
741
- message: "Enter your GitHub repository (owner/repo):",
742
- validate: (value) => {
743
- const pattern = /^[\w-]+\/[\w-]+$/;
744
- return pattern.test(value) || "Invalid format. Use: owner/repo";
710
+ const { repo } = await inquirer2.prompt([
711
+ {
712
+ type: "input",
713
+ name: "repo",
714
+ message: "Enter your GitHub repository (owner/repo):",
715
+ validate: (value) => {
716
+ const pattern = /^[\w-]+\/[\w-]+$/;
717
+ return pattern.test(value) || "Invalid format. Use: owner/repo";
718
+ }
745
719
  }
746
- });
720
+ ]);
747
721
  if (!repo) {
748
722
  console.log(chalk3.red("\nRepository is required"));
749
723
  process.exit(1);
@@ -751,68 +725,85 @@ async function promptForGitHubRepo() {
751
725
  return repo;
752
726
  }
753
727
  async function runConfigWizard() {
754
- console.log(chalk3.blue.bold("\n\u{1F9D9} Config.bifrost Wizard\n"));
728
+ drawBox(
729
+ "CONFIG.BIFROST WIZARD",
730
+ [
731
+ "This wizard will guide you through creating a config.bifrost file for your template.",
732
+ "",
733
+ "This configuration enables your template to be shared with the community."
734
+ ]
735
+ );
755
736
  const configPath = path5.join(process.cwd(), "config.bifrost");
756
737
  if (await fs6.pathExists(configPath)) {
757
- const { overwrite } = await prompts3({
758
- type: "confirm",
759
- name: "overwrite",
760
- message: "config.bifrost already exists. Overwrite?",
761
- initial: false
762
- });
738
+ const { overwrite } = await inquirer2.prompt([
739
+ {
740
+ type: "confirm",
741
+ name: "overwrite",
742
+ message: "config.bifrost already exists. Overwrite?",
743
+ default: false
744
+ }
745
+ ]);
763
746
  if (!overwrite) {
764
747
  const existingConfig = await fs6.readJson(configPath);
765
748
  return existingConfig;
766
749
  }
767
750
  }
768
751
  const detectedRepo = await detectGitHubRepo();
769
- const responses = await prompts3([
752
+ drawBox(
753
+ "TEMPLATE INFORMATION",
754
+ [
755
+ "Provide basic information about your template.",
756
+ "",
757
+ "This helps users discover and understand your template."
758
+ ]
759
+ );
760
+ const responses = await inquirer2.prompt([
770
761
  {
771
- type: "text",
762
+ type: "input",
772
763
  name: "name",
773
764
  message: "Template name:",
774
765
  validate: (value) => value.trim().length > 0 || "Name is required"
775
766
  },
776
767
  {
777
- type: "text",
768
+ type: "input",
778
769
  name: "description",
779
770
  message: "Description:",
780
771
  validate: (value) => value.trim().length > 0 || "Description is required"
781
772
  },
782
773
  {
783
- type: "text",
774
+ type: "input",
784
775
  name: "platform",
785
776
  message: "Platform:",
786
- initial: "remix",
777
+ default: "remix",
787
778
  validate: (value) => value.trim().length > 0 || "Platform is required"
788
779
  },
789
780
  {
790
- type: "text",
781
+ type: "input",
791
782
  name: "github",
792
783
  message: "GitHub repository (owner/repo):",
793
- initial: detectedRepo || "",
784
+ default: detectedRepo || "",
794
785
  validate: (value) => {
795
786
  const pattern = /^[\w-]+\/[\w-]+$/;
796
787
  return pattern.test(value) || "Invalid format. Use: owner/repo";
797
788
  }
798
789
  },
799
790
  {
800
- type: "text",
791
+ type: "input",
801
792
  name: "tags",
802
793
  message: "Tags (comma-separated):",
803
794
  validate: (value) => value.trim().length > 0 || "At least one tag is required"
804
795
  },
805
796
  {
806
- type: "text",
797
+ type: "input",
807
798
  name: "postInstall",
808
799
  message: "Post-install scripts (comma-separated npm script names):",
809
- initial: ""
800
+ default: ""
810
801
  },
811
802
  {
812
- type: "text",
803
+ type: "input",
813
804
  name: "plugins",
814
805
  message: "Plugins to include (comma-separated owner/repo):",
815
- initial: ""
806
+ default: ""
816
807
  }
817
808
  ]);
818
809
  if (!responses.name) {
@@ -832,11 +823,16 @@ async function runConfigWizard() {
832
823
  plugins: responses.plugins ? responses.plugins.split(",").map((p) => p.trim()).filter(Boolean) : []
833
824
  };
834
825
  await fs6.writeJson(configPath, config, { spaces: 2 });
835
- console.log(chalk3.green("\n\u2705 config.bifrost created successfully!\n"));
836
- console.log(chalk3.cyan("Configuration:"));
837
- console.log(chalk3.gray("\u2500".repeat(50)));
838
- console.log(chalk3.white(JSON.stringify(config, null, 2)));
839
- console.log(chalk3.gray("\u2500".repeat(50)));
826
+ drawBox(
827
+ "SUCCESS",
828
+ [
829
+ chalk3.green("\u2705 config.bifrost created successfully!"),
830
+ "",
831
+ "Configuration:",
832
+ "",
833
+ ...JSON.stringify(config, null, 2).split("\n").map((line) => chalk3.white(line))
834
+ ]
835
+ );
840
836
  return config;
841
837
  }
842
838
 
@@ -844,10 +840,10 @@ async function runConfigWizard() {
844
840
  import fs7 from "fs-extra";
845
841
  import path6 from "path";
846
842
  import chalk4 from "chalk";
847
- import prompts4 from "prompts";
848
- import { execSync as execSync2 } from "child_process";
843
+ import prompts from "prompts";
844
+ import { execSync as execSync3 } from "child_process";
849
845
  var REGISTRY_REPO = "A5GARD/BIFROST";
850
- var REGISTRY_FILE = "dist/registry.bifrost";
846
+ var REGISTRY_FILE = "registry.bifrost";
851
847
  async function verifyPublicRepo(github) {
852
848
  try {
853
849
  const response = await fetch(`https://api.github.com/repos/${github}`);
@@ -864,7 +860,7 @@ async function submitTemplate() {
864
860
  let config;
865
861
  if (!await fs7.pathExists(configPath)) {
866
862
  console.log(chalk4.yellow("\u26A0 config.bifrost not found\n"));
867
- const { runWizard } = await prompts4({
863
+ const { runWizard } = await prompts({
868
864
  type: "confirm",
869
865
  name: "runWizard",
870
866
  message: "Would you like to run the config wizard to create it?",
@@ -883,7 +879,7 @@ async function submitTemplate() {
883
879
  if (!isPublic) {
884
880
  console.log(chalk4.red("\n\u274C Repository must be public"));
885
881
  console.log(chalk4.yellow("Please make your repository public before submitting"));
886
- const { madePublic } = await prompts4({
882
+ const { madePublic } = await prompts({
887
883
  type: "confirm",
888
884
  name: "madePublic",
889
885
  message: "Have you made the repository public?",
@@ -913,7 +909,7 @@ async function submitTemplate() {
913
909
  console.log(`Plugins: ${chalk4.white(config.plugins.join(", "))}`);
914
910
  }
915
911
  console.log(chalk4.gray("\u2500".repeat(50)));
916
- const { confirm } = await prompts4({
912
+ const { confirm } = await prompts({
917
913
  type: "confirm",
918
914
  name: "confirm",
919
915
  message: "Submit this template to the registry?",
@@ -933,13 +929,13 @@ async function submitTemplate() {
933
929
  tags: config.tags
934
930
  };
935
931
  console.log(chalk4.blue("\n\u{1F504} Forking registry repository..."));
936
- execSync2(`gh repo fork ${REGISTRY_REPO} --clone=false`, { stdio: "inherit" });
937
- const username = execSync2("gh api user -q .login", { encoding: "utf-8" }).trim();
932
+ execSync3(`gh repo fork ${REGISTRY_REPO} --clone=false`, { stdio: "inherit" });
933
+ const username = execSync3("gh api user -q .login", { encoding: "utf-8" }).trim();
938
934
  const forkRepo = `${username}/BIFROST`;
939
935
  console.log(chalk4.blue("\u{1F4E5} Cloning forked repository..."));
940
936
  const tempDir = path6.join(process.cwd(), ".bifrost-temp");
941
937
  await fs7.ensureDir(tempDir);
942
- execSync2(`gh repo clone ${forkRepo} ${tempDir}`, { stdio: "inherit" });
938
+ execSync3(`gh repo clone ${forkRepo} ${tempDir}`, { stdio: "inherit" });
943
939
  console.log(chalk4.blue("\u{1F4CB} Fetching current registry..."));
944
940
  const registryUrl = `https://raw.githubusercontent.com/${REGISTRY_REPO}/main/${REGISTRY_FILE}`;
945
941
  const registryResponse = await fetch(registryUrl);
@@ -959,11 +955,11 @@ async function submitTemplate() {
959
955
  await fs7.writeJson(registryPath, registry, { spaces: 2 });
960
956
  console.log(chalk4.blue("\u{1F4BE} Committing changes..."));
961
957
  process.chdir(tempDir);
962
- execSync2("git add .", { stdio: "inherit" });
963
- execSync2(`git commit -m "Add/Update template: ${config.name}"`, { stdio: "inherit" });
964
- execSync2("git push", { stdio: "inherit" });
958
+ execSync3("git add .", { stdio: "inherit" });
959
+ execSync3(`git commit -m "Add/Update template: ${config.name}"`, { stdio: "inherit" });
960
+ execSync3("git push", { stdio: "inherit" });
965
961
  console.log(chalk4.blue("\u{1F500} Creating pull request..."));
966
- const prUrl = execSync2(
962
+ const prUrl = execSync3(
967
963
  `gh pr create --repo ${REGISTRY_REPO} --title "Add template: ${config.name}" --body "Submitting template ${config.name} to the registry.
968
964
 
969
965
  Platform: ${config.platform}
@@ -990,129 +986,430 @@ Description: ${config.description}"`,
990
986
  }
991
987
  }
992
988
 
993
- // src/cli.ts
989
+ // src/index.ts
990
+ var VERSION = "1.0.0";
994
991
  async function loadRegistry() {
995
- const registryFile = Bun.file(new URL("../registry.bifrost", import.meta.url));
992
+ const registryFile = Bun.file(new URL("../dist/registry.bifrost", import.meta.url));
996
993
  return await registryFile.json();
997
994
  }
998
- async function runCLI(argv) {
999
- const DEFAULT_STACKS = await loadRegistry();
1000
- const program = new Command();
1001
- program.name("@a5gard/bifrost").description("Create a new project with platform-agnostic templates").version("1.0.0").argument("[projectName]", "The project name").option("-t, --template <owner/repo>", "The template to use (format: owner/repo)").option("-p, --pkg-mgr <pm>", `Package manager to use (${PACKAGE_MANAGERS.join(", ")})`).option("--no-install", "Skip dependency installation").option("--list-templates", "List all available community templates").option("--wizard", "Run config.bifrost wizard").option("--submit", "Submit template to bifrost registry").option("-h, --help", "Show help").action(async (projectName, options) => {
1002
- if (options.help) {
1003
- showHelp();
1004
- process.exit(0);
995
+ function drawBox2(title, content, footer) {
996
+ const width = 117;
997
+ const horizontalLine = "\u2500".repeat(width - 2);
998
+ console.log(`\u256D${horizontalLine}\u256E`);
999
+ console.log(`\u2502${title.padStart(Math.floor((width - 2 + title.length) / 2)).padEnd(width - 2)}\u2502`);
1000
+ console.log(`\u251C${horizontalLine}\u2524`);
1001
+ content.forEach((line) => {
1002
+ console.log(`\u2502 ${line.padEnd(width - 4)} \u2502`);
1003
+ });
1004
+ if (footer) {
1005
+ console.log(`\u251C${horizontalLine}\u2524`);
1006
+ console.log(`\u2502${footer.padStart(Math.floor((width - 2 + footer.length) / 2)).padEnd(width - 2)}\u2502`);
1007
+ }
1008
+ console.log(`\u2570${horizontalLine}\u256F`);
1009
+ }
1010
+ async function promptMainMenu() {
1011
+ drawBox2(
1012
+ "@a5gard/bifrost",
1013
+ [
1014
+ "Platform-agnostic project creator with extensible template system.",
1015
+ "",
1016
+ "Choose an action to get started."
1017
+ ]
1018
+ );
1019
+ const { action } = await inquirer3.prompt([
1020
+ {
1021
+ type: "list",
1022
+ name: "action",
1023
+ message: "What would you like to do?",
1024
+ choices: [
1025
+ { name: "Create a new project", value: "create" },
1026
+ { name: "config.bifrost wizard", value: "wizard" },
1027
+ { name: "Submit template to bifrost registry", value: "submit" }
1028
+ ]
1005
1029
  }
1006
- if (options.listTemplates) {
1007
- showTemplates(DEFAULT_STACKS);
1008
- process.exit(0);
1030
+ ]);
1031
+ return action;
1032
+ }
1033
+ async function promptProjectName() {
1034
+ drawBox2(
1035
+ "PROJECT SETUP",
1036
+ [
1037
+ "Enter a name for your new project.",
1038
+ "",
1039
+ "This will be used as the directory name and package name."
1040
+ ]
1041
+ );
1042
+ const { projectName } = await inquirer3.prompt([
1043
+ {
1044
+ type: "input",
1045
+ name: "projectName",
1046
+ message: "What would you like to name your new project?",
1047
+ validate: (input) => {
1048
+ if (!input.trim()) return "Project name is required";
1049
+ return true;
1050
+ }
1051
+ }
1052
+ ]);
1053
+ return projectName;
1054
+ }
1055
+ async function promptPlatform() {
1056
+ drawBox2(
1057
+ "SELECT PLATFORM",
1058
+ [
1059
+ "Choose the platform/framework for your new project.",
1060
+ "",
1061
+ "Available platforms are listed below."
1062
+ ]
1063
+ );
1064
+ const platformChoices = Object.entries(PLATFORMS).map(([key, platform2]) => ({
1065
+ name: platform2.name,
1066
+ value: key
1067
+ }));
1068
+ const { platform } = await inquirer3.prompt([
1069
+ {
1070
+ type: "list",
1071
+ name: "platform",
1072
+ message: "Which platform would you like to use?",
1073
+ choices: platformChoices
1009
1074
  }
1010
- if (options.wizard) {
1011
- await runConfigWizard();
1012
- process.exit(0);
1075
+ ]);
1076
+ return platform;
1077
+ }
1078
+ async function promptTemplateChoice(platform) {
1079
+ const platformData = PLATFORMS[platform];
1080
+ if (!platformData.templates) {
1081
+ return { useTemplate: false };
1082
+ }
1083
+ drawBox2(
1084
+ "INSTALLATION TYPE",
1085
+ [
1086
+ "Choose between the platform default or select from available templates.",
1087
+ "",
1088
+ "Templates provide pre-configured setups for specific use cases."
1089
+ ]
1090
+ );
1091
+ const { choice } = await inquirer3.prompt([
1092
+ {
1093
+ type: "list",
1094
+ name: "choice",
1095
+ message: "Do you prefer to use the platform's default install or would you like to opt for a template instead?",
1096
+ choices: [
1097
+ { name: "Platform Default", value: "default" },
1098
+ { name: "Choose Template", value: "template" }
1099
+ ]
1013
1100
  }
1014
- if (options.submit) {
1015
- await submitTemplate();
1016
- process.exit(0);
1101
+ ]);
1102
+ if (choice === "default") {
1103
+ const templateKeys = Object.keys(platformData.templates);
1104
+ if (templateKeys.length === 1) {
1105
+ return { useTemplate: true, template: platformData.templates[templateKeys[0]].repo };
1017
1106
  }
1018
- try {
1019
- if (options.pkgMgr && !PACKAGE_MANAGERS.includes(options.pkgMgr)) {
1020
- console.error(chalk5.red(`Invalid package manager. Must be one of: ${PACKAGE_MANAGERS.join(", ")}`));
1021
- process.exit(1);
1022
- }
1023
- let finalProjectName = projectName;
1024
- let finalStack = options.template;
1025
- let finalPackageManager = options.pkgMgr;
1026
- let finalInstall = options.noInstall === false;
1027
- const prompted = await promptForMissingOptions(
1028
- finalProjectName,
1029
- finalStack,
1030
- finalPackageManager,
1031
- finalInstall ? void 0 : false
1032
- );
1033
- finalProjectName = prompted.projectName;
1034
- finalStack = prompted.template;
1035
- finalPackageManager = prompted.packageManager;
1036
- finalInstall = prompted.install;
1037
- const gitPush = prompted.gitPush;
1038
- const runWizard = prompted.runWizard;
1039
- const submitToRegistry = prompted.submitToRegistry;
1040
- const validProjectName = toValidPackageName(finalProjectName);
1041
- await createProject({
1042
- projectName: validProjectName,
1043
- template: finalStack,
1044
- packageManager: finalPackageManager,
1045
- install: finalInstall,
1046
- gitPush
1047
- });
1048
- if (runWizard) {
1049
- await runConfigWizard();
1107
+ drawBox2(
1108
+ `${platformData.name.toUpperCase()} DEFAULT OPTIONS`,
1109
+ [
1110
+ "This platform offers multiple default starter options.",
1111
+ "",
1112
+ "Select the one that best fits your needs."
1113
+ ]
1114
+ );
1115
+ const defaultChoices = Object.entries(platformData.templates).map(([key, tmpl]) => ({
1116
+ name: `${tmpl.name} - ${tmpl.description}`,
1117
+ value: tmpl.repo,
1118
+ short: tmpl.name
1119
+ }));
1120
+ const { template: template2 } = await inquirer3.prompt([
1121
+ {
1122
+ type: "list",
1123
+ name: "template",
1124
+ message: "Which default would you like to use?",
1125
+ choices: defaultChoices
1050
1126
  }
1051
- if (submitToRegistry) {
1052
- await submitTemplate();
1053
- }
1054
- } catch (error) {
1055
- console.error();
1056
- console.error(chalk5.red("Error:"), error instanceof Error ? error.message : "Unknown error");
1057
- console.error();
1058
- process.exit(1);
1127
+ ]);
1128
+ return { useTemplate: true, template: template2 };
1129
+ }
1130
+ const registry = await loadRegistry();
1131
+ const platformTemplates = registry.filter((t) => t.platform === platform);
1132
+ if (platformTemplates.length === 0) {
1133
+ console.log(chalk5.yellow("No community templates available for this platform. Using default."));
1134
+ const templateKeys = Object.keys(platformData.templates);
1135
+ return { useTemplate: true, template: platformData.templates[templateKeys[0]].repo };
1136
+ }
1137
+ drawBox2(
1138
+ `${platformData.name.toUpperCase()} TEMPLATES`,
1139
+ [
1140
+ "Select a community template from the available options below.",
1141
+ "",
1142
+ "Each template includes specific configurations and best practices."
1143
+ ]
1144
+ );
1145
+ const templateChoices = platformTemplates.map((t) => ({
1146
+ name: `${t.owner}/${t.repo} - ${t.description}`,
1147
+ value: `${t.owner}/${t.repo}`,
1148
+ short: `${t.owner}/${t.repo}`
1149
+ }));
1150
+ const { template } = await inquirer3.prompt([
1151
+ {
1152
+ type: "list",
1153
+ name: "template",
1154
+ message: "Which template would you like to use?",
1155
+ choices: templateChoices
1059
1156
  }
1060
- });
1061
- await program.parseAsync(argv);
1157
+ ]);
1158
+ return { useTemplate: true, template };
1062
1159
  }
1063
- function showHelp() {
1064
- console.log(`
1065
- ${chalk5.bold("Usage:")}
1066
-
1067
- ${chalk5.cyan("$ bunx @a5gard/bifrost")} ${chalk5.gray("<projectName> <...options>")}
1068
-
1069
- ${chalk5.bold("Examples:")}
1070
-
1071
- ${chalk5.cyan("$ bunx @a5gard/bifrost")}
1072
- ${chalk5.cyan("$ bunx @a5gard/bifrost my-app")}
1073
- ${chalk5.cyan("$ bunx @a5gard/bifrost my-app --template remix-run/indie-template")}
1074
- ${chalk5.cyan("$ bunx @a5gard/bifrost my-app -s owner/repo -p bun")}
1075
- ${chalk5.cyan("$ bunx @a5gard/bifrost my-app -s owner/repo --no-install")}
1076
- ${chalk5.cyan("$ bunx @a5gard/bifrost --list-templates")}
1077
- ${chalk5.cyan("$ bunx @a5gard/bifrost --wizard")}
1078
- ${chalk5.cyan("$ bunx @a5gard/bifrost --submit")}
1079
-
1080
- ${chalk5.bold("Options:")}
1081
-
1082
- ${chalk5.cyan("--help, -h")} Print this help message
1083
- ${chalk5.cyan("--version, -V")} Print the CLI version
1084
- ${chalk5.cyan("--template, -s")} Stack to use (format: owner/repo)
1085
- ${chalk5.cyan("--pkg-mgr, -p")} Package manager (npm, pnpm, yarn, bun)
1086
- ${chalk5.cyan("--no-install")} Skip dependency installation
1087
- ${chalk5.cyan("--list-templates")} List all available community templates
1088
- ${chalk5.cyan("--wizard")} Run config.bifrost wizard
1089
- ${chalk5.cyan("--submit")} Submit template to bifrost registry
1090
- `);
1160
+ async function promptPackageManager() {
1161
+ drawBox2(
1162
+ "PACKAGE MANAGER",
1163
+ [
1164
+ "Select your preferred package manager for dependency installation.",
1165
+ "",
1166
+ "Supported: npm, pnpm, yarn, bun"
1167
+ ]
1168
+ );
1169
+ const { packageManager } = await inquirer3.prompt([
1170
+ {
1171
+ type: "list",
1172
+ name: "packageManager",
1173
+ message: "Which package manager do you prefer?",
1174
+ choices: PACKAGE_MANAGERS.map((pm) => ({ name: pm, value: pm }))
1175
+ }
1176
+ ]);
1177
+ return packageManager;
1091
1178
  }
1092
- function showTemplates(DEFAULT_STACKS) {
1093
- console.log();
1094
- console.log(chalk5.bold("Available Community Templates"));
1095
- console.log();
1096
- const groupedByPlatform = DEFAULT_STACKS.reduce((acc, template) => {
1179
+ async function promptAdditionalOptions() {
1180
+ drawBox2(
1181
+ "ADDITIONAL OPTIONS",
1182
+ [
1183
+ "Configure additional features and tools for your project.",
1184
+ "",
1185
+ "Would you like tailwind and its requirements to be installed and configured:"
1186
+ ]
1187
+ );
1188
+ const { options } = await inquirer3.prompt([
1189
+ {
1190
+ type: "checkbox",
1191
+ name: "options",
1192
+ message: "Select the options you would like to include (use spacebar to toggle):",
1193
+ choices: [
1194
+ { name: "Using the base tailwind config", value: "tailwindBase" },
1195
+ { name: "Using the preset ngin", value: "tailwindNgin" },
1196
+ { name: "Pre-install MI\xD0GAR\xD0R UI components", value: "midgardr" },
1197
+ { name: "Pre-install @a5gard/baldr icons", value: "baldr" },
1198
+ { name: "Auto install the project's libraries once the project has initialized", value: "install", checked: true },
1199
+ { name: "Auto create and push the first commit to GitHub", value: "gitPush" }
1200
+ ]
1201
+ }
1202
+ ]);
1203
+ return {
1204
+ tailwindBase: options.includes("tailwindBase"),
1205
+ tailwindNgin: options.includes("tailwindNgin"),
1206
+ midgardr: options.includes("midgardr"),
1207
+ baldr: options.includes("baldr"),
1208
+ install: options.includes("install"),
1209
+ gitPush: options.includes("gitPush")
1210
+ };
1211
+ }
1212
+ function showTemplates(registry, platformFilter) {
1213
+ let filteredTemplates = registry;
1214
+ if (platformFilter) {
1215
+ filteredTemplates = registry.filter((t) => t.platform === platformFilter);
1216
+ if (filteredTemplates.length === 0) {
1217
+ console.log(chalk5.yellow(`No templates found for platform: ${platformFilter}`));
1218
+ return;
1219
+ }
1220
+ }
1221
+ const groupedByPlatform = filteredTemplates.reduce((acc, template) => {
1097
1222
  if (!acc[template.platform]) {
1098
1223
  acc[template.platform] = [];
1099
1224
  }
1100
1225
  acc[template.platform].push(template);
1101
1226
  return acc;
1102
1227
  }, {});
1103
- Object.entries(groupedByPlatform).forEach(([platform, template]) => {
1104
- console.log(chalk5.bold.cyan(`${platform.toUpperCase()}`));
1105
- console.log();
1106
- template.forEach((template2) => {
1107
- console.log(` ${chalk5.green("\u203A")} ${chalk5.bold(`${template2.owner}/${template2.repo}`)}`);
1108
- console.log(` ${chalk5.gray(template2.description)}`);
1109
- console.log(` ${chalk5.gray(`Tags: ${template2.tags.join(", ")}`)}`);
1110
- console.log();
1111
- });
1112
- });
1228
+ console.log();
1229
+ drawBox2(
1230
+ "AVAILABLE COMMUNITY TEMPLATES",
1231
+ Object.entries(groupedByPlatform).flatMap(([platform, templates]) => {
1232
+ const lines = [
1233
+ "",
1234
+ chalk5.bold.cyan(platform.toUpperCase()),
1235
+ ""
1236
+ ];
1237
+ templates.forEach((template) => {
1238
+ lines.push(` ${chalk5.green("\u203A")} ${chalk5.bold(`${template.owner}/${template.repo}`)}`);
1239
+ lines.push(` ${chalk5.gray(template.description)}`);
1240
+ lines.push(` ${chalk5.gray(`Tags: ${template.tags.join(", ")}`)}`);
1241
+ lines.push("");
1242
+ });
1243
+ return lines;
1244
+ })
1245
+ );
1246
+ console.log();
1113
1247
  console.log(chalk5.gray("Use any template with: ") + chalk5.cyan("bunx @a5gard/bifrost my-app --template owner/repo"));
1114
1248
  console.log();
1115
1249
  }
1116
-
1117
- // src/index.ts
1118
- runCLI(process.argv);
1250
+ function showHelp() {
1251
+ drawBox2(
1252
+ "BIFROST CLI HELP",
1253
+ [
1254
+ chalk5.white("BIFR\xD6ST unifies the fragmented landscape of project starters. Instead of learning npx create-remix, npx create-next-app, npx create-vite, and so on\u2014use one CLI for all platforms with community-driven templates and a plugin system."),
1255
+ chalk5.white("Whenever a platform has been selected, you have the option of using the default installer provided by the platform's creators, or you may opt instead to use a configured template that was created by other developers."),
1256
+ chalk5.white("Templates are opinionated variants that will include file scaffolding, configurations in place to meet the needed requirements, route files, pre-installed libraries and more."),
1257
+ chalk5.white("Allowing you to hit the ground running when starting a new project, instead of wasting time or getting bogged down with all the required to-do items whenever a new app is created."),
1258
+ chalk5.white("Currently focusing on React-based platforms. Once the details are ironed out, that focus will be expanded upon however I can."),
1259
+ "",
1260
+ chalk5.white("BIFR\xD6ST is not only striving to fill a gap where no one else has even really attempted, but also introducing a plugin system that can be used with, alongside, or on its own with the default installer."),
1261
+ chalk5.white("A plugin will contain everything needed to add that feature to your project. For example, one-time password authentication for Remix Run. The plugin will contain and install all required route files, create/update all required configuration files, and will ensure all required libraries are installed within the project."),
1262
+ chalk5.white("Once the plugin's installation process has completed, other than setting up your own personal Resend account, the plugin will be ready to use."),
1263
+ chalk5.white(""),
1264
+ chalk5.white("Another benefit that has come from the plugin system: for developers that can't live with a one-template-fits-all lifestyle. Instead, create a bare-bones essential app where only the libraries, configs, and routes that are absolutely essential, no matter the scenario, are included."),
1265
+ chalk5.white("At which time, instead of configuring several time-consuming full-stack variations, you can create plugins to fill in the needs of whatever use case you can think of. So instead of having several projects that need not only to be taken care of in all the forms that are required, where at times you will be updating the same configs and libraries across all variants."),
1266
+ chalk5.white("Because we don't want to deal with all of the headaches that come along with it, not to mention the time spent going that route. In its place, have one app that will serve as the foundation for all the plugins."),
1267
+ chalk5.white("In the end, there's one app and one plugin to take care of instead of updating the same auth library across 4, 5, or whatever number of applications."),
1268
+ chalk5.white(""),
1269
+ "",
1270
+ chalk5.bold("Usage:"),
1271
+ "",
1272
+ ` ${chalk5.cyan("$ bunx @a5gard/bifrost")} ${chalk5.gray("<projectName> <...options>")}`,
1273
+ "",
1274
+ chalk5.bold("Examples:"),
1275
+ "",
1276
+ ` ${chalk5.cyan("$ bunx @a5gard/bifrost")}`,
1277
+ ` ${chalk5.cyan("$ bunx @a5gard/bifrost my-app")}`,
1278
+ ` ${chalk5.cyan("$ bunx @a5gard/bifrost my-app --template remix-run/indie-template")}`,
1279
+ ` ${chalk5.cyan("$ bunx @a5gard/bifrost my-app -t owner/repo -p bun")}`,
1280
+ ` ${chalk5.cyan("$ bunx @a5gard/bifrost my-app -t owner/repo --no-install")}`,
1281
+ ` ${chalk5.cyan("$ bunx @a5gard/bifrost --list-templates")}`,
1282
+ ` ${chalk5.cyan("$ bunx @a5gard/bifrost --list-templates remix")}`,
1283
+ ` ${chalk5.cyan("$ bunx @a5gard/bifrost --wizard")}`,
1284
+ ` ${chalk5.cyan("$ bunx @a5gard/bifrost --submit")}`,
1285
+ "",
1286
+ chalk5.bold("Options:"),
1287
+ "",
1288
+ ` ${chalk5.cyan("--help, -h")} Print this help message`,
1289
+ ` ${chalk5.cyan("--version, -V")} Print the CLI version`,
1290
+ ` ${chalk5.cyan("--template, -t")} Template to use (format: owner/repo)`,
1291
+ ` ${chalk5.cyan("--pkg-mgr, -p")} Package manager (npm, pnpm, yarn, bun)`,
1292
+ ` ${chalk5.cyan("--no-install")} Skip dependency installation`,
1293
+ ` ${chalk5.cyan("--list-templates")} List all available community templates`,
1294
+ ` ${chalk5.cyan("--wizard")} Run config.bifrost wizard`,
1295
+ ` ${chalk5.cyan("--submit")} Submit template to bifrost registry`,
1296
+ ""
1297
+ ]
1298
+ );
1299
+ }
1300
+ async function runCLI(argv) {
1301
+ const registry = await loadRegistry();
1302
+ const args = argv.slice(2);
1303
+ const flags = {};
1304
+ let projectName;
1305
+ for (let i = 0; i < args.length; i++) {
1306
+ const arg = args[i];
1307
+ if (arg === "--help" || arg === "-h") {
1308
+ flags.help = true;
1309
+ } else if (arg === "--version" || arg === "-V") {
1310
+ flags.version = true;
1311
+ } else if (arg === "--list-templates") {
1312
+ flags.listTemplates = true;
1313
+ if (args[i + 1] && !args[i + 1].startsWith("-")) {
1314
+ flags.platform = args[++i];
1315
+ }
1316
+ } else if (arg === "--wizard") {
1317
+ flags.wizard = true;
1318
+ } else if (arg === "--submit") {
1319
+ flags.submit = true;
1320
+ } else if (arg === "--no-install") {
1321
+ flags.noInstall = true;
1322
+ } else if (arg === "--template" || arg === "-t") {
1323
+ flags.template = args[++i];
1324
+ } else if (arg === "--pkg-mgr" || arg === "-p") {
1325
+ flags.pkgMgr = args[++i];
1326
+ } else if (!arg.startsWith("-") && !projectName) {
1327
+ projectName = arg;
1328
+ }
1329
+ }
1330
+ if (flags.help) {
1331
+ showHelp();
1332
+ process.exit(0);
1333
+ }
1334
+ if (flags.version) {
1335
+ console.log(`BIFR\xD6ST V${VERSION}`);
1336
+ process.exit(0);
1337
+ }
1338
+ if (flags.listTemplates) {
1339
+ showTemplates(registry, flags.platform);
1340
+ process.exit(0);
1341
+ }
1342
+ if (flags.wizard) {
1343
+ await runConfigWizard();
1344
+ process.exit(0);
1345
+ }
1346
+ if (flags.submit) {
1347
+ await submitTemplate();
1348
+ process.exit(0);
1349
+ }
1350
+ try {
1351
+ if (flags.pkgMgr && !PACKAGE_MANAGERS.includes(flags.pkgMgr)) {
1352
+ console.error(chalk5.red(`Invalid package manager. Must be one of: ${PACKAGE_MANAGERS.join(", ")}`));
1353
+ process.exit(1);
1354
+ }
1355
+ let action = "create";
1356
+ let finalProjectName = projectName;
1357
+ let finalTemplate = flags.template;
1358
+ let finalPackageManager = flags.pkgMgr;
1359
+ let finalInstall = flags.noInstall ? false : void 0;
1360
+ let tailwindBase = false;
1361
+ let tailwindNgin = false;
1362
+ let midgardr = false;
1363
+ let baldr = false;
1364
+ let gitPush = false;
1365
+ if (!projectName && !flags.template) {
1366
+ action = await promptMainMenu();
1367
+ if (action === "wizard") {
1368
+ await runConfigWizard();
1369
+ process.exit(0);
1370
+ }
1371
+ if (action === "submit") {
1372
+ await submitTemplate();
1373
+ process.exit(0);
1374
+ }
1375
+ }
1376
+ if (!finalProjectName) {
1377
+ finalProjectName = await promptProjectName();
1378
+ }
1379
+ if (!finalTemplate) {
1380
+ const platform = await promptPlatform();
1381
+ const templateChoice = await promptTemplateChoice(platform);
1382
+ finalTemplate = templateChoice.template;
1383
+ }
1384
+ if (!finalPackageManager) {
1385
+ finalPackageManager = await promptPackageManager();
1386
+ }
1387
+ const additionalOptions = await promptAdditionalOptions();
1388
+ tailwindBase = additionalOptions.tailwindBase;
1389
+ tailwindNgin = additionalOptions.tailwindNgin;
1390
+ midgardr = additionalOptions.midgardr;
1391
+ baldr = additionalOptions.baldr;
1392
+ finalInstall = finalInstall !== void 0 ? finalInstall : additionalOptions.install;
1393
+ gitPush = additionalOptions.gitPush;
1394
+ const validProjectName = toValidPackageName(finalProjectName);
1395
+ await createProject({
1396
+ projectName: validProjectName,
1397
+ template: finalTemplate,
1398
+ packageManager: finalPackageManager,
1399
+ install: finalInstall,
1400
+ gitPush,
1401
+ tailwindBase,
1402
+ tailwindNgin,
1403
+ midgardr,
1404
+ baldr
1405
+ });
1406
+ } catch (error) {
1407
+ console.error();
1408
+ console.error(chalk5.red("Error:"), error instanceof Error ? error.message : "Unknown error");
1409
+ console.error();
1410
+ process.exit(1);
1411
+ }
1412
+ }
1413
+ export {
1414
+ runCLI
1415
+ };