wlmaker 1.3.0 → 1.4.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.
Files changed (2) hide show
  1. package/dist/cli.mjs +886 -148
  2. package/package.json +2 -1
package/dist/cli.mjs CHANGED
@@ -3,7 +3,7 @@
3
3
  // src/cli.ts
4
4
  import { createRequire } from "module";
5
5
  import { Command } from "commander";
6
- import chalk9 from "chalk";
6
+ import chalk10 from "chalk";
7
7
 
8
8
  // src/core/create-bloc.ts
9
9
  import * as fs3 from "fs";
@@ -956,11 +956,12 @@ async function createUseCase(name, tierInput, options) {
956
956
  }
957
957
 
958
958
  // src/interactive.ts
959
- import * as fs13 from "fs";
959
+ import * as fs14 from "fs";
960
960
  import * as os from "os";
961
- import * as path12 from "path";
961
+ import * as path13 from "path";
962
+ import { execSync as execSync2 } from "child_process";
962
963
  import * as clack from "@clack/prompts";
963
- import chalk8 from "chalk";
964
+ import chalk9 from "chalk";
964
965
 
965
966
  // src/core/create-endpoint.ts
966
967
  import * as fs9 from "fs";
@@ -1049,8 +1050,8 @@ ${paramsClass}
1049
1050
  }
