@solid-stack/mason 1.0.25 → 1.0.27

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/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import fs4 from 'fs';
2
2
  import path2 from 'path';
3
- import * as p15 from '@clack/prompts';
4
- import pc15 from 'picocolors';
3
+ import * as p19 from '@clack/prompts';
4
+ import pc14 from 'picocolors';
5
5
  import { fileURLToPath } from 'url';
6
6
  import { execSync } from 'child_process';
7
7
  import { createHash } from 'crypto';
@@ -112,17 +112,17 @@ function writeFileSafely(absolutePath, content, options = {}) {
112
112
  function printGeneratedFiles(files, isDryRun = false) {
113
113
  console.log("");
114
114
  if (isDryRun) {
115
- p15.log.warn(pc15.yellow(pc15.bold("[DRY RUN PREVIEW] No files were written to disk.")));
115
+ p19.log.warn(pc14.yellow(pc14.bold("[DRY RUN PREVIEW] No files were written to disk.")));
116
116
  }
117
117
  for (const f of files) {
118
118
  if (f.action === "created") {
119
- p15.log.success(`${pc15.bold(f.relativePath)} ${pc15.dim("(created)")}`);
119
+ p19.log.success(`${pc14.bold(f.relativePath)} ${pc14.dim("(created)")}`);
120
120
  } else if (f.action === "overwritten") {
121
- p15.log.warn(`${pc15.bold(f.relativePath)} ${pc15.dim("(overwritten)")}`);
121
+ p19.log.warn(`${pc14.bold(f.relativePath)} ${pc14.dim("(overwritten)")}`);
122
122
  } else if (f.action === "skipped") {
123
- p15.log.info(`${pc15.dim(f.relativePath)} ${pc15.dim("(skipped - already exists, pass -o to overwrite)")}`);
123
+ p19.log.info(`${pc14.dim(f.relativePath)} ${pc14.dim("(skipped - already exists, pass -o to overwrite)")}`);
124
124
  } else {
125
- p15.log.info(`${pc15.bold(f.relativePath)} ${pc15.dim("(preview)")}`);
125
+ p19.log.info(`${pc14.bold(f.relativePath)} ${pc14.dim("(preview)")}`);
126
126
  }
127
127
  }
128
128
  }
@@ -324,23 +324,23 @@ var PromptCancelledError = class extends Error {
324
324
  }
325
325
  };
