create-krispya 0.12.0 → 0.14.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.
@@ -1,185 +1,27 @@
1
- import { readFile, access } from 'fs/promises';
1
+ import { readFile, access, constants as constants$1 } from 'fs/promises';
2
2
  import { constants } from 'fs';
3
3
  import { join } from 'path';
4
+ import Conf from 'conf';
4
5
  import color from 'chalk';
5
6
 
6
- const gitAttributesContent = [
7
- "* text eol=lf",
8
- "*.png binary",
9
- "*.jpg binary",
10
- "*.jpeg binary",
11
- "*.gif binary",
12
- "*.ico binary",
13
- "*.mov binary",
14
- "*.mp4 binary",
15
- "*.mp3 binary",
16
- "*.flv binary",
17
- "*.fla binary",
18
- "*.wav binary",
19
- "*.swf binary",
20
- "*.gz binary",
21
- "*.zip binary",
22
- "*.7z binary",
23
- "*.ttf binary",
24
- "*.eot binary",
25
- "*.woff binary",
26
- "*.pyc binary",
27
- "*.pdf binary",
28
- "*.glb binary",
29
- "*.gltf binary"
30
- ].join("\n");
31
-
32
- const ALL_AI_PLATFORMS = ["agents", "claude"];
33
- const AI_PLATFORM_LABELS = {
34
- agents: "AGENTS.md",
35
- claude: "CLAUDE.md"
36
- };
37
- const AI_PLATFORM_HINTS = {
38
- agents: "OpenAI, Cursor, Windsurf, etc.",
39
- claude: "Claude Code"
40
- };
41
- function renderAiFiles(files, params) {
42
- const { platforms, isMonorepo, configStrategy, hasTypecheck, ...rest } = params;
43
- if (platforms.length === 0) return;
44
- const content = generateWorkspace({
45
- ...rest,
46
- isMonorepo: !!isMonorepo,
47
- configStrategy: configStrategy ?? "stealth",
48
- hasTypecheck: hasTypecheck ?? false
49
- });
50
- const pointer = "See [`AGENTS.md`](./Agents.md) for agent context.\n";
51
- const hasAgents = platforms.includes("agents");
52
- const hasClaude = platforms.includes("claude");
53
- const isSingleton = platforms.length === 1;
54
- if (hasAgents) files["AGENTS.md"] = { type: "text", content };
55
- if (hasClaude) {
56
- if (isSingleton) {
57
- files["CLAUDE.md"] = { type: "text", content };
58
- } else {
59
- files["CLAUDE.md"] = { type: "text", content: pointer };
60
- }
61
- }
62
- }
63
- function generateWorkspace(ctx) {
64
- const { packageManager, linter, formatter, hasTypecheck } = ctx;
65
- const exampleFiles = "src/App.tsx src/core/systems/move-entity.ts";
66
- const commands = getAfterEditingCommands(ctx, exampleFiles);
67
- const sections = [
68
- "# Workspace Tools",
69
- "",
70
- `- **Package Manager:** ${packageManager}`,
71
- `- **Linter:** ${linter}`,
72
- `- **Formatter:** ${formatter}`,
73
- "",
74
- "## After Editing",
75
- ""
76
- ];
77
- if (hasTypecheck) {
78
- sections.push(
79
- "\u2705 After editing files, check the types for errors and then format and lint only the files changed for the current task."
80
- );
81
- } else {
82
- sections.push(
83
- "\u2705 After editing files, format and lint only the files changed for the current task."
84
- );
85
- }
86
- sections.push("", "```sh", "# Example");
87
- if (hasTypecheck) {
88
- sections.push(runScript(packageManager, "typecheck"));
89
- }
90
- sections.push(
91
- "# Run format and lint for only files modified",
92
- commands.format,
93
- commands.lint,
94
- "```",
95
- "",
96
- "\u274C Avoid unless explicitly approved:",
97
- "",
98
- "```sh",
99
- runScript(packageManager, "format"),
100
- runScript(packageManager, "lint"),
101
- "```",
102
- ""
103
- );
104
- return sections.join("\n");
105
- }
106
- function getAfterEditingCommands(ctx, files) {
107
- return {
108
- format: getFormatChangedFilesCommand(ctx, files),
109
- lint: getLintChangedFilesCommand(ctx, files)
110
- };
7
+ const PACKAGE_MANAGER_NAMES = /* @__PURE__ */ new Set(["pnpm", "npm", "yarn"]);
8
+ function isPackageManagerName(value) {
9
+ return PACKAGE_MANAGER_NAMES.has(value);
111
10
  }
112
- function getFormatChangedFilesCommand(ctx, files) {
113
- const exec = getExecCommand(ctx.packageManager);
114
- if (ctx.formatter === "prettier") {
115
- const configPath = getPrettierConfigPath(ctx);
116
- const ignorePath = getPrettierIgnorePath(ctx);
117
- const configFlag2 = configPath == null ? "" : ` --config ${configPath}`;
118
- const ignoreFlag = ignorePath == null ? "" : ` --ignore-path ${ignorePath}`;
119
- return `${exec} prettier${configFlag2}${ignoreFlag} --write ${files}`;
120
- }
121
- if (ctx.formatter === "oxfmt") {
122
- const configPath = getOxfmtConfigPath(ctx);
123
- return `${exec} oxfmt -c ${configPath} --write ${files}`;
124
- }
125
- const configFlag = ctx.isMonorepo || ctx.configStrategy === "root" ? "" : " --config-path .config";
126
- return `${exec} biome format${configFlag} --write ${files}`;
127
- }
128
- function getLintChangedFilesCommand(ctx, files) {
129
- const exec = getExecCommand(ctx.packageManager);
130
- if (ctx.linter === "oxlint") {
131
- if (!ctx.isMonorepo) {
132
- return runScript(ctx.packageManager, "lint", files);
133
- }
134
- return `${exec} oxlint ${files}`;
135
- }
136
- if (ctx.linter === "eslint") {
137
- const configFlag2 = ctx.configStrategy === "stealth" ? " --config .config/eslint.config.js" : "";
138
- return `${exec} eslint${configFlag2} ${files}`;
11
+ function parsePackageManagerSpec(value) {
12
+ if (value == null || value.length === 0) {
13
+ return void 0;
139
14
  }
140
- const configFlag = ctx.isMonorepo || ctx.configStrategy === "root" ? "" : " --config-path .config";
141
- return `${exec} biome lint${configFlag} ${files}`;
142
- }
143
- function getPrettierConfigPath(ctx) {
144
- if (ctx.isMonorepo) return ".config/prettier/base.json";
145
- if (ctx.configStrategy === "stealth") return ".config/prettier.json";
146
- return void 0;
147
- }
148
- function getPrettierIgnorePath(ctx) {
149
- if (ctx.isMonorepo) return ".config/prettier/prettierignore";
150
- if (ctx.configStrategy === "stealth") return ".config/prettierignore";
151
- return void 0;
152
- }
153
- function getOxfmtConfigPath(ctx) {
154
- if (ctx.isMonorepo) return ".config/oxfmt/base.json";
155
- if (ctx.configStrategy === "stealth") return ".config/oxfmt.json";
156
- return "oxfmt.json";
157
- }
158
- function runScript(packageManager, script, args) {
159
- const suffix = args == null ? "" : ` ${args}`;
160
- if (packageManager === "npm") {
161
- return `npm run ${script}${args == null ? "" : ` --${suffix}`}`;
15
+ const atIndex = value.indexOf("@");
16
+ const name = atIndex === -1 ? value : value.slice(0, atIndex);
17
+ if (!isPackageManagerName(name)) {
18
+ return void 0;
162
19
  }
163
- if (packageManager === "yarn") {
164
- return `yarn ${script}${suffix}`;
20
+ if (atIndex === -1) {
21
+ return { name };
165
22
  }
166
- return `${packageManager} ${script}${args == null ? "" : ` --${suffix}`}`;
167
- }
168
- function getExecCommand(packageManager) {
169
- if (packageManager === "npm") return "npm exec --";
170
- if (packageManager === "yarn") return "yarn exec";
171
- return `${packageManager} exec`;
172
- }
173
-
174
- function getLanguageFromTemplate(template) {
175
- return template.endsWith("-js") ? "javascript" : "typescript";
176
- }
177
- function getBaseTemplate(template) {
178
- return template.replace("-js", "");
179
- }
180
- function shouldEnableReactCompiler(options) {
181
- const template = options.template ?? "vanilla";
182
- return getBaseTemplate(template) === "react" && (options.projectType ?? "app") === "app";
23
+ const version = value.slice(atIndex + 1);
24
+ return version.length > 0 ? { name, version } : { name };
183
25
  }
184
26
 
185
27
  function unique(...array) {
@@ -268,6 +110,10 @@ function validatePackageName(name) {
268
110
  }
269
111
  return validateNameSegment(name, "Package name");
270
112
  }
113
+ function getPackageDirectoryName(name) {
114
+ const slashIndex = name.indexOf("/");
115
+ return slashIndex === -1 ? name : name.slice(slashIndex + 1);
116
+ }
271
117
 
272
118
  function generateRandomName() {
273
119
  const adjectives = [
@@ -379,150 +225,600 @@ async function detectLinterFromConfig(root) {
379
225
  return "biome";
380
226
  }
381
227
  }
382
- return void 0;
228
+ return void 0;
229
+ }
230
+ async function detectFormatterFromConfig(root) {
231
+ if (await pathExists(join(root, ".config/prettier"))) return "prettier";
232
+ if (await pathExists(join(root, ".config/oxfmt"))) return "oxfmt";
233
+ if (await pathExists(join(root, "biome.json"))) {
234
+ try {
235
+ const content = await readFile(join(root, "biome.json"), "utf-8");
236
+ const config = JSON.parse(content);
237
+ if (config.formatter?.enabled !== false) return "biome";
238
+ } catch {
239
+ return "biome";
240
+ }
241
+ }
242
+ return void 0;
243
+ }
244
+ function detectLinterFromDeps(devDeps) {
245
+ if (!devDeps) return void 0;
246
+ if (devDeps["@biomejs/biome"]) return "biome";
247
+ if (devDeps.eslint) return "eslint";
248
+ if (devDeps.oxlint) return "oxlint";
249
+ return void 0;
250
+ }
251
+ function detectFormatterFromDeps(devDeps) {
252
+ if (!devDeps) return void 0;
253
+ if (devDeps["@biomejs/biome"]) return "biome";
254
+ if (devDeps.prettier) return "prettier";
255
+ if (devDeps.oxfmt) return "oxfmt";
256
+ return void 0;
257
+ }
258
+ async function detectTooling(root) {
259
+ try {
260
+ const pkgPath = join(root, "package.json");
261
+ const content = await readFile(pkgPath, "utf-8");
262
+ const pkg = JSON.parse(content);
263
+ const linter = detectLinterFromScript(pkg.scripts?.lint) ?? await detectLinterFromConfig(root) ?? detectLinterFromDeps(pkg.devDependencies);
264
+ const formatter = detectFormatterFromScript(pkg.scripts?.format) ?? await detectFormatterFromConfig(root) ?? detectFormatterFromDeps(pkg.devDependencies);
265
+ return { linter, formatter };
266
+ } catch {
267
+ return { linter: void 0, formatter: void 0 };
268
+ }
269
+ }
270
+
271
+ const DEFAULT_MINIMUM_RELEASE_AGE_MINUTES = 1440;
272
+ const MINUTE_IN_MS = 60 * 1e3;
273
+ function getMinimumReleaseAgeMinutes(options) {
274
+ return Math.max(0, options?.minimumReleaseAgeMinutes ?? DEFAULT_MINIMUM_RELEASE_AGE_MINUTES);
275
+ }
276
+ function isVersionOldEnough(version, time, options) {
277
+ const minimumReleaseAgeMinutes = getMinimumReleaseAgeMinutes(options);
278
+ if (minimumReleaseAgeMinutes === 0) return true;
279
+ const publishedAt = time?.[version];
280
+ if (publishedAt == null) return false;
281
+ const publishedTime = Date.parse(publishedAt);
282
+ if (!Number.isFinite(publishedTime)) return false;
283
+ return (options?.now ?? Date.now()) - publishedTime >= minimumReleaseAgeMinutes * MINUTE_IN_MS;
284
+ }
285
+ function getInstallableVersions(versions, time, options) {
286
+ return [...versions].filter((version) => isVersionOldEnough(version, time, options));
287
+ }
288
+ async function fetchNpmPackageMetadata(packageName) {
289
+ const response = await fetch(`https://registry.npmjs.org/${packageName}`);
290
+ return await response.json();
291
+ }
292
+ async function getLatestNpmVersion(packageName, fallback, options) {
293
+ try {
294
+ if (getMinimumReleaseAgeMinutes(options) === 0) {
295
+ const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`);
296
+ const data2 = await response.json();
297
+ return data2.version;
298
+ }
299
+ const data = await fetchNpmPackageMetadata(packageName);
300
+ const latestVersion = data["dist-tags"]?.latest;
301
+ if (latestVersion != null && isVersionOldEnough(latestVersion, data.time, options)) {
302
+ return latestVersion;
303
+ }
304
+ const latestInstallableVersion = getInstallableVersions(
305
+ Object.keys(data.versions ?? {}),
306
+ data.time,
307
+ options
308
+ ).sort((a, b) => compareNumericSemver(b, a))[0];
309
+ return latestInstallableVersion ?? fallback;
310
+ } catch {
311
+ return fallback;
312
+ }
313
+ }
314
+ function getSemverMajor(version) {
315
+ if (version == null) return void 0;
316
+ const major = Number.parseInt(version, 10);
317
+ return Number.isFinite(major) ? major : void 0;
318
+ }
319
+ function getSemverMajorString(version) {
320
+ return String(getSemverMajor(version) ?? version.split(".")[0]);
321
+ }
322
+ function compareNumericSemver(a, b) {
323
+ const aParts = a.split(".").map((part) => Number.parseInt(part, 10) || 0);
324
+ const bParts = b.split(".").map((part) => Number.parseInt(part, 10) || 0);
325
+ const maxLength = Math.max(aParts.length, bParts.length);
326
+ for (let index = 0; index < maxLength; index += 1) {
327
+ const difference = (aParts[index] ?? 0) - (bParts[index] ?? 0);
328
+ if (difference !== 0) {
329
+ return difference;
330
+ }
331
+ }
332
+ return 0;
333
+ }
334
+ function getLatestMatchingMajorVersion(versions, majorVersion, time, options) {
335
+ return getInstallableVersions(versions, time, options).filter((version) => version.split(".")[0] === majorVersion).sort((a, b) => compareNumericSemver(b, a))[0];
336
+ }
337
+ async function getLatestNpmMajorVersion(packageName, majorVersion, fallback, options) {
338
+ try {
339
+ const data = await fetchNpmPackageMetadata(packageName);
340
+ const latestMatchingVersion = getLatestMatchingMajorVersion(
341
+ Object.keys(data.versions ?? {}),
342
+ majorVersion,
343
+ data.time,
344
+ options
345
+ );
346
+ return latestMatchingVersion ?? fallback;
347
+ } catch {
348
+ return fallback;
349
+ }
350
+ }
351
+ async function getLatestNpmMajorVersionAtOrBelow(packageName, majorVersion, fallback, options) {
352
+ try {
353
+ const data = await fetchNpmPackageMetadata(packageName);
354
+ const versions = Object.keys(data.versions ?? {});
355
+ const requestedMajor = getSemverMajor(majorVersion);
356
+ if (requestedMajor !== void 0) {
357
+ for (let major = requestedMajor; major >= 0; major -= 1) {
358
+ const latestMatchingVersion = getLatestMatchingMajorVersion(
359
+ versions,
360
+ String(major),
361
+ data.time,
362
+ options
363
+ );
364
+ if (latestMatchingVersion != null) {
365
+ return latestMatchingVersion;
366
+ }
367
+ }
368
+ }
369
+ return fallback;
370
+ } catch {
371
+ return fallback;
372
+ }
373
+ }
374
+ async function getLatestPnpmVersion() {
375
+ return getLatestNpmVersion("pnpm", "10.11.0");
376
+ }
377
+ async function getLatestYarnVersion() {
378
+ return getLatestNpmVersion("yarn", "4.6.0");
379
+ }
380
+ async function getLatestNpmCliVersion() {
381
+ return getLatestNpmVersion("npm", "11.0.0");
382
+ }
383
+ async function getLatestNodeVersion() {
384
+ try {
385
+ const response = await fetch("https://nodejs.org/dist/index.json");
386
+ const data = await response.json();
387
+ const latestVersion = data[0];
388
+ if (latestVersion) {
389
+ return latestVersion.version.replace(/^v/, "");
390
+ }
391
+ return "25.0.0";
392
+ } catch {
393
+ return "25.0.0";
394
+ }
395
+ }
396
+
397
+ function parseWorkspaceYamlContent(content) {
398
+ const directories = [];
399
+ let inPackagesSection = false;
400
+ for (const line of content.split("\n")) {
401
+ const trimmed = line.trim();
402
+ if (trimmed === "packages:") {
403
+ inPackagesSection = true;
404
+ continue;
405
+ }
406
+ if (inPackagesSection && trimmed && !line.startsWith(" ") && !line.startsWith(" ") && !trimmed.startsWith("-")) {
407
+ break;
408
+ }
409
+ if (inPackagesSection && trimmed.startsWith("-")) {
410
+ const entry = trimmed.slice(1).trim().replace(/^["']|["']$/g, "").replace(/^\.\//, "").replace(/\/\*.*$/, "");
411
+ if (entry && !entry.startsWith(".")) {
412
+ directories.push(entry);
413
+ }
414
+ }
415
+ }
416
+ return directories;
417
+ }
418
+
419
+ const pnpm10Capabilities = {
420
+ pnpmWorkspaceVersionPolicy: "manage-package-manager-versions",
421
+ pnpmBuildDependencyPolicy: "onlyBuiltDependencies"
422
+ };
423
+ const pnpm10Requirements = {
424
+ node: "18.12"
425
+ };
426
+ const pnpm11Capabilities = {
427
+ pnpmWorkspaceVersionPolicy: "pmOnFail",
428
+ pnpmBuildDependencyPolicy: "allowBuilds"
429
+ };
430
+ const pnpm11Requirements = {
431
+ node: "22.13"
432
+ };
433
+ function getPnpmCapabilities(major) {
434
+ switch (major) {
435
+ case 10:
436
+ return pnpm10Capabilities;
437
+ case 11:
438
+ return pnpm11Capabilities;
439
+ default:
440
+ if (major != null && major >= 12) return pnpm11Capabilities;
441
+ else return pnpm10Capabilities;
442
+ }
443
+ }
444
+ function getPnpmRequirements(major) {
445
+ switch (major) {
446
+ case 10:
447
+ return pnpm10Requirements;
448
+ case 11:
449
+ return pnpm11Requirements;
450
+ default:
451
+ if (major != null && major >= 12) return pnpm11Requirements;
452
+ else return pnpm10Requirements;
453
+ }
454
+ }
455
+ function getPnpmProfile(spec) {
456
+ const major = getSemverMajor(spec.version);
457
+ return {
458
+ ...spec,
459
+ major,
460
+ capabilities: getPnpmCapabilities(major),
461
+ requirements: getPnpmRequirements(major)
462
+ };
463
+ }
464
+ function renderPnpmWorkspaceConfig(options) {
465
+ const {
466
+ profile,
467
+ manageVersions = true,
468
+ packages = [],
469
+ buildDependencies = { esbuild: true }
470
+ } = options;
471
+ const lines = [];
472
+ if (manageVersions) {
473
+ if (profile.capabilities.pnpmWorkspaceVersionPolicy === "pmOnFail") {
474
+ lines.push("pmOnFail: download", "");
475
+ } else {
476
+ lines.push("manage-package-manager-versions: true", "");
477
+ }
478
+ }
479
+ if (packages.length > 0) {
480
+ lines.push("packages:", ...packages.map((pattern) => ` - "${pattern}"`), "");
481
+ }
482
+ if (profile.capabilities.pnpmBuildDependencyPolicy === "allowBuilds") {
483
+ lines.push("allowBuilds:");
484
+ for (const [dependency, allowed] of Object.entries(buildDependencies)) {
485
+ lines.push(` ${dependency}: ${allowed ? "true" : "false"}`);
486
+ }
487
+ } else {
488
+ const allowedDependencies = Object.entries(buildDependencies).filter(([, allowed]) => allowed).map(([dependency]) => dependency);
489
+ lines.push("onlyBuiltDependencies:");
490
+ for (const dependency of allowedDependencies) {
491
+ lines.push(` - ${dependency}`);
492
+ }
493
+ }
494
+ return lines.join("\n");
495
+ }
496
+
497
+ function getPackageManagerProfile(spec) {
498
+ const major = getSemverMajor(spec.version);
499
+ if (spec.name === "pnpm") {
500
+ return getPnpmProfile(spec);
501
+ }
502
+ return {
503
+ ...spec,
504
+ major,
505
+ capabilities: {},
506
+ requirements: {}
507
+ };
508
+ }
509
+
510
+ function getPackageManagerSpec(packageManager) {
511
+ return packageManager ?? { name: "pnpm" };
512
+ }
513
+ function getPackageManagerName(packageManager) {
514
+ return getPackageManagerSpec(packageManager).name;
515
+ }
516
+ function formatPackageManager(packageManager) {
517
+ const spec = getPackageManagerSpec(packageManager);
518
+ return spec.version ? `${spec.name}@${spec.version}` : spec.name;
519
+ }
520
+
521
+ async function resolvePackageManager(options) {
522
+ const packageManager = getPackageManagerSpec(options.packageManager);
523
+ if (packageManager.version == null) {
524
+ if (packageManager.name === "pnpm") {
525
+ packageManager.version = await getLatestPnpmVersion();
526
+ } else if (packageManager.name === "yarn") {
527
+ packageManager.version = await getLatestYarnVersion();
528
+ } else if (packageManager.name === "npm") {
529
+ packageManager.version = await getLatestNpmCliVersion();
530
+ }
531
+ } else if (/^\d+$/.test(packageManager.version)) {
532
+ const fallback = `${packageManager.version}.0.0`;
533
+ packageManager.version = await getLatestNpmMajorVersion(
534
+ packageManager.name,
535
+ packageManager.version,
536
+ fallback
537
+ );
538
+ }
539
+ return packageManager;
540
+ }
541
+ async function resolvePackageManagerProfile(options) {
542
+ return getPackageManagerProfile(await resolvePackageManager(options));
543
+ }
544
+
545
+ function getEngineSpec(engine) {
546
+ return engine ?? { name: "node" };
547
+ }
548
+ function getEngineName(engine) {
549
+ return getEngineSpec(engine).name;
550
+ }
551
+ function formatEngine(engine) {
552
+ const spec = getEngineSpec(engine);
553
+ return spec.version ? `${spec.name}@${spec.version}` : spec.name;
554
+ }
555
+ function parseEngine(engines) {
556
+ if (engines == null) {
557
+ return void 0;
558
+ }
559
+ const [name, range] = Object.entries(engines).find(
560
+ ([engineName]) => engineName !== "npm" && engineName !== "pnpm" && engineName !== "yarn"
561
+ ) ?? [];
562
+ if (name == null) {
563
+ return void 0;
564
+ }
565
+ const version = range?.match(/(\d+(?:\.\d+(?:\.\d+)?)?)/)?.[1];
566
+ return { name, version };
567
+ }
568
+ async function resolveEngine(options) {
569
+ const engine = getEngineSpec(options.engine);
570
+ if ((engine.version == null || engine.version === "latest") && engine.name === "node") {
571
+ engine.version = await getLatestNodeVersion();
572
+ }
573
+ return engine;
574
+ }
575
+
576
+ function getLanguageFromTemplate(template) {
577
+ return template.endsWith("-js") ? "javascript" : "typescript";
578
+ }
579
+ function getBaseTemplate(template) {
580
+ return template.replace("-js", "");
581
+ }
582
+ function shouldEnableReactCompiler(options) {
583
+ const template = options.template ?? "vanilla";
584
+ return getBaseTemplate(template) === "react" && (options.projectType ?? "app") === "app";
585
+ }
586
+
587
+ const config = new Conf({
588
+ projectName: "create-krispya"
589
+ });
590
+ function getAiPlatforms() {
591
+ return config.get("aiPlatforms");
592
+ }
593
+ function setAiPlatforms(platforms) {
594
+ config.set("aiPlatforms", platforms);
595
+ }
596
+ function getConfigStrategy() {
597
+ return config.get("configStrategy") ?? "stealth";
598
+ }
599
+ function setConfigStrategy(strategy) {
600
+ config.set("configStrategy", strategy);
601
+ }
602
+ function clearConfig() {
603
+ config.clear();
604
+ }
605
+ function getConfigPath() {
606
+ return config.path;
607
+ }
608
+
609
+ const ALL_AI_PLATFORMS = ["agents", "claude"];
610
+ const AI_PLATFORM_LABELS = {
611
+ agents: "AGENTS.md",
612
+ claude: "CLAUDE.md"
613
+ };
614
+ const AI_PLATFORM_HINTS = {
615
+ agents: "OpenAI, Cursor, Windsurf, etc.",
616
+ claude: "Claude Code"
617
+ };
618
+ function renderAiFiles(files, params) {
619
+ const { platforms, isMonorepo, configStrategy, hasTypecheck, ...rest } = params;
620
+ if (platforms.length === 0) return;
621
+ const content = generateWorkspace({
622
+ ...rest,
623
+ isMonorepo: !!isMonorepo,
624
+ configStrategy: configStrategy ?? "stealth",
625
+ hasTypecheck: hasTypecheck ?? false
626
+ });
627
+ const pointer = "See [`AGENTS.md`](./Agents.md) for agent context.\n";
628
+ const hasAgents = platforms.includes("agents");
629
+ const hasClaude = platforms.includes("claude");
630
+ const isSingleton = platforms.length === 1;
631
+ if (hasAgents) files["AGENTS.md"] = { type: "text", content };
632
+ if (hasClaude) {
633
+ if (isSingleton) {
634
+ files["CLAUDE.md"] = { type: "text", content };
635
+ } else {
636
+ files["CLAUDE.md"] = { type: "text", content: pointer };
637
+ }
638
+ }
639
+ }
640
+ function generateWorkspace(ctx) {
641
+ const { packageManager, linter, formatter, hasTypecheck } = ctx;
642
+ const exampleFiles = "src/App.tsx src/core/systems/move-entity.ts";
643
+ const commands = getAfterEditingCommands(ctx, exampleFiles);
644
+ const sections = [
645
+ "# Workspace Tools",
646
+ "",
647
+ `- **Package Manager:** ${packageManager}`,
648
+ `- **Linter:** ${linter}`,
649
+ `- **Formatter:** ${formatter}`,
650
+ "",
651
+ "## After Editing",
652
+ ""
653
+ ];
654
+ if (hasTypecheck) {
655
+ sections.push(
656
+ "\u2705 After editing files, check the types for errors and then format and lint only the files changed for the current task."
657
+ );
658
+ } else {
659
+ sections.push(
660
+ "\u2705 After editing files, format and lint only the files changed for the current task."
661
+ );
662
+ }
663
+ sections.push("", "```sh", "# Example");
664
+ if (hasTypecheck) {
665
+ sections.push(runScript(packageManager, "typecheck"));
666
+ }
667
+ sections.push(
668
+ "# Run format and lint for only files modified",
669
+ commands.format,
670
+ commands.lint,
671
+ "```",
672
+ "",
673
+ "\u274C Avoid unless explicitly approved:",
674
+ "",
675
+ "```sh",
676
+ runScript(packageManager, "format"),
677
+ runScript(packageManager, "lint"),
678
+ "```",
679
+ ""
680
+ );
681
+ return sections.join("\n");
682
+ }
683
+ function getAfterEditingCommands(ctx, files) {
684
+ return {
685
+ format: getFormatChangedFilesCommand(ctx, files),
686
+ lint: getLintChangedFilesCommand(ctx, files)
687
+ };
688
+ }
689
+ function getFormatChangedFilesCommand(ctx, files) {
690
+ const exec = getExecCommand(ctx.packageManager);
691
+ if (ctx.formatter === "prettier") {
692
+ const configPath = getPrettierConfigPath(ctx);
693
+ const ignorePath = getPrettierIgnorePath(ctx);
694
+ const configFlag2 = configPath == null ? "" : ` --config ${configPath}`;
695
+ const ignoreFlag = ignorePath == null ? "" : ` --ignore-path ${ignorePath}`;
696
+ return `${exec} prettier${configFlag2}${ignoreFlag} --write ${files}`;
697
+ }
698
+ if (ctx.formatter === "oxfmt") {
699
+ const configPath = getOxfmtConfigPath(ctx);
700
+ return `${exec} oxfmt -c ${configPath} --write ${files}`;
701
+ }
702
+ const configFlag = ctx.isMonorepo || ctx.configStrategy === "root" ? "" : " --config-path .config";
703
+ return `${exec} biome format${configFlag} --write ${files}`;
383
704
  }
384
- async function detectFormatterFromConfig(root) {
385
- if (await pathExists(join(root, ".config/prettier"))) return "prettier";
386
- if (await pathExists(join(root, ".config/oxfmt"))) return "oxfmt";
387
- if (await pathExists(join(root, "biome.json"))) {
388
- try {
389
- const content = await readFile(join(root, "biome.json"), "utf-8");
390
- const config = JSON.parse(content);
391
- if (config.formatter?.enabled !== false) return "biome";
392
- } catch {
393
- return "biome";
705
+ function getLintChangedFilesCommand(ctx, files) {
706
+ const exec = getExecCommand(ctx.packageManager);
707
+ if (ctx.linter === "oxlint") {
708
+ if (!ctx.isMonorepo) {
709
+ return runScript(ctx.packageManager, "lint", files);
394
710
  }
711
+ return `${exec} oxlint ${files}`;
395
712
  }
396
- return void 0;
713
+ if (ctx.linter === "eslint") {
714
+ const configFlag2 = ctx.configStrategy === "stealth" ? " --config .config/eslint.config.js" : "";
715
+ return `${exec} eslint${configFlag2} ${files}`;
716
+ }
717
+ const configFlag = ctx.isMonorepo || ctx.configStrategy === "root" ? "" : " --config-path .config";
718
+ return `${exec} biome lint${configFlag} ${files}`;
397
719
  }
398
- function detectLinterFromDeps(devDeps) {
399
- if (!devDeps) return void 0;
400
- if (devDeps["@biomejs/biome"]) return "biome";
401
- if (devDeps.eslint) return "eslint";
402
- if (devDeps.oxlint) return "oxlint";
720
+ function getPrettierConfigPath(ctx) {
721
+ if (ctx.isMonorepo) return ".config/prettier/base.json";
722
+ if (ctx.configStrategy === "stealth") return ".config/prettier.json";
403
723
  return void 0;
404
724
  }
405
- function detectFormatterFromDeps(devDeps) {
406
- if (!devDeps) return void 0;
407
- if (devDeps["@biomejs/biome"]) return "biome";
408
- if (devDeps.prettier) return "prettier";
409
- if (devDeps.oxfmt) return "oxfmt";
725
+ function getPrettierIgnorePath(ctx) {
726
+ if (ctx.isMonorepo) return ".config/prettier/prettierignore";
727
+ if (ctx.configStrategy === "stealth") return ".config/prettierignore";
410
728
  return void 0;
411
729
  }
412
- async function detectTooling(root) {
413
- try {
414
- const pkgPath = join(root, "package.json");
415
- const content = await readFile(pkgPath, "utf-8");
416
- const pkg = JSON.parse(content);
417
- const linter = detectLinterFromScript(pkg.scripts?.lint) ?? await detectLinterFromConfig(root) ?? detectLinterFromDeps(pkg.devDependencies);
418
- const formatter = detectFormatterFromScript(pkg.scripts?.format) ?? await detectFormatterFromConfig(root) ?? detectFormatterFromDeps(pkg.devDependencies);
419
- return { linter, formatter };
420
- } catch {
421
- return { linter: void 0, formatter: void 0 };
422
- }
730
+ function getOxfmtConfigPath(ctx) {
731
+ if (ctx.isMonorepo) return ".config/oxfmt/base.json";
732
+ if (ctx.configStrategy === "stealth") return ".config/oxfmt.json";
733
+ return "oxfmt.json";
423
734
  }
424
-
425
- async function getLatestNpmVersion(packageName, fallback) {
426
- try {
427
- const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`);
428
- const data = await response.json();
429
- return data.version;
430
- } catch {
431
- return fallback;
735
+ function runScript(packageManager, script, args) {
736
+ const suffix = args == null ? "" : ` ${args}`;
737
+ if (packageManager === "npm") {
738
+ return `npm run ${script}${args == null ? "" : ` --${suffix}`}`;
432
739
  }
433
- }
434
- function compareNumericSemver(a, b) {
435
- const aParts = a.split(".").map((part) => Number.parseInt(part, 10) || 0);
436
- const bParts = b.split(".").map((part) => Number.parseInt(part, 10) || 0);
437
- const maxLength = Math.max(aParts.length, bParts.length);
438
- for (let index = 0; index < maxLength; index += 1) {
439
- const difference = (aParts[index] ?? 0) - (bParts[index] ?? 0);
440
- if (difference !== 0) {
441
- return difference;
442
- }
740
+ if (packageManager === "yarn") {
741
+ return `yarn ${script}${suffix}`;
443
742
  }
444
- return 0;
445
- }
446
- function getLatestMatchingMajorVersion(versions, majorVersion) {
447
- return [...versions].filter((version) => version.split(".")[0] === majorVersion).sort((a, b) => compareNumericSemver(b, a))[0];
743
+ return `${packageManager} ${script}${args == null ? "" : ` --${suffix}`}`;
448
744
  }
449
- async function getLatestNpmMajorVersion(packageName, majorVersion, fallback) {
450
- try {
451
- const response = await fetch(`https://registry.npmjs.org/${packageName}`);
452
- const data = await response.json();
453
- const latestMatchingVersion = getLatestMatchingMajorVersion(
454
- Object.keys(data.versions ?? {}),
455
- majorVersion
456
- );
457
- return latestMatchingVersion ?? fallback;
458
- } catch {
459
- return fallback;
460
- }
745
+ function getExecCommand(packageManager) {
746
+ if (packageManager === "npm") return "npm exec --";
747
+ if (packageManager === "yarn") return "yarn exec";
748
+ return `${packageManager} exec`;
461
749
  }
462
- async function getLatestNpmMajorVersionAtOrBelow(packageName, majorVersion, fallback) {
463
- try {
464
- const response = await fetch(`https://registry.npmjs.org/${packageName}`);
465
- const data = await response.json();
466
- const versions = Object.keys(data.versions ?? {});
467
- const requestedMajor = Number.parseInt(majorVersion, 10);
468
- if (Number.isFinite(requestedMajor)) {
469
- for (let major = requestedMajor; major >= 0; major -= 1) {
470
- const latestMatchingVersion = getLatestMatchingMajorVersion(versions, String(major));
471
- if (latestMatchingVersion != null) {
472
- return latestMatchingVersion;
473
- }
474
- }
750
+
751
+ async function checkAnyExists(paths) {
752
+ for (const path of paths) {
753
+ try {
754
+ await access(path, constants$1.F_OK);
755
+ return true;
756
+ } catch {
475
757
  }
476
- return fallback;
477
- } catch {
478
- return fallback;
479
758
  }
759
+ return false;
480
760
  }
481
- async function getLatestPnpmVersion() {
482
- return getLatestNpmVersion("pnpm", "10.11.0");
483
- }
484
- async function getLatestYarnVersion() {
485
- return getLatestNpmVersion("yarn", "4.6.0");
486
- }
487
- async function getLatestNpmCliVersion() {
488
- return getLatestNpmVersion("npm", "11.0.0");
489
- }
490
- async function getLatestNodeVersion() {
761
+ async function validateWorkspace(monorepoRoot) {
762
+ const errors = [];
763
+ const tsConfigPath = join(monorepoRoot, ".config/typescript/package.json");
491
764
  try {
492
- const response = await fetch("https://nodejs.org/dist/index.json");
493
- const data = await response.json();
494
- const latestVersion = data[0];
495
- if (latestVersion) {
496
- return latestVersion.version.replace(/^v/, "");
497
- }
498
- return "25.0.0";
765
+ await access(tsConfigPath, constants$1.F_OK);
499
766
  } catch {
500
- return "25.0.0";
767
+ errors.push("Missing .config/typescript package");
501
768
  }
502
- }
503
-
504
- function parseWorkspaceYamlContent(content) {
505
- const directories = [];
506
- let inPackagesSection = false;
507
- for (const line of content.split("\n")) {
508
- const trimmed = line.trim();
509
- if (trimmed === "packages:") {
510
- inPackagesSection = true;
511
- continue;
512
- }
513
- if (inPackagesSection && trimmed && !line.startsWith(" ") && !line.startsWith(" ") && !trimmed.startsWith("-")) {
514
- break;
515
- }
516
- if (inPackagesSection && trimmed.startsWith("-")) {
517
- const entry = trimmed.slice(1).trim().replace(/^["']|["']$/g, "").replace(/^\.\//, "").replace(/\/\*.*$/, "");
518
- if (entry && !entry.startsWith(".")) {
519
- directories.push(entry);
520
- }
521
- }
769
+ const linterPaths = [
770
+ join(monorepoRoot, ".config/oxlint/package.json"),
771
+ join(monorepoRoot, ".config/eslint/package.json"),
772
+ join(monorepoRoot, "eslint.config.js"),
773
+ join(monorepoRoot, "biome.json")
774
+ ];
775
+ const hasLinter = await checkAnyExists(linterPaths);
776
+ if (!hasLinter) {
777
+ errors.push(
778
+ "Missing linter config (.config/oxlint, .config/eslint, eslint.config.js, or biome.json)"
779
+ );
522
780
  }
523
- return directories;
781
+ const formatterPaths = [
782
+ join(monorepoRoot, ".config/oxfmt/package.json"),
783
+ join(monorepoRoot, ".config/prettier/package.json"),
784
+ join(monorepoRoot, ".prettierrc.json"),
785
+ join(monorepoRoot, "biome.json")
786
+ ];
787
+ const hasFormatter = await checkAnyExists(formatterPaths);
788
+ if (!hasFormatter) {
789
+ errors.push(
790
+ "Missing formatter config (.config/oxfmt, .config/prettier, .prettierrc.json, or biome.json)"
791
+ );
792
+ }
793
+ return { valid: errors.length === 0, errors };
524
794
  }
525
795
 
796
+ const gitAttributesContent = [
797
+ "* text eol=lf",
798
+ "*.png binary",
799
+ "*.jpg binary",
800
+ "*.jpeg binary",
801
+ "*.gif binary",
802
+ "*.ico binary",
803
+ "*.mov binary",
804
+ "*.mp4 binary",
805
+ "*.mp3 binary",
806
+ "*.flv binary",
807
+ "*.fla binary",
808
+ "*.wav binary",
809
+ "*.swf binary",
810
+ "*.gz binary",
811
+ "*.zip binary",
812
+ "*.7z binary",
813
+ "*.ttf binary",
814
+ "*.eot binary",
815
+ "*.woff binary",
816
+ "*.pyc binary",
817
+ "*.pdf binary",
818
+ "*.glb binary",
819
+ "*.gltf binary"
820
+ ].join("\n");
821
+
526
822
  const PACKAGE_VERSION_DEFINITIONS = {
527
823
  "@babel/core": { fallbackVersion: "7.29.0" },
528
824
  "@biomejs/biome": { fallbackVersion: "2.0.0" },
@@ -546,7 +842,7 @@ const PACKAGE_VERSION_DEFINITIONS = {
546
842
  "@vitejs/plugin-react": { fallbackVersion: "6.0.1" },
547
843
  "@viverse/cli": { fallbackVersion: "0.9.5-beta.8" },
548
844
  eslint: { fallbackVersion: "9.17.0" },
549
- "eslint-plugin-react-hooks": { fallbackVersion: "5.1.0" },
845
+ "eslint-plugin-react-hooks": { fallbackVersion: "7.1.1" },
550
846
  "babel-plugin-react-compiler": { fallbackVersion: "1.0.0" },
551
847
  jsdom: { fallbackVersion: "26.0.0" },
552
848
  koota: { fallbackVersion: "0.4.0" },
@@ -594,68 +890,6 @@ function formatResolvedPackageVersion(versions, packageName, prefix) {
594
890
  function assignResolvedPackageVersion(target, versions, packageName, prefix) {
595
891
  target[packageName] = formatResolvedPackageVersion(versions, packageName, prefix);
596
892
  }
597
- function getPackageManagerSpec(packageManager) {
598
- return packageManager ?? { name: "pnpm" };
599
- }
600
- function getPackageManagerName(packageManager) {
601
- return getPackageManagerSpec(packageManager).name;
602
- }
603
- function formatPackageManager(packageManager) {
604
- const spec = getPackageManagerSpec(packageManager);
605
- return spec.version ? `${spec.name}@${spec.version}` : spec.name;
606
- }
607
- function parsePackageManager(packageManager) {
608
- if (packageManager == null || packageManager.length === 0) {
609
- return void 0;
610
- }
611
- const atIndex = packageManager.indexOf("@");
612
- if (atIndex === -1) {
613
- return { name: packageManager };
614
- }
615
- return {
616
- name: packageManager.slice(0, atIndex),
617
- version: packageManager.slice(atIndex + 1)
618
- };
619
- }
620
- function getEngineSpec(engine) {
621
- return engine ?? { name: "node" };
622
- }
623
- function getEngineName(engine) {
624
- return getEngineSpec(engine).name;
625
- }
626
- function parseEngine(engines) {
627
- if (engines == null) {
628
- return void 0;
629
- }
630
- const [name, range] = Object.entries(engines).find(
631
- ([engineName]) => engineName !== "npm" && engineName !== "pnpm" && engineName !== "yarn"
632
- ) ?? [];
633
- if (name == null) {
634
- return void 0;
635
- }
636
- const version = range?.match(/(\d+(?:\.\d+(?:\.\d+)?)?)/)?.[1];
637
- return { name, version };
638
- }
639
- async function resolvePackageManager(options) {
640
- const packageManager = getPackageManagerSpec(options.packageManager);
641
- if (packageManager.version == null) {
642
- if (packageManager.name === "pnpm") {
643
- packageManager.version = await getLatestPnpmVersion();
644
- } else if (packageManager.name === "yarn") {
645
- packageManager.version = await getLatestYarnVersion();
646
- } else if (packageManager.name === "npm") {
647
- packageManager.version = await getLatestNpmCliVersion();
648
- }
649
- }
650
- return packageManager;
651
- }
652
- async function resolveEngine(options) {
653
- const engine = getEngineSpec(options.engine);
654
- if ((engine.version == null || engine.version === "latest") && engine.name === "node") {
655
- engine.version = await getLatestNodeVersion();
656
- }
657
- return engine;
658
- }
659
893
  function formatNodeTypesVersion(versions = {}, _engine) {
660
894
  const resolvedVersion = versions["@types/node"];
661
895
  if (resolvedVersion != null) {
@@ -672,7 +906,7 @@ async function resolveNodeTypesVersion(engine, versions = {}) {
672
906
  return void 0;
673
907
  }
674
908
  const nodeVersion = engineSpec.version ?? await getLatestNodeVersion();
675
- const majorVersion = nodeVersion.split(".")[0];
909
+ const majorVersion = getSemverMajorString(nodeVersion);
676
910
  return getLatestNpmMajorVersionAtOrBelow(
677
911
  "@types/node",
678
912
  majorVersion,
@@ -833,6 +1067,9 @@ function collectProjectPackageNames(options) {
833
1067
  if (isTypescript) {
834
1068
  addPackageName(packageNames, explicitVersions, "oxlint-tsgolint");
835
1069
  }
1070
+ if (useReactCompiler) {
1071
+ addPackageName(packageNames, explicitVersions, "eslint-plugin-react-hooks");
1072
+ }
836
1073
  }
837
1074
  } else if (linter === "biome") {
838
1075
  addPackageName(packageNames, explicitVersions, "@biomejs/biome");
@@ -1192,12 +1429,12 @@ function renderPackageJson(params) {
1192
1429
  if (!isMonorepoPackage) {
1193
1430
  const engines = {};
1194
1431
  if (packageManager.version != null) {
1195
- const majorVersion = packageManager.version.split(".")[0];
1432
+ const majorVersion = getSemverMajorString(packageManager.version);
1196
1433
  engines[packageManager.name] = `>=${majorVersion}.0.0`;
1197
1434
  packageJson.packageManager = formatPackageManager(packageManager);
1198
1435
  }
1199
1436
  if (engine.version != null) {
1200
- const majorVersion = engine.version.split(".")[0];
1437
+ const majorVersion = getSemverMajorString(engine.version);
1201
1438
  engines[engine.name] = `>=${majorVersion}.0.0`;
1202
1439
  }
1203
1440
  if (Object.keys(engines).length > 0) {
@@ -1209,15 +1446,12 @@ function renderPackageJson(params) {
1209
1446
  content: JSON.stringify(packageJson, null, 2)
1210
1447
  };
1211
1448
  if (isPnpm && !options.workspaceRoot) {
1212
- const manageVersions = options.pnpmManageVersions ?? true;
1213
- const workspaceLines = [];
1214
- if (manageVersions) {
1215
- workspaceLines.push("manage-package-manager-versions: true", "");
1216
- }
1217
- workspaceLines.push("onlyBuiltDependencies:", " - esbuild");
1218
1449
  files["pnpm-workspace.yaml"] = {
1219
1450
  type: "text",
1220
- content: workspaceLines.join("\n")
1451
+ content: renderPnpmWorkspaceConfig({
1452
+ profile: getPackageManagerProfile(packageManager),
1453
+ manageVersions: options.pnpmManageVersions ?? true
1454
+ })
1221
1455
  };
1222
1456
  }
1223
1457
  return { files };
@@ -1904,6 +2138,27 @@ const defaultLinterMetaConfig = {
1904
2138
  }
1905
2139
  };
1906
2140
 
2141
+ const REACT_HOOKS_JS_PLUGIN = {
2142
+ name: "react-hooks-js",
2143
+ specifier: "eslint-plugin-react-hooks"
2144
+ };
2145
+ const REACT_COMPILER_RULES = {
2146
+ "react-hooks-js/component-hook-factories": "error",
2147
+ "react-hooks-js/config": "error",
2148
+ "react-hooks-js/error-boundaries": "error",
2149
+ "react-hooks-js/gating": "error",
2150
+ "react-hooks-js/globals": "error",
2151
+ "react-hooks-js/immutability": "error",
2152
+ "react-hooks-js/incompatible-library": "warn",
2153
+ "react-hooks-js/preserve-manual-memoization": "warn",
2154
+ "react-hooks-js/purity": "error",
2155
+ "react-hooks-js/refs": "error",
2156
+ "react-hooks-js/set-state-in-effect": "error",
2157
+ "react-hooks-js/set-state-in-render": "error",
2158
+ "react-hooks-js/static-components": "error",
2159
+ "react-hooks-js/unsupported-syntax": "warn",
2160
+ "react-hooks-js/use-memo": "error"
2161
+ };
1907
2162
  function renderOxlintConfig(params) {
1908
2163
  const config = params.config ?? defaultLinterMetaConfig;
1909
2164
  const { rules } = config;
@@ -1914,6 +2169,7 @@ function renderOxlintConfig(params) {
1914
2169
  return {
1915
2170
  $schema: params.schemaPath,
1916
2171
  plugins,
2172
+ ...params.reactCompiler === true ? { jsPlugins: [REACT_HOOKS_JS_PLUGIN] } : {},
1917
2173
  ...params.typescript === true ? { options: { typeAware: true } } : {},
1918
2174
  rules: {
1919
2175
  "no-unused-vars": [
@@ -1928,7 +2184,8 @@ function renderOxlintConfig(params) {
1928
2184
  "no-unused-expressions": [
1929
2185
  rules.noUnusedExpressions.level,
1930
2186
  { allowShortCircuit: rules.noUnusedExpressions.allowShortCircuit }
1931
- ]
2187
+ ],
2188
+ ...params.reactCompiler === true ? REACT_COMPILER_RULES : {}
1932
2189
  },
1933
2190
  ignorePatterns: config.ignorePatterns
1934
2191
  };
@@ -2343,12 +2600,12 @@ function renderMonorepo(params) {
2343
2600
  };
2344
2601
  const engines = {};
2345
2602
  if (isPnpm && packageManager.version) {
2346
- const majorVersion = packageManager.version.split(".")[0];
2603
+ const majorVersion = getSemverMajorString(packageManager.version);
2347
2604
  engines.pnpm = `>=${majorVersion}.0.0`;
2348
2605
  rootPackageJson.packageManager = formatPackageManager(packageManager);
2349
2606
  }
2350
2607
  if (engine?.version) {
2351
- const majorVersion = engine.version.split(".")[0];
2608
+ const majorVersion = getSemverMajorString(engine.version);
2352
2609
  engines[engine.name] = `>=${majorVersion}.0.0`;
2353
2610
  }
2354
2611
  if (Object.keys(engines).length > 0) {
@@ -2359,15 +2616,13 @@ function renderMonorepo(params) {
2359
2616
  content: JSON.stringify(rootPackageJson, null, 2)
2360
2617
  };
2361
2618
  if (isPnpm) {
2362
- const workspaceLines = [];
2363
- if (pnpmManageVersions) {
2364
- workspaceLines.push("manage-package-manager-versions: true", "");
2365
- }
2366
- workspaceLines.push("packages:", ' - ".config/*"', ' - "apps/*"', ' - "packages/*"', "");
2367
- workspaceLines.push("onlyBuiltDependencies:", " - esbuild");
2368
2619
  files["pnpm-workspace.yaml"] = {
2369
2620
  type: "text",
2370
- content: workspaceLines.join("\n")
2621
+ content: renderPnpmWorkspaceConfig({
2622
+ profile: getPackageManagerProfile(packageManager),
2623
+ manageVersions: pnpmManageVersions,
2624
+ packages: [".config/*", "apps/*", "packages/*"]
2625
+ })
2371
2626
  };
2372
2627
  }
2373
2628
  files["tsconfig.json"] = {
@@ -2872,6 +3127,7 @@ function planOxlint(builder, options) {
2872
3127
  const baseTemplate = getBaseTemplate(template);
2873
3128
  const isTypescript = getLanguageFromTemplate(template) === "typescript";
2874
3129
  const isReact = baseTemplate === "react" || baseTemplate === "r3f";
3130
+ const useReactCompiler = shouldEnableReactCompiler(builder.options);
2875
3131
  const isMonorepo = builder.options.workspaceRoot != null;
2876
3132
  if (isMonorepo) {
2877
3133
  builder.addDevDependency("@config/oxlint", { version: "workspace:*" });
@@ -2882,10 +3138,14 @@ function planOxlint(builder, options) {
2882
3138
  if (isTypescript) {
2883
3139
  builder.addDevDependency("oxlint-tsgolint");
2884
3140
  }
3141
+ if (useReactCompiler) {
3142
+ builder.addDevDependency("eslint-plugin-react-hooks");
3143
+ }
2885
3144
  const isStealth = builder.isStealthConfig();
2886
3145
  const oxlintConfig = renderOxlintConfig({
2887
3146
  schemaPath: isStealth ? "../node_modules/oxlint/configuration_schema.json" : "./node_modules/oxlint/configuration_schema.json",
2888
3147
  react: isReact,
3148
+ reactCompiler: useReactCompiler,
2889
3149
  typescript: isTypescript,
2890
3150
  config: options.config
2891
3151
  });
@@ -3587,6 +3847,21 @@ async function resolveWorkspaceFacts(input) {
3587
3847
  });
3588
3848
  }
3589
3849
 
3850
+ function materializeJobs(jobs) {
3851
+ const files = {};
3852
+ for (const job of jobs) {
3853
+ if (job.type === "write-file") {
3854
+ files[job.path] = job.file;
3855
+ } else if (job.type === "merge-pnpm-workspace") {
3856
+ files[job.path] = {
3857
+ type: "text",
3858
+ content: job.content
3859
+ };
3860
+ }
3861
+ }
3862
+ return files;
3863
+ }
3864
+
3590
3865
  async function planProject(input) {
3591
3866
  const planInput = isProjectPlanInput(input) ? input : resolveProjectPlanInput(input);
3592
3867
  return createProjectPlan(await resolveProjectFacts(planInput));
@@ -3865,8 +4140,13 @@ function createProjectPlan(planInput) {
3865
4140
  platforms: planInput.aiAgents.config.platforms
3866
4141
  });
3867
4142
  }
4143
+ const jobs = Object.entries(files).map(([path, file]) => ({
4144
+ type: "write-file",
4145
+ path,
4146
+ file
4147
+ }));
3868
4148
  return {
3869
- files,
4149
+ files: materializeJobs(jobs),
3870
4150
  dependencies,
3871
4151
  devDependencies,
3872
4152
  peerDependencies,
@@ -3894,8 +4174,13 @@ async function planWorkspace(input) {
3894
4174
  const planInput = isWorkspacePlanInput(input) ? input : resolveWorkspacePlanInput(input);
3895
4175
  const resolvedInput = await resolveWorkspaceFacts(planInput);
3896
4176
  const { files } = renderMonorepo(workspacePlanInputToMonorepoParams(resolvedInput));
4177
+ const jobs = Object.entries(files).map(([path, file]) => ({
4178
+ type: "write-file",
4179
+ path,
4180
+ file
4181
+ }));
3897
4182
  return {
3898
- files,
4183
+ files: materializeJobs(jobs),
3899
4184
  dependencies: {},
3900
4185
  devDependencies: {},
3901
4186
  peerDependencies: {},
@@ -3908,4 +4193,4 @@ async function planWorkspace(input) {
3908
4193
  };
3909
4194
  }
3910
4195
 
3911
- export { ALL_AI_PLATFORMS as A, parsePackageManager as B, parseEngine as C, renderTypescriptConfigPackage as D, renderOxlintConfigPackage as E, renderEslintConfigPackage as F, renderOxfmtConfigPackage as G, renderPrettierConfigPackage as H, resolveMonorepoRootPackageVersions as I, getResolvedPackageVersion as J, renderVscodeFiles as K, renderAiFiles as L, renderVscodeFiles$1 as M, renderEditorConfig as N, renderGitignore as O, toPrettierIgnoreContent as P, mergePackageJsonScripts as Q, renderViteConfig as R, packageJsonScripts as S, resolveDefaultPackageJsonScripts as T, formatResolvedPackageVersion as U, renderOxlintConfig as V, getBaseTemplate as a, getLanguageFromTemplate as b, getLatestNodeVersion as c, detectTooling as d, getLatestNpmCliVersion as e, getLatestNpmMajorVersion as f, generateRandomName as g, getLatestNpmMajorVersionAtOrBelow as h, getLatestNpmVersion as i, getLatestPnpmVersion as j, getLatestYarnVersion as k, planProject as l, merge as m, planWorkspace as n, projectPlanInputToOptions as o, parseWorkspaceYamlContent as p, resolveWorkspacePlanInput as q, resolveProjectPlanInput as r, shouldEnableReactCompiler as s, getEngineName as t, unique as u, validatePackageName as v, workspacePlanInputToMonorepoParams as w, getPackageManagerName as x, AI_PLATFORM_LABELS as y, AI_PLATFORM_HINTS as z };
4196
+ export { formatNodeTypesVersion as $, ALL_AI_PLATFORMS as A, renderGitignore as B, toPrettierIgnoreContent as C, mergePackageJsonScripts as D, getPackageManagerProfile as E, renderPnpmWorkspaceConfig as F, renderViteConfig as G, packageJsonScripts as H, resolveDefaultPackageJsonScripts as I, formatResolvedPackageVersion as J, getSemverMajorString as K, formatPackageManager as L, renderOxlintConfig as M, resolvePackageManager as N, getSemverMajor as O, compareNumericSemver as P, getPackageDirectoryName as Q, planProject as R, resolveProjectPlanInput as S, validatePackageName as T, clearConfig as U, getConfigPath as V, resolveWorkspacePlanInput as W, planWorkspace as X, DEFAULT_MINIMUM_RELEASE_AGE_MINUTES as Y, assignResolvedPackageVersion as Z, formatEngine as _, getPackageManagerName as a, getEngineSpec as a0, getLatestNodeVersion as a1, getLatestNpmCliVersion as a2, getLatestNpmMajorVersion as a3, getLatestNpmMajorVersionAtOrBelow as a4, getLatestNpmVersion as a5, getLatestPnpmVersion as a6, getLatestYarnVersion as a7, getPackageFallbackVersion as a8, getPackageManagerSpec as a9, isPackageManagerName as aa, materializeJobs as ab, merge as ac, projectPlanInputToOptions as ad, resolveEngine as ae, resolvePackageManagerProfile as af, resolvePackageVersions as ag, resolveProjectFacts as ah, resolveProjectPackageVersions as ai, resolveWorkspaceFacts as aj, setAiPlatforms as ak, setConfigStrategy as al, unique as am, workspacePlanInputToMonorepoParams as an, getBaseTemplate as b, getLanguageFromTemplate as c, generateRandomName as d, getConfigStrategy as e, getAiPlatforms as f, getEngineName as g, AI_PLATFORM_LABELS as h, AI_PLATFORM_HINTS as i, detectTooling as j, parseEngine as k, parseWorkspaceYamlContent as l, renderOxlintConfigPackage as m, renderEslintConfigPackage as n, renderOxfmtConfigPackage as o, parsePackageManagerSpec as p, renderPrettierConfigPackage as q, renderTypescriptConfigPackage as r, shouldEnableReactCompiler as s, resolveMonorepoRootPackageVersions as t, getResolvedPackageVersion as u, validateWorkspace as v, renderVscodeFiles as w, renderAiFiles as x, renderVscodeFiles$1 as y, renderEditorConfig as z };