create-krispya 0.5.1 → 0.5.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/cli.cjs CHANGED
@@ -159,6 +159,12 @@ function getReuseWindow() {
159
159
  function setReuseWindow(reuse) {
160
160
  config.set("reuseWindow", reuse);
161
161
  }
162
+ function getAiFiles() {
163
+ return config.get("aiFiles");
164
+ }
165
+ function setAiFiles(files) {
166
+ config.set("aiFiles", files);
167
+ }
162
168
  function clearConfig() {
163
169
  config.clear();
164
170
  }
@@ -446,31 +452,14 @@ async function promptForMonorepoCustomization(name) {
446
452
  p__namespace.cancel("Operation cancelled.");
447
453
  process.exit(0);
448
454
  }
449
- const packageManager = await p__namespace.select({
450
- message: "Package manager",
451
- options: [
452
- { value: "pnpm", label: "pnpm" },
453
- { value: "npm", label: "npm" },
454
- { value: "yarn", label: "yarn" }
455
- ],
456
- initialValue: "pnpm"
455
+ const managePnpm = await p__namespace.confirm({
456
+ message: "Enable manage-package-manager-versions?",
457
+ initialValue: true
457
458
  });
458
- if (p__namespace.isCancel(packageManager)) {
459
+ if (p__namespace.isCancel(managePnpm)) {
459
460
  p__namespace.cancel("Operation cancelled.");
460
461
  process.exit(0);
461
462
  }
462
- let pnpmManageVersions = true;
463
- if (packageManager === "pnpm") {
464
- const managePnpm = await p__namespace.confirm({
465
- message: "Enable manage-package-manager-versions?",
466
- initialValue: true
467
- });
468
- if (p__namespace.isCancel(managePnpm)) {
469
- p__namespace.cancel("Operation cancelled.");
470
- process.exit(0);
471
- }
472
- pnpmManageVersions = managePnpm;
473
- }
474
463
  const linter = await p__namespace.select({
475
464
  message: "Linter",
476
465
  options: [
@@ -501,8 +490,8 @@ async function promptForMonorepoCustomization(name) {
501
490
  name,
502
491
  projectType: "monorepo",
503
492
  nodeVersion,
504
- packageManager,
505
- pnpmManageVersions,
493
+ packageManager: "pnpm",
494
+ pnpmManageVersions: managePnpm,
506
495
  linter,
507
496
  formatter
508
497
  };
@@ -686,8 +675,79 @@ async function promptForPackageOptions(projectName, projectType, inheritedToolin
686
675
  return promptForCustomization(template, projectName, projectType, integrations, inheritedTooling);
687
676
  }
688
677
 
678
+ async function checkAnyExists(paths) {
679
+ for (const path of paths) {
680
+ try {
681
+ await promises.access(path, promises.constants.F_OK);
682
+ return true;
683
+ } catch {
684
+ }
685
+ }
686
+ return false;
687
+ }
688
+ async function validateWorkspace(monorepoRoot) {
689
+ const errors = [];
690
+ const tsConfigPath = path.join(monorepoRoot, ".config/typescript/package.json");
691
+ try {
692
+ await promises.access(tsConfigPath, promises.constants.F_OK);
693
+ } catch {
694
+ errors.push("Missing .config/typescript package");
695
+ }
696
+ const linterPaths = [
697
+ path.join(monorepoRoot, ".config/oxlint/package.json"),
698
+ path.join(monorepoRoot, ".config/eslint/package.json"),
699
+ path.join(monorepoRoot, "eslint.config.js"),
700
+ path.join(monorepoRoot, "biome.json")
701
+ ];
702
+ const hasLinter = await checkAnyExists(linterPaths);
703
+ if (!hasLinter) {
704
+ errors.push(
705
+ "Missing linter config (.config/oxlint, .config/eslint, eslint.config.js, or biome.json)"
706
+ );
707
+ }
708
+ const formatterPaths = [
709
+ path.join(monorepoRoot, ".config/oxfmt/package.json"),
710
+ path.join(monorepoRoot, ".config/prettier/package.json"),
711
+ path.join(monorepoRoot, ".prettierrc.json"),
712
+ path.join(monorepoRoot, "biome.json")
713
+ ];
714
+ const hasFormatter = await checkAnyExists(formatterPaths);
715
+ if (!hasFormatter) {
716
+ errors.push(
717
+ "Missing formatter config (.config/oxfmt, .config/prettier, .prettierrc.json, or biome.json)"
718
+ );
719
+ }
720
+ return { valid: errors.length === 0, errors };
721
+ }
722
+
689
723
  const require$1 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href)));
690
724
  const pkg = require$1("../package.json");
725
+ async function fileExists(path) {
726
+ try {
727
+ await promises.access(path, fs.constants.F_OK);
728
+ return true;
729
+ } catch {
730
+ return false;
731
+ }
732
+ }
733
+ async function writeGeneratedFiles(basePath, files) {
734
+ const filePaths = Object.keys(files).sort();
735
+ for (const filePath of filePaths) {
736
+ const fullFilePath = path.join(basePath, filePath);
737
+ await promises.mkdir(path.dirname(fullFilePath), { recursive: true });
738
+ const file = files[filePath];
739
+ if (file.type === "text") {
740
+ await promises.writeFile(fullFilePath, file.content);
741
+ } else {
742
+ const response = await undici.fetch(file.url);
743
+ await promises.writeFile(fullFilePath, response.body);
744
+ }
745
+ }
746
+ }
747
+ function calculateWorkspaceRoot(packagePath) {
748
+ const segments = packagePath.split(/[/\\]/).filter(Boolean);
749
+ return segments.map(() => "..").join("/");
750
+ }
691
751
  async function detectMonorepoRoot() {
692
752
  let currentDir = process$1.cwd();
693
753
  const root = path.resolve("/");
@@ -705,12 +765,21 @@ async function detectMonorepoRoot() {
705
765
  }
706
766
  return null;
707
767
  }
768
+ async function parseWorkspaceDirectories(monorepoRoot) {
769
+ try {
770
+ const workspaceFile = path.join(monorepoRoot, "pnpm-workspace.yaml");
771
+ const content = await promises.readFile(workspaceFile, "utf-8");
772
+ return index.parseWorkspaceYamlContent(content);
773
+ } catch {
774
+ return [];
775
+ }
776
+ }
708
777
  async function detectWorkspaceTooling(monorepoRoot) {
709
778
  try {
710
779
  const pkgPath = path.join(monorepoRoot, "package.json");
711
780
  const content = await promises.readFile(pkgPath, "utf-8");
712
- const pkg2 = JSON.parse(content);
713
- const devDeps = pkg2.devDependencies ?? {};
781
+ const pkgJson = JSON.parse(content);
782
+ const devDeps = pkgJson.devDependencies ?? {};
714
783
  const linter = devDeps.oxlint ? "oxlint" : devDeps.eslint ? "eslint" : devDeps["@biomejs/biome"] ? "biome" : void 0;
715
784
  const formatter = devDeps.oxfmt ? "oxfmt" : devDeps.prettier ? "prettier" : devDeps["@biomejs/biome"] ? "biome" : void 0;
716
785
  return { linter, formatter };
@@ -718,13 +787,33 @@ async function detectWorkspaceTooling(monorepoRoot) {
718
787
  return {};
719
788
  }
720
789
  }
790
+ async function detectExistingConfigs(monorepoRoot) {
791
+ const configs = {};
792
+ const eslintPath = path.join(monorepoRoot, "eslint.config.js");
793
+ if (await fileExists(eslintPath)) {
794
+ configs.linter = "eslint";
795
+ configs.eslintConfigPath = eslintPath;
796
+ }
797
+ const prettierPath = path.join(monorepoRoot, ".prettierrc.json");
798
+ if (await fileExists(prettierPath)) {
799
+ configs.formatter = "prettier";
800
+ configs.prettierConfigPath = prettierPath;
801
+ }
802
+ const biomePath = path.join(monorepoRoot, "biome.json");
803
+ if (await fileExists(biomePath)) {
804
+ configs.biomeConfigPath = biomePath;
805
+ if (!configs.linter) configs.linter = "biome";
806
+ if (!configs.formatter) configs.formatter = "biome";
807
+ }
808
+ return configs;
809
+ }
721
810
  async function getMonorepoScope(monorepoRoot) {
722
811
  try {
723
812
  const pkgPath = path.join(monorepoRoot, "package.json");
724
813
  const content = await promises.readFile(pkgPath, "utf-8");
725
- const pkg2 = JSON.parse(content);
726
- if (pkg2.name) {
727
- return pkg2.name.replace(/^@/, "").replace(/\/.*$/, "");
814
+ const pkgJson = JSON.parse(content);
815
+ if (pkgJson.name) {
816
+ return pkgJson.name.replace(/^@/, "").replace(/\/.*$/, "");
728
817
  }
729
818
  } catch {
730
819
  }
@@ -732,56 +821,264 @@ async function getMonorepoScope(monorepoRoot) {
732
821
  }
733
822
  async function getWorkspacePackages(monorepoRoot) {
734
823
  const packagesDir = path.join(monorepoRoot, "packages");
735
- const packages = [];
736
824
  try {
737
825
  const { readdir } = await import('fs/promises');
738
826
  const entries = await readdir(packagesDir, { withFileTypes: true });
827
+ const names = [];
739
828
  for (const entry of entries) {
740
- if (entry.isDirectory()) {
741
- try {
742
- const pkgJsonPath = path.join(packagesDir, entry.name, "package.json");
743
- const content = await promises.readFile(pkgJsonPath, "utf-8");
744
- const pkg2 = JSON.parse(content);
745
- if (pkg2.name) {
746
- packages.push({ name: pkg2.name, path: `packages/${entry.name}` });
747
- }
748
- } catch {
749
- }
829
+ if (!entry.isDirectory()) continue;
830
+ try {
831
+ const content = await promises.readFile(
832
+ path.join(packagesDir, entry.name, "package.json"),
833
+ "utf-8"
834
+ );
835
+ const pkg2 = JSON.parse(content);
836
+ if (pkg2.name) names.push(pkg2.name);
837
+ } catch {
750
838
  }
751
839
  }
840
+ return names;
841
+ } catch {
842
+ return [];
843
+ }
844
+ }
845
+ async function ensureConfigInWorkspace(monorepoRoot) {
846
+ const workspacePath = path.join(monorepoRoot, "pnpm-workspace.yaml");
847
+ let content;
848
+ try {
849
+ content = await promises.readFile(workspacePath, "utf-8");
850
+ } catch {
851
+ content = `packages:
852
+ - ".config/*"
853
+ - "packages/*"
854
+ `;
855
+ await promises.writeFile(workspacePath, content);
856
+ return;
857
+ }
858
+ if (content.includes(".config/*") || content.includes('".config/*"')) {
859
+ return;
860
+ }
861
+ const lines = content.split("\n");
862
+ const packagesIndex = lines.findIndex(
863
+ (line) => line.trim().startsWith("packages:")
864
+ );
865
+ if (packagesIndex === -1) {
866
+ content = `packages:
867
+ - ".config/*"
868
+ ${content}`;
869
+ } else {
870
+ lines.splice(packagesIndex + 1, 0, ' - ".config/*"');
871
+ content = lines.join("\n");
872
+ }
873
+ await promises.writeFile(workspacePath, content);
874
+ }
875
+ async function migrateEslintConfig(monorepoRoot, files) {
876
+ const configBasePath = ".config/eslint";
877
+ const existingConfigPath = path.join(monorepoRoot, "eslint.config.js");
878
+ let existingContent;
879
+ try {
880
+ existingContent = await promises.readFile(existingConfigPath, "utf-8");
881
+ } catch {
882
+ index.generateEslintConfigPackage(files);
883
+ return;
884
+ }
885
+ files[`${configBasePath}/package.json`] = {
886
+ type: "text",
887
+ content: JSON.stringify(
888
+ {
889
+ name: "@config/eslint",
890
+ version: "0.1.0",
891
+ private: true,
892
+ type: "module",
893
+ exports: {
894
+ "./base": "./base.js",
895
+ "./react": "./react.js"
896
+ }
897
+ },
898
+ null,
899
+ 2
900
+ )
901
+ };
902
+ files[`${configBasePath}/README.md`] = {
903
+ type: "text",
904
+ content: `# \`@config/eslint\`
905
+
906
+ Shared ESLint configurations.
907
+
908
+ ## Usage
909
+
910
+ In your package's \`eslint.config.js\`:
911
+
912
+ \`\`\`js
913
+ import base from "@config/eslint/base";
914
+
915
+ export default [...base];
916
+ \`\`\`
917
+
918
+ ## Available Configs
919
+
920
+ - \`base\` - Base ESLint rules (migrated from root)
921
+ - \`react\` - React-specific rules
922
+ `
923
+ };
924
+ files[`${configBasePath}/base.js`] = {
925
+ type: "text",
926
+ content: existingContent
927
+ };
928
+ files[`${configBasePath}/react.js`] = {
929
+ type: "text",
930
+ content: `import react from "eslint-plugin-react";
931
+ import reactHooks from "eslint-plugin-react-hooks";
932
+
933
+ export default [
934
+ {
935
+ plugins: {
936
+ react,
937
+ "react-hooks": reactHooks,
938
+ },
939
+ rules: {
940
+ ...react.configs.recommended.rules,
941
+ ...reactHooks.configs.recommended.rules,
942
+ "react/react-in-jsx-scope": "off",
943
+ },
944
+ settings: {
945
+ react: {
946
+ version: "detect",
947
+ },
948
+ },
949
+ },
950
+ ];
951
+ `
952
+ };
953
+ }
954
+ async function migratePrettierConfig(monorepoRoot, files) {
955
+ const configBasePath = ".config/prettier";
956
+ const existingConfigPath = path.join(monorepoRoot, ".prettierrc.json");
957
+ let existingContent;
958
+ try {
959
+ existingContent = await promises.readFile(existingConfigPath, "utf-8");
752
960
  } catch {
961
+ index.generatePrettierConfigPackage(files);
962
+ return;
753
963
  }
754
- return packages;
964
+ files[`${configBasePath}/package.json`] = {
965
+ type: "text",
966
+ content: JSON.stringify(
967
+ {
968
+ name: "@config/prettier",
969
+ version: "0.1.0",
970
+ private: true,
971
+ exports: {
972
+ "./base": "./base.json"
973
+ }
974
+ },
975
+ null,
976
+ 2
977
+ )
978
+ };
979
+ files[`${configBasePath}/README.md`] = {
980
+ type: "text",
981
+ content: `# \`@config/prettier\`
982
+
983
+ Shared Prettier configurations.
984
+
985
+ ## Usage
986
+
987
+ In your package's \`.prettierrc\`:
988
+
989
+ \`\`\`json
990
+ "@config/prettier/base"
991
+ \`\`\`
992
+
993
+ Or in \`package.json\`:
994
+
995
+ \`\`\`json
996
+ {
997
+ "prettier": "@config/prettier/base"
998
+ }
999
+ \`\`\`
1000
+
1001
+ ## Available Configs
1002
+
1003
+ - \`base\` - Base Prettier rules (migrated from root)
1004
+ `
1005
+ };
1006
+ files[`${configBasePath}/base.json`] = {
1007
+ type: "text",
1008
+ content: existingContent
1009
+ };
755
1010
  }
756
1011
  async function createPackageInWorkspace(monorepoRoot, packageManager, inheritedTooling, scope) {
1012
+ const workspaceDirectories = await parseWorkspaceDirectories(monorepoRoot);
1013
+ const defaultDirectories = ["apps", "packages"];
1014
+ const hasCustomDirectories = workspaceDirectories.length > 0 && !workspaceDirectories.every((dir) => defaultDirectories.includes(dir));
757
1015
  const packageType = await promptForInitialPackage();
758
1016
  if (packageType === "skip") {
759
1017
  return false;
760
1018
  }
1019
+ const defaultDir = packageType === "app" ? "apps" : "packages";
761
1020
  const packageNameInput = await p__namespace.text({
762
1021
  message: "Package name?",
763
- placeholder: `Scoped to @${scope}/`,
1022
+ initialValue: `@${scope}/`,
764
1023
  validate: (value) => {
765
- if (!value.length) return "Package name is required";
1024
+ const validationError = index.validatePackageName(value);
1025
+ if (validationError) return validationError;
1026
+ const dirName = value.includes("/") ? value.split("/").pop() : value;
1027
+ if (!dirName) return "Package name is required";
1028
+ if (!hasCustomDirectories) {
1029
+ const targetPath = path.join(monorepoRoot, defaultDir, dirName);
1030
+ try {
1031
+ const { statSync } = require$1("fs");
1032
+ statSync(targetPath);
1033
+ return `Directory ${defaultDir}/${dirName} already exists`;
1034
+ } catch {
1035
+ }
1036
+ }
766
1037
  }
767
1038
  });
768
1039
  if (p__namespace.isCancel(packageNameInput)) {
769
1040
  return false;
770
1041
  }
771
- const shortName = packageNameInput;
772
- const scopedName = `@${scope}/${shortName}`;
773
- const targetDir = packageType === "app" ? "apps" : "packages";
774
- const packagePath = path.join(targetDir, shortName);
775
- const workspaceRoot = "../..";
1042
+ const scopedName = packageNameInput;
1043
+ const shortName = scopedName.includes("/") ? scopedName.split("/").pop() : scopedName;
776
1044
  const packageOptions = await promptForPackageOptions(
777
1045
  scopedName,
778
1046
  packageType,
779
1047
  inheritedTooling
780
1048
  );
1049
+ let targetDir = defaultDir;
1050
+ if (hasCustomDirectories && workspaceDirectories.length > 0) {
1051
+ const dirChoice = await p__namespace.select({
1052
+ message: "Target directory",
1053
+ options: workspaceDirectories.map((dir) => ({
1054
+ value: dir,
1055
+ label: dir
1056
+ })),
1057
+ initialValue: workspaceDirectories.includes(defaultDir) ? defaultDir : workspaceDirectories[0]
1058
+ });
1059
+ if (p__namespace.isCancel(dirChoice)) {
1060
+ return false;
1061
+ }
1062
+ targetDir = dirChoice;
1063
+ const targetPath = path.join(monorepoRoot, targetDir, shortName);
1064
+ try {
1065
+ const { statSync } = require$1("fs");
1066
+ statSync(targetPath);
1067
+ p__namespace.log.error(`Directory ${targetDir}/${shortName} already exists`);
1068
+ return false;
1069
+ } catch {
1070
+ }
1071
+ }
1072
+ const relativePkgPath = path.join(targetDir, shortName);
1073
+ const workspaceRoot = calculateWorkspaceRoot(relativePkgPath);
781
1074
  packageOptions.workspaceRoot = workspaceRoot;
782
1075
  packageOptions.name = scopedName;
783
1076
  if (packageManager === "pnpm") {
784
1077
  packageOptions.pnpmVersion = await index.getLatestPnpmVersion();
1078
+ } else if (packageManager === "yarn") {
1079
+ packageOptions.yarnVersion = await index.getLatestYarnVersion();
1080
+ } else if (packageManager === "npm") {
1081
+ packageOptions.npmVersion = await index.getLatestNpmCliVersion();
785
1082
  }
786
1083
  const nodeVersion = packageOptions.nodeVersion ?? "latest";
787
1084
  if (nodeVersion === "latest") {
@@ -847,40 +1144,26 @@ async function createPackageInWorkspace(monorepoRoot, packageManager, inheritedT
847
1144
  }
848
1145
  await Promise.all(versionPromises);
849
1146
  packageOptions.versions = versions;
850
- if (packageType === "app") {
851
- const workspacePackages = await getWorkspacePackages(monorepoRoot);
852
- if (workspacePackages.length > 0) {
853
- const selectedDeps = await p__namespace.multiselect({
854
- message: "Add workspace dependencies?",
855
- options: workspacePackages.map((pkg2) => ({
856
- value: pkg2.name,
857
- label: pkg2.name.replace(/^@[^/]+\//, "")
858
- })),
859
- required: false
860
- });
861
- if (!p__namespace.isCancel(selectedDeps) && selectedDeps.length > 0) {
862
- packageOptions.workspaceDependencies = selectedDeps;
863
- }
1147
+ const workspacePackages = packageType === "app" ? await getWorkspacePackages(monorepoRoot) : [];
1148
+ if (workspacePackages.length > 0) {
1149
+ const selectedDeps = await p__namespace.multiselect({
1150
+ message: "Add workspace dependencies?",
1151
+ options: workspacePackages.map((name) => ({ value: name, label: name })),
1152
+ required: false
1153
+ });
1154
+ if (!p__namespace.isCancel(selectedDeps) && selectedDeps.length > 0) {
1155
+ packageOptions.workspaceDependencies = selectedDeps;
864
1156
  }
865
1157
  }
866
- const basePath = path.join(monorepoRoot, packagePath);
867
- const s = p__namespace.spinner();
868
- s.start("Creating package...");
1158
+ const outputPath = path.join(monorepoRoot, relativePkgPath);
1159
+ const spinner = p__namespace.spinner();
1160
+ spinner.start("Creating package...");
869
1161
  try {
870
1162
  const files = index.generate(packageOptions);
871
- const filePaths = Object.keys(files).sort();
872
- for (const filePath of filePaths) {
873
- const fullFilePath = path.join(basePath, filePath);
874
- await promises.mkdir(path.dirname(fullFilePath), { recursive: true });
875
- const file = files[filePath];
876
- if (file.type === "text") {
877
- await promises.writeFile(fullFilePath, file.content);
878
- } else {
879
- const response = await undici.fetch(file.url);
880
- await promises.writeFile(fullFilePath, response.body);
881
- }
882
- }
883
- s.stop(color__default.green.inverse(` \u2713 Package created at ${packagePath}! `));
1163
+ await writeGeneratedFiles(outputPath, files);
1164
+ spinner.stop(
1165
+ color__default.green.inverse(` \u2713 Package created at ${relativePkgPath}! `)
1166
+ );
884
1167
  const addAnother = await p__namespace.select({
885
1168
  message: "Add another package?",
886
1169
  options: [
@@ -891,12 +1174,12 @@ async function createPackageInWorkspace(monorepoRoot, packageManager, inheritedT
891
1174
  });
892
1175
  return !p__namespace.isCancel(addAnother) && addAnother === "yes";
893
1176
  } catch (error) {
894
- s.stop("Failed to create package");
1177
+ spinner.stop("Failed to create package");
895
1178
  p__namespace.log.error(String(error));
896
1179
  return false;
897
1180
  }
898
1181
  }
899
- async function promptAndOpenEditor(basePath) {
1182
+ async function promptAndOpenEditor(projectPath) {
900
1183
  const savedEditor = getPreferredEditor();
901
1184
  let selectedEditor;
902
1185
  if (savedEditor && savedEditor !== "skip") {
@@ -946,7 +1229,7 @@ async function promptAndOpenEditor(basePath) {
946
1229
  try {
947
1230
  await openInEditor(
948
1231
  selectedEditor,
949
- basePath,
1232
+ projectPath,
950
1233
  getReuseWindow()
951
1234
  );
952
1235
  p__namespace.log.success(`Opening in ${editorNames[selectedEditor]}...`);
@@ -957,14 +1240,673 @@ async function promptAndOpenEditor(basePath) {
957
1240
  }
958
1241
  }
959
1242
  }
1243
+ async function handleCheckCommand() {
1244
+ const monorepoRoot = await detectMonorepoRoot();
1245
+ if (!monorepoRoot) {
1246
+ console.log(color__default.red("\u2717") + " Not a monorepo workspace");
1247
+ process.exit(1);
1248
+ }
1249
+ const { valid, errors } = await validateWorkspace(monorepoRoot);
1250
+ if (valid) {
1251
+ console.log(color__default.green("\u2713") + " Valid monorepo workspace");
1252
+ console.log(color__default.dim(` ${monorepoRoot}`));
1253
+ } else {
1254
+ console.log(color__default.red("\u2717") + " Invalid monorepo workspace");
1255
+ console.log(color__default.dim(` ${monorepoRoot}`));
1256
+ for (const error of errors) {
1257
+ console.log(color__default.red(` \u2022 ${error}`));
1258
+ }
1259
+ }
1260
+ process.exit(valid ? 0 : 1);
1261
+ }
1262
+ async function handleFixCommand(options) {
1263
+ const monorepoRoot = await detectMonorepoRoot();
1264
+ if (!monorepoRoot) {
1265
+ console.log(color__default.red("\u2717") + " Not a monorepo workspace");
1266
+ console.log(color__default.dim(" Run this command from within a monorepo"));
1267
+ process.exit(1);
1268
+ }
1269
+ const { valid, errors } = await validateWorkspace(monorepoRoot);
1270
+ if (valid) {
1271
+ console.log(color__default.green("\u2713") + " Workspace is already valid");
1272
+ console.log(color__default.dim(` ${monorepoRoot}`));
1273
+ process.exit(0);
1274
+ }
1275
+ console.log(color__default.yellow("!") + " Invalid monorepo workspace");
1276
+ for (const error of errors) {
1277
+ console.log(color__default.dim(` \u2022 ${error}`));
1278
+ }
1279
+ console.log();
1280
+ const tooling = await detectWorkspaceTooling(monorepoRoot);
1281
+ const existingConfigs = await detectExistingConfigs(monorepoRoot);
1282
+ const detectedLinter = tooling.linter ?? existingConfigs.linter ?? "oxlint";
1283
+ const detectedFormatter = tooling.formatter ?? existingConfigs.formatter ?? "oxfmt";
1284
+ const isNonInteractive = options.linter && options.formatter;
1285
+ let linter;
1286
+ let formatter;
1287
+ if (isNonInteractive) {
1288
+ linter = options.linter;
1289
+ formatter = options.formatter;
1290
+ } else {
1291
+ const linterChoice = await p__namespace.select({
1292
+ message: "Linter",
1293
+ options: [
1294
+ {
1295
+ value: "oxlint",
1296
+ label: "oxlint" + (tooling.linter === "oxlint" ? color__default.dim(" (installed)") : "")
1297
+ },
1298
+ {
1299
+ value: "eslint",
1300
+ label: "eslint" + (tooling.linter === "eslint" || existingConfigs.linter === "eslint" ? color__default.dim(" (installed)") : "")
1301
+ },
1302
+ {
1303
+ value: "biome",
1304
+ label: "biome" + (tooling.linter === "biome" ? color__default.dim(" (installed)") : "")
1305
+ }
1306
+ ],
1307
+ initialValue: detectedLinter
1308
+ });
1309
+ if (p__namespace.isCancel(linterChoice)) {
1310
+ p__namespace.cancel("Operation cancelled.");
1311
+ process.exit(0);
1312
+ }
1313
+ const formatterChoice = await p__namespace.select({
1314
+ message: "Formatter",
1315
+ options: [
1316
+ {
1317
+ value: "oxfmt",
1318
+ label: "oxfmt" + (tooling.formatter === "oxfmt" ? color__default.dim(" (installed)") : "")
1319
+ },
1320
+ {
1321
+ value: "prettier",
1322
+ label: "prettier" + (tooling.formatter === "prettier" || existingConfigs.formatter === "prettier" ? color__default.dim(" (installed)") : "")
1323
+ },
1324
+ {
1325
+ value: "biome",
1326
+ label: "biome" + (tooling.formatter === "biome" ? color__default.dim(" (installed)") : "")
1327
+ }
1328
+ ],
1329
+ initialValue: detectedFormatter
1330
+ });
1331
+ if (p__namespace.isCancel(formatterChoice)) {
1332
+ p__namespace.cancel("Operation cancelled.");
1333
+ process.exit(0);
1334
+ }
1335
+ linter = linterChoice;
1336
+ formatter = formatterChoice;
1337
+ }
1338
+ console.log();
1339
+ const spinner = p__namespace.spinner();
1340
+ spinner.start("Fixing workspace...");
1341
+ try {
1342
+ const files = {};
1343
+ const tsConfigExists = await fileExists(
1344
+ path.join(monorepoRoot, ".config/typescript/package.json")
1345
+ );
1346
+ if (!tsConfigExists) {
1347
+ index.generateTypescriptConfigPackage(files);
1348
+ }
1349
+ if (linter === "oxlint") {
1350
+ const oxlintExists = await fileExists(
1351
+ path.join(monorepoRoot, ".config/oxlint/package.json")
1352
+ );
1353
+ if (!oxlintExists) index.generateOxlintConfigPackage(files);
1354
+ } else if (linter === "eslint") {
1355
+ const eslintPkgExists = await fileExists(
1356
+ path.join(monorepoRoot, ".config/eslint/package.json")
1357
+ );
1358
+ if (!eslintPkgExists) {
1359
+ if (existingConfigs.eslintConfigPath) {
1360
+ await migrateEslintConfig(monorepoRoot, files);
1361
+ } else {
1362
+ index.generateEslintConfigPackage(files);
1363
+ }
1364
+ }
1365
+ }
1366
+ if (formatter === "oxfmt") {
1367
+ const oxfmtExists = await fileExists(
1368
+ path.join(monorepoRoot, ".config/oxfmt/package.json")
1369
+ );
1370
+ if (!oxfmtExists) index.generateOxfmtConfigPackage(files);
1371
+ } else if (formatter === "prettier") {
1372
+ const prettierPkgExists = await fileExists(
1373
+ path.join(monorepoRoot, ".config/prettier/package.json")
1374
+ );
1375
+ if (!prettierPkgExists) {
1376
+ if (existingConfigs.prettierConfigPath) {
1377
+ await migratePrettierConfig(monorepoRoot, files);
1378
+ } else {
1379
+ index.generatePrettierConfigPackage(files);
1380
+ }
1381
+ }
1382
+ }
1383
+ if ((linter === "biome" || formatter === "biome") && !existingConfigs.biomeConfigPath) {
1384
+ const biomeConfig = {
1385
+ $schema: "https://biomejs.dev/schemas/1.9.4/schema.json",
1386
+ vcs: {
1387
+ enabled: true,
1388
+ clientKind: "git",
1389
+ useIgnoreFile: true
1390
+ },
1391
+ linter: {
1392
+ enabled: linter === "biome",
1393
+ rules: {
1394
+ recommended: true
1395
+ }
1396
+ },
1397
+ formatter: {
1398
+ enabled: formatter === "biome"
1399
+ }
1400
+ };
1401
+ files["biome.json"] = {
1402
+ type: "text",
1403
+ content: JSON.stringify(biomeConfig, null, 2)
1404
+ };
1405
+ }
1406
+ for (const [filePath, file] of Object.entries(files)) {
1407
+ const fullPath = path.join(monorepoRoot, filePath);
1408
+ await promises.mkdir(path.dirname(fullPath), { recursive: true });
1409
+ await promises.writeFile(fullPath, file.content);
1410
+ }
1411
+ await ensureConfigInWorkspace(monorepoRoot);
1412
+ if (existingConfigs.eslintConfigPath && linter === "eslint") {
1413
+ try {
1414
+ await promises.unlink(existingConfigs.eslintConfigPath);
1415
+ } catch {
1416
+ }
1417
+ }
1418
+ if (existingConfigs.prettierConfigPath && formatter === "prettier") {
1419
+ try {
1420
+ await promises.unlink(existingConfigs.prettierConfigPath);
1421
+ } catch {
1422
+ }
1423
+ }
1424
+ spinner.stop(color__default.green("\u2713") + " Workspace fixed!");
1425
+ const generated = Object.keys(files).filter(
1426
+ (f) => f.endsWith("package.json")
1427
+ );
1428
+ for (const pkgFile of generated) {
1429
+ const pkgName = pkgFile.replace("/package.json", "");
1430
+ console.log(color__default.dim(` Generated ${pkgName}`));
1431
+ }
1432
+ const vscodeSettingsExists = await fileExists(
1433
+ path.join(monorepoRoot, ".vscode/settings.json")
1434
+ );
1435
+ const vscodeExtensionsExists = await fileExists(
1436
+ path.join(monorepoRoot, ".vscode/extensions.json")
1437
+ );
1438
+ const vscodeExists = vscodeSettingsExists && vscodeExtensionsExists;
1439
+ if (!vscodeExists) {
1440
+ let addVscode = false;
1441
+ if (isNonInteractive) {
1442
+ addVscode = true;
1443
+ } else {
1444
+ const vscodeChoice = await p__namespace.confirm({
1445
+ message: "Generate VS Code settings?",
1446
+ initialValue: true
1447
+ });
1448
+ addVscode = !p__namespace.isCancel(vscodeChoice) && vscodeChoice;
1449
+ }
1450
+ if (addVscode) {
1451
+ const vscodeFiles = {};
1452
+ index.generateVscodeFiles(vscodeFiles, linter, formatter);
1453
+ for (const [filePath, file] of Object.entries(vscodeFiles)) {
1454
+ const fullPath = path.join(monorepoRoot, filePath);
1455
+ await promises.mkdir(path.dirname(fullPath), { recursive: true });
1456
+ await promises.writeFile(fullPath, file.content);
1457
+ }
1458
+ console.log(color__default.dim(" Generated .vscode/settings.json"));
1459
+ console.log(color__default.dim(" Generated .vscode/extensions.json"));
1460
+ }
1461
+ }
1462
+ const aiFilePaths = {
1463
+ "cursor-rules": ".cursor/rules",
1464
+ "agents-md": "AGENTS.md",
1465
+ "claude-md": "CLAUDE.md",
1466
+ "copilot-md": ".github/copilot-instructions.md"
1467
+ };
1468
+ const existingAiFiles = [];
1469
+ for (const [choice, path$1] of Object.entries(aiFilePaths)) {
1470
+ if (await fileExists(path.join(monorepoRoot, path$1))) {
1471
+ existingAiFiles.push(choice);
1472
+ }
1473
+ }
1474
+ let selectedAiFiles = [];
1475
+ const savedAiFiles = getAiFiles();
1476
+ const availableChoices = ["cursor-rules", "agents-md", "claude-md", "copilot-md"].filter((c) => !existingAiFiles.includes(c));
1477
+ if (availableChoices.length === 0) {
1478
+ } else if (isNonInteractive) {
1479
+ const preferred = savedAiFiles ?? ["cursor-rules"];
1480
+ selectedAiFiles = preferred.filter((f) => availableChoices.includes(f));
1481
+ } else if (savedAiFiles && savedAiFiles.length > 0) {
1482
+ const availableSaved = savedAiFiles.filter(
1483
+ (f) => availableChoices.includes(f)
1484
+ );
1485
+ if (availableSaved.length > 0) {
1486
+ const savedLabels = availableSaved.map((f) => aiFilePaths[f]).join(", ");
1487
+ const useDefault = await p__namespace.confirm({
1488
+ message: `Generate AI instruction files? ${color__default.dim(
1489
+ `(${savedLabels})`
1490
+ )}`,
1491
+ initialValue: true
1492
+ });
1493
+ if (!p__namespace.isCancel(useDefault) && useDefault) {
1494
+ selectedAiFiles = availableSaved;
1495
+ }
1496
+ }
1497
+ } else {
1498
+ const aiFilesChoice = await p__namespace.multiselect({
1499
+ message: "Generate AI instruction files?",
1500
+ options: availableChoices.map((c) => ({
1501
+ value: c,
1502
+ label: aiFilePaths[c],
1503
+ hint: c === "cursor-rules" ? "Cursor AI" : c === "agents-md" ? "GitHub Copilot, general" : c === "claude-md" ? "Claude" : "GitHub Copilot"
1504
+ })),
1505
+ required: false
1506
+ });
1507
+ if (!p__namespace.isCancel(aiFilesChoice) && aiFilesChoice.length > 0) {
1508
+ selectedAiFiles = aiFilesChoice;
1509
+ const saveChoice = await p__namespace.confirm({
1510
+ message: "Save as default for future?",
1511
+ initialValue: true
1512
+ });
1513
+ if (!p__namespace.isCancel(saveChoice) && saveChoice) {
1514
+ setAiFiles(selectedAiFiles);
1515
+ }
1516
+ }
1517
+ }
1518
+ if (selectedAiFiles.length > 0) {
1519
+ const scope = await getMonorepoScope(monorepoRoot);
1520
+ const aiFilesOutput = {};
1521
+ index.generateAiFiles(aiFilesOutput, {
1522
+ name: scope,
1523
+ packageManager: "pnpm",
1524
+ linter,
1525
+ formatter,
1526
+ aiFiles: selectedAiFiles
1527
+ });
1528
+ for (const [filePath, file] of Object.entries(aiFilesOutput)) {
1529
+ const fullPath = path.join(monorepoRoot, filePath);
1530
+ await promises.mkdir(path.dirname(fullPath), { recursive: true });
1531
+ await promises.writeFile(fullPath, file.content);
1532
+ console.log(color__default.dim(` Generated ${filePath}`));
1533
+ }
1534
+ }
1535
+ process.exit(0);
1536
+ } catch (error) {
1537
+ spinner.stop(color__default.red("\u2717") + " Failed to fix workspace");
1538
+ console.error(error);
1539
+ process.exit(1);
1540
+ }
1541
+ }
1542
+ async function handleWorkspaceCommand(name, options) {
1543
+ const monorepoRoot = await detectMonorepoRoot();
1544
+ if (!monorepoRoot) {
1545
+ console.error(
1546
+ color__default.red("Error:") + " --workspace flag requires being inside a monorepo"
1547
+ );
1548
+ process.exit(1);
1549
+ }
1550
+ if (!name) {
1551
+ console.error(
1552
+ color__default.red("Error:") + " Package name is required with --workspace flag"
1553
+ );
1554
+ console.log(
1555
+ color__default.dim(
1556
+ " Example: pnpm create krispya my-lib --workspace --type library"
1557
+ )
1558
+ );
1559
+ process.exit(1);
1560
+ }
1561
+ const scope = await getMonorepoScope(monorepoRoot);
1562
+ const inheritedTooling = await detectWorkspaceTooling(monorepoRoot);
1563
+ const projectType = options.type ?? "app";
1564
+ const defaultDir = projectType === "library" ? "packages" : "apps";
1565
+ const targetDir = options.dir ?? defaultDir;
1566
+ const template = options.template ?? "vanilla";
1567
+ const baseTemplate = index.getBaseTemplate(template);
1568
+ const scopedName = name.startsWith("@") ? name : `@${scope}/${name}`;
1569
+ const fullPackagePath = path.join(monorepoRoot, targetDir, name);
1570
+ try {
1571
+ await promises.access(fullPackagePath, fs.constants.F_OK);
1572
+ console.error(
1573
+ color__default.red("Error:") + ` Directory ${targetDir}/${name} already exists`
1574
+ );
1575
+ process.exit(1);
1576
+ } catch {
1577
+ }
1578
+ const versions = {};
1579
+ const versionPromises = [];
1580
+ const isLibrary = projectType === "library";
1581
+ if (!isLibrary) {
1582
+ versionPromises.push(
1583
+ index.getLatestNpmVersion("vite", "6.3.4").then((v) => {
1584
+ versions.vite = v;
1585
+ })
1586
+ );
1587
+ }
1588
+ const linter = inheritedTooling.linter ?? options.linter ?? "oxlint";
1589
+ const formatter = inheritedTooling.formatter ?? options.formatter ?? "oxfmt";
1590
+ await Promise.all(versionPromises);
1591
+ const relativePkgPath = path.join(targetDir, name);
1592
+ const workspaceRoot = calculateWorkspaceRoot(relativePkgPath);
1593
+ const generateOptions = {
1594
+ name: scopedName,
1595
+ projectType,
1596
+ libraryBundler: isLibrary ? options.bundler ?? "unbuild" : void 0,
1597
+ template,
1598
+ linter,
1599
+ formatter,
1600
+ workspaceRoot,
1601
+ versions,
1602
+ ...baseTemplate === "r3f" && {
1603
+ drei: options.drei ? {} : void 0,
1604
+ handle: options.handle ? {} : void 0,
1605
+ leva: options.leva ? {} : void 0,
1606
+ postprocessing: options.postprocessing ? {} : void 0,
1607
+ rapier: options.rapier ? {} : void 0,
1608
+ xr: options.xr ? {} : void 0,
1609
+ uikit: options.uikit ? {} : void 0,
1610
+ offscreen: options.offscreen ? {} : void 0,
1611
+ zustand: options.zustand ? {} : void 0,
1612
+ koota: options.koota ? {} : void 0,
1613
+ viverse: options.viverse ? {} : void 0,
1614
+ triplex: options.triplex ? {} : void 0
1615
+ }
1616
+ };
1617
+ console.log(
1618
+ color__default.cyan("Creating") + ` ${scopedName} in ${targetDir}/${name}...`
1619
+ );
1620
+ try {
1621
+ const files = index.generate(generateOptions);
1622
+ await writeGeneratedFiles(fullPackagePath, files);
1623
+ console.log(
1624
+ color__default.green("\u2713") + ` Created ${scopedName} at ${targetDir}/${name}`
1625
+ );
1626
+ process.exit(0);
1627
+ } catch (error) {
1628
+ console.error(color__default.red("Error:") + " Failed to create package");
1629
+ console.error(String(error));
1630
+ process.exit(1);
1631
+ }
1632
+ }
1633
+ async function handleMonorepoCreation(generateOptions) {
1634
+ const { generateMonorepo } = await import('./chunks/index.cjs').then(function (n) { return n.monorepo; });
1635
+ const packageManager = generateOptions.packageManager || "pnpm";
1636
+ if (packageManager === "pnpm") {
1637
+ generateOptions.pnpmVersion = await index.getLatestPnpmVersion();
1638
+ } else if (packageManager === "yarn") {
1639
+ generateOptions.yarnVersion = await index.getLatestYarnVersion();
1640
+ } else if (packageManager === "npm") {
1641
+ generateOptions.npmVersion = await index.getLatestNpmCliVersion();
1642
+ }
1643
+ const nodeVersion = generateOptions.nodeVersion ?? "latest";
1644
+ if (nodeVersion === "latest") {
1645
+ generateOptions.nodeVersion = await index.getLatestNodeVersion();
1646
+ }
1647
+ const savedAiFiles = getAiFiles();
1648
+ let selectedAiFiles = [];
1649
+ if (savedAiFiles && savedAiFiles.length > 0) {
1650
+ const aiFileLabels = {
1651
+ "cursor-rules": ".cursor/rules",
1652
+ "agents-md": "AGENTS.md",
1653
+ "claude-md": "CLAUDE.md",
1654
+ "copilot-md": ".github/copilot-instructions.md"
1655
+ };
1656
+ const savedLabels = savedAiFiles.map((f) => aiFileLabels[f]).join(", ");
1657
+ const useDefault = await p__namespace.confirm({
1658
+ message: `Generate AI instruction files? ${color__default.dim(
1659
+ `(${savedLabels})`
1660
+ )}`,
1661
+ initialValue: true
1662
+ });
1663
+ if (!p__namespace.isCancel(useDefault) && useDefault) {
1664
+ selectedAiFiles = savedAiFiles;
1665
+ }
1666
+ } else {
1667
+ const aiFilesChoice = await p__namespace.multiselect({
1668
+ message: "Generate AI instruction files?",
1669
+ options: [
1670
+ { value: "cursor-rules", label: ".cursor/rules", hint: "Cursor AI" },
1671
+ {
1672
+ value: "agents-md",
1673
+ label: "AGENTS.md",
1674
+ hint: "GitHub Copilot, general"
1675
+ },
1676
+ { value: "claude-md", label: "CLAUDE.md", hint: "Claude" },
1677
+ {
1678
+ value: "copilot-md",
1679
+ label: ".github/copilot-instructions.md",
1680
+ hint: "GitHub Copilot"
1681
+ }
1682
+ ],
1683
+ required: false
1684
+ });
1685
+ if (!p__namespace.isCancel(aiFilesChoice) && aiFilesChoice.length > 0) {
1686
+ selectedAiFiles = aiFilesChoice;
1687
+ const saveChoice = await p__namespace.confirm({
1688
+ message: "Save as default for future monorepos?",
1689
+ initialValue: true
1690
+ });
1691
+ if (!p__namespace.isCancel(saveChoice) && saveChoice) {
1692
+ setAiFiles(selectedAiFiles);
1693
+ }
1694
+ }
1695
+ }
1696
+ const projectPath = path.join(process$1.cwd(), generateOptions.name);
1697
+ const spinner = p__namespace.spinner();
1698
+ spinner.start("Creating monorepo workspace...");
1699
+ try {
1700
+ const { files } = generateMonorepo({
1701
+ name: generateOptions.name,
1702
+ linter: generateOptions.linter ?? "oxlint",
1703
+ formatter: generateOptions.formatter ?? "oxfmt",
1704
+ packageManager,
1705
+ pnpmVersion: generateOptions.pnpmVersion,
1706
+ pnpmManageVersions: generateOptions.pnpmManageVersions,
1707
+ nodeVersion: generateOptions.nodeVersion,
1708
+ aiFiles: selectedAiFiles.length > 0 ? selectedAiFiles : void 0
1709
+ });
1710
+ const filePaths = Object.keys(files).sort();
1711
+ for (const filePath of filePaths) {
1712
+ const fullFilePath = path.join(projectPath, filePath);
1713
+ await promises.mkdir(path.dirname(fullFilePath), { recursive: true });
1714
+ const file = files[filePath];
1715
+ if (file.type === "text") {
1716
+ await promises.writeFile(fullFilePath, file.content);
1717
+ }
1718
+ }
1719
+ spinner.stop(color__default.green.inverse(" \u2713 Monorepo workspace created! "));
1720
+ const newMonorepoTooling = {
1721
+ linter: generateOptions.linter,
1722
+ formatter: generateOptions.formatter
1723
+ };
1724
+ const scope = generateOptions.name;
1725
+ let addMore = true;
1726
+ while (addMore) {
1727
+ addMore = await createPackageInWorkspace(
1728
+ projectPath,
1729
+ packageManager,
1730
+ newMonorepoTooling,
1731
+ scope
1732
+ );
1733
+ }
1734
+ const nextSteps = [
1735
+ `cd ${generateOptions.name}`,
1736
+ `${packageManager} install`,
1737
+ `${packageManager} run dev`
1738
+ ].join("\n");
1739
+ p__namespace.note(nextSteps, "Next steps");
1740
+ await promptAndOpenEditor(projectPath);
1741
+ p__namespace.outro(color__default.green("Happy coding! \u2728"));
1742
+ process.exit(0);
1743
+ } catch (error) {
1744
+ spinner.stop("Failed to create monorepo workspace");
1745
+ p__namespace.log.error(String(error));
1746
+ process.exit(1);
1747
+ }
1748
+ }
1749
+ async function handleStandaloneProjectCreation(generateOptions) {
1750
+ const base = generateOptions.template ? index.getBaseTemplate(generateOptions.template) : "vanilla";
1751
+ const defaultFallbackName = base === "vanilla" ? "vanilla-app" : base === "react" ? "react-app" : "react-three-app";
1752
+ generateOptions.name ??= defaultFallbackName;
1753
+ const packageManager = generateOptions.packageManager || "pnpm";
1754
+ if (packageManager === "pnpm") {
1755
+ generateOptions.pnpmVersion = await index.getLatestPnpmVersion();
1756
+ } else if (packageManager === "yarn") {
1757
+ generateOptions.yarnVersion = await index.getLatestYarnVersion();
1758
+ } else if (packageManager === "npm") {
1759
+ generateOptions.npmVersion = await index.getLatestNpmCliVersion();
1760
+ }
1761
+ const nodeVersion = generateOptions.nodeVersion ?? "latest";
1762
+ if (nodeVersion === "latest") {
1763
+ generateOptions.nodeVersion = await index.getLatestNodeVersion();
1764
+ }
1765
+ const versions = {};
1766
+ const versionPromises = [];
1767
+ const isLibrary = generateOptions.projectType === "library";
1768
+ const testing = generateOptions.testing ?? (isLibrary ? "vitest" : "none");
1769
+ if (testing === "vitest") {
1770
+ versionPromises.push(
1771
+ index.getLatestNpmVersion("vitest", "4.0.0").then((v) => {
1772
+ versions.vitest = v;
1773
+ })
1774
+ );
1775
+ }
1776
+ if (!isLibrary) {
1777
+ versionPromises.push(
1778
+ index.getLatestNpmVersion("vite", "6.3.4").then((v) => {
1779
+ versions.vite = v;
1780
+ })
1781
+ );
1782
+ }
1783
+ const linter = generateOptions.linter ?? "oxlint";
1784
+ if (linter === "eslint") {
1785
+ versionPromises.push(
1786
+ index.getLatestNpmVersion("eslint", "9.17.0").then((v) => {
1787
+ versions.eslint = v;
1788
+ })
1789
+ );
1790
+ } else if (linter === "oxlint") {
1791
+ versionPromises.push(
1792
+ index.getLatestNpmVersion("oxlint", "0.16.0").then((v) => {
1793
+ versions.oxlint = v;
1794
+ })
1795
+ );
1796
+ } else if (linter === "biome") {
1797
+ versionPromises.push(
1798
+ index.getLatestNpmVersion("@biomejs/biome", "1.9.4").then((v) => {
1799
+ versions.biome = v;
1800
+ })
1801
+ );
1802
+ }
1803
+ const formatter = generateOptions.formatter ?? "oxfmt";
1804
+ if (formatter === "prettier") {
1805
+ versionPromises.push(
1806
+ index.getLatestNpmVersion("prettier", "3.4.2").then((v) => {
1807
+ versions.prettier = v;
1808
+ })
1809
+ );
1810
+ } else if (formatter === "oxfmt") {
1811
+ versionPromises.push(
1812
+ index.getLatestNpmVersion("oxfmt", "0.1.0").then((v) => {
1813
+ versions.oxfmt = v;
1814
+ })
1815
+ );
1816
+ } else if (formatter === "biome" && linter !== "biome") {
1817
+ versionPromises.push(
1818
+ index.getLatestNpmVersion("@biomejs/biome", "1.9.4").then((v) => {
1819
+ versions.biome = v;
1820
+ })
1821
+ );
1822
+ }
1823
+ await Promise.all(versionPromises);
1824
+ generateOptions.versions = versions;
1825
+ const projectPath = path.join(process$1.cwd(), generateOptions.name);
1826
+ const spinner = p__namespace.spinner();
1827
+ spinner.start("Creating project...");
1828
+ try {
1829
+ const files = index.generate(generateOptions);
1830
+ await writeGeneratedFiles(projectPath, files);
1831
+ spinner.stop(color__default.green.inverse(" \u2713 Project created! "));
1832
+ const nextSteps = isLibrary ? [
1833
+ `cd ${generateOptions.name}`,
1834
+ `${packageManager} install`,
1835
+ `${packageManager} run build`
1836
+ ].join("\n") : [
1837
+ `cd ${generateOptions.name}`,
1838
+ `${packageManager} install`,
1839
+ `${packageManager} run dev`
1840
+ ].join("\n");
1841
+ p__namespace.note(nextSteps, "Next steps");
1842
+ await promptAndOpenEditor(projectPath);
1843
+ p__namespace.outro(color__default.green("Happy coding! \u2728"));
1844
+ } catch (error) {
1845
+ spinner.stop("Failed to create project");
1846
+ p__namespace.log.error(String(error));
1847
+ process.exit(1);
1848
+ }
1849
+ }
1850
+ async function handleInteractiveMonorepoMode(monorepoRoot) {
1851
+ const choice = await p__namespace.select({
1852
+ message: "Detected monorepo workspace",
1853
+ options: [
1854
+ { value: "add", label: "Add new package to this workspace" },
1855
+ { value: "standalone", label: "Create standalone project" }
1856
+ ],
1857
+ initialValue: "add"
1858
+ });
1859
+ if (p__namespace.isCancel(choice)) {
1860
+ p__namespace.cancel("Operation cancelled.");
1861
+ process.exit(0);
1862
+ }
1863
+ if (choice === "add") {
1864
+ const inheritedTooling = await detectWorkspaceTooling(monorepoRoot);
1865
+ if (inheritedTooling.linter || inheritedTooling.formatter) {
1866
+ const toolingInfo = [
1867
+ inheritedTooling.linter && `linter: ${inheritedTooling.linter}`,
1868
+ inheritedTooling.formatter && `formatter: ${inheritedTooling.formatter}`
1869
+ ].filter(Boolean).join(", ");
1870
+ p__namespace.log.info(`Using workspace tooling (${toolingInfo})`);
1871
+ }
1872
+ const scope = await getMonorepoScope(monorepoRoot);
1873
+ let addMore = true;
1874
+ while (addMore) {
1875
+ addMore = await createPackageInWorkspace(
1876
+ monorepoRoot,
1877
+ "pnpm",
1878
+ inheritedTooling,
1879
+ scope
1880
+ );
1881
+ }
1882
+ p__namespace.note(
1883
+ [`cd ${monorepoRoot}`, "pnpm install", "pnpm run dev"].join("\n"),
1884
+ "Next steps"
1885
+ );
1886
+ await promptAndOpenEditor(monorepoRoot);
1887
+ p__namespace.outro(color__default.green("Happy coding! \u2728"));
1888
+ process.exit(0);
1889
+ }
1890
+ }
960
1891
  async function main() {
961
- const program = new commander.Command().name("create-krispya").description("CLI for creating Vanilla, React, and React Three Fiber projects").argument("[name]", "name for the project").option("--type <type>", "project type: app or library (default: app)").option(
1892
+ const program = new commander.Command().name("create-krispya").description(
1893
+ "CLI for creating Vanilla, React, and React Three Fiber projects"
1894
+ ).argument("[name]", "name for the project").option("--type <type>", "project type: app or library (default: app)").option(
962
1895
  "--bundler <bundler>",
963
1896
  "library bundler: unbuild or tsdown (default: unbuild, only for libraries)"
964
1897
  ).option(
965
1898
  "--template <type>",
966
1899
  "project template: vanilla, vanilla-js, react, react-js, r3f, r3f-js (default: vanilla)"
967
- ).option("--linter <type>", "linter: eslint, oxlint, or biome (default: oxlint)").option("--formatter <type>", "formatter: prettier, oxfmt, or biome (default: oxfmt)").option("--drei", "add @react-three/drei (r3f only)").option("--handle", "add @react-three/handle (r3f only)").option("--leva", "add leva (r3f only)").option("--postprocessing", "add @react-three/postprocessing (r3f only)").option("--rapier", "add @react-three/rapier (r3f only)").option("--xr", "add @react-three/xr (r3f only)").option("--uikit", "add @react-three/uikit (r3f only)").option("--offscreen", "add @react-three/offscreen (r3f only)").option("--zustand", "add zustand (r3f only)").option("--koota", "add koota (r3f only)").option("--triplex", "set up triplex development environment (r3f only)").option("--viverse", "set up viverse deployment (r3f only)").option("--package-manager <manager>", "specify package manager (e.g. npm, yarn, pnpm)").option(
1900
+ ).option(
1901
+ "--linter <type>",
1902
+ "linter: eslint, oxlint, or biome (default: oxlint)"
1903
+ ).option(
1904
+ "--formatter <type>",
1905
+ "formatter: prettier, oxfmt, or biome (default: oxfmt)"
1906
+ ).option("--drei", "add @react-three/drei (r3f only)").option("--handle", "add @react-three/handle (r3f only)").option("--leva", "add leva (r3f only)").option("--postprocessing", "add @react-three/postprocessing (r3f only)").option("--rapier", "add @react-three/rapier (r3f only)").option("--xr", "add @react-three/xr (r3f only)").option("--uikit", "add @react-three/uikit (r3f only)").option("--offscreen", "add @react-three/offscreen (r3f only)").option("--zustand", "add zustand (r3f only)").option("--koota", "add koota (r3f only)").option("--triplex", "set up triplex development environment (r3f only)").option("--viverse", "set up viverse deployment (r3f only)").option(
1907
+ "--package-manager <manager>",
1908
+ "specify package manager (e.g. npm, yarn, pnpm)"
1909
+ ).option(
968
1910
  "--pnpm-manage-versions",
969
1911
  "enable manage-package-manager-versions in pnpm-workspace.yaml (default: true)"
970
1912
  ).option(
@@ -973,7 +1915,22 @@ async function main() {
973
1915
  ).option(
974
1916
  "--node-version <version>",
975
1917
  'set Node.js version for engines.node field (default: "latest")'
976
- ).option("-y, --yes", "Skip prompts and use default values").option("--clear-config", "Clear saved preferences (e.g. editor choice)").option("--config-path", "Print the path to the config file").action(async (name, options) => {
1918
+ ).option(
1919
+ "--workspace",
1920
+ "Add package to current monorepo workspace (non-interactive)"
1921
+ ).option(
1922
+ "--dir <directory>",
1923
+ "Target directory for --workspace (default: apps/ or packages/)"
1924
+ ).option("--clear-config", "Clear saved preferences (e.g. editor choice)").option("--config-path", "Print the path to the config file").option(
1925
+ "--check",
1926
+ "Check if current directory is in a valid monorepo workspace"
1927
+ ).option("--fix", "Fix monorepo by generating missing .config packages").option(
1928
+ "--path <directory>",
1929
+ "Run in specified directory instead of current working directory"
1930
+ ).action(async (name, options) => {
1931
+ if (options.path) {
1932
+ process.chdir(options.path);
1933
+ }
977
1934
  if (options.clearConfig) {
978
1935
  clearConfig();
979
1936
  console.log("Configuration cleared.");
@@ -983,44 +1940,57 @@ async function main() {
983
1940
  console.log(getConfigPath());
984
1941
  process.exit(0);
985
1942
  }
1943
+ if (name?.startsWith("-")) {
1944
+ switch (name) {
1945
+ case "--version":
1946
+ case "-V":
1947
+ console.log(pkg.version);
1948
+ process.exit(0);
1949
+ case "--help":
1950
+ case "-h":
1951
+ program.help();
1952
+ break;
1953
+ case "--clear-config":
1954
+ clearConfig();
1955
+ console.log("Configuration cleared.");
1956
+ process.exit(0);
1957
+ case "--config-path":
1958
+ console.log(getConfigPath());
1959
+ process.exit(0);
1960
+ case "--check":
1961
+ await handleCheckCommand();
1962
+ break;
1963
+ case "--fix":
1964
+ options.fix = true;
1965
+ break;
1966
+ default:
1967
+ console.error(color__default.red(`Unknown option: ${name}`));
1968
+ process.exit(1);
1969
+ }
1970
+ }
1971
+ if (options.check) {
1972
+ await handleCheckCommand();
1973
+ }
1974
+ if (options.fix) {
1975
+ await handleFixCommand(options);
1976
+ }
1977
+ if (options.dir && !options.workspace) {
1978
+ console.error(color__default.red("Error:") + " --dir requires --workspace flag");
1979
+ console.log(
1980
+ color__default.dim(
1981
+ " Example: pnpm create krispya my-lib --workspace --dir examples"
1982
+ )
1983
+ );
1984
+ process.exit(1);
1985
+ }
1986
+ if (options.workspace) {
1987
+ await handleWorkspaceCommand(name, options);
1988
+ }
986
1989
  console.clear();
987
1990
  p__namespace.intro(color__default.bgCyan(color__default.black(` create-krispya v${pkg.version} `)));
988
1991
  const monorepoRoot = await detectMonorepoRoot();
989
1992
  if (monorepoRoot && Object.keys(options).length === 0) {
990
- const choice = await p__namespace.select({
991
- message: "Detected monorepo workspace",
992
- options: [
993
- { value: "add", label: "Add new package to this workspace" },
994
- { value: "standalone", label: "Create standalone project" }
995
- ],
996
- initialValue: "add"
997
- });
998
- if (p__namespace.isCancel(choice)) {
999
- p__namespace.cancel("Operation cancelled.");
1000
- process.exit(0);
1001
- }
1002
- if (choice === "add") {
1003
- const inheritedTooling = await detectWorkspaceTooling(monorepoRoot);
1004
- if (inheritedTooling.linter || inheritedTooling.formatter) {
1005
- const toolingInfo = [
1006
- inheritedTooling.linter && `linter: ${inheritedTooling.linter}`,
1007
- inheritedTooling.formatter && `formatter: ${inheritedTooling.formatter}`
1008
- ].filter(Boolean).join(", ");
1009
- p__namespace.log.info(`Using workspace tooling (${toolingInfo})`);
1010
- }
1011
- const scope = await getMonorepoScope(monorepoRoot);
1012
- let addMore = true;
1013
- while (addMore) {
1014
- addMore = await createPackageInWorkspace(monorepoRoot, "pnpm", inheritedTooling, scope);
1015
- }
1016
- p__namespace.note(
1017
- [`cd ${monorepoRoot}`, "pnpm install", "pnpm run dev"].join("\n"),
1018
- "Next steps"
1019
- );
1020
- await promptAndOpenEditor(monorepoRoot);
1021
- p__namespace.outro(color__default.green("Happy coding! \u2728"));
1022
- process.exit(0);
1023
- }
1993
+ await handleInteractiveMonorepoMode(monorepoRoot);
1024
1994
  }
1025
1995
  let generateOptions;
1026
1996
  if (Object.keys(options).length > 0) {
@@ -1057,226 +2027,9 @@ async function main() {
1057
2027
  generateOptions = await promptForOptions(name);
1058
2028
  }
1059
2029
  if (generateOptions.projectType === "monorepo") {
1060
- const { generateMonorepo } = await import('./chunks/index.cjs').then(function (n) { return n.monorepo; });
1061
- const packageManager2 = generateOptions.packageManager || "pnpm";
1062
- if (packageManager2 === "pnpm") {
1063
- generateOptions.pnpmVersion = await index.getLatestPnpmVersion();
1064
- }
1065
- const nodeVersion2 = generateOptions.nodeVersion ?? "latest";
1066
- if (nodeVersion2 === "latest") {
1067
- generateOptions.nodeVersion = await index.getLatestNodeVersion();
1068
- }
1069
- const basePath2 = path.join(process$1.cwd(), generateOptions.name);
1070
- const s2 = p__namespace.spinner();
1071
- s2.start("Creating monorepo workspace...");
1072
- try {
1073
- const { files } = generateMonorepo({
1074
- name: generateOptions.name,
1075
- linter: generateOptions.linter ?? "oxlint",
1076
- formatter: generateOptions.formatter ?? "oxfmt",
1077
- packageManager: packageManager2,
1078
- pnpmVersion: generateOptions.pnpmVersion,
1079
- pnpmManageVersions: generateOptions.pnpmManageVersions,
1080
- nodeVersion: generateOptions.nodeVersion
1081
- });
1082
- const filePaths = Object.keys(files).sort();
1083
- for (const filePath of filePaths) {
1084
- const fullFilePath = path.join(basePath2, filePath);
1085
- await promises.mkdir(path.dirname(fullFilePath), { recursive: true });
1086
- const file = files[filePath];
1087
- if (file.type === "text") {
1088
- await promises.writeFile(fullFilePath, file.content);
1089
- }
1090
- }
1091
- s2.stop(color__default.green.inverse(" \u2713 Monorepo workspace created! "));
1092
- const newMonorepoTooling = {
1093
- linter: generateOptions.linter,
1094
- formatter: generateOptions.formatter
1095
- };
1096
- const scope = generateOptions.name;
1097
- let addMore = true;
1098
- while (addMore) {
1099
- addMore = await createPackageInWorkspace(basePath2, packageManager2, newMonorepoTooling, scope);
1100
- }
1101
- const nextSteps = [
1102
- `cd ${generateOptions.name}`,
1103
- `${packageManager2} install`,
1104
- `${packageManager2} run dev`
1105
- ].join("\n");
1106
- p__namespace.note(nextSteps, "Next steps");
1107
- await promptAndOpenEditor(basePath2);
1108
- p__namespace.outro(color__default.green("Happy coding! \u2728"));
1109
- process.exit(0);
1110
- } catch (error) {
1111
- s2.stop("Failed to create monorepo workspace");
1112
- p__namespace.log.error(String(error));
1113
- process.exit(1);
1114
- }
1115
- }
1116
- const base = generateOptions.template ? index.getBaseTemplate(generateOptions.template) : "vanilla";
1117
- const defaultFallbackName = base === "vanilla" ? "vanilla-app" : base === "react" ? "react-app" : "react-three-app";
1118
- generateOptions.name ??= defaultFallbackName;
1119
- const packageManager = generateOptions.packageManager || "pnpm";
1120
- if (packageManager === "pnpm") {
1121
- generateOptions.pnpmVersion = await index.getLatestPnpmVersion();
1122
- }
1123
- const nodeVersion = generateOptions.nodeVersion ?? "latest";
1124
- if (nodeVersion === "latest") {
1125
- generateOptions.nodeVersion = await index.getLatestNodeVersion();
1126
- }
1127
- const versions = {};
1128
- const versionPromises = [];
1129
- const isLibrary = generateOptions.projectType === "library";
1130
- const testing = generateOptions.testing ?? (isLibrary ? "vitest" : "none");
1131
- if (testing === "vitest") {
1132
- versionPromises.push(
1133
- index.getLatestNpmVersion("vitest", "4.0.0").then((v) => {
1134
- versions.vitest = v;
1135
- })
1136
- );
1137
- }
1138
- if (!isLibrary) {
1139
- versionPromises.push(
1140
- index.getLatestNpmVersion("vite", "6.3.4").then((v) => {
1141
- versions.vite = v;
1142
- })
1143
- );
1144
- }
1145
- const linter = generateOptions.linter ?? "oxlint";
1146
- if (linter === "eslint") {
1147
- versionPromises.push(
1148
- index.getLatestNpmVersion("eslint", "9.17.0").then((v) => {
1149
- versions.eslint = v;
1150
- })
1151
- );
1152
- } else if (linter === "oxlint") {
1153
- versionPromises.push(
1154
- index.getLatestNpmVersion("oxlint", "0.16.0").then((v) => {
1155
- versions.oxlint = v;
1156
- })
1157
- );
1158
- } else if (linter === "biome") {
1159
- versionPromises.push(
1160
- index.getLatestNpmVersion("@biomejs/biome", "1.9.4").then((v) => {
1161
- versions.biome = v;
1162
- })
1163
- );
1164
- }
1165
- const formatter = generateOptions.formatter ?? "oxfmt";
1166
- if (formatter === "prettier") {
1167
- versionPromises.push(
1168
- index.getLatestNpmVersion("prettier", "3.4.2").then((v) => {
1169
- versions.prettier = v;
1170
- })
1171
- );
1172
- } else if (formatter === "oxfmt") {
1173
- versionPromises.push(
1174
- index.getLatestNpmVersion("oxfmt", "0.1.0").then((v) => {
1175
- versions.oxfmt = v;
1176
- })
1177
- );
1178
- } else if (formatter === "biome" && linter !== "biome") {
1179
- versionPromises.push(
1180
- index.getLatestNpmVersion("@biomejs/biome", "1.9.4").then((v) => {
1181
- versions.biome = v;
1182
- })
1183
- );
1184
- }
1185
- await Promise.all(versionPromises);
1186
- generateOptions.versions = versions;
1187
- const basePath = path.join(process$1.cwd(), generateOptions.name);
1188
- const s = p__namespace.spinner();
1189
- s.start("Creating project...");
1190
- try {
1191
- const files = index.generate(generateOptions);
1192
- const filePaths = Object.keys(files).sort();
1193
- for (const filePath of filePaths) {
1194
- const fullFilePath = path.join(basePath, filePath);
1195
- await promises.mkdir(path.dirname(fullFilePath), { recursive: true });
1196
- const file = files[filePath];
1197
- if (file.type === "text") {
1198
- await promises.writeFile(fullFilePath, file.content);
1199
- } else {
1200
- const response = await undici.fetch(file.url);
1201
- await promises.writeFile(fullFilePath, response.body);
1202
- }
1203
- }
1204
- s.stop(color__default.green.inverse(" \u2713 Project created! "));
1205
- const isLibrary2 = generateOptions.projectType === "library";
1206
- const nextSteps = isLibrary2 ? [
1207
- `cd ${generateOptions.name}`,
1208
- `${packageManager} install`,
1209
- `${packageManager} run build`
1210
- ].join("\n") : [
1211
- `cd ${generateOptions.name}`,
1212
- `${packageManager} install`,
1213
- `${packageManager} run dev`
1214
- ].join("\n");
1215
- p__namespace.note(nextSteps, "Next steps");
1216
- const savedEditor = getPreferredEditor();
1217
- let selectedEditor;
1218
- if (savedEditor && savedEditor !== "skip") {
1219
- const useDefault = await p__namespace.confirm({
1220
- message: `Open in editor? ${color__default.dim(`(${editorNames[savedEditor]})`)}`,
1221
- initialValue: true
1222
- });
1223
- if (p__namespace.isCancel(useDefault)) {
1224
- selectedEditor = void 0;
1225
- } else if (useDefault) {
1226
- selectedEditor = savedEditor;
1227
- } else {
1228
- selectedEditor = "skip";
1229
- }
1230
- } else {
1231
- const openEditor = await p__namespace.select({
1232
- message: "Open project in editor?",
1233
- options: [
1234
- { value: "skip", label: "Skip" },
1235
- { value: "cursor", label: "Cursor" },
1236
- { value: "code", label: "VS Code" },
1237
- { value: "webstorm", label: "WebStorm" }
1238
- ],
1239
- initialValue: "skip"
1240
- });
1241
- if (!p__namespace.isCancel(openEditor)) {
1242
- selectedEditor = openEditor;
1243
- const saveChoice = await p__namespace.confirm({
1244
- message: `Save ${editorNames[selectedEditor] ?? "Skip"} as default editor?`,
1245
- initialValue: true
1246
- });
1247
- if (!p__namespace.isCancel(saveChoice) && saveChoice) {
1248
- setPreferredEditor(selectedEditor);
1249
- if (selectedEditor === "cursor" || selectedEditor === "code") {
1250
- const reuseChoice = await p__namespace.confirm({
1251
- message: "Reuse current window when opening projects?",
1252
- initialValue: false
1253
- });
1254
- if (!p__namespace.isCancel(reuseChoice)) {
1255
- setReuseWindow(reuseChoice);
1256
- }
1257
- }
1258
- }
1259
- }
1260
- }
1261
- if (selectedEditor && selectedEditor !== "skip") {
1262
- try {
1263
- await openInEditor(
1264
- selectedEditor,
1265
- basePath,
1266
- getReuseWindow()
1267
- );
1268
- p__namespace.log.success(`Opening in ${editorNames[selectedEditor]}...`);
1269
- } catch {
1270
- p__namespace.log.warn(
1271
- `Could not open ${editorNames[selectedEditor]}. Make sure the CLI command is in your PATH.`
1272
- );
1273
- }
1274
- }
1275
- p__namespace.outro(color__default.green("Happy coding! \u2728"));
1276
- } catch (error) {
1277
- s.stop("Failed to create project");
1278
- p__namespace.log.error(String(error));
1279
- process.exit(1);
2030
+ await handleMonorepoCreation(generateOptions);
2031
+ } else {
2032
+ await handleStandaloneProjectCreation(generateOptions);
1280
2033
  }
1281
2034
  });
1282
2035
  await program.parseAsync();