326
326
  function handleCancel(result) {
327
- if (p15.isCancel(result)) {
328
- p15.cancel("Operation cancelled.");
327
+ if (p19.isCancel(result)) {
328
+ p19.cancel("Operation cancelled.");
329
329
  throw new PromptCancelledError();
330
330
  }
331
331
  return result;
332
332
  }
333
333
  function printBanner() {
334
334
  console.log("");
335
- p15.intro(pc15.bgCyan(pc15.black(" @solid-stack/mason ")));
335
+ p19.intro(pc14.bgCyan(pc14.black(" @solid-stack/mason ")));
336
336
  }
337
337
  async function promptProperties(options = {}) {
338
338
  const properties = options.defaults ? [...options.defaults] : [];
339
339
  if (properties.length > 0) {
340
- const summary = properties.map((p18) => `${p18.name}: ${p18.type}${p18.optional ? "?" : ""}`).join(", ");
341
- p15.log.info(pc15.dim(`Pre-configured fields: ${summary}`));
340
+ const summary = properties.map((p22) => `${p22.name}: ${p22.type}${p22.optional ? "?" : ""}`).join(", ");
341
+ p19.log.info(pc14.dim(`Pre-configured fields: ${summary}`));
342
342
  const addMore = handleCancel(
343
- await p15.confirm({
343
+ await p19.confirm({
344
344
  message: "Would you like to add custom fields/properties?",
345
345
  initialValue: false
346
346
  })
@@ -349,10 +349,10 @@ async function promptProperties(options = {}) {
349
349
  return properties;
350
350
  }
351
351
  }
352
- p15.log.info(pc15.cyan("Enter properties (leave name blank when finished):"));
352
+ p19.log.info(pc14.cyan("Enter properties (leave name blank when finished):"));
353
353
  while (true) {
354
354
  const name = handleCancel(
355
- await p15.text({
355
+ await p19.text({
356
356
  message: "Property name (or press Enter to finish):",
357
357
  placeholder: "e.g. title, email, status"
358
358
  })
@@ -361,7 +361,7 @@ async function promptProperties(options = {}) {
361
361
  break;
362
362
  }
363
363
  const type = handleCancel(
364
- await p15.select({
364
+ await p19.select({
365
365
  message: `Type for property "${name.trim()}":`,
366
366
  options: [
367
367
  { value: "string", label: "string" },
@@ -378,7 +378,7 @@ async function promptProperties(options = {}) {
378
378
  let finalType = type;
379
379
  if (type === "custom") {
380
380
  finalType = handleCancel(
381
- await p15.text({
381
+ await p19.text({
382
382
  message: `Enter custom type for "${name.trim()}":`,
383
383
  placeholder: "e.g. UserRole, string | null",
384
384
  validate: (val) => val && val.trim() ? void 0 : "Type cannot be empty"
@@ -386,7 +386,7 @@ async function promptProperties(options = {}) {
386
386
  );
387
387
  }
388
388
  const optional = handleCancel(
389
- await p15.confirm({
389
+ await p19.confirm({
390
390
  message: `Is "${name.trim()}" optional?`,
391
391
  initialValue: false
392
392
  })
@@ -396,8 +396,8 @@ async function promptProperties(options = {}) {
396
396
  type: finalType.trim(),
397
397
  optional
398
398
  });
399
- p15.log.success(
400
- pc15.green(`Added: ${name.trim()}${optional ? "?" : ""}: ${finalType.trim()}`)
399
+ p19.log.success(
400
+ pc14.green(`Added: ${name.trim()}${optional ? "?" : ""}: ${finalType.trim()}`)
401
401
  );
402
402
  }
403
403
  return properties;
@@ -605,8 +605,8 @@ function getMissingNpmPackages(projectRoot, requiredPackages) {
605
605
  ...Object.keys(pkg.devDependencies || {}),
606
606
  ...Object.keys(pkg.peerDependencies || {})
607
607
  ]);
608
- return requiredPackages.filter((p18) => {
609
- const nameOnly = p18.startsWith("@") ? `@${p18.slice(1).split("@")[0] || ""}` : p18.split("@")[0] || p18;
608
+ return requiredPackages.filter((p22) => {
609
+ const nameOnly = p22.startsWith("@") ? `@${p22.slice(1).split("@")[0] || ""}` : p22.split("@")[0] || p22;
610
610
  return !installed.has(nameOnly);
611
611
  });
612
612
  } catch {
@@ -631,7 +631,7 @@ function installNpmPackages(projectRoot, packages, pm) {
631
631
  installCmd = `npm install ${packages.join(" ")}`;
632
632
  break;
633
633
  }
634
- p15.log.info(`Installing missing dependencies with ${manager}: ${pc15.cyan(packages.join(", "))}`);
634
+ p19.log.info(`Installing missing dependencies with ${manager}: ${pc14.cyan(packages.join(", "))}`);
635
635
  execSync(installCmd, {
636
636
  cwd: projectRoot,
637
637
  stdio: "inherit"
@@ -833,7 +833,7 @@ function generateInterfaceCode(ifaceName, kind = "abstractClass", entityName, me
833
833
  const idKey = `${entityKey}Id`;
834
834
  const listKey = `${entityKey}s`;
835
835
  const formattedCustomMethods = customMethods.map((m) => {
836
- const params = m.params && m.params.length > 0 ? m.params.map((p18) => `${p18.name}: ${p18.type}`).join("; ") : "";
836
+ const params = m.params && m.params.length > 0 ? m.params.map((p22) => `${p22.name}: ${p22.type}`).join("; ") : "";
837
837
  const returnType = m.returnType ? `Promise<${m.returnType}>` : "Promise<void>";
838
838
  return {
839
839
  name: m.name,
@@ -1414,22 +1414,22 @@ function generateUseCaseCode(useCaseName, dependencies = [], inputProps = [], ou
1414
1414
  from: /^I[A-Z]/.test(dep) ? `../domain/${dep}.js` : `./${dep}.js`
1415
1415
  }));
1416
1416
  const domainTypesToImport = new Set(domainImports);
1417
- for (const p18 of [...inputProps, ...outputProps]) {
1418
- const rawType = p18.type.trim();
1417
+ for (const p22 of [...inputProps, ...outputProps]) {
1418
+ const rawType = p22.type.trim();
1419
1419
  const baseType = rawType.replace(/\[\]$/, "").trim();
1420
1420
  if (!primitiveTypes.has(baseType) && /^[A-Z][A-Za-z0-9_]*$/.test(baseType) && !dependencies.includes(baseType)) {
1421
1421
  domainTypesToImport.add(baseType);
1422
1422
  }
1423
1423
  }
1424
- const formattedInputProps = inputProps.map((p18) => ({
1425
- name: p18.name,
1426
- optional: p18.optional,
1427
- type: p18.optional && !p18.type.includes("undefined") ? `${p18.type} | undefined` : p18.type
1424
+ const formattedInputProps = inputProps.map((p22) => ({
1425
+ name: p22.name,
1426
+ optional: p22.optional,
1427
+ type: p22.optional && !p22.type.includes("undefined") ? `${p22.type} | undefined` : p22.type
1428
1428
  }));
1429
- const formattedOutputProps = outputProps.map((p18) => ({
1430
- name: p18.name,
1431
- optional: p18.optional,
1432
- type: p18.optional && !p18.type.includes("undefined") ? `${p18.type} | undefined` : p18.type
1429
+ const formattedOutputProps = outputProps.map((p22) => ({
1430
+ name: p22.name,
1431
+ optional: p22.optional,
1432
+ type: p22.optional && !p22.type.includes("undefined") ? `${p22.type} | undefined` : p22.type
1433
1433
  }));
1434
1434
  const formattedDeps = dependencies.map((dep) => ({
1435
1435
  key: toCamelCase(dep),
@@ -2104,7 +2104,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2104
2104
  }
2105
2105
  if (!name) {
2106
2106
  name = handleCancel(
2107
- await p15.text({
2107
+ await p19.text({
2108
2108
  message: "What is the feature name?",
2109
2109
  placeholder: "e.g. users, auth, orders, billing",
2110
2110
  validate: (val) => {
@@ -2120,7 +2120,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2120
2120
  let targetDirRel = defaultDir;
2121
2121
  if (!flags.yes && !flags.target && !flags.dest) {
2122
2122
  targetDirRel = handleCancel(
2123
- await p15.text({
2123
+ await p19.text({
2124
2124
  message: "Target directory for the feature:",
2125
2125
  initialValue: defaultDir,
2126
2126
  defaultValue: defaultDir
@@ -2142,14 +2142,14 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2142
2142
  const customUseCases = [];
2143
2143
  if (!flags.yes) {
2144
2144
  includeExpress = handleCancel(
2145
- await p15.confirm({
2145
+ await p19.confirm({
2146
2146
  message: "Include Express Presentation layer (pxExpress routes & handlers)?",
2147
2147
  initialValue: true
2148
2148
  })
2149
2149
  );
2150
2150
  if (includeExpress) {
2151
2151
  routePath = handleCancel(
2152
- await p15.text({
2152
+ await p19.text({
2153
2153
  message: "Express route prefix:",
2154
2154
  initialValue: `/${normalizedName}`,
2155
2155
  defaultValue: `/${normalizedName}`
@@ -2157,14 +2157,14 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2157
2157
  ).trim();
2158
2158
  }
2159
2159
  includeDomain = handleCancel(
2160
- await p15.confirm({
2160
+ await p19.confirm({
2161
2161
  message: "Include Domain Entity/Entities?",
2162
2162
  initialValue: true
2163
2163
  })
2164
2164
  );
2165
2165
  if (includeDomain) {
2166
2166
  const domainInput = handleCancel(
2167
- await p15.text({
2167
+ await p19.text({
2168
2168
  message: "Domain entity name(s) (comma-separated):",
2169
2169
  initialValue: defaultEntity,
2170
2170
  defaultValue: defaultEntity,
@@ -2183,21 +2183,21 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2183
2183
  entityNames = [defaultEntity];
2184
2184
  }
2185
2185
  includeInterface = handleCancel(
2186
- await p15.confirm({
2186
+ await p19.confirm({
2187
2187
  message: `Include Domain Repository interface(s) for ${entityNames.join(", ")}?`,
2188
2188
  initialValue: true
2189
2189
  })
2190
2190
  );
2191
2191
  if (includeInterface) {
2192
2192
  includeInfra = handleCancel(
2193
- await p15.confirm({
2193
+ await p19.confirm({
2194
2194
  message: `Include Infrastructure Stub implementation(s) for ${entityNames.join(", ")}?`,
2195
2195
  initialValue: true
2196
2196
  })
2197
2197
  );
2198
2198
  }
2199
2199
  includeUseCase = handleCancel(
2200
- await p15.confirm({
2200
+ await p19.confirm({
2201
2201
  message: "Include Domain Use Cases?",
2202
2202
  initialValue: true
2203
2203
  })
@@ -2211,7 +2211,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2211
2211
  { value: "delete", label: "Delete (Delete<Entity>)", hint: "DELETE /:id" }
2212
2212
  ];
2213
2213
  crudOperations = handleCancel(
2214
- await p15.multiselect({
2214
+ await p19.multiselect({
2215
2215
  message: "Select standard CRUD use cases to generate:",
2216
2216
  options: crudChoices,
2217
2217
  initialValues: ["create", "read", "list", "update", "delete"],
@@ -2219,13 +2219,13 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2219
2219
  })
2220
2220
  );
2221
2221
  includeTests = handleCancel(
2222
- await p15.confirm({
2222
+ await p19.confirm({
2223
2223
  message: "Generate Vitest unit tests for use cases?",
2224
2224
  initialValue: true
2225
2225
  })
2226
2226
  );
2227
2227
  const wantCustom = handleCancel(
2228
- await p15.confirm({
2228
+ await p19.confirm({
2229
2229
  message: "Do you want to add any custom use cases to this feature?",
2230
2230
  initialValue: false
2231
2231
  })
@@ -2235,7 +2235,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2235
2235
  const availableIfaces = entityNames.map((e) => `I${e}Repository`);
2236
2236
  while (addMore) {
2237
2237
  const customName = handleCancel(
2238
- await p15.text({
2238
+ await p19.text({
2239
2239
  message: "Custom Use Case name:",
2240
2240
  placeholder: "e.g. ResetPassword, VerifyEmail, AssignRole",
2241
2241
  validate: (v) => v && v.trim() ? void 0 : "Use Case name cannot be empty"
@@ -2245,7 +2245,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2245
2245
  let selectedDeps = [];
2246
2246
  if (availableIfaces.length > 0) {
2247
2247
  selectedDeps = handleCancel(
2248
- await p15.multiselect({
2248
+ await p19.multiselect({
2249
2249
  message: `Select domain interfaces to inject into ${customUcName}:`,
2250
2250
  options: availableIfaces.map((i) => ({ value: i, label: i })),
2251
2251
  required: false
@@ -2254,7 +2254,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2254
2254
  }
2255
2255
  let customInputProps = [];
2256
2256
  const hasInput = handleCancel(
2257
- await p15.confirm({
2257
+ await p19.confirm({
2258
2258
  message: `Does ${customUcName} accept input arguments?`,
2259
2259
  initialValue: true
2260
2260
  })
@@ -2265,7 +2265,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2265
2265
  }
2266
2266
  let customOutputProps = [];
2267
2267
  const hasCustomOutput = handleCancel(
2268
- await p15.confirm({
2268
+ await p19.confirm({
2269
2269
  message: `Does ${customUcName} return custom output data?`,
2270
2270
  initialValue: false
2271
2271
  })
@@ -2275,7 +2275,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2275
2275
  });
2276
2276
  }
2277
2277
  const customIncludeTest = handleCancel(
2278
- await p15.confirm({
2278
+ await p19.confirm({
2279
2279
  message: `Generate Vitest unit test for ${customUcName}?`,
2280
2280
  initialValue: true
2281
2281
  })
@@ -2285,14 +2285,14 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2285
2285
  let customHttpPath = `/${toKebabCase(customUcName)}`;
2286
2286
  if (includeExpress) {
2287
2287
  customCreateHttp = handleCancel(
2288
- await p15.confirm({
2288
+ await p19.confirm({
2289
2289
  message: `Generate a matching ExpressRoute HTTP handler for ${customUcName}?`,
2290
2290
  initialValue: true
2291
2291
  })
2292
2292
  );
2293
2293
  if (customCreateHttp) {
2294
2294
  customHttpMethod = handleCancel(
2295
- await p15.select({
2295
+ await p19.select({
2296
2296
  message: "HTTP method for route:",
2297
2297
  options: [
2298
2298
  { value: "post", label: "POST" },
@@ -2305,7 +2305,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2305
2305
  })
2306
2306
  );
2307
2307
  customHttpPath = handleCancel(
2308
- await p15.text({
2308
+ await p19.text({
2309
2309
  message: "Route subpath (relative to feature prefix):",
2310
2310
  initialValue: `/${toKebabCase(customUcName)}`,
2311
2311
  defaultValue: `/${toKebabCase(customUcName)}`
@@ -2326,7 +2326,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2326
2326
  validationFields: customInputProps
2327
2327
  });
2328
2328
  addMore = handleCancel(
2329
- await p15.confirm({
2329
+ await p19.confirm({
2330
2330
  message: "Add another custom use case?",
2331
2331
  initialValue: false
2332
2332
  })
@@ -2335,7 +2335,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2335
2335
  }
2336
2336
  }
2337
2337
  includeDiProvider = handleCancel(
2338
- await p15.confirm({
2338
+ await p19.confirm({
2339
2339
  message: "Generate / update feature DI provider (diProvider.ts)?",
2340
2340
  initialValue: true
2341
2341
  })
@@ -2347,8 +2347,8 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2347
2347
  entityNames = [defaultEntity];
2348
2348
  }
2349
2349
  }
2350
- const s = p15.spinner();
2351
- s.start(`Generating feature '${pc15.cyan(normalizedName)}'...`);
2350
+ const s = p19.spinner();
2351
+ s.start(`Generating feature '${pc14.cyan(normalizedName)}'...`);
2352
2352
  const options = {
2353
2353
  name: normalizedName,
2354
2354
  projectRoot,
@@ -2394,8 +2394,8 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2394
2394
  }
2395
2395
  s.stop("Feature scaffolded successfully!");
2396
2396
  printGeneratedFiles(files, options.dryRun);
2397
- p15.outro(
2398
- pc15.green(
2397
+ p19.outro(
2398
+ pc14.green(
2399
2399
  `\u2713 Feature '${normalizedName}' initialized in ${path2.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
2400
2400
  )
2401
2401
  );
@@ -2509,7 +2509,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
2509
2509
  }
2510
2510
  if (!name) {
2511
2511
  name = handleCancel(
2512
- await p15.text({
2512
+ await p19.text({
2513
2513
  message: "What is the shared module name?",
2514
2514
  placeholder: "e.g. hasher, time, uuid, cache, logger",
2515
2515
  validate: (val) => {
@@ -2525,7 +2525,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
2525
2525
  let targetDirRel = defaultDir;
2526
2526
  if (!flags.yes && !flags.target && !flags.dest) {
2527
2527
  targetDirRel = handleCancel(
2528
- await p15.text({
2528
+ await p19.text({
2529
2529
  message: "Target directory for the shared module:",
2530
2530
  initialValue: defaultDir,
2531
2531
  defaultValue: defaultDir
@@ -2537,21 +2537,21 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
2537
2537
  let includeStub = true;
2538
2538
  if (!flags.yes) {
2539
2539
  description = handleCancel(
2540
- await p15.text({
2540
+ await p19.text({
2541
2541
  message: "Module description (optional):",
2542
2542
  placeholder: "e.g. In-memory caching and Redis port abstraction",
2543
2543
  initialValue: ""
2544
2544
  })
2545
2545
  ).trim();
2546
2546
  includeStub = handleCancel(
2547
- await p15.confirm({
2547
+ await p19.confirm({
2548
2548
  message: "Include stub implementation in infrastructure/?",
2549
2549
  initialValue: true
2550
2550
  })
2551
2551
  );
2552
2552
  }
2553
- const s = p15.spinner();
2554
- s.start(`Generating shared module '${pc15.cyan(normalizedName)}'...`);
2553
+ const s = p19.spinner();
2554
+ s.start(`Generating shared module '${pc14.cyan(normalizedName)}'...`);
2555
2555
  const files = generateShared({
2556
2556
  name: normalizedName,
2557
2557
  description,
@@ -2564,8 +2564,8 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
2564
2564
  });
2565
2565
  s.stop("Shared module generated successfully!");
2566
2566
  printGeneratedFiles(files, Boolean(flags.dryRun));
2567
- p15.outro(
2568
- pc15.green(
2567
+ p19.outro(
2568
+ pc14.green(
2569
2569
  `\u2713 Shared module '${normalizedName}' initialized in ${path2.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
2570
2570
  )
2571
2571
  );
@@ -2579,14 +2579,14 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2579
2579
  const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
2580
2580
  featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
2581
2581
  const selected = handleCancel(
2582
- await p15.select({
2582
+ await p19.select({
2583
2583
  message: "Select target feature:",
2584
2584
  options: featureOptions
2585
2585
  })
2586
2586
  );
2587
2587
  if (selected === "__custom__") {
2588
2588
  feature = handleCancel(
2589
- await p15.text({
2589
+ await p19.text({
2590
2590
  message: "Enter feature name:",
2591
2591
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2592
2592
  })
@@ -2596,7 +2596,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2596
2596
  }
2597
2597
  } else {
2598
2598
  feature = handleCancel(
2599
- await p15.text({
2599
+ await p19.text({
2600
2600
  message: "Enter target feature name:",
2601
2601
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2602
2602
  })
@@ -2607,7 +2607,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2607
2607
  let name = useCaseNameArg?.trim();
2608
2608
  if (!name) {
2609
2609
  name = handleCancel(
2610
- await p15.text({
2610
+ await p19.text({
2611
2611
  message: "Use Case name:",
2612
2612
  placeholder: "e.g. CreateUser, GetUser, CancelOrder",
2613
2613
  validate: (v) => v && v.trim() ? void 0 : "Use Case name cannot be empty"
@@ -2623,7 +2623,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2623
2623
  label: i.name
2624
2624
  }));
2625
2625
  selectedDeps = handleCancel(
2626
- await p15.multiselect({
2626
+ await p19.multiselect({
2627
2627
  message: "Select domain interfaces to inject into use case:",
2628
2628
  options: multiOptions,
2629
2629
  required: false
@@ -2638,7 +2638,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2638
2638
  let httpPath = "/";
2639
2639
  if (!flags.yes) {
2640
2640
  const hasInput = handleCancel(
2641
- await p15.confirm({
2641
+ await p19.confirm({
2642
2642
  message: "Does this use case accept input arguments?",
2643
2643
  initialValue: true
2644
2644
  })
@@ -2648,7 +2648,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2648
2648
  });
2649
2649
  }
2650
2650
  const hasCustomOutput = handleCancel(
2651
- await p15.confirm({
2651
+ await p19.confirm({
2652
2652
  message: "Does this use case return custom output data?",
2653
2653
  initialValue: false
2654
2654
  })
@@ -2658,20 +2658,20 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2658
2658
  });
2659
2659
  }
2660
2660
  includeTest = handleCancel(
2661
- await p15.confirm({
2661
+ await p19.confirm({
2662
2662
  message: "Generate Vitest unit test skeleton (.test.ts)?",
2663
2663
  initialValue: true
2664
2664
  })
2665
2665
  );
2666
2666
  createHttp = handleCancel(
2667
- await p15.confirm({
2667
+ await p19.confirm({
2668
2668
  message: "Generate a matching ExpressRoute HTTP handler?",
2669
2669
  initialValue: false
2670
2670
  })
2671
2671
  );
2672
2672
  if (createHttp) {
2673
2673
  httpMethod = handleCancel(
2674
- await p15.select({
2674
+ await p19.select({
2675
2675
  message: "HTTP method for route:",
2676
2676
  options: [
2677
2677
  { value: "post", label: "POST" },
@@ -2684,7 +2684,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2684
2684
  })
2685
2685
  );
2686
2686
  httpPath = handleCancel(
2687
- await p15.text({
2687
+ await p19.text({
2688
2688
  message: "Route subpath (relative to feature prefix):",
2689
2689
  initialValue: "/",
2690
2690
  defaultValue: "/"
@@ -2692,8 +2692,8 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2692
2692
  ).trim();
2693
2693
  }
2694
2694
  }
2695
- const s = p15.spinner();
2696
- s.start(`Generating use case '${pc15.cyan(ucName)}'...`);
2695
+ const s = p19.spinner();
2696
+ s.start(`Generating use case '${pc14.cyan(ucName)}'...`);
2697
2697
  const options = {
2698
2698
  name: ucName,
2699
2699
  feature,
@@ -2725,7 +2725,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2725
2725
  }
2726
2726
  s.stop("Use case generated successfully!");
2727
2727
  printGeneratedFiles(files, options.dryRun);
2728
- p15.outro(pc15.green(`Use case '${ucName}' created in feature '${feature}'!`));
2728
+ p19.outro(pc14.green(`Use case '${ucName}' created in feature '${feature}'!`));
2729
2729
  }
2730
2730
  async function runDomainCommand(domainNameArg, flags = {}) {
2731
2731
  const projectRoot = findProjectRoot();
@@ -2736,14 +2736,14 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2736
2736
  const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
2737
2737
  featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
2738
2738
  const selected = handleCancel(
2739
- await p15.select({
2739
+ await p19.select({
2740
2740
  message: "Select target feature:",
2741
2741
  options: featureOptions
2742
2742
  })
2743
2743
  );
2744
2744
  if (selected === "__custom__") {
2745
2745
  feature = handleCancel(
2746
- await p15.text({
2746
+ await p19.text({
2747
2747
  message: "Enter feature name:",
2748
2748
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2749
2749
  })
@@ -2753,7 +2753,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2753
2753
  }
2754
2754
  } else {
2755
2755
  feature = handleCancel(
2756
- await p15.text({
2756
+ await p19.text({
2757
2757
  message: "Enter target feature name:",
2758
2758
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2759
2759
  })
@@ -2764,7 +2764,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2764
2764
  let name = domainNameArg?.trim();
2765
2765
  if (!name) {
2766
2766
  name = handleCancel(
2767
- await p15.text({
2767
+ await p19.text({
2768
2768
  message: "Domain model / entity name:",
2769
2769
  placeholder: "e.g. User, Order, Product, Address",
2770
2770
  validate: (v) => v && v.trim() ? void 0 : "Domain name cannot be empty"
@@ -2776,7 +2776,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2776
2776
  let properties = [];
2777
2777
  if (!flags.yes) {
2778
2778
  kind = handleCancel(
2779
- await p15.select({
2779
+ await p19.select({
2780
2780
  message: "Select domain model type:",
2781
2781
  options: [
2782
2782
  { value: "interface", label: "TypeScript Interface (pure data structure)" },
@@ -2795,8 +2795,8 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2795
2795
  defaults: kind === "valueObject" ? [{ name: "value", type: "string" }] : defaultProps
2796
2796
  });
2797
2797
  }
2798
- const s = p15.spinner();
2799
- s.start(`Generating domain model '${pc15.cyan(entityName)}'...`);
2798
+ const s = p19.spinner();
2799
+ s.start(`Generating domain model '${pc14.cyan(entityName)}'...`);
2800
2800
  const options = {
2801
2801
  name: entityName,
2802
2802
  feature,
@@ -2809,7 +2809,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2809
2809
  const file = generateDomain(options);
2810
2810
  s.stop("Domain model generated successfully!");
2811
2811
  printGeneratedFiles([file], options.dryRun);
2812
- p15.outro(pc15.green(`Domain model '${entityName}' created in feature '${feature}'!`));
2812
+ p19.outro(pc14.green(`Domain model '${entityName}' created in feature '${feature}'!`));
2813
2813
  }
2814
2814
  async function runInfrastructureCommand(classNameArg, flags = {}) {
2815
2815
  const projectRoot = findProjectRoot();
@@ -2818,7 +2818,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2818
2818
  let feature = flags.feature?.trim();
2819
2819
  if (!flags.yes && !flags.global && !flags.feature) {
2820
2820
  scope = handleCancel(
2821
- await p15.select({
2821
+ await p19.select({
2822
2822
  message: "Where should this infrastructure implementation be placed?",
2823
2823
  options: [
2824
2824
  { value: "feature", label: "Feature-level (features/<feature>/infrastructure)" },
@@ -2833,14 +2833,14 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2833
2833
  const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
2834
2834
  featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
2835
2835
  const selected = handleCancel(
2836
- await p15.select({
2836
+ await p19.select({
2837
2837
  message: "Select target feature:",
2838
2838
  options: featureOptions
2839
2839
  })
2840
2840
  );
2841
2841
  if (selected === "__custom__") {
2842
2842
  feature = handleCancel(
2843
- await p15.text({
2843
+ await p19.text({
2844
2844
  message: "Enter feature name:",
2845
2845
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2846
2846
  })
@@ -2850,7 +2850,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2850
2850
  }
2851
2851
  } else {
2852
2852
  feature = handleCancel(
2853
- await p15.text({
2853
+ await p19.text({
2854
2854
  message: "Enter target feature name:",
2855
2855
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2856
2856
  })
@@ -2863,7 +2863,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2863
2863
  let name = classNameArg?.trim();
2864
2864
  if (!name) {
2865
2865
  name = handleCancel(
2866
- await p15.text({
2866
+ await p19.text({
2867
2867
  message: "Infrastructure class name:",
2868
2868
  placeholder: "e.g. StubUserRepository, PostgresUserRepository, S3FileStorage",
2869
2869
  validate: (v) => v && v.trim() ? void 0 : "Class name cannot be empty"
@@ -2882,7 +2882,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2882
2882
  (i) => className.toLowerCase().includes(removeInterfacePrefix(i.name).toLowerCase())
2883
2883
  );
2884
2884
  const ifaceChoice = handleCancel(
2885
- await p15.select({
2885
+ await p19.select({
2886
2886
  message: "Does this class implement a domain interface or abstract class?",
2887
2887
  options: [
2888
2888
  ...interfaces.map((i) => ({ value: i.name, label: i.name })),
@@ -2894,7 +2894,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2894
2894
  );
2895
2895
  if (ifaceChoice === "__custom__") {
2896
2896
  implementsInterface = handleCancel(
2897
- await p15.text({
2897
+ await p19.text({
2898
2898
  message: "Enter interface or abstract class name:",
2899
2899
  placeholder: "e.g. IUserRepository, BaseStorageAdapter",
2900
2900
  validate: (v) => v && v.trim() ? void 0 : "Name cannot be empty"
@@ -2905,14 +2905,14 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2905
2905
  }
2906
2906
  } else {
2907
2907
  const wantImpl = handleCancel(
2908
- await p15.confirm({
2908
+ await p19.confirm({
2909
2909
  message: "Does this class implement a domain interface or abstract class?",
2910
2910
  initialValue: false
2911
2911
  })
2912
2912
  );
2913
2913
  if (wantImpl) {
2914
2914
  implementsInterface = handleCancel(
2915
- await p15.text({
2915
+ await p19.text({
2916
2916
  message: "Enter interface or abstract class name:",
2917
2917
  placeholder: "e.g. IUserRepository, BaseStorageAdapter",
2918
2918
  validate: (v) => v && v.trim() ? void 0 : "Name cannot be empty"
@@ -2932,7 +2932,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2932
2932
  const allInterfaces = listDomainInterfaces(projectRoot);
2933
2933
  if (allInterfaces.length > 0) {
2934
2934
  const ifaceChoice = handleCancel(
2935
- await p15.select({
2935
+ await p19.select({
2936
2936
  message: "Does this class implement an interface or abstract class?",
2937
2937
  options: [
2938
2938
  ...allInterfaces.map((i) => ({ value: i.name, label: `${i.name} (${i.feature})` })),
@@ -2944,7 +2944,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2944
2944
  );
2945
2945
  if (ifaceChoice === "__custom__") {
2946
2946
  implementsInterface = handleCancel(
2947
- await p15.text({
2947
+ await p19.text({
2948
2948
  message: "Enter interface or abstract class name:",
2949
2949
  placeholder: "e.g. IAuthRepository",
2950
2950
  validate: (v) => v && v.trim() ? void 0 : "Name cannot be empty"
@@ -2957,7 +2957,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2957
2957
  }
2958
2958
  if (!flags.yes && !flags.kind) {
2959
2959
  kind = handleCancel(
2960
- await p15.select({
2960
+ await p19.select({
2961
2961
  message: "Select implementation kind:",
2962
2962
  options: [
2963
2963
  { value: "stub", label: "In-Memory Stub (with in-memory store and test helper methods)" },
@@ -2968,7 +2968,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2968
2968
  );
2969
2969
  if (scope === "feature" && feature && implementsInterface) {
2970
2970
  registerInProvider = handleCancel(
2971
- await p15.confirm({
2971
+ await p19.confirm({
2972
2972
  message: "Register this implementation in feature diProvider.ts?",
2973
2973
  initialValue: true
2974
2974
  })
@@ -2977,8 +2977,8 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2977
2977
  } else if (flags.yes && scope === "feature" && feature && implementsInterface) {
2978
2978
  registerInProvider = !flags.noProvider;
2979
2979
  }
2980
- const s = p15.spinner();
2981
- s.start(`Generating infrastructure class '${pc15.cyan(className)}'...`);
2980
+ const s = p19.spinner();
2981
+ s.start(`Generating infrastructure class '${pc14.cyan(className)}'...`);
2982
2982
  const files = [];
2983
2983
  const options = {
2984
2984
  name: className,
@@ -3013,7 +3013,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
3013
3013
  }
3014
3014
  s.stop("Infrastructure class generated successfully!");
3015
3015
  printGeneratedFiles(files, options.dryRun);
3016
- p15.outro(pc15.green(`Infrastructure class '${className}' created!`));
3016
+ p19.outro(pc14.green(`Infrastructure class '${className}' created!`));
3017
3017
  }
3018
3018
  async function runInterfaceCommand(interfaceNameArg, flags = {}) {
3019
3019
  const projectRoot = findProjectRoot();
@@ -3024,14 +3024,14 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
3024
3024
  const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
3025
3025
  featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
3026
3026
  const selected = handleCancel(
3027
- await p15.select({
3027
+ await p19.select({
3028
3028
  message: "Select target feature:",
3029
3029
  options: featureOptions
3030
3030
  })
3031
3031
  );
3032
3032
  if (selected === "__custom__") {
3033
3033
  feature = handleCancel(
3034
- await p15.text({
3034
+ await p19.text({
3035
3035
  message: "Enter feature name:",
3036
3036
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
3037
3037
  })
@@ -3041,7 +3041,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
3041
3041
  }
3042
3042
  } else {
3043
3043
  feature = handleCancel(
3044
- await p15.text({
3044
+ await p19.text({
3045
3045
  message: "Enter target feature name:",
3046
3046
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
3047
3047
  })
@@ -3052,7 +3052,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
3052
3052
  let name = interfaceNameArg?.trim();
3053
3053
  if (!name) {
3054
3054
  name = handleCancel(
3055
- await p15.text({
3055
+ await p19.text({
3056
3056
  message: "Domain interface name:",
3057
3057
  placeholder: "e.g. IUserRepository, IPaymentService",
3058
3058
  validate: (v) => v && v.trim() ? void 0 : "Interface name cannot be empty"
@@ -3070,7 +3070,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
3070
3070
  if (!flags.yes) {
3071
3071
  if (existingEntities.length > 0) {
3072
3072
  const selectedEntity = handleCancel(
3073
- await p15.select({
3073
+ await p19.select({
3074
3074
  message: "Select domain entity associated with this interface:",
3075
3075
  options: [
3076
3076
  ...existingEntities.map((e) => ({ value: e.name, label: e.name })),
@@ -3081,7 +3081,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
3081
3081
  );
3082
3082
  if (selectedEntity === "__custom__") {
3083
3083
  entityName = handleCancel(
3084
- await p15.text({
3084
+ await p19.text({
3085
3085
  message: "Enter entity name:",
3086
3086
  defaultValue: removeInterfacePrefix(ifaceName).replace(/Repository$|Service$/, "")
3087
3087
  })
@@ -3091,7 +3091,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
3091
3091
  }
3092
3092
  }
3093
3093
  kind = handleCancel(
3094
- await p15.select({
3094
+ await p19.select({
3095
3095
  message: "Interface definition style:",
3096
3096
  options: [
3097
3097
  { value: "abstractClass", label: "Abstract Class (Recommended - acts as DI token & type)" },
@@ -3101,7 +3101,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
3101
3101
  })
3102
3102
  );
3103
3103
  methodsPreset = handleCancel(
3104
- await p15.select({
3104
+ await p19.select({
3105
3105
  message: "Method signature preset:",
3106
3106
  options: [
3107
3107
  { value: "crud", label: "Standard CRUD (GetById, List, Create, Update, Delete)" },
@@ -3111,22 +3111,22 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
3111
3111
  })
3112
3112
  );
3113
3113
  generateStub = handleCancel(
3114
- await p15.confirm({
3114
+ await p19.confirm({
3115
3115
  message: "Auto-generate Infrastructure Stub implementation?",
3116
3116
  initialValue: true
3117
3117
  })
3118
3118
  );
3119
3119
  if (generateStub) {
3120
3120
  registerInProvider = handleCancel(
3121
- await p15.confirm({
3121
+ await p19.confirm({
3122
3122
  message: "Auto-register interface & stub in feature diProvider.ts?",
3123
3123
  initialValue: true
3124
3124
  })
3125
3125
  );
3126
3126
  }
3127
3127
  }
3128
- const s = p15.spinner();
3129
- s.start(`Generating interface '${pc15.cyan(ifaceName)}'...`);
3128
+ const s = p19.spinner();
3129
+ s.start(`Generating interface '${pc14.cyan(ifaceName)}'...`);
3130
3130
  const files = [];
3131
3131
  const options = {
3132
3132
  name: ifaceName,
@@ -3175,7 +3175,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
3175
3175
  }
3176
3176
  s.stop("Interface generated successfully!");
3177
3177
  printGeneratedFiles(files, options.dryRun);
3178
- p15.outro(pc15.green(`Interface '${ifaceName}' created in feature '${feature}'!`));
3178
+ p19.outro(pc14.green(`Interface '${ifaceName}' created in feature '${feature}'!`));
3179
3179
  }
3180
3180
  async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
3181
3181
  const projectRoot = findProjectRoot();
@@ -3186,14 +3186,14 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
3186
3186
  const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
3187
3187
  featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
3188
3188
  const selected = handleCancel(
3189
- await p15.select({
3189
+ await p19.select({
3190
3190
  message: "Select target feature:",
3191
3191
  options: featureOptions
3192
3192
  })
3193
3193
  );
3194
3194
  if (selected === "__custom__") {
3195
3195
  feature = handleCancel(
3196
- await p15.text({
3196
+ await p19.text({
3197
3197
  message: "Enter feature name:",
3198
3198
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
3199
3199
  })
@@ -3203,7 +3203,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
3203
3203
  }
3204
3204
  } else {
3205
3205
  feature = handleCancel(
3206
- await p15.text({
3206
+ await p19.text({
3207
3207
  message: "Enter target feature name:",
3208
3208
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
3209
3209
  })
@@ -3214,7 +3214,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
3214
3214
  let name = handlerNameArg?.trim();
3215
3215
  if (!name) {
3216
3216
  name = handleCancel(
3217
- await p15.text({
3217
+ await p19.text({
3218
3218
  message: "HTTP Route Handler class name:",
3219
3219
  placeholder: "e.g. CreateUserHttp, GetUserHttp, HealthHttp",
3220
3220
  validate: (v) => v && v.trim() ? void 0 : "Handler name cannot be empty"
@@ -3233,7 +3233,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
3233
3233
  let validationFields = [];
3234
3234
  if (!flags.yes) {
3235
3235
  method = handleCancel(
3236
- await p15.select({
3236
+ await p19.select({
3237
3237
  message: "HTTP Method:",
3238
3238
  options: [
3239
3239
  { value: "get", label: "GET" },
@@ -3247,7 +3247,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
3247
3247
  );
3248
3248
  const defaultPath = method === "get" ? "/" : "/";
3249
3249
  subPath = handleCancel(
3250
- await p15.text({
3250
+ await p19.text({
3251
3251
  message: "Route subpath (relative to feature prefix):",
3252
3252
  initialValue: defaultPath,
3253
3253
  defaultValue: defaultPath
@@ -3258,7 +3258,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
3258
3258
  const ucOptions = useCases.map((u) => ({ value: u.name, label: u.name }));
3259
3259
  ucOptions.push({ value: "__none__", label: "None / Standalone Handler" });
3260
3260
  const chosenUc = handleCancel(
3261
- await p15.select({
3261
+ await p19.select({
3262
3262
  message: "Connect this handler to a domain Use Case?",
3263
3263
  options: ucOptions,
3264
3264
  initialValue: ucOptions[0]?.value
@@ -3269,7 +3269,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
3269
3269
  }
3270
3270
  }
3271
3271
  includeValidation = handleCancel(
3272
- await p15.confirm({
3272
+ await p19.confirm({
3273
3273
  message: "Include Zod inbound request validation?",
3274
3274
  initialValue: method !== "delete"
3275
3275
  })
@@ -3277,7 +3277,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
3277
3277
  if (includeValidation) {
3278
3278
  const defaultSource = method === "get" ? "query" : "body";
3279
3279
  validationSource = handleCancel(
3280
- await p15.select({
3280
+ await p19.select({
3281
3281
  message: "Validation data source:",
3282
3282
  options: [
3283
3283
  { value: "body", label: "req.body (for POST / PUT / PATCH payloads)" },
@@ -3291,8 +3291,8 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
3291
3291
  });
3292
3292
  }
3293
3293
  }
3294
- const s = p15.spinner();
3295
- s.start(`Generating HTTP handler '${pc15.cyan(className)}'...`);
3294
+ const s = p19.spinner();
3295
+ s.start(`Generating HTTP handler '${pc14.cyan(className)}'...`);
3296
3296
  const options = {
3297
3297
  name: className,
3298
3298
  feature,
@@ -3310,7 +3310,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
3310
3310
  const files = generateHttpHandler(options);
3311
3311
  s.stop("HTTP handler generated successfully!");
3312
3312
  printGeneratedFiles(files, options.dryRun);
3313
- p15.outro(pc15.green(`HTTP handler '${className}' created in feature '${feature}'!`));
3313
+ p19.outro(pc14.green(`HTTP handler '${className}' created in feature '${feature}'!`));
3314
3314
  }
3315
3315
  function generateInjectableCode(className, dependencies = [], methods = [], implementsInterface) {
3316
3316
  const formattedDeps = dependencies.map((dep) => ({
@@ -3320,7 +3320,7 @@ function generateInjectableCode(className, dependencies = [], methods = [], impl
3320
3320
  const formattedMethods = methods.map((m) => {
3321
3321
  const isAsync = m.async !== false;
3322
3322
  const retType = m.returnType ? isAsync ? `Promise<${m.returnType}>` : m.returnType : isAsync ? "Promise<void>" : "void";
3323
- const params = m.params && m.params.length > 0 ? m.params.map((p18) => `${p18.name}: ${p18.type}`).join(", ") : "";
3323
+ const params = m.params && m.params.length > 0 ? m.params.map((p22) => `${p22.name}: ${p22.type}`).join(", ") : "";
3324
3324
  return {
3325
3325
  name: m.name,
3326
3326
  isAsync,
@@ -3390,7 +3390,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3390
3390
  let name = classNameArg?.trim();
3391
3391
  if (!name) {
3392
3392
  name = handleCancel(
3393
- await p15.text({
3393
+ await p19.text({
3394
3394
  message: "Injectable class name:",
3395
3395
  placeholder: "e.g. PasswordHasher, TokenService, UserTransformer",
3396
3396
  validate: (v) => v && v.trim() ? void 0 : "Class name cannot be empty"
@@ -3403,7 +3403,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3403
3403
  let customPath;
3404
3404
  if (!flags.yes) {
3405
3405
  location = handleCancel(
3406
- await p15.select({
3406
+ await p19.select({
3407
3407
  message: "Select target architectural location:",
3408
3408
  options: [
3409
3409
  { value: "feature-service", label: "Feature Service (features/<feature>/services/)" },
@@ -3421,14 +3421,14 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3421
3421
  const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
3422
3422
  featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
3423
3423
  const selected = handleCancel(
3424
- await p15.select({
3424
+ await p19.select({
3425
3425
  message: "Select target feature:",
3426
3426
  options: featureOptions
3427
3427
  })
3428
3428
  );
3429
3429
  if (selected === "__custom__") {
3430
3430
  feature = handleCancel(
3431
- await p15.text({
3431
+ await p19.text({
3432
3432
  message: "Enter feature name:",
3433
3433
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
3434
3434
  })
@@ -3438,7 +3438,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3438
3438
  }
3439
3439
  } else {
3440
3440
  feature = handleCancel(
3441
- await p15.text({
3441
+ await p19.text({
3442
3442
  message: "Enter target feature name:",
3443
3443
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
3444
3444
  })
@@ -3448,7 +3448,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3448
3448
  feature = toCamelCase(feature);
3449
3449
  } else if (location === "custom") {
3450
3450
  customPath = handleCancel(
3451
- await p15.text({
3451
+ await p19.text({
3452
3452
  message: "Enter destination directory path:",
3453
3453
  placeholder: "e.g. src/services or lib/utils",
3454
3454
  validate: (v) => v && v.trim() ? void 0 : "Path cannot be empty"
@@ -3459,14 +3459,14 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3459
3459
  let generateInterface2 = false;
3460
3460
  if (!flags.yes) {
3461
3461
  generateInterface2 = handleCancel(
3462
- await p15.confirm({
3462
+ await p19.confirm({
3463
3463
  message: `Generate abstract class interface (I${className}) as runtime DI token?`,
3464
3464
  initialValue: false
3465
3465
  })
3466
3466
  );
3467
3467
  }
3468
- const s = p15.spinner();
3469
- s.start(`Generating injectable '${pc15.cyan(className)}'...`);
3468
+ const s = p19.spinner();
3469
+ s.start(`Generating injectable '${pc14.cyan(className)}'...`);
3470
3470
  const options = {
3471
3471
  name: className,
3472
3472
  feature,
@@ -3480,17 +3480,17 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3480
3480
  const files = generateInjectable(options);
3481
3481
  s.stop("Injectable class generated successfully!");
3482
3482
  printGeneratedFiles(files, options.dryRun);
3483
- p15.outro(pc15.green(`Injectable '${className}' created successfully!`));
3483
+ p19.outro(pc14.green(`Injectable '${className}' created successfully!`));
3484
3484
  }
3485
- async function runEnvCommand(flags = {}) {
3485
+ async function runEnvInit(flags = {}) {
3486
3486
  const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
3487
- const s = p15.spinner();
3487
+ const s = p19.spinner();
3488
3488
  s.start("Scanning for .example environment files...");
3489
3489
  const files = fs4.readdirSync(projectRoot);
3490
3490
  const exampleFiles = files.filter((file) => file.startsWith(".env") && file.endsWith(".example")).sort();
3491
3491
  if (exampleFiles.length === 0) {
3492
3492
  s.stop("No .example files found.");
3493
- p15.log.info(pc15.yellow(`No files matching '.env*.example' were found in ${projectRoot}`));
3493
+ p19.log.info(pc14.yellow(`No files matching '.env*.example' were found in ${projectRoot}`));
3494
3494
  return [];
3495
3495
  }
3496
3496
  s.message(`Found ${exampleFiles.length} template(s). Generating...`);
@@ -3499,25 +3499,669 @@ async function runEnvCommand(flags = {}) {
3499
3499
  const sourcePath = path2.join(projectRoot, exampleFile);
3500
3500
  const targetFile = exampleFile.replace(/\.example$/, "");
3501
3501
  const targetPath = path2.join(projectRoot, targetFile);
3502
+ let overwrite = flags.overwrite;
3503
+ if (overwrite === void 0 && !flags.dryRun && !flags.yes && !process.env.VITEST && process.stdin.isTTY && fs4.existsSync(targetPath)) {
3504
+ overwrite = await p19.confirm({
3505
+ message: `${targetFile} already exists. Overwrite?`,
3506
+ initialValue: false
3507
+ });
3508
+ if (p19.isCancel(overwrite)) {
3509
+ p19.cancel("Operation cancelled.");
3510
+ process.exit(0);
3511
+ }
3512
+ }
3502
3513
  const content = fs4.readFileSync(sourcePath, "utf-8");
3503
3514
  const genFile = writeFileSafely(targetPath, content, {
3504
3515
  projectRoot,
3505
3516
  dryRun: Boolean(flags.dryRun),
3506
- overwrite: Boolean(flags.overwrite)
3517
+ overwrite: Boolean(overwrite)
3507
3518
  });
3508
3519
  generatedFiles.push(genFile);
3509
3520
  }
3510
3521
  s.stop("Environment files processed!");
3511
3522
  printGeneratedFiles(generatedFiles, Boolean(flags.dryRun));
3512
- p15.outro(pc15.green("\u2713 Local environment files are ready!"));
3523
+ p19.outro(pc14.green("\u2713 Local environment files are ready!"));
3513
3524
  return generatedFiles;
3514
3525
  }
3526
+ var runEnvCommand = runEnvInit;
3527
+ var EnvEditor = class {
3528
+ lines;
3529
+ filePath;
3530
+ constructor(filePath) {
3531
+ this.filePath = filePath;
3532
+ this.lines = fs4.existsSync(filePath) ? fs4.readFileSync(filePath, "utf-8").split(/\r?\n/) : [];
3533
+ }
3534
+ getLines() {
3535
+ return [...this.lines];
3536
+ }
3537
+ getKeys() {
3538
+ return this.lines.filter((line) => line.trim() && !line.trim().startsWith("#")).map((line) => {
3539
+ const eqIdx = line.indexOf("=");
3540
+ return eqIdx !== -1 ? line.substring(0, eqIdx).trim() : line.trim();
3541
+ }).filter(Boolean);
3542
+ }
3543
+ getValue(key) {
3544
+ const line = this.lines.find((l) => {
3545
+ const trimmed = l.trim();
3546
+ if (trimmed.startsWith("#") || !trimmed) return false;
3547
+ const eqIdx = l.indexOf("=");
3548
+ const k = eqIdx !== -1 ? l.substring(0, eqIdx).trim() : l.trim();
3549
+ return k === key;
3550
+ });
3551
+ if (!line) return null;
3552
+ const eqIndex = line.indexOf("=");
3553
+ return eqIndex !== -1 ? line.substring(eqIndex + 1).trim() : "";
3554
+ }
3555
+ has(key) {
3556
+ return this.getKeys().includes(key);
3557
+ }
3558
+ set(key, value) {
3559
+ const newLine = `${key}=${value}`;
3560
+ const index = this.lines.findIndex((l) => {
3561
+ const trimmed = l.trim();
3562
+ if (trimmed.startsWith("#") || !trimmed) return false;
3563
+ const eqIdx = l.indexOf("=");
3564
+ const k = eqIdx !== -1 ? l.substring(0, eqIdx).trim() : l.trim();
3565
+ return k === key;
3566
+ });
3567
+ if (index !== -1) {
3568
+ this.lines[index] = newLine;
3569
+ } else {
3570
+ if (this.lines.length > 0 && this.lines[this.lines.length - 1] !== "") {
3571
+ this.lines.push("");
3572
+ }
3573
+ this.lines.push(newLine);
3574
+ }
3575
+ }
3576
+ delete(key) {
3577
+ const initialLen = this.lines.length;
3578
+ this.lines = this.lines.filter((l) => {
3579
+ const trimmed = l.trim();
3580
+ if (trimmed.startsWith("#") || !trimmed) return true;
3581
+ const eqIdx = l.indexOf("=");
3582
+ const k = eqIdx !== -1 ? l.substring(0, eqIdx).trim() : l.trim();
3583
+ return k !== key;
3584
+ });
3585
+ return this.lines.length !== initialLen;
3586
+ }
3587
+ redactAll() {
3588
+ this.lines = this.lines.map((line) => {
3589
+ const trimmed = line.trim();
3590
+ if (trimmed.startsWith("#") || !trimmed) return line;
3591
+ const eqIndex = line.indexOf("=");
3592
+ return eqIndex !== -1 ? line.substring(0, eqIndex + 1) : line;
3593
+ });
3594
+ }
3595
+ save() {
3596
+ const dir = path2.dirname(this.filePath);
3597
+ if (!fs4.existsSync(dir)) {
3598
+ fs4.mkdirSync(dir, { recursive: true });
3599
+ }
3600
+ fs4.writeFileSync(this.filePath, this.lines.join("\n"), "utf-8");
3601
+ }
3602
+ };
3603
+ function discoverEnvironments(projectRoot) {
3604
+ if (!fs4.existsSync(projectRoot)) {
3605
+ return ["local", "dev", "prod"];
3606
+ }
3607
+ const files = fs4.readdirSync(projectRoot);
3608
+ const envs = /* @__PURE__ */ new Set();
3609
+ for (const file of files) {
3610
+ if (file.startsWith(".env")) {
3611
+ const cleanName = file.replace(/\.example$/, "");
3612
+ const match = cleanName.match(/^\.env\.([a-zA-Z0-9_-]+)$/);
3613
+ if (match && match[1]) {
3614
+ envs.add(match[1]);
3615
+ } else if (cleanName === ".env") {
3616
+ envs.add("local");
3617
+ }
3618
+ }
3619
+ }
3620
+ return envs.size > 0 ? Array.from(envs) : ["local", "dev", "prod"];
3621
+ }
3622
+ function resolveEnvFilename(envName, isExample = false) {
3623
+ const base = envName === "local" ? ".env" : `.env.${envName}`;
3624
+ return isExample ? `${base}.example` : base;
3625
+ }
3626
+
3627
+ // src/commands/env/initExamples.ts
3628
+ async function runEnvInitExamples(flags = {}) {
3629
+ const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
3630
+ const s = p19.spinner();
3631
+ s.start("Scanning for local environment files...");
3632
+ const files = fs4.readdirSync(projectRoot, { withFileTypes: true });
3633
+ const envFiles = files.filter((dirent) => dirent.isFile() && dirent.name.startsWith(".env") && !dirent.name.endsWith(".example")).map((dirent) => dirent.name).sort();
3634
+ if (envFiles.length === 0) {
3635
+ s.stop("No local .env files found.");
3636
+ p19.log.info(pc14.yellow(`No files matching '.env*' were found in ${projectRoot}`));
3637
+ return [];
3638
+ }
3639
+ s.message(`Found ${envFiles.length} local .env file(s). Generating templates...`);
3640
+ const generatedFiles = [];
3641
+ for (const envFile of envFiles) {
3642
+ const sourcePath = path2.join(projectRoot, envFile);
3643
+ const targetFile = `${envFile}.example`;
3644
+ const targetPath = path2.join(projectRoot, targetFile);
3645
+ let overwrite = flags.overwrite;
3646
+ if (overwrite === void 0 && !flags.dryRun && !flags.yes && !process.env.VITEST && process.stdin.isTTY && fs4.existsSync(targetPath)) {
3647
+ overwrite = await p19.confirm({
3648
+ message: `${targetFile} already exists. Overwrite with redacted values?`,
3649
+ initialValue: true
3650
+ });
3651
+ if (p19.isCancel(overwrite)) {
3652
+ p19.cancel("Operation cancelled.");
3653
+ process.exit(0);
3654
+ }
3655
+ }
3656
+ const editor = new EnvEditor(sourcePath);
3657
+ editor.redactAll();
3658
+ const redactedContent = editor.getLines().join("\n");
3659
+ const genFile = writeFileSafely(targetPath, redactedContent, {
3660
+ projectRoot,
3661
+ dryRun: Boolean(flags.dryRun),
3662
+ overwrite: Boolean(overwrite)
3663
+ });
3664
+ generatedFiles.push(genFile);
3665
+ }
3666
+ s.stop("Environment example templates generated!");
3667
+ printGeneratedFiles(generatedFiles, Boolean(flags.dryRun));
3668
+ p19.outro(pc14.green("\u2713 .env*.example files are up to date and safely redacted!"));
3669
+ return generatedFiles;
3670
+ }
3671
+ var runEnvExamplesCommand = runEnvInitExamples;
3672
+ function isInteractive(flags) {
3673
+ if (flags.yes || Boolean(flags.nonInteractive)) return false;
3674
+ if (process.env.VITEST) return false;
3675
+ return Boolean(process.stdin.isTTY);
3676
+ }
3677
+ function getAllExistingKeys(projectRoot, envs) {
3678
+ const keys = /* @__PURE__ */ new Set();
3679
+ for (const env of envs) {
3680
+ const localPath = path2.join(projectRoot, resolveEnvFilename(env, false));
3681
+ const examplePath = path2.join(projectRoot, resolveEnvFilename(env, true));
3682
+ if (fs4.existsSync(localPath)) {
3683
+ new EnvEditor(localPath).getKeys().forEach((k) => keys.add(k));
3684
+ }
3685
+ if (fs4.existsSync(examplePath)) {
3686
+ new EnvEditor(examplePath).getKeys().forEach((k) => keys.add(k));
3687
+ }
3688
+ }
3689
+ return Array.from(keys).sort();
3690
+ }
3691
+ async function runEnvAdd(key, value, flags = {}) {
3692
+ const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
3693
+ const allEnvs = discoverEnvironments(projectRoot);
3694
+ let targetKey = key?.trim();
3695
+ if (!targetKey) {
3696
+ if (!isInteractive(flags)) {
3697
+ throw new Error("Variable key is required for 'mason env add <key> [value]'");
3698
+ }
3699
+ targetKey = handleCancel(
3700
+ await p19.text({
3701
+ message: "Enter the variable key name:",
3702
+ placeholder: "e.g. DATABASE_URL, STRIPE_KEY, PORT",
3703
+ validate: (val) => {
3704
+ if (!val || !val.trim()) return "Variable key name cannot be empty";
3705
+ if (/\s/.test(val.trim())) return "Variable key cannot contain spaces";
3706
+ if (val.includes("=")) return "Variable key cannot contain '='";
3707
+ return void 0;
3708
+ }
3709
+ })
3710
+ ).trim();
3711
+ }
3712
+ let targetEnvs = [];
3713
+ if (flags.env) {
3714
+ targetEnvs = String(flags.env).split(",").map((s) => s.trim()).filter(Boolean);
3715
+ } else if (!isInteractive(flags)) {
3716
+ targetEnvs = allEnvs;
3717
+ } else {
3718
+ targetEnvs = handleCancel(
3719
+ await p19.multiselect({
3720
+ message: `Which environments should receive ${pc14.bold(targetKey)}?`,
3721
+ options: allEnvs.map((env) => ({
3722
+ value: env,
3723
+ label: env === "local" ? "local (.env)" : `${env} (.env.${env})`
3724
+ })),
3725
+ initialValues: allEnvs,
3726
+ required: true
3727
+ })
3728
+ );
3729
+ }
3730
+ if (targetEnvs.length === 0) {
3731
+ p19.log.warn("No environments selected. Nothing was changed.");
3732
+ return { key: targetKey, environments: [] };
3733
+ }
3734
+ for (const env of targetEnvs) {
3735
+ let envVal = value;
3736
+ if (envVal === void 0) {
3737
+ if (!isInteractive(flags)) {
3738
+ envVal = "";
3739
+ } else {
3740
+ envVal = handleCancel(
3741
+ await p19.text({
3742
+ message: `Value for ${pc14.cyan(targetKey)} in ${pc14.bold(env)} (${resolveEnvFilename(env)})?`,
3743
+ initialValue: "",
3744
+ placeholder: "Leave empty for blank value"
3745
+ })
3746
+ );
3747
+ }
3748
+ } else if (isInteractive(flags) && !flags.yes) {
3749
+ envVal = handleCancel(
3750
+ await p19.text({
3751
+ message: `Value for ${pc14.cyan(targetKey)} in ${pc14.bold(env)} (${resolveEnvFilename(env)})?`,
3752
+ initialValue: value ?? "",
3753
+ placeholder: "Leave empty for blank value"
3754
+ })
3755
+ );
3756
+ }
3757
+ const localPath = path2.join(projectRoot, resolveEnvFilename(env, false));
3758
+ const examplePath = path2.join(projectRoot, resolveEnvFilename(env, true));
3759
+ if (flags.dryRun) {
3760
+ p19.log.info(
3761
+ pc14.cyan(`[DRY RUN] Would set ${targetKey}=${envVal} in ${resolveEnvFilename(env, false)}`)
3762
+ );
3763
+ p19.log.info(
3764
+ pc14.cyan(`[DRY RUN] Would add ${targetKey}= to ${resolveEnvFilename(env, true)}`)
3765
+ );
3766
+ } else {
3767
+ const localEditor = new EnvEditor(localPath);
3768
+ localEditor.set(targetKey, envVal);
3769
+ localEditor.save();
3770
+ const exampleEditor = new EnvEditor(examplePath);
3771
+ exampleEditor.set(targetKey, "");
3772
+ exampleEditor.save();
3773
+ }
3774
+ }
3775
+ if (!flags.dryRun) {
3776
+ p19.log.success(
3777
+ pc14.green(
3778
+ `\u2713 Added ${pc14.bold(targetKey)} to [${targetEnvs.join(", ")}] and updated .example templates.`
3779
+ )
3780
+ );
3781
+ }
3782
+ return { key: targetKey, environments: targetEnvs };
3783
+ }
3784
+ async function runEnvUpdate(key, value, flags = {}) {
3785
+ const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
3786
+ const allEnvs = discoverEnvironments(projectRoot);
3787
+ const existingKeys = getAllExistingKeys(projectRoot, allEnvs);
3788
+ let targetKey = key?.trim();
3789
+ if (!targetKey) {
3790
+ if (!isInteractive(flags)) {
3791
+ throw new Error("Variable key is required for 'mason env update <key> [value]'");
3792
+ }
3793
+ if (existingKeys.length === 0) {
3794
+ p19.log.error(pc14.red("No environment variables found to update."));
3795
+ return { key: "", environments: [] };
3796
+ }
3797
+ targetKey = handleCancel(
3798
+ await p19.select({
3799
+ message: "Select a variable to update:",
3800
+ options: existingKeys.map((k) => ({ value: k, label: k }))
3801
+ })
3802
+ );
3803
+ }
3804
+ const envsWithKey = allEnvs.filter((env) => {
3805
+ const localPath = path2.join(projectRoot, resolveEnvFilename(env, false));
3806
+ return fs4.existsSync(localPath) && new EnvEditor(localPath).has(targetKey);
3807
+ });
3808
+ let targetEnvs = [];
3809
+ if (flags.env) {
3810
+ targetEnvs = String(flags.env).split(",").map((s) => s.trim()).filter(Boolean);
3811
+ } else if (!isInteractive(flags)) {
3812
+ targetEnvs = envsWithKey.length > 0 ? envsWithKey : allEnvs;
3813
+ } else {
3814
+ targetEnvs = handleCancel(
3815
+ await p19.multiselect({
3816
+ message: `Which environments should be updated for ${pc14.bold(targetKey)}?`,
3817
+ options: allEnvs.map((env) => ({
3818
+ value: env,
3819
+ label: env === "local" ? "local (.env)" : `${env} (.env.${env})`,
3820
+ hint: envsWithKey.includes(env) ? "currently exists" : "not set"
3821
+ })),
3822
+ initialValues: envsWithKey.length > 0 ? envsWithKey : allEnvs,
3823
+ required: true
3824
+ })
3825
+ );
3826
+ }
3827
+ if (targetEnvs.length === 0) {
3828
+ p19.log.warn("No environments selected. Nothing was changed.");
3829
+ return { key: targetKey, environments: [] };
3830
+ }
3831
+ for (const env of targetEnvs) {
3832
+ const localPath = path2.join(projectRoot, resolveEnvFilename(env, false));
3833
+ const localEditor = new EnvEditor(localPath);
3834
+ const currentVal = localEditor.getValue(targetKey);
3835
+ let newVal = value;
3836
+ if (newVal === void 0) {
3837
+ if (!isInteractive(flags)) {
3838
+ newVal = currentVal ?? "";
3839
+ } else {
3840
+ newVal = handleCancel(
3841
+ await p19.text({
3842
+ message: `New value for ${pc14.cyan(targetKey)} in ${pc14.bold(env)}? (Current: "${currentVal ?? ""}")`,
3843
+ initialValue: currentVal ?? ""
3844
+ })
3845
+ );
3846
+ }
3847
+ } else if (isInteractive(flags) && !flags.yes) {
3848
+ newVal = handleCancel(
3849
+ await p19.text({
3850
+ message: `New value for ${pc14.cyan(targetKey)} in ${pc14.bold(env)}? (Current: "${currentVal ?? ""}")`,
3851
+ initialValue: value ?? currentVal ?? ""
3852
+ })
3853
+ );
3854
+ }
3855
+ if (flags.dryRun) {
3856
+ p19.log.info(
3857
+ pc14.cyan(`[DRY RUN] Would update ${targetKey}=${newVal} in ${resolveEnvFilename(env, false)}`)
3858
+ );
3859
+ } else {
3860
+ localEditor.set(targetKey, newVal);
3861
+ localEditor.save();
3862
+ const examplePath = path2.join(projectRoot, resolveEnvFilename(env, true));
3863
+ const exampleEditor = new EnvEditor(examplePath);
3864
+ if (!exampleEditor.has(targetKey)) {
3865
+ exampleEditor.set(targetKey, "");
3866
+ exampleEditor.save();
3867
+ }
3868
+ }
3869
+ }
3870
+ if (!flags.dryRun) {
3871
+ p19.log.success(
3872
+ pc14.green(`\u2713 Updated ${pc14.bold(targetKey)} in [${targetEnvs.join(", ")}].`)
3873
+ );
3874
+ }
3875
+ return { key: targetKey, environments: targetEnvs };
3876
+ }
3877
+ async function runEnvDelete(key, flags = {}) {
3878
+ const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
3879
+ const allEnvs = discoverEnvironments(projectRoot);
3880
+ const existingKeys = getAllExistingKeys(projectRoot, allEnvs);
3881
+ let targetKey = key?.trim();
3882
+ if (!targetKey) {
3883
+ if (!isInteractive(flags)) {
3884
+ throw new Error("Variable key is required for 'mason env delete <key>'");
3885
+ }
3886
+ if (existingKeys.length === 0) {
3887
+ p19.log.error(pc14.red("No environment variables found to delete."));
3888
+ return { key: "", environments: [] };
3889
+ }
3890
+ targetKey = handleCancel(
3891
+ await p19.select({
3892
+ message: "Select a variable to delete:",
3893
+ options: existingKeys.map((k) => ({ value: k, label: k }))
3894
+ })
3895
+ );
3896
+ }
3897
+ const envsWithKey = allEnvs.filter((env) => {
3898
+ const localPath = path2.join(projectRoot, resolveEnvFilename(env, false));
3899
+ const examplePath = path2.join(projectRoot, resolveEnvFilename(env, true));
3900
+ return fs4.existsSync(localPath) && new EnvEditor(localPath).has(targetKey) || fs4.existsSync(examplePath) && new EnvEditor(examplePath).has(targetKey);
3901
+ });
3902
+ let targetEnvs = [];
3903
+ if (flags.env) {
3904
+ targetEnvs = String(flags.env).split(",").map((s) => s.trim()).filter(Boolean);
3905
+ } else if (!isInteractive(flags)) {
3906
+ targetEnvs = envsWithKey.length > 0 ? envsWithKey : allEnvs;
3907
+ } else {
3908
+ targetEnvs = handleCancel(
3909
+ await p19.multiselect({
3910
+ message: `Which environments should remove ${pc14.bold(targetKey)}?`,
3911
+ options: allEnvs.map((env) => ({
3912
+ value: env,
3913
+ label: env === "local" ? "local (.env)" : `${env} (.env.${env})`,
3914
+ ...envsWithKey.includes(env) ? { hint: "currently present" } : {}
3915
+ })),
3916
+ initialValues: envsWithKey.length > 0 ? envsWithKey : allEnvs,
3917
+ required: true
3918
+ })
3919
+ );
3920
+ }
3921
+ if (targetEnvs.length === 0) {
3922
+ p19.log.warn("No environments selected. Nothing was deleted.");
3923
+ return { key: targetKey, environments: [] };
3924
+ }
3925
+ if (isInteractive(flags) && !flags.yes) {
3926
+ const confirm13 = handleCancel(
3927
+ await p19.confirm({
3928
+ message: `Are you sure you want to delete ${pc14.bold(targetKey)} from ${targetEnvs.join(", ")} and their .example templates?`,
3929
+ initialValue: false
3930
+ })
3931
+ );
3932
+ if (!confirm13) {
3933
+ p19.log.warn("Operation cancelled.");
3934
+ return { key: targetKey, environments: [] };
3935
+ }
3936
+ }
3937
+ for (const env of targetEnvs) {
3938
+ const localPath = path2.join(projectRoot, resolveEnvFilename(env, false));
3939
+ const examplePath = path2.join(projectRoot, resolveEnvFilename(env, true));
3940
+ if (flags.dryRun) {
3941
+ p19.log.info(
3942
+ pc14.cyan(`[DRY RUN] Would delete ${targetKey} from ${resolveEnvFilename(env, false)} and ${resolveEnvFilename(env, true)}`)
3943
+ );
3944
+ } else {
3945
+ if (fs4.existsSync(localPath)) {
3946
+ const localEditor = new EnvEditor(localPath);
3947
+ localEditor.delete(targetKey);
3948
+ localEditor.save();
3949
+ }
3950
+ if (fs4.existsSync(examplePath)) {
3951
+ const exampleEditor = new EnvEditor(examplePath);
3952
+ exampleEditor.delete(targetKey);
3953
+ exampleEditor.save();
3954
+ }
3955
+ }
3956
+ }
3957
+ if (!flags.dryRun) {
3958
+ p19.log.success(
3959
+ pc14.green(`\u2713 Deleted ${pc14.bold(targetKey)} from [${targetEnvs.join(", ")}] and .example templates.`)
3960
+ );
3961
+ }
3962
+ return { key: targetKey, environments: targetEnvs };
3963
+ }
3964
+ async function runEnvAudit(flags = {}) {
3965
+ const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
3966
+ const envs = discoverEnvironments(projectRoot);
3967
+ p19.intro(pc14.bgCyan(pc14.black(" mason env audit ")));
3968
+ const items = [];
3969
+ let totalIssues = 0;
3970
+ let hasDrift = false;
3971
+ let hasErrors = false;
3972
+ for (const env of envs) {
3973
+ const localFile = resolveEnvFilename(env, false);
3974
+ const exampleFile = resolveEnvFilename(env, true);
3975
+ const localPath = path2.join(projectRoot, localFile);
3976
+ const examplePath = path2.join(projectRoot, exampleFile);
3977
+ const localExists = fs4.existsSync(localPath);
3978
+ const exampleExists = fs4.existsSync(examplePath);
3979
+ const localEditor = new EnvEditor(localPath);
3980
+ const exampleEditor = new EnvEditor(examplePath);
3981
+ const localKeys = localExists ? localEditor.getKeys() : [];
3982
+ const exampleKeys = exampleExists ? exampleEditor.getKeys() : [];
3983
+ const missingInLocal = exampleKeys.filter((k) => !localKeys.includes(k));
3984
+ const missingInExample = localKeys.filter((k) => !exampleKeys.includes(k));
3985
+ const emptyLocals = localKeys.filter((k) => {
3986
+ const val = localEditor.getValue(k);
3987
+ return val === "" || val === null;
3988
+ });
3989
+ const envIssues = (!localExists ? 1 : 0) + (!exampleExists ? 1 : 0) + missingInLocal.length + missingInExample.length + emptyLocals.length;
3990
+ totalIssues += envIssues;
3991
+ if (missingInExample.length > 0 || !exampleExists) hasDrift = true;
3992
+ if (missingInLocal.length > 0 || !localExists) hasErrors = true;
3993
+ items.push({
3994
+ env,
3995
+ localFile,
3996
+ exampleFile,
3997
+ localExists,
3998
+ exampleExists,
3999
+ missingInLocal,
4000
+ missingInExample,
4001
+ emptyLocals
4002
+ });
4003
+ if (!localExists && !exampleExists) {
4004
+ p19.log.warn(
4005
+ pc14.yellow(`\u25B2 ${pc14.bold(env)}: Neither '${localFile}' nor '${exampleFile}' exist.`)
4006
+ );
4007
+ continue;
4008
+ }
4009
+ if (!localExists) {
4010
+ p19.log.error(
4011
+ pc14.red(`\u2717 ${pc14.bold(env)}: Local file '${localFile}' is missing! Run 'mason env init' to create it.`)
4012
+ );
4013
+ continue;
4014
+ }
4015
+ if (!exampleExists) {
4016
+ p19.log.warn(
4017
+ pc14.yellow(`\u25B2 ${pc14.bold(env)}: Template '${exampleFile}' is missing! Run 'mason env init-examples' to generate it.`)
4018
+ );
4019
+ continue;
4020
+ }
4021
+ if (envIssues === 0) {
4022
+ p19.log.success(
4023
+ pc14.green(`\u2713 ${pc14.bold(env)} (${localFile} \u2194 ${exampleFile}): All ${localKeys.length} keys in sync!`)
4024
+ );
4025
+ } else {
4026
+ p19.log.step(
4027
+ pc14.bold(pc14.cyan(`Environment: ${env} (${localFile} \u2194 ${exampleFile})`))
4028
+ );
4029
+ if (missingInLocal.length > 0) {
4030
+ p19.log.error(
4031
+ pc14.red(` \u25CF Missing locally (${missingInLocal.length} in template but missing in ${localFile}):`)
4032
+ );
4033
+ for (const k of missingInLocal) {
4034
+ p19.log.message(pc14.red(` - ${k}`));
4035
+ }
4036
+ }
4037
+ if (missingInExample.length > 0) {
4038
+ p19.log.warn(
4039
+ pc14.yellow(` \u25CF Template drift (${missingInExample.length} local keys missing in ${exampleFile}):`)
4040
+ );
4041
+ for (const k of missingInExample) {
4042
+ p19.log.message(pc14.yellow(` - ${k}`));
4043
+ }
4044
+ }
4045
+ if (emptyLocals.length > 0) {
4046
+ p19.log.info(
4047
+ pc14.dim(` \u25B2 Empty values (${emptyLocals.length} defined with blank value in ${localFile}):`)
4048
+ );
4049
+ for (const k of emptyLocals) {
4050
+ p19.log.message(pc14.dim(` - ${k}`));
4051
+ }
4052
+ }
4053
+ }
4054
+ }
4055
+ if (totalIssues === 0) {
4056
+ p19.outro(pc14.green("\u2713 All environment files are in sync! No drift detected."));
4057
+ } else {
4058
+ p19.outro(
4059
+ pc14.yellow(`Found ${totalIssues} environment issue(s) across inspected environments.`)
4060
+ );
4061
+ }
4062
+ return {
4063
+ hasDrift,
4064
+ hasErrors,
4065
+ totalIssues,
4066
+ items
4067
+ };
4068
+ }
4069
+
4070
+ // src/commands/env/index.ts
4071
+ async function runEnvMenu(flags = {}) {
4072
+ const task = handleCancel(
4073
+ await p19.select({
4074
+ message: "What environment task do you want to run?",
4075
+ options: [
4076
+ {
4077
+ value: "add",
4078
+ label: "Add Variable",
4079
+ hint: "Add a new key across selected environments and sync .example templates"
4080
+ },
4081
+ {
4082
+ value: "update",
4083
+ label: "Update Variable",
4084
+ hint: "Update an existing variable value across environments"
4085
+ },
4086
+ {
4087
+ value: "delete",
4088
+ label: "Delete Variable",
4089
+ hint: "Delete a variable from .env and .example files"
4090
+ },
4091
+ {
4092
+ value: "audit",
4093
+ label: "Audit Drift",
4094
+ hint: "Scan for missing keys, uncommitted secrets, or template drift"
4095
+ },
4096
+ {
4097
+ value: "init",
4098
+ label: "Init Files (.env)",
4099
+ hint: "Clone .env*.example templates to local .env files"
4100
+ },
4101
+ {
4102
+ value: "init-examples",
4103
+ label: "Init Templates (.env.example)",
4104
+ hint: "Safely clone and redact local .env files to .example templates"
4105
+ }
4106
+ ]
4107
+ })
4108
+ );
4109
+ switch (task) {
4110
+ case "add":
4111
+ return await runEnvAdd(void 0, void 0, flags);
4112
+ case "update":
4113
+ return await runEnvUpdate(void 0, void 0, flags);
4114
+ case "delete":
4115
+ return await runEnvDelete(void 0, flags);
4116
+ case "audit":
4117
+ return await runEnvAudit(flags);
4118
+ case "init":
4119
+ return await runEnvInit(flags);
4120
+ case "init-examples":
4121
+ return await runEnvInitExamples(flags);
4122
+ }
4123
+ }
4124
+ async function handleEnv(subcommand, args = [], flags = {}) {
4125
+ if (!subcommand) {
4126
+ return await runEnvMenu(flags);
4127
+ }
4128
+ const normalized = subcommand.trim().toLowerCase();
4129
+ switch (normalized) {
4130
+ case "init":
4131
+ return await runEnvInit(flags);
4132
+ case "init-examples":
4133
+ case "initexamples":
4134
+ return await runEnvInitExamples(flags);
4135
+ case "add":
4136
+ return await runEnvAdd(args[0], args[1], flags);
4137
+ case "update":
4138
+ return await runEnvUpdate(args[0], args[1], flags);
4139
+ case "delete":
4140
+ case "remove":
4141
+ case "rm":
4142
+ return await runEnvDelete(args[0], flags);
4143
+ case "audit":
4144
+ return await runEnvAudit(flags);
4145
+ default:
4146
+ p19.log.error(
4147
+ pc14.red(
4148
+ `Unknown env subcommand: "${subcommand}". Allowed subcommands: init, init-examples, add, update, delete, audit.`
4149
+ )
4150
+ );
4151
+ if (process.env.VITEST) {
4152
+ throw new Error(
4153
+ `Unknown env subcommand: "${subcommand}". Allowed subcommands: init, init-examples, add, update, delete, audit.`
4154
+ );
4155
+ }
4156
+ process.exit(1);
4157
+ }
4158
+ }
3515
4159
 
3516
4160
  // src/commands/menu.ts
3517
4161
  async function runInteractiveMenu(flags = {}) {
3518
4162
  printBanner();
3519
4163
  const choice = handleCancel(
3520
- await p15.select({
4164
+ await p19.select({
3521
4165
  message: "What would you like to create in your architecture?",
3522
4166
  options: [
3523
4167
  {
@@ -3562,8 +4206,8 @@ async function runInteractiveMenu(flags = {}) {
3562
4206
  },
3563
4207
  {
3564
4208
  value: "env",
3565
- label: "Generate Env Files",
3566
- hint: "Clone .env*.example files to local .env equivalents"
4209
+ label: "Environment Variables",
4210
+ hint: "Manage .env files, add/delete keys, and sync templates"
3567
4211
  }
3568
4212
  ]
3569
4213
  })
@@ -3594,7 +4238,7 @@ async function runInteractiveMenu(flags = {}) {
3594
4238
  await runInjectableCommand(void 0, flags);
3595
4239
  break;
3596
4240
  case "env":
3597
- await runEnvCommand(flags);
4241
+ await runEnvMenu(flags);
3598
4242
  break;
3599
4243
  }
3600
4244
  }
@@ -3633,8 +4277,8 @@ async function handleCreate(type, name, flags = {}) {
3633
4277
  await runInjectableCommand(name, flags);
3634
4278
  break;
3635
4279
  default:
3636
- p15.log.error(
3637
- pc15.red(
4280
+ p19.log.error(
4281
+ pc14.red(
3638
4282
  `Unknown component type: "${type}". Allowed types: feature, shared, usecase, domain, infrastructure, interface, httpHandler, injectable.`
3639
4283
  )
3640
4284
  );
@@ -3642,9 +4286,9 @@ async function handleCreate(type, name, flags = {}) {
3642
4286
  }
3643
4287
  }
3644
4288
  var importPrompts = {
3645
- select: p15.select,
3646
- text: p15.text,
3647
- confirm: p15.confirm
4289
+ select: p19.select,
4290
+ text: p19.text,
4291
+ confirm: p19.confirm
3648
4292
  };
3649
4293
  function isTestFile(filePath) {
3650
4294
  const normalized = filePath.replace(/\\/g, "/");
@@ -3754,19 +4398,19 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3754
4398
  );
3755
4399
  }
3756
4400
  if (type !== "shared" && type !== "feature") {
3757
- p15.log.error(pc15.red(`Invalid module type: "${typeArg}". Must be "shared" or "feature".`));
4401
+ p19.log.error(pc14.red(`Invalid module type: "${typeArg}". Must be "shared" or "feature".`));
3758
4402
  process.exit(1);
3759
4403
  }
3760
4404
  const registryBase = getRegistryBase(projectRoot, flags.registry);
3761
- const s = p15.spinner();
3762
- s.start(`Connecting to registry at ${pc15.dim(registryBase)}...`);
4405
+ const s = p19.spinner();
4406
+ s.start(`Connecting to registry at ${pc14.dim(registryBase)}...`);
3763
4407
  let index;
3764
4408
  try {
3765
4409
  index = await fetchRegistryIndex(registryBase);
3766
4410
  s.stop("Registry index loaded.");
3767
4411
  } catch (err) {
3768
- s.stop(pc15.red("Failed to load registry index."));
3769
- p15.log.error(pc15.red(err.message || String(err)));
4412
+ s.stop(pc14.red("Failed to load registry index."));
4413
+ p19.log.error(pc14.red(err.message || String(err)));
3770
4414
  process.exit(1);
3771
4415
  }
3772
4416
  const availableList = type === "shared" ? index.shared : index.features;
@@ -3774,7 +4418,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3774
4418
  let name = nameArg?.trim();
3775
4419
  if (!name) {
3776
4420
  if (availableList.length === 0) {
3777
- p15.log.warn(`No ${type} modules available in the registry.`);
4421
+ p19.log.warn(`No ${type} modules available in the registry.`);
3778
4422
  return;
3779
4423
  }
3780
4424
  name = handleCancel(
@@ -3790,9 +4434,9 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3790
4434
  }
3791
4435
  if (!candidateNames.includes(name)) {
3792
4436
  const closest = findClosestMatch(name, candidateNames);
3793
- p15.log.error(pc15.red(`\u274C Module '${name}' not found in the ${type} registry.`));
4437
+ p19.log.error(pc14.red(`\u274C Module '${name}' not found in the ${type} registry.`));
3794
4438
  if (closest) {
3795
- p15.log.info(pc15.yellow(`Did you mean '${pc15.cyan(closest)}'?`));
4439
+ p19.log.info(pc14.yellow(`Did you mean '${pc14.cyan(closest)}'?`));
3796
4440
  }
3797
4441
  process.exit(1);
3798
4442
  }
@@ -3804,7 +4448,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3804
4448
  try {
3805
4449
  manifest = await fetchModuleManifest(registryBase, modType, modName);
3806
4450
  } catch (err) {
3807
- p15.log.error(pc15.red(`Failed to fetch manifest for ${modType} '${modName}': ${err.message}`));
4451
+ p19.log.error(pc14.red(`Failed to fetch manifest for ${modType} '${modName}': ${err.message}`));
3808
4452
  process.exit(1);
3809
4453
  }
3810
4454
  nodes.set(key, {
@@ -3844,7 +4488,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3844
4488
  buildTopologicalOrder(rootKey);
3845
4489
  const resolutions = /* @__PURE__ */ new Map();
3846
4490
  const globalAliasMap = /* @__PURE__ */ new Map();
3847
- p15.log.step(`Importing ${type}: ${pc15.cyan(name)}...`);
4491
+ p19.log.step(`Importing ${type}: ${pc14.cyan(name)}...`);
3848
4492
  for (const key of orderedKeys) {
3849
4493
  const node = nodes.get(key);
3850
4494
  const { type: modType, name: registryName, manifest } = node;
@@ -3876,8 +4520,8 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3876
4520
  const programmaticPointTo = pointToMap[registryName] || pointToMap[`${modType}/${registryName}`];
3877
4521
  if (programmaticPointTo) {
3878
4522
  if (!checkModuleExists(projectRoot, modType, programmaticPointTo)) {
3879
- p15.log.error(
3880
- pc15.red(
4523
+ p19.log.error(
4524
+ pc14.red(
3881
4525
  `Module '${programmaticPointTo}' specified in --point-to does not exist in ${folder}.`
3882
4526
  )
3883
4527
  );
@@ -3885,7 +4529,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3885
4529
  }
3886
4530
  globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${programmaticPointTo}/`);
3887
4531
  globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${programmaticPointTo}`);
3888
- p15.log.step(`Pointing '${modType}/${registryName}' to existing '${folder}/${programmaticPointTo}'`);
4532
+ p19.log.step(`Pointing '${modType}/${registryName}' to existing '${folder}/${programmaticPointTo}'`);
3889
4533
  resolutions.set(key, {
3890
4534
  type: modType,
3891
4535
  registryName,
@@ -3901,7 +4545,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3901
4545
  globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${programmaticAlias}`);
3902
4546
  const baseDir = getModuleBaseDir(projectRoot, modType);
3903
4547
  const displayDest = path2.relative(projectRoot, path2.join(baseDir, programmaticAlias));
3904
- p15.log.step(`Importing '${registryName}' into '${displayDest}'...`);
4548
+ p19.log.step(`Importing '${registryName}' into '${displayDest}'...`);
3905
4549
  resolutions.set(key, {
3906
4550
  type: modType,
3907
4551
  registryName,
@@ -3913,7 +4557,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3913
4557
  }
3914
4558
  const existingDir = getExistingModuleDir(projectRoot, modType, registryName);
3915
4559
  if (!existingDir) {
3916
- p15.log.info(` \u21B3 Resolving required ${modType} dependency: ${pc15.cyan(registryName)}`);
4560
+ p19.log.info(` \u21B3 Resolving required ${modType} dependency: ${pc14.cyan(registryName)}`);
3917
4561
  resolutions.set(key, {
3918
4562
  type: modType,
3919
4563
  registryName,
@@ -3929,7 +4573,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3929
4573
  const remoteIntegrity = manifest.integrity || entry?.integrity;
3930
4574
  const localHash = computeDirectoryHash(existingDir);
3931
4575
  if (remoteIntegrity && localHash === remoteIntegrity) {
3932
- p15.log.step(`\u2713 ${modType}/${registryName} is up to date (reusing existing)`);
4576
+ p19.log.step(`\u2713 ${modType}/${registryName} is up to date (reusing existing)`);
3933
4577
  resolutions.set(key, {
3934
4578
  type: modType,
3935
4579
  registryName,
@@ -3940,7 +4584,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3940
4584
  continue;
3941
4585
  }
3942
4586
  if (flags.overwrite) {
3943
- p15.log.step(`Overwriting '${modType}/${registryName}' with registry version (--overwrite)...`);
4587
+ p19.log.step(`Overwriting '${modType}/${registryName}' with registry version (--overwrite)...`);
3944
4588
  resolutions.set(key, {
3945
4589
  type: modType,
3946
4590
  registryName,
@@ -3951,7 +4595,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3951
4595
  continue;
3952
4596
  }
3953
4597
  if (flags.yes) {
3954
- p15.log.info(`Using existing '${modType}/${registryName}'`);
4598
+ p19.log.info(`Using existing '${modType}/${registryName}'`);
3955
4599
  resolutions.set(key, {
3956
4600
  type: modType,
3957
4601
  registryName,
@@ -3985,7 +4629,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3985
4629
  })
3986
4630
  );
3987
4631
  if (choice === "keep_existing" || choice === "use_existing") {
3988
- p15.log.step(`Skipping '${modType}/${registryName}' (keeping existing)`);
4632
+ p19.log.step(`Skipping '${modType}/${registryName}' (keeping existing)`);
3989
4633
  resolutions.set(key, {
3990
4634
  type: modType,
3991
4635
  registryName,
@@ -4057,7 +4701,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
4057
4701
  chosenName = chosenName.replace(/^@\/(shared|features)\//, "").replace(/^(src\/)?(shared|features)\//, "").replace(/^\/+|\/+$/g, "").trim();
4058
4702
  globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${chosenName}/`);
4059
4703
  globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${chosenName}`);
4060
- p15.log.step(`Pointing '${modType}/${registryName}' to existing '${folder}/${chosenName}'`);
4704
+ p19.log.step(`Pointing '${modType}/${registryName}' to existing '${folder}/${chosenName}'`);
4061
4705
  resolutions.set(key, {
4062
4706
  type: modType,
4063
4707
  registryName,
@@ -4089,7 +4733,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
4089
4733
  globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${newName}`);
4090
4734
  const baseDir = getModuleBaseDir(projectRoot, modType);
4091
4735
  const displayDest = path2.relative(projectRoot, path2.join(baseDir, newName));
4092
- p15.log.step(`Importing '${registryName}' into '${displayDest}'...`);
4736
+ p19.log.step(`Importing '${registryName}' into '${displayDest}'...`);
4093
4737
  resolutions.set(key, {
4094
4738
  type: modType,
4095
4739
  registryName,
@@ -4100,7 +4744,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
4100
4744
  continue;
4101
4745
  }
4102
4746
  if (choice === "overwrite") {
4103
- p15.log.step(`Overwriting '${modType}/${registryName}' with registry version...`);
4747
+ p19.log.step(`Overwriting '${modType}/${registryName}' with registry version...`);
4104
4748
  resolutions.set(key, {
4105
4749
  type: modType,
4106
4750
  registryName,
@@ -4143,8 +4787,8 @@ async function handleImport(typeArg, nameArg, flags = {}) {
4143
4787
  const targetDir = path2.join(getModuleBaseDir(projectRoot, res.type), res.targetName);
4144
4788
  const isOverwrite = Boolean(flags.overwrite || res.action === "overwrite");
4145
4789
  if (key !== rootKey) {
4146
- p15.log.step(
4147
- `Importing ${res.type}: ${pc15.cyan(res.targetName)}${res.targetName !== res.registryName ? pc15.dim(` (from ${res.registryName})`) : ""}...`
4790
+ p19.log.step(
4791
+ `Importing ${res.type}: ${pc14.cyan(res.targetName)}${res.targetName !== res.registryName ? pc14.dim(` (from ${res.registryName})`) : ""}...`
4148
4792
  );
4149
4793
  }
4150
4794
  if (res.action === "overwrite" && !flags.dryRun) {
@@ -4162,8 +4806,8 @@ async function handleImport(typeArg, nameArg, flags = {}) {
4162
4806
  const remoteFilePath = `${folder}/${res.registryName}/${file}`;
4163
4807
  const localFilePath = path2.join(targetDir, file);
4164
4808
  if (fs4.existsSync(localFilePath) && !isOverwrite) {
4165
- p15.log.warn(
4166
- ` \u21B3 Skipping existing file: ${pc15.dim(path2.relative(projectRoot, localFilePath))} (use --overwrite to replace)`
4809
+ p19.log.warn(
4810
+ ` \u21B3 Skipping existing file: ${pc14.dim(path2.relative(projectRoot, localFilePath))} (use --overwrite to replace)`
4167
4811
  );
4168
4812
  continue;
4169
4813
  }
@@ -4175,18 +4819,18 @@ async function handleImport(typeArg, nameArg, flags = {}) {
4175
4819
  }
4176
4820
  const displayPath = path2.relative(projectRoot, localFilePath);
4177
4821
  totalWrittenFiles.push(displayPath);
4178
- p15.log.message(` ${pc15.green("+")} ${displayPath}`);
4822
+ p19.log.message(` ${pc14.green("+")} ${displayPath}`);
4179
4823
  }
4180
4824
  }
4181
4825
  const missingNpm = getMissingNpmPackages(projectRoot, Array.from(requiredNpmPackages));
4182
4826
  if (missingNpm.length > 0) {
4183
4827
  if (flags.skipInstall || flags.dryRun) {
4184
- p15.log.info(`Required npm dependencies to install: ${pc15.cyan(missingNpm.join(", "))}`);
4828
+ p19.log.info(`Required npm dependencies to install: ${pc14.cyan(missingNpm.join(", "))}`);
4185
4829
  } else {
4186
4830
  try {
4187
4831
  installNpmPackages(projectRoot, missingNpm);
4188
4832
  } catch (err) {
4189
- p15.log.warn(
4833
+ p19.log.warn(
4190
4834
  `Could not automatically install packages (${err.message}). Please run: npm install ${missingNpm.join(
4191
4835
  " "
4192
4836
  )}`
@@ -4195,13 +4839,13 @@ async function handleImport(typeArg, nameArg, flags = {}) {
4195
4839
  }
4196
4840
  }
4197
4841
  if (flags.dryRun) {
4198
- p15.log.info(pc15.yellow("Dry run complete: No files were written to disk."));
4842
+ p19.log.info(pc14.yellow("Dry run complete: No files were written to disk."));
4199
4843
  } else {
4200
4844
  const rootRes = resolutions.get(rootKey);
4201
4845
  const baseDir = getModuleBaseDir(projectRoot, type);
4202
4846
  const displayTarget = path2.relative(projectRoot, path2.join(baseDir, rootRes.targetName));
4203
- p15.log.success(
4204
- pc15.green(
4847
+ p19.log.success(
4848
+ pc14.green(
4205
4849
  `Successfully imported ${type} '${name}' (${totalWrittenFiles.length} file${totalWrittenFiles.length === 1 ? "" : "s"} placed in ${displayTarget})`
4206
4850
  )
4207
4851
  );
@@ -4424,10 +5068,10 @@ var FeatureDependencyGraph = class {
4424
5068
  if (cycle) return cycle;
4425
5069
  } else if (inStack.has(next)) {
4426
5070
  const cycle = [next];
4427
- let p18 = curr;
4428
- while (p18 !== next) {
4429
- cycle.push(p18);
4430
- p18 = parent.get(p18) || next;
5071
+ let p22 = curr;
5072
+ while (p22 !== next) {
5073
+ cycle.push(p22);
5074
+ p22 = parent.get(p22) || next;
4431
5075
  }
4432
5076
  cycle.push(next);
4433
5077
  cycle.reverse();
@@ -5329,15 +5973,15 @@ ${compiledDetails}
5329
5973
  // src/commands/lint.ts
5330
5974
  function formatViolation(v) {
5331
5975
  const lines = [];
5332
- lines.push(pc15.underline(pc15.cyan(v.relativePath)) + pc15.dim(":") + pc15.yellow(String(v.line)));
5976
+ lines.push(pc14.underline(pc14.cyan(v.relativePath)) + pc14.dim(":") + pc14.yellow(String(v.line)));
5333
5977
  const gutter = `${String(v.line).padStart(4, " ")} | `;
5334
- lines.push(pc15.dim(gutter) + v.code);
5978
+ lines.push(pc14.dim(gutter) + v.code);
5335
5979
  const categoryShort = v.category.replace(" Violation", "");
5336
5980
  lines.push(
5337
- " " + pc15.red(pc15.bold("\u2716")) + " " + pc15.red(`[${categoryShort}]`) + " " + v.advice
5981
+ " " + pc14.red(pc14.bold("\u2716")) + " " + pc14.red(`[${categoryShort}]`) + " " + v.advice
5338
5982
  );
5339
5983
  if (v.fixExample?.good) {
5340
- lines.push(" " + pc15.dim("\u2192") + " " + pc15.dim(v.fixExample.good));
5984
+ lines.push(" " + pc14.dim("\u2192") + " " + pc14.dim(v.fixExample.good));
5341
5985
  }
5342
5986
  return lines.join("\n");
5343
5987
  }
@@ -5362,7 +6006,7 @@ async function handleLint(flags = {}) {
5362
6006
  console.log("");
5363
6007
  }
5364
6008
  console.log(
5365
- pc15.red(
6009
+ pc14.red(
5366
6010
  `\u2716 Dependency rules check failed with ${summary.totalViolations} error${summary.totalViolations > 1 ? "s" : ""} across ${summary.violatedFeatures} feature${summary.violatedFeatures > 1 ? "s" : ""} (${summary.totalFeatures} features scanned).`
5367
6011
  )
5368
6012
  );
@@ -5381,7 +6025,7 @@ ${compiledDetails}
5381
6025
  }
5382
6026
  if (!quiet) {
5383
6027
  console.log(
5384
- pc15.green(
6028
+ pc14.green(
5385
6029
  `\u2714 Dependency rules verified across ${summary.totalFeatures} feature${summary.totalFeatures === 1 ? "" : "s"}.`
5386
6030
  )
5387
6031
  );
@@ -5396,7 +6040,7 @@ ${compiledDetails}
5396
6040
  console.log(formatViolation(v));
5397
6041
  console.log("");
5398
6042
  }
5399
- console.log(pc15.red(`\u2716 Dependency rules check failed (1 error).`));
6043
+ console.log(pc14.red(`\u2716 Dependency rules check failed (1 error).`));
5400
6044
  console.log("");
5401
6045
  }
5402
6046
  }
@@ -5406,33 +6050,42 @@ ${compiledDetails}
5406
6050
  async function handleInit(type, flags = {}) {
5407
6051
  if (!type) {
5408
6052
  const choice = handleCancel(
5409
- await p15.select({
6053
+ await p19.select({
5410
6054
  message: "What would you like to initialize?",
5411
6055
  options: [
5412
6056
  {
5413
6057
  value: "env",
5414
6058
  label: "Environment Files",
5415
6059
  hint: "Generate local .env files from .env*.example templates"
6060
+ },
6061
+ {
6062
+ value: "env-examples",
6063
+ label: "Sync Env Examples",
6064
+ hint: "Safely clone and redact local .env files to .example templates"
5416
6065
  }
5417
6066
  ]
5418
6067
  })
5419
6068
  );
5420
6069
  if (choice === "env") {
5421
- await runEnvCommand(flags);
6070
+ await runEnvInit(flags);
6071
+ } else if (choice === "env-examples") {
6072
+ await runEnvInitExamples(flags);
5422
6073
  }
5423
6074
  return;
5424
6075
  }
5425
6076
  const normalized = type.trim().toLowerCase();
5426
6077
  if (normalized === "env") {
5427
- await runEnvCommand(flags);
6078
+ await runEnvInit(flags);
6079
+ } else if (normalized === "env-examples" || normalized === "envexamples") {
6080
+ await runEnvInitExamples(flags);
5428
6081
  } else {
5429
- p15.log.error(
5430
- pc15.red(
5431
- `Unknown init target: "${type}". Allowed targets: env.`
6082
+ p19.log.error(
6083
+ pc14.red(
6084
+ `Unknown init target: "${type}". Allowed targets: env, env-examples.`
5432
6085
  )
5433
6086
  );
5434
6087
  if (process.env.VITEST) {
5435
- throw new Error(`Unknown init target: "${type}". Allowed targets: env.`);
6088
+ throw new Error(`Unknown init target: "${type}". Allowed targets: env, env-examples.`);
5436
6089
  }
5437
6090
  process.exit(1);
5438
6091
  }
@@ -5443,8 +6096,8 @@ function createCli() {
5443
6096
  const cli = cac("mason");
5444
6097
  cli.command("[...args]", "Interactive scaffolding and import wizard").option("-d, --dry-run", "Preview changes without writing to disk").option("-o, --overwrite", "Overwrite existing files").option("-y, --yes", "Accept defaults and skip interactive prompts").option("--registry", "Run in registry mode").option("--point-to <mapping>", "Point a dependency to an existing local module (e.g. --point-to time:mytime)").option("--alias <mapping>", "Import a dependency fresh under an alias name (e.g. --alias time:time-mason)").option("--tests", "Include test files (use --no-tests to exclude)").action(async (args, flags) => {
5445
6098
  if (args && args.length > 0) {
5446
- p15.log.error(
5447
- pc15.red(
6099
+ p19.log.error(
6100
+ pc14.red(
5448
6101
  `Unknown command: "${args.join(" ")}". Use --help to list available commands.`
5449
6102
  )
5450
6103
  );
@@ -5455,7 +6108,7 @@ function createCli() {
5455
6108
  const mode = getEffectiveMode(projectRoot, flags);
5456
6109
  if (mode === "registry") {
5457
6110
  const choice2 = handleCancel(
5458
- await p15.select({
6111
+ await p19.select({
5459
6112
  message: "\u{1F3DB}\uFE0F Mason (Registry Mode): What would you like to scaffold?",
5460
6113
  options: [
5461
6114
  {
@@ -5498,7 +6151,7 @@ function createCli() {
5498
6151
  return;
5499
6152
  }
5500
6153
  const choice = handleCancel(
5501
- await p15.select({
6154
+ await p19.select({
5502
6155
  message: "\u{1F9F1} Mason: What would you like to do?",
5503
6156
  options: [
5504
6157
  {
@@ -5517,9 +6170,9 @@ function createCli() {
5517
6170
  hint: "Scan codebase and verify Clean Architecture dependency rules"
5518
6171
  },
5519
6172
  {
5520
- value: "init",
5521
- label: "Initialize environment",
5522
- hint: "Generate local .env files from .env*.example templates"
6173
+ value: "env",
6174
+ label: "Environment variables",
6175
+ hint: "Manage .env files, add/delete keys, and sync templates"
5523
6176
  }
5524
6177
  ]
5525
6178
  })
@@ -5530,8 +6183,8 @@ function createCli() {
5530
6183
  await handleImport(void 0, void 0, flags);
5531
6184
  } else if (choice === "lint") {
5532
6185
  await handleLint(flags);
5533
- } else if (choice === "init") {
5534
- await handleInit("env", flags);
6186
+ } else if (choice === "env") {
6187
+ await handleEnv(void 0, [], flags);
5535
6188
  }
5536
6189
  } catch (err) {
5537
6190
  handleCliError(err);
@@ -5579,16 +6232,36 @@ function createCli() {
5579
6232
  handleCliError(err);
5580
6233
  }
5581
6234
  });
6235
+ cli.command(
6236
+ "env [subcommand] [...args]",
6237
+ "Environment variable management and template synchronization (init, init-examples, add, update, delete, audit)"
6238
+ ).option("-d, --dry-run", "Preview changes without writing to disk").option("-o, --overwrite", "Overwrite existing files").option("-e, --env <environments>", "Comma-separated target environments (e.g. dev,prod)").example("mason env").example("mason env init").example("mason env init-examples").example("mason env add API_KEY secret123").example("mason env update API_KEY newsecret").example("mason env delete API_KEY").example("mason env audit").action(async (subcommand, args = [], flags = {}) => {
6239
+ try {
6240
+ await handleEnv(subcommand, args, flags);
6241
+ } catch (err) {
6242
+ handleCliError(err);
6243
+ }
6244
+ });
5582
6245
  cli.command(
5583
6246
  "init [type]",
5584
- "Initialize project configurations and resources (e.g. 'mason init env')"
5585
- ).option("-d, --dry-run", "Preview changes without writing to disk").option("-o, --overwrite", "Overwrite existing .env files").example("mason init env").example("mason init env --overwrite").example("mason init env --dry-run").action(async (type, flags = {}) => {
6247
+ "Initialize project configurations (legacy shorthand: use 'mason env init')"
6248
+ ).option("-d, --dry-run", "Preview changes without writing to disk").option("-o, --overwrite", "Overwrite existing files").example("mason init env").example("mason init env-examples").action(async (type, flags = {}) => {
5586
6249
  try {
5587
6250
  await handleInit(type, flags);
5588
6251
  } catch (err) {
5589
6252
  handleCliError(err);
5590
6253
  }
5591
6254
  });
6255
+ cli.command(
6256
+ "env-examples",
6257
+ "Shorthand for 'mason env init-examples'"
6258
+ ).option("-d, --dry-run", "Preview changes without writing to disk").option("-o, --overwrite", "Overwrite existing .env*.example files").action(async (flags = {}) => {
6259
+ try {
6260
+ await handleEnv("init-examples", [], flags);
6261
+ } catch (err) {
6262
+ handleCliError(err);
6263
+ }
6264
+ });
5592
6265
  const createSubtypes = [
5593
6266
  "feature",
5594
6267
  "shared",
@@ -5620,7 +6293,7 @@ function handleCliError(err) {
5620
6293
  process.exit(0);
5621
6294
  }
5622
6295
  const message = err instanceof Error ? err.message : String(err);
5623
- p15.log.error(pc15.red(`Error: ${message}`));
6296
+ p19.log.error(pc14.red(`Error: ${message}`));
5624
6297
  if (process.env.VITEST) {
5625
6298
  throw err;
5626
6299
  }
@@ -5631,6 +6304,6 @@ async function runCli(argv = process.argv) {
5631
6304
  cli.parse(argv, { run: true });
5632
6305
  }
5633
6306
 
5634
- export { DEFAULT_REGISTRY_BASE, DependencyRuleError, FeatureDependencyGraph, PromptCancelledError, computeDirectoryHash, concreteInfraTemplate, createCli, detectPackageManager, diProviderTemplate, domainClassTemplate, domainInterfaceTemplate, domainValueObjectTemplate, ensureInterfacePrefix, expressIndexTemplate, fetchModuleManifest, fetchRegistryFile, fetchRegistryIndex, findClosestMatch, findInterfaceFile, findLocalRegistry, findProjectRoot, generateConcreteInfraCode, generateDomain, generateDomainEntityCode, generateFeature, generateHttpHandler, generateHttpHandlerCode, generateInfrastructure, generateInjectable, generateInjectableCode, generateInterface, generateInterfaceCode, generateShared, generateStubCode, generateUseCase, generateUseCaseCode, generateUseCaseTestCode, getAllFiles, getConfiguredFeaturesDir, getConfiguredSharedDir, getEffectiveMode, getMissingNpmPackages, getRegistryBase, getTemplatesDir, handleCancel, handleCreate, handleImport, handleInit, handleLint, hbs, httpHandlerTemplate, injectableInterfaceTemplate, injectableTemplate, installNpmPackages, interfaceTemplate, isValidIdentifier, levenshteinDistance, lintCodebase, listDomainEntities, listDomainInterfaces, listExistingFeatures, listExistingShared, listUseCases, loadInterfaceHierarchy, loadMasonConfig, loadTemplate, normalizeRegistryUrl, parseInterfaceMethodsAndImports, printBanner, printGeneratedFiles, promptProperties, removeInterfacePrefix, renderTemplate, renderTemplateFile, resolveFeaturesDir, resolveSharedDir, runCli, runEnvCommand, runSharedCommand, stubInfraTemplate, synthesizeCrudMethods, toCamelCase, toKebabCase, toPascalCase, toPlural, toSingular, toStubName, updateOrGenerateDiProvider, useCaseTemplate, useCaseTestTemplate, writeFileSafely };
6307
+ export { DEFAULT_REGISTRY_BASE, DependencyRuleError, EnvEditor, FeatureDependencyGraph, PromptCancelledError, computeDirectoryHash, concreteInfraTemplate, createCli, detectPackageManager, diProviderTemplate, discoverEnvironments, domainClassTemplate, domainInterfaceTemplate, domainValueObjectTemplate, ensureInterfacePrefix, expressIndexTemplate, fetchModuleManifest, fetchRegistryFile, fetchRegistryIndex, findClosestMatch, findInterfaceFile, findLocalRegistry, findProjectRoot, generateConcreteInfraCode, generateDomain, generateDomainEntityCode, generateFeature, generateHttpHandler, generateHttpHandlerCode, generateInfrastructure, generateInjectable, generateInjectableCode, generateInterface, generateInterfaceCode, generateShared, generateStubCode, generateUseCase, generateUseCaseCode, generateUseCaseTestCode, getAllFiles, getConfiguredFeaturesDir, getConfiguredSharedDir, getEffectiveMode, getMissingNpmPackages, getRegistryBase, getTemplatesDir, handleCancel, handleCreate, handleEnv, handleImport, handleInit, handleLint, hbs, httpHandlerTemplate, injectableInterfaceTemplate, injectableTemplate, installNpmPackages, interfaceTemplate, isValidIdentifier, levenshteinDistance, lintCodebase, listDomainEntities, listDomainInterfaces, listExistingFeatures, listExistingShared, listUseCases, loadInterfaceHierarchy, loadMasonConfig, loadTemplate, normalizeRegistryUrl, parseInterfaceMethodsAndImports, printBanner, printGeneratedFiles, promptProperties, removeInterfacePrefix, renderTemplate, renderTemplateFile, resolveEnvFilename, resolveFeaturesDir, resolveSharedDir, runCli, runEnvAdd, runEnvAudit, runEnvCommand, runEnvDelete, runEnvExamplesCommand, runEnvInit, runEnvInitExamples, runEnvMenu, runEnvUpdate, runSharedCommand, stubInfraTemplate, synthesizeCrudMethods, toCamelCase, toKebabCase, toPascalCase, toPlural, toSingular, toStubName, updateOrGenerateDiProvider, useCaseTemplate, useCaseTestTemplate, writeFileSafely };
5635
6308
  //# sourceMappingURL=index.js.map
5636
6309
  //# sourceMappingURL=index.js.map