1050
1051
  `;
1051
1052
  }
1052
- function retrofitMethod(methodName, path13, httpMethod, params, returnType) {
1053
- const httpAnnotation = `@${httpMethod.toUpperCase()}('${path13}')`;
1053
+ function retrofitMethod(methodName, path14, httpMethod, params, returnType) {
1054
+ const httpAnnotation = `@${httpMethod.toUpperCase()}('${path14}')`;
1054
1055
  const paramList = params.map((p) => {
1055
1056
  if (p.isPath) return `@Path('${p.name}') ${p.type} ${p.name}`;
1056
1057
  if (p.isBody) return `@Body() ${p.type} ${p.name}`;
@@ -1211,15 +1212,15 @@ async function createEndpoint(options) {
1211
1212
  const methodParamsForSignature = methodParams.map((p) => ({ name: p.name, type: p.type }));
1212
1213
  const modelType = `${pascal}Model`;
1213
1214
  const entityType = `${pascal}Entity`;
1214
- const spinner2 = (msg) => console.log(chalk4.cyan(` \u2192 ${msg}`));
1215
- spinner2("Generating entity");
1215
+ const spinner3 = (msg) => console.log(chalk4.cyan(` \u2192 ${msg}`));
1216
+ spinner3("Generating entity");
1216
1217
  const entityDir = path8.join(lib, "domain", "entities", feature);
1217
1218
  fs9.mkdirSync(entityDir, { recursive: true });
1218
1219
  fs9.writeFileSync(
1219
1220
  path8.join(entityDir, `${useCaseSnake}_entity.dart`),
1220
1221
  entityBoilerplate(pascal)
1221
1222
  );
1222
- spinner2("Generating model");
1223
+ spinner3("Generating model");
1223
1224
  const modelDir = path8.join(lib, "data", "models", feature);
1224
1225
  fs9.mkdirSync(modelDir, { recursive: true });
1225
1226
  fs9.writeFileSync(
@@ -1227,7 +1228,7 @@ async function createEndpoint(options) {
1227
1228
  modelBoilerplate(useCaseSnake, pascal, options.projectName)
1228
1229
  );
1229
1230
  if (needsBody) {
1230
- spinner2("Generating request model");
1231
+ spinner3("Generating request model");
1231
1232
  const reqModelDir = path8.join(lib, "data", "models", feature);
1232
1233
  fs9.mkdirSync(reqModelDir, { recursive: true });
1233
1234
  fs9.writeFileSync(
@@ -1235,7 +1236,7 @@ async function createEndpoint(options) {
1235
1236
  requestModelBoilerplate(useCaseSnake, pascal)
1236
1237
  );
1237
1238
  }
1238
- spinner2("Injecting Retrofit method");
1239
+ spinner3("Injecting Retrofit method");
1239
1240
  const bffPath = path8.resolve(options.bffApiFile);
1240
1241
  if (fs9.existsSync(bffPath)) {
1241
1242
  const retrofitParams = buildRetrofitParams(options, pathParams, needsBody);
@@ -1250,7 +1251,7 @@ async function createEndpoint(options) {
1250
1251
  } else {
1251
1252
  console.log(chalk4.yellow(` \u26A0 BFF API file not found: ${bffPath}`));
1252
1253
  }
1253
- spinner2("Injecting datasource method");
1254
+ spinner3("Injecting datasource method");
1254
1255
  const dsPath = path8.resolve(datasourceFile);
1255
1256
  if (fs9.existsSync(dsPath)) {
1256
1257
  const dsMethod = datasourceMethod(
@@ -1262,7 +1263,7 @@ async function createEndpoint(options) {
1262
1263
  } else {
1263
1264
  console.log(chalk4.yellow(` \u26A0 Datasource file not found: ${dsPath}`));
1264
1265
  }
1265
- spinner2("Injecting repository interface method");
1266
+ spinner3("Injecting repository interface method");
1266
1267
  const repoIfacePath = path8.resolve(repositoryInterfaceFile);
1267
1268
  if (fs9.existsSync(repoIfacePath)) {
1268
1269
  const ifaceMethod = repositoryInterfaceMethod(
@@ -1274,7 +1275,7 @@ async function createEndpoint(options) {
1274
1275
  } else {
1275
1276
  console.log(chalk4.yellow(` \u26A0 Repository interface not found: ${repoIfacePath}`));
1276
1277
  }
1277
- spinner2("Injecting repository implementation method");
1278
+ spinner3("Injecting repository implementation method");
1278
1279
  const repoImplPath = path8.resolve(repositoryImplFile);
1279
1280
  if (fs9.existsSync(repoImplPath)) {
1280
1281
  const dsVarName = camelCase2(datasourceClassName);
@@ -1289,7 +1290,7 @@ async function createEndpoint(options) {
1289
1290
  } else {
1290
1291
  console.log(chalk4.yellow(` \u26A0 Repository implementation not found: ${repoImplPath}`));
1291
1292
  }
1292
- spinner2("Generating UseCase");
1293
+ spinner3("Generating UseCase");
1293
1294
  const useCaseDir = path8.join(lib, "domain", "usecases", feature);
1294
1295
  fs9.mkdirSync(useCaseDir, { recursive: true });
1295
1296
  fs9.writeFileSync(
@@ -1303,7 +1304,7 @@ async function createEndpoint(options) {
1303
1304
  options.projectName
1304
1305
  )
1305
1306
  );
1306
- spinner2("Updating barrel files");
1307
+ spinner3("Updating barrel files");
1307
1308
  const dsFileName = path8.basename(datasourceFile);
1308
1309
  injectExport(
1309
1310
  path8.join(lib, "data", "datasources", "datasources.dart"),
@@ -1331,7 +1332,7 @@ async function createEndpoint(options) {
1331
1332
  `export '${feature}/${feature}.dart';`
1332
1333
  );
1333
1334
  if (options.diTarget && options.diTarget !== "none") {
1334
- spinner2("Registering in DI modules");
1335
+ spinner3("Registering in DI modules");
1335
1336
  const appBaseDir = findAppBasePackageDir(options.projectRoot, options.diTarget);
1336
1337
  if (appBaseDir) {
1337
1338
  const domainPascal = pascalCase4(domain);
@@ -1464,24 +1465,594 @@ function injectModuleRegistration(filePath, className, registrationCode, dedupKe
1464
1465
  fs9.writeFileSync(filePath, newContent);
1465
1466
  }
1466
1467
 
1467
- // src/core/docs-serve.ts
1468
- import { spawn as spawn2 } from "child_process";
1468
+ // src/core/create-env-var.ts
1469
1469
  import * as fs10 from "fs";
1470
1470
  import * as path9 from "path";
1471
+ import { execSync } from "child_process";
1471
1472
  import chalk5 from "chalk";
1473
+ import { camelCase as camelCase3 } from "change-case";
1474
+ function screamingSnakeToCamel(name) {
1475
+ return camelCase3(name.toLowerCase());
1476
+ }
1477
+ function dartTypeForGetter(type) {
1478
+ return type;
1479
+ }
1480
+ function typedJsonValue(defaultValue, dartType) {
1481
+ if (!defaultValue || !defaultValue.trim()) {
1482
+ switch (dartType) {
1483
+ case "int":
1484
+ return 0;
1485
+ case "bool":
1486
+ return false;
1487
+ case "List<String>":
1488
+ return [];
1489
+ default:
1490
+ return "";
1491
+ }
1492
+ }
1493
+ switch (dartType) {
1494
+ case "int":
1495
+ return parseInt(defaultValue, 10);
1496
+ case "bool":
1497
+ return defaultValue === "true";
1498
+ case "List<String>":
1499
+ return defaultValue.split(",").map((s) => s.trim());
1500
+ default:
1501
+ return defaultValue;
1502
+ }
1503
+ }
1504
+ function abstractGetterCode(camelName, dartType) {
1505
+ const type = dartTypeForGetter(dartType);
1506
+ return `${type} get ${camelName};`;
1507
+ }
1508
+ function buildEnvGetterCode(camelName, constName, dartType) {
1509
+ const type = dartTypeForGetter(dartType);
1510
+ switch (dartType) {
1511
+ case "String":
1512
+ return `@override
1513
+ String get ${camelName} => const String.fromEnvironment('${constName}');`;
1514
+ case "int":
1515
+ return `@override
1516
+ int get ${camelName} => const int.fromEnvironment('${constName}');`;
1517
+ case "bool":
1518
+ return `@override
1519
+ bool get ${camelName} => const bool.fromEnvironment('${constName}');`;
1520
+ case "List<String>":
1521
+ return [
1522
+ "@override",
1523
+ `List<String> get ${camelName} {`,
1524
+ ` const rawString = String.fromEnvironment('${constName}');`,
1525
+ " if (rawString.isEmpty) return [];",
1526
+ " return rawString.split(',').map((e) => e.trim()).toList();",
1527
+ "}"
1528
+ ].join("\n");
1529
+ }
1530
+ }
1531
+ function appConfigDefaultValue(dartType) {
1532
+ switch (dartType) {
1533
+ case "String":
1534
+ return "''";
1535
+ case "int":
1536
+ return "0";
1537
+ case "bool":
1538
+ return "false";
1539
+ case "List<String>":
1540
+ return "const []";
1541
+ }
1542
+ }
1543
+ function appConfigModelJsonKey(camelName, dartType) {
1544
+ switch (dartType) {
1545
+ case "bool":
1546
+ return `@override
1547
+ @JsonKey(name: '${camelName}', fromJson: AppConfigModel.parseBool)`;
1548
+ case "int":
1549
+ return `@override
1550
+ @JsonKey(name: '${camelName}', fromJson: AppConfigModel.parseInt)`;
1551
+ case "List<String>":
1552
+ return `@override
1553
+ @JsonKey(name: '${camelName}', fromJson: AppConfigModel.parseToList)`;
1554
+ default:
1555
+ return `@override
1556
+ @JsonKey(name: '${camelName}')`;
1557
+ }
1558
+ }
1559
+ function discoverAppsWithEnv(monorepoRoot) {
1560
+ const appsDir = path9.join(monorepoRoot, "apps");
1561
+ if (!fs10.existsSync(appsDir)) return [];
1562
+ return fs10.readdirSync(appsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).filter((d) => fs10.existsSync(path9.join(appsDir, d.name, "env"))).map((d) => d.name).sort();
1563
+ }
1564
+ function injectIntoJsonFile(filePath, key, value, isTemplate) {
1565
+ if (!fs10.existsSync(filePath)) {
1566
+ console.log(chalk5.yellow(` Skipping ${filePath} (not found)`));
1567
+ return;
1568
+ }
1569
+ const content = fs10.readFileSync(filePath, "utf8");
1570
+ const json = JSON.parse(content);
1571
+ if (key in json) {
1572
+ console.log(chalk5.yellow(` Key "${key}" already exists in ${path9.basename(filePath)}`));
1573
+ return;
1574
+ }
1575
+ json[key] = isTemplate ? `\${${key}}` : value;
1576
+ fs10.writeFileSync(filePath, JSON.stringify(json, null, 2) + "\n");
1577
+ console.log(chalk5.green(` Updated ${path9.basename(filePath)}`));
1578
+ }
1579
+ function injectAbstractGetter(monorepoRoot, camelName, dartType) {
1580
+ const packagesDir = path9.join(monorepoRoot, "packages");
1581
+ if (!fs10.existsSync(packagesDir)) return;
1582
+ const envFile = findFileInPackages(packagesDir, "app_environment.dart");
1583
+ if (!envFile) {
1584
+ console.log(chalk5.yellow(" app_environment.dart not found in any package"));
1585
+ return;
1586
+ }
1587
+ const content = fs10.readFileSync(envFile, "utf8");
1588
+ const dedup = `get ${camelName}`;
1589
+ if (content.includes(dedup)) {
1590
+ console.log(chalk5.yellow(` '${camelName}' already in AppEnvironment`));
1591
+ return;
1592
+ }
1593
+ const getterRegex = /\n\s+\w+(?:<[^>]+>)?\s+get\s+\w+;/g;
1594
+ let lastGetterMatch = null;
1595
+ let match;
1596
+ while ((match = getterRegex.exec(content)) !== null) {
1597
+ lastGetterMatch = match;
1598
+ }
1599
+ if (!lastGetterMatch) {
1600
+ console.log(chalk5.yellow(" No getters found in AppEnvironment"));
1601
+ return;
1602
+ }
1603
+ const insertPos = lastGetterMatch.index + lastGetterMatch[0].length;
1604
+ const getterLine = `
1605
+ ${abstractGetterCode(camelName, dartType)}`;
1606
+ const newContent = content.slice(0, insertPos) + getterLine + content.slice(insertPos);
1607
+ fs10.writeFileSync(envFile, newContent);
1608
+ console.log(chalk5.green(` Injected abstract getter in ${formatPkgPath(monorepoRoot, envFile)}`));
1609
+ }
1610
+ function injectBuildEnvGetter(monorepoRoot, variableName, camelName, dartType) {
1611
+ const packagesDir = path9.join(monorepoRoot, "packages");
1612
+ if (!fs10.existsSync(packagesDir)) return;
1613
+ const buildEnvFile = findFileInPackages(packagesDir, "app_build_environment.dart");
1614
+ if (!buildEnvFile) {
1615
+ console.log(chalk5.yellow(" app_build_environment.dart not found in any package"));
1616
+ return;
1617
+ }
1618
+ const code = buildEnvGetterCode(camelName, variableName, dartType);
1619
+ try {
1620
+ injectMethod(buildEnvFile, "AppBuildEnvironment", code, `get ${camelName}`);
1621
+ console.log(chalk5.green(` Injected build env getter in ${formatPkgPath(monorepoRoot, buildEnvFile)}`));
1622
+ } catch (e) {
1623
+ console.log(chalk5.yellow(` Skipped: ${e.message}`));
1624
+ }
1625
+ }
1626
+ function injectIntoRemoteConfigDefaults(vendorsPath, camelName, dartType) {
1627
+ const content = fs10.readFileSync(vendorsPath, "utf8");
1628
+ if (content.includes(`'${camelName}'`) || content.includes(`"${camelName}"`)) {
1629
+ console.log(chalk5.yellow(` '${camelName}' already in ${path9.basename(vendorsPath)}`));
1630
+ return;
1631
+ }
1632
+ const defaultValuesRegex = /final\s+defaultValues\s*=\s*(?:<String,\s*dynamic>\s*)?\{/;
1633
+ const match = content.match(defaultValuesRegex);
1634
+ if (!match) {
1635
+ console.log(chalk5.yellow(` Could not find defaultValues map in ${path9.basename(vendorsPath)}`));
1636
+ return;
1637
+ }
1638
+ const mapStart = content.indexOf(match[0]) + match[0].length;
1639
+ let braceCount = 1;
1640
+ let mapEnd = -1;
1641
+ for (let i = mapStart; i < content.length; i++) {
1642
+ if (content[i] === "{") braceCount++;
1643
+ else if (content[i] === "}") {
1644
+ braceCount--;
1645
+ if (braceCount === 0) {
1646
+ mapEnd = i;
1647
+ break;
1648
+ }
1649
+ }
1650
+ }
1651
+ if (mapEnd === -1) {
1652
+ console.log(chalk5.yellow(` Could not find end of defaultValues map`));
1653
+ return;
1654
+ }
1655
+ const valueExpr = dartType === "List<String>" ? `jsonEncode(env.${camelName})` : `env.${camelName}`;
1656
+ const beforeClose = content.slice(0, mapEnd);
1657
+ const mapLines = beforeClose.split("\n");
1658
+ let lastEntryIndent = " ";
1659
+ for (let i = mapLines.length - 1; i >= 0; i--) {
1660
+ const trimmed = mapLines[i].trim();
1661
+ if (trimmed.startsWith("'") || trimmed.startsWith('"')) {
1662
+ const leadingSpaces = mapLines[i].match(/^(\s*)/)?.[1] ?? "";
1663
+ lastEntryIndent = leadingSpaces;
1664
+ break;
1665
+ }
1666
+ }
1667
+ const insertion = `${lastEntryIndent}'${camelName}': ${valueExpr},
1668
+ `;
1669
+ const newContent = content.slice(0, mapEnd) + insertion + content.slice(mapEnd);
1670
+ fs10.writeFileSync(vendorsPath, newContent);
1671
+ console.log(chalk5.green(` Injected into VendorsModule defaultValues`));
1672
+ }
1673
+ function injectAppConfigProperty(configPath, camelName, dartType) {
1674
+ const content = fs10.readFileSync(configPath, "utf8");
1675
+ if (content.includes(`final ${dartTypeForGetter(dartType)} ${camelName}`)) {
1676
+ console.log(chalk5.yellow(` '${camelName}' already in AppConfig`));
1677
+ return;
1678
+ }
1679
+ const fieldLine = ` final ${dartTypeForGetter(dartType)} ${camelName};
1680
+ `;
1681
+ let newContent = content;
1682
+ const propsMatch = newContent.match(/@override\n\s+List<Object\?>/);
1683
+ if (propsMatch) {
1684
+ const propsPos = newContent.indexOf(propsMatch[0]);
1685
+ newContent = newContent.slice(0, propsPos) + fieldLine + newContent.slice(propsPos);
1686
+ } else {
1687
+ newContent = injectBeforeClassClose(newContent, "AppConfig", ` final ${dartTypeForGetter(dartType)} ${camelName};`);
1688
+ }
1689
+ newContent = injectConstructorParam(newContent, "AppConfig", camelName, dartType);
1690
+ newContent = injectIntoPropsList(newContent, camelName);
1691
+ fs10.writeFileSync(configPath, newContent);
1692
+ console.log(chalk5.green(` Injected into AppConfig entity`));
1693
+ }
1694
+ function injectAppConfigModelProperty(modelPath, camelName, dartType) {
1695
+ const content = fs10.readFileSync(modelPath, "utf8");
1696
+ if (content.includes(`final ${dartTypeForGetter(dartType)} ${camelName}`)) {
1697
+ console.log(chalk5.yellow(` '${camelName}' already in AppConfigModel`));
1698
+ return;
1699
+ }
1700
+ const jsonKey = appConfigModelJsonKey(camelName, dartType);
1701
+ const fieldLines = jsonKey ? ` ${jsonKey}
1702
+ final ${dartTypeForGetter(dartType)} ${camelName};
1703
+ ` : ` final ${dartTypeForGetter(dartType)} ${camelName};
1704
+ `;
1705
+ let newContent = content;
1706
+ const staticPos = newContent.search(/\n\s+static\s/);
1707
+ const factoryPos = newContent.search(/\n\s+factory\s+AppConfigModel/);
1708
+ let insertAnchor = -1;
1709
+ if (staticPos !== -1 && factoryPos !== -1) {
1710
+ insertAnchor = Math.min(staticPos, factoryPos);
1711
+ } else if (staticPos !== -1) {
1712
+ insertAnchor = staticPos;
1713
+ } else if (factoryPos !== -1) {
1714
+ insertAnchor = factoryPos;
1715
+ }
1716
+ if (insertAnchor !== -1) {
1717
+ newContent = newContent.slice(0, insertAnchor + 1) + fieldLines + newContent.slice(insertAnchor + 1);
1718
+ } else {
1719
+ newContent = injectBeforeClassClose(newContent, "AppConfigModel", fieldLines);
1720
+ }
1721
+ newContent = injectConstructorParam(newContent, "AppConfigModel", camelName, dartType);
1722
+ newContent = injectIntoSuperCall(newContent, camelName);
1723
+ fs10.writeFileSync(modelPath, newContent);
1724
+ console.log(chalk5.green(` Injected into AppConfigModel`));
1725
+ }
1726
+ function injectBeforeClassClose(content, className, line) {
1727
+ const classRegex = new RegExp(`class\\s+${className}\\s*[^{]*\\{`);
1728
+ const classMatch = content.match(classRegex);
1729
+ if (!classMatch) {
1730
+ throw new Error(`Class "${className}" not found`);
1731
+ }
1732
+ const classStart = content.indexOf(classMatch[0]);
1733
+ let braceCount = 0;
1734
+ let classEnd = -1;
1735
+ let foundOpen = false;
1736
+ for (let i = classStart; i < content.length; i++) {
1737
+ if (content[i] === "{") {
1738
+ braceCount++;
1739
+ foundOpen = true;
1740
+ } else if (content[i] === "}") {
1741
+ braceCount--;
1742
+ if (foundOpen && braceCount === 0) {
1743
+ classEnd = i;
1744
+ break;
1745
+ }
1746
+ }
1747
+ }
1748
+ if (classEnd === -1) {
1749
+ throw new Error(`Could not find closing brace for class "${className}"`);
1750
+ }
1751
+ return content.slice(0, classEnd) + line + "\n" + content.slice(classEnd);
1752
+ }
1753
+ function injectConstructorParam(content, className, camelName, dartType) {
1754
+ const classRegex = new RegExp(`class\\s+${className}\\s*[^{]*\\{`);
1755
+ const classMatch = content.match(classRegex);
1756
+ if (!classMatch) return content;
1757
+ const classStart = content.indexOf(classMatch[0]);
1758
+ const constructorPattern = new RegExp(
1759
+ `(?:const\\s+)?${className}\\s*\\(`
1760
+ );
1761
+ const searchFrom = classStart;
1762
+ const constructorMatch = content.slice(searchFrom).match(constructorPattern);
1763
+ if (!constructorMatch) return content;
1764
+ const constructorAbsStart = searchFrom + constructorMatch.index;
1765
+ const parenStart = content.indexOf("(", constructorAbsStart);
1766
+ const afterParen = content.slice(parenStart + 1).trimStart();
1767
+ if (afterParen.startsWith("{")) {
1768
+ const braceOffset = content.slice(parenStart + 1).indexOf("{");
1769
+ const braceStart = parenStart + 1 + braceOffset;
1770
+ let braceCount = 1;
1771
+ let braceEnd = -1;
1772
+ for (let i = braceStart + 1; i < content.length; i++) {
1773
+ if (content[i] === "{") braceCount++;
1774
+ else if (content[i] === "}") {
1775
+ braceCount--;
1776
+ if (braceCount === 0) {
1777
+ braceEnd = i;
1778
+ break;
1779
+ }
1780
+ }
1781
+ }
1782
+ if (braceEnd === -1) return content;
1783
+ const beforeClose = content.slice(0, braceEnd);
1784
+ const paramLines = beforeClose.split("\n");
1785
+ let lastParamIndent = " ";
1786
+ for (let i = paramLines.length - 1; i >= 0; i--) {
1787
+ const trimmed = paramLines[i].trim();
1788
+ if (trimmed.startsWith("this.") || trimmed.startsWith("required")) {
1789
+ const leadingSpaces = paramLines[i].match(/^(\s*)/)?.[1] ?? "";
1790
+ lastParamIndent = leadingSpaces;
1791
+ break;
1792
+ }
1793
+ }
1794
+ const defaultVal = appConfigDefaultValue(dartType);
1795
+ const insertion = `${lastParamIndent}this.${camelName} = ${defaultVal},
1796
+ `;
1797
+ return content.slice(0, braceEnd) + insertion + content.slice(braceEnd);
1798
+ } else {
1799
+ let parenCount = 1;
1800
+ let parenEnd = -1;
1801
+ for (let i = parenStart + 1; i < content.length; i++) {
1802
+ if (content[i] === "(") parenCount++;
1803
+ else if (content[i] === ")") {
1804
+ parenCount--;
1805
+ if (parenCount === 0) {
1806
+ parenEnd = i;
1807
+ break;
1808
+ }
1809
+ }
1810
+ }
1811
+ if (parenEnd === -1) return content;
1812
+ const beforeClose = content.slice(0, parenEnd);
1813
+ const paramLines = beforeClose.split("\n");
1814
+ let lastParamIndent = " ";
1815
+ for (let i = paramLines.length - 1; i >= 0; i--) {
1816
+ const trimmed = paramLines[i].trim();
1817
+ if (trimmed.length > 0) {
1818
+ const leadingSpaces = paramLines[i].match(/^(\s*)/)?.[1] ?? "";
1819
+ lastParamIndent = leadingSpaces;
1820
+ break;
1821
+ }
1822
+ }
1823
+ const defaultVal = appConfigDefaultValue(dartType);
1824
+ const insertion = `${lastParamIndent}this.${camelName} = ${defaultVal},
1825
+ `;
1826
+ return content.slice(0, parenEnd) + insertion + content.slice(parenEnd);
1827
+ }
1828
+ }
1829
+ function injectIntoPropsList(content, camelName) {
1830
+ const propsRegex = /List<Object\?>\s+get\s+props\s*=>\s*\[/;
1831
+ const match = content.match(propsRegex);
1832
+ if (!match) return content;
1833
+ const propsStart = content.indexOf(match[0]) + match[0].length;
1834
+ let bracketCount = 1;
1835
+ let bracketEnd = -1;
1836
+ for (let i = propsStart; i < content.length; i++) {
1837
+ if (content[i] === "[") bracketCount++;
1838
+ else if (content[i] === "]") {
1839
+ bracketCount--;
1840
+ if (bracketCount === 0) {
1841
+ bracketEnd = i;
1842
+ break;
1843
+ }
1844
+ }
1845
+ }
1846
+ if (bracketEnd === -1) return content;
1847
+ const beforeClose = content.slice(0, bracketEnd);
1848
+ const propLines = beforeClose.split("\n");
1849
+ let lastPropIndent = " ";
1850
+ for (let i = propLines.length - 1; i >= 0; i--) {
1851
+ const trimmed = propLines[i].trim();
1852
+ if (trimmed.length > 0 && trimmed !== "[") {
1853
+ const leadingSpaces = propLines[i].match(/^(\s*)/)?.[1] ?? "";
1854
+ lastPropIndent = leadingSpaces;
1855
+ break;
1856
+ }
1857
+ }
1858
+ const insertion = `${lastPropIndent}${camelName},
1859
+ `;
1860
+ return content.slice(0, bracketEnd) + insertion + content.slice(bracketEnd);
1861
+ }
1862
+ function injectIntoSuperCall(content, camelName) {
1863
+ const superRegex = /super\s*\(\s*/;
1864
+ const match = content.match(superRegex);
1865
+ if (!match) return content;
1866
+ const superStart = content.indexOf(match[0]) + match[0].length;
1867
+ let parenCount = 1;
1868
+ let parenEnd = -1;
1869
+ for (let i = superStart; i < content.length; i++) {
1870
+ if (content[i] === "(") parenCount++;
1871
+ else if (content[i] === ")") {
1872
+ parenCount--;
1873
+ if (parenCount === 0) {
1874
+ parenEnd = i;
1875
+ break;
1876
+ }
1877
+ }
1878
+ }
1879
+ if (parenEnd === -1) return content;
1880
+ const beforeClose = content.slice(0, parenEnd);
1881
+ const superLines = beforeClose.split("\n");
1882
+ let lastEntryIndent = " ";
1883
+ for (let i = superLines.length - 1; i >= 0; i--) {
1884
+ const trimmed = superLines[i].trim();
1885
+ if (trimmed.length > 0 && trimmed.includes(":")) {
1886
+ const leadingSpaces = superLines[i].match(/^(\s*)/)?.[1] ?? "";
1887
+ lastEntryIndent = leadingSpaces;
1888
+ break;
1889
+ }
1890
+ }
1891
+ const insertion = `${lastEntryIndent}${camelName}: ${camelName},
1892
+ `;
1893
+ return content.slice(0, parenEnd) + insertion + content.slice(parenEnd);
1894
+ }
1895
+ function findFileRecursive(dir, fileName) {
1896
+ const entries = fs10.readdirSync(dir, { withFileTypes: true });
1897
+ for (const entry of entries) {
1898
+ const fullPath = path9.join(dir, entry.name);
1899
+ if (entry.isDirectory()) {
1900
+ const result = findFileRecursive(fullPath, fileName);
1901
+ if (result) return result;
1902
+ } else if (entry.name === fileName) {
1903
+ return fullPath;
1904
+ }
1905
+ }
1906
+ return null;
1907
+ }
1908
+ function discoverVendorsModules(monorepoRoot) {
1909
+ const packagesDir = path9.join(monorepoRoot, "packages");
1910
+ if (!fs10.existsSync(packagesDir)) return [];
1911
+ const results = [];
1912
+ const packages = fs10.readdirSync(packagesDir, { withFileTypes: true }).filter((d) => d.isDirectory());
1913
+ for (const pkg2 of packages) {
1914
+ const pkgDir = path9.join(packagesDir, pkg2.name);
1915
+ const found = findFileRecursive(pkgDir, "vendors_module.dart");
1916
+ if (found) results.push(pkg2.name);
1917
+ }
1918
+ return results.sort();
1919
+ }
1920
+ function findAppConfigFile(monorepoRoot) {
1921
+ const packagesDir = path9.join(monorepoRoot, "packages");
1922
+ if (!fs10.existsSync(packagesDir)) return null;
1923
+ return findClassInPackages(packagesDir, "AppConfig");
1924
+ }
1925
+ function findAppConfigModelFile(monorepoRoot) {
1926
+ const packagesDir = path9.join(monorepoRoot, "packages");
1927
+ if (!fs10.existsSync(packagesDir)) return null;
1928
+ return findClassInPackages(packagesDir, "AppConfigModel");
1929
+ }
1930
+ function findClassInPackages(packagesDir, className) {
1931
+ const packages = fs10.readdirSync(packagesDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => path9.join(packagesDir, d.name));
1932
+ for (const pkgDir of packages) {
1933
+ const found = findClassFileRecursive(pkgDir, className);
1934
+ if (found) return found;
1935
+ }
1936
+ return null;
1937
+ }
1938
+ function findClassFileRecursive(dir, className) {
1939
+ const classRegex = new RegExp(`\\bclass\\s+${className}\\b`);
1940
+ const entries = fs10.readdirSync(dir, { withFileTypes: true });
1941
+ for (const entry of entries) {
1942
+ const fullPath = path9.join(dir, entry.name);
1943
+ if (entry.isDirectory()) {
1944
+ const result = findClassFileRecursive(fullPath, className);
1945
+ if (result) return result;
1946
+ } else if (entry.name.endsWith(".dart") && !entry.name.endsWith(".g.dart")) {
1947
+ const content = fs10.readFileSync(fullPath, "utf8");
1948
+ if (classRegex.test(content)) {
1949
+ return fullPath;
1950
+ }
1951
+ }
1952
+ }
1953
+ return null;
1954
+ }
1955
+ function findFileInPackages(packagesDir, fileName) {
1956
+ const packages = fs10.readdirSync(packagesDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => path9.join(packagesDir, d.name));
1957
+ for (const pkgDir of packages) {
1958
+ const found = findFileRecursive(pkgDir, fileName);
1959
+ if (found) return found;
1960
+ }
1961
+ return null;
1962
+ }
1963
+ function formatPkgPath(monorepoRoot, absPath) {
1964
+ return path9.relative(monorepoRoot, absPath);
1965
+ }
1966
+ var spinner = (msg) => console.log(chalk5.cyan(` \u2192 ${msg}`));
1967
+ async function createEnvVar(options) {
1968
+ const {
1969
+ monorepoRoot,
1970
+ variableName,
1971
+ dartType,
1972
+ defaultValue,
1973
+ selectedApps,
1974
+ includeInRemoteConfig,
1975
+ includeInAppConfig
1976
+ } = options;
1977
+ const camelName = screamingSnakeToCamel(variableName);
1978
+ console.log(chalk5.bold(`
1979
+ Adding environment variable: ${chalk5.cyan(variableName)}
1980
+ `));
1981
+ spinner("PASO 1: Updating env JSON files");
1982
+ const jsonValue = typedJsonValue(defaultValue, dartType);
1983
+ const appsDir = path9.join(monorepoRoot, "apps");
1984
+ for (const app of selectedApps) {
1985
+ const appEnvDir = path9.join(appsDir, app, "env");
1986
+ const files = [
1987
+ { name: "example.env.json", isTemplate: true },
1988
+ { name: "development.env.json", isTemplate: false },
1989
+ { name: "production.env.json", isTemplate: false }
1990
+ ];
1991
+ for (const { name, isTemplate } of files) {
1992
+ injectIntoJsonFile(path9.join(appEnvDir, name), variableName, jsonValue, isTemplate);
1993
+ }
1994
+ }
1995
+ spinner("PASO 2: Injecting into AppEnvironment");
1996
+ injectAbstractGetter(monorepoRoot, camelName, dartType);
1997
+ spinner("PASO 3: Injecting into AppBuildEnvironment");
1998
+ injectBuildEnvGetter(monorepoRoot, variableName, camelName, dartType);
1999
+ if (includeInRemoteConfig) {
2000
+ spinner("PASO 4: Injecting into VendorsModule RemoteConfig");
2001
+ for (const pkgName of options.vendorsTargets) {
2002
+ const pkgDir = path9.join(monorepoRoot, "packages", pkgName);
2003
+ const vendorsPath = findFileRecursive(pkgDir, "vendors_module.dart");
2004
+ if (vendorsPath) {
2005
+ console.log(chalk5.cyan(` \u2192 ${pkgName}/vendors_module.dart`));
2006
+ injectIntoRemoteConfigDefaults(vendorsPath, camelName, dartType);
2007
+ } else {
2008
+ console.log(chalk5.yellow(` vendors_module.dart not found in ${pkgName}`));
2009
+ }
2010
+ }
2011
+ }
2012
+ if (includeInAppConfig) {
2013
+ spinner("PASO 5: Injecting into AppConfig entity");
2014
+ const configPath = findAppConfigFile(monorepoRoot);
2015
+ if (configPath) {
2016
+ injectAppConfigProperty(configPath, camelName, dartType);
2017
+ } else {
2018
+ console.log(chalk5.yellow(" app_config.dart not found, skipping AppConfig"));
2019
+ }
2020
+ spinner("PASO 6: Injecting into AppConfigModel");
2021
+ const modelPath = findAppConfigModelFile(monorepoRoot);
2022
+ if (modelPath) {
2023
+ injectAppConfigModelProperty(modelPath, camelName, dartType);
2024
+ } else {
2025
+ console.log(chalk5.yellow(" app_config_model.dart not found, skipping AppConfigModel"));
2026
+ }
2027
+ }
2028
+ console.log(chalk5.green("\nEnvironment variable added successfully!"));
2029
+ spinner("Running melos format...");
2030
+ try {
2031
+ execSync("melos format", { cwd: monorepoRoot, stdio: "pipe" });
2032
+ console.log(chalk5.green(" melos format completed"));
2033
+ } catch {
2034
+ console.log(chalk5.yellow(" melos format failed (you may need to run it manually)"));
2035
+ }
2036
+ }
2037
+
2038
+ // src/core/docs-serve.ts
2039
+ import { spawn as spawn2 } from "child_process";
2040
+ import * as fs11 from "fs";
2041
+ import * as path10 from "path";
2042
+ import chalk6 from "chalk";
1472
2043
  function detectBookDir(startDir) {
1473
2044
  const root = findMonorepoRoot(startDir) ?? startDir;
1474
- const bookDir = path9.join(root, "book");
1475
- if (fs10.existsSync(bookDir) && (fs10.existsSync(path9.join(bookDir, "docusaurus.config.js")) || fs10.existsSync(path9.join(bookDir, "docusaurus.config.ts")) || fs10.existsSync(path9.join(bookDir, "docusaurus.config.mjs")))) {
2045
+ const bookDir = path10.join(root, "book");
2046
+ if (fs11.existsSync(bookDir) && (fs11.existsSync(path10.join(bookDir, "docusaurus.config.js")) || fs11.existsSync(path10.join(bookDir, "docusaurus.config.ts")) || fs11.existsSync(path10.join(bookDir, "docusaurus.config.mjs")))) {
1476
2047
  return bookDir;
1477
2048
  }
1478
2049
  return void 0;
1479
2050
  }
1480
2051
  function serveBook(bookDir) {
1481
2052
  return new Promise((resolve4, reject) => {
1482
- const nodeModules = path9.join(bookDir, "node_modules");
1483
- if (!fs10.existsSync(nodeModules)) {
1484
- console.log(chalk5.cyan("Installing book dependencies..."));
2053
+ const nodeModules = path10.join(bookDir, "node_modules");
2054
+ if (!fs11.existsSync(nodeModules)) {
2055
+ console.log(chalk6.cyan("Installing book dependencies..."));
1485
2056
  const install = spawn2("npm", ["install"], {
1486
2057
  cwd: bookDir,
1487
2058
  stdio: "inherit"
@@ -1515,16 +2086,16 @@ function startDevServer(bookDir, done) {
1515
2086
  }
1516
2087
 
1517
2088
  // src/core/docs-commands.ts
1518
- import * as fs11 from "fs";
1519
- import * as path10 from "path";
1520
- import chalk6 from "chalk";
2089
+ import * as fs12 from "fs";
2090
+ import * as path11 from "path";
2091
+ import chalk7 from "chalk";
1521
2092
  import YAML2 from "yaml";
1522
2093
  function parseMakefile(filePath) {
1523
- if (!fs11.existsSync(filePath)) return [];
1524
- const content = fs11.readFileSync(filePath, "utf8");
2094
+ if (!fs12.existsSync(filePath)) return [];
2095
+ const content = fs12.readFileSync(filePath, "utf8");
1525
2096
  const lines = content.split("\n");
1526
2097
  const commands = [];
1527
- const source = path10.basename(filePath);
2098
+ const source = path11.basename(filePath);
1528
2099
  let pendingComments = [];
1529
2100
  for (let i = 0; i < lines.length; i++) {
1530
2101
  const line = lines[i];
@@ -1554,13 +2125,13 @@ function parseMakefile(filePath) {
1554
2125
  return commands;
1555
2126
  }
1556
2127
  function parseMelosScripts(filePath) {
1557
- if (!fs11.existsSync(filePath)) return [];
1558
- const content = fs11.readFileSync(filePath, "utf8");
2128
+ if (!fs12.existsSync(filePath)) return [];
2129
+ const content = fs12.readFileSync(filePath, "utf8");
1559
2130
  const parsed = YAML2.parse(content);
1560
2131
  const scripts = parsed?.scripts;
1561
2132
  if (!scripts || typeof scripts !== "object") return [];
1562
2133
  const commands = [];
1563
- const source = path10.basename(filePath);
2134
+ const source = path11.basename(filePath);
1564
2135
  for (const [name, value] of Object.entries(scripts)) {
1565
2136
  let description = "";
1566
2137
  let command = "";
@@ -1582,17 +2153,17 @@ function parseMelosScripts(filePath) {
1582
2153
  function discoverCommands(startDir) {
1583
2154
  const root = findMonorepoRoot(startDir) ?? startDir;
1584
2155
  const commands = [];
1585
- const rootMakefile = path10.join(root, "Makefile");
2156
+ const rootMakefile = path11.join(root, "Makefile");
1586
2157
  commands.push(...parseMakefile(rootMakefile));
1587
- const melosFile = path10.join(root, "melos.yaml");
2158
+ const melosFile = path11.join(root, "melos.yaml");
1588
2159
  commands.push(...parseMelosScripts(melosFile));
1589
- const bookMakefile = path10.join(root, "book", "Makefile");
2160
+ const bookMakefile = path11.join(root, "book", "Makefile");
1590
2161
  commands.push(...parseMakefile(bookMakefile));
1591
2162
  return commands;
1592
2163
  }
1593
2164
  function displayCommands(commands) {
1594
2165
  if (commands.length === 0) {
1595
- console.log(chalk6.yellow("No commands found."));
2166
+ console.log(chalk7.yellow("No commands found."));
1596
2167
  return;
1597
2168
  }
1598
2169
  const groups = /* @__PURE__ */ new Map();
@@ -1602,13 +2173,13 @@ function displayCommands(commands) {
1602
2173
  groups.get(key).push(cmd);
1603
2174
  }
1604
2175
  for (const [group, entries] of groups) {
1605
- console.log(chalk6.bold(chalk6.cyan(`
2176
+ console.log(chalk7.bold(chalk7.cyan(`
1606
2177
  ${group}`)));
1607
- console.log(chalk6.cyan("\u2500".repeat(group.length)));
2178
+ console.log(chalk7.cyan("\u2500".repeat(group.length)));
1608
2179
  const maxNameLen = Math.max(...entries.map((e) => e.name.length));
1609
2180
  for (const entry of entries) {
1610
- const name = chalk6.white(entry.name.padEnd(maxNameLen + 2));
1611
- const desc = entry.description ? chalk6.gray(entry.description) : chalk6.dim("(no description)");
2181
+ const name = chalk7.white(entry.name.padEnd(maxNameLen + 2));
2182
+ const desc = entry.description ? chalk7.gray(entry.description) : chalk7.dim("(no description)");
1612
2183
  console.log(` ${name} ${desc}`);
1613
2184
  }
1614
2185
  }
@@ -1616,9 +2187,9 @@ ${group}`)));
1616
2187
  }
1617
2188
 
1618
2189
  // src/core/docs-architecture.ts
1619
- import * as fs12 from "fs";
1620
- import * as path11 from "path";
1621
- import chalk7 from "chalk";
2190
+ import * as fs13 from "fs";
2191
+ import * as path12 from "path";
2192
+ import chalk8 from "chalk";
1622
2193
  import YAML3 from "yaml";
1623
2194
  var KEY_DEPS = [
1624
2195
  "freezed",
@@ -1636,18 +2207,18 @@ var KEY_DEPS = [
1636
2207
  function discoverArchitecture(startDir) {
1637
2208
  const root = findMonorepoRoot(startDir);
1638
2209
  if (!root) return null;
1639
- const melosPath = path11.join(root, "melos.yaml");
1640
- if (!fs12.existsSync(melosPath)) return null;
1641
- const melosContent = fs12.readFileSync(melosPath, "utf8");
2210
+ const melosPath = path12.join(root, "melos.yaml");
2211
+ if (!fs13.existsSync(melosPath)) return null;
2212
+ const melosContent = fs13.readFileSync(melosPath, "utf8");
1642
2213
  const melos = YAML3.parse(melosContent);
1643
- const projectName = melos?.name ?? path11.basename(root);
2214
+ const projectName = melos?.name ?? path12.basename(root);
1644
2215
  const apps = [];
1645
- const appsDir = path11.join(root, "apps");
1646
- if (fs12.existsSync(appsDir)) {
1647
- for (const entry of fs12.readdirSync(appsDir, { withFileTypes: true })) {
2216
+ const appsDir = path12.join(root, "apps");
2217
+ if (fs13.existsSync(appsDir)) {
2218
+ for (const entry of fs13.readdirSync(appsDir, { withFileTypes: true })) {
1648
2219
  if (!entry.isDirectory()) continue;
1649
- const candidate = path11.join(appsDir, entry.name);
1650
- if (fs12.existsSync(path11.join(candidate, "pubspec.yaml"))) {
2220
+ const candidate = path12.join(appsDir, entry.name);
2221
+ if (fs13.existsSync(path12.join(candidate, "pubspec.yaml"))) {
1651
2222
  apps.push({ name: entry.name, dir: candidate });
1652
2223
  }
1653
2224
  }
@@ -1655,14 +2226,14 @@ function discoverArchitecture(startDir) {
1655
2226
  const packages = [];
1656
2227
  const packageBases = ["packages", "packages/features"];
1657
2228
  for (const base of packageBases) {
1658
- const baseDir = path11.join(root, base);
1659
- if (!fs12.existsSync(baseDir)) continue;
1660
- for (const entry of fs12.readdirSync(baseDir, { withFileTypes: true })) {
2229
+ const baseDir = path12.join(root, base);
2230
+ if (!fs13.existsSync(baseDir)) continue;
2231
+ for (const entry of fs13.readdirSync(baseDir, { withFileTypes: true })) {
1661
2232
  if (!entry.isDirectory()) continue;
1662
- const candidate = path11.join(baseDir, entry.name);
1663
- const pubspecPath = path11.join(candidate, "pubspec.yaml");
1664
- if (!fs12.existsSync(pubspecPath)) continue;
1665
- const content = fs12.readFileSync(pubspecPath, "utf8");
2233
+ const candidate = path12.join(baseDir, entry.name);
2234
+ const pubspecPath = path12.join(candidate, "pubspec.yaml");
2235
+ if (!fs13.existsSync(pubspecPath)) continue;
2236
+ const content = fs13.readFileSync(pubspecPath, "utf8");
1666
2237
  const pubspec = YAML3.parse(content);
1667
2238
  const deps = {
1668
2239
  ...pubspec?.dependencies,
@@ -1697,30 +2268,30 @@ function displayArchitecture(info) {
1697
2268
  const L = "\u2514\u2500\u2500 ";
1698
2269
  const I = "\u2502 ";
1699
2270
  const S = " ";
1700
- console.log(chalk7.bold(chalk7.cyan(info.projectName)) + chalk7.gray(` (${info.root})`));
2271
+ console.log(chalk8.bold(chalk8.cyan(info.projectName)) + chalk8.gray(` (${info.root})`));
1701
2272
  console.log(`${I}`);
1702
2273
  if (info.apps.length > 0) {
1703
- console.log(chalk7.white(`${T}apps/`));
2274
+ console.log(chalk8.white(`${T}apps/`));
1704
2275
  for (let i = 0; i < info.apps.length; i++) {
1705
2276
  const app = info.apps[i];
1706
2277
  const prefix = i === info.apps.length - 1 ? `${I}${S}${L}` : `${I}${S}${T}`;
1707
- console.log(`${prefix}${chalk7.green(app.name)}`);
2278
+ console.log(`${prefix}${chalk8.green(app.name)}`);
1708
2279
  }
1709
2280
  }
1710
2281
  if (info.packages.length > 0) {
1711
2282
  const rootPkgs = info.packages.filter(
1712
- (p) => !p.dir.includes(`${path11.sep}features${path11.sep}`)
2283
+ (p) => !p.dir.includes(`${path12.sep}features${path12.sep}`)
1713
2284
  );
1714
2285
  const featurePkgs = info.packages.filter(
1715
- (p) => p.dir.includes(`${path11.sep}features${path11.sep}`)
2286
+ (p) => p.dir.includes(`${path12.sep}features${path12.sep}`)
1716
2287
  );
1717
- console.log(chalk7.white(`${T}packages/`));
2288
+ console.log(chalk8.white(`${T}packages/`));
1718
2289
  for (let i = 0; i < rootPkgs.length; i++) {
1719
2290
  const pkg2 = rootPkgs[i];
1720
2291
  const isLast = i === rootPkgs.length - 1 && featurePkgs.length === 0;
1721
2292
  const prefix = isLast ? `${I}${S}${L}` : `${I}${S}${T}`;
1722
- const deps = pkg2.keyDeps.length > 0 ? chalk7.dim(` [${pkg2.keyDeps.join(", ")}]`) : "";
1723
- console.log(`${prefix}${chalk7.yellow(pkg2.name)}${deps}`);
2293
+ const deps = pkg2.keyDeps.length > 0 ? chalk8.dim(` [${pkg2.keyDeps.join(", ")}]`) : "";
2294
+ console.log(`${prefix}${chalk8.yellow(pkg2.name)}${deps}`);
1724
2295
  }
1725
2296
  if (featurePkgs.length > 0) {
1726
2297
  console.log(`${I}${S}${T}features/`);
@@ -1728,21 +2299,21 @@ function displayArchitecture(info) {
1728
2299
  const pkg2 = featurePkgs[i];
1729
2300
  const isLast = i === featurePkgs.length - 1;
1730
2301
  const prefix = isLast ? `${I}${S}${S}${L}` : `${I}${S}${S}${T}`;
1731
- const deps = pkg2.keyDeps.length > 0 ? chalk7.dim(` [${pkg2.keyDeps.join(", ")}]`) : "";
1732
- console.log(`${prefix}${chalk7.yellow(pkg2.name)}${deps}`);
2302
+ const deps = pkg2.keyDeps.length > 0 ? chalk8.dim(` [${pkg2.keyDeps.join(", ")}]`) : "";
2303
+ console.log(`${prefix}${chalk8.yellow(pkg2.name)}${deps}`);
1733
2304
  }
1734
2305
  }
1735
2306
  }
1736
2307
  if (info.hasDesignSystem) {
1737
- const tiers = info.designSystemTiers.length > 0 ? chalk7.dim(` (${info.designSystemTiers.join(", ")})`) : "";
1738
- console.log(`${T}${chalk7.magenta("design_system")}${tiers}`);
2308
+ const tiers = info.designSystemTiers.length > 0 ? chalk8.dim(` (${info.designSystemTiers.join(", ")})`) : "";
2309
+ console.log(`${T}${chalk8.magenta("design_system")}${tiers}`);
1739
2310
  }
1740
- const bookDir = path11.join(info.root, "book");
1741
- if (fs12.existsSync(bookDir)) {
1742
- console.log(`${T}${chalk7.blue("book/")}` + chalk7.dim(" (Docusaurus)"));
2311
+ const bookDir = path12.join(info.root, "book");
2312
+ if (fs13.existsSync(bookDir)) {
2313
+ console.log(`${T}${chalk8.blue("book/")}` + chalk8.dim(" (Docusaurus)"));
1743
2314
  }
1744
- if (fs12.existsSync(path11.join(info.root, "melos.yaml"))) {
1745
- console.log(`${L}${chalk7.gray("melos.yaml")}`);
2315
+ if (fs13.existsSync(path12.join(info.root, "melos.yaml"))) {
2316
+ console.log(`${L}${chalk8.gray("melos.yaml")}`);
1746
2317
  }
1747
2318
  console.log();
1748
2319
  }
@@ -1754,7 +2325,7 @@ async function resolveProject() {
1754
2325
  s.start("Analyzing current directory...");
1755
2326
  const cwdProject = analyzeProject(process.cwd());
1756
2327
  if (cwdProject && (cwdProject.hasFreezed || cwdProject.hasBloc)) {
1757
- s.stop(`Found ${chalk8.green(cwdProject.projectName)}`);
2328
+ s.stop(`Found ${chalk9.green(cwdProject.projectName)}`);
1758
2329
  return cwdProject;
1759
2330
  }
1760
2331
  s.message("Looking for Melos monorepo...");
@@ -1767,8 +2338,8 @@ async function resolveProject() {
1767
2338
  }
1768
2339
  }
1769
2340
  s.message("Scanning for Flutter projects...");
1770
- const homeDev = path12.join(os.homedir(), "Development");
1771
- if (fs13.existsSync(homeDev)) {
2341
+ const homeDev = path13.join(os.homedir(), "Development");
2342
+ if (fs14.existsSync(homeDev)) {
1772
2343
  const projects = discoverProjects(homeDev, 2);
1773
2344
  if (projects.length > 0) {
1774
2345
  s.stop(`Found ${projects.length} Flutter project(s)`);
@@ -1776,12 +2347,12 @@ async function resolveProject() {
1776
2347
  }
1777
2348
  }
1778
2349
  s.stop("No Flutter projects found");
1779
- clack.outro(chalk8.red("Could not find any Flutter project with freezed or flutter_bloc."));
2350
+ clack.outro(chalk9.red("Could not find any Flutter project with freezed or flutter_bloc."));
1780
2351
  return null;
1781
2352
  }
1782
2353
  async function selectPackage(projects) {
1783
2354
  if (projects.length === 1) {
1784
- clack.log.info(`Using ${chalk8.green(projects[0].projectName)}`);
2355
+ clack.log.info(`Using ${chalk9.green(projects[0].projectName)}`);
1785
2356
  return projects[0];
1786
2357
  }
1787
2358
  const selected = await clack.select({
@@ -1789,7 +2360,7 @@ async function selectPackage(projects) {
1789
2360
  options: projects.map((p) => ({
1790
2361
  value: p,
1791
2362
  label: p.projectName,
1792
- hint: path12.relative(os.homedir(), p.projectRoot)
2363
+ hint: path13.relative(os.homedir(), p.projectRoot)
1793
2364
  }))
1794
2365
  });
1795
2366
  if (clack.isCancel(selected)) {
@@ -1837,13 +2408,13 @@ async function blocFlow(project) {
1837
2408
  clack.cancel("Cancelled");
1838
2409
  return;
1839
2410
  }
1840
- targetDir = path12.resolve(customPath);
2411
+ targetDir = path13.resolve(customPath);
1841
2412
  } else {
1842
- targetDir = path12.join(project.projectRoot, "lib", "features", feature);
2413
+ targetDir = path13.join(project.projectRoot, "lib", "features", feature);
1843
2414
  }
1844
- } else if (fs13.existsSync(path12.join(project.projectRoot, "lib", "bloc"))) {
1845
- targetDir = path12.join(project.projectRoot, "lib", "bloc");
1846
- clack.log.info(`Target: ${chalk8.cyan("lib/bloc/")}`);
2415
+ } else if (fs14.existsSync(path13.join(project.projectRoot, "lib", "bloc"))) {
2416
+ targetDir = path13.join(project.projectRoot, "lib", "bloc");
2417
+ clack.log.info(`Target: ${chalk9.cyan("lib/bloc/")}`);
1847
2418
  } else {
1848
2419
  clack.note(
1849
2420
  "No lib/features/ directory found. Provide a target path manually.",
@@ -1860,7 +2431,7 @@ async function blocFlow(project) {
1860
2431
  clack.cancel("Cancelled");
1861
2432
  return;
1862
2433
  }
1863
- targetDir = path12.resolve(customPath);
2434
+ targetDir = path13.resolve(customPath);
1864
2435
  }
1865
2436
  const defaultRun = project.hasBuildRunner;
1866
2437
  const runBuildRunner2 = await clack.confirm({
@@ -1879,10 +2450,10 @@ async function blocFlow(project) {
1879
2450
  buildRunner: runBuildRunner2
1880
2451
  });
1881
2452
  genSpinner.stop("BLoC generated");
1882
- clack.outro(chalk8.green("Done!"));
2453
+ clack.outro(chalk9.green("Done!"));
1883
2454
  } catch (error) {
1884
2455
  genSpinner.stop("Failed");
1885
- clack.outro(chalk8.red(`Error: ${error}`));
2456
+ clack.outro(chalk9.red(`Error: ${error}`));
1886
2457
  }
1887
2458
  }
1888
2459
  async function widgetFlow() {
@@ -1890,14 +2461,14 @@ async function widgetFlow() {
1890
2461
  const ds = detectDesignSystem(projectRoot);
1891
2462
  if (!ds) {
1892
2463
  clack.outro(
1893
- chalk8.red(
2464
+ chalk9.red(
1894
2465
  "No design system detected. Run this command from a project with wl_design_system/ directory."
1895
2466
  )
1896
2467
  );
1897
2468
  return;
1898
2469
  }
1899
2470
  clack.log.info(
1900
- `Design system: ${chalk8.cyan(path12.relative(projectRoot, ds.componentsDir))}`
2471
+ `Design system: ${chalk9.cyan(path13.relative(projectRoot, ds.componentsDir))}`
1901
2472
  );
1902
2473
  const name = await clack.text({
1903
2474
  message: "Widget name (snake_case, without wl_ prefix)",
@@ -1962,17 +2533,17 @@ async function widgetFlow() {
1962
2533
  genSpinner.stop(`Use-case skipped: ${e}`);
1963
2534
  }
1964
2535
  }
1965
- clack.outro(chalk8.green("Done!"));
2536
+ clack.outro(chalk9.green("Done!"));
1966
2537
  } catch (error) {
1967
2538
  genSpinner.stop("Failed");
1968
- clack.outro(chalk8.red(`Error: ${error}`));
2539
+ clack.outro(chalk9.red(`Error: ${error}`));
1969
2540
  }
1970
2541
  }
1971
2542
  async function useCaseFlow() {
1972
2543
  const ds = detectDesignSystem(process.cwd());
1973
2544
  if (!ds) {
1974
2545
  clack.outro(
1975
- chalk8.red(
2546
+ chalk9.red(
1976
2547
  "No design system detected. Ensure wl_design_system/ directory exists."
1977
2548
  )
1978
2549
  );
@@ -1980,7 +2551,7 @@ async function useCaseFlow() {
1980
2551
  }
1981
2552
  if (!ds.widgetbookDir) {
1982
2553
  clack.outro(
1983
- chalk8.red(
2554
+ chalk9.red(
1984
2555
  "No widgetbook package detected. Ensure apps/widgetbook/ exists."
1985
2556
  )
1986
2557
  );
@@ -2029,18 +2600,18 @@ async function useCaseFlow() {
2029
2600
  buildRunner: runBuildRunner2
2030
2601
  });
2031
2602
  genSpinner.stop("Use-case generated");
2032
- clack.outro(chalk8.green("Done!"));
2603
+ clack.outro(chalk9.green("Done!"));
2033
2604
  } catch (error) {
2034
2605
  genSpinner.stop("Failed");
2035
- clack.outro(chalk8.red(`Error: ${error}`));
2606
+ clack.outro(chalk9.red(`Error: ${error}`));
2036
2607
  }
2037
2608
  }
2038
2609
  function findBffFiles(dir) {
2039
2610
  const results = [];
2040
- if (!fs13.existsSync(dir)) return results;
2041
- const entries = fs13.readdirSync(dir, { withFileTypes: true });
2611
+ if (!fs14.existsSync(dir)) return results;
2612
+ const entries = fs14.readdirSync(dir, { withFileTypes: true });
2042
2613
  for (const entry of entries) {
2043
- const fullPath = path12.join(dir, entry.name);
2614
+ const fullPath = path13.join(dir, entry.name);
2044
2615
  if (entry.isDirectory()) {
2045
2616
  results.push(...findBffFiles(fullPath));
2046
2617
  } else if (entry.isFile() && entry.name.endsWith(".dart") && !entry.name.includes(".g.")) {
@@ -2052,8 +2623,8 @@ function findBffFiles(dir) {
2052
2623
  function inferBffFile(bffFiles, endpointPath) {
2053
2624
  const firstSegment = endpointPath.replace(/^\//, "").split("/")[0].toLowerCase();
2054
2625
  if (!firstSegment) return null;
2055
- const withDomains = bffFiles.filter((f) => /^bff_.+_api\.dart$/.test(path12.basename(f))).map((f) => {
2056
- const base = path12.basename(f, ".dart");
2626
+ const withDomains = bffFiles.filter((f) => /^bff_.+_api\.dart$/.test(path13.basename(f))).map((f) => {
2627
+ const base = path13.basename(f, ".dart");
2057
2628
  const domain = base.replace(/^bff_/, "").replace(/_api$/, "");
2058
2629
  return { file: f, domain };
2059
2630
  });
@@ -2080,19 +2651,19 @@ async function resolveEndpointProject() {
2080
2651
  s.message(`Monorepo found at ${monorepoRoot}`);
2081
2652
  const packageBases = ["packages", "packages/features"];
2082
2653
  for (const base of packageBases) {
2083
- const baseDir = path12.join(monorepoRoot, base);
2084
- if (!fs13.existsSync(baseDir)) continue;
2085
- const entries = fs13.readdirSync(baseDir, { withFileTypes: true });
2654
+ const baseDir = path13.join(monorepoRoot, base);
2655
+ if (!fs14.existsSync(baseDir)) continue;
2656
+ const entries = fs14.readdirSync(baseDir, { withFileTypes: true });
2086
2657
  for (const entry of entries) {
2087
2658
  if (!entry.isDirectory()) continue;
2088
- const candidate = path12.join(baseDir, entry.name);
2089
- if (!fs13.existsSync(path12.join(candidate, "pubspec.yaml"))) continue;
2090
- const bffPath = path12.join(candidate, "lib", "data", "api", "bff");
2091
- s.message(`Checking ${candidate} \u2192 bff exists: ${fs13.existsSync(bffPath)}`);
2092
- if (fs13.existsSync(bffPath)) {
2659
+ const candidate = path13.join(baseDir, entry.name);
2660
+ if (!fs14.existsSync(path13.join(candidate, "pubspec.yaml"))) continue;
2661
+ const bffPath = path13.join(candidate, "lib", "data", "api", "bff");
2662
+ s.message(`Checking ${candidate} \u2192 bff exists: ${fs14.existsSync(bffPath)}`);
2663
+ if (fs14.existsSync(bffPath)) {
2093
2664
  const project2 = analyzeProject(candidate);
2094
2665
  if (project2) {
2095
- s.stop(`Using ${chalk8.green(project2.projectName)}`);
2666
+ s.stop(`Using ${chalk9.green(project2.projectName)}`);
2096
2667
  return project2;
2097
2668
  }
2098
2669
  }
@@ -2102,8 +2673,8 @@ async function resolveEndpointProject() {
2102
2673
  s.message("No monorepo root found");
2103
2674
  }
2104
2675
  const cwdProject = analyzeProject(process.cwd());
2105
- if (cwdProject && fs13.existsSync(path12.join(cwdProject.projectRoot, "lib", "data", "api", "bff"))) {
2106
- s.stop(`Using ${chalk8.green(cwdProject.projectName)}`);
2676
+ if (cwdProject && fs14.existsSync(path13.join(cwdProject.projectRoot, "lib", "data", "api", "bff"))) {
2677
+ s.stop(`Using ${chalk9.green(cwdProject.projectName)}`);
2107
2678
  return cwdProject;
2108
2679
  }
2109
2680
  s.stop("No BFF package found");
@@ -2112,33 +2683,33 @@ async function resolveEndpointProject() {
2112
2683
  placeholder: "e.g. /path/to/my-package or ./packages/core",
2113
2684
  validate: (v) => {
2114
2685
  if (!v.trim()) return "Path is required";
2115
- const resolved2 = path12.resolve(v.trim());
2116
- if (!fs13.existsSync(resolved2)) return "Path does not exist";
2117
- if (!fs13.existsSync(path12.join(resolved2, "pubspec.yaml"))) return "No pubspec.yaml found at this path";
2118
- if (!fs13.existsSync(path12.join(resolved2, "lib", "data", "api", "bff"))) return "No lib/data/api/bff/ found at this path";
2686
+ const resolved2 = path13.resolve(v.trim());
2687
+ if (!fs14.existsSync(resolved2)) return "Path does not exist";
2688
+ if (!fs14.existsSync(path13.join(resolved2, "pubspec.yaml"))) return "No pubspec.yaml found at this path";
2689
+ if (!fs14.existsSync(path13.join(resolved2, "lib", "data", "api", "bff"))) return "No lib/data/api/bff/ found at this path";
2119
2690
  }
2120
2691
  });
2121
2692
  if (clack.isCancel(manualPath)) {
2122
2693
  clack.cancel("Cancelled");
2123
2694
  return null;
2124
2695
  }
2125
- const resolved = path12.resolve(manualPath);
2696
+ const resolved = path13.resolve(manualPath);
2126
2697
  const project = analyzeProject(resolved);
2127
2698
  if (!project) {
2128
- clack.outro(chalk8.red(`Could not analyze project at ${resolved}`));
2699
+ clack.outro(chalk9.red(`Could not analyze project at ${resolved}`));
2129
2700
  return null;
2130
2701
  }
2131
- clack.log.info(`Using ${chalk8.green(project.projectName)}`);
2702
+ clack.log.info(`Using ${chalk9.green(project.projectName)}`);
2132
2703
  return project;
2133
2704
  }
2134
2705
  async function endpointFlow() {
2135
2706
  const project = await resolveEndpointProject();
2136
2707
  if (!project) return;
2137
- const lib = path12.join(project.projectRoot, "lib");
2138
- const bffDir = path12.join(lib, "data", "api", "bff");
2708
+ const lib = path13.join(project.projectRoot, "lib");
2709
+ const bffDir = path13.join(lib, "data", "api", "bff");
2139
2710
  const bffFiles = findBffFiles(bffDir);
2140
2711
  if (bffFiles.length === 0) {
2141
- clack.outro(chalk8.red("No BFF API files found. Ensure lib/data/api/bff/ exists with .dart files."));
2712
+ clack.outro(chalk9.red("No BFF API files found. Ensure lib/data/api/bff/ exists with .dart files."));
2142
2713
  return;
2143
2714
  }
2144
2715
  const httpMethod = await clack.select({
@@ -2168,13 +2739,13 @@ async function endpointFlow() {
2168
2739
  }
2169
2740
  let bffApiFile = inferBffFile(bffFiles, endpointPath);
2170
2741
  if (bffApiFile) {
2171
- clack.log.info(`Auto-detected BFF file: ${chalk8.cyan(path12.basename(bffApiFile))}`);
2742
+ clack.log.info(`Auto-detected BFF file: ${chalk9.cyan(path13.basename(bffApiFile))}`);
2172
2743
  } else {
2173
2744
  const selected = await clack.select({
2174
2745
  message: "Could not auto-detect BFF file. Select one:",
2175
2746
  options: bffFiles.map((f) => ({
2176
2747
  value: f,
2177
- label: path12.relative(bffDir, f)
2748
+ label: path13.relative(bffDir, f)
2178
2749
  }))
2179
2750
  });
2180
2751
  if (clack.isCancel(selected)) {
@@ -2238,14 +2809,14 @@ async function endpointFlow() {
2238
2809
  diLazySingleton
2239
2810
  });
2240
2811
  genSpinner.stop("Endpoint generated");
2241
- clack.outro(chalk8.green("Done!"));
2812
+ clack.outro(chalk9.green("Done!"));
2242
2813
  } catch (error) {
2243
2814
  genSpinner.stop("Failed");
2244
- clack.outro(chalk8.red(`Error: ${error}`));
2815
+ clack.outro(chalk9.red(`Error: ${error}`));
2245
2816
  }
2246
2817
  }
2247
2818
  async function docsInteractiveMode() {
2248
- clack.intro(chalk8.bgCyan(chalk8.black(" wlmaker docs ")));
2819
+ clack.intro(chalk9.bgCyan(chalk9.black(" wlmaker docs ")));
2249
2820
  const action = await clack.select({
2250
2821
  message: "What do you want to do?",
2251
2822
  options: [
@@ -2260,44 +2831,200 @@ async function docsInteractiveMode() {
2260
2831
  }
2261
2832
  switch (action) {
2262
2833
  case "serve": {
2834
+ const serveMode = await clack.select({
2835
+ message: "How do you want to view the docs?",
2836
+ options: [
2837
+ { value: "local", label: "Local", hint: "Start Docusaurus dev server locally" },
2838
+ { value: "remote", label: "Remote", hint: "Open the deployed docs in your browser" }
2839
+ ]
2840
+ });
2841
+ if (clack.isCancel(serveMode)) {
2842
+ clack.cancel("Cancelled");
2843
+ return;
2844
+ }
2845
+ if (serveMode === "remote") {
2846
+ const challenge = await clack.text({
2847
+ message: "Pregunta de seguridad: Qu\xE9 dice mechi cuando tiene una duda en el dise\xF1o?",
2848
+ placeholder: "...",
2849
+ validate: (input) => {
2850
+ const normalized = input.trim().toLowerCase().replace(/[áä]/g, "a").replace(/[éë]/g, "e").replace(/[íï]/g, "i").replace(/[óö]/g, "o").replace(/[úü]/g, "u");
2851
+ const expected = "deja le pregunto a cesita";
2852
+ if (normalized !== expected) {
2853
+ return "Respuesta incorrecta. Vuelve cuando sepas el lore.";
2854
+ }
2855
+ }
2856
+ });
2857
+ if (clack.isCancel(challenge)) {
2858
+ clack.cancel("Cancelled");
2859
+ return;
2860
+ }
2861
+ const remoteUrl = "https://dc-wl-docs.web.app/development_workflow/";
2862
+ if (!remoteUrl) {
2863
+ clack.outro(chalk9.red("Remote docs URL not configured. Set WL_DOCS_URL in your environment."));
2864
+ return;
2865
+ }
2866
+ clack.log.info(`Opening remote docs: ${chalk9.cyan(remoteUrl)}`);
2867
+ execSync2(`open "${remoteUrl}"`, { stdio: "ignore" });
2868
+ clack.outro(chalk9.green("Opened remote docs in browser"));
2869
+ return;
2870
+ }
2263
2871
  const bookDir = detectBookDir(process.cwd());
2264
2872
  if (!bookDir) {
2265
2873
  clack.outro(
2266
- chalk8.red("No Docusaurus book/ directory found. Run from a monorepo root.")
2874
+ chalk9.red("No Docusaurus book/ directory found. Run from a monorepo root.")
2267
2875
  );
2268
2876
  return;
2269
2877
  }
2270
- clack.log.info(`Serving docs from ${chalk8.cyan(bookDir)}`);
2878
+ clack.log.info(`Serving docs from ${chalk9.cyan(bookDir)}`);
2271
2879
  await serveBook(bookDir);
2272
2880
  break;
2273
2881
  }
2274
2882
  case "commands": {
2275
2883
  const commands = discoverCommands(process.cwd());
2276
2884
  if (commands.length === 0) {
2277
- clack.outro(chalk8.yellow("No commands found. Run from a monorepo root."));
2885
+ clack.outro(chalk9.yellow("No commands found. Run from a monorepo root."));
2278
2886
  return;
2279
2887
  }
2280
- clack.log.info(`Found ${chalk8.green(commands.length.toString())} command(s)`);
2888
+ clack.log.info(`Found ${chalk9.green(commands.length.toString())} command(s)`);
2281
2889
  displayCommands(commands);
2282
- clack.outro(chalk8.green("Done!"));
2890
+ clack.outro(chalk9.green("Done!"));
2283
2891
  break;
2284
2892
  }
2285
2893
  case "architecture": {
2286
2894
  const info = discoverArchitecture(process.cwd());
2287
2895
  if (!info) {
2288
2896
  clack.outro(
2289
- chalk8.yellow("No monorepo detected. Run from within a Melos monorepo.")
2897
+ chalk9.yellow("No monorepo detected. Run from within a Melos monorepo.")
2290
2898
  );
2291
2899
  return;
2292
2900
  }
2293
2901
  displayArchitecture(info);
2294
- clack.outro(chalk8.green("Done!"));
2902
+ clack.outro(chalk9.green("Done!"));
2295
2903
  break;
2296
2904
  }
2297
2905
  }
2298
2906
  }
2907
+ var SCREAMING_SNAKE_REGEX = /^[A-Z][A-Z0-9_]*$/;
2908
+ async function envVarFlow() {
2909
+ const monorepoRoot = findMonorepoRoot(process.cwd());
2910
+ if (!monorepoRoot) {
2911
+ clack.outro(chalk9.red("Not inside a monorepo. Run from within a Melos monorepo."));
2912
+ return;
2913
+ }
2914
+ const apps = discoverAppsWithEnv(monorepoRoot);
2915
+ if (apps.length === 0) {
2916
+ clack.outro(chalk9.red("No apps with env/ directory found in the monorepo."));
2917
+ return;
2918
+ }
2919
+ const variableName = await clack.text({
2920
+ message: "Variable name (SCREAMING_SNAKE_CASE)",
2921
+ placeholder: "MY_FEATURE_FLAG",
2922
+ validate: (v) => {
2923
+ if (!v.trim()) return "Name is required";
2924
+ if (!SCREAMING_SNAKE_REGEX.test(v)) return "Must be SCREAMING_SNAKE_CASE (uppercase, numbers, underscores)";
2925
+ }
2926
+ });
2927
+ if (clack.isCancel(variableName)) {
2928
+ clack.cancel("Cancelled");
2929
+ return;
2930
+ }
2931
+ const dartType = await clack.select({
2932
+ message: "Variable type",
2933
+ options: [
2934
+ { value: "String", label: "String" },
2935
+ { value: "int", label: "int" },
2936
+ { value: "bool", label: "bool" },
2937
+ { value: "List<String>", label: "List<String>" }
2938
+ ]
2939
+ });
2940
+ if (clack.isCancel(dartType)) {
2941
+ clack.cancel("Cancelled");
2942
+ return;
2943
+ }
2944
+ const selectedType = dartType;
2945
+ const defaultValue = await clack.text({
2946
+ message: "Default value (leave empty to skip)",
2947
+ placeholder: selectedType === "bool" ? "false" : selectedType === "int" ? "0" : "",
2948
+ validate: (v) => {
2949
+ if (!v || !v.trim()) return void 0;
2950
+ if (selectedType === "int" && !/^-?\d+$/.test(v)) return "Must be an integer";
2951
+ if (selectedType === "bool" && !["true", "false"].includes(v)) return "Must be true or false";
2952
+ }
2953
+ });
2954
+ if (clack.isCancel(defaultValue)) {
2955
+ clack.cancel("Cancelled");
2956
+ return;
2957
+ }
2958
+ const selectedApps = await clack.multiselect({
2959
+ message: "Select apps to add the variable to",
2960
+ options: [
2961
+ { value: "__all__", label: "All apps" },
2962
+ ...apps.map((app) => ({ value: app, label: app }))
2963
+ ],
2964
+ required: true
2965
+ });
2966
+ if (clack.isCancel(selectedApps)) {
2967
+ clack.cancel("Cancelled");
2968
+ return;
2969
+ }
2970
+ const appsList = selectedApps.includes("__all__") ? apps : selectedApps;
2971
+ const includeInRemoteConfig = await clack.confirm({
2972
+ message: "Add to VendorsModule RemoteConfig defaultValues?",
2973
+ initialValue: true
2974
+ });
2975
+ if (clack.isCancel(includeInRemoteConfig)) {
2976
+ clack.cancel("Cancelled");
2977
+ return;
2978
+ }
2979
+ let vendorsTargets = [];
2980
+ if (includeInRemoteConfig) {
2981
+ const vendorsModules = discoverVendorsModules(monorepoRoot);
2982
+ if (vendorsModules.length > 0) {
2983
+ const vendorsSelection = await clack.multiselect({
2984
+ message: "Which VendorsModule(s) to update?",
2985
+ options: [
2986
+ { value: "__all__", label: "All vendors" },
2987
+ ...vendorsModules.map((v) => ({ value: v, label: v }))
2988
+ ],
2989
+ required: true
2990
+ });
2991
+ if (clack.isCancel(vendorsSelection)) {
2992
+ clack.cancel("Cancelled");
2993
+ return;
2994
+ }
2995
+ vendorsTargets = vendorsSelection.includes("__all__") ? vendorsModules : vendorsSelection;
2996
+ }
2997
+ }
2998
+ const includeInAppConfig = await clack.confirm({
2999
+ message: "Add to AppConfig entity and AppConfigModel?",
3000
+ initialValue: true
3001
+ });
3002
+ if (clack.isCancel(includeInAppConfig)) {
3003
+ clack.cancel("Cancelled");
3004
+ return;
3005
+ }
3006
+ const genSpinner = clack.spinner();
3007
+ genSpinner.start("Adding environment variable...");
3008
+ try {
3009
+ await createEnvVar({
3010
+ monorepoRoot,
3011
+ variableName,
3012
+ dartType: selectedType,
3013
+ defaultValue: defaultValue || void 0,
3014
+ selectedApps: appsList,
3015
+ vendorsTargets,
3016
+ includeInRemoteConfig,
3017
+ includeInAppConfig
3018
+ });
3019
+ genSpinner.stop("Done");
3020
+ clack.outro(chalk9.green("Environment variable added!"));
3021
+ } catch (error) {
3022
+ genSpinner.stop("Failed");
3023
+ clack.outro(chalk9.red(`Error: ${error}`));
3024
+ }
3025
+ }
2299
3026
  async function interactiveMode() {
2300
- clack.intro(chalk8.bgCyan(chalk8.black(" wlmaker ")));
3027
+ clack.intro(chalk9.bgCyan(chalk9.black(" wlmaker ")));
2301
3028
  const createType = await clack.select({
2302
3029
  message: "What do you want to create?",
2303
3030
  options: [
@@ -2313,6 +3040,11 @@ async function interactiveMode() {
2313
3040
  label: "Endpoint",
2314
3041
  hint: "BFF Clean Architecture stack"
2315
3042
  },
3043
+ {
3044
+ value: "env-var",
3045
+ label: "Env Var",
3046
+ hint: "Add environment variable to monorepo stack"
3047
+ },
2316
3048
  {
2317
3049
  value: "docs",
2318
3050
  label: "Docs",
@@ -2341,6 +3073,9 @@ async function interactiveMode() {
2341
3073
  case "endpoint":
2342
3074
  await endpointFlow();
2343
3075
  break;
3076
+ case "env-var":
3077
+ await envVarFlow();
3078
+ break;
2344
3079
  case "docs":
2345
3080
  await docsInteractiveMode();
2346
3081
  break;
@@ -2363,7 +3098,7 @@ program.command("bloc").description("Create a new BLoC with Freezed sealed class
2363
3098
  try {
2364
3099
  await createBloc(name, options);
2365
3100
  } catch (error) {
2366
- console.error(chalk9.red(`Error: ${error}`));
3101
+ console.error(chalk10.red(`Error: ${error}`));
2367
3102
  process.exit(1);
2368
3103
  }
2369
3104
  }
@@ -2380,12 +3115,12 @@ program.command("widget").description("Create a new widget in the design system"
2380
3115
  projectRoot: options.dir,
2381
3116
  buildRunner: false
2382
3117
  });
2383
- console.log(chalk9.green("Widgetbook use-case created"));
3118
+ console.log(chalk10.green("Widgetbook use-case created"));
2384
3119
  } catch {
2385
- console.log(chalk9.yellow("Use-case skipped (may already exist)"));
3120
+ console.log(chalk10.yellow("Use-case skipped (may already exist)"));
2386
3121
  }
2387
3122
  } catch (error) {
2388
- console.error(chalk9.red(`Error: ${error}`));
3123
+ console.error(chalk10.red(`Error: ${error}`));
2389
3124
  process.exit(1);
2390
3125
  }
2391
3126
  }
@@ -2398,7 +3133,7 @@ program.command("usecase").description("Create a Widgetbook use-case for an exis
2398
3133
  buildRunner: options.buildRunner
2399
3134
  });
2400
3135
  } catch (error) {
2401
- console.error(chalk9.red(`Error: ${error}`));
3136
+ console.error(chalk10.red(`Error: ${error}`));
2402
3137
  process.exit(1);
2403
3138
  }
2404
3139
  }
@@ -2408,30 +3143,33 @@ program.command("endpoint").description("Generate Clean Architecture stack for a
2408
3143
  if (!project) return;
2409
3144
  await endpointFlow(project);
2410
3145
  });
3146
+ program.command("env-var").description("Add an environment variable across the Flutter monorepo").action(async () => {
3147
+ await envVarFlow();
3148
+ });
2411
3149
  var docsCmd = program.command("docs").description("Project documentation tools");
2412
3150
  docsCmd.command("serve").description("Start Docusaurus dev server").option("-d, --dir <path>", "project root directory", process.cwd()).action(async (options) => {
2413
3151
  const bookDir = detectBookDir(options.dir);
2414
3152
  if (!bookDir) {
2415
- console.error(chalk9.red("No Docusaurus book/ directory found. Run from a monorepo root."));
3153
+ console.error(chalk10.red("No Docusaurus book/ directory found. Run from a monorepo root."));
2416
3154
  process.exit(1);
2417
3155
  }
2418
- console.log(chalk9.cyan(`Serving docs from ${bookDir}`));
3156
+ console.log(chalk10.cyan(`Serving docs from ${bookDir}`));
2419
3157
  await serveBook(bookDir);
2420
3158
  });
2421
3159
  docsCmd.command("commands").description("Show Makefile & melos commands reference").option("-d, --dir <path>", "project root directory", process.cwd()).action(async (options) => {
2422
3160
  const commands = discoverCommands(options.dir);
2423
3161
  if (commands.length === 0) {
2424
- console.log(chalk9.yellow("No commands found. Run from a monorepo root."));
3162
+ console.log(chalk10.yellow("No commands found. Run from a monorepo root."));
2425
3163
  return;
2426
3164
  }
2427
- console.log(chalk9.green(`Found ${commands.length} command(s)
3165
+ console.log(chalk10.green(`Found ${commands.length} command(s)
2428
3166
  `));
2429
3167
  displayCommands(commands);
2430
3168
  });
2431
3169
  docsCmd.command("architecture").description("Display monorepo architecture tree").option("-d, --dir <path>", "project root directory", process.cwd()).action(async (options) => {
2432
3170
  const info = discoverArchitecture(options.dir);
2433
3171
  if (!info) {
2434
- console.log(chalk9.yellow("No monorepo detected. Run from within a Melos monorepo."));
3172
+ console.log(chalk10.yellow("No monorepo detected. Run from within a Melos monorepo."));
2435
3173
  return;
2436
3174
  }
2437
3175
  displayArchitecture(info);