wlmaker 1.4.1 → 1.6.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.
package/dist/cli.mjs CHANGED
@@ -1,9 +1,12 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ installMcpServer
4
+ } from "./chunk-OXBECDQE.mjs";
2
5
 
3
6
  // src/cli.ts
4
7
  import { createRequire } from "module";
5
8
  import { Command } from "commander";
6
- import chalk10 from "chalk";
9
+ import chalk12 from "chalk";
7
10
 
8
11
  // src/core/create-bloc.ts
9
12
  import * as fs3 from "fs";
@@ -106,17 +109,17 @@ function hasBuildRunner(projectRoot) {
106
109
  return /build_runner/.test(pubspec);
107
110
  }
108
111
  function runBuildRunner(projectRoot) {
109
- return new Promise((resolve4) => {
112
+ return new Promise((resolve5) => {
110
113
  const child = spawn(
111
114
  "dart",
112
115
  ["run", "build_runner", "build", "--delete-conflicting-outputs"],
113
116
  { cwd: projectRoot, stdio: "inherit" }
114
117
  );
115
118
  child.on("close", (code) => {
116
- resolve4();
119
+ resolve5();
117
120
  });
118
121
  child.on("error", () => {
119
- resolve4();
122
+ resolve5();
120
123
  });
121
124
  });
122
125
  }
@@ -955,19 +958,154 @@ async function createUseCase(name, tierInput, options) {
955
958
  }
956
959
  }
957
960
 
961
+ // src/core/create-page.ts
962
+ import * as fs8 from "fs";
963
+ import * as path8 from "path";
964
+ import chalk4 from "chalk";
965
+
966
+ // src/core/page-templates.ts
967
+ import { pascalCase as pascalCase4 } from "change-case";
968
+ function pageTemplate(name, packageName) {
969
+ const pascal = pascalCase4(name);
970
+ const routePath = "/" + name;
971
+ const imports = [
972
+ `package:flutter/material.dart`,
973
+ `package:go_router/go_router.dart`,
974
+ `package:${packageName}/pages/views/${name}_view.dart`
975
+ ].sort();
976
+ return `${imports.map((i) => `import '${i}';`).join("\n")}
977
+
978
+ class ${pascal}Page extends GoRoute {
979
+ ${pascal}Page({super.name, super.routes})
980
+ : super(
981
+ path: fullPath,
982
+ pageBuilder: (context, state) =>
983
+ const MaterialPage(child: ${pascal}View()),
984
+ );
985
+
986
+ static const fullPath = '${routePath}';
987
+
988
+ static void open(BuildContext context) => context.go(fullPath);
989
+ }
990
+ `;
991
+ }
992
+ function viewTemplate(name) {
993
+ const pascal = pascalCase4(name);
994
+ return `import 'package:flutter/material.dart';
995
+
996
+ class ${pascal}View extends StatelessWidget {
997
+ const ${pascal}View({super.key});
998
+
999
+ @override
1000
+ Widget build(BuildContext context) {
1001
+ return Scaffold(
1002
+ appBar: AppBar(title: const Text('${pascal}')),
1003
+ body: const Center(child: Text('${pascal}')),
1004
+ );
1005
+ }
1006
+ }
1007
+ `;
1008
+ }
1009
+
1010
+ // src/core/create-page.ts
1011
+ var SNAKE_CASE_REGEX4 = /^[a-z][a-z0-9_]*$/;
1012
+ async function createPage(name, options) {
1013
+ const pagesPath = path8.resolve(options.pagesPath);
1014
+ if (!name || name.trim().length === 0) {
1015
+ throw new Error("Page name is required.");
1016
+ }
1017
+ if (!SNAKE_CASE_REGEX4.test(name)) {
1018
+ throw new Error("Page name must be snake_case (lowercase letters, digits, underscores).");
1019
+ }
1020
+ if (!fs8.existsSync(pagesPath)) {
1021
+ throw new Error(`Path does not exist: ${pagesPath}`);
1022
+ }
1023
+ const packageName = extractPackageName(pagesPath);
1024
+ const pageFile = path8.join(pagesPath, `${name}_page.dart`);
1025
+ const viewsDir = path8.join(pagesPath, "views");
1026
+ const viewFile = path8.join(viewsDir, `${name}_view.dart`);
1027
+ if (fs8.existsSync(pageFile)) {
1028
+ throw new Error(`Page "${name}" already exists at ${pageFile}`);
1029
+ }
1030
+ if (fs8.existsSync(viewFile)) {
1031
+ throw new Error(`View "${name}" already exists at ${viewFile}`);
1032
+ }
1033
+ fs8.mkdirSync(viewsDir, { recursive: true });
1034
+ if (!packageName) {
1035
+ throw new Error(
1036
+ `Could not determine package name from path: ${pagesPath}
1037
+ Expected path pattern: .../packages/<name>/lib/pages`
1038
+ );
1039
+ }
1040
+ fs8.writeFileSync(pageFile, pageTemplate(name, packageName));
1041
+ console.log(chalk4.green(` \u2713 Page file created: ${pageFile}`));
1042
+ fs8.writeFileSync(viewFile, viewTemplate(name));
1043
+ console.log(chalk4.green(` \u2713 View file created: ${viewFile}`));
1044
+ updateViewsBarrel(viewsDir, name);
1045
+ updatePagesBarrel(pagesPath, name);
1046
+ }
1047
+ function updateViewsBarrel(viewsDir, name) {
1048
+ const barrelPath = path8.join(viewsDir, "views.dart");
1049
+ const exportLine = `export '${name}_view.dart';`;
1050
+ let lines = [];
1051
+ if (fs8.existsSync(barrelPath)) {
1052
+ const content = fs8.readFileSync(barrelPath, "utf8");
1053
+ lines = content.split("\n").filter((l) => l.trim().length > 0);
1054
+ }
1055
+ if (lines.includes(exportLine)) {
1056
+ console.log(chalk4.blue(" \xB7 Export already in views.dart"));
1057
+ return;
1058
+ }
1059
+ lines.push(exportLine);
1060
+ lines.sort();
1061
+ fs8.writeFileSync(barrelPath, lines.join("\n") + "\n");
1062
+ console.log(chalk4.green(" \u2713 views.dart updated"));
1063
+ }
1064
+ function updatePagesBarrel(pagesPath, name) {
1065
+ const barrelPath = path8.join(pagesPath, "pages.dart");
1066
+ const pageExport = `export '${name}_page.dart';`;
1067
+ const viewsExport = `export 'views/views.dart';`;
1068
+ let lines = [];
1069
+ if (fs8.existsSync(barrelPath)) {
1070
+ const content = fs8.readFileSync(barrelPath, "utf8");
1071
+ lines = content.split("\n").filter((l) => l.trim().length > 0);
1072
+ }
1073
+ let modified = false;
1074
+ if (!lines.includes(pageExport)) {
1075
+ lines.push(pageExport);
1076
+ modified = true;
1077
+ }
1078
+ if (!lines.includes(viewsExport)) {
1079
+ lines.push(viewsExport);
1080
+ modified = true;
1081
+ }
1082
+ if (modified) {
1083
+ lines.sort();
1084
+ fs8.writeFileSync(barrelPath, lines.join("\n") + "\n");
1085
+ console.log(chalk4.green(" \u2713 pages.dart updated"));
1086
+ } else {
1087
+ console.log(chalk4.blue(" \xB7 pages.dart already up to date"));
1088
+ }
1089
+ }
1090
+ function extractPackageName(pagesPath) {
1091
+ const normalized = pagesPath.replace(/\\/g, "/");
1092
+ const match = normalized.match(/\/([^/]+)\/lib\/pages\/?$/);
1093
+ return match ? match[1] : null;
1094
+ }
1095
+
958
1096
  // src/interactive.ts
959
- import * as fs14 from "fs";
1097
+ import * as fs16 from "fs";
960
1098
  import * as os from "os";
961
- import * as path13 from "path";
962
- import { execSync as execSync2 } from "child_process";
1099
+ import * as path15 from "path";
1100
+ import { execSync as execSync3 } from "child_process";
963
1101
  import * as clack from "@clack/prompts";
964
- import chalk9 from "chalk";
1102
+ import chalk11 from "chalk";
965
1103
 
966
1104
  // src/core/create-endpoint.ts
967
- import * as fs9 from "fs";
968
- import * as path8 from "path";
969
- import chalk4 from "chalk";
970
- import { pascalCase as pascalCase4, camelCase as camelCase2 } from "change-case";
1105
+ import * as fs10 from "fs";
1106
+ import * as path9 from "path";
1107
+ import chalk5 from "chalk";
1108
+ import { pascalCase as pascalCase5, camelCase as camelCase2 } from "change-case";
971
1109
 
972
1110
  // src/core/endpoint-templates.ts
973
1111
  import { camelCase } from "change-case";
@@ -1050,8 +1188,8 @@ ${paramsClass}
1050
1188
  }
