create-krispya 0.15.0 → 0.17.0

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.
@@ -277,6 +277,7 @@ async function detectTooling(root) {
277
277
 
278
278
  const DEFAULT_MINIMUM_RELEASE_AGE_MINUTES = 1440;
279
279
  const MINUTE_IN_MS = 60 * 1e3;
280
+ const SEMVER_PATTERN = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
280
281
  function getMinimumReleaseAgeMinutes(options) {
281
282
  return Math.max(0, options?.minimumReleaseAgeMinutes ?? DEFAULT_MINIMUM_RELEASE_AGE_MINUTES);
282
283
  }
@@ -292,6 +293,62 @@ function isVersionOldEnough(version, time, options) {
292
293
  function getInstallableVersions(versions, time, options) {
293
294
  return [...versions].filter((version) => isVersionOldEnough(version, time, options));
294
295
  }
296
+ function parseSemver(version) {
297
+ const match = version.match(SEMVER_PATTERN);
298
+ if (match == null) return void 0;
299
+ return {
300
+ major: Number.parseInt(match[1], 10),
301
+ minor: Number.parseInt(match[2], 10),
302
+ patch: Number.parseInt(match[3], 10),
303
+ prerelease: match[4]?.split(".") ?? []
304
+ };
305
+ }
306
+ function hasPrerelease(version) {
307
+ return (parseSemver(version)?.prerelease.length ?? 0) > 0;
308
+ }
309
+ function comparePrerelease(aParts, bParts) {
310
+ if (aParts.length === 0 && bParts.length === 0) return 0;
311
+ if (aParts.length === 0) return 1;
312
+ if (bParts.length === 0) return -1;
313
+ const maxLength = Math.max(aParts.length, bParts.length);
314
+ for (let index = 0; index < maxLength; index += 1) {
315
+ const aPart = aParts[index];
316
+ const bPart = bParts[index];
317
+ if (aPart == null) return -1;
318
+ if (bPart == null) return 1;
319
+ if (aPart === bPart) continue;
320
+ const aNumber = /^\d+$/.test(aPart) ? Number.parseInt(aPart, 10) : void 0;
321
+ const bNumber = /^\d+$/.test(bPart) ? Number.parseInt(bPart, 10) : void 0;
322
+ if (aNumber != null && bNumber != null) return aNumber - bNumber;
323
+ if (aNumber != null) return -1;
324
+ if (bNumber != null) return 1;
325
+ return aPart.localeCompare(bPart);
326
+ }
327
+ return 0;
328
+ }
329
+ function compareSemver(a, b) {
330
+ const aParsed = parseSemver(a);
331
+ const bParsed = parseSemver(b);
332
+ if (aParsed == null || bParsed == null) {
333
+ return compareNumericSemver(a, b);
334
+ }
335
+ const majorDifference = aParsed.major - bParsed.major;
336
+ if (majorDifference !== 0) return majorDifference;
337
+ const minorDifference = aParsed.minor - bParsed.minor;
338
+ if (minorDifference !== 0) return minorDifference;
339
+ const patchDifference = aParsed.patch - bParsed.patch;
340
+ if (patchDifference !== 0) return patchDifference;
341
+ return comparePrerelease(aParsed.prerelease, bParsed.prerelease);
342
+ }
343
+ function getLatestChannelVersions(versions, latestVersion) {
344
+ const availableVersions = [...versions];
345
+ if (latestVersion == null) return availableVersions.filter((version) => !hasPrerelease(version));
346
+ const latestIsPrerelease = hasPrerelease(latestVersion);
347
+ return availableVersions.filter((version) => {
348
+ if (!latestIsPrerelease && hasPrerelease(version)) return false;
349
+ return compareSemver(version, latestVersion) <= 0;
350
+ });
351
+ }
295
352
  async function fetchNpmPackageMetadata(packageName) {
296
353
  const response = await fetch(`https://registry.npmjs.org/${packageName}`);
297
354
  return await response.json();
@@ -309,10 +366,10 @@ async function getLatestNpmVersion(packageName, fallback, options) {
309
366
  return latestVersion;
310
367
  }
311
368
  const latestInstallableVersion = getInstallableVersions(
312
- Object.keys(data.versions ?? {}),
369
+ getLatestChannelVersions(Object.keys(data.versions ?? {}), latestVersion),
313
370
  data.time,
314
371
  options
315
- ).sort((a, b) => compareNumericSemver(b, a))[0];
372
+ ).sort((a, b) => compareSemver(b, a))[0];
316
373
  return latestInstallableVersion ?? fallback;
317
374
  } catch {
318
375
  return fallback;
@@ -339,7 +396,7 @@ function compareNumericSemver(a, b) {
339
396
  return 0;
340
397
  }
341
398
  function getLatestMatchingMajorVersion(versions, majorVersion, time, options) {
342
- return getInstallableVersions(versions, time, options).filter((version) => version.split(".")[0] === majorVersion).sort((a, b) => compareNumericSemver(b, a))[0];
399
+ return getInstallableVersions(versions, time, options).filter((version) => version.split(".")[0] === majorVersion).filter((version) => !hasPrerelease(version)).sort((a, b) => compareSemver(b, a))[0];
343
400
  }
344
401
  async function getLatestNpmMajorVersion(packageName, majorVersion, fallback, options) {
345
402
  try {
@@ -468,6 +525,9 @@ function getPnpmProfile(spec) {
468
525
  requirements: getPnpmRequirements(major)
469
526
  };
470
527
  }
528
+ function formatPnpmWorkspaceYamlKey(key) {
529
+ return key.startsWith("@") ? JSON.stringify(key) : key;
530
+ }
471
531
  function renderPnpmWorkspaceConfig(options) {
472
532
  const {
473
533
  profile,
@@ -489,7 +549,7 @@ function renderPnpmWorkspaceConfig(options) {
489
549
  if (profile.capabilities.pnpmBuildDependencyPolicy === "allowBuilds") {
490
550
  lines.push("allowBuilds:");
491
551
  for (const [dependency, allowed] of Object.entries(buildDependencies)) {
492
- lines.push(` ${dependency}: ${allowed ? "true" : "false"}`);
552
+ lines.push(` ${formatPnpmWorkspaceYamlKey(dependency)}: ${allowed ? "true" : "false"}`);
493
553
  }
494
554
  } else {
495
555
  const allowedDependencies = Object.entries(buildDependencies).filter(([, allowed]) => allowed).map(([dependency]) => dependency);
@@ -614,6 +674,8 @@ function getConfigPath() {
614
674
  }
615
675
 
616
676
  const ALL_AI_PLATFORMS = ["agents", "claude"];
677
+ const AI_FILE_MANAGED_BEGIN = "<!-- managed:start -->";
678
+ const AI_FILE_MANAGED_END = "<!-- managed:end -->";
617
679
  const AI_PLATFORM_LABELS = {
618
680
  agents: "AGENTS.md",
619
681
  claude: "CLAUDE.md"
@@ -625,12 +687,14 @@ const AI_PLATFORM_HINTS = {
625
687
  function renderAiFiles(files, params) {
626
688
  const { platforms, isMonorepo, configStrategy, hasTypecheck, ...rest } = params;
627
689
  if (platforms.length === 0) return;
628
- const content = generateWorkspace({
629
- ...rest,
630
- isMonorepo: !!isMonorepo,
631
- configStrategy: configStrategy ?? "stealth",
632
- hasTypecheck: hasTypecheck ?? false
633
- });
690
+ const content = renderManagedAiFileContent(
691
+ generateWorkspace({
692
+ ...rest,
693
+ isMonorepo: !!isMonorepo,
694
+ configStrategy: configStrategy ?? "stealth",
695
+ hasTypecheck: hasTypecheck ?? false
696
+ })
697
+ );
634
698
  const pointer = "See [`AGENTS.md`](./Agents.md) for agent context.\n";
635
699
  const hasAgents = platforms.includes("agents");
636
700
  const hasClaude = platforms.includes("claude");
@@ -644,18 +708,89 @@ function renderAiFiles(files, params) {
644
708
  }
645
709
  }
646
710
  }
711
+ function renderManagedAiFileContent(content) {
712
+ return [AI_FILE_MANAGED_BEGIN, content.trimEnd(), AI_FILE_MANAGED_END, ""].join("\n");
713
+ }
714
+ function getManagedBlockRange$1(content) {
715
+ const beginIndex = content.indexOf(AI_FILE_MANAGED_BEGIN);
716
+ const endIndex = content.indexOf(AI_FILE_MANAGED_END);
717
+ if (beginIndex === -1 && endIndex === -1) {
718
+ return { status: "missing" };
719
+ }
720
+ if (beginIndex === -1 || endIndex === -1 || endIndex < beginIndex) {
721
+ return { status: "conflicted" };
722
+ }
723
+ return {
724
+ status: "found",
725
+ beginIndex,
726
+ endIndex: endIndex + AI_FILE_MANAGED_END.length
727
+ };
728
+ }
729
+ function getManagedInnerContent(content) {
730
+ const range = getManagedBlockRange$1(content);
731
+ if (range.status !== "found") return void 0;
732
+ return content.slice(
733
+ range.beginIndex + AI_FILE_MANAGED_BEGIN.length,
734
+ range.endIndex - AI_FILE_MANAGED_END.length
735
+ ).replace(/^\r?\n/, "").replace(/\r?\n$/, "");
736
+ }
737
+ function mergeMissingManagedAiBlock(currentContent, expectedContent, legacyContent) {
738
+ if (currentContent.trim() === "") return expectedContent;
739
+ if (legacyContent != null && currentContent.startsWith(legacyContent)) {
740
+ const suffix = currentContent.slice(legacyContent.length);
741
+ return suffix === "" ? expectedContent : `${expectedContent.trimEnd()}${suffix}`;
742
+ }
743
+ return `${expectedContent.trimEnd()}
744
+
745
+ ${currentContent.trim()}
746
+ `;
747
+ }
748
+ function isManagedAiFilePath(path) {
749
+ return path === "AGENTS.md" || path === "CLAUDE.md";
750
+ }
751
+ function mergeAiFileContent(currentContent, expectedContent) {
752
+ if (!expectedContent.includes(AI_FILE_MANAGED_BEGIN)) {
753
+ return {
754
+ content: expectedContent,
755
+ mergeSafe: false
756
+ };
757
+ }
758
+ const range = getManagedBlockRange$1(currentContent);
759
+ if (range.status === "found") {
760
+ return {
761
+ content: `${currentContent.slice(0, range.beginIndex)}${expectedContent.trimEnd()}${currentContent.slice(
762
+ range.endIndex
763
+ )}`,
764
+ mergeSafe: true
765
+ };
766
+ }
767
+ if (range.status === "conflicted") {
768
+ return {
769
+ content: expectedContent,
770
+ mergeSafe: false
771
+ };
772
+ }
773
+ return {
774
+ content: mergeMissingManagedAiBlock(
775
+ currentContent,
776
+ expectedContent,
777
+ getManagedInnerContent(expectedContent)
778
+ ),
779
+ mergeSafe: true
780
+ };
781
+ }
647
782
  function generateWorkspace(ctx) {
648
783
  const { packageManager, linter, formatter, hasTypecheck } = ctx;
649
784
  const exampleFiles = "src/App.tsx src/core/systems/move-entity.ts";
650
785
  const commands = getAfterEditingCommands(ctx, exampleFiles);
651
786
  const sections = [
652
- "# Workspace Tools",
787
+ "## Workspace Tools",
653
788
  "",
654
789
  `- **Package Manager:** ${packageManager}`,
655
790
  `- **Linter:** ${linter}`,
656
791
  `- **Formatter:** ${formatter}`,
657
792
  "",
658
- "## After Editing",
793
+ "### After Editing",
659
794
  ""
660
795
  ];
661
796
  if (hasTypecheck) {
@@ -1111,6 +1246,54 @@ function isEnabledOption(option) {
1111
1246
  return option != null && option !== false;
1112
1247
  }
1113
1248
 
1249
+ function renderJson(value, options = {}) {
1250
+ const json = JSON.stringify(value, null, 2);
1251
+ const content = options.inlineArrays === false ? json : inlinePrimitiveArrays(json, options);
1252
+ return `${content}
1253
+ `;
1254
+ }
1255
+ function inlinePrimitiveArrays(json, options) {
1256
+ const printWidth = options.printWidth ?? 102;
1257
+ const lines = json.split("\n");
1258
+ const output = [];
1259
+ for (let index = 0; index < lines.length; index++) {
1260
+ const line = lines[index];
1261
+ const openIndex = line.indexOf("[");
1262
+ if (openIndex === -1 || line.slice(openIndex).trim() !== "[") {
1263
+ output.push(line);
1264
+ continue;
1265
+ }
1266
+ const items = [];
1267
+ let cursor = index + 1;
1268
+ let closingComma = "";
1269
+ for (; cursor < lines.length; cursor++) {
1270
+ const item = lines[cursor].trim();
1271
+ const closingMatch = item.match(/^\](,?)$/);
1272
+ if (closingMatch) {
1273
+ closingComma = closingMatch[1] ?? "";
1274
+ break;
1275
+ }
1276
+ if (!/^(?:"(?:[^"\\]|\\.)*"|-?\d+(?:\.\d+)?|true|false|null),?$/.test(item)) {
1277
+ items.length = 0;
1278
+ break;
1279
+ }
1280
+ items.push(item.replace(/,$/, ""));
1281
+ }
1282
+ if (items.length === 0 || cursor >= lines.length) {
1283
+ output.push(line);
1284
+ continue;
1285
+ }
1286
+ const nextLine = `${line.slice(0, openIndex)}[${items.join(", ")}]${closingComma}`;
1287
+ if (nextLine.length > printWidth) {
1288
+ output.push(line);
1289
+ continue;
1290
+ }
1291
+ output.push(nextLine);
1292
+ index = cursor;
1293
+ }
1294
+ return output.join("\n");
1295
+ }
1296
+
1114
1297
  function renderTypescriptConfig(baseTemplateOrParams) {
1115
1298
  const params = typeof baseTemplateOrParams === "string" ? { baseTemplate: baseTemplateOrParams } : baseTemplateOrParams;
1116
1299
  const {
@@ -1142,15 +1325,11 @@ function renderTypescriptConfig(baseTemplateOrParams) {
1142
1325
  const baseConfig = isReact || isR3f ? "@config/typescript/react.json" : "@config/typescript/app.json";
1143
1326
  files["tsconfig.json"] = {
1144
1327
  type: "text",
1145
- content: JSON.stringify(
1146
- {
1147
- $schema: "https://json.schemastore.org/tsconfig",
1148
- extends: baseConfig,
1149
- include: ["src/**/*", "tests/**/*"]
1150
- },
1151
- null,
1152
- 2
1153
- )
1328
+ content: renderJson({
1329
+ $schema: "https://json.schemastore.org/tsconfig",
1330
+ extends: baseConfig,
1331
+ include: ["src/**/*", "tests/**/*"]
1332
+ })
1154
1333
  };
1155
1334
  return { files, devDependencies };
1156
1335
  }
@@ -1162,7 +1341,7 @@ function renderTypescriptConfig(baseTemplateOrParams) {
1162
1341
  };
1163
1342
  files["tsconfig.json"] = {
1164
1343
  type: "text",
1165
- content: JSON.stringify(tsConfig, null, 2)
1344
+ content: renderJson(tsConfig)
1166
1345
  };
1167
1346
  const tsConfigApp = {
1168
1347
  $schema: "https://json.schemastore.org/tsconfig",
@@ -1185,7 +1364,7 @@ function renderTypescriptConfig(baseTemplateOrParams) {
1185
1364
  };
1186
1365
  files[".config/tsconfig.app.json"] = {
1187
1366
  type: "text",
1188
- content: JSON.stringify(tsConfigApp, null, 2)
1367
+ content: renderJson(tsConfigApp)
1189
1368
  };
1190
1369
  const tsConfigNode = {
1191
1370
  $schema: "https://json.schemastore.org/tsconfig",
@@ -1207,7 +1386,7 @@ function renderTypescriptConfig(baseTemplateOrParams) {
1207
1386
  };
1208
1387
  files[".config/tsconfig.node.json"] = {
1209
1388
  type: "text",
1210
- content: JSON.stringify(tsConfigNode, null, 2)
1389
+ content: renderJson(tsConfigNode)
1211
1390
  };
1212
1391
  } else {
1213
1392
  const tsConfig = {
@@ -1217,7 +1396,7 @@ function renderTypescriptConfig(baseTemplateOrParams) {
1217
1396
  };
1218
1397
  files["tsconfig.json"] = {
1219
1398
  type: "text",
1220
- content: JSON.stringify(tsConfig, null, 2)
1399
+ content: renderJson(tsConfig)
1221
1400
  };
1222
1401
  const tsConfigApp = {
1223
1402
  $schema: "https://json.schemastore.org/tsconfig",
@@ -1240,7 +1419,7 @@ function renderTypescriptConfig(baseTemplateOrParams) {
1240
1419
  };
1241
1420
  files["tsconfig.app.json"] = {
1242
1421
  type: "text",
1243
- content: JSON.stringify(tsConfigApp, null, 2)
1422
+ content: renderJson(tsConfigApp)
1244
1423
  };
1245
1424
  const tsConfigNode = {
1246
1425
  $schema: "https://json.schemastore.org/tsconfig",
@@ -1262,7 +1441,7 @@ function renderTypescriptConfig(baseTemplateOrParams) {
1262
1441
  };
1263
1442
  files["tsconfig.node.json"] = {
1264
1443
  type: "text",
1265
- content: JSON.stringify(tsConfigNode, null, 2)
1444
+ content: renderJson(tsConfigNode)
1266
1445
  };
1267
1446
  }
1268
1447
  return { files, devDependencies };
@@ -1450,7 +1629,7 @@ function renderPackageJson(params) {
1450
1629
  }
1451
1630
  files["package.json"] = {
1452
1631
  type: "text",
1453
- content: JSON.stringify(packageJson, null, 2)
1632
+ content: renderJson(packageJson, { inlineArrays: false })
1454
1633
  };
1455
1634
  if (isPnpm && !options.workspaceRoot) {
1456
1635
  files["pnpm-workspace.yaml"] = {
@@ -1822,6 +2001,8 @@ function renderTestFiles(params) {
1822
2001
  return files;
1823
2002
  }
1824
2003
 
2004
+ const GITIGNORE_MANAGED_BEGIN = "# managed:start";
2005
+ const GITIGNORE_MANAGED_END = "# managed:end";
1825
2006
  const COMMON_GITIGNORE_LINES = [
1826
2007
  "node_modules",
1827
2008
  "dist",
@@ -1831,13 +2012,77 @@ const COMMON_GITIGNORE_LINES = [
1831
2012
  "!.env.example",
1832
2013
  ".pnpm-store"
1833
2014
  ];
2015
+ function getCoreGitignoreLines(variant) {
2016
+ return variant === "workspace-root" ? [...COMMON_GITIGNORE_LINES, ".DS_Store"] : COMMON_GITIGNORE_LINES;
2017
+ }
2018
+ function renderManagedGitignoreBlock(variant) {
2019
+ return [GITIGNORE_MANAGED_BEGIN, ...getCoreGitignoreLines(variant), GITIGNORE_MANAGED_END].join(
2020
+ "\n"
2021
+ );
2022
+ }
1834
2023
  function renderGitignore(variant) {
1835
- const lines = variant === "workspace-root" ? [...COMMON_GITIGNORE_LINES, ".DS_Store"] : COMMON_GITIGNORE_LINES;
1836
2024
  return {
1837
2025
  type: "text",
1838
- content: lines.join("\n")
2026
+ content: renderManagedGitignoreBlock(variant)
1839
2027
  };
1840
2028
  }
2029
+ function getManagedBlockRange(content) {
2030
+ const beginIndex = content.indexOf(GITIGNORE_MANAGED_BEGIN);
2031
+ const endIndex = content.indexOf(GITIGNORE_MANAGED_END);
2032
+ if (beginIndex === -1 && endIndex === -1) {
2033
+ return { status: "missing" };
2034
+ }
2035
+ if (beginIndex === -1 || endIndex === -1 || endIndex < beginIndex) {
2036
+ return { status: "conflicted" };
2037
+ }
2038
+ return {
2039
+ status: "found",
2040
+ beginIndex,
2041
+ endIndex: endIndex + GITIGNORE_MANAGED_END.length
2042
+ };
2043
+ }
2044
+ function trimBlankEdges(lines) {
2045
+ let start = 0;
2046
+ let end = lines.length;
2047
+ while (start < end && lines[start]?.trim() === "") {
2048
+ start++;
2049
+ }
2050
+ while (end > start && lines[end - 1]?.trim() === "") {
2051
+ end--;
2052
+ }
2053
+ return lines.slice(start, end);
2054
+ }
2055
+ function mergeGitignoreContent(currentContent, variant) {
2056
+ const managedBlock = renderManagedGitignoreBlock(variant);
2057
+ const range = getManagedBlockRange(currentContent);
2058
+ if (range.status === "found") {
2059
+ return {
2060
+ content: `${currentContent.slice(0, range.beginIndex)}${managedBlock}${currentContent.slice(
2061
+ range.endIndex
2062
+ )}`,
2063
+ mergeSafe: true
2064
+ };
2065
+ }
2066
+ if (range.status === "conflicted") {
2067
+ return {
2068
+ content: managedBlock,
2069
+ mergeSafe: false
2070
+ };
2071
+ }
2072
+ const coreLines = new Set(getCoreGitignoreLines(variant));
2073
+ const customLines = trimBlankEdges(
2074
+ currentContent.split(/\r?\n/).filter((line) => !coreLines.has(line.trim()))
2075
+ );
2076
+ return {
2077
+ content: customLines.length > 0 ? `${managedBlock}
2078
+
2079
+ ${customLines.join("\n")}` : managedBlock,
2080
+ mergeSafe: true
2081
+ };
2082
+ }
2083
+ function detectGitignoreVariant(content) {
2084
+ return content.split(/\r?\n/).includes(".DS_Store") ? "workspace-root" : "standalone";
2085
+ }
1841
2086
 
1842
2087
  const defaultFormatterMetaConfig = {
1843
2088
  printWidth: 102,
@@ -1849,6 +2094,8 @@ const defaultFormatterMetaConfig = {
1849
2094
  bracketSpacing: true,
1850
2095
  arrowParens: "always",
1851
2096
  ignorePatterns: [
2097
+ "dist/",
2098
+ "**/dist/",
1852
2099
  "package-lock.json",
1853
2100
  "npm-shrinkwrap.json",
1854
2101
  "pnpm-lock.yaml",
@@ -2008,13 +2255,9 @@ function renderVscodeFiles$1(params) {
2008
2255
  const uniqueRecommendations = [...new Set(recommendations)];
2009
2256
  files[".vscode/extensions.json"] = {
2010
2257
  type: "text",
2011
- content: JSON.stringify(
2012
- {
2013
- recommendations: uniqueRecommendations
2014
- },
2015
- null,
2016
- 2
2017
- )
2258
+ content: renderJson({
2259
+ recommendations: uniqueRecommendations
2260
+ })
2018
2261
  };
2019
2262
  }
2020
2263
  const resolvedSettings = {
@@ -2027,7 +2270,7 @@ function renderVscodeFiles$1(params) {
2027
2270
  );
2028
2271
  files[".vscode/settings.json"] = {
2029
2272
  type: "text",
2030
- content: JSON.stringify(sortedSettings, null, 2)
2273
+ content: renderJson(sortedSettings)
2031
2274
  };
2032
2275
  }
2033
2276
  return files;
@@ -2202,15 +2445,14 @@ function renderTypescriptConfigPackage(files) {
2202
2445
  const basePath = ".config/typescript";
2203
2446
  files[`${basePath}/package.json`] = {
2204
2447
  type: "text",
2205
- content: JSON.stringify(
2448
+ content: renderJson(
2206
2449
  {
2207
2450
  name: "@config/typescript",
2208
2451
  version: "0.1.0",
2209
2452
  private: true,
2210
2453
  files: ["base.json", "app.json", "node.json", "react.json"]
2211
2454
  },
2212
- null,
2213
- 2
2455
+ { inlineArrays: false }
2214
2456
  )
2215
2457
  };
2216
2458
  files[`${basePath}/README.md`] = {
@@ -2240,82 +2482,65 @@ In your package's \`tsconfig.json\`:
2240
2482
  };
2241
2483
  files[`${basePath}/base.json`] = {
2242
2484
  type: "text",
2243
- content: JSON.stringify(
2244
- {
2245
- $schema: "https://json.schemastore.org/tsconfig",
2246
- compilerOptions: {
2247
- target: "ESNext",
2248
- module: "ESNext",
2249
- moduleResolution: "bundler",
2250
- esModuleInterop: true,
2251
- allowSyntheticDefaultImports: true,
2252
- strict: true,
2253
- skipLibCheck: true,
2254
- composite: true,
2255
- rewriteRelativeImportExtensions: true,
2256
- erasableSyntaxOnly: true
2257
- }
2258
- },
2259
- null,
2260
- 2
2261
- )
2485
+ content: renderJson({
2486
+ $schema: "https://json.schemastore.org/tsconfig",
2487
+ compilerOptions: {
2488
+ target: "ESNext",
2489
+ module: "ESNext",
2490
+ moduleResolution: "bundler",
2491
+ esModuleInterop: true,
2492
+ allowSyntheticDefaultImports: true,
2493
+ strict: true,
2494
+ skipLibCheck: true,
2495
+ composite: true,
2496
+ rewriteRelativeImportExtensions: true,
2497
+ erasableSyntaxOnly: true
2498
+ }
2499
+ })
2262
2500
  };
2263
2501
  files[`${basePath}/app.json`] = {
2264
2502
  type: "text",
2265
- content: JSON.stringify(
2266
- {
2267
- $schema: "https://json.schemastore.org/tsconfig",
2268
- extends: "./base.json",
2269
- compilerOptions: {
2270
- lib: ["DOM", "DOM.Iterable", "ESNext"]
2271
- }
2272
- },
2273
- null,
2274
- 2
2275
- )
2503
+ content: renderJson({
2504
+ $schema: "https://json.schemastore.org/tsconfig",
2505
+ extends: "./base.json",
2506
+ compilerOptions: {
2507
+ lib: ["DOM", "DOM.Iterable", "ESNext"]
2508
+ }
2509
+ })
2276
2510
  };
2277
2511
  files[`${basePath}/node.json`] = {
2278
2512
  type: "text",
2279
- content: JSON.stringify(
2280
- {
2281
- $schema: "https://json.schemastore.org/tsconfig",
2282
- extends: "./base.json",
2283
- compilerOptions: {
2284
- lib: ["ESNext"]
2285
- }
2286
- },
2287
- null,
2288
- 2
2289
- )
2513
+ content: renderJson({
2514
+ $schema: "https://json.schemastore.org/tsconfig",
2515
+ extends: "./base.json",
2516
+ compilerOptions: {
2517
+ lib: ["ESNext"]
2518
+ }
2519
+ })
2290
2520
  };
2291
2521
  files[`${basePath}/react.json`] = {
2292
2522
  type: "text",
2293
- content: JSON.stringify(
2294
- {
2295
- $schema: "https://json.schemastore.org/tsconfig",
2296
- extends: "./app.json",
2297
- compilerOptions: {
2298
- jsx: "react-jsx"
2299
- }
2300
- },
2301
- null,
2302
- 2
2303
- )
2523
+ content: renderJson({
2524
+ $schema: "https://json.schemastore.org/tsconfig",
2525
+ extends: "./app.json",
2526
+ compilerOptions: {
2527
+ jsx: "react-jsx"
2528
+ }
2529
+ })
2304
2530
  };
2305
2531
  }
2306
2532
  function renderOxlintConfigPackage(files) {
2307
2533
  const basePath = ".config/oxlint";
2308
2534
  files[`${basePath}/package.json`] = {
2309
2535
  type: "text",
2310
- content: JSON.stringify(
2536
+ content: renderJson(
2311
2537
  {
2312
2538
  name: "@config/oxlint",
2313
2539
  version: "0.1.0",
2314
2540
  private: true,
2315
2541
  files: ["base.json", "react.json"]
2316
2542
  },
2317
- null,
2318
- 2
2543
+ { inlineArrays: false }
2319
2544
  )
2320
2545
  };
2321
2546
  files[`${basePath}/README.md`] = {
@@ -2340,25 +2565,21 @@ oxlint -c node_modules/@config/oxlint/base.json
2340
2565
  };
2341
2566
  files[`${basePath}/base.json`] = {
2342
2567
  type: "text",
2343
- content: JSON.stringify(
2568
+ content: renderJson(
2344
2569
  renderOxlintConfig({
2345
2570
  schemaPath: "./node_modules/oxlint/configuration_schema.json",
2346
2571
  typescript: true
2347
- }),
2348
- null,
2349
- 2
2572
+ })
2350
2573
  )
2351
2574
  };
2352
2575
  files[`${basePath}/react.json`] = {
2353
2576
  type: "text",
2354
- content: JSON.stringify(
2577
+ content: renderJson(
2355
2578
  renderOxlintConfig({
2356
2579
  schemaPath: "./node_modules/oxlint/configuration_schema.json",
2357
2580
  react: true,
2358
2581
  typescript: true
2359
- }),
2360
- null,
2361
- 2
2582
+ })
2362
2583
  )
2363
2584
  };
2364
2585
  }
@@ -2366,7 +2587,7 @@ function renderEslintConfigPackage(files) {
2366
2587
  const basePath = ".config/eslint";
2367
2588
  files[`${basePath}/package.json`] = {
2368
2589
  type: "text",
2369
- content: JSON.stringify(
2590
+ content: renderJson(
2370
2591
  {
2371
2592
  name: "@config/eslint",
2372
2593
  version: "0.1.0",
@@ -2382,8 +2603,7 @@ function renderEslintConfigPackage(files) {
2382
2603
  "typescript-eslint": "^8.18.0"
2383
2604
  }
2384
2605
  },
2385
- null,
2386
- 2
2606
+ { inlineArrays: false }
2387
2607
  )
2388
2608
  };
2389
2609
  files[`${basePath}/README.md`] = {
@@ -2418,52 +2638,52 @@ export default [...react];
2418
2638
  };
2419
2639
  files[`${basePath}/base.js`] = {
2420
2640
  type: "text",
2421
- content: `import js from "@eslint/js";
2422
- import tseslint from "typescript-eslint";
2641
+ content: `import js from '@eslint/js';
2642
+ import tseslint from 'typescript-eslint';
2423
2643
 
2424
2644
  export default tseslint.config(
2425
2645
  js.configs.recommended,
2426
2646
  ...tseslint.configs.recommended,
2427
2647
  {
2428
2648
  rules: {
2429
- "@typescript-eslint/no-unused-vars": [
2430
- "error",
2649
+ '@typescript-eslint/no-unused-vars': [
2650
+ 'error',
2431
2651
  {
2432
- argsIgnorePattern: "^_",
2433
- varsIgnorePattern: "^_",
2434
- caughtErrorsIgnorePattern: "^_",
2652
+ argsIgnorePattern: '^_',
2653
+ varsIgnorePattern: '^_',
2654
+ caughtErrorsIgnorePattern: '^_',
2435
2655
  },
2436
2656
  ],
2437
2657
  },
2438
2658
  },
2439
2659
  {
2440
- ignores: ["dist/**", "node_modules/**"],
2660
+ ignores: ['dist/**', 'node_modules/**'],
2441
2661
  }
2442
2662
  );
2443
2663
  `
2444
2664
  };
2445
2665
  files[`${basePath}/react.js`] = {
2446
2666
  type: "text",
2447
- content: `import js from "@eslint/js";
2448
- import tseslint from "typescript-eslint";
2667
+ content: `import js from '@eslint/js';
2668
+ import tseslint from 'typescript-eslint';
2449
2669
 
2450
2670
  export default tseslint.config(
2451
2671
  js.configs.recommended,
2452
2672
  ...tseslint.configs.recommended,
2453
2673
  {
2454
2674
  rules: {
2455
- "@typescript-eslint/no-unused-vars": [
2456
- "error",
2675
+ '@typescript-eslint/no-unused-vars': [
2676
+ 'error',
2457
2677
  {
2458
- argsIgnorePattern: "^_",
2459
- varsIgnorePattern: "^_",
2460
- caughtErrorsIgnorePattern: "^_",
2678
+ argsIgnorePattern: '^_',
2679
+ varsIgnorePattern: '^_',
2680
+ caughtErrorsIgnorePattern: '^_',
2461
2681
  },
2462
2682
  ],
2463
2683
  },
2464
2684
  },
2465
2685
  {
2466
- ignores: ["dist/**", "node_modules/**"],
2686
+ ignores: ['dist/**', 'node_modules/**'],
2467
2687
  }
2468
2688
  );
2469
2689
  `
@@ -2473,7 +2693,7 @@ function renderPrettierConfigPackage(files) {
2473
2693
  const basePath = ".config/prettier";
2474
2694
  files[`${basePath}/package.json`] = {
2475
2695
  type: "text",
2476
- content: JSON.stringify(
2696
+ content: renderJson(
2477
2697
  {
2478
2698
  name: "@config/prettier",
2479
2699
  version: "0.1.0",
@@ -2484,8 +2704,7 @@ function renderPrettierConfigPackage(files) {
2484
2704
  },
2485
2705
  files: ["base.json", "prettierignore"]
2486
2706
  },
2487
- null,
2488
- 2
2707
+ { inlineArrays: false }
2489
2708
  )
2490
2709
  };
2491
2710
  files[`${basePath}/README.md`] = {
@@ -2517,7 +2736,7 @@ Or in \`.prettierrc.json\`:
2517
2736
  };
2518
2737
  files[`${basePath}/base.json`] = {
2519
2738
  type: "text",
2520
- content: JSON.stringify(toPrettierConfig(), null, 2)
2739
+ content: renderJson(toPrettierConfig())
2521
2740
  };
2522
2741
  files[`${basePath}/prettierignore`] = {
2523
2742
  type: "text",
@@ -2528,15 +2747,14 @@ function renderOxfmtConfigPackage(files) {
2528
2747
  const basePath = ".config/oxfmt";
2529
2748
  files[`${basePath}/package.json`] = {
2530
2749
  type: "text",
2531
- content: JSON.stringify(
2750
+ content: renderJson(
2532
2751
  {
2533
2752
  name: "@config/oxfmt",
2534
2753
  version: "0.1.0",
2535
2754
  private: true,
2536
2755
  files: ["base.json"]
2537
2756
  },
2538
- null,
2539
- 2
2757
+ { inlineArrays: false }
2540
2758
  )
2541
2759
  };
2542
2760
  files[`${basePath}/README.md`] = {
@@ -2560,7 +2778,7 @@ oxfmt -c node_modules/@config/oxfmt/base.json --write .
2560
2778
  };
2561
2779
  files[`${basePath}/base.json`] = {
2562
2780
  type: "text",
2563
- content: JSON.stringify(toOxfmtConfig(), null, 2)
2781
+ content: renderJson(toOxfmtConfig())
2564
2782
  };
2565
2783
  }
2566
2784
 
@@ -2620,7 +2838,7 @@ function renderMonorepo(params) {
2620
2838
  }
2621
2839
  files["package.json"] = {
2622
2840
  type: "text",
2623
- content: JSON.stringify(rootPackageJson, null, 2)
2841
+ content: renderJson(rootPackageJson, { inlineArrays: false })
2624
2842
  };
2625
2843
  if (isPnpm) {
2626
2844
  files["pnpm-workspace.yaml"] = {
@@ -2690,7 +2908,7 @@ export default [...base];
2690
2908
  };
2691
2909
  files["biome.json"] = {
2692
2910
  type: "text",
2693
- content: JSON.stringify(biomeConfig, null, 2)
2911
+ content: renderJson(biomeConfig)
2694
2912
  };
2695
2913
  }
2696
2914
  if (formatter === "oxfmt") {
@@ -2820,7 +3038,7 @@ function planBiome(builder, options) {
2820
3038
  if (isStealth) {
2821
3039
  builder.addFile(".config/biome.json", {
2822
3040
  type: "text",
2823
- content: JSON.stringify(biomeConfig, null, 2)
3041
+ content: renderJson(biomeConfig)
2824
3042
  });
2825
3043
  if (options.linter) {
2826
3044
  builder.addScripts(packageJsonScripts.lint.biome(".config"));
@@ -2831,7 +3049,7 @@ function planBiome(builder, options) {
2831
3049
  } else {
2832
3050
  builder.addFile("biome.json", {
2833
3051
  type: "text",
2834
- content: JSON.stringify(biomeConfig, null, 2)
3052
+ content: renderJson(biomeConfig)
2835
3053
  });
2836
3054
  if (options.linter) {
2837
3055
  builder.addScripts(packageJsonScripts.lint.biome());
@@ -3108,13 +3326,13 @@ function planOxfmt(builder, options) {
3108
3326
  if (isStealth) {
3109
3327
  builder.addFile(".config/oxfmt.json", {
3110
3328
  type: "text",
3111
- content: JSON.stringify(toOxfmtConfig(options.config), null, 2)
3329
+ content: renderJson(toOxfmtConfig(options.config))
3112
3330
  });
3113
3331
  builder.addScripts(packageJsonScripts.format.oxfmt(".config/oxfmt.json"));
3114
3332
  } else {
3115
3333
  builder.addFile("oxfmt.json", {
3116
3334
  type: "text",
3117
- content: JSON.stringify(toOxfmtConfig(options.config), null, 2)
3335
+ content: renderJson(toOxfmtConfig(options.config))
3118
3336
  });
3119
3337
  builder.addScripts(packageJsonScripts.format.oxfmt("oxfmt.json"));
3120
3338
  }
@@ -3159,13 +3377,13 @@ function planOxlint(builder, options) {
3159
3377
  if (isStealth) {
3160
3378
  builder.addFile(".config/oxlint.json", {
3161
3379
  type: "text",
3162
- content: JSON.stringify(oxlintConfig, null, 2)
3380
+ content: renderJson(oxlintConfig)
3163
3381
  });
3164
3382
  builder.addScripts(packageJsonScripts.lint.oxlint(".config/oxlint.json"));
3165
3383
  } else {
3166
3384
  builder.addFile("oxlint.json", {
3167
3385
  type: "text",
3168
- content: JSON.stringify(oxlintConfig, null, 2)
3386
+ content: renderJson(oxlintConfig)
3169
3387
  });
3170
3388
  builder.addScripts(packageJsonScripts.lint.oxlint());
3171
3389
  }
@@ -3204,7 +3422,7 @@ function planPrettier(builder, options) {
3204
3422
  if (isStealth) {
3205
3423
  builder.addFile(".config/prettier.json", {
3206
3424
  type: "text",
3207
- content: JSON.stringify(toPrettierConfig(options.config), null, 2)
3425
+ content: renderJson(toPrettierConfig(options.config))
3208
3426
  });
3209
3427
  builder.addFile(".config/prettierignore", {
3210
3428
  type: "text",
@@ -3216,7 +3434,7 @@ function planPrettier(builder, options) {
3216
3434
  } else {
3217
3435
  builder.addFile(".prettierrc", {
3218
3436
  type: "text",
3219
- content: JSON.stringify(toPrettierConfig(options.config), null, 2)
3437
+ content: renderJson(toPrettierConfig(options.config))
3220
3438
  });
3221
3439
  builder.addFile(".prettierignore", {
3222
3440
  type: "text",
@@ -4207,6 +4425,7 @@ exports.DEFAULT_MINIMUM_RELEASE_AGE_MINUTES = DEFAULT_MINIMUM_RELEASE_AGE_MINUTE
4207
4425
  exports.assignResolvedPackageVersion = assignResolvedPackageVersion;
4208
4426
  exports.clearConfig = clearConfig;
4209
4427
  exports.compareNumericSemver = compareNumericSemver;
4428
+ exports.detectGitignoreVariant = detectGitignoreVariant;
4210
4429
  exports.detectTooling = detectTooling;
4211
4430
  exports.formatEngine = formatEngine;
4212
4431
  exports.formatNodeTypesVersion = formatNodeTypesVersion;
@@ -4235,9 +4454,12 @@ exports.getPackageManagerSpec = getPackageManagerSpec;
4235
4454
  exports.getResolvedPackageVersion = getResolvedPackageVersion;
4236
4455
  exports.getSemverMajor = getSemverMajor;
4237
4456
  exports.getSemverMajorString = getSemverMajorString;
4457
+ exports.isManagedAiFilePath = isManagedAiFilePath;
4238
4458
  exports.isPackageManagerName = isPackageManagerName;
4239
4459
  exports.materializeJobs = materializeJobs;
4240
4460
  exports.merge = merge;
4461
+ exports.mergeAiFileContent = mergeAiFileContent;
4462
+ exports.mergeGitignoreContent = mergeGitignoreContent;
4241
4463
  exports.mergePackageJsonScripts = mergePackageJsonScripts;
4242
4464
  exports.packageJsonScripts = packageJsonScripts;
4243
4465
  exports.parseEngine = parseEngine;
@@ -4250,6 +4472,7 @@ exports.renderAiFiles = renderAiFiles;
4250
4472
  exports.renderEditorConfig = renderEditorConfig;
4251
4473
  exports.renderEslintConfigPackage = renderEslintConfigPackage;
4252
4474
  exports.renderGitignore = renderGitignore;
4475
+ exports.renderJson = renderJson;
4253
4476
  exports.renderOxfmtConfigPackage = renderOxfmtConfigPackage;
4254
4477
  exports.renderOxlintConfig = renderOxlintConfig;
4255
4478
  exports.renderOxlintConfigPackage = renderOxlintConfigPackage;