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.
@@ -3,189 +3,32 @@
3
3
  const promises = require('fs/promises');
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
+ const Conf = require('conf');
6
7
  const color = require('chalk');
7
8
 
8
9
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
9
10
 
11
+ const Conf__default = /*#__PURE__*/_interopDefaultCompat(Conf);
10
12
  const color__default = /*#__PURE__*/_interopDefaultCompat(color);
11
13
 
12
- const gitAttributesContent = [
13
- "* text eol=lf",
14
- "*.png binary",
15
- "*.jpg binary",
16
- "*.jpeg binary",
17
- "*.gif binary",
18
- "*.ico binary",
19
- "*.mov binary",
20
- "*.mp4 binary",
21
- "*.mp3 binary",
22
- "*.flv binary",
23
- "*.fla binary",
24
- "*.wav binary",
25
- "*.swf binary",
26
- "*.gz binary",
27
- "*.zip binary",
28
- "*.7z binary",
29
- "*.ttf binary",
30
- "*.eot binary",
31
- "*.woff binary",
32
- "*.pyc binary",
33
- "*.pdf binary",
34
- "*.glb binary",
35
- "*.gltf binary"
36
- ].join("\n");
37
-
38
- const ALL_AI_PLATFORMS = ["agents", "claude"];
39
- const AI_PLATFORM_LABELS = {
40
- agents: "AGENTS.md",
41
- claude: "CLAUDE.md"
42
- };
43
- const AI_PLATFORM_HINTS = {
44
- agents: "OpenAI, Cursor, Windsurf, etc.",
45
- claude: "Claude Code"
46
- };
47
- function renderAiFiles(files, params) {
48
- const { platforms, isMonorepo, configStrategy, hasTypecheck, ...rest } = params;
49
- if (platforms.length === 0) return;
50
- const content = generateWorkspace({
51
- ...rest,
52
- isMonorepo: !!isMonorepo,
53
- configStrategy: configStrategy ?? "stealth",
54
- hasTypecheck: hasTypecheck ?? false
55
- });
56
- const pointer = "See [`AGENTS.md`](./Agents.md) for agent context.\n";
57
- const hasAgents = platforms.includes("agents");
58
- const hasClaude = platforms.includes("claude");
59
- const isSingleton = platforms.length === 1;
60
- if (hasAgents) files["AGENTS.md"] = { type: "text", content };
61
- if (hasClaude) {
62
- if (isSingleton) {
63
- files["CLAUDE.md"] = { type: "text", content };
64
- } else {
65
- files["CLAUDE.md"] = { type: "text", content: pointer };
66
- }
67
- }
14
+ const PACKAGE_MANAGER_NAMES = /* @__PURE__ */ new Set(["pnpm", "npm", "yarn"]);
15
+ function isPackageManagerName(value) {
16
+ return PACKAGE_MANAGER_NAMES.has(value);
68
17
  }
69
- function generateWorkspace(ctx) {
70
- const { packageManager, linter, formatter, hasTypecheck } = ctx;
71
- const exampleFiles = "src/App.tsx src/core/systems/move-entity.ts";
72
- const commands = getAfterEditingCommands(ctx, exampleFiles);
73
- const sections = [
74
- "# Workspace Tools",
75
- "",
76
- `- **Package Manager:** ${packageManager}`,
77
- `- **Linter:** ${linter}`,
78
- `- **Formatter:** ${formatter}`,
79
- "",
80
- "## After Editing",
81
- ""
82
- ];
83
- if (hasTypecheck) {
84
- sections.push(
85
- "\u2705 After editing files, check the types for errors and then format and lint only the files changed for the current task."
86
- );
87
- } else {
88
- sections.push(
89
- "\u2705 After editing files, format and lint only the files changed for the current task."
90
- );
91
- }
92
- sections.push("", "```sh", "# Example");
93
- if (hasTypecheck) {
94
- sections.push(runScript(packageManager, "typecheck"));
95
- }
96
- sections.push(
97
- "# Run format and lint for only files modified",
98
- commands.format,
99
- commands.lint,
100
- "```",
101
- "",
102
- "\u274C Avoid unless explicitly approved:",
103
- "",
104
- "```sh",
105
- runScript(packageManager, "format"),
106
- runScript(packageManager, "lint"),
107
- "```",
108
- ""
109
- );
110
- return sections.join("\n");
111
- }
112
- function getAfterEditingCommands(ctx, files) {
113
- return {
114
- format: getFormatChangedFilesCommand(ctx, files),
115
- lint: getLintChangedFilesCommand(ctx, files)
116
- };
117
- }
118
- function getFormatChangedFilesCommand(ctx, files) {
119
- const exec = getExecCommand(ctx.packageManager);
120
- if (ctx.formatter === "prettier") {
121
- const configPath = getPrettierConfigPath(ctx);
122
- const ignorePath = getPrettierIgnorePath(ctx);
123
- const configFlag2 = configPath == null ? "" : ` --config ${configPath}`;
124
- const ignoreFlag = ignorePath == null ? "" : ` --ignore-path ${ignorePath}`;
125
- return `${exec} prettier${configFlag2}${ignoreFlag} --write ${files}`;
126
- }
127
- if (ctx.formatter === "oxfmt") {
128
- const configPath = getOxfmtConfigPath(ctx);
129
- return `${exec} oxfmt -c ${configPath} --write ${files}`;
130
- }
131
- const configFlag = ctx.isMonorepo || ctx.configStrategy === "root" ? "" : " --config-path .config";
132
- return `${exec} biome format${configFlag} --write ${files}`;
133
- }
134
- function getLintChangedFilesCommand(ctx, files) {
135
- const exec = getExecCommand(ctx.packageManager);
136
- if (ctx.linter === "oxlint") {
137
- if (!ctx.isMonorepo) {
138
- return runScript(ctx.packageManager, "lint", files);
139
- }
140
- return `${exec} oxlint ${files}`;
141
- }
142
- if (ctx.linter === "eslint") {
143
- const configFlag2 = ctx.configStrategy === "stealth" ? " --config .config/eslint.config.js" : "";
144
- return `${exec} eslint${configFlag2} ${files}`;
18
+ function parsePackageManagerSpec(value) {
19
+ if (value == null || value.length === 0) {
20
+ return void 0;
145
21
  }
146
- const configFlag = ctx.isMonorepo || ctx.configStrategy === "root" ? "" : " --config-path .config";
147
- return `${exec} biome lint${configFlag} ${files}`;
148
- }
149
- function getPrettierConfigPath(ctx) {
150
- if (ctx.isMonorepo) return ".config/prettier/base.json";
151
- if (ctx.configStrategy === "stealth") return ".config/prettier.json";
152
- return void 0;
153
- }
154
- function getPrettierIgnorePath(ctx) {
155
- if (ctx.isMonorepo) return ".config/prettier/prettierignore";
156
- if (ctx.configStrategy === "stealth") return ".config/prettierignore";
157
- return void 0;
158
- }
159
- function getOxfmtConfigPath(ctx) {
160
- if (ctx.isMonorepo) return ".config/oxfmt/base.json";
161
- if (ctx.configStrategy === "stealth") return ".config/oxfmt.json";
162
- return "oxfmt.json";
163
- }
164
- function runScript(packageManager, script, args) {
165
- const suffix = args == null ? "" : ` ${args}`;
166
- if (packageManager === "npm") {
167
- return `npm run ${script}${args == null ? "" : ` --${suffix}`}`;
22
+ const atIndex = value.indexOf("@");
23
+ const name = atIndex === -1 ? value : value.slice(0, atIndex);
24
+ if (!isPackageManagerName(name)) {
25
+ return void 0;
168
26
  }
169
- if (packageManager === "yarn") {
170
- return `yarn ${script}${suffix}`;
27
+ if (atIndex === -1) {
28
+ return { name };
171
29
  }
172
- return `${packageManager} ${script}${args == null ? "" : ` --${suffix}`}`;
173
- }
174
- function getExecCommand(packageManager) {
175
- if (packageManager === "npm") return "npm exec --";
176
- if (packageManager === "yarn") return "yarn exec";
177
- return `${packageManager} exec`;
178
- }
179
-
180
- function getLanguageFromTemplate(template) {
181
- return template.endsWith("-js") ? "javascript" : "typescript";
182
- }
183
- function getBaseTemplate(template) {
184
- return template.replace("-js", "");
185
- }
186
- function shouldEnableReactCompiler(options) {
187
- const template = options.template ?? "vanilla";
188
- return getBaseTemplate(template) === "react" && (options.projectType ?? "app") === "app";
30
+ const version = value.slice(atIndex + 1);
31
+ return version.length > 0 ? { name, version } : { name };
189
32
  }
190
33
 
191
34
  function unique(...array) {
@@ -274,6 +117,10 @@ function validatePackageName(name) {
274
117
  }
275
118
  return validateNameSegment(name, "Package name");
276
119
  }
120
+ function getPackageDirectoryName(name) {
121
+ const slashIndex = name.indexOf("/");
122
+ return slashIndex === -1 ? name : name.slice(slashIndex + 1);
123
+ }
277
124
 
278
125
  function generateRandomName() {
279
126
  const adjectives = [
@@ -385,150 +232,600 @@ async function detectLinterFromConfig(root) {
385
232
  return "biome";
386
233
  }
387
234
  }
388
- return void 0;
235
+ return void 0;
236
+ }
237
+ async function detectFormatterFromConfig(root) {
238
+ if (await pathExists(path.join(root, ".config/prettier"))) return "prettier";
239
+ if (await pathExists(path.join(root, ".config/oxfmt"))) return "oxfmt";
240
+ if (await pathExists(path.join(root, "biome.json"))) {
241
+ try {
242
+ const content = await promises.readFile(path.join(root, "biome.json"), "utf-8");
243
+ const config = JSON.parse(content);
244
+ if (config.formatter?.enabled !== false) return "biome";
245
+ } catch {
246
+ return "biome";
247
+ }
248
+ }
249
+ return void 0;
250
+ }
251
+ function detectLinterFromDeps(devDeps) {
252
+ if (!devDeps) return void 0;
253
+ if (devDeps["@biomejs/biome"]) return "biome";
254
+ if (devDeps.eslint) return "eslint";
255
+ if (devDeps.oxlint) return "oxlint";
256
+ return void 0;
257
+ }
258
+ function detectFormatterFromDeps(devDeps) {
259
+ if (!devDeps) return void 0;
260
+ if (devDeps["@biomejs/biome"]) return "biome";
261
+ if (devDeps.prettier) return "prettier";
262
+ if (devDeps.oxfmt) return "oxfmt";
263
+ return void 0;
264
+ }
265
+ async function detectTooling(root) {
266
+ try {
267
+ const pkgPath = path.join(root, "package.json");
268
+ const content = await promises.readFile(pkgPath, "utf-8");
269
+ const pkg = JSON.parse(content);
270
+ const linter = detectLinterFromScript(pkg.scripts?.lint) ?? await detectLinterFromConfig(root) ?? detectLinterFromDeps(pkg.devDependencies);
271
+ const formatter = detectFormatterFromScript(pkg.scripts?.format) ?? await detectFormatterFromConfig(root) ?? detectFormatterFromDeps(pkg.devDependencies);
272
+ return { linter, formatter };
273
+ } catch {
274
+ return { linter: void 0, formatter: void 0 };
275
+ }
276
+ }
277
+
278
+ const DEFAULT_MINIMUM_RELEASE_AGE_MINUTES = 1440;
279
+ const MINUTE_IN_MS = 60 * 1e3;
280
+ function getMinimumReleaseAgeMinutes(options) {
281
+ return Math.max(0, options?.minimumReleaseAgeMinutes ?? DEFAULT_MINIMUM_RELEASE_AGE_MINUTES);
282
+ }
283
+ function isVersionOldEnough(version, time, options) {
284
+ const minimumReleaseAgeMinutes = getMinimumReleaseAgeMinutes(options);
285
+ if (minimumReleaseAgeMinutes === 0) return true;
286
+ const publishedAt = time?.[version];
287
+ if (publishedAt == null) return false;
288
+ const publishedTime = Date.parse(publishedAt);
289
+ if (!Number.isFinite(publishedTime)) return false;
290
+ return (options?.now ?? Date.now()) - publishedTime >= minimumReleaseAgeMinutes * MINUTE_IN_MS;
291
+ }
292
+ function getInstallableVersions(versions, time, options) {
293
+ return [...versions].filter((version) => isVersionOldEnough(version, time, options));
294
+ }
295
+ async function fetchNpmPackageMetadata(packageName) {
296
+ const response = await fetch(`https://registry.npmjs.org/${packageName}`);
297
+ return await response.json();
298
+ }
299
+ async function getLatestNpmVersion(packageName, fallback, options) {
300
+ try {
301
+ if (getMinimumReleaseAgeMinutes(options) === 0) {
302
+ const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`);
303
+ const data2 = await response.json();
304
+ return data2.version;
305
+ }
306
+ const data = await fetchNpmPackageMetadata(packageName);
307
+ const latestVersion = data["dist-tags"]?.latest;
308
+ if (latestVersion != null && isVersionOldEnough(latestVersion, data.time, options)) {
309
+ return latestVersion;
310
+ }
311
+ const latestInstallableVersion = getInstallableVersions(
312
+ Object.keys(data.versions ?? {}),
313
+ data.time,
314
+ options
315
+ ).sort((a, b) => compareNumericSemver(b, a))[0];
316
+ return latestInstallableVersion ?? fallback;
317
+ } catch {
318
+ return fallback;
319
+ }
320
+ }
321
+ function getSemverMajor(version) {
322
+ if (version == null) return void 0;
323
+ const major = Number.parseInt(version, 10);
324
+ return Number.isFinite(major) ? major : void 0;
325
+ }
326
+ function getSemverMajorString(version) {
327
+ return String(getSemverMajor(version) ?? version.split(".")[0]);
328
+ }
329
+ function compareNumericSemver(a, b) {
330
+ const aParts = a.split(".").map((part) => Number.parseInt(part, 10) || 0);
331
+ const bParts = b.split(".").map((part) => Number.parseInt(part, 10) || 0);
332
+ const maxLength = Math.max(aParts.length, bParts.length);
333
+ for (let index = 0; index < maxLength; index += 1) {
334
+ const difference = (aParts[index] ?? 0) - (bParts[index] ?? 0);
335
+ if (difference !== 0) {
336
+ return difference;
337
+ }
338
+ }
339
+ return 0;
340
+ }
341
+ 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];
343
+ }
344
+ async function getLatestNpmMajorVersion(packageName, majorVersion, fallback, options) {
345
+ try {
346
+ const data = await fetchNpmPackageMetadata(packageName);
347
+ const latestMatchingVersion = getLatestMatchingMajorVersion(
348
+ Object.keys(data.versions ?? {}),
349
+ majorVersion,
350
+ data.time,
351
+ options
352
+ );
353
+ return latestMatchingVersion ?? fallback;
354
+ } catch {
355
+ return fallback;
356
+ }
357
+ }
358
+ async function getLatestNpmMajorVersionAtOrBelow(packageName, majorVersion, fallback, options) {
359
+ try {
360
+ const data = await fetchNpmPackageMetadata(packageName);
361
+ const versions = Object.keys(data.versions ?? {});
362
+ const requestedMajor = getSemverMajor(majorVersion);
363
+ if (requestedMajor !== void 0) {
364
+ for (let major = requestedMajor; major >= 0; major -= 1) {
365
+ const latestMatchingVersion = getLatestMatchingMajorVersion(
366
+ versions,
367
+ String(major),
368
+ data.time,
369
+ options
370
+ );
371
+ if (latestMatchingVersion != null) {
372
+ return latestMatchingVersion;
373
+ }
374
+ }
375
+ }
376
+ return fallback;
377
+ } catch {
378
+ return fallback;
379
+ }
380
+ }
381
+ async function getLatestPnpmVersion() {
382
+ return getLatestNpmVersion("pnpm", "10.11.0");
383
+ }
384
+ async function getLatestYarnVersion() {
385
+ return getLatestNpmVersion("yarn", "4.6.0");
386
+ }
387
+ async function getLatestNpmCliVersion() {
388
+ return getLatestNpmVersion("npm", "11.0.0");
389
+ }
390
+ async function getLatestNodeVersion() {
391
+ try {
392
+ const response = await fetch("https://nodejs.org/dist/index.json");
393
+ const data = await response.json();
394
+ const latestVersion = data[0];
395
+ if (latestVersion) {
396
+ return latestVersion.version.replace(/^v/, "");
397
+ }
398
+ return "25.0.0";
399
+ } catch {
400
+ return "25.0.0";
401
+ }
402
+ }
403
+
404
+ function parseWorkspaceYamlContent(content) {
405
+ const directories = [];
406
+ let inPackagesSection = false;
407
+ for (const line of content.split("\n")) {
408
+ const trimmed = line.trim();
409
+ if (trimmed === "packages:") {
410
+ inPackagesSection = true;
411
+ continue;
412
+ }
413
+ if (inPackagesSection && trimmed && !line.startsWith(" ") && !line.startsWith(" ") && !trimmed.startsWith("-")) {
414
+ break;
415
+ }
416
+ if (inPackagesSection && trimmed.startsWith("-")) {
417
+ const entry = trimmed.slice(1).trim().replace(/^["']|["']$/g, "").replace(/^\.\//, "").replace(/\/\*.*$/, "");
418
+ if (entry && !entry.startsWith(".")) {
419
+ directories.push(entry);
420
+ }
421
+ }
422
+ }
423
+ return directories;
424
+ }
425
+
426
+ const pnpm10Capabilities = {
427
+ pnpmWorkspaceVersionPolicy: "manage-package-manager-versions",
428
+ pnpmBuildDependencyPolicy: "onlyBuiltDependencies"
429
+ };
430
+ const pnpm10Requirements = {
431
+ node: "18.12"
432
+ };
433
+ const pnpm11Capabilities = {
434
+ pnpmWorkspaceVersionPolicy: "pmOnFail",
435
+ pnpmBuildDependencyPolicy: "allowBuilds"
436
+ };
437
+ const pnpm11Requirements = {
438
+ node: "22.13"
439
+ };
440
+ function getPnpmCapabilities(major) {
441
+ switch (major) {
442
+ case 10:
443
+ return pnpm10Capabilities;
444
+ case 11:
445
+ return pnpm11Capabilities;
446
+ default:
447
+ if (major != null && major >= 12) return pnpm11Capabilities;
448
+ else return pnpm10Capabilities;
449
+ }
450
+ }
451
+ function getPnpmRequirements(major) {
452
+ switch (major) {
453
+ case 10:
454
+ return pnpm10Requirements;
455
+ case 11:
456
+ return pnpm11Requirements;
457
+ default:
458
+ if (major != null && major >= 12) return pnpm11Requirements;
459
+ else return pnpm10Requirements;
460
+ }
461
+ }
462
+ function getPnpmProfile(spec) {
463
+ const major = getSemverMajor(spec.version);
464
+ return {
465
+ ...spec,
466
+ major,
467
+ capabilities: getPnpmCapabilities(major),
468
+ requirements: getPnpmRequirements(major)
469
+ };
470
+ }
471
+ function renderPnpmWorkspaceConfig(options) {
472
+ const {
473
+ profile,
474
+ manageVersions = true,
475
+ packages = [],
476
+ buildDependencies = { esbuild: true }
477
+ } = options;
478
+ const lines = [];
479
+ if (manageVersions) {
480
+ if (profile.capabilities.pnpmWorkspaceVersionPolicy === "pmOnFail") {
481
+ lines.push("pmOnFail: download", "");
482
+ } else {
483
+ lines.push("manage-package-manager-versions: true", "");
484
+ }
485
+ }
486
+ if (packages.length > 0) {
487
+ lines.push("packages:", ...packages.map((pattern) => ` - "${pattern}"`), "");
488
+ }
489
+ if (profile.capabilities.pnpmBuildDependencyPolicy === "allowBuilds") {
490
+ lines.push("allowBuilds:");
491
+ for (const [dependency, allowed] of Object.entries(buildDependencies)) {
492
+ lines.push(` ${dependency}: ${allowed ? "true" : "false"}`);
493
+ }
494
+ } else {
495
+ const allowedDependencies = Object.entries(buildDependencies).filter(([, allowed]) => allowed).map(([dependency]) => dependency);
496
+ lines.push("onlyBuiltDependencies:");
497
+ for (const dependency of allowedDependencies) {
498
+ lines.push(` - ${dependency}`);
499
+ }
500
+ }
501
+ return lines.join("\n");
502
+ }
503
+
504
+ function getPackageManagerProfile(spec) {
505
+ const major = getSemverMajor(spec.version);
506
+ if (spec.name === "pnpm") {
507
+ return getPnpmProfile(spec);
508
+ }
509
+ return {
510
+ ...spec,
511
+ major,
512
+ capabilities: {},
513
+ requirements: {}
514
+ };
515
+ }
516
+
517
+ function getPackageManagerSpec(packageManager) {
518
+ return packageManager ?? { name: "pnpm" };
519
+ }
520
+ function getPackageManagerName(packageManager) {
521
+ return getPackageManagerSpec(packageManager).name;
522
+ }
523
+ function formatPackageManager(packageManager) {
524
+ const spec = getPackageManagerSpec(packageManager);
525
+ return spec.version ? `${spec.name}@${spec.version}` : spec.name;
526
+ }
527
+
528
+ async function resolvePackageManager(options) {
529
+ const packageManager = getPackageManagerSpec(options.packageManager);
530
+ if (packageManager.version == null) {
531
+ if (packageManager.name === "pnpm") {
532
+ packageManager.version = await getLatestPnpmVersion();
533
+ } else if (packageManager.name === "yarn") {
534
+ packageManager.version = await getLatestYarnVersion();
535
+ } else if (packageManager.name === "npm") {
536
+ packageManager.version = await getLatestNpmCliVersion();
537
+ }
538
+ } else if (/^\d+$/.test(packageManager.version)) {
539
+ const fallback = `${packageManager.version}.0.0`;
540
+ packageManager.version = await getLatestNpmMajorVersion(
541
+ packageManager.name,
542
+ packageManager.version,
543
+ fallback
544
+ );
545
+ }
546
+ return packageManager;
547
+ }
548
+ async function resolvePackageManagerProfile(options) {
549
+ return getPackageManagerProfile(await resolvePackageManager(options));
550
+ }
551
+
552
+ function getEngineSpec(engine) {
553
+ return engine ?? { name: "node" };
554
+ }
555
+ function getEngineName(engine) {
556
+ return getEngineSpec(engine).name;
557
+ }
558
+ function formatEngine(engine) {
559
+ const spec = getEngineSpec(engine);
560
+ return spec.version ? `${spec.name}@${spec.version}` : spec.name;
561
+ }
562
+ function parseEngine(engines) {
563
+ if (engines == null) {
564
+ return void 0;
565
+ }
566
+ const [name, range] = Object.entries(engines).find(
567
+ ([engineName]) => engineName !== "npm" && engineName !== "pnpm" && engineName !== "yarn"
568
+ ) ?? [];
569
+ if (name == null) {
570
+ return void 0;
571
+ }
572
+ const version = range?.match(/(\d+(?:\.\d+(?:\.\d+)?)?)/)?.[1];
573
+ return { name, version };
574
+ }
575
+ async function resolveEngine(options) {
576
+ const engine = getEngineSpec(options.engine);
577
+ if ((engine.version == null || engine.version === "latest") && engine.name === "node") {
578
+ engine.version = await getLatestNodeVersion();
579
+ }
580
+ return engine;
581
+ }
582
+
583
+ function getLanguageFromTemplate(template) {
584
+ return template.endsWith("-js") ? "javascript" : "typescript";
585
+ }
586
+ function getBaseTemplate(template) {
587
+ return template.replace("-js", "");
588
+ }
589
+ function shouldEnableReactCompiler(options) {
590
+ const template = options.template ?? "vanilla";
591
+ return getBaseTemplate(template) === "react" && (options.projectType ?? "app") === "app";
592
+ }
593
+
594
+ const config = new Conf__default({
595
+ projectName: "create-krispya"
596
+ });
597
+ function getAiPlatforms() {
598
+ return config.get("aiPlatforms");
599
+ }
600
+ function setAiPlatforms(platforms) {
601
+ config.set("aiPlatforms", platforms);
602
+ }
603
+ function getConfigStrategy() {
604
+ return config.get("configStrategy") ?? "stealth";
605
+ }
606
+ function setConfigStrategy(strategy) {
607
+ config.set("configStrategy", strategy);
608
+ }
609
+ function clearConfig() {
610
+ config.clear();
611
+ }
612
+ function getConfigPath() {
613
+ return config.path;
614
+ }
615
+
616
+ const ALL_AI_PLATFORMS = ["agents", "claude"];
617
+ const AI_PLATFORM_LABELS = {
618
+ agents: "AGENTS.md",
619
+ claude: "CLAUDE.md"
620
+ };
621
+ const AI_PLATFORM_HINTS = {
622
+ agents: "OpenAI, Cursor, Windsurf, etc.",
623
+ claude: "Claude Code"
624
+ };
625
+ function renderAiFiles(files, params) {
626
+ const { platforms, isMonorepo, configStrategy, hasTypecheck, ...rest } = params;
627
+ if (platforms.length === 0) return;
628
+ const content = generateWorkspace({
629
+ ...rest,
630
+ isMonorepo: !!isMonorepo,
631
+ configStrategy: configStrategy ?? "stealth",
632
+ hasTypecheck: hasTypecheck ?? false
633
+ });
634
+ const pointer = "See [`AGENTS.md`](./Agents.md) for agent context.\n";
635
+ const hasAgents = platforms.includes("agents");
636
+ const hasClaude = platforms.includes("claude");
637
+ const isSingleton = platforms.length === 1;
638
+ if (hasAgents) files["AGENTS.md"] = { type: "text", content };
639
+ if (hasClaude) {
640
+ if (isSingleton) {
641
+ files["CLAUDE.md"] = { type: "text", content };
642
+ } else {
643
+ files["CLAUDE.md"] = { type: "text", content: pointer };
644
+ }
645
+ }
646
+ }
647
+ function generateWorkspace(ctx) {
648
+ const { packageManager, linter, formatter, hasTypecheck } = ctx;
649
+ const exampleFiles = "src/App.tsx src/core/systems/move-entity.ts";
650
+ const commands = getAfterEditingCommands(ctx, exampleFiles);
651
+ const sections = [
652
+ "# Workspace Tools",
653
+ "",
654
+ `- **Package Manager:** ${packageManager}`,
655
+ `- **Linter:** ${linter}`,
656
+ `- **Formatter:** ${formatter}`,
657
+ "",
658
+ "## After Editing",
659
+ ""
660
+ ];
661
+ if (hasTypecheck) {
662
+ sections.push(
663
+ "\u2705 After editing files, check the types for errors and then format and lint only the files changed for the current task."
664
+ );
665
+ } else {
666
+ sections.push(
667
+ "\u2705 After editing files, format and lint only the files changed for the current task."
668
+ );
669
+ }
670
+ sections.push("", "```sh", "# Example");
671
+ if (hasTypecheck) {
672
+ sections.push(runScript(packageManager, "typecheck"));
673
+ }
674
+ sections.push(
675
+ "# Run format and lint for only files modified",
676
+ commands.format,
677
+ commands.lint,
678
+ "```",
679
+ "",
680
+ "\u274C Avoid unless explicitly approved:",
681
+ "",
682
+ "```sh",
683
+ runScript(packageManager, "format"),
684
+ runScript(packageManager, "lint"),
685
+ "```",
686
+ ""
687
+ );
688
+ return sections.join("\n");
689
+ }
690
+ function getAfterEditingCommands(ctx, files) {
691
+ return {
692
+ format: getFormatChangedFilesCommand(ctx, files),
693
+ lint: getLintChangedFilesCommand(ctx, files)
694
+ };
695
+ }
696
+ function getFormatChangedFilesCommand(ctx, files) {
697
+ const exec = getExecCommand(ctx.packageManager);
698
+ if (ctx.formatter === "prettier") {
699
+ const configPath = getPrettierConfigPath(ctx);
700
+ const ignorePath = getPrettierIgnorePath(ctx);
701
+ const configFlag2 = configPath == null ? "" : ` --config ${configPath}`;
702
+ const ignoreFlag = ignorePath == null ? "" : ` --ignore-path ${ignorePath}`;
703
+ return `${exec} prettier${configFlag2}${ignoreFlag} --write ${files}`;
704
+ }
705
+ if (ctx.formatter === "oxfmt") {
706
+ const configPath = getOxfmtConfigPath(ctx);
707
+ return `${exec} oxfmt -c ${configPath} --write ${files}`;
708
+ }
709
+ const configFlag = ctx.isMonorepo || ctx.configStrategy === "root" ? "" : " --config-path .config";
710
+ return `${exec} biome format${configFlag} --write ${files}`;
389
711
  }
390
- async function detectFormatterFromConfig(root) {
391
- if (await pathExists(path.join(root, ".config/prettier"))) return "prettier";
392
- if (await pathExists(path.join(root, ".config/oxfmt"))) return "oxfmt";
393
- if (await pathExists(path.join(root, "biome.json"))) {
394
- try {
395
- const content = await promises.readFile(path.join(root, "biome.json"), "utf-8");
396
- const config = JSON.parse(content);
397
- if (config.formatter?.enabled !== false) return "biome";
398
- } catch {
399
- return "biome";
712
+ function getLintChangedFilesCommand(ctx, files) {
713
+ const exec = getExecCommand(ctx.packageManager);
714
+ if (ctx.linter === "oxlint") {
715
+ if (!ctx.isMonorepo) {
716
+ return runScript(ctx.packageManager, "lint", files);
400
717
  }
718
+ return `${exec} oxlint ${files}`;
401
719
  }
402
- return void 0;
720
+ if (ctx.linter === "eslint") {
721
+ const configFlag2 = ctx.configStrategy === "stealth" ? " --config .config/eslint.config.js" : "";
722
+ return `${exec} eslint${configFlag2} ${files}`;
723
+ }
724
+ const configFlag = ctx.isMonorepo || ctx.configStrategy === "root" ? "" : " --config-path .config";
725
+ return `${exec} biome lint${configFlag} ${files}`;
403
726
  }
404
- function detectLinterFromDeps(devDeps) {
405
- if (!devDeps) return void 0;
406
- if (devDeps["@biomejs/biome"]) return "biome";
407
- if (devDeps.eslint) return "eslint";
408
- if (devDeps.oxlint) return "oxlint";
727
+ function getPrettierConfigPath(ctx) {
728
+ if (ctx.isMonorepo) return ".config/prettier/base.json";
729
+ if (ctx.configStrategy === "stealth") return ".config/prettier.json";
409
730
  return void 0;
410
731
  }
411
- function detectFormatterFromDeps(devDeps) {
412
- if (!devDeps) return void 0;
413
- if (devDeps["@biomejs/biome"]) return "biome";
414
- if (devDeps.prettier) return "prettier";
415
- if (devDeps.oxfmt) return "oxfmt";
732
+ function getPrettierIgnorePath(ctx) {
733
+ if (ctx.isMonorepo) return ".config/prettier/prettierignore";
734
+ if (ctx.configStrategy === "stealth") return ".config/prettierignore";
416
735
  return void 0;
417
736
  }
418
- async function detectTooling(root) {
419
- try {
420
- const pkgPath = path.join(root, "package.json");
421
- const content = await promises.readFile(pkgPath, "utf-8");
422
- const pkg = JSON.parse(content);
423
- const linter = detectLinterFromScript(pkg.scripts?.lint) ?? await detectLinterFromConfig(root) ?? detectLinterFromDeps(pkg.devDependencies);
424
- const formatter = detectFormatterFromScript(pkg.scripts?.format) ?? await detectFormatterFromConfig(root) ?? detectFormatterFromDeps(pkg.devDependencies);
425
- return { linter, formatter };
426
- } catch {
427
- return { linter: void 0, formatter: void 0 };
428
- }
737
+ function getOxfmtConfigPath(ctx) {
738
+ if (ctx.isMonorepo) return ".config/oxfmt/base.json";
739
+ if (ctx.configStrategy === "stealth") return ".config/oxfmt.json";
740
+ return "oxfmt.json";
429
741
  }
430
-
431
- async function getLatestNpmVersion(packageName, fallback) {
432
- try {
433
- const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`);
434
- const data = await response.json();
435
- return data.version;
436
- } catch {
437
- return fallback;
742
+ function runScript(packageManager, script, args) {
743
+ const suffix = args == null ? "" : ` ${args}`;
744
+ if (packageManager === "npm") {
745
+ return `npm run ${script}${args == null ? "" : ` --${suffix}`}`;
438
746
  }
439
- }
440
- function compareNumericSemver(a, b) {
441
- const aParts = a.split(".").map((part) => Number.parseInt(part, 10) || 0);
442
- const bParts = b.split(".").map((part) => Number.parseInt(part, 10) || 0);
443
- const maxLength = Math.max(aParts.length, bParts.length);
444
- for (let index = 0; index < maxLength; index += 1) {
445
- const difference = (aParts[index] ?? 0) - (bParts[index] ?? 0);
446
- if (difference !== 0) {
447
- return difference;
448
- }
747
+ if (packageManager === "yarn") {
748
+ return `yarn ${script}${suffix}`;
449
749
  }
450
- return 0;
451
- }
452
- function getLatestMatchingMajorVersion(versions, majorVersion) {
453
- return [...versions].filter((version) => version.split(".")[0] === majorVersion).sort((a, b) => compareNumericSemver(b, a))[0];
750
+ return `${packageManager} ${script}${args == null ? "" : ` --${suffix}`}`;
454
751
  }
455
- async function getLatestNpmMajorVersion(packageName, majorVersion, fallback) {
456
- try {
457
- const response = await fetch(`https://registry.npmjs.org/${packageName}`);
458
- const data = await response.json();
459
- const latestMatchingVersion = getLatestMatchingMajorVersion(
460
- Object.keys(data.versions ?? {}),
461
- majorVersion
462
- );
463
- return latestMatchingVersion ?? fallback;
464
- } catch {
465
- return fallback;
466
- }
752
+ function getExecCommand(packageManager) {
753
+ if (packageManager === "npm") return "npm exec --";
754
+ if (packageManager === "yarn") return "yarn exec";
755
+ return `${packageManager} exec`;
467
756
  }
468
- async function getLatestNpmMajorVersionAtOrBelow(packageName, majorVersion, fallback) {
469
- try {
470
- const response = await fetch(`https://registry.npmjs.org/${packageName}`);
471
- const data = await response.json();
472
- const versions = Object.keys(data.versions ?? {});
473
- const requestedMajor = Number.parseInt(majorVersion, 10);
474
- if (Number.isFinite(requestedMajor)) {
475
- for (let major = requestedMajor; major >= 0; major -= 1) {
476
- const latestMatchingVersion = getLatestMatchingMajorVersion(versions, String(major));
477
- if (latestMatchingVersion != null) {
478
- return latestMatchingVersion;
479
- }
480
- }
757
+
758
+ async function checkAnyExists(paths) {
759
+ for (const path of paths) {
760
+ try {
761
+ await promises.access(path, promises.constants.F_OK);
762
+ return true;
763
+ } catch {
481
764
  }
482
- return fallback;
483
- } catch {
484
- return fallback;
485
765
  }
766
+ return false;
486
767
  }
487
- async function getLatestPnpmVersion() {
488
- return getLatestNpmVersion("pnpm", "10.11.0");
489
- }
490
- async function getLatestYarnVersion() {
491
- return getLatestNpmVersion("yarn", "4.6.0");
492
- }
493
- async function getLatestNpmCliVersion() {
494
- return getLatestNpmVersion("npm", "11.0.0");
495
- }
496
- async function getLatestNodeVersion() {
768
+ async function validateWorkspace(monorepoRoot) {
769
+ const errors = [];
770
+ const tsConfigPath = path.join(monorepoRoot, ".config/typescript/package.json");
497
771
  try {
498
- const response = await fetch("https://nodejs.org/dist/index.json");
499
- const data = await response.json();
500
- const latestVersion = data[0];
501
- if (latestVersion) {
502
- return latestVersion.version.replace(/^v/, "");
503
- }
504
- return "25.0.0";
772
+ await promises.access(tsConfigPath, promises.constants.F_OK);
505
773
  } catch {
506
- return "25.0.0";
774
+ errors.push("Missing .config/typescript package");
507
775
  }
508
- }
509
-
510
- function parseWorkspaceYamlContent(content) {
511
- const directories = [];
512
- let inPackagesSection = false;
513
- for (const line of content.split("\n")) {
514
- const trimmed = line.trim();
515
- if (trimmed === "packages:") {
516
- inPackagesSection = true;
517
- continue;
518
- }
519
- if (inPackagesSection && trimmed && !line.startsWith(" ") && !line.startsWith(" ") && !trimmed.startsWith("-")) {
520
- break;
521
- }
522
- if (inPackagesSection && trimmed.startsWith("-")) {
523
- const entry = trimmed.slice(1).trim().replace(/^["']|["']$/g, "").replace(/^\.\//, "").replace(/\/\*.*$/, "");
524
- if (entry && !entry.startsWith(".")) {
525
- directories.push(entry);
526
- }
527
- }
776
+ const linterPaths = [
777
+ path.join(monorepoRoot, ".config/oxlint/package.json"),
778
+ path.join(monorepoRoot, ".config/eslint/package.json"),
779
+ path.join(monorepoRoot, "eslint.config.js"),
780
+ path.join(monorepoRoot, "biome.json")
781
+ ];
782
+ const hasLinter = await checkAnyExists(linterPaths);
783
+ if (!hasLinter) {
784
+ errors.push(
785
+ "Missing linter config (.config/oxlint, .config/eslint, eslint.config.js, or biome.json)"
786
+ );
528
787
  }
529
- return directories;
788
+ const formatterPaths = [
789
+ path.join(monorepoRoot, ".config/oxfmt/package.json"),
790
+ path.join(monorepoRoot, ".config/prettier/package.json"),
791
+ path.join(monorepoRoot, ".prettierrc.json"),
792
+ path.join(monorepoRoot, "biome.json")
793
+ ];
794
+ const hasFormatter = await checkAnyExists(formatterPaths);
795
+ if (!hasFormatter) {
796
+ errors.push(
797
+ "Missing formatter config (.config/oxfmt, .config/prettier, .prettierrc.json, or biome.json)"
798
+ );
799
+ }
800
+ return { valid: errors.length === 0, errors };
530
801
  }
531
802
 
803
+ const gitAttributesContent = [
804
+ "* text eol=lf",
805
+ "*.png binary",
806
+ "*.jpg binary",
807
+ "*.jpeg binary",
808
+ "*.gif binary",
809
+ "*.ico binary",
810
+ "*.mov binary",
811
+ "*.mp4 binary",
812
+ "*.mp3 binary",
813
+ "*.flv binary",
814
+ "*.fla binary",
815
+ "*.wav binary",
816
+ "*.swf binary",
817
+ "*.gz binary",
818
+ "*.zip binary",
819
+ "*.7z binary",
820
+ "*.ttf binary",
821
+ "*.eot binary",
822
+ "*.woff binary",
823
+ "*.pyc binary",
824
+ "*.pdf binary",
825
+ "*.glb binary",
826
+ "*.gltf binary"
827
+ ].join("\n");
828
+
532
829
  const PACKAGE_VERSION_DEFINITIONS = {
533
830
  "@babel/core": { fallbackVersion: "7.29.0" },
534
831
  "@biomejs/biome": { fallbackVersion: "2.0.0" },
@@ -552,7 +849,7 @@ const PACKAGE_VERSION_DEFINITIONS = {
552
849
  "@vitejs/plugin-react": { fallbackVersion: "6.0.1" },
553
850
  "@viverse/cli": { fallbackVersion: "0.9.5-beta.8" },
554
851
  eslint: { fallbackVersion: "9.17.0" },
555
- "eslint-plugin-react-hooks": { fallbackVersion: "5.1.0" },
852
+ "eslint-plugin-react-hooks": { fallbackVersion: "7.1.1" },
556
853
  "babel-plugin-react-compiler": { fallbackVersion: "1.0.0" },
557
854
  jsdom: { fallbackVersion: "26.0.0" },
558
855
  koota: { fallbackVersion: "0.4.0" },
@@ -600,68 +897,6 @@ function formatResolvedPackageVersion(versions, packageName, prefix) {
600
897
  function assignResolvedPackageVersion(target, versions, packageName, prefix) {
601
898
  target[packageName] = formatResolvedPackageVersion(versions, packageName, prefix);
602
899
  }
603
- function getPackageManagerSpec(packageManager) {
604
- return packageManager ?? { name: "pnpm" };
605
- }
606
- function getPackageManagerName(packageManager) {
607
- return getPackageManagerSpec(packageManager).name;
608
- }
609
- function formatPackageManager(packageManager) {
610
- const spec = getPackageManagerSpec(packageManager);
611
- return spec.version ? `${spec.name}@${spec.version}` : spec.name;
612
- }
613
- function parsePackageManager(packageManager) {
614
- if (packageManager == null || packageManager.length === 0) {
615
- return void 0;
616
- }
617
- const atIndex = packageManager.indexOf("@");
618
- if (atIndex === -1) {
619
- return { name: packageManager };
620
- }
621
- return {
622
- name: packageManager.slice(0, atIndex),
623
- version: packageManager.slice(atIndex + 1)
624
- };
625
- }
626
- function getEngineSpec(engine) {
627
- return engine ?? { name: "node" };
628
- }
629
- function getEngineName(engine) {
630
- return getEngineSpec(engine).name;
631
- }
632
- function parseEngine(engines) {
633
- if (engines == null) {
634
- return void 0;
635
- }
636
- const [name, range] = Object.entries(engines).find(
637
- ([engineName]) => engineName !== "npm" && engineName !== "pnpm" && engineName !== "yarn"
638
- ) ?? [];
639
- if (name == null) {
640
- return void 0;
641
- }
642
- const version = range?.match(/(\d+(?:\.\d+(?:\.\d+)?)?)/)?.[1];
643
- return { name, version };
644
- }
645
- async function resolvePackageManager(options) {
646
- const packageManager = getPackageManagerSpec(options.packageManager);
647
- if (packageManager.version == null) {
648
- if (packageManager.name === "pnpm") {
649
- packageManager.version = await getLatestPnpmVersion();
650
- } else if (packageManager.name === "yarn") {
651
- packageManager.version = await getLatestYarnVersion();
652
- } else if (packageManager.name === "npm") {
653
- packageManager.version = await getLatestNpmCliVersion();
654
- }
655
- }
656
- return packageManager;
657
- }
658
- async function resolveEngine(options) {
659
- const engine = getEngineSpec(options.engine);
660
- if ((engine.version == null || engine.version === "latest") && engine.name === "node") {
661
- engine.version = await getLatestNodeVersion();
662
- }
663
- return engine;
664
- }
665
900
  function formatNodeTypesVersion(versions = {}, _engine) {
666
901
  const resolvedVersion = versions["@types/node"];
667
902
  if (resolvedVersion != null) {
@@ -678,7 +913,7 @@ async function resolveNodeTypesVersion(engine, versions = {}) {
678
913
  return void 0;
679
914
  }
680
915
  const nodeVersion = engineSpec.version ?? await getLatestNodeVersion();
681
- const majorVersion = nodeVersion.split(".")[0];
916
+ const majorVersion = getSemverMajorString(nodeVersion);
682
917
  return getLatestNpmMajorVersionAtOrBelow(
683
918
  "@types/node",
684
919
  majorVersion,
@@ -839,6 +1074,9 @@ function collectProjectPackageNames(options) {
839
1074
  if (isTypescript) {
840
1075
  addPackageName(packageNames, explicitVersions, "oxlint-tsgolint");
841
1076
  }
1077
+ if (useReactCompiler) {
1078
+ addPackageName(packageNames, explicitVersions, "eslint-plugin-react-hooks");
1079
+ }
842
1080
  }
843
1081
  } else if (linter === "biome") {
844
1082
  addPackageName(packageNames, explicitVersions, "@biomejs/biome");
@@ -1198,12 +1436,12 @@ function renderPackageJson(params) {
1198
1436
  if (!isMonorepoPackage) {
1199
1437
  const engines = {};
1200
1438
  if (packageManager.version != null) {
1201
- const majorVersion = packageManager.version.split(".")[0];
1439
+ const majorVersion = getSemverMajorString(packageManager.version);
1202
1440
  engines[packageManager.name] = `>=${majorVersion}.0.0`;
1203
1441
  packageJson.packageManager = formatPackageManager(packageManager);
1204
1442
  }
1205
1443
  if (engine.version != null) {
1206
- const majorVersion = engine.version.split(".")[0];
1444
+ const majorVersion = getSemverMajorString(engine.version);
1207
1445
  engines[engine.name] = `>=${majorVersion}.0.0`;
1208
1446
  }
1209
1447
  if (Object.keys(engines).length > 0) {
@@ -1215,15 +1453,12 @@ function renderPackageJson(params) {
1215
1453
  content: JSON.stringify(packageJson, null, 2)
1216
1454
  };
1217
1455
  if (isPnpm && !options.workspaceRoot) {
1218
- const manageVersions = options.pnpmManageVersions ?? true;
1219
- const workspaceLines = [];
1220
- if (manageVersions) {
1221
- workspaceLines.push("manage-package-manager-versions: true", "");
1222
- }
1223
- workspaceLines.push("onlyBuiltDependencies:", " - esbuild");
1224
1456
  files["pnpm-workspace.yaml"] = {
1225
1457
  type: "text",
1226
- content: workspaceLines.join("\n")
1458
+ content: renderPnpmWorkspaceConfig({
1459
+ profile: getPackageManagerProfile(packageManager),
1460
+ manageVersions: options.pnpmManageVersions ?? true
1461
+ })
1227
1462
  };
1228
1463
  }
1229
1464
  return { files };
@@ -1910,6 +2145,27 @@ const defaultLinterMetaConfig = {
1910
2145
  }
1911
2146
  };
1912
2147
 
2148
+ const REACT_HOOKS_JS_PLUGIN = {
2149
+ name: "react-hooks-js",
2150
+ specifier: "eslint-plugin-react-hooks"
2151
+ };
2152
+ const REACT_COMPILER_RULES = {
2153
+ "react-hooks-js/component-hook-factories": "error",
2154
+ "react-hooks-js/config": "error",
2155
+ "react-hooks-js/error-boundaries": "error",
2156
+ "react-hooks-js/gating": "error",
2157
+ "react-hooks-js/globals": "error",
2158
+ "react-hooks-js/immutability": "error",
2159
+ "react-hooks-js/incompatible-library": "warn",
2160
+ "react-hooks-js/preserve-manual-memoization": "warn",
2161
+ "react-hooks-js/purity": "error",
2162
+ "react-hooks-js/refs": "error",
2163
+ "react-hooks-js/set-state-in-effect": "error",
2164
+ "react-hooks-js/set-state-in-render": "error",
2165
+ "react-hooks-js/static-components": "error",
2166
+ "react-hooks-js/unsupported-syntax": "warn",
2167
+ "react-hooks-js/use-memo": "error"
2168
+ };
1913
2169
  function renderOxlintConfig(params) {
1914
2170
  const config = params.config ?? defaultLinterMetaConfig;
1915
2171
  const { rules } = config;
@@ -1920,6 +2176,7 @@ function renderOxlintConfig(params) {
1920
2176
  return {
1921
2177
  $schema: params.schemaPath,
1922
2178
  plugins,
2179
+ ...params.reactCompiler === true ? { jsPlugins: [REACT_HOOKS_JS_PLUGIN] } : {},
1923
2180
  ...params.typescript === true ? { options: { typeAware: true } } : {},
1924
2181
  rules: {
1925
2182
  "no-unused-vars": [
@@ -1934,7 +2191,8 @@ function renderOxlintConfig(params) {
1934
2191
  "no-unused-expressions": [
1935
2192
  rules.noUnusedExpressions.level,
1936
2193
  { allowShortCircuit: rules.noUnusedExpressions.allowShortCircuit }
1937
- ]
2194
+ ],
2195
+ ...params.reactCompiler === true ? REACT_COMPILER_RULES : {}
1938
2196
  },
1939
2197
  ignorePatterns: config.ignorePatterns
1940
2198
  };
@@ -2349,12 +2607,12 @@ function renderMonorepo(params) {
2349
2607
  };
2350
2608
  const engines = {};
2351
2609
  if (isPnpm && packageManager.version) {
2352
- const majorVersion = packageManager.version.split(".")[0];
2610
+ const majorVersion = getSemverMajorString(packageManager.version);
2353
2611
  engines.pnpm = `>=${majorVersion}.0.0`;
2354
2612
  rootPackageJson.packageManager = formatPackageManager(packageManager);
2355
2613
  }
2356
2614
  if (engine?.version) {
2357
- const majorVersion = engine.version.split(".")[0];
2615
+ const majorVersion = getSemverMajorString(engine.version);
2358
2616
  engines[engine.name] = `>=${majorVersion}.0.0`;
2359
2617
  }
2360
2618
  if (Object.keys(engines).length > 0) {
@@ -2365,15 +2623,13 @@ function renderMonorepo(params) {
2365
2623
  content: JSON.stringify(rootPackageJson, null, 2)
2366
2624
  };
2367
2625
  if (isPnpm) {
2368
- const workspaceLines = [];
2369
- if (pnpmManageVersions) {
2370
- workspaceLines.push("manage-package-manager-versions: true", "");
2371
- }
2372
- workspaceLines.push("packages:", ' - ".config/*"', ' - "apps/*"', ' - "packages/*"', "");
2373
- workspaceLines.push("onlyBuiltDependencies:", " - esbuild");
2374
2626
  files["pnpm-workspace.yaml"] = {
2375
2627
  type: "text",
2376
- content: workspaceLines.join("\n")
2628
+ content: renderPnpmWorkspaceConfig({
2629
+ profile: getPackageManagerProfile(packageManager),
2630
+ manageVersions: pnpmManageVersions,
2631
+ packages: [".config/*", "apps/*", "packages/*"]
2632
+ })
2377
2633
  };
2378
2634
  }
2379
2635
  files["tsconfig.json"] = {
@@ -2878,6 +3134,7 @@ function planOxlint(builder, options) {
2878
3134
  const baseTemplate = getBaseTemplate(template);
2879
3135
  const isTypescript = getLanguageFromTemplate(template) === "typescript";
2880
3136
  const isReact = baseTemplate === "react" || baseTemplate === "r3f";
3137
+ const useReactCompiler = shouldEnableReactCompiler(builder.options);
2881
3138
  const isMonorepo = builder.options.workspaceRoot != null;
2882
3139
  if (isMonorepo) {
2883
3140
  builder.addDevDependency("@config/oxlint", { version: "workspace:*" });
@@ -2888,10 +3145,14 @@ function planOxlint(builder, options) {
2888
3145
  if (isTypescript) {
2889
3146
  builder.addDevDependency("oxlint-tsgolint");
2890
3147
  }
3148
+ if (useReactCompiler) {
3149
+ builder.addDevDependency("eslint-plugin-react-hooks");
3150
+ }
2891
3151
  const isStealth = builder.isStealthConfig();
2892
3152
  const oxlintConfig = renderOxlintConfig({
2893
3153
  schemaPath: isStealth ? "../node_modules/oxlint/configuration_schema.json" : "./node_modules/oxlint/configuration_schema.json",
2894
3154
  react: isReact,
3155
+ reactCompiler: useReactCompiler,
2895
3156
  typescript: isTypescript,
2896
3157
  config: options.config
2897
3158
  });
@@ -3593,6 +3854,21 @@ async function resolveWorkspaceFacts(input) {
3593
3854
  });
3594
3855
  }
3595
3856
 
3857
+ function materializeJobs(jobs) {
3858
+ const files = {};
3859
+ for (const job of jobs) {
3860
+ if (job.type === "write-file") {
3861
+ files[job.path] = job.file;
3862
+ } else if (job.type === "merge-pnpm-workspace") {
3863
+ files[job.path] = {
3864
+ type: "text",
3865
+ content: job.content
3866
+ };
3867
+ }
3868
+ }
3869
+ return files;
3870
+ }
3871
+
3596
3872
  async function planProject(input) {
3597
3873
  const planInput = isProjectPlanInput(input) ? input : resolveProjectPlanInput(input);
3598
3874
  return createProjectPlan(await resolveProjectFacts(planInput));
@@ -3871,8 +4147,13 @@ function createProjectPlan(planInput) {
3871
4147
  platforms: planInput.aiAgents.config.platforms
3872
4148
  });
3873
4149
  }
4150
+ const jobs = Object.entries(files).map(([path, file]) => ({
4151
+ type: "write-file",
4152
+ path,
4153
+ file
4154
+ }));
3874
4155
  return {
3875
- files,
4156
+ files: materializeJobs(jobs),
3876
4157
  dependencies,
3877
4158
  devDependencies,
3878
4159
  peerDependencies,
@@ -3900,8 +4181,13 @@ async function planWorkspace(input) {
3900
4181
  const planInput = isWorkspacePlanInput(input) ? input : resolveWorkspacePlanInput(input);
3901
4182
  const resolvedInput = await resolveWorkspaceFacts(planInput);
3902
4183
  const { files } = renderMonorepo(workspacePlanInputToMonorepoParams(resolvedInput));
4184
+ const jobs = Object.entries(files).map(([path, file]) => ({
4185
+ type: "write-file",
4186
+ path,
4187
+ file
4188
+ }));
3903
4189
  return {
3904
- files,
4190
+ files: materializeJobs(jobs),
3905
4191
  dependencies: {},
3906
4192
  devDependencies: {},
3907
4193
  peerDependencies: {},
@@ -3917,11 +4203,22 @@ async function planWorkspace(input) {
3917
4203
  exports.AI_PLATFORM_HINTS = AI_PLATFORM_HINTS;
3918
4204
  exports.AI_PLATFORM_LABELS = AI_PLATFORM_LABELS;
3919
4205
  exports.ALL_AI_PLATFORMS = ALL_AI_PLATFORMS;
4206
+ exports.DEFAULT_MINIMUM_RELEASE_AGE_MINUTES = DEFAULT_MINIMUM_RELEASE_AGE_MINUTES;
4207
+ exports.assignResolvedPackageVersion = assignResolvedPackageVersion;
4208
+ exports.clearConfig = clearConfig;
4209
+ exports.compareNumericSemver = compareNumericSemver;
3920
4210
  exports.detectTooling = detectTooling;
4211
+ exports.formatEngine = formatEngine;
4212
+ exports.formatNodeTypesVersion = formatNodeTypesVersion;
4213
+ exports.formatPackageManager = formatPackageManager;
3921
4214
  exports.formatResolvedPackageVersion = formatResolvedPackageVersion;
3922
4215
  exports.generateRandomName = generateRandomName;
4216
+ exports.getAiPlatforms = getAiPlatforms;
3923
4217
  exports.getBaseTemplate = getBaseTemplate;
4218
+ exports.getConfigPath = getConfigPath;
4219
+ exports.getConfigStrategy = getConfigStrategy;
3924
4220
  exports.getEngineName = getEngineName;
4221
+ exports.getEngineSpec = getEngineSpec;
3925
4222
  exports.getLanguageFromTemplate = getLanguageFromTemplate;
3926
4223
  exports.getLatestNodeVersion = getLatestNodeVersion;
3927
4224
  exports.getLatestNpmCliVersion = getLatestNpmCliVersion;
@@ -3930,13 +4227,21 @@ exports.getLatestNpmMajorVersionAtOrBelow = getLatestNpmMajorVersionAtOrBelow;
3930
4227
  exports.getLatestNpmVersion = getLatestNpmVersion;
3931
4228
  exports.getLatestPnpmVersion = getLatestPnpmVersion;
3932
4229
  exports.getLatestYarnVersion = getLatestYarnVersion;
4230
+ exports.getPackageDirectoryName = getPackageDirectoryName;
4231
+ exports.getPackageFallbackVersion = getPackageFallbackVersion;
3933
4232
  exports.getPackageManagerName = getPackageManagerName;
4233
+ exports.getPackageManagerProfile = getPackageManagerProfile;
4234
+ exports.getPackageManagerSpec = getPackageManagerSpec;
3934
4235
  exports.getResolvedPackageVersion = getResolvedPackageVersion;
4236
+ exports.getSemverMajor = getSemverMajor;
4237
+ exports.getSemverMajorString = getSemverMajorString;
4238
+ exports.isPackageManagerName = isPackageManagerName;
4239
+ exports.materializeJobs = materializeJobs;
3935
4240
  exports.merge = merge;
3936
4241
  exports.mergePackageJsonScripts = mergePackageJsonScripts;
3937
4242
  exports.packageJsonScripts = packageJsonScripts;
3938
4243
  exports.parseEngine = parseEngine;
3939
- exports.parsePackageManager = parsePackageManager;
4244
+ exports.parsePackageManagerSpec = parsePackageManagerSpec;
3940
4245
  exports.parseWorkspaceYamlContent = parseWorkspaceYamlContent;
3941
4246
  exports.planProject = planProject;
3942
4247
  exports.planWorkspace = planWorkspace;
@@ -3948,17 +4253,28 @@ exports.renderGitignore = renderGitignore;
3948
4253
  exports.renderOxfmtConfigPackage = renderOxfmtConfigPackage;
3949
4254
  exports.renderOxlintConfig = renderOxlintConfig;
3950
4255
  exports.renderOxlintConfigPackage = renderOxlintConfigPackage;
4256
+ exports.renderPnpmWorkspaceConfig = renderPnpmWorkspaceConfig;
3951
4257
  exports.renderPrettierConfigPackage = renderPrettierConfigPackage;
3952
4258
  exports.renderTypescriptConfigPackage = renderTypescriptConfigPackage;
3953
4259
  exports.renderViteConfig = renderViteConfig;
3954
4260
  exports.renderVscodeFiles = renderVscodeFiles;
3955
4261
  exports.renderVscodeFiles$1 = renderVscodeFiles$1;
3956
4262
  exports.resolveDefaultPackageJsonScripts = resolveDefaultPackageJsonScripts;
4263
+ exports.resolveEngine = resolveEngine;
3957
4264
  exports.resolveMonorepoRootPackageVersions = resolveMonorepoRootPackageVersions;
4265
+ exports.resolvePackageManager = resolvePackageManager;
4266
+ exports.resolvePackageManagerProfile = resolvePackageManagerProfile;
4267
+ exports.resolvePackageVersions = resolvePackageVersions;
4268
+ exports.resolveProjectFacts = resolveProjectFacts;
4269
+ exports.resolveProjectPackageVersions = resolveProjectPackageVersions;
3958
4270
  exports.resolveProjectPlanInput = resolveProjectPlanInput;
4271
+ exports.resolveWorkspaceFacts = resolveWorkspaceFacts;
3959
4272
  exports.resolveWorkspacePlanInput = resolveWorkspacePlanInput;
4273
+ exports.setAiPlatforms = setAiPlatforms;
4274
+ exports.setConfigStrategy = setConfigStrategy;
3960
4275
  exports.shouldEnableReactCompiler = shouldEnableReactCompiler;
3961
4276
  exports.toPrettierIgnoreContent = toPrettierIgnoreContent;
3962
4277
  exports.unique = unique;
3963
4278
  exports.validatePackageName = validatePackageName;
4279
+ exports.validateWorkspace = validateWorkspace;
3964
4280
  exports.workspacePlanInputToMonorepoParams = workspacePlanInputToMonorepoParams;