1051
1189
  `;
1052
1190
  }
1053
- function retrofitMethod(methodName, path14, httpMethod, params, returnType) {
1054
- const httpAnnotation = `@${httpMethod.toUpperCase()}('${path14}')`;
1191
+ function retrofitMethod(methodName, path16, httpMethod, params, returnType) {
1192
+ const httpAnnotation = `@${httpMethod.toUpperCase()}('${path16}')`;
1055
1193
  const paramList = params.map((p) => {
1056
1194
  if (p.isPath) return `@Path('${p.name}') ${p.type} ${p.name}`;
1057
1195
  if (p.isBody) return `@Body() ${p.type} ${p.name}`;
@@ -1117,9 +1255,9 @@ ${annotation}${useCaseClass} ${useCaseCamel}(${repoInterface} repository) =>
1117
1255
  }
1118
1256
 
1119
1257
  // src/core/dart-injector.ts
1120
- import * as fs8 from "fs";
1258
+ import * as fs9 from "fs";
1121
1259
  function injectMethod(filePath, className, methodCode, dedupKey) {
1122
- const content = fs8.readFileSync(filePath, "utf8");
1260
+ const content = fs9.readFileSync(filePath, "utf8");
1123
1261
  const dedup = dedupKey ?? findSignature(methodCode);
1124
1262
  if (dedup && content.includes(dedup)) {
1125
1263
  return;
@@ -1150,10 +1288,10 @@ function injectMethod(filePath, className, methodCode, dedupKey) {
1150
1288
  }
1151
1289
  const indentedMethod = methodCode.split("\n").map((line) => line.trim() ? ` ${line}` : "").join("\n");
1152
1290
  const newContent = content.slice(0, classEnd) + "\n" + indentedMethod + "\n" + content.slice(classEnd);
1153
- fs8.writeFileSync(filePath, newContent);
1291
+ fs9.writeFileSync(filePath, newContent);
1154
1292
  }
1155
1293
  function injectImport(filePath, importLine) {
1156
- const content = fs8.readFileSync(filePath, "utf8");
1294
+ const content = fs9.readFileSync(filePath, "utf8");
1157
1295
  if (content.includes(importLine.trim())) {
1158
1296
  return;
1159
1297
  }
@@ -1163,15 +1301,15 @@ function injectImport(filePath, importLine) {
1163
1301
  const lastImport = imports[imports.length - 1];
1164
1302
  const insertPos = lastImport.index + lastImport[0].length;
1165
1303
  const newContent = content.slice(0, insertPos) + "\n" + importLine + content.slice(insertPos);
1166
- fs8.writeFileSync(filePath, newContent);
1304
+ fs9.writeFileSync(filePath, newContent);
1167
1305
  } else {
1168
- fs8.writeFileSync(filePath, importLine + "\n\n" + content);
1306
+ fs9.writeFileSync(filePath, importLine + "\n\n" + content);
1169
1307
  }
1170
1308
  }
1171
1309
  function injectExport(filePath, exportLine) {
1172
1310
  let content = "";
1173
- if (fs8.existsSync(filePath)) {
1174
- content = fs8.readFileSync(filePath, "utf8");
1311
+ if (fs9.existsSync(filePath)) {
1312
+ content = fs9.readFileSync(filePath, "utf8");
1175
1313
  }
1176
1314
  if (content.includes(exportLine)) {
1177
1315
  return;
@@ -1179,7 +1317,7 @@ function injectExport(filePath, exportLine) {
1179
1317
  const lines = content.split("\n").filter((l) => l.trim().length > 0);
1180
1318
  lines.push(exportLine);
1181
1319
  lines.sort();
1182
- fs8.writeFileSync(filePath, lines.join("\n") + "\n");
1320
+ fs9.writeFileSync(filePath, lines.join("\n") + "\n");
1183
1321
  }
1184
1322
  function findSignature(methodCode) {
1185
1323
  const lines = methodCode.trim().split("\n");
@@ -1194,51 +1332,51 @@ function findSignature(methodCode) {
1194
1332
 
1195
1333
  // src/core/create-endpoint.ts
1196
1334
  async function createEndpoint(options) {
1197
- const lib = path8.join(options.projectRoot, "lib");
1198
- const pascal = pascalCase4(options.useCaseName);
1199
- const useCasePascal = pascalCase4(options.useCaseName);
1335
+ const lib = path9.join(options.projectRoot, "lib");
1336
+ const pascal = pascalCase5(options.useCaseName);
1337
+ const useCasePascal = pascalCase5(options.useCaseName);
1200
1338
  const useCaseSnake = options.useCaseName;
1201
1339
  const needsBody = ["POST", "PUT", "PATCH"].includes(options.httpMethod);
1202
1340
  const domain = extractDomain(options.bffApiFile);
1203
- const datasourceFile = path8.join(lib, "data", "datasources", `${domain}_rest_datasource.dart`);
1204
- const datasourceClassName = `${pascalCase4(domain)}RestDataSource`;
1205
- const repositoryInterfaceFile = path8.join(lib, "domain", "repositories", `${domain}_repository.dart`);
1206
- const repositoryInterfaceName = `${pascalCase4(domain)}Repository`;
1207
- const repositoryImplFile = path8.join(lib, "data", "repositories", `${domain}_repository_data.dart`);
1208
- const repositoryImplClassName = `${pascalCase4(domain)}RepositoryData`;
1341
+ const datasourceFile = path9.join(lib, "data", "datasources", `${domain}_rest_datasource.dart`);
1342
+ const datasourceClassName = `${pascalCase5(domain)}RestDataSource`;
1343
+ const repositoryInterfaceFile = path9.join(lib, "domain", "repositories", `${domain}_repository.dart`);
1344
+ const repositoryInterfaceName = `${pascalCase5(domain)}Repository`;
1345
+ const repositoryImplFile = path9.join(lib, "data", "repositories", `${domain}_repository_data.dart`);
1346
+ const repositoryImplClassName = `${pascalCase5(domain)}RepositoryData`;
1209
1347
  const feature = domain;
1210
1348
  const pathParams = extractPathParams(options.endpointPath);
1211
1349
  const methodParams = buildMethodParams(options, pathParams, needsBody);
1212
1350
  const methodParamsForSignature = methodParams.map((p) => ({ name: p.name, type: p.type }));
1213
1351
  const modelType = `${pascal}Model`;
1214
1352
  const entityType = `${pascal}Entity`;
1215
- const spinner3 = (msg) => console.log(chalk4.cyan(` \u2192 ${msg}`));
1353
+ const spinner3 = (msg) => console.log(chalk5.cyan(` \u2192 ${msg}`));
1216
1354
  spinner3("Generating entity");
1217
- const entityDir = path8.join(lib, "domain", "entities", feature);
1218
- fs9.mkdirSync(entityDir, { recursive: true });
1219
- fs9.writeFileSync(
1220
- path8.join(entityDir, `${useCaseSnake}_entity.dart`),
1355
+ const entityDir = path9.join(lib, "domain", "entities", feature);
1356
+ fs10.mkdirSync(entityDir, { recursive: true });
1357
+ fs10.writeFileSync(
1358
+ path9.join(entityDir, `${useCaseSnake}_entity.dart`),
1221
1359
  entityBoilerplate(pascal)
1222
1360
  );
1223
1361
  spinner3("Generating model");
1224
- const modelDir = path8.join(lib, "data", "models", feature);
1225
- fs9.mkdirSync(modelDir, { recursive: true });
1226
- fs9.writeFileSync(
1227
- path8.join(modelDir, `${useCaseSnake}_model.dart`),
1362
+ const modelDir = path9.join(lib, "data", "models", feature);
1363
+ fs10.mkdirSync(modelDir, { recursive: true });
1364
+ fs10.writeFileSync(
1365
+ path9.join(modelDir, `${useCaseSnake}_model.dart`),
1228
1366
  modelBoilerplate(useCaseSnake, pascal, options.projectName)
1229
1367
  );
1230
1368
  if (needsBody) {
1231
1369
  spinner3("Generating request model");
1232
- const reqModelDir = path8.join(lib, "data", "models", feature);
1233
- fs9.mkdirSync(reqModelDir, { recursive: true });
1234
- fs9.writeFileSync(
1235
- path8.join(reqModelDir, `${useCaseSnake}_request_model.dart`),
1370
+ const reqModelDir = path9.join(lib, "data", "models", feature);
1371
+ fs10.mkdirSync(reqModelDir, { recursive: true });
1372
+ fs10.writeFileSync(
1373
+ path9.join(reqModelDir, `${useCaseSnake}_request_model.dart`),
1236
1374
  requestModelBoilerplate(useCaseSnake, pascal)
1237
1375
  );
1238
1376
  }
1239
1377
  spinner3("Injecting Retrofit method");
1240
- const bffPath = path8.resolve(options.bffApiFile);
1241
- if (fs9.existsSync(bffPath)) {
1378
+ const bffPath = path9.resolve(options.bffApiFile);
1379
+ if (fs10.existsSync(bffPath)) {
1242
1380
  const retrofitParams = buildRetrofitParams(options, pathParams, needsBody);
1243
1381
  const method = retrofitMethod(
1244
1382
  camelCase2(options.useCaseName),
@@ -1249,11 +1387,11 @@ async function createEndpoint(options) {
1249
1387
  );
1250
1388
  injectMethod(bffPath, extractClassName(bffPath), method);
1251
1389
  } else {
1252
- console.log(chalk4.yellow(` \u26A0 BFF API file not found: ${bffPath}`));
1390
+ console.log(chalk5.yellow(` \u26A0 BFF API file not found: ${bffPath}`));
1253
1391
  }
1254
1392
  spinner3("Injecting datasource method");
1255
- const dsPath = path8.resolve(datasourceFile);
1256
- if (fs9.existsSync(dsPath)) {
1393
+ const dsPath = path9.resolve(datasourceFile);
1394
+ if (fs10.existsSync(dsPath)) {
1257
1395
  const dsMethod = datasourceMethod(
1258
1396
  camelCase2(options.useCaseName),
1259
1397
  modelType,
@@ -1261,11 +1399,11 @@ async function createEndpoint(options) {
1261
1399
  );
1262
1400
  injectMethod(dsPath, datasourceClassName, dsMethod);
1263
1401
  } else {
1264
- console.log(chalk4.yellow(` \u26A0 Datasource file not found: ${dsPath}`));
1402
+ console.log(chalk5.yellow(` \u26A0 Datasource file not found: ${dsPath}`));
1265
1403
  }
1266
1404
  spinner3("Injecting repository interface method");
1267
- const repoIfacePath = path8.resolve(repositoryInterfaceFile);
1268
- if (fs9.existsSync(repoIfacePath)) {
1405
+ const repoIfacePath = path9.resolve(repositoryInterfaceFile);
1406
+ if (fs10.existsSync(repoIfacePath)) {
1269
1407
  const ifaceMethod = repositoryInterfaceMethod(
1270
1408
  camelCase2(options.useCaseName),
1271
1409
  entityType,
@@ -1273,11 +1411,11 @@ async function createEndpoint(options) {
1273
1411
  );
1274
1412
  injectMethod(repoIfacePath, repositoryInterfaceName, ifaceMethod);
1275
1413
  } else {
1276
- console.log(chalk4.yellow(` \u26A0 Repository interface not found: ${repoIfacePath}`));
1414
+ console.log(chalk5.yellow(` \u26A0 Repository interface not found: ${repoIfacePath}`));
1277
1415
  }
1278
1416
  spinner3("Injecting repository implementation method");
1279
- const repoImplPath = path8.resolve(repositoryImplFile);
1280
- if (fs9.existsSync(repoImplPath)) {
1417
+ const repoImplPath = path9.resolve(repositoryImplFile);
1418
+ if (fs10.existsSync(repoImplPath)) {
1281
1419
  const dsVarName = camelCase2(datasourceClassName);
1282
1420
  const implMethod = repositoryImplMethod(
1283
1421
  camelCase2(options.useCaseName),
@@ -1288,13 +1426,13 @@ async function createEndpoint(options) {
1288
1426
  );
1289
1427
  injectMethod(repoImplPath, repositoryImplClassName, implMethod);
1290
1428
  } else {
1291
- console.log(chalk4.yellow(` \u26A0 Repository implementation not found: ${repoImplPath}`));
1429
+ console.log(chalk5.yellow(` \u26A0 Repository implementation not found: ${repoImplPath}`));
1292
1430
  }
1293
1431
  spinner3("Generating UseCase");
1294
- const useCaseDir = path8.join(lib, "domain", "usecases", feature);
1295
- fs9.mkdirSync(useCaseDir, { recursive: true });
1296
- fs9.writeFileSync(
1297
- path8.join(useCaseDir, `${useCaseSnake}_usecase.dart`),
1432
+ const useCaseDir = path9.join(lib, "domain", "usecases", feature);
1433
+ fs10.mkdirSync(useCaseDir, { recursive: true });
1434
+ fs10.writeFileSync(
1435
+ path9.join(useCaseDir, `${useCaseSnake}_usecase.dart`),
1298
1436
  useCaseTemplate2(
1299
1437
  useCasePascal,
1300
1438
  camelCase2(options.useCaseName),
@@ -1305,39 +1443,39 @@ async function createEndpoint(options) {
1305
1443
  )
1306
1444
  );
1307
1445
  spinner3("Updating barrel files");
1308
- const dsFileName = path8.basename(datasourceFile);
1446
+ const dsFileName = path9.basename(datasourceFile);
1309
1447
  injectExport(
1310
- path8.join(lib, "data", "datasources", "datasources.dart"),
1448
+ path9.join(lib, "data", "datasources", "datasources.dart"),
1311
1449
  `export '${dsFileName}';`
1312
1450
  );
1313
- const entityBarrel = path8.join(lib, "domain", "entities", feature, `${feature}.dart`);
1451
+ const entityBarrel = path9.join(lib, "domain", "entities", feature, `${feature}.dart`);
1314
1452
  injectExport(entityBarrel, `export '${useCaseSnake}_entity.dart';`);
1315
1453
  injectExport(
1316
- path8.join(lib, "domain", "entities", "entities.dart"),
1454
+ path9.join(lib, "domain", "entities", "entities.dart"),
1317
1455
  `export '${feature}/${feature}.dart';`
1318
1456
  );
1319
- const modelBarrel = path8.join(lib, "data", "models", feature, `${feature}.dart`);
1457
+ const modelBarrel = path9.join(lib, "data", "models", feature, `${feature}.dart`);
1320
1458
  injectExport(modelBarrel, `export '${useCaseSnake}_model.dart';`);
1321
1459
  if (needsBody) {
1322
1460
  injectExport(modelBarrel, `export '${useCaseSnake}_request_model.dart';`);
1323
1461
  }
1324
1462
  injectExport(
1325
- path8.join(lib, "data", "models", "models.dart"),
1463
+ path9.join(lib, "data", "models", "models.dart"),
1326
1464
  `export '${feature}/${feature}.dart';`
1327
1465
  );
1328
- const useCaseBarrel = path8.join(useCaseDir, `${feature}.dart`);
1466
+ const useCaseBarrel = path9.join(useCaseDir, `${feature}.dart`);
1329
1467
  injectExport(useCaseBarrel, `export '${useCaseSnake}_usecase.dart';`);
1330
1468
  injectExport(
1331
- path8.join(lib, "domain", "usecases", "usecases.dart"),
1469
+ path9.join(lib, "domain", "usecases", "usecases.dart"),
1332
1470
  `export '${feature}/${feature}.dart';`
1333
1471
  );
1334
1472
  if (options.diTarget && options.diTarget !== "none") {
1335
1473
  spinner3("Registering in DI modules");
1336
1474
  const appBaseDir = findAppBasePackageDir(options.projectRoot, options.diTarget);
1337
1475
  if (appBaseDir) {
1338
- const domainPascal = pascalCase4(domain);
1476
+ const domainPascal = pascalCase5(domain);
1339
1477
  const lazy = options.diLazySingleton !== false;
1340
- const dsModuleFile = path8.join(appBaseDir, "datasources_module.dart");
1478
+ const dsModuleFile = path9.join(appBaseDir, "datasources_module.dart");
1341
1479
  const dsRegistration = datasourceModuleRegistration(domainPascal, lazy);
1342
1480
  injectModuleRegistration(
1343
1481
  dsModuleFile,
@@ -1345,7 +1483,7 @@ async function createEndpoint(options) {
1345
1483
  dsRegistration,
1346
1484
  `${domainPascal}RestDataSource`
1347
1485
  );
1348
- const repoModuleFile = path8.join(appBaseDir, "repositories_module.dart");
1486
+ const repoModuleFile = path9.join(appBaseDir, "repositories_module.dart");
1349
1487
  const repoRegistration = repositoryModuleRegistration(domainPascal, lazy);
1350
1488
  injectModuleRegistration(
1351
1489
  repoModuleFile,
@@ -1353,7 +1491,7 @@ async function createEndpoint(options) {
1353
1491
  repoRegistration,
1354
1492
  `${domainPascal}RepositoryData`
1355
1493
  );
1356
- const ucModuleFile = path8.join(appBaseDir, "usecases_module.dart");
1494
+ const ucModuleFile = path9.join(appBaseDir, "usecases_module.dart");
1357
1495
  const ucRegistration = useCaseModuleRegistration(useCasePascal, domainPascal, lazy);
1358
1496
  injectModuleRegistration(
1359
1497
  ucModuleFile,
@@ -1366,14 +1504,14 @@ async function createEndpoint(options) {
1366
1504
  injectImport(repoModuleFile, coreImport);
1367
1505
  injectImport(ucModuleFile, coreImport);
1368
1506
  } else {
1369
- console.log(chalk4.yellow(` \u26A0 Package "${options.diTarget}" not found in monorepo`));
1507
+ console.log(chalk5.yellow(` \u26A0 Package "${options.diTarget}" not found in monorepo`));
1370
1508
  }
1371
1509
  }
1372
- console.log(chalk4.green(`
1510
+ console.log(chalk5.green(`
1373
1511
  \u2713 Endpoint "${options.useCaseName}" generated successfully.`));
1374
1512
  }
1375
1513
  function extractDomain(bffApiFile) {
1376
- const baseName = path8.basename(bffApiFile);
1514
+ const baseName = path9.basename(bffApiFile);
1377
1515
  const match = baseName.match(/^bff_(.+)_api\.dart$/);
1378
1516
  if (match) return match[1];
1379
1517
  return baseName.replace(/\.dart$/, "");
@@ -1390,7 +1528,7 @@ function extractPathParams(endpointPath) {
1390
1528
  function buildMethodParams(options, pathParams, hasRequestBody) {
1391
1529
  const params = [...pathParams];
1392
1530
  if (hasRequestBody) {
1393
- const reqPascal = pascalCase4(options.useCaseName);
1531
+ const reqPascal = pascalCase5(options.useCaseName);
1394
1532
  params.push({ name: "body", type: `${reqPascal}RequestModel` });
1395
1533
  }
1396
1534
  return params;
@@ -1401,43 +1539,43 @@ function buildRetrofitParams(options, pathParams, hasRequestBody) {
1401
1539
  params.push({ ...pp, isPath: true });
1402
1540
  }
1403
1541
  if (hasRequestBody) {
1404
- const reqPascal = pascalCase4(options.useCaseName);
1542
+ const reqPascal = pascalCase5(options.useCaseName);
1405
1543
  params.push({ name: "body", type: `${reqPascal}RequestModel`, isBody: true });
1406
1544
  }
1407
1545
  return params;
1408
1546
  }
1409
1547
  function extractClassName(filePath) {
1410
- const content = fs9.readFileSync(filePath, "utf8");
1548
+ const content = fs10.readFileSync(filePath, "utf8");
1411
1549
  const match = content.match(/class\s+(\w+)\s+/);
1412
1550
  if (!match) throw new Error(`No class found in ${filePath}`);
1413
1551
  return match[1];
1414
1552
  }
1415
1553
  function findAppBasePackageDir(projectRoot, packageName) {
1416
- let dir = path8.resolve(projectRoot);
1554
+ let dir = path9.resolve(projectRoot);
1417
1555
  for (let i = 0; i < 10; i++) {
1418
- const candidate = path8.join(dir, "packages", packageName, "lib", "dependencies");
1419
- if (fs9.existsSync(candidate)) {
1556
+ const candidate = path9.join(dir, "packages", packageName, "lib", "dependencies");
1557
+ if (fs10.existsSync(candidate)) {
1420
1558
  return candidate;
1421
1559
  }
1422
- const parent = path8.dirname(dir);
1560
+ const parent = path9.dirname(dir);
1423
1561
  if (parent === dir) break;
1424
1562
  dir = parent;
1425
1563
  }
1426
1564
  return null;
1427
1565
  }
1428
1566
  function injectModuleRegistration(filePath, className, registrationCode, dedupKey) {
1429
- if (!fs9.existsSync(filePath)) {
1430
- console.log(chalk4.yellow(` \u26A0 Module file not found: ${filePath}`));
1567
+ if (!fs10.existsSync(filePath)) {
1568
+ console.log(chalk5.yellow(` \u26A0 Module file not found: ${filePath}`));
1431
1569
  return;
1432
1570
  }
1433
- const content = fs9.readFileSync(filePath, "utf8");
1571
+ const content = fs10.readFileSync(filePath, "utf8");
1434
1572
  if (content.includes(dedupKey)) {
1435
1573
  return;
1436
1574
  }
1437
1575
  const classRegex = new RegExp(`class\\s+${className}\\s*[^{]*\\{`);
1438
1576
  const classMatch = content.match(classRegex);
1439
1577
  if (!classMatch) {
1440
- console.log(chalk4.yellow(` \u26A0 Class "${className}" not found in ${filePath}`));
1578
+ console.log(chalk5.yellow(` \u26A0 Class "${className}" not found in ${filePath}`));
1441
1579
  return;
1442
1580
  }
1443
1581
  const classStart = content.indexOf(classMatch[0]);
@@ -1457,19 +1595,19 @@ function injectModuleRegistration(filePath, className, registrationCode, dedupKe
1457
1595
  }
1458
1596
  }
1459
1597
  if (classEnd === -1) {
1460
- console.log(chalk4.yellow(` \u26A0 Could not find closing brace for "${className}" in ${filePath}`));
1598
+ console.log(chalk5.yellow(` \u26A0 Could not find closing brace for "${className}" in ${filePath}`));
1461
1599
  return;
1462
1600
  }
1463
1601
  const indentedCode = registrationCode.split("\n").map((line) => line.trim() ? ` ${line}` : "").join("\n");
1464
1602
  const newContent = content.slice(0, classEnd) + "\n" + indentedCode + "\n" + content.slice(classEnd);
1465
- fs9.writeFileSync(filePath, newContent);
1603
+ fs10.writeFileSync(filePath, newContent);
1466
1604
  }
1467
1605
 
1468
1606
  // src/core/create-env-var.ts
1469
- import * as fs10 from "fs";
1470
- import * as path9 from "path";
1607
+ import * as fs11 from "fs";
1608
+ import * as path10 from "path";
1471
1609
  import { execSync } from "child_process";
1472
- import chalk5 from "chalk";
1610
+ import chalk6 from "chalk";
1473
1611
  import { camelCase as camelCase3 } from "change-case";
1474
1612
  function screamingSnakeToCamel(name) {
1475
1613
  return camelCase3(name.toLowerCase());
@@ -1557,38 +1695,39 @@ function appConfigModelJsonKey(camelName, dartType) {
1557
1695
  }
1558
1696
  }
1559
1697
  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();
1698
+ const appsDir = path10.join(monorepoRoot, "apps");
1699
+ if (!fs11.existsSync(appsDir)) return [];
1700
+ return fs11.readdirSync(appsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).filter((d) => fs11.existsSync(path10.join(appsDir, d.name, "env"))).map((d) => d.name).sort();
1563
1701
  }
1564
1702
  function injectIntoJsonFile(filePath, key, value, isTemplate) {
1565
- if (!fs10.existsSync(filePath)) {
1566
- console.log(chalk5.yellow(` Skipping ${filePath} (not found)`));
1567
- return;
1703
+ if (!fs11.existsSync(filePath)) {
1704
+ console.log(chalk6.yellow(` Skipping ${filePath} (not found)`));
1705
+ return null;
1568
1706
  }
1569
- const content = fs10.readFileSync(filePath, "utf8");
1707
+ const content = fs11.readFileSync(filePath, "utf8");
1570
1708
  const json = JSON.parse(content);
1571
1709
  if (key in json) {
1572
- console.log(chalk5.yellow(` Key "${key}" already exists in ${path9.basename(filePath)}`));
1573
- return;
1710
+ console.log(chalk6.yellow(` Key "${key}" already exists in ${path10.basename(filePath)}`));
1711
+ return null;
1574
1712
  }
1575
1713
  json[key] = isTemplate ? `\${${key}}` : value;
1576
- fs10.writeFileSync(filePath, JSON.stringify(json, null, 2) + "\n");
1577
- console.log(chalk5.green(` Updated ${path9.basename(filePath)}`));
1714
+ fs11.writeFileSync(filePath, JSON.stringify(json, null, 2) + "\n");
1715
+ console.log(chalk6.green(` Updated ${path10.basename(filePath)}`));
1716
+ return filePath;
1578
1717
  }
1579
1718
  function injectAbstractGetter(monorepoRoot, camelName, dartType) {
1580
- const packagesDir = path9.join(monorepoRoot, "packages");
1581
- if (!fs10.existsSync(packagesDir)) return;
1719
+ const packagesDir = path10.join(monorepoRoot, "packages");
1720
+ if (!fs11.existsSync(packagesDir)) return null;
1582
1721
  const envFile = findFileInPackages(packagesDir, "app_environment.dart");
1583
1722
  if (!envFile) {
1584
- console.log(chalk5.yellow(" app_environment.dart not found in any package"));
1585
- return;
1723
+ console.log(chalk6.yellow(" app_environment.dart not found in any package"));
1724
+ return null;
1586
1725
  }
1587
- const content = fs10.readFileSync(envFile, "utf8");
1726
+ const content = fs11.readFileSync(envFile, "utf8");
1588
1727
  const dedup = `get ${camelName}`;
1589
1728
  if (content.includes(dedup)) {
1590
- console.log(chalk5.yellow(` '${camelName}' already in AppEnvironment`));
1591
- return;
1729
+ console.log(chalk6.yellow(` '${camelName}' already in AppEnvironment`));
1730
+ return null;
1592
1731
  }
1593
1732
  const getterRegex = /\n\s+\w+(?:<[^>]+>)?\s+get\s+\w+;/g;
1594
1733
  let lastGetterMatch = null;
@@ -1597,43 +1736,46 @@ function injectAbstractGetter(monorepoRoot, camelName, dartType) {
1597
1736
  lastGetterMatch = match;
1598
1737
  }
1599
1738
  if (!lastGetterMatch) {
1600
- console.log(chalk5.yellow(" No getters found in AppEnvironment"));
1601
- return;
1739
+ console.log(chalk6.yellow(" No getters found in AppEnvironment"));
1740
+ return null;
1602
1741
  }
1603
1742
  const insertPos = lastGetterMatch.index + lastGetterMatch[0].length;
1604
1743
  const getterLine = `
1605
1744
  ${abstractGetterCode(camelName, dartType)}`;
1606
1745
  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)}`));
1746
+ fs11.writeFileSync(envFile, newContent);
1747
+ console.log(chalk6.green(` Injected abstract getter in ${formatPkgPath(monorepoRoot, envFile)}`));
1748
+ return envFile;
1609
1749
  }
1610
1750
  function injectBuildEnvGetter(monorepoRoot, variableName, camelName, dartType) {
1611
- const packagesDir = path9.join(monorepoRoot, "packages");
1612
- if (!fs10.existsSync(packagesDir)) return;
1751
+ const packagesDir = path10.join(monorepoRoot, "packages");
1752
+ if (!fs11.existsSync(packagesDir)) return null;
1613
1753
  const buildEnvFile = findFileInPackages(packagesDir, "app_build_environment.dart");
1614
1754
  if (!buildEnvFile) {
1615
- console.log(chalk5.yellow(" app_build_environment.dart not found in any package"));
1616
- return;
1755
+ console.log(chalk6.yellow(" app_build_environment.dart not found in any package"));
1756
+ return null;
1617
1757
  }
1618
1758
  const code = buildEnvGetterCode(camelName, variableName, dartType);
1619
1759
  try {
1620
1760
  injectMethod(buildEnvFile, "AppBuildEnvironment", code, `get ${camelName}`);
1621
- console.log(chalk5.green(` Injected build env getter in ${formatPkgPath(monorepoRoot, buildEnvFile)}`));
1761
+ console.log(chalk6.green(` Injected build env getter in ${formatPkgPath(monorepoRoot, buildEnvFile)}`));
1762
+ return buildEnvFile;
1622
1763
  } catch (e) {
1623
- console.log(chalk5.yellow(` Skipped: ${e.message}`));
1764
+ console.log(chalk6.yellow(` Skipped: ${e.message}`));
1765
+ return null;
1624
1766
  }
1625
1767
  }
1626
1768
  function injectIntoRemoteConfigDefaults(vendorsPath, camelName, dartType) {
1627
- const content = fs10.readFileSync(vendorsPath, "utf8");
1769
+ const content = fs11.readFileSync(vendorsPath, "utf8");
1628
1770
  if (content.includes(`'${camelName}'`) || content.includes(`"${camelName}"`)) {
1629
- console.log(chalk5.yellow(` '${camelName}' already in ${path9.basename(vendorsPath)}`));
1630
- return;
1771
+ console.log(chalk6.yellow(` '${camelName}' already in ${path10.basename(vendorsPath)}`));
1772
+ return null;
1631
1773
  }
1632
1774
  const defaultValuesRegex = /final\s+defaultValues\s*=\s*(?:<String,\s*dynamic>\s*)?\{/;
1633
1775
  const match = content.match(defaultValuesRegex);
1634
1776
  if (!match) {
1635
- console.log(chalk5.yellow(` Could not find defaultValues map in ${path9.basename(vendorsPath)}`));
1636
- return;
1777
+ console.log(chalk6.yellow(` Could not find defaultValues map in ${path10.basename(vendorsPath)}`));
1778
+ return null;
1637
1779
  }
1638
1780
  const mapStart = content.indexOf(match[0]) + match[0].length;
1639
1781
  let braceCount = 1;
@@ -1649,8 +1791,8 @@ function injectIntoRemoteConfigDefaults(vendorsPath, camelName, dartType) {
1649
1791
  }
1650
1792
  }
1651
1793
  if (mapEnd === -1) {
1652
- console.log(chalk5.yellow(` Could not find end of defaultValues map`));
1653
- return;
1794
+ console.log(chalk6.yellow(` Could not find end of defaultValues map`));
1795
+ return null;
1654
1796
  }
1655
1797
  const valueExpr = dartType === "List<String>" ? `jsonEncode(env.${camelName})` : `env.${camelName}`;
1656
1798
  const beforeClose = content.slice(0, mapEnd);
@@ -1667,48 +1809,49 @@ function injectIntoRemoteConfigDefaults(vendorsPath, camelName, dartType) {
1667
1809
  const insertion = `${lastEntryIndent}'${camelName}': ${valueExpr},
1668
1810
  `;
1669
1811
  const newContent = content.slice(0, mapEnd) + insertion + content.slice(mapEnd);
1670
- fs10.writeFileSync(vendorsPath, newContent);
1671
- console.log(chalk5.green(` Injected into VendorsModule defaultValues`));
1812
+ fs11.writeFileSync(vendorsPath, newContent);
1813
+ console.log(chalk6.green(` Injected into VendorsModule defaultValues`));
1814
+ return vendorsPath;
1672
1815
  }
1673
1816
  function injectAppConfigProperty(configPath, camelName, dartType) {
1674
- const content = fs10.readFileSync(configPath, "utf8");
1817
+ const content = fs11.readFileSync(configPath, "utf8");
1675
1818
  if (content.includes(`final ${dartTypeForGetter(dartType)} ${camelName}`)) {
1676
- console.log(chalk5.yellow(` '${camelName}' already in AppConfig`));
1677
- return;
1819
+ console.log(chalk6.yellow(` '${camelName}' already in AppConfig`));
1820
+ return null;
1678
1821
  }
1679
1822
  const fieldLine = ` final ${dartTypeForGetter(dartType)} ${camelName};
1680
1823
  `;
1681
1824
  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);
1825
+ const propsSeparator = newContent.search(/\n\n(\s*)@override\n\s+List<Object\?>/);
1826
+ if (propsSeparator !== -1) {
1827
+ newContent = newContent.slice(0, propsSeparator + 1) + fieldLine + newContent.slice(propsSeparator + 1);
1686
1828
  } else {
1687
1829
  newContent = injectBeforeClassClose(newContent, "AppConfig", ` final ${dartTypeForGetter(dartType)} ${camelName};`);
1688
1830
  }
1689
1831
  newContent = injectConstructorParam(newContent, "AppConfig", camelName, dartType);
1690
1832
  newContent = injectIntoPropsList(newContent, camelName);
1691
- fs10.writeFileSync(configPath, newContent);
1692
- console.log(chalk5.green(` Injected into AppConfig entity`));
1833
+ fs11.writeFileSync(configPath, newContent);
1834
+ console.log(chalk6.green(` Injected into AppConfig entity`));
1835
+ return configPath;
1693
1836
  }
1694
1837
  function injectAppConfigModelProperty(modelPath, camelName, dartType) {
1695
- const content = fs10.readFileSync(modelPath, "utf8");
1838
+ const content = fs11.readFileSync(modelPath, "utf8");
1696
1839
  if (content.includes(`final ${dartTypeForGetter(dartType)} ${camelName}`)) {
1697
- console.log(chalk5.yellow(` '${camelName}' already in AppConfigModel`));
1698
- return;
1840
+ console.log(chalk6.yellow(` '${camelName}' already in AppConfigModel`));
1841
+ return null;
1699
1842
  }
1700
1843
  const jsonKey = appConfigModelJsonKey(camelName, dartType);
1701
- const fieldLines = jsonKey ? ` ${jsonKey}
1844
+ const fieldLines = jsonKey ? `
1845
+ ${jsonKey}
1846
+ final ${dartTypeForGetter(dartType)} ${camelName};
1847
+ ` : `
1702
1848
  final ${dartTypeForGetter(dartType)} ${camelName};
1703
- ` : ` final ${dartTypeForGetter(dartType)} ${camelName};
1704
1849
  `;
1705
1850
  let newContent = content;
1706
1851
  const staticPos = newContent.search(/\n\s+static\s/);
1707
1852
  const factoryPos = newContent.search(/\n\s+factory\s+AppConfigModel/);
1708
1853
  let insertAnchor = -1;
1709
- if (staticPos !== -1 && factoryPos !== -1) {
1710
- insertAnchor = Math.min(staticPos, factoryPos);
1711
- } else if (staticPos !== -1) {
1854
+ if (staticPos !== -1) {
1712
1855
  insertAnchor = staticPos;
1713
1856
  } else if (factoryPos !== -1) {
1714
1857
  insertAnchor = factoryPos;
@@ -1720,8 +1863,9 @@ function injectAppConfigModelProperty(modelPath, camelName, dartType) {
1720
1863
  }
1721
1864
  newContent = injectConstructorParam(newContent, "AppConfigModel", camelName, dartType);
1722
1865
  newContent = injectIntoSuperCall(newContent, camelName);
1723
- fs10.writeFileSync(modelPath, newContent);
1724
- console.log(chalk5.green(` Injected into AppConfigModel`));
1866
+ fs11.writeFileSync(modelPath, newContent);
1867
+ console.log(chalk6.green(` Injected into AppConfigModel`));
1868
+ return modelPath;
1725
1869
  }
1726
1870
  function injectBeforeClassClose(content, className, line) {
1727
1871
  const classRegex = new RegExp(`class\\s+${className}\\s*[^{]*\\{`);
@@ -1893,9 +2037,9 @@ function injectIntoSuperCall(content, camelName) {
1893
2037
  return content.slice(0, parenEnd) + insertion + content.slice(parenEnd);
1894
2038
  }
1895
2039
  function findFileRecursive(dir, fileName) {
1896
- const entries = fs10.readdirSync(dir, { withFileTypes: true });
2040
+ const entries = fs11.readdirSync(dir, { withFileTypes: true });
1897
2041
  for (const entry of entries) {
1898
- const fullPath = path9.join(dir, entry.name);
2042
+ const fullPath = path10.join(dir, entry.name);
1899
2043
  if (entry.isDirectory()) {
1900
2044
  const result = findFileRecursive(fullPath, fileName);
1901
2045
  if (result) return result;
@@ -1906,29 +2050,29 @@ function findFileRecursive(dir, fileName) {
1906
2050
  return null;
1907
2051
  }
1908
2052
  function discoverVendorsModules(monorepoRoot) {
1909
- const packagesDir = path9.join(monorepoRoot, "packages");
1910
- if (!fs10.existsSync(packagesDir)) return [];
2053
+ const packagesDir = path10.join(monorepoRoot, "packages");
2054
+ if (!fs11.existsSync(packagesDir)) return [];
1911
2055
  const results = [];
1912
- const packages = fs10.readdirSync(packagesDir, { withFileTypes: true }).filter((d) => d.isDirectory());
2056
+ const packages = fs11.readdirSync(packagesDir, { withFileTypes: true }).filter((d) => d.isDirectory());
1913
2057
  for (const pkg2 of packages) {
1914
- const pkgDir = path9.join(packagesDir, pkg2.name);
2058
+ const pkgDir = path10.join(packagesDir, pkg2.name);
1915
2059
  const found = findFileRecursive(pkgDir, "vendors_module.dart");
1916
2060
  if (found) results.push(pkg2.name);
1917
2061
  }
1918
2062
  return results.sort();
1919
2063
  }
1920
2064
  function findAppConfigFile(monorepoRoot) {
1921
- const packagesDir = path9.join(monorepoRoot, "packages");
1922
- if (!fs10.existsSync(packagesDir)) return null;
2065
+ const packagesDir = path10.join(monorepoRoot, "packages");
2066
+ if (!fs11.existsSync(packagesDir)) return null;
1923
2067
  return findClassInPackages(packagesDir, "AppConfig");
1924
2068
  }
1925
2069
  function findAppConfigModelFile(monorepoRoot) {
1926
- const packagesDir = path9.join(monorepoRoot, "packages");
1927
- if (!fs10.existsSync(packagesDir)) return null;
2070
+ const packagesDir = path10.join(monorepoRoot, "packages");
2071
+ if (!fs11.existsSync(packagesDir)) return null;
1928
2072
  return findClassInPackages(packagesDir, "AppConfigModel");
1929
2073
  }
1930
2074
  function findClassInPackages(packagesDir, className) {
1931
- const packages = fs10.readdirSync(packagesDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => path9.join(packagesDir, d.name));
2075
+ const packages = fs11.readdirSync(packagesDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => path10.join(packagesDir, d.name));
1932
2076
  for (const pkgDir of packages) {
1933
2077
  const found = findClassFileRecursive(pkgDir, className);
1934
2078
  if (found) return found;
@@ -1937,14 +2081,14 @@ function findClassInPackages(packagesDir, className) {
1937
2081
  }
1938
2082
  function findClassFileRecursive(dir, className) {
1939
2083
  const classRegex = new RegExp(`\\bclass\\s+${className}\\b`);
1940
- const entries = fs10.readdirSync(dir, { withFileTypes: true });
2084
+ const entries = fs11.readdirSync(dir, { withFileTypes: true });
1941
2085
  for (const entry of entries) {
1942
- const fullPath = path9.join(dir, entry.name);
2086
+ const fullPath = path10.join(dir, entry.name);
1943
2087
  if (entry.isDirectory()) {
1944
2088
  const result = findClassFileRecursive(fullPath, className);
1945
2089
  if (result) return result;
1946
2090
  } else if (entry.name.endsWith(".dart") && !entry.name.endsWith(".g.dart")) {
1947
- const content = fs10.readFileSync(fullPath, "utf8");
2091
+ const content = fs11.readFileSync(fullPath, "utf8");
1948
2092
  if (classRegex.test(content)) {
1949
2093
  return fullPath;
1950
2094
  }
@@ -1953,7 +2097,7 @@ function findClassFileRecursive(dir, className) {
1953
2097
  return null;
1954
2098
  }
1955
2099
  function findFileInPackages(packagesDir, fileName) {
1956
- const packages = fs10.readdirSync(packagesDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => path9.join(packagesDir, d.name));
2100
+ const packages = fs11.readdirSync(packagesDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => path10.join(packagesDir, d.name));
1957
2101
  for (const pkgDir of packages) {
1958
2102
  const found = findFileRecursive(pkgDir, fileName);
1959
2103
  if (found) return found;
@@ -1961,9 +2105,9 @@ function findFileInPackages(packagesDir, fileName) {
1961
2105
  return null;
1962
2106
  }
1963
2107
  function formatPkgPath(monorepoRoot, absPath) {
1964
- return path9.relative(monorepoRoot, absPath);
2108
+ return path10.relative(monorepoRoot, absPath);
1965
2109
  }
1966
- var spinner = (msg) => console.log(chalk5.cyan(` \u2192 ${msg}`));
2110
+ var spinner = (msg) => console.log(chalk6.cyan(` \u2192 ${msg}`));
1967
2111
  async function createEnvVar(options) {
1968
2112
  const {
1969
2113
  monorepoRoot,
@@ -1975,37 +2119,42 @@ async function createEnvVar(options) {
1975
2119
  includeInAppConfig
1976
2120
  } = options;
1977
2121
  const camelName = screamingSnakeToCamel(variableName);
1978
- console.log(chalk5.bold(`
1979
- Adding environment variable: ${chalk5.cyan(variableName)}
2122
+ const modifiedFiles = [];
2123
+ console.log(chalk6.bold(`
2124
+ Adding environment variable: ${chalk6.cyan(variableName)}
1980
2125
  `));
1981
2126
  spinner("PASO 1: Updating env JSON files");
1982
2127
  const jsonValue = typedJsonValue(defaultValue, dartType);
1983
- const appsDir = path9.join(monorepoRoot, "apps");
2128
+ const appsDir = path10.join(monorepoRoot, "apps");
1984
2129
  for (const app of selectedApps) {
1985
- const appEnvDir = path9.join(appsDir, app, "env");
2130
+ const appEnvDir = path10.join(appsDir, app, "env");
1986
2131
  const files = [
1987
2132
  { name: "example.env.json", isTemplate: true },
1988
2133
  { name: "development.env.json", isTemplate: false },
1989
2134
  { name: "production.env.json", isTemplate: false }
1990
2135
  ];
1991
2136
  for (const { name, isTemplate } of files) {
1992
- injectIntoJsonFile(path9.join(appEnvDir, name), variableName, jsonValue, isTemplate);
2137
+ const modified = injectIntoJsonFile(path10.join(appEnvDir, name), variableName, jsonValue, isTemplate);
2138
+ if (modified) modifiedFiles.push(modified);
1993
2139
  }
1994
2140
  }
1995
2141
  spinner("PASO 2: Injecting into AppEnvironment");
1996
- injectAbstractGetter(monorepoRoot, camelName, dartType);
2142
+ const envFile = injectAbstractGetter(monorepoRoot, camelName, dartType);
2143
+ if (envFile) modifiedFiles.push(envFile);
1997
2144
  spinner("PASO 3: Injecting into AppBuildEnvironment");
1998
- injectBuildEnvGetter(monorepoRoot, variableName, camelName, dartType);
2145
+ const buildEnvFile = injectBuildEnvGetter(monorepoRoot, variableName, camelName, dartType);
2146
+ if (buildEnvFile) modifiedFiles.push(buildEnvFile);
1999
2147
  if (includeInRemoteConfig) {
2000
2148
  spinner("PASO 4: Injecting into VendorsModule RemoteConfig");
2001
2149
  for (const pkgName of options.vendorsTargets) {
2002
- const pkgDir = path9.join(monorepoRoot, "packages", pkgName);
2150
+ const pkgDir = path10.join(monorepoRoot, "packages", pkgName);
2003
2151
  const vendorsPath = findFileRecursive(pkgDir, "vendors_module.dart");
2004
2152
  if (vendorsPath) {
2005
- console.log(chalk5.cyan(` \u2192 ${pkgName}/vendors_module.dart`));
2006
- injectIntoRemoteConfigDefaults(vendorsPath, camelName, dartType);
2153
+ console.log(chalk6.cyan(` \u2192 ${pkgName}/vendors_module.dart`));
2154
+ const modified = injectIntoRemoteConfigDefaults(vendorsPath, camelName, dartType);
2155
+ if (modified) modifiedFiles.push(modified);
2007
2156
  } else {
2008
- console.log(chalk5.yellow(` vendors_module.dart not found in ${pkgName}`));
2157
+ console.log(chalk6.yellow(` vendors_module.dart not found in ${pkgName}`));
2009
2158
  }
2010
2159
  }
2011
2160
  }
@@ -2013,46 +2162,476 @@ Adding environment variable: ${chalk5.cyan(variableName)}
2013
2162
  spinner("PASO 5: Injecting into AppConfig entity");
2014
2163
  const configPath = findAppConfigFile(monorepoRoot);
2015
2164
  if (configPath) {
2016
- injectAppConfigProperty(configPath, camelName, dartType);
2165
+ const modified = injectAppConfigProperty(configPath, camelName, dartType);
2166
+ if (modified) modifiedFiles.push(modified);
2017
2167
  } else {
2018
- console.log(chalk5.yellow(" app_config.dart not found, skipping AppConfig"));
2168
+ console.log(chalk6.yellow(" app_config.dart not found, skipping AppConfig"));
2019
2169
  }
2020
2170
  spinner("PASO 6: Injecting into AppConfigModel");
2021
2171
  const modelPath = findAppConfigModelFile(monorepoRoot);
2022
2172
  if (modelPath) {
2023
- injectAppConfigModelProperty(modelPath, camelName, dartType);
2173
+ const modified = injectAppConfigModelProperty(modelPath, camelName, dartType);
2174
+ if (modified) modifiedFiles.push(modified);
2024
2175
  } else {
2025
- console.log(chalk5.yellow(" app_config_model.dart not found, skipping AppConfigModel"));
2176
+ console.log(chalk6.yellow(" app_config_model.dart not found, skipping AppConfigModel"));
2177
+ }
2178
+ }
2179
+ console.log(chalk6.green("\nEnvironment variable added successfully!"));
2180
+ const dartFiles = modifiedFiles.filter((f) => f.endsWith(".dart"));
2181
+ if (dartFiles.length > 0) {
2182
+ spinner("Running dart format on modified files...");
2183
+ try {
2184
+ const fileList = dartFiles.join(" ");
2185
+ execSync(`dart format ${fileList}`, { cwd: monorepoRoot, stdio: "pipe" });
2186
+ console.log(chalk6.green(` Formatted ${dartFiles.length} file(s)`));
2187
+ } catch {
2188
+ console.log(chalk6.yellow(" dart format failed (you may need to run it manually)"));
2026
2189
  }
2027
2190
  }
2028
- console.log(chalk5.green("\nEnvironment variable added successfully!"));
2029
- spinner("Running melos format...");
2191
+ }
2192
+
2193
+ // src/core/create-package.ts
2194
+ import * as fs12 from "fs";
2195
+ import * as path11 from "path";
2196
+ import { execSync as execSync2 } from "child_process";
2197
+ import chalk7 from "chalk";
2198
+ var MAIN_TESTER_CONTENT = `// ignore_for_file: avoid_print
2199
+
2200
+ import 'dart:io';
2201
+
2202
+ void main() {
2203
+ final currentDir = Directory.current;
2204
+ final testDir = Directory('\${currentDir.path}/test');
2205
+
2030
2206
  try {
2031
- execSync("melos format", { cwd: monorepoRoot, stdio: "pipe" });
2032
- console.log(chalk5.green(" melos format completed"));
2207
+ final entities = testDir.listSync(recursive: true);
2208
+ final testFiles = <String>[];
2209
+
2210
+ print('Finding test files in \${testDir.path}:');
2211
+ print('----------------------------------------');
2212
+
2213
+ for (final entity in entities) {
2214
+ if (entity is File &&
2215
+ entity.path.endsWith('_test.dart') &&
2216
+ !entity.path.endsWith('/main_test.dart')) {
2217
+ final fullPath = entity.path;
2218
+ final relativePath = fullPath.substring(
2219
+ testDir.path.length + 1,
2220
+ );
2221
+
2222
+ print('Found test file: $relativePath');
2223
+ testFiles.add(relativePath);
2224
+ }
2225
+ }
2226
+
2227
+ print('----------------------------------------');
2228
+ print('Total test files found: \${testFiles.length}');
2229
+
2230
+ final mainTestContent = StringBuffer();
2231
+
2232
+ for (final file in testFiles) {
2233
+ final importName = file.replaceAll('/', '_').replaceAll('.dart', '');
2234
+ mainTestContent.writeln("import '$file' as $importName;");
2235
+ }
2236
+
2237
+ mainTestContent.writeln('\\nvoid main() {');
2238
+
2239
+ for (final file in testFiles) {
2240
+ final importName = file.replaceAll('/', '_').replaceAll('.dart', '');
2241
+ mainTestContent.writeln(' $importName.main();');
2242
+ }
2243
+
2244
+ mainTestContent.writeln('}');
2245
+
2246
+ final mainTestFile = File('\${testDir.path}/main_test.dart')
2247
+ ..writeAsStringSync(mainTestContent.toString());
2248
+
2249
+ print('\\nCreated file: \${mainTestFile.path}');
2250
+ print('----------------------------------------');
2251
+ } on Exception catch (e) {
2252
+ print('Error: $e');
2253
+ }
2254
+ }
2255
+ `;
2256
+ function buildPubspecContent(packageName) {
2257
+ const description = `${capitalize(packageName.replace(/_/g, " "))} package`;
2258
+ return `name: ${packageName}
2259
+ description: "${description}"
2260
+ version: 0.0.1
2261
+ publish_to: none
2262
+
2263
+ environment:
2264
+ sdk: ^3.8.1
2265
+ flutter: ">=1.17.0"
2266
+
2267
+ dependencies:
2268
+ core:
2269
+ design_system:
2270
+ flutter:
2271
+ sdk: flutter
2272
+ flutter_bloc: ^9.1.1
2273
+ freezed_annotation: ^3.1.0
2274
+ go_router: ^16.0.0
2275
+ localization:
2276
+
2277
+ dev_dependencies:
2278
+ bloc_test: ^10.0.0
2279
+ build_runner: ^2.5.4
2280
+ flutter_test:
2281
+ sdk: flutter
2282
+ freezed: ^3.2.0
2283
+ mocktail: ^1.0.4
2284
+ very_good_analysis: ^9.0.0
2285
+
2286
+ dependency_overrides:
2287
+ source_gen: ^4.2.0
2288
+
2289
+ flutter:
2290
+ uses-material-design: true
2291
+ `;
2292
+ }
2293
+ function buildGitignoreContent() {
2294
+ return `# Miscellaneous
2295
+ *.class
2296
+ *.log
2297
+ *.pyc
2298
+ *.swp
2299
+ .DS_Store
2300
+ .atom/
2301
+ .buildlog/
2302
+ .history
2303
+ .svn/
2304
+ migrate_working_dir/
2305
+
2306
+ # IntelliJ related
2307
+ *.iml
2308
+ *.ipr
2309
+ *.iws
2310
+ .idea/
2311
+
2312
+ # Flutter/Dart/Pub related
2313
+ /pubspec.lock
2314
+ **/doc/api/
2315
+ .dart_tool/
2316
+ .flutter-plugins
2317
+ .flutter-plugins-dependencies
2318
+ build/
2319
+
2320
+ coverage/
2321
+
2322
+ test/main_test.dart
2323
+ `;
2324
+ }
2325
+ function buildMakefileContent() {
2326
+ return `.PHONY: test
2327
+ test:
2328
+ dart test/main_tester.dart
2329
+ dart format test/main_test.dart
2330
+ dart fix --apply test/main_test.dart
2331
+ flutter test --coverage test/main_test.dart
2332
+ `;
2333
+ }
2334
+ function buildAnalysisOptionsContent() {
2335
+ return `include: ../../analysis_options.yaml
2336
+ `;
2337
+ }
2338
+ function buildBarrelExport(packageName) {
2339
+ return `export 'bloc/bloc.dart';
2340
+ export 'pages/pages.dart';
2341
+ `;
2342
+ }
2343
+ function buildEmptyBarrel() {
2344
+ return "";
2345
+ }
2346
+ function buildGitHubWorkflow(packageName) {
2347
+ return `name: packages/${packageName}
2348
+
2349
+ permissions:
2350
+ contents: read
2351
+ pull-requests: write
2352
+
2353
+ concurrency:
2354
+ group: \${{ github.workflow }}-\${{ github.event.pull_request.number || github.ref }}-${packageName}
2355
+ cancel-in-progress: true
2356
+
2357
+ on:
2358
+ push:
2359
+ branches:
2360
+ - master
2361
+ paths:
2362
+ - "packages/${packageName}/**"
2363
+ - ".github/workflows/${packageName}.yaml"
2364
+ - ".github/workflows/test.yaml"
2365
+ pull_request:
2366
+ branches:
2367
+ - master
2368
+ paths:
2369
+ - "packages/${packageName}/**"
2370
+ - ".github/workflows/${packageName}.yaml"
2371
+ - ".github/workflows/test.yaml"
2372
+
2373
+ jobs:
2374
+ test:
2375
+ uses: ./.github/workflows/test.yaml
2376
+ with:
2377
+ package_name: ${packageName}
2378
+ working_directory: packages/${packageName}
2379
+ `;
2380
+ }
2381
+ function capitalize(s) {
2382
+ return s.replace(/\b\w/g, (c) => c.toUpperCase());
2383
+ }
2384
+ function findWorkspaceFile(monorepoRoot) {
2385
+ const files = fs12.readdirSync(monorepoRoot);
2386
+ return files.find((f) => f.endsWith(".code-workspace")) ?? null;
2387
+ }
2388
+ function updateCodeWorkspace(monorepoRoot, packageName) {
2389
+ const wsFileName = findWorkspaceFile(monorepoRoot);
2390
+ if (!wsFileName) {
2391
+ console.log(chalk7.yellow(" No .code-workspace file found, skipping"));
2392
+ return;
2393
+ }
2394
+ const wsPath = path11.join(monorepoRoot, wsFileName);
2395
+ const content = fs12.readFileSync(wsPath, "utf8");
2396
+ const folderPath = `packages/${packageName}`;
2397
+ if (content.includes(`"path": "${folderPath}"`)) {
2398
+ console.log(chalk7.yellow(` Already in ${wsFileName}`));
2399
+ return;
2400
+ }
2401
+ const folderName = `\u{1F4E6} ${packageName}`;
2402
+ const newBlock = ` {
2403
+ "name": "${folderName}",
2404
+ "path": "${folderPath}"
2405
+ },`;
2406
+ const lines = content.split("\n");
2407
+ const foldersLineIdx = lines.findIndex((l) => l.includes('"folders"'));
2408
+ if (foldersLineIdx === -1) {
2409
+ console.log(chalk7.yellow(' No "folders" key found in workspace, skipping'));
2410
+ return;
2411
+ }
2412
+ let foldersOpenBracket = -1;
2413
+ for (let j = foldersLineIdx; j < lines.length; j++) {
2414
+ if (lines[j].includes("[")) {
2415
+ foldersOpenBracket = j;
2416
+ break;
2417
+ }
2418
+ }
2419
+ if (foldersOpenBracket === -1) {
2420
+ console.log(chalk7.yellow(" Could not find folders array, skipping"));
2421
+ return;
2422
+ }
2423
+ let bracketCount = 0;
2424
+ let foldersCloseBracket = -1;
2425
+ for (let j = foldersOpenBracket; j < lines.length; j++) {
2426
+ for (const ch of lines[j]) {
2427
+ if (ch === "[") bracketCount++;
2428
+ else if (ch === "]") {
2429
+ bracketCount--;
2430
+ if (bracketCount === 0) {
2431
+ foldersCloseBracket = j;
2432
+ break;
2433
+ }
2434
+ }
2435
+ }
2436
+ if (foldersCloseBracket !== -1) break;
2437
+ }
2438
+ if (foldersCloseBracket === -1) {
2439
+ console.log(chalk7.yellow(" Could not find folders array end, skipping"));
2440
+ return;
2441
+ }
2442
+ let widgetbookBlockStart = -1;
2443
+ let idx = foldersOpenBracket + 1;
2444
+ while (idx <= foldersCloseBracket) {
2445
+ const trimmed = lines[idx].trim();
2446
+ if (trimmed === "{" || trimmed.startsWith("{")) {
2447
+ let braceCount = 0;
2448
+ let blockEnd = -1;
2449
+ for (let j = idx; j <= foldersCloseBracket; j++) {
2450
+ for (const ch of lines[j]) {
2451
+ if (ch === "{") braceCount++;
2452
+ else if (ch === "}") {
2453
+ braceCount--;
2454
+ if (braceCount === 0) {
2455
+ blockEnd = j;
2456
+ break;
2457
+ }
2458
+ }
2459
+ }
2460
+ if (blockEnd !== -1) break;
2461
+ }
2462
+ if (blockEnd !== -1) {
2463
+ const blockText = lines.slice(idx, blockEnd + 1).join("\n");
2464
+ if (blockText.includes("widgetbook")) {
2465
+ widgetbookBlockStart = idx;
2466
+ break;
2467
+ }
2468
+ idx = blockEnd + 1;
2469
+ continue;
2470
+ }
2471
+ }
2472
+ idx++;
2473
+ }
2474
+ if (widgetbookBlockStart === -1) {
2475
+ console.log(chalk7.yellow(" No widgetbook entry found in workspace, skipping"));
2476
+ return;
2477
+ }
2478
+ lines.splice(widgetbookBlockStart, 0, newBlock);
2479
+ fs12.writeFileSync(wsPath, lines.join("\n"));
2480
+ console.log(chalk7.green(` Updated ${wsFileName}`));
2481
+ }
2482
+ function updateHelixConfig(monorepoRoot, packageName) {
2483
+ const helixPath = path11.join(monorepoRoot, ".helix", "languages.toml");
2484
+ if (!fs12.existsSync(helixPath)) {
2485
+ console.log(chalk7.yellow(" No .helix/languages.toml found, skipping"));
2486
+ return;
2487
+ }
2488
+ let content = fs12.readFileSync(helixPath, "utf8");
2489
+ const entry = `{ path = "packages/${packageName}" }`;
2490
+ if (content.includes(entry)) {
2491
+ console.log(chalk7.yellow(" Already in .helix/languages.toml"));
2492
+ return;
2493
+ }
2494
+ const workspaceLineRegex = /^\s*\{\s*path\s*=\s*"packages\//;
2495
+ const lines = content.split("\n");
2496
+ let lastPackageLineIdx = -1;
2497
+ for (let i = 0; i < lines.length; i++) {
2498
+ if (workspaceLineRegex.test(lines[i])) {
2499
+ lastPackageLineIdx = i;
2500
+ }
2501
+ }
2502
+ if (lastPackageLineIdx === -1) {
2503
+ const closingBracketIdx = lines.findIndex((l) => l.trim() === "]");
2504
+ if (closingBracketIdx === -1) {
2505
+ console.log(chalk7.yellow(" Could not parse .helix/languages.toml"));
2506
+ return;
2507
+ }
2508
+ lines.splice(closingBracketIdx, 0, ` { path = "packages/${packageName}" },`);
2509
+ } else {
2510
+ const allPackageEntries = [];
2511
+ for (let i = 0; i < lines.length; i++) {
2512
+ const m = lines[i].match(/^\s*\{\s*path\s*=\s*"packages\/([^"]+)"\s*\}/);
2513
+ if (m) {
2514
+ allPackageEntries.push({ lineIdx: i, entry: m[1] });
2515
+ }
2516
+ }
2517
+ allPackageEntries.push({ lineIdx: -1, entry: packageName });
2518
+ allPackageEntries.sort((a, b) => a.entry.localeCompare(b.entry));
2519
+ const insertIdx = allPackageEntries.findIndex((e) => e.entry === packageName);
2520
+ let targetLine;
2521
+ if (insertIdx === 0) {
2522
+ const firstLine = lines.findIndex((l) => workspaceLineRegex.test(l));
2523
+ targetLine = firstLine;
2524
+ } else {
2525
+ const prevEntry = allPackageEntries[insertIdx - 1];
2526
+ targetLine = prevEntry.lineIdx + 1;
2527
+ }
2528
+ lines.splice(targetLine, 0, ` { path = "packages/${packageName}" },`);
2529
+ }
2530
+ fs12.writeFileSync(helixPath, lines.join("\n"));
2531
+ console.log(chalk7.green(" Updated .helix/languages.toml"));
2532
+ }
2533
+ async function createPackage(options) {
2534
+ const { monorepoRoot, packageName } = options;
2535
+ const pkgDir = path11.join(monorepoRoot, "packages", packageName);
2536
+ if (fs12.existsSync(pkgDir)) {
2537
+ throw new Error(`packages/${packageName} already exists`);
2538
+ }
2539
+ console.log(chalk7.bold(`
2540
+ Creating package: ${chalk7.cyan(packageName)}
2541
+ `));
2542
+ console.log(chalk7.cyan(" \u2192 Running flutter create..."));
2543
+ execSync2(`flutter create -t package packages/${packageName}`, {
2544
+ cwd: monorepoRoot,
2545
+ stdio: "pipe"
2546
+ });
2547
+ const autoTestFile = path11.join(pkgDir, "test", `${packageName}_test.dart`);
2548
+ if (fs12.existsSync(autoTestFile)) {
2549
+ fs12.unlinkSync(autoTestFile);
2550
+ }
2551
+ console.log(chalk7.cyan(" \u2192 Customizing package files..."));
2552
+ fs12.writeFileSync(
2553
+ path11.join(pkgDir, "pubspec.yaml"),
2554
+ buildPubspecContent(packageName)
2555
+ );
2556
+ fs12.writeFileSync(
2557
+ path11.join(pkgDir, ".gitignore"),
2558
+ buildGitignoreContent()
2559
+ );
2560
+ fs12.writeFileSync(
2561
+ path11.join(pkgDir, "analysis_options.yaml"),
2562
+ buildAnalysisOptionsContent()
2563
+ );
2564
+ fs12.writeFileSync(
2565
+ path11.join(pkgDir, "Makefile"),
2566
+ buildMakefileContent()
2567
+ );
2568
+ const libDir = path11.join(pkgDir, "lib");
2569
+ fs12.writeFileSync(
2570
+ path11.join(libDir, `${packageName}.dart`),
2571
+ buildBarrelExport(packageName)
2572
+ );
2573
+ fs12.mkdirSync(path11.join(libDir, "bloc"), { recursive: true });
2574
+ fs12.writeFileSync(
2575
+ path11.join(libDir, "bloc", "bloc.dart"),
2576
+ buildEmptyBarrel()
2577
+ );
2578
+ fs12.mkdirSync(path11.join(libDir, "pages"), { recursive: true });
2579
+ fs12.writeFileSync(
2580
+ path11.join(libDir, "pages", "pages.dart"),
2581
+ buildEmptyBarrel()
2582
+ );
2583
+ const testDir = path11.join(pkgDir, "test");
2584
+ if (!fs12.existsSync(testDir)) {
2585
+ fs12.mkdirSync(testDir, { recursive: true });
2586
+ }
2587
+ fs12.writeFileSync(
2588
+ path11.join(testDir, "main_tester.dart"),
2589
+ MAIN_TESTER_CONTENT
2590
+ );
2591
+ console.log(chalk7.cyan(" \u2192 Updating workspace configuration..."));
2592
+ updateCodeWorkspace(monorepoRoot, packageName);
2593
+ updateHelixConfig(monorepoRoot, packageName);
2594
+ console.log(chalk7.cyan(" \u2192 Creating GitHub Actions workflow..."));
2595
+ const workflowsDir = path11.join(monorepoRoot, ".github", "workflows");
2596
+ if (fs12.existsSync(workflowsDir)) {
2597
+ fs12.writeFileSync(
2598
+ path11.join(workflowsDir, `${packageName}.yaml`),
2599
+ buildGitHubWorkflow(packageName)
2600
+ );
2601
+ console.log(chalk7.green(` Created .github/workflows/${packageName}.yaml`));
2602
+ } else {
2603
+ console.log(chalk7.yellow(" No .github/workflows/ directory found, skipping CI workflow"));
2604
+ }
2605
+ console.log(chalk7.cyan(" \u2192 Running melos bootstrap..."));
2606
+ try {
2607
+ execSync2("melos bootstrap", { cwd: monorepoRoot, stdio: "pipe" });
2608
+ console.log(chalk7.green(" melos bootstrap completed"));
2033
2609
  } catch {
2034
- console.log(chalk5.yellow(" melos format failed (you may need to run it manually)"));
2610
+ console.log(chalk7.yellow(" melos bootstrap failed (you may need to run it manually)"));
2035
2611
  }
2612
+ console.log(chalk7.green(`
2613
+ Package "${packageName}" created successfully!`));
2614
+ console.log(chalk7.gray(` ${pkgDir}`));
2036
2615
  }
2037
2616
 
2038
2617
  // src/core/docs-serve.ts
2039
2618
  import { spawn as spawn2 } from "child_process";
2040
- import * as fs11 from "fs";
2041
- import * as path10 from "path";
2042
- import chalk6 from "chalk";
2619
+ import * as fs13 from "fs";
2620
+ import * as path12 from "path";
2621
+ import chalk8 from "chalk";
2043
2622
  function detectBookDir(startDir) {
2044
2623
  const root = findMonorepoRoot(startDir) ?? startDir;
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")))) {
2624
+ const bookDir = path12.join(root, "book");
2625
+ if (fs13.existsSync(bookDir) && (fs13.existsSync(path12.join(bookDir, "docusaurus.config")) || fs13.existsSync(path12.join(bookDir, "docusaurus.config.ts")) || fs13.existsSync(path12.join(bookDir, "docusaurus.config.mjs")))) {
2047
2626
  return bookDir;
2048
2627
  }
2049
2628
  return void 0;
2050
2629
  }
2051
2630
  function serveBook(bookDir) {
2052
- return new Promise((resolve4, reject) => {
2053
- const nodeModules = path10.join(bookDir, "node_modules");
2054
- if (!fs11.existsSync(nodeModules)) {
2055
- console.log(chalk6.cyan("Installing book dependencies..."));
2631
+ return new Promise((resolve5, reject) => {
2632
+ const nodeModules = path12.join(bookDir, "node_modules");
2633
+ if (!fs13.existsSync(nodeModules)) {
2634
+ console.log(chalk8.cyan("Installing book dependencies..."));
2056
2635
  const install = spawn2("npm", ["install"], {
2057
2636
  cwd: bookDir,
2058
2637
  stdio: "inherit"
@@ -2062,13 +2641,13 @@ function serveBook(bookDir) {
2062
2641
  reject(new Error(`npm install failed with code ${code}`));
2063
2642
  return;
2064
2643
  }
2065
- startDevServer(bookDir, resolve4);
2644
+ startDevServer(bookDir, resolve5);
2066
2645
  });
2067
2646
  install.on("error", (err) => {
2068
2647
  reject(err);
2069
2648
  });
2070
2649
  } else {
2071
- startDevServer(bookDir, resolve4);
2650
+ startDevServer(bookDir, resolve5);
2072
2651
  }
2073
2652
  });
2074
2653
  }
@@ -2086,16 +2665,16 @@ function startDevServer(bookDir, done) {
2086
2665
  }
2087
2666
 
2088
2667
  // src/core/docs-commands.ts
2089
- import * as fs12 from "fs";
2090
- import * as path11 from "path";
2091
- import chalk7 from "chalk";
2668
+ import * as fs14 from "fs";
2669
+ import * as path13 from "path";
2670
+ import chalk9 from "chalk";
2092
2671
  import YAML2 from "yaml";
2093
2672
  function parseMakefile(filePath) {
2094
- if (!fs12.existsSync(filePath)) return [];
2095
- const content = fs12.readFileSync(filePath, "utf8");
2673
+ if (!fs14.existsSync(filePath)) return [];
2674
+ const content = fs14.readFileSync(filePath, "utf8");
2096
2675
  const lines = content.split("\n");
2097
2676
  const commands = [];
2098
- const source = path11.basename(filePath);
2677
+ const source = path13.basename(filePath);
2099
2678
  let pendingComments = [];
2100
2679
  for (let i = 0; i < lines.length; i++) {
2101
2680
  const line = lines[i];
@@ -2125,13 +2704,13 @@ function parseMakefile(filePath) {
2125
2704
  return commands;
2126
2705
  }
2127
2706
  function parseMelosScripts(filePath) {
2128
- if (!fs12.existsSync(filePath)) return [];
2129
- const content = fs12.readFileSync(filePath, "utf8");
2707
+ if (!fs14.existsSync(filePath)) return [];
2708
+ const content = fs14.readFileSync(filePath, "utf8");
2130
2709
  const parsed = YAML2.parse(content);
2131
2710
  const scripts = parsed?.scripts;
2132
2711
  if (!scripts || typeof scripts !== "object") return [];
2133
2712
  const commands = [];
2134
- const source = path11.basename(filePath);
2713
+ const source = path13.basename(filePath);
2135
2714
  for (const [name, value] of Object.entries(scripts)) {
2136
2715
  let description = "";
2137
2716
  let command = "";
@@ -2153,17 +2732,17 @@ function parseMelosScripts(filePath) {
2153
2732
  function discoverCommands(startDir) {
2154
2733
  const root = findMonorepoRoot(startDir) ?? startDir;
2155
2734
  const commands = [];
2156
- const rootMakefile = path11.join(root, "Makefile");
2735
+ const rootMakefile = path13.join(root, "Makefile");
2157
2736
  commands.push(...parseMakefile(rootMakefile));
2158
- const melosFile = path11.join(root, "melos.yaml");
2737
+ const melosFile = path13.join(root, "melos.yaml");
2159
2738
  commands.push(...parseMelosScripts(melosFile));
2160
- const bookMakefile = path11.join(root, "book", "Makefile");
2739
+ const bookMakefile = path13.join(root, "book", "Makefile");
2161
2740
  commands.push(...parseMakefile(bookMakefile));
2162
2741
  return commands;
2163
2742
  }
2164
2743
  function displayCommands(commands) {
2165
2744
  if (commands.length === 0) {
2166
- console.log(chalk7.yellow("No commands found."));
2745
+ console.log(chalk9.yellow("No commands found."));
2167
2746
  return;
2168
2747
  }
2169
2748
  const groups = /* @__PURE__ */ new Map();
@@ -2173,13 +2752,13 @@ function displayCommands(commands) {
2173
2752
  groups.get(key).push(cmd);
2174
2753
  }
2175
2754
  for (const [group, entries] of groups) {
2176
- console.log(chalk7.bold(chalk7.cyan(`
2755
+ console.log(chalk9.bold(chalk9.cyan(`
2177
2756
  ${group}`)));
2178
- console.log(chalk7.cyan("\u2500".repeat(group.length)));
2757
+ console.log(chalk9.cyan("\u2500".repeat(group.length)));
2179
2758
  const maxNameLen = Math.max(...entries.map((e) => e.name.length));
2180
2759
  for (const entry of entries) {
2181
- const name = chalk7.white(entry.name.padEnd(maxNameLen + 2));
2182
- const desc = entry.description ? chalk7.gray(entry.description) : chalk7.dim("(no description)");
2760
+ const name = chalk9.white(entry.name.padEnd(maxNameLen + 2));
2761
+ const desc = entry.description ? chalk9.gray(entry.description) : chalk9.dim("(no description)");
2183
2762
  console.log(` ${name} ${desc}`);
2184
2763
  }
2185
2764
  }
@@ -2187,9 +2766,9 @@ ${group}`)));
2187
2766
  }
2188
2767
 
2189
2768
  // src/core/docs-architecture.ts
2190
- import * as fs13 from "fs";
2191
- import * as path12 from "path";
2192
- import chalk8 from "chalk";
2769
+ import * as fs15 from "fs";
2770
+ import * as path14 from "path";
2771
+ import chalk10 from "chalk";
2193
2772
  import YAML3 from "yaml";
2194
2773
  var KEY_DEPS = [
2195
2774
  "freezed",
@@ -2207,18 +2786,18 @@ var KEY_DEPS = [
2207
2786
  function discoverArchitecture(startDir) {
2208
2787
  const root = findMonorepoRoot(startDir);
2209
2788
  if (!root) return null;
2210
- const melosPath = path12.join(root, "melos.yaml");
2211
- if (!fs13.existsSync(melosPath)) return null;
2212
- const melosContent = fs13.readFileSync(melosPath, "utf8");
2789
+ const melosPath = path14.join(root, "melos.yaml");
2790
+ if (!fs15.existsSync(melosPath)) return null;
2791
+ const melosContent = fs15.readFileSync(melosPath, "utf8");
2213
2792
  const melos = YAML3.parse(melosContent);
2214
- const projectName = melos?.name ?? path12.basename(root);
2793
+ const projectName = melos?.name ?? path14.basename(root);
2215
2794
  const apps = [];
2216
- const appsDir = path12.join(root, "apps");
2217
- if (fs13.existsSync(appsDir)) {
2218
- for (const entry of fs13.readdirSync(appsDir, { withFileTypes: true })) {
2795
+ const appsDir = path14.join(root, "apps");
2796
+ if (fs15.existsSync(appsDir)) {
2797
+ for (const entry of fs15.readdirSync(appsDir, { withFileTypes: true })) {
2219
2798
  if (!entry.isDirectory()) continue;
2220
- const candidate = path12.join(appsDir, entry.name);
2221
- if (fs13.existsSync(path12.join(candidate, "pubspec.yaml"))) {
2799
+ const candidate = path14.join(appsDir, entry.name);
2800
+ if (fs15.existsSync(path14.join(candidate, "pubspec.yaml"))) {
2222
2801
  apps.push({ name: entry.name, dir: candidate });
2223
2802
  }
2224
2803
  }
@@ -2226,14 +2805,14 @@ function discoverArchitecture(startDir) {
2226
2805
  const packages = [];
2227
2806
  const packageBases = ["packages", "packages/features"];
2228
2807
  for (const base of packageBases) {
2229
- const baseDir = path12.join(root, base);
2230
- if (!fs13.existsSync(baseDir)) continue;
2231
- for (const entry of fs13.readdirSync(baseDir, { withFileTypes: true })) {
2808
+ const baseDir = path14.join(root, base);
2809
+ if (!fs15.existsSync(baseDir)) continue;
2810
+ for (const entry of fs15.readdirSync(baseDir, { withFileTypes: true })) {
2232
2811
  if (!entry.isDirectory()) continue;
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");
2812
+ const candidate = path14.join(baseDir, entry.name);
2813
+ const pubspecPath = path14.join(candidate, "pubspec.yaml");
2814
+ if (!fs15.existsSync(pubspecPath)) continue;
2815
+ const content = fs15.readFileSync(pubspecPath, "utf8");
2237
2816
  const pubspec = YAML3.parse(content);
2238
2817
  const deps = {
2239
2818
  ...pubspec?.dependencies,
@@ -2268,30 +2847,30 @@ function displayArchitecture(info) {
2268
2847
  const L = "\u2514\u2500\u2500 ";
2269
2848
  const I = "\u2502 ";
2270
2849
  const S = " ";
2271
- console.log(chalk8.bold(chalk8.cyan(info.projectName)) + chalk8.gray(` (${info.root})`));
2850
+ console.log(chalk10.bold(chalk10.cyan(info.projectName)) + chalk10.gray(` (${info.root})`));
2272
2851
  console.log(`${I}`);
2273
2852
  if (info.apps.length > 0) {
2274
- console.log(chalk8.white(`${T}apps/`));
2853
+ console.log(chalk10.white(`${T}apps/`));
2275
2854
  for (let i = 0; i < info.apps.length; i++) {
2276
2855
  const app = info.apps[i];
2277
2856
  const prefix = i === info.apps.length - 1 ? `${I}${S}${L}` : `${I}${S}${T}`;
2278
- console.log(`${prefix}${chalk8.green(app.name)}`);
2857
+ console.log(`${prefix}${chalk10.green(app.name)}`);
2279
2858
  }
2280
2859
  }
2281
2860
  if (info.packages.length > 0) {
2282
2861
  const rootPkgs = info.packages.filter(
2283
- (p) => !p.dir.includes(`${path12.sep}features${path12.sep}`)
2862
+ (p) => !p.dir.includes(`${path14.sep}features${path14.sep}`)
2284
2863
  );
2285
2864
  const featurePkgs = info.packages.filter(
2286
- (p) => p.dir.includes(`${path12.sep}features${path12.sep}`)
2865
+ (p) => p.dir.includes(`${path14.sep}features${path14.sep}`)
2287
2866
  );
2288
- console.log(chalk8.white(`${T}packages/`));
2867
+ console.log(chalk10.white(`${T}packages/`));
2289
2868
  for (let i = 0; i < rootPkgs.length; i++) {
2290
2869
  const pkg2 = rootPkgs[i];
2291
2870
  const isLast = i === rootPkgs.length - 1 && featurePkgs.length === 0;
2292
2871
  const prefix = isLast ? `${I}${S}${L}` : `${I}${S}${T}`;
2293
- const deps = pkg2.keyDeps.length > 0 ? chalk8.dim(` [${pkg2.keyDeps.join(", ")}]`) : "";
2294
- console.log(`${prefix}${chalk8.yellow(pkg2.name)}${deps}`);
2872
+ const deps = pkg2.keyDeps.length > 0 ? chalk10.dim(` [${pkg2.keyDeps.join(", ")}]`) : "";
2873
+ console.log(`${prefix}${chalk10.yellow(pkg2.name)}${deps}`);
2295
2874
  }
2296
2875
  if (featurePkgs.length > 0) {
2297
2876
  console.log(`${I}${S}${T}features/`);
@@ -2299,33 +2878,33 @@ function displayArchitecture(info) {
2299
2878
  const pkg2 = featurePkgs[i];
2300
2879
  const isLast = i === featurePkgs.length - 1;
2301
2880
  const prefix = isLast ? `${I}${S}${S}${L}` : `${I}${S}${S}${T}`;
2302
- const deps = pkg2.keyDeps.length > 0 ? chalk8.dim(` [${pkg2.keyDeps.join(", ")}]`) : "";
2303
- console.log(`${prefix}${chalk8.yellow(pkg2.name)}${deps}`);
2881
+ const deps = pkg2.keyDeps.length > 0 ? chalk10.dim(` [${pkg2.keyDeps.join(", ")}]`) : "";
2882
+ console.log(`${prefix}${chalk10.yellow(pkg2.name)}${deps}`);
2304
2883
  }
2305
2884
  }
2306
2885
  }
2307
2886
  if (info.hasDesignSystem) {
2308
- const tiers = info.designSystemTiers.length > 0 ? chalk8.dim(` (${info.designSystemTiers.join(", ")})`) : "";
2309
- console.log(`${T}${chalk8.magenta("design_system")}${tiers}`);
2887
+ const tiers = info.designSystemTiers.length > 0 ? chalk10.dim(` (${info.designSystemTiers.join(", ")})`) : "";
2888
+ console.log(`${T}${chalk10.magenta("design_system")}${tiers}`);
2310
2889
  }
2311
- const bookDir = path12.join(info.root, "book");
2312
- if (fs13.existsSync(bookDir)) {
2313
- console.log(`${T}${chalk8.blue("book/")}` + chalk8.dim(" (Docusaurus)"));
2890
+ const bookDir = path14.join(info.root, "book");
2891
+ if (fs15.existsSync(bookDir)) {
2892
+ console.log(`${T}${chalk10.blue("book/")}` + chalk10.dim(" (Docusaurus)"));
2314
2893
  }
2315
- if (fs13.existsSync(path12.join(info.root, "melos.yaml"))) {
2316
- console.log(`${L}${chalk8.gray("melos.yaml")}`);
2894
+ if (fs15.existsSync(path14.join(info.root, "melos.yaml"))) {
2895
+ console.log(`${L}${chalk10.gray("melos.yaml")}`);
2317
2896
  }
2318
2897
  console.log();
2319
2898
  }
2320
2899
 
2321
2900
  // src/interactive.ts
2322
- var SNAKE_CASE_REGEX4 = /^[a-z][a-z0-9_]*$/;
2901
+ var SNAKE_CASE_REGEX5 = /^[a-z][a-z0-9_]*$/;
2323
2902
  async function resolveProject() {
2324
2903
  const s = clack.spinner();
2325
2904
  s.start("Analyzing current directory...");
2326
2905
  const cwdProject = analyzeProject(process.cwd());
2327
2906
  if (cwdProject && (cwdProject.hasFreezed || cwdProject.hasBloc)) {
2328
- s.stop(`Found ${chalk9.green(cwdProject.projectName)}`);
2907
+ s.stop(`Found ${chalk11.green(cwdProject.projectName)}`);
2329
2908
  return cwdProject;
2330
2909
  }
2331
2910
  s.message("Looking for Melos monorepo...");
@@ -2338,8 +2917,8 @@ async function resolveProject() {
2338
2917
  }
2339
2918
  }
2340
2919
  s.message("Scanning for Flutter projects...");
2341
- const homeDev = path13.join(os.homedir(), "Development");
2342
- if (fs14.existsSync(homeDev)) {
2920
+ const homeDev = path15.join(os.homedir(), "Development");
2921
+ if (fs16.existsSync(homeDev)) {
2343
2922
  const projects = discoverProjects(homeDev, 2);
2344
2923
  if (projects.length > 0) {
2345
2924
  s.stop(`Found ${projects.length} Flutter project(s)`);
@@ -2347,12 +2926,12 @@ async function resolveProject() {
2347
2926
  }
2348
2927
  }
2349
2928
  s.stop("No Flutter projects found");
2350
- clack.outro(chalk9.red("Could not find any Flutter project with freezed or flutter_bloc."));
2929
+ clack.outro(chalk11.red("Could not find any Flutter project with freezed or flutter_bloc."));
2351
2930
  return null;
2352
2931
  }
2353
2932
  async function selectPackage(projects) {
2354
2933
  if (projects.length === 1) {
2355
- clack.log.info(`Using ${chalk9.green(projects[0].projectName)}`);
2934
+ clack.log.info(`Using ${chalk11.green(projects[0].projectName)}`);
2356
2935
  return projects[0];
2357
2936
  }
2358
2937
  const selected = await clack.select({
@@ -2360,7 +2939,7 @@ async function selectPackage(projects) {
2360
2939
  options: projects.map((p) => ({
2361
2940
  value: p,
2362
2941
  label: p.projectName,
2363
- hint: path13.relative(os.homedir(), p.projectRoot)
2942
+ hint: path15.relative(os.homedir(), p.projectRoot)
2364
2943
  }))
2365
2944
  });
2366
2945
  if (clack.isCancel(selected)) {
@@ -2375,7 +2954,7 @@ async function blocFlow(project) {
2375
2954
  placeholder: "e.g. user_login",
2376
2955
  validate: (value) => {
2377
2956
  if (!value.trim()) return "Name is required";
2378
- if (!SNAKE_CASE_REGEX4.test(value))
2957
+ if (!SNAKE_CASE_REGEX5.test(value))
2379
2958
  return "Must be snake_case (lowercase, digits, underscores)";
2380
2959
  }
2381
2960
  });
@@ -2408,13 +2987,13 @@ async function blocFlow(project) {
2408
2987
  clack.cancel("Cancelled");
2409
2988
  return;
2410
2989
  }
2411
- targetDir = path13.resolve(customPath);
2990
+ targetDir = path15.resolve(customPath);
2412
2991
  } else {
2413
- targetDir = path13.join(project.projectRoot, "lib", "features", feature);
2992
+ targetDir = path15.join(project.projectRoot, "lib", "features", feature);
2414
2993
  }
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/")}`);
2994
+ } else if (fs16.existsSync(path15.join(project.projectRoot, "lib", "bloc"))) {
2995
+ targetDir = path15.join(project.projectRoot, "lib", "bloc");
2996
+ clack.log.info(`Target: ${chalk11.cyan("lib/bloc/")}`);
2418
2997
  } else {
2419
2998
  clack.note(
2420
2999
  "No lib/features/ directory found. Provide a target path manually.",
@@ -2431,7 +3010,7 @@ async function blocFlow(project) {
2431
3010
  clack.cancel("Cancelled");
2432
3011
  return;
2433
3012
  }
2434
- targetDir = path13.resolve(customPath);
3013
+ targetDir = path15.resolve(customPath);
2435
3014
  }
2436
3015
  const defaultRun = project.hasBuildRunner;
2437
3016
  const runBuildRunner2 = await clack.confirm({
@@ -2450,10 +3029,10 @@ async function blocFlow(project) {
2450
3029
  buildRunner: runBuildRunner2
2451
3030
  });
2452
3031
  genSpinner.stop("BLoC generated");
2453
- clack.outro(chalk9.green("Done!"));
3032
+ clack.outro(chalk11.green("Done!"));
2454
3033
  } catch (error) {
2455
3034
  genSpinner.stop("Failed");
2456
- clack.outro(chalk9.red(`Error: ${error}`));
3035
+ clack.outro(chalk11.red(`Error: ${error}`));
2457
3036
  }
2458
3037
  }
2459
3038
  async function widgetFlow() {
@@ -2461,14 +3040,14 @@ async function widgetFlow() {
2461
3040
  const ds = detectDesignSystem(projectRoot);
2462
3041
  if (!ds) {
2463
3042
  clack.outro(
2464
- chalk9.red(
3043
+ chalk11.red(
2465
3044
  "No design system detected. Run this command from a project with wl_design_system/ directory."
2466
3045
  )
2467
3046
  );
2468
3047
  return;
2469
3048
  }
2470
3049
  clack.log.info(
2471
- `Design system: ${chalk9.cyan(path13.relative(projectRoot, ds.componentsDir))}`
3050
+ `Design system: ${chalk11.cyan(path15.relative(projectRoot, ds.componentsDir))}`
2472
3051
  );
2473
3052
  const name = await clack.text({
2474
3053
  message: "Widget name (snake_case, without wl_ prefix)",
@@ -2476,7 +3055,7 @@ async function widgetFlow() {
2476
3055
  validate: (value) => {
2477
3056
  if (!value.trim()) return "Name is required";
2478
3057
  const clean = value.startsWith("wl_") ? value.slice(3) : value;
2479
- if (!SNAKE_CASE_REGEX4.test(clean))
3058
+ if (!SNAKE_CASE_REGEX5.test(clean))
2480
3059
  return "Must be snake_case (lowercase, digits, underscores)";
2481
3060
  }
2482
3061
  });
@@ -2533,17 +3112,17 @@ async function widgetFlow() {
2533
3112
  genSpinner.stop(`Use-case skipped: ${e}`);
2534
3113
  }
2535
3114
  }
2536
- clack.outro(chalk9.green("Done!"));
3115
+ clack.outro(chalk11.green("Done!"));
2537
3116
  } catch (error) {
2538
3117
  genSpinner.stop("Failed");
2539
- clack.outro(chalk9.red(`Error: ${error}`));
3118
+ clack.outro(chalk11.red(`Error: ${error}`));
2540
3119
  }
2541
3120
  }
2542
3121
  async function useCaseFlow() {
2543
3122
  const ds = detectDesignSystem(process.cwd());
2544
3123
  if (!ds) {
2545
3124
  clack.outro(
2546
- chalk9.red(
3125
+ chalk11.red(
2547
3126
  "No design system detected. Ensure wl_design_system/ directory exists."
2548
3127
  )
2549
3128
  );
@@ -2551,7 +3130,7 @@ async function useCaseFlow() {
2551
3130
  }
2552
3131
  if (!ds.widgetbookDir) {
2553
3132
  clack.outro(
2554
- chalk9.red(
3133
+ chalk11.red(
2555
3134
  "No widgetbook package detected. Ensure apps/widgetbook/ exists."
2556
3135
  )
2557
3136
  );
@@ -2563,7 +3142,7 @@ async function useCaseFlow() {
2563
3142
  validate: (value) => {
2564
3143
  if (!value.trim()) return "Name is required";
2565
3144
  const clean = value.startsWith("wl_") ? value.slice(3) : value;
2566
- if (!SNAKE_CASE_REGEX4.test(clean))
3145
+ if (!SNAKE_CASE_REGEX5.test(clean))
2567
3146
  return "Must be snake_case (lowercase, digits, underscores)";
2568
3147
  }
2569
3148
  });
@@ -2600,18 +3179,123 @@ async function useCaseFlow() {
2600
3179
  buildRunner: runBuildRunner2
2601
3180
  });
2602
3181
  genSpinner.stop("Use-case generated");
2603
- clack.outro(chalk9.green("Done!"));
3182
+ clack.outro(chalk11.green("Done!"));
2604
3183
  } catch (error) {
2605
3184
  genSpinner.stop("Failed");
2606
- clack.outro(chalk9.red(`Error: ${error}`));
3185
+ clack.outro(chalk11.red(`Error: ${error}`));
3186
+ }
3187
+ }
3188
+ function discoverMonorepoPackages(monorepoRoot) {
3189
+ const results = [];
3190
+ const packageBases = ["packages", "packages/features"];
3191
+ for (const base of packageBases) {
3192
+ const baseDir = path15.join(monorepoRoot, base);
3193
+ if (!fs16.existsSync(baseDir)) continue;
3194
+ const entries = fs16.readdirSync(baseDir, { withFileTypes: true });
3195
+ for (const entry of entries) {
3196
+ if (!entry.isDirectory()) continue;
3197
+ const pkgDir = path15.join(baseDir, entry.name);
3198
+ if (fs16.existsSync(path15.join(pkgDir, "pubspec.yaml"))) {
3199
+ results.push({ name: entry.name, pagesDir: path15.join(pkgDir, "lib", "pages") });
3200
+ }
3201
+ }
2607
3202
  }
3203
+ return results.sort((a, b) => a.name.localeCompare(b.name));
3204
+ }
3205
+ async function pageFlow(initialName, initialPath) {
3206
+ const name = initialName ?? await clack.text({
3207
+ message: "Page name (snake_case)",
3208
+ placeholder: "e.g. profile, order_detail",
3209
+ validate: (value) => {
3210
+ if (!value.trim()) return "Name is required";
3211
+ if (!SNAKE_CASE_REGEX5.test(value))
3212
+ return "Must be snake_case (lowercase, digits, underscores)";
3213
+ }
3214
+ });
3215
+ if (clack.isCancel(name)) {
3216
+ clack.cancel("Cancelled");
3217
+ return;
3218
+ }
3219
+ let pagesPath = initialPath;
3220
+ if (!pagesPath) {
3221
+ const monorepoRoot = findMonorepoRoot(process.cwd());
3222
+ if (monorepoRoot) {
3223
+ const packages = discoverMonorepoPackages(monorepoRoot);
3224
+ if (packages.length > 0) {
3225
+ const selected = await clack.select({
3226
+ message: "Select package where the page will be created",
3227
+ options: [
3228
+ ...packages.map((p) => ({
3229
+ value: p.pagesDir,
3230
+ label: p.name,
3231
+ hint: path15.relative(monorepoRoot, p.pagesDir)
3232
+ })),
3233
+ { value: "__custom__", label: "Custom path..." }
3234
+ ]
3235
+ });
3236
+ if (clack.isCancel(selected)) {
3237
+ clack.cancel("Cancelled");
3238
+ return;
3239
+ }
3240
+ if (selected === "__custom__") {
3241
+ const customPath = await clack.text({
3242
+ message: "Absolute path to pages directory",
3243
+ placeholder: "/path/to/packages/home/lib/pages",
3244
+ validate: (v) => {
3245
+ if (!v.trim()) return "Path is required";
3246
+ const resolved = path15.resolve(v.trim());
3247
+ if (!fs16.existsSync(resolved)) return "Path does not exist";
3248
+ }
3249
+ });
3250
+ if (clack.isCancel(customPath)) {
3251
+ clack.cancel("Cancelled");
3252
+ return;
3253
+ }
3254
+ pagesPath = path15.resolve(customPath);
3255
+ } else {
3256
+ pagesPath = selected;
3257
+ }
3258
+ } else {
3259
+ pagesPath = await askManualPagesPath();
3260
+ }
3261
+ } else {
3262
+ pagesPath = await askManualPagesPath();
3263
+ }
3264
+ }
3265
+ if (!pagesPath) return;
3266
+ const genSpinner = clack.spinner();
3267
+ genSpinner.start("Creating page...");
3268
+ try {
3269
+ await createPage(name, { pagesPath });
3270
+ genSpinner.stop("Page created");
3271
+ clack.outro(chalk11.green("Done!"));
3272
+ } catch (error) {
3273
+ genSpinner.stop("Failed");
3274
+ clack.outro(chalk11.red(`Error: ${error}`));
3275
+ }
3276
+ }
3277
+ async function askManualPagesPath() {
3278
+ const customPath = await clack.text({
3279
+ message: "Absolute path to pages directory",
3280
+ placeholder: "/path/to/project/lib/pages",
3281
+ validate: (v) => {
3282
+ if (!v.trim()) return "Path is required";
3283
+ const resolved = path15.resolve(v.trim());
3284
+ if (!fs16.existsSync(resolved)) return "Path does not exist";
3285
+ }
3286
+ });
3287
+ if (clack.isCancel(customPath)) {
3288
+ clack.cancel("Cancelled");
3289
+ return void 0;
3290
+ }
3291
+ return path15.resolve(customPath);
2608
3292
  }
2609
3293
  function findBffFiles(dir) {
2610
3294
  const results = [];
2611
- if (!fs14.existsSync(dir)) return results;
2612
- const entries = fs14.readdirSync(dir, { withFileTypes: true });
3295
+ if (!fs16.existsSync(dir)) return results;
3296
+ const entries = fs16.readdirSync(dir, { withFileTypes: true });
2613
3297
  for (const entry of entries) {
2614
- const fullPath = path13.join(dir, entry.name);
3298
+ const fullPath = path15.join(dir, entry.name);
2615
3299
  if (entry.isDirectory()) {
2616
3300
  results.push(...findBffFiles(fullPath));
2617
3301
  } else if (entry.isFile() && entry.name.endsWith(".dart") && !entry.name.includes(".g.")) {
@@ -2623,8 +3307,8 @@ function findBffFiles(dir) {
2623
3307
  function inferBffFile(bffFiles, endpointPath) {
2624
3308
  const firstSegment = endpointPath.replace(/^\//, "").split("/")[0].toLowerCase();
2625
3309
  if (!firstSegment) return null;
2626
- const withDomains = bffFiles.filter((f) => /^bff_.+_api\.dart$/.test(path13.basename(f))).map((f) => {
2627
- const base = path13.basename(f, ".dart");
3310
+ const withDomains = bffFiles.filter((f) => /^bff_.+_api\.dart$/.test(path15.basename(f))).map((f) => {
3311
+ const base = path15.basename(f, ".dart");
2628
3312
  const domain = base.replace(/^bff_/, "").replace(/_api$/, "");
2629
3313
  return { file: f, domain };
2630
3314
  });
@@ -2651,19 +3335,19 @@ async function resolveEndpointProject() {
2651
3335
  s.message(`Monorepo found at ${monorepoRoot}`);
2652
3336
  const packageBases = ["packages", "packages/features"];
2653
3337
  for (const base of packageBases) {
2654
- const baseDir = path13.join(monorepoRoot, base);
2655
- if (!fs14.existsSync(baseDir)) continue;
2656
- const entries = fs14.readdirSync(baseDir, { withFileTypes: true });
3338
+ const baseDir = path15.join(monorepoRoot, base);
3339
+ if (!fs16.existsSync(baseDir)) continue;
3340
+ const entries = fs16.readdirSync(baseDir, { withFileTypes: true });
2657
3341
  for (const entry of entries) {
2658
3342
  if (!entry.isDirectory()) continue;
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)) {
3343
+ const candidate = path15.join(baseDir, entry.name);
3344
+ if (!fs16.existsSync(path15.join(candidate, "pubspec.yaml"))) continue;
3345
+ const bffPath = path15.join(candidate, "lib", "data", "api", "bff");
3346
+ s.message(`Checking ${candidate} \u2192 bff exists: ${fs16.existsSync(bffPath)}`);
3347
+ if (fs16.existsSync(bffPath)) {
2664
3348
  const project2 = analyzeProject(candidate);
2665
3349
  if (project2) {
2666
- s.stop(`Using ${chalk9.green(project2.projectName)}`);
3350
+ s.stop(`Using ${chalk11.green(project2.projectName)}`);
2667
3351
  return project2;
2668
3352
  }
2669
3353
  }
@@ -2673,8 +3357,8 @@ async function resolveEndpointProject() {
2673
3357
  s.message("No monorepo root found");
2674
3358
  }
2675
3359
  const cwdProject = analyzeProject(process.cwd());
2676
- if (cwdProject && fs14.existsSync(path13.join(cwdProject.projectRoot, "lib", "data", "api", "bff"))) {
2677
- s.stop(`Using ${chalk9.green(cwdProject.projectName)}`);
3360
+ if (cwdProject && fs16.existsSync(path15.join(cwdProject.projectRoot, "lib", "data", "api", "bff"))) {
3361
+ s.stop(`Using ${chalk11.green(cwdProject.projectName)}`);
2678
3362
  return cwdProject;
2679
3363
  }
2680
3364
  s.stop("No BFF package found");
@@ -2683,33 +3367,33 @@ async function resolveEndpointProject() {
2683
3367
  placeholder: "e.g. /path/to/my-package or ./packages/core",
2684
3368
  validate: (v) => {
2685
3369
  if (!v.trim()) return "Path is required";
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";
3370
+ const resolved2 = path15.resolve(v.trim());
3371
+ if (!fs16.existsSync(resolved2)) return "Path does not exist";
3372
+ if (!fs16.existsSync(path15.join(resolved2, "pubspec.yaml"))) return "No pubspec.yaml found at this path";
3373
+ if (!fs16.existsSync(path15.join(resolved2, "lib", "data", "api", "bff"))) return "No lib/data/api/bff/ found at this path";
2690
3374
  }
2691
3375
  });
2692
3376
  if (clack.isCancel(manualPath)) {
2693
3377
  clack.cancel("Cancelled");
2694
3378
  return null;
2695
3379
  }
2696
- const resolved = path13.resolve(manualPath);
3380
+ const resolved = path15.resolve(manualPath);
2697
3381
  const project = analyzeProject(resolved);
2698
3382
  if (!project) {
2699
- clack.outro(chalk9.red(`Could not analyze project at ${resolved}`));
3383
+ clack.outro(chalk11.red(`Could not analyze project at ${resolved}`));
2700
3384
  return null;
2701
3385
  }
2702
- clack.log.info(`Using ${chalk9.green(project.projectName)}`);
3386
+ clack.log.info(`Using ${chalk11.green(project.projectName)}`);
2703
3387
  return project;
2704
3388
  }
2705
3389
  async function endpointFlow() {
2706
3390
  const project = await resolveEndpointProject();
2707
3391
  if (!project) return;
2708
- const lib = path13.join(project.projectRoot, "lib");
2709
- const bffDir = path13.join(lib, "data", "api", "bff");
3392
+ const lib = path15.join(project.projectRoot, "lib");
3393
+ const bffDir = path15.join(lib, "data", "api", "bff");
2710
3394
  const bffFiles = findBffFiles(bffDir);
2711
3395
  if (bffFiles.length === 0) {
2712
- clack.outro(chalk9.red("No BFF API files found. Ensure lib/data/api/bff/ exists with .dart files."));
3396
+ clack.outro(chalk11.red("No BFF API files found. Ensure lib/data/api/bff/ exists with .dart files."));
2713
3397
  return;
2714
3398
  }
2715
3399
  const httpMethod = await clack.select({
@@ -2739,13 +3423,13 @@ async function endpointFlow() {
2739
3423
  }
2740
3424
  let bffApiFile = inferBffFile(bffFiles, endpointPath);
2741
3425
  if (bffApiFile) {
2742
- clack.log.info(`Auto-detected BFF file: ${chalk9.cyan(path13.basename(bffApiFile))}`);
3426
+ clack.log.info(`Auto-detected BFF file: ${chalk11.cyan(path15.basename(bffApiFile))}`);
2743
3427
  } else {
2744
3428
  const selected = await clack.select({
2745
3429
  message: "Could not auto-detect BFF file. Select one:",
2746
3430
  options: bffFiles.map((f) => ({
2747
3431
  value: f,
2748
- label: path13.relative(bffDir, f)
3432
+ label: path15.relative(bffDir, f)
2749
3433
  }))
2750
3434
  });
2751
3435
  if (clack.isCancel(selected)) {
@@ -2763,7 +3447,7 @@ async function endpointFlow() {
2763
3447
  initialValue: inferredName,
2764
3448
  validate: (v) => {
2765
3449
  if (!v.trim()) return "Name is required";
2766
- if (!SNAKE_CASE_REGEX4.test(v)) return "Must be snake_case";
3450
+ if (!SNAKE_CASE_REGEX5.test(v)) return "Must be snake_case";
2767
3451
  }
2768
3452
  });
2769
3453
  if (clack.isCancel(useCaseName)) {
@@ -2809,14 +3493,49 @@ async function endpointFlow() {
2809
3493
  diLazySingleton
2810
3494
  });
2811
3495
  genSpinner.stop("Endpoint generated");
2812
- clack.outro(chalk9.green("Done!"));
3496
+ clack.outro(chalk11.green("Done!"));
3497
+ } catch (error) {
3498
+ genSpinner.stop("Failed");
3499
+ clack.outro(chalk11.red(`Error: ${error}`));
3500
+ }
3501
+ }
3502
+ async function packageFlow() {
3503
+ const monorepoRoot = findMonorepoRoot(process.cwd());
3504
+ if (!monorepoRoot) {
3505
+ clack.outro(chalk11.red("Not inside a monorepo. Run from within a Melos monorepo."));
3506
+ return;
3507
+ }
3508
+ clack.log.info(`Monorepo: ${chalk11.cyan(path15.relative(os.homedir(), monorepoRoot))}`);
3509
+ const name = await clack.text({
3510
+ message: "Package name (snake_case)",
3511
+ placeholder: "e.g. rewards, promotions, notifications",
3512
+ validate: (value) => {
3513
+ if (!value || !value.trim()) return "Name is required";
3514
+ if (!SNAKE_CASE_REGEX5.test(value)) return "Must be snake_case (lowercase, digits, underscores)";
3515
+ const pkgDir = path15.join(monorepoRoot, "packages", value);
3516
+ if (fs16.existsSync(pkgDir)) return `packages/${value} already exists`;
3517
+ }
3518
+ });
3519
+ if (clack.isCancel(name)) {
3520
+ clack.cancel("Cancelled");
3521
+ return;
3522
+ }
3523
+ const genSpinner = clack.spinner();
3524
+ genSpinner.start("Creating package...");
3525
+ try {
3526
+ await createPackage({
3527
+ monorepoRoot,
3528
+ packageName: name
3529
+ });
3530
+ genSpinner.stop("Package created");
3531
+ clack.outro(chalk11.green("Done!"));
2813
3532
  } catch (error) {
2814
3533
  genSpinner.stop("Failed");
2815
- clack.outro(chalk9.red(`Error: ${error}`));
3534
+ clack.outro(chalk11.red(`Error: ${error}`));
2816
3535
  }
2817
3536
  }
2818
3537
  async function docsInteractiveMode() {
2819
- clack.intro(chalk9.bgCyan(chalk9.black(" wlmaker docs ")));
3538
+ clack.intro(chalk11.bgCyan(chalk11.black(" wlmaker docs ")));
2820
3539
  const action = await clack.select({
2821
3540
  message: "What do you want to do?",
2822
3541
  options: [
@@ -2860,46 +3579,46 @@ async function docsInteractiveMode() {
2860
3579
  }
2861
3580
  const remoteUrl = "https://dc-wl-docs.web.app/development_workflow/";
2862
3581
  if (!remoteUrl) {
2863
- clack.outro(chalk9.red("Remote docs URL not configured. Set WL_DOCS_URL in your environment."));
3582
+ clack.outro(chalk11.red("Remote docs URL not configured. Set WL_DOCS_URL in your environment."));
2864
3583
  return;
2865
3584
  }
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"));
3585
+ clack.log.info(`Opening remote docs: ${chalk11.cyan(remoteUrl)}`);
3586
+ execSync3(`open "${remoteUrl}"`, { stdio: "ignore" });
3587
+ clack.outro(chalk11.green("Opened remote docs in browser"));
2869
3588
  return;
2870
3589
  }
2871
3590
  const bookDir = detectBookDir(process.cwd());
2872
3591
  if (!bookDir) {
2873
3592
  clack.outro(
2874
- chalk9.red("No Docusaurus book/ directory found. Run from a monorepo root.")
3593
+ chalk11.red("No Docusaurus book/ directory found. Run from a monorepo root.")
2875
3594
  );
2876
3595
  return;
2877
3596
  }
2878
- clack.log.info(`Serving docs from ${chalk9.cyan(bookDir)}`);
3597
+ clack.log.info(`Serving docs from ${chalk11.cyan(bookDir)}`);
2879
3598
  await serveBook(bookDir);
2880
3599
  break;
2881
3600
  }
2882
3601
  case "commands": {
2883
3602
  const commands = discoverCommands(process.cwd());
2884
3603
  if (commands.length === 0) {
2885
- clack.outro(chalk9.yellow("No commands found. Run from a monorepo root."));
3604
+ clack.outro(chalk11.yellow("No commands found. Run from a monorepo root."));
2886
3605
  return;
2887
3606
  }
2888
- clack.log.info(`Found ${chalk9.green(commands.length.toString())} command(s)`);
3607
+ clack.log.info(`Found ${chalk11.green(commands.length.toString())} command(s)`);
2889
3608
  displayCommands(commands);
2890
- clack.outro(chalk9.green("Done!"));
3609
+ clack.outro(chalk11.green("Done!"));
2891
3610
  break;
2892
3611
  }
2893
3612
  case "architecture": {
2894
3613
  const info = discoverArchitecture(process.cwd());
2895
3614
  if (!info) {
2896
3615
  clack.outro(
2897
- chalk9.yellow("No monorepo detected. Run from within a Melos monorepo.")
3616
+ chalk11.yellow("No monorepo detected. Run from within a Melos monorepo.")
2898
3617
  );
2899
3618
  return;
2900
3619
  }
2901
3620
  displayArchitecture(info);
2902
- clack.outro(chalk9.green("Done!"));
3621
+ clack.outro(chalk11.green("Done!"));
2903
3622
  break;
2904
3623
  }
2905
3624
  }
@@ -2908,12 +3627,12 @@ var SCREAMING_SNAKE_REGEX = /^[A-Z][A-Z0-9_]*$/;
2908
3627
  async function envVarFlow() {
2909
3628
  const monorepoRoot = findMonorepoRoot(process.cwd());
2910
3629
  if (!monorepoRoot) {
2911
- clack.outro(chalk9.red("Not inside a monorepo. Run from within a Melos monorepo."));
3630
+ clack.outro(chalk11.red("Not inside a monorepo. Run from within a Melos monorepo."));
2912
3631
  return;
2913
3632
  }
2914
3633
  const apps = discoverAppsWithEnv(monorepoRoot);
2915
3634
  if (apps.length === 0) {
2916
- clack.outro(chalk9.red("No apps with env/ directory found in the monorepo."));
3635
+ clack.outro(chalk11.red("No apps with env/ directory found in the monorepo."));
2917
3636
  return;
2918
3637
  }
2919
3638
  const variableName = await clack.text({
@@ -3017,14 +3736,14 @@ async function envVarFlow() {
3017
3736
  includeInAppConfig
3018
3737
  });
3019
3738
  genSpinner.stop("Done");
3020
- clack.outro(chalk9.green("Environment variable added!"));
3739
+ clack.outro(chalk11.green("Environment variable added!"));
3021
3740
  } catch (error) {
3022
3741
  genSpinner.stop("Failed");
3023
- clack.outro(chalk9.red(`Error: ${error}`));
3742
+ clack.outro(chalk11.red(`Error: ${error}`));
3024
3743
  }
3025
3744
  }
3026
3745
  async function interactiveMode() {
3027
- clack.intro(chalk9.bgCyan(chalk9.black(" wlmaker ")));
3746
+ clack.intro(chalk11.bgCyan(chalk11.black(" wlmaker ")));
3028
3747
  const createType = await clack.select({
3029
3748
  message: "What do you want to create?",
3030
3749
  options: [
@@ -3035,11 +3754,21 @@ async function interactiveMode() {
3035
3754
  label: "Widgetbook Use-Case",
3036
3755
  hint: "Component showcase"
3037
3756
  },
3757
+ {
3758
+ value: "page",
3759
+ label: "Page",
3760
+ hint: "GoRoute + View with barrels"
3761
+ },
3038
3762
  {
3039
3763
  value: "endpoint",
3040
3764
  label: "Endpoint",
3041
3765
  hint: "BFF Clean Architecture stack"
3042
3766
  },
3767
+ {
3768
+ value: "package",
3769
+ label: "Package",
3770
+ hint: "Create a new package in the monorepo"
3771
+ },
3043
3772
  {
3044
3773
  value: "env-var",
3045
3774
  label: "Env Var",
@@ -3070,9 +3799,15 @@ async function interactiveMode() {
3070
3799
  case "usecase":
3071
3800
  await useCaseFlow();
3072
3801
  break;
3802
+ case "page":
3803
+ await pageFlow();
3804
+ break;
3073
3805
  case "endpoint":
3074
3806
  await endpointFlow();
3075
3807
  break;
3808
+ case "package":
3809
+ await packageFlow();
3810
+ break;
3076
3811
  case "env-var":
3077
3812
  await envVarFlow();
3078
3813
  break;
@@ -3082,6 +3817,288 @@ async function interactiveMode() {
3082
3817
  }
3083
3818
  }
3084
3819
 
3820
+ // src/mcp-server.ts
3821
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3822
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3823
+ import {
3824
+ CallToolRequestSchema,
3825
+ ErrorCode,
3826
+ ListToolsRequestSchema,
3827
+ McpError
3828
+ } from "@modelcontextprotocol/sdk/types.js";
3829
+ async function runMcpServer() {
3830
+ const server = new Server(
3831
+ {
3832
+ name: "wlmaker-cli",
3833
+ version: "1.5.0"
3834
+ },
3835
+ {
3836
+ capabilities: {
3837
+ tools: {}
3838
+ }
3839
+ }
3840
+ );
3841
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
3842
+ return {
3843
+ tools: [
3844
+ {
3845
+ name: "create_bloc",
3846
+ description: "Create a new BLoC with Freezed sealed classes in the Flutter project",
3847
+ inputSchema: {
3848
+ type: "object",
3849
+ properties: {
3850
+ name: {
3851
+ type: "string",
3852
+ description: "BLoC name in snake_case (e.g. user_login)"
3853
+ },
3854
+ dir: {
3855
+ type: "string",
3856
+ description: "Target directory for the BLoC"
3857
+ }
3858
+ },
3859
+ required: ["name"]
3860
+ }
3861
+ },
3862
+ {
3863
+ name: "create_widget",
3864
+ description: "Create a new widget in the design system",
3865
+ inputSchema: {
3866
+ type: "object",
3867
+ properties: {
3868
+ name: {
3869
+ type: "string",
3870
+ description: "Widget name in snake_case (e.g. toggle)"
3871
+ },
3872
+ tier: {
3873
+ type: "string",
3874
+ description: "Tier: atom, molecule, organism, template",
3875
+ enum: ["atom", "molecule", "organism", "template"]
3876
+ },
3877
+ dir: {
3878
+ type: "string",
3879
+ description: "Project root directory (optional)"
3880
+ }
3881
+ },
3882
+ required: ["name", "tier"]
3883
+ }
3884
+ },
3885
+ {
3886
+ name: "create_page",
3887
+ description: "Create a new page (GoRoute + View) with barrel updates",
3888
+ inputSchema: {
3889
+ type: "object",
3890
+ properties: {
3891
+ name: {
3892
+ type: "string",
3893
+ description: "Page name in snake_case (e.g. profile, order_detail)"
3894
+ },
3895
+ path: {
3896
+ type: "string",
3897
+ description: "Absolute path to the pages directory"
3898
+ }
3899
+ },
3900
+ required: ["name", "path"]
3901
+ }
3902
+ },
3903
+ {
3904
+ name: "create_usecase",
3905
+ description: "Create a Widgetbook use-case for an existing widget",
3906
+ inputSchema: {
3907
+ type: "object",
3908
+ properties: {
3909
+ name: {
3910
+ type: "string",
3911
+ description: "Widget name in snake_case (e.g. toggle)"
3912
+ },
3913
+ tier: {
3914
+ type: "string",
3915
+ description: "Tier: atom, molecule, organism, template",
3916
+ enum: ["atom", "molecule", "organism", "template"]
3917
+ },
3918
+ dir: {
3919
+ type: "string",
3920
+ description: "Project root directory (optional)"
3921
+ }
3922
+ },
3923
+ required: ["name", "tier"]
3924
+ }
3925
+ },
3926
+ {
3927
+ name: "create_endpoint",
3928
+ description: "Generate Clean Architecture stack for a BFF endpoint",
3929
+ inputSchema: {
3930
+ type: "object",
3931
+ properties: {
3932
+ projectRoot: { type: "string", description: "Root directory of the BFF package" },
3933
+ projectName: { type: "string", description: "Name of the BFF package" },
3934
+ httpMethod: {
3935
+ type: "string",
3936
+ enum: ["GET", "POST", "PUT", "DELETE", "PATCH"],
3937
+ description: "HTTP method"
3938
+ },
3939
+ endpointPath: { type: "string", description: "Endpoint path (e.g. /api/users/{id})" },
3940
+ bffApiFile: { type: "string", description: "Absolute or relative path to the BFF API dart file" },
3941
+ useCaseName: { type: "string", description: "UseCase name in snake_case" },
3942
+ diTarget: { type: "string", description: "DI target (e.g. app_base, none)", default: "none" },
3943
+ diLazySingleton: { type: "boolean", description: "Use @lazySingleton", default: true }
3944
+ },
3945
+ required: ["projectRoot", "projectName", "httpMethod", "endpointPath", "bffApiFile", "useCaseName"]
3946
+ }
3947
+ },
3948
+ {
3949
+ name: "create_package",
3950
+ description: "Create a new package in the monorepo",
3951
+ inputSchema: {
3952
+ type: "object",
3953
+ properties: {
3954
+ monorepoRoot: { type: "string", description: "Root directory of the monorepo" },
3955
+ packageName: { type: "string", description: "Package name in snake_case" }
3956
+ },
3957
+ required: ["monorepoRoot", "packageName"]
3958
+ }
3959
+ },
3960
+ {
3961
+ name: "create_env_var",
3962
+ description: "Add an environment variable across the Flutter monorepo",
3963
+ inputSchema: {
3964
+ type: "object",
3965
+ properties: {
3966
+ monorepoRoot: { type: "string", description: "Root directory of the monorepo" },
3967
+ variableName: { type: "string", description: "Variable name in SCREAMING_SNAKE_CASE" },
3968
+ dartType: {
3969
+ type: "string",
3970
+ enum: ["String", "int", "bool", "List<String>"],
3971
+ description: "Dart type"
3972
+ },
3973
+ defaultValue: { type: "string", description: "Default value (optional)" },
3974
+ selectedApps: { type: "array", items: { type: "string" }, description: "List of apps to update" },
3975
+ vendorsTargets: { type: "array", items: { type: "string" }, description: "List of vendor module packages" },
3976
+ includeInRemoteConfig: { type: "boolean", description: "Include in Remote Config" },
3977
+ includeInAppConfig: { type: "boolean", description: "Include in AppConfig entity" }
3978
+ },
3979
+ required: ["monorepoRoot", "variableName", "dartType", "selectedApps", "vendorsTargets", "includeInRemoteConfig", "includeInAppConfig"]
3980
+ }
3981
+ },
3982
+ {
3983
+ name: "docs_commands",
3984
+ description: "Show Makefile & melos commands reference",
3985
+ inputSchema: {
3986
+ type: "object",
3987
+ properties: {
3988
+ dir: { type: "string", description: "Project root directory" }
3989
+ },
3990
+ required: ["dir"]
3991
+ }
3992
+ },
3993
+ {
3994
+ name: "docs_architecture",
3995
+ description: "Display monorepo architecture info as JSON",
3996
+ inputSchema: {
3997
+ type: "object",
3998
+ properties: {
3999
+ dir: { type: "string", description: "Project root directory" }
4000
+ },
4001
+ required: ["dir"]
4002
+ }
4003
+ }
4004
+ ]
4005
+ };
4006
+ });
4007
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4008
+ try {
4009
+ if (request.params.name === "create_bloc") {
4010
+ const { name, dir } = request.params.arguments;
4011
+ const targetDir = dir || process.cwd();
4012
+ await createBloc(name, { dir: targetDir, buildRunner: false });
4013
+ return {
4014
+ content: [{ type: "text", text: `Successfully created BLoC ${name} in ${targetDir}` }]
4015
+ };
4016
+ }
4017
+ if (request.params.name === "create_widget") {
4018
+ const { name, tier, dir } = request.params.arguments;
4019
+ const projectRoot = dir || process.cwd();
4020
+ await createWidget(name, tier, { projectRoot, pattern: void 0 });
4021
+ try {
4022
+ await createUseCase(name, tier, { projectRoot, buildRunner: false });
4023
+ return {
4024
+ content: [{ type: "text", text: `Successfully created Widget ${name} (${tier}) and its UseCase in ${projectRoot}` }]
4025
+ };
4026
+ } catch {
4027
+ return {
4028
+ content: [{ type: "text", text: `Successfully created Widget ${name} (${tier}) in ${projectRoot} (UseCase creation skipped/failed)` }]
4029
+ };
4030
+ }
4031
+ }
4032
+ if (request.params.name === "create_page") {
4033
+ const { name, path: path16 } = request.params.arguments;
4034
+ await createPage(name, { pagesPath: path16 });
4035
+ return {
4036
+ content: [{ type: "text", text: `Successfully created Page ${name} in ${path16}` }]
4037
+ };
4038
+ }
4039
+ if (request.params.name === "create_usecase") {
4040
+ const { name, tier, dir } = request.params.arguments;
4041
+ const projectRoot = dir || process.cwd();
4042
+ await createUseCase(name, tier, { projectRoot, buildRunner: false });
4043
+ return {
4044
+ content: [{ type: "text", text: `Successfully created UseCase for ${name} (${tier}) in ${projectRoot}` }]
4045
+ };
4046
+ }
4047
+ if (request.params.name === "create_endpoint") {
4048
+ const args = request.params.arguments;
4049
+ await createEndpoint({
4050
+ ...args,
4051
+ diTarget: args.diTarget || "none",
4052
+ diLazySingleton: args.diLazySingleton ?? true
4053
+ });
4054
+ return {
4055
+ content: [{ type: "text", text: `Successfully generated endpoint stack for ${args.useCaseName}` }]
4056
+ };
4057
+ }
4058
+ if (request.params.name === "create_package") {
4059
+ const { monorepoRoot, packageName } = request.params.arguments;
4060
+ await createPackage({ monorepoRoot, packageName });
4061
+ return {
4062
+ content: [{ type: "text", text: `Successfully created package ${packageName} in ${monorepoRoot}/packages` }]
4063
+ };
4064
+ }
4065
+ if (request.params.name === "create_env_var") {
4066
+ const args = request.params.arguments;
4067
+ await createEnvVar(args);
4068
+ return {
4069
+ content: [{ type: "text", text: `Successfully added environment variable ${args.variableName}` }]
4070
+ };
4071
+ }
4072
+ if (request.params.name === "docs_commands") {
4073
+ const { dir } = request.params.arguments;
4074
+ const commands = discoverCommands(dir || process.cwd());
4075
+ return {
4076
+ content: [{ type: "text", text: JSON.stringify(commands, null, 2) }]
4077
+ };
4078
+ }
4079
+ if (request.params.name === "docs_architecture") {
4080
+ const { dir } = request.params.arguments;
4081
+ const info = discoverArchitecture(dir || process.cwd());
4082
+ if (!info) {
4083
+ return { content: [{ type: "text", text: "No monorepo detected." }] };
4084
+ }
4085
+ return {
4086
+ content: [{ type: "text", text: JSON.stringify(info, null, 2) }]
4087
+ };
4088
+ }
4089
+ throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
4090
+ } catch (error) {
4091
+ return {
4092
+ content: [{ type: "text", text: `Error: ${error?.message || String(error)}` }],
4093
+ isError: true
4094
+ };
4095
+ }
4096
+ });
4097
+ const transport = new StdioServerTransport();
4098
+ await server.connect(transport);
4099
+ console.error("wlmaker-cli MCP server running on stdio");
4100
+ }
4101
+
3085
4102
  // src/cli.ts
3086
4103
  var require2 = createRequire(import.meta.url);
3087
4104
  var pkg = require2("../package.json");
@@ -3098,7 +4115,7 @@ program.command("bloc").description("Create a new BLoC with Freezed sealed class
3098
4115
  try {
3099
4116
  await createBloc(name, options);
3100
4117
  } catch (error) {
3101
- console.error(chalk10.red(`Error: ${error}`));
4118
+ console.error(chalk12.red(`Error: ${error}`));
3102
4119
  process.exit(1);
3103
4120
  }
3104
4121
  }
@@ -3115,12 +4132,12 @@ program.command("widget").description("Create a new widget in the design system"
3115
4132
  projectRoot: options.dir,
3116
4133
  buildRunner: false
3117
4134
  });
3118
- console.log(chalk10.green("Widgetbook use-case created"));
4135
+ console.log(chalk12.green("Widgetbook use-case created"));
3119
4136
  } catch {
3120
- console.log(chalk10.yellow("Use-case skipped (may already exist)"));
4137
+ console.log(chalk12.yellow("Use-case skipped (may already exist)"));
3121
4138
  }
3122
4139
  } catch (error) {
3123
- console.error(chalk10.red(`Error: ${error}`));
4140
+ console.error(chalk12.red(`Error: ${error}`));
3124
4141
  process.exit(1);
3125
4142
  }
3126
4143
  }
@@ -3133,15 +4150,30 @@ program.command("usecase").description("Create a Widgetbook use-case for an exis
3133
4150
  buildRunner: options.buildRunner
3134
4151
  });
3135
4152
  } catch (error) {
3136
- console.error(chalk10.red(`Error: ${error}`));
4153
+ console.error(chalk12.red(`Error: ${error}`));
4154
+ process.exit(1);
4155
+ }
4156
+ }
4157
+ );
4158
+ program.command("page").description("Create a new page (GoRoute + View) with barrel updates").argument("[name]", "Page name in snake_case (e.g. profile, order_detail)").option("-p, --path <path>", "absolute path to the pages directory").action(
4159
+ async (name, options) => {
4160
+ if (!name || !options.path) {
4161
+ await pageFlow(name, options.path);
4162
+ return;
4163
+ }
4164
+ try {
4165
+ await createPage(name, { pagesPath: options.path });
4166
+ } catch (error) {
4167
+ console.error(chalk12.red(`Error: ${error}`));
3137
4168
  process.exit(1);
3138
4169
  }
3139
4170
  }
3140
4171
  );
3141
4172
  program.command("endpoint").description("Generate Clean Architecture stack for a BFF endpoint").action(async () => {
3142
- const project = await resolveProject();
3143
- if (!project) return;
3144
- await endpointFlow(project);
4173
+ await endpointFlow();
4174
+ });
4175
+ program.command("package").description("Create a new package in the monorepo").action(async () => {
4176
+ await packageFlow();
3145
4177
  });
3146
4178
  program.command("env-var").description("Add an environment variable across the Flutter monorepo").action(async () => {
3147
4179
  await envVarFlow();
@@ -3150,30 +4182,36 @@ var docsCmd = program.command("docs").description("Project documentation tools")
3150
4182
  docsCmd.command("serve").description("Start Docusaurus dev server").option("-d, --dir <path>", "project root directory", process.cwd()).action(async (options) => {
3151
4183
  const bookDir = detectBookDir(options.dir);
3152
4184
  if (!bookDir) {
3153
- console.error(chalk10.red("No Docusaurus book/ directory found. Run from a monorepo root."));
4185
+ console.error(chalk12.red("No Docusaurus book/ directory found. Run from a monorepo root."));
3154
4186
  process.exit(1);
3155
4187
  }
3156
- console.log(chalk10.cyan(`Serving docs from ${bookDir}`));
4188
+ console.log(chalk12.cyan(`Serving docs from ${bookDir}`));
3157
4189
  await serveBook(bookDir);
3158
4190
  });
3159
4191
  docsCmd.command("commands").description("Show Makefile & melos commands reference").option("-d, --dir <path>", "project root directory", process.cwd()).action(async (options) => {
3160
4192
  const commands = discoverCommands(options.dir);
3161
4193
  if (commands.length === 0) {
3162
- console.log(chalk10.yellow("No commands found. Run from a monorepo root."));
4194
+ console.log(chalk12.yellow("No commands found. Run from a monorepo root."));
3163
4195
  return;
3164
4196
  }
3165
- console.log(chalk10.green(`Found ${commands.length} command(s)
4197
+ console.log(chalk12.green(`Found ${commands.length} command(s)
3166
4198
  `));
3167
4199
  displayCommands(commands);
3168
4200
  });
3169
4201
  docsCmd.command("architecture").description("Display monorepo architecture tree").option("-d, --dir <path>", "project root directory", process.cwd()).action(async (options) => {
3170
4202
  const info = discoverArchitecture(options.dir);
3171
4203
  if (!info) {
3172
- console.log(chalk10.yellow("No monorepo detected. Run from within a Melos monorepo."));
4204
+ console.log(chalk12.yellow("No monorepo detected. Run from within a Melos monorepo."));
3173
4205
  return;
3174
4206
  }
3175
4207
  displayArchitecture(info);
3176
4208
  });
4209
+ program.command("mcp").description("Start the wlmaker MCP server (used automatically by AI clients)").action(async () => {
4210
+ await runMcpServer();
4211
+ });
4212
+ program.command("mcp-install").description("Manually install/register the wlmaker MCP server into compatible clients (Claude, Cursor, Cline)").action(async () => {
4213
+ await installMcpServer();
4214
+ });
3177
4215
  docsCmd.action(async () => {
3178
4216
  await docsInteractiveMode();
3179
4217
  });