@solid-stack/mason 1.0.24 → 1.0.26

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/bin.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { cac } from 'cac';
3
- import * as p14 from '@clack/prompts';
4
- import pc14 from 'picocolors';
5
- import fs9 from 'fs';
3
+ import * as p16 from '@clack/prompts';
4
+ import pc16 from 'picocolors';
5
+ import fs11 from 'fs';
6
6
  import path2 from 'path';
7
7
  import { fileURLToPath } from 'url';
8
8
  import Handlebars from 'handlebars';
@@ -12,7 +12,7 @@ import { createHash } from 'crypto';
12
12
  function writeFileSafely(absolutePath, content, options = {}) {
13
13
  const projectRoot = options.projectRoot || process.cwd();
14
14
  const relPath = path2.relative(projectRoot, absolutePath).replace(/\\/g, "/");
15
- const existedBefore = fs9.existsSync(absolutePath);
15
+ const existedBefore = fs11.existsSync(absolutePath);
16
16
  if (options.dryRun) {
17
17
  return {
18
18
  relativePath: relPath,
@@ -32,10 +32,10 @@ function writeFileSafely(absolutePath, content, options = {}) {
32
32
  };
33
33
  }
34
34
  const dir = path2.dirname(absolutePath);
35
- if (!fs9.existsSync(dir)) {
36
- fs9.mkdirSync(dir, { recursive: true });
35
+ if (!fs11.existsSync(dir)) {
36
+ fs11.mkdirSync(dir, { recursive: true });
37
37
  }
38
- fs9.writeFileSync(absolutePath, content, "utf-8");
38
+ fs11.writeFileSync(absolutePath, content, "utf-8");
39
39
  return {
40
40
  relativePath: relPath,
41
41
  absolutePath,
@@ -47,17 +47,17 @@ function writeFileSafely(absolutePath, content, options = {}) {
47
47
  function printGeneratedFiles(files, isDryRun = false) {
48
48
  console.log("");
49
49
  if (isDryRun) {
50
- p14.log.warn(pc14.yellow(pc14.bold("[DRY RUN PREVIEW] No files were written to disk.")));
50
+ p16.log.warn(pc16.yellow(pc16.bold("[DRY RUN PREVIEW] No files were written to disk.")));
51
51
  }
52
52
  for (const f of files) {
53
53
  if (f.action === "created") {
54
- p14.log.success(`${pc14.bold(f.relativePath)} ${pc14.dim("(created)")}`);
54
+ p16.log.success(`${pc16.bold(f.relativePath)} ${pc16.dim("(created)")}`);
55
55
  } else if (f.action === "overwritten") {
56
- p14.log.warn(`${pc14.bold(f.relativePath)} ${pc14.dim("(overwritten)")}`);
56
+ p16.log.warn(`${pc16.bold(f.relativePath)} ${pc16.dim("(overwritten)")}`);
57
57
  } else if (f.action === "skipped") {
58
- p14.log.info(`${pc14.dim(f.relativePath)} ${pc14.dim("(skipped - already exists, pass -o to overwrite)")}`);
58
+ p16.log.info(`${pc16.dim(f.relativePath)} ${pc16.dim("(skipped - already exists, pass -o to overwrite)")}`);
59
59
  } else {
60
- p14.log.info(`${pc14.bold(f.relativePath)} ${pc14.dim("(preview)")}`);
60
+ p16.log.info(`${pc16.bold(f.relativePath)} ${pc16.dim("(preview)")}`);
61
61
  }
62
62
  }
63
63
  }
@@ -129,9 +129,9 @@ function loadMasonConfig(startDir = process.cwd()) {
129
129
  let current = path2.resolve(startDir);
130
130
  while (true) {
131
131
  const configPath = path2.join(current, "mason.config.json");
132
- if (fs9.existsSync(configPath)) {
132
+ if (fs11.existsSync(configPath)) {
133
133
  try {
134
- return JSON.parse(fs9.readFileSync(configPath, "utf-8"));
134
+ return JSON.parse(fs11.readFileSync(configPath, "utf-8"));
135
135
  } catch {
136
136
  return null;
137
137
  }
@@ -146,7 +146,7 @@ function getEffectiveMode(projectRoot, flags) {
146
146
  if (flags?.registry) return "registry";
147
147
  const config = loadMasonConfig(projectRoot);
148
148
  if (config?.mode) return config.mode;
149
- if (fs9.existsSync(path2.join(projectRoot, "index.json")) && (fs9.existsSync(path2.join(projectRoot, "features")) || fs9.existsSync(path2.join(projectRoot, "shared")) || fs9.existsSync(path2.join(projectRoot, "src/features")) || fs9.existsSync(path2.join(projectRoot, "src/shared")))) {
149
+ if (fs11.existsSync(path2.join(projectRoot, "index.json")) && (fs11.existsSync(path2.join(projectRoot, "features")) || fs11.existsSync(path2.join(projectRoot, "shared")) || fs11.existsSync(path2.join(projectRoot, "src/features")) || fs11.existsSync(path2.join(projectRoot, "src/shared")))) {
150
150
  return "registry";
151
151
  }
152
152
  return "app";
@@ -160,12 +160,12 @@ function getConfiguredFeaturesDir(projectRoot, flags) {
160
160
  }
161
161
  const mode = getEffectiveMode(projectRoot, flags);
162
162
  if (mode === "registry") {
163
- if (fs9.existsSync(path2.join(projectRoot, "src/features"))) {
163
+ if (fs11.existsSync(path2.join(projectRoot, "src/features"))) {
164
164
  return path2.join(projectRoot, "src/features");
165
165
  }
166
166
  return path2.join(projectRoot, "features");
167
167
  }
168
- if (fs9.existsSync(path2.join(projectRoot, "src/features"))) {
168
+ if (fs11.existsSync(path2.join(projectRoot, "src/features"))) {
169
169
  return path2.join(projectRoot, "src/features");
170
170
  }
171
171
  return path2.join(projectRoot, "features");
@@ -179,12 +179,12 @@ function getConfiguredSharedDir(projectRoot, flags) {
179
179
  }
180
180
  const mode = getEffectiveMode(projectRoot, flags);
181
181
  if (mode === "registry") {
182
- if (fs9.existsSync(path2.join(projectRoot, "src/shared"))) {
182
+ if (fs11.existsSync(path2.join(projectRoot, "src/shared"))) {
183
183
  return path2.join(projectRoot, "src/shared");
184
184
  }
185
185
  return path2.join(projectRoot, "shared");
186
186
  }
187
- if (fs9.existsSync(path2.join(projectRoot, "src/shared"))) {
187
+ if (fs11.existsSync(path2.join(projectRoot, "src/shared"))) {
188
188
  return path2.join(projectRoot, "src/shared");
189
189
  }
190
190
  return path2.join(projectRoot, "shared");
@@ -194,7 +194,7 @@ function getConfiguredSharedDir(projectRoot, flags) {
194
194
  function findProjectRoot(startDir = process.cwd()) {
195
195
  let current = path2.resolve(startDir);
196
196
  while (true) {
197
- if (fs9.existsSync(path2.join(current, "package.json"))) {
197
+ if (fs11.existsSync(path2.join(current, "package.json"))) {
198
198
  return current;
199
199
  }
200
200
  const parent = path2.dirname(current);
@@ -212,9 +212,9 @@ function resolveSharedDir(projectRoot, flags) {
212
212
  }
213
213
  function listExistingFeatures(projectRoot) {
214
214
  const featuresDir = resolveFeaturesDir(projectRoot);
215
- if (!fs9.existsSync(featuresDir)) return [];
215
+ if (!fs11.existsSync(featuresDir)) return [];
216
216
  try {
217
- const entries = fs9.readdirSync(featuresDir, { withFileTypes: true });
217
+ const entries = fs11.readdirSync(featuresDir, { withFileTypes: true });
218
218
  return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
219
219
  } catch {
220
220
  return [];
@@ -225,9 +225,9 @@ function listDomainInterfaces(projectRoot, featureName) {
225
225
  const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
226
226
  for (const feat of features) {
227
227
  const domainDir = path2.join(resolveFeaturesDir(projectRoot), feat, "domain");
228
- if (!fs9.existsSync(domainDir)) continue;
228
+ if (!fs11.existsSync(domainDir)) continue;
229
229
  try {
230
- const files = fs9.readdirSync(domainDir);
230
+ const files = fs11.readdirSync(domainDir);
231
231
  for (const file of files) {
232
232
  if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
233
233
  const name = file.replace(/\.ts$/, "");
@@ -240,7 +240,7 @@ function listDomainInterfaces(projectRoot, featureName) {
240
240
  });
241
241
  } else {
242
242
  try {
243
- const content = fs9.readFileSync(fullPath, "utf-8");
243
+ const content = fs11.readFileSync(fullPath, "utf-8");
244
244
  if (/export\s+(?:abstract\s+class|interface)\s+(\w+)/.test(content)) {
245
245
  results.push({
246
246
  name,
@@ -263,9 +263,9 @@ function listDomainEntities(projectRoot, featureName) {
263
263
  const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
264
264
  for (const feat of features) {
265
265
  const domainDir = path2.join(resolveFeaturesDir(projectRoot), feat, "domain");
266
- if (!fs9.existsSync(domainDir)) continue;
266
+ if (!fs11.existsSync(domainDir)) continue;
267
267
  try {
268
- const files = fs9.readdirSync(domainDir);
268
+ const files = fs11.readdirSync(domainDir);
269
269
  for (const file of files) {
270
270
  if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
271
271
  const name = file.replace(/\.ts$/, "");
@@ -288,9 +288,9 @@ function listUseCases(projectRoot, featureName) {
288
288
  const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
289
289
  for (const feat of features) {
290
290
  const useCasesDir = path2.join(resolveFeaturesDir(projectRoot), feat, "useCases");
291
- if (!fs9.existsSync(useCasesDir)) continue;
291
+ if (!fs11.existsSync(useCasesDir)) continue;
292
292
  try {
293
- const files = fs9.readdirSync(useCasesDir);
293
+ const files = fs11.readdirSync(useCasesDir);
294
294
  for (const file of files) {
295
295
  if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
296
296
  const name = file.replace(/\.ts$/, "");
@@ -322,29 +322,29 @@ hbs.registerHelper("join", (arr, sep) => {
322
322
  });
323
323
  var cachedTemplatesDir = null;
324
324
  function getTemplatesDir() {
325
- if (cachedTemplatesDir && fs9.existsSync(cachedTemplatesDir)) {
325
+ if (cachedTemplatesDir && fs11.existsSync(cachedTemplatesDir)) {
326
326
  return cachedTemplatesDir;
327
327
  }
328
328
  const currentDir = typeof __dirname !== "undefined" ? __dirname : path2.dirname(fileURLToPath(import.meta.url));
329
329
  const cand1 = path2.resolve(currentDir, "../../templates");
330
- if (fs9.existsSync(cand1)) {
330
+ if (fs11.existsSync(cand1)) {
331
331
  cachedTemplatesDir = cand1;
332
332
  return cand1;
333
333
  }
334
334
  const cand2 = path2.resolve(currentDir, "../templates");
335
- if (fs9.existsSync(cand2)) {
335
+ if (fs11.existsSync(cand2)) {
336
336
  cachedTemplatesDir = cand2;
337
337
  return cand2;
338
338
  }
339
339
  const cand3 = path2.resolve(currentDir, "templates");
340
- if (fs9.existsSync(cand3)) {
340
+ if (fs11.existsSync(cand3)) {
341
341
  cachedTemplatesDir = cand3;
342
342
  return cand3;
343
343
  }
344
344
  let curr = currentDir;
345
345
  while (true) {
346
346
  const candTemplates = path2.join(curr, "templates");
347
- if (fs9.existsSync(candTemplates)) {
347
+ if (fs11.existsSync(candTemplates)) {
348
348
  cachedTemplatesDir = candTemplates;
349
349
  return candTemplates;
350
350
  }
@@ -357,7 +357,7 @@ function getTemplatesDir() {
357
357
  var templateCache = /* @__PURE__ */ new Map();
358
358
  function loadTemplate(relativePath) {
359
359
  const fullPath = path2.isAbsolute(relativePath) ? relativePath : path2.join(getTemplatesDir(), relativePath);
360
- return fs9.readFileSync(fullPath, "utf-8");
360
+ return fs11.readFileSync(fullPath, "utf-8");
361
361
  }
362
362
  function renderTemplateFile(relativePath, context) {
363
363
  let compiled = templateCache.get(relativePath);
@@ -469,7 +469,7 @@ function generateInterfaceCode(ifaceName, kind = "abstractClass", entityName, me
469
469
  const idKey = `${entityKey}Id`;
470
470
  const listKey = `${entityKey}s`;
471
471
  const formattedCustomMethods = customMethods.map((m) => {
472
- const params = m.params && m.params.length > 0 ? m.params.map((p16) => `${p16.name}: ${p16.type}`).join("; ") : "";
472
+ const params = m.params && m.params.length > 0 ? m.params.map((p19) => `${p19.name}: ${p19.type}`).join("; ") : "";
473
473
  const returnType = m.returnType ? `Promise<${m.returnType}>` : "Promise<void>";
474
474
  return {
475
475
  name: m.name,
@@ -730,7 +730,7 @@ function parseInterfaceMethodsAndImports(content, targetIfaceName) {
730
730
  };
731
731
  }
732
732
  function findInterfaceFile(projectRoot, ifaceName, featureName, customPath) {
733
- if (customPath && fs9.existsSync(customPath)) {
733
+ if (customPath && fs11.existsSync(customPath)) {
734
734
  return { filePath: customPath, ifaceName };
735
735
  }
736
736
  const candidateNames = [
@@ -741,21 +741,21 @@ function findInterfaceFile(projectRoot, ifaceName, featureName, customPath) {
741
741
  const features = featureName ? [featureName, ...listExistingFeatures(projectRoot).filter((f) => f !== featureName)] : listExistingFeatures(projectRoot);
742
742
  for (const feat of features) {
743
743
  const domainDir = path2.join(resolveFeaturesDir(projectRoot), feat, "domain");
744
- if (!fs9.existsSync(domainDir)) continue;
744
+ if (!fs11.existsSync(domainDir)) continue;
745
745
  for (const cand of candidateNames) {
746
746
  const p1 = path2.join(domainDir, `${cand}.ts`);
747
- if (fs9.existsSync(p1)) {
747
+ if (fs11.existsSync(p1)) {
748
748
  return { filePath: p1, ifaceName: cand, feature: feat };
749
749
  }
750
750
  }
751
751
  }
752
752
  for (const cand of candidateNames) {
753
753
  const p1 = path2.join(projectRoot, "domain", `${cand}.ts`);
754
- if (fs9.existsSync(p1)) return { filePath: p1, ifaceName: cand };
754
+ if (fs11.existsSync(p1)) return { filePath: p1, ifaceName: cand };
755
755
  const p22 = path2.join(projectRoot, "src", "domain", `${cand}.ts`);
756
- if (fs9.existsSync(p22)) return { filePath: p22, ifaceName: cand };
756
+ if (fs11.existsSync(p22)) return { filePath: p22, ifaceName: cand };
757
757
  const p32 = path2.join(projectRoot, "interfaces", `${cand}.ts`);
758
- if (fs9.existsSync(p32)) return { filePath: p32, ifaceName: cand };
758
+ if (fs11.existsSync(p32)) return { filePath: p32, ifaceName: cand };
759
759
  }
760
760
  return null;
761
761
  }
@@ -778,7 +778,7 @@ function loadInterfaceHierarchy(projectRoot, initialIfaceName, featureName, cust
778
778
  while (currentFile && !visited.has(currentFile)) {
779
779
  visited.add(currentFile);
780
780
  try {
781
- const content = fs9.readFileSync(currentFile, "utf-8");
781
+ const content = fs11.readFileSync(currentFile, "utf-8");
782
782
  const parsed = parseInterfaceMethodsAndImports(content, currentName);
783
783
  for (const m of parsed.methods) {
784
784
  if (!allMethods.some((existing) => existing.name === m.name)) {
@@ -1050,22 +1050,22 @@ function generateUseCaseCode(useCaseName, dependencies = [], inputProps = [], ou
1050
1050
  from: /^I[A-Z]/.test(dep) ? `../domain/${dep}.js` : `./${dep}.js`
1051
1051
  }));
1052
1052
  const domainTypesToImport = new Set(domainImports);
1053
- for (const p16 of [...inputProps, ...outputProps]) {
1054
- const rawType = p16.type.trim();
1053
+ for (const p19 of [...inputProps, ...outputProps]) {
1054
+ const rawType = p19.type.trim();
1055
1055
  const baseType = rawType.replace(/\[\]$/, "").trim();
1056
1056
  if (!primitiveTypes.has(baseType) && /^[A-Z][A-Za-z0-9_]*$/.test(baseType) && !dependencies.includes(baseType)) {
1057
1057
  domainTypesToImport.add(baseType);
1058
1058
  }
1059
1059
  }
1060
- const formattedInputProps = inputProps.map((p16) => ({
1061
- name: p16.name,
1062
- optional: p16.optional,
1063
- type: p16.optional && !p16.type.includes("undefined") ? `${p16.type} | undefined` : p16.type
1060
+ const formattedInputProps = inputProps.map((p19) => ({
1061
+ name: p19.name,
1062
+ optional: p19.optional,
1063
+ type: p19.optional && !p19.type.includes("undefined") ? `${p19.type} | undefined` : p19.type
1064
1064
  }));
1065
- const formattedOutputProps = outputProps.map((p16) => ({
1066
- name: p16.name,
1067
- optional: p16.optional,
1068
- type: p16.optional && !p16.type.includes("undefined") ? `${p16.type} | undefined` : p16.type
1065
+ const formattedOutputProps = outputProps.map((p19) => ({
1066
+ name: p19.name,
1067
+ optional: p19.optional,
1068
+ type: p19.optional && !p19.type.includes("undefined") ? `${p19.type} | undefined` : p19.type
1069
1069
  }));
1070
1070
  const formattedDeps = dependencies.map((dep) => ({
1071
1071
  key: toCamelCase(dep),
@@ -1252,7 +1252,7 @@ function generateHttpHandler(options) {
1252
1252
  const pxDir = path2.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress");
1253
1253
  const handlersDir = path2.join(pxDir, "handlers");
1254
1254
  const pxIndexPath = path2.join(pxDir, "index.ts");
1255
- if (!fs9.existsSync(pxIndexPath)) {
1255
+ if (!fs11.existsSync(pxIndexPath)) {
1256
1256
  const featurePath = options.routePrefix || `/${options.feature.toLowerCase()}`;
1257
1257
  const pxIndexContent = renderTemplate(expressIndexTemplate, {
1258
1258
  routePath: featurePath
@@ -1289,8 +1289,8 @@ function updateOrGenerateDiProvider(featureDir, featureName, bindings = [], opti
1289
1289
  const providerPath = path2.join(featureDir, "diProvider.ts");
1290
1290
  const pascalFeature = toPascalCase(featureName);
1291
1291
  const providerModuleName = `${pascalFeature}Provider`;
1292
- if (fs9.existsSync(providerPath) && !options.overwrite) {
1293
- let content = fs9.readFileSync(providerPath, "utf-8");
1292
+ if (fs11.existsSync(providerPath) && !options.overwrite) {
1293
+ let content = fs11.readFileSync(providerPath, "utf-8");
1294
1294
  content = content.replace(/\n*export\s+const\s+ScaffoldProvider\s*=[^;]+;?/g, "");
1295
1295
  for (const b of bindings) {
1296
1296
  const ifaceImp = b.ifaceImportPath || `./domain/${b.ifaceName}.js`;
@@ -1736,23 +1736,23 @@ var PromptCancelledError = class extends Error {
1736
1736
  }
1737
1737
  };
1738
1738
  function handleCancel(result) {
1739
- if (p14.isCancel(result)) {
1740
- p14.cancel("Operation cancelled.");
1739
+ if (p16.isCancel(result)) {
1740
+ p16.cancel("Operation cancelled.");
1741
1741
  throw new PromptCancelledError();
1742
1742
  }
1743
1743
  return result;
1744
1744
  }
1745
1745
  function printBanner() {
1746
1746
  console.log("");
1747
- p14.intro(pc14.bgCyan(pc14.black(" @solid-stack/mason ")));
1747
+ p16.intro(pc16.bgCyan(pc16.black(" @solid-stack/mason ")));
1748
1748
  }
1749
1749
  async function promptProperties(options = {}) {
1750
1750
  const properties = options.defaults ? [...options.defaults] : [];
1751
1751
  if (properties.length > 0) {
1752
- const summary = properties.map((p16) => `${p16.name}: ${p16.type}${p16.optional ? "?" : ""}`).join(", ");
1753
- p14.log.info(pc14.dim(`Pre-configured fields: ${summary}`));
1752
+ const summary = properties.map((p19) => `${p19.name}: ${p19.type}${p19.optional ? "?" : ""}`).join(", ");
1753
+ p16.log.info(pc16.dim(`Pre-configured fields: ${summary}`));
1754
1754
  const addMore = handleCancel(
1755
- await p14.confirm({
1755
+ await p16.confirm({
1756
1756
  message: "Would you like to add custom fields/properties?",
1757
1757
  initialValue: false
1758
1758
  })
@@ -1761,10 +1761,10 @@ async function promptProperties(options = {}) {
1761
1761
  return properties;
1762
1762
  }
1763
1763
  }
1764
- p14.log.info(pc14.cyan("Enter properties (leave name blank when finished):"));
1764
+ p16.log.info(pc16.cyan("Enter properties (leave name blank when finished):"));
1765
1765
  while (true) {
1766
1766
  const name = handleCancel(
1767
- await p14.text({
1767
+ await p16.text({
1768
1768
  message: "Property name (or press Enter to finish):",
1769
1769
  placeholder: "e.g. title, email, status"
1770
1770
  })
@@ -1773,7 +1773,7 @@ async function promptProperties(options = {}) {
1773
1773
  break;
1774
1774
  }
1775
1775
  const type = handleCancel(
1776
- await p14.select({
1776
+ await p16.select({
1777
1777
  message: `Type for property "${name.trim()}":`,
1778
1778
  options: [
1779
1779
  { value: "string", label: "string" },
@@ -1790,7 +1790,7 @@ async function promptProperties(options = {}) {
1790
1790
  let finalType = type;
1791
1791
  if (type === "custom") {
1792
1792
  finalType = handleCancel(
1793
- await p14.text({
1793
+ await p16.text({
1794
1794
  message: `Enter custom type for "${name.trim()}":`,
1795
1795
  placeholder: "e.g. UserRole, string | null",
1796
1796
  validate: (val) => val && val.trim() ? void 0 : "Type cannot be empty"
@@ -1798,7 +1798,7 @@ async function promptProperties(options = {}) {
1798
1798
  );
1799
1799
  }
1800
1800
  const optional = handleCancel(
1801
- await p14.confirm({
1801
+ await p16.confirm({
1802
1802
  message: `Is "${name.trim()}" optional?`,
1803
1803
  initialValue: false
1804
1804
  })
@@ -1808,8 +1808,8 @@ async function promptProperties(options = {}) {
1808
1808
  type: finalType.trim(),
1809
1809
  optional
1810
1810
  });
1811
- p14.log.success(
1812
- pc14.green(`Added: ${name.trim()}${optional ? "?" : ""}: ${finalType.trim()}`)
1811
+ p16.log.success(
1812
+ pc16.green(`Added: ${name.trim()}${optional ? "?" : ""}: ${finalType.trim()}`)
1813
1813
  );
1814
1814
  }
1815
1815
  return properties;
@@ -1825,7 +1825,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1825
1825
  }
1826
1826
  if (!name) {
1827
1827
  name = handleCancel(
1828
- await p14.text({
1828
+ await p16.text({
1829
1829
  message: "What is the feature name?",
1830
1830
  placeholder: "e.g. users, auth, orders, billing",
1831
1831
  validate: (val) => {
@@ -1841,7 +1841,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1841
1841
  let targetDirRel = defaultDir;
1842
1842
  if (!flags.yes && !flags.target && !flags.dest) {
1843
1843
  targetDirRel = handleCancel(
1844
- await p14.text({
1844
+ await p16.text({
1845
1845
  message: "Target directory for the feature:",
1846
1846
  initialValue: defaultDir,
1847
1847
  defaultValue: defaultDir
@@ -1863,14 +1863,14 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1863
1863
  const customUseCases = [];
1864
1864
  if (!flags.yes) {
1865
1865
  includeExpress = handleCancel(
1866
- await p14.confirm({
1866
+ await p16.confirm({
1867
1867
  message: "Include Express Presentation layer (pxExpress routes & handlers)?",
1868
1868
  initialValue: true
1869
1869
  })
1870
1870
  );
1871
1871
  if (includeExpress) {
1872
1872
  routePath = handleCancel(
1873
- await p14.text({
1873
+ await p16.text({
1874
1874
  message: "Express route prefix:",
1875
1875
  initialValue: `/${normalizedName}`,
1876
1876
  defaultValue: `/${normalizedName}`
@@ -1878,14 +1878,14 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1878
1878
  ).trim();
1879
1879
  }
1880
1880
  includeDomain = handleCancel(
1881
- await p14.confirm({
1881
+ await p16.confirm({
1882
1882
  message: "Include Domain Entity/Entities?",
1883
1883
  initialValue: true
1884
1884
  })
1885
1885
  );
1886
1886
  if (includeDomain) {
1887
1887
  const domainInput = handleCancel(
1888
- await p14.text({
1888
+ await p16.text({
1889
1889
  message: "Domain entity name(s) (comma-separated):",
1890
1890
  initialValue: defaultEntity,
1891
1891
  defaultValue: defaultEntity,
@@ -1904,21 +1904,21 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1904
1904
  entityNames = [defaultEntity];
1905
1905
  }
1906
1906
  includeInterface = handleCancel(
1907
- await p14.confirm({
1907
+ await p16.confirm({
1908
1908
  message: `Include Domain Repository interface(s) for ${entityNames.join(", ")}?`,
1909
1909
  initialValue: true
1910
1910
  })
1911
1911
  );
1912
1912
  if (includeInterface) {
1913
1913
  includeInfra = handleCancel(
1914
- await p14.confirm({
1914
+ await p16.confirm({
1915
1915
  message: `Include Infrastructure Stub implementation(s) for ${entityNames.join(", ")}?`,
1916
1916
  initialValue: true
1917
1917
  })
1918
1918
  );
1919
1919
  }
1920
1920
  includeUseCase = handleCancel(
1921
- await p14.confirm({
1921
+ await p16.confirm({
1922
1922
  message: "Include Domain Use Cases?",
1923
1923
  initialValue: true
1924
1924
  })
@@ -1932,7 +1932,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1932
1932
  { value: "delete", label: "Delete (Delete<Entity>)", hint: "DELETE /:id" }
1933
1933
  ];
1934
1934
  crudOperations = handleCancel(
1935
- await p14.multiselect({
1935
+ await p16.multiselect({
1936
1936
  message: "Select standard CRUD use cases to generate:",
1937
1937
  options: crudChoices,
1938
1938
  initialValues: ["create", "read", "list", "update", "delete"],
@@ -1940,13 +1940,13 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1940
1940
  })
1941
1941
  );
1942
1942
  includeTests = handleCancel(
1943
- await p14.confirm({
1943
+ await p16.confirm({
1944
1944
  message: "Generate Vitest unit tests for use cases?",
1945
1945
  initialValue: true
1946
1946
  })
1947
1947
  );
1948
1948
  const wantCustom = handleCancel(
1949
- await p14.confirm({
1949
+ await p16.confirm({
1950
1950
  message: "Do you want to add any custom use cases to this feature?",
1951
1951
  initialValue: false
1952
1952
  })
@@ -1956,7 +1956,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1956
1956
  const availableIfaces = entityNames.map((e) => `I${e}Repository`);
1957
1957
  while (addMore) {
1958
1958
  const customName = handleCancel(
1959
- await p14.text({
1959
+ await p16.text({
1960
1960
  message: "Custom Use Case name:",
1961
1961
  placeholder: "e.g. ResetPassword, VerifyEmail, AssignRole",
1962
1962
  validate: (v) => v && v.trim() ? void 0 : "Use Case name cannot be empty"
@@ -1966,7 +1966,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1966
1966
  let selectedDeps = [];
1967
1967
  if (availableIfaces.length > 0) {
1968
1968
  selectedDeps = handleCancel(
1969
- await p14.multiselect({
1969
+ await p16.multiselect({
1970
1970
  message: `Select domain interfaces to inject into ${customUcName}:`,
1971
1971
  options: availableIfaces.map((i) => ({ value: i, label: i })),
1972
1972
  required: false
@@ -1975,7 +1975,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1975
1975
  }
1976
1976
  let customInputProps = [];
1977
1977
  const hasInput = handleCancel(
1978
- await p14.confirm({
1978
+ await p16.confirm({
1979
1979
  message: `Does ${customUcName} accept input arguments?`,
1980
1980
  initialValue: true
1981
1981
  })
@@ -1986,7 +1986,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1986
1986
  }
1987
1987
  let customOutputProps = [];
1988
1988
  const hasCustomOutput = handleCancel(
1989
- await p14.confirm({
1989
+ await p16.confirm({
1990
1990
  message: `Does ${customUcName} return custom output data?`,
1991
1991
  initialValue: false
1992
1992
  })
@@ -1996,7 +1996,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1996
1996
  });
1997
1997
  }
1998
1998
  const customIncludeTest = handleCancel(
1999
- await p14.confirm({
1999
+ await p16.confirm({
2000
2000
  message: `Generate Vitest unit test for ${customUcName}?`,
2001
2001
  initialValue: true
2002
2002
  })
@@ -2006,14 +2006,14 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2006
2006
  let customHttpPath = `/${toKebabCase(customUcName)}`;
2007
2007
  if (includeExpress) {
2008
2008
  customCreateHttp = handleCancel(
2009
- await p14.confirm({
2009
+ await p16.confirm({
2010
2010
  message: `Generate a matching ExpressRoute HTTP handler for ${customUcName}?`,
2011
2011
  initialValue: true
2012
2012
  })
2013
2013
  );
2014
2014
  if (customCreateHttp) {
2015
2015
  customHttpMethod = handleCancel(
2016
- await p14.select({
2016
+ await p16.select({
2017
2017
  message: "HTTP method for route:",
2018
2018
  options: [
2019
2019
  { value: "post", label: "POST" },
@@ -2026,7 +2026,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2026
2026
  })
2027
2027
  );
2028
2028
  customHttpPath = handleCancel(
2029
- await p14.text({
2029
+ await p16.text({
2030
2030
  message: "Route subpath (relative to feature prefix):",
2031
2031
  initialValue: `/${toKebabCase(customUcName)}`,
2032
2032
  defaultValue: `/${toKebabCase(customUcName)}`
@@ -2047,7 +2047,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2047
2047
  validationFields: customInputProps
2048
2048
  });
2049
2049
  addMore = handleCancel(
2050
- await p14.confirm({
2050
+ await p16.confirm({
2051
2051
  message: "Add another custom use case?",
2052
2052
  initialValue: false
2053
2053
  })
@@ -2056,7 +2056,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2056
2056
  }
2057
2057
  }
2058
2058
  includeDiProvider = handleCancel(
2059
- await p14.confirm({
2059
+ await p16.confirm({
2060
2060
  message: "Generate / update feature DI provider (diProvider.ts)?",
2061
2061
  initialValue: true
2062
2062
  })
@@ -2068,8 +2068,8 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2068
2068
  entityNames = [defaultEntity];
2069
2069
  }
2070
2070
  }
2071
- const s = p14.spinner();
2072
- s.start(`Generating feature '${pc14.cyan(normalizedName)}'...`);
2071
+ const s = p16.spinner();
2072
+ s.start(`Generating feature '${pc16.cyan(normalizedName)}'...`);
2073
2073
  const options = {
2074
2074
  name: normalizedName,
2075
2075
  projectRoot,
@@ -2091,7 +2091,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2091
2091
  const files = generateFeature(options);
2092
2092
  if (isRegistry) {
2093
2093
  const registryPath = path2.join(targetDir, "registry.json");
2094
- if (!fs9.existsSync(registryPath)) {
2094
+ if (!fs11.existsSync(registryPath)) {
2095
2095
  const manifest = {
2096
2096
  name: toKebabCase(normalizedName),
2097
2097
  type: "feature",
@@ -2115,8 +2115,8 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2115
2115
  }
2116
2116
  s.stop("Feature scaffolded successfully!");
2117
2117
  printGeneratedFiles(files, options.dryRun);
2118
- p14.outro(
2119
- pc14.green(
2118
+ p16.outro(
2119
+ pc16.green(
2120
2120
  `\u2713 Feature '${normalizedName}' initialized in ${path2.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
2121
2121
  )
2122
2122
  );
@@ -2230,7 +2230,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
2230
2230
  }
2231
2231
  if (!name) {
2232
2232
  name = handleCancel(
2233
- await p14.text({
2233
+ await p16.text({
2234
2234
  message: "What is the shared module name?",
2235
2235
  placeholder: "e.g. hasher, time, uuid, cache, logger",
2236
2236
  validate: (val) => {
@@ -2246,7 +2246,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
2246
2246
  let targetDirRel = defaultDir;
2247
2247
  if (!flags.yes && !flags.target && !flags.dest) {
2248
2248
  targetDirRel = handleCancel(
2249
- await p14.text({
2249
+ await p16.text({
2250
2250
  message: "Target directory for the shared module:",
2251
2251
  initialValue: defaultDir,
2252
2252
  defaultValue: defaultDir
@@ -2258,21 +2258,21 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
2258
2258
  let includeStub = true;
2259
2259
  if (!flags.yes) {
2260
2260
  description = handleCancel(
2261
- await p14.text({
2261
+ await p16.text({
2262
2262
  message: "Module description (optional):",
2263
2263
  placeholder: "e.g. In-memory caching and Redis port abstraction",
2264
2264
  initialValue: ""
2265
2265
  })
2266
2266
  ).trim();
2267
2267
  includeStub = handleCancel(
2268
- await p14.confirm({
2268
+ await p16.confirm({
2269
2269
  message: "Include stub implementation in infrastructure/?",
2270
2270
  initialValue: true
2271
2271
  })
2272
2272
  );
2273
2273
  }
2274
- const s = p14.spinner();
2275
- s.start(`Generating shared module '${pc14.cyan(normalizedName)}'...`);
2274
+ const s = p16.spinner();
2275
+ s.start(`Generating shared module '${pc16.cyan(normalizedName)}'...`);
2276
2276
  const files = generateShared({
2277
2277
  name: normalizedName,
2278
2278
  description,
@@ -2285,8 +2285,8 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
2285
2285
  });
2286
2286
  s.stop("Shared module generated successfully!");
2287
2287
  printGeneratedFiles(files, Boolean(flags.dryRun));
2288
- p14.outro(
2289
- pc14.green(
2288
+ p16.outro(
2289
+ pc16.green(
2290
2290
  `\u2713 Shared module '${normalizedName}' initialized in ${path2.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
2291
2291
  )
2292
2292
  );
@@ -2300,14 +2300,14 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2300
2300
  const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
2301
2301
  featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
2302
2302
  const selected = handleCancel(
2303
- await p14.select({
2303
+ await p16.select({
2304
2304
  message: "Select target feature:",
2305
2305
  options: featureOptions
2306
2306
  })
2307
2307
  );
2308
2308
  if (selected === "__custom__") {
2309
2309
  feature = handleCancel(
2310
- await p14.text({
2310
+ await p16.text({
2311
2311
  message: "Enter feature name:",
2312
2312
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2313
2313
  })
@@ -2317,7 +2317,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2317
2317
  }
2318
2318
  } else {
2319
2319
  feature = handleCancel(
2320
- await p14.text({
2320
+ await p16.text({
2321
2321
  message: "Enter target feature name:",
2322
2322
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2323
2323
  })
@@ -2328,7 +2328,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2328
2328
  let name = useCaseNameArg?.trim();
2329
2329
  if (!name) {
2330
2330
  name = handleCancel(
2331
- await p14.text({
2331
+ await p16.text({
2332
2332
  message: "Use Case name:",
2333
2333
  placeholder: "e.g. CreateUser, GetUser, CancelOrder",
2334
2334
  validate: (v) => v && v.trim() ? void 0 : "Use Case name cannot be empty"
@@ -2344,7 +2344,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2344
2344
  label: i.name
2345
2345
  }));
2346
2346
  selectedDeps = handleCancel(
2347
- await p14.multiselect({
2347
+ await p16.multiselect({
2348
2348
  message: "Select domain interfaces to inject into use case:",
2349
2349
  options: multiOptions,
2350
2350
  required: false
@@ -2359,7 +2359,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2359
2359
  let httpPath = "/";
2360
2360
  if (!flags.yes) {
2361
2361
  const hasInput = handleCancel(
2362
- await p14.confirm({
2362
+ await p16.confirm({
2363
2363
  message: "Does this use case accept input arguments?",
2364
2364
  initialValue: true
2365
2365
  })
@@ -2369,7 +2369,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2369
2369
  });
2370
2370
  }
2371
2371
  const hasCustomOutput = handleCancel(
2372
- await p14.confirm({
2372
+ await p16.confirm({
2373
2373
  message: "Does this use case return custom output data?",
2374
2374
  initialValue: false
2375
2375
  })
@@ -2379,20 +2379,20 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2379
2379
  });
2380
2380
  }
2381
2381
  includeTest = handleCancel(
2382
- await p14.confirm({
2382
+ await p16.confirm({
2383
2383
  message: "Generate Vitest unit test skeleton (.test.ts)?",
2384
2384
  initialValue: true
2385
2385
  })
2386
2386
  );
2387
2387
  createHttp = handleCancel(
2388
- await p14.confirm({
2388
+ await p16.confirm({
2389
2389
  message: "Generate a matching ExpressRoute HTTP handler?",
2390
2390
  initialValue: false
2391
2391
  })
2392
2392
  );
2393
2393
  if (createHttp) {
2394
2394
  httpMethod = handleCancel(
2395
- await p14.select({
2395
+ await p16.select({
2396
2396
  message: "HTTP method for route:",
2397
2397
  options: [
2398
2398
  { value: "post", label: "POST" },
@@ -2405,7 +2405,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2405
2405
  })
2406
2406
  );
2407
2407
  httpPath = handleCancel(
2408
- await p14.text({
2408
+ await p16.text({
2409
2409
  message: "Route subpath (relative to feature prefix):",
2410
2410
  initialValue: "/",
2411
2411
  defaultValue: "/"
@@ -2413,8 +2413,8 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2413
2413
  ).trim();
2414
2414
  }
2415
2415
  }
2416
- const s = p14.spinner();
2417
- s.start(`Generating use case '${pc14.cyan(ucName)}'...`);
2416
+ const s = p16.spinner();
2417
+ s.start(`Generating use case '${pc16.cyan(ucName)}'...`);
2418
2418
  const options = {
2419
2419
  name: ucName,
2420
2420
  feature,
@@ -2446,7 +2446,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2446
2446
  }
2447
2447
  s.stop("Use case generated successfully!");
2448
2448
  printGeneratedFiles(files, options.dryRun);
2449
- p14.outro(pc14.green(`Use case '${ucName}' created in feature '${feature}'!`));
2449
+ p16.outro(pc16.green(`Use case '${ucName}' created in feature '${feature}'!`));
2450
2450
  }
2451
2451
  async function runDomainCommand(domainNameArg, flags = {}) {
2452
2452
  const projectRoot = findProjectRoot();
@@ -2457,14 +2457,14 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2457
2457
  const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
2458
2458
  featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
2459
2459
  const selected = handleCancel(
2460
- await p14.select({
2460
+ await p16.select({
2461
2461
  message: "Select target feature:",
2462
2462
  options: featureOptions
2463
2463
  })
2464
2464
  );
2465
2465
  if (selected === "__custom__") {
2466
2466
  feature = handleCancel(
2467
- await p14.text({
2467
+ await p16.text({
2468
2468
  message: "Enter feature name:",
2469
2469
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2470
2470
  })
@@ -2474,7 +2474,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2474
2474
  }
2475
2475
  } else {
2476
2476
  feature = handleCancel(
2477
- await p14.text({
2477
+ await p16.text({
2478
2478
  message: "Enter target feature name:",
2479
2479
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2480
2480
  })
@@ -2485,7 +2485,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2485
2485
  let name = domainNameArg?.trim();
2486
2486
  if (!name) {
2487
2487
  name = handleCancel(
2488
- await p14.text({
2488
+ await p16.text({
2489
2489
  message: "Domain model / entity name:",
2490
2490
  placeholder: "e.g. User, Order, Product, Address",
2491
2491
  validate: (v) => v && v.trim() ? void 0 : "Domain name cannot be empty"
@@ -2497,7 +2497,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2497
2497
  let properties = [];
2498
2498
  if (!flags.yes) {
2499
2499
  kind = handleCancel(
2500
- await p14.select({
2500
+ await p16.select({
2501
2501
  message: "Select domain model type:",
2502
2502
  options: [
2503
2503
  { value: "interface", label: "TypeScript Interface (pure data structure)" },
@@ -2516,8 +2516,8 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2516
2516
  defaults: kind === "valueObject" ? [{ name: "value", type: "string" }] : defaultProps
2517
2517
  });
2518
2518
  }
2519
- const s = p14.spinner();
2520
- s.start(`Generating domain model '${pc14.cyan(entityName)}'...`);
2519
+ const s = p16.spinner();
2520
+ s.start(`Generating domain model '${pc16.cyan(entityName)}'...`);
2521
2521
  const options = {
2522
2522
  name: entityName,
2523
2523
  feature,
@@ -2530,7 +2530,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2530
2530
  const file = generateDomain(options);
2531
2531
  s.stop("Domain model generated successfully!");
2532
2532
  printGeneratedFiles([file], options.dryRun);
2533
- p14.outro(pc14.green(`Domain model '${entityName}' created in feature '${feature}'!`));
2533
+ p16.outro(pc16.green(`Domain model '${entityName}' created in feature '${feature}'!`));
2534
2534
  }
2535
2535
  async function runInfrastructureCommand(classNameArg, flags = {}) {
2536
2536
  const projectRoot = findProjectRoot();
@@ -2539,7 +2539,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2539
2539
  let feature = flags.feature?.trim();
2540
2540
  if (!flags.yes && !flags.global && !flags.feature) {
2541
2541
  scope = handleCancel(
2542
- await p14.select({
2542
+ await p16.select({
2543
2543
  message: "Where should this infrastructure implementation be placed?",
2544
2544
  options: [
2545
2545
  { value: "feature", label: "Feature-level (features/<feature>/infrastructure)" },
@@ -2554,14 +2554,14 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2554
2554
  const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
2555
2555
  featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
2556
2556
  const selected = handleCancel(
2557
- await p14.select({
2557
+ await p16.select({
2558
2558
  message: "Select target feature:",
2559
2559
  options: featureOptions
2560
2560
  })
2561
2561
  );
2562
2562
  if (selected === "__custom__") {
2563
2563
  feature = handleCancel(
2564
- await p14.text({
2564
+ await p16.text({
2565
2565
  message: "Enter feature name:",
2566
2566
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2567
2567
  })
@@ -2571,7 +2571,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2571
2571
  }
2572
2572
  } else {
2573
2573
  feature = handleCancel(
2574
- await p14.text({
2574
+ await p16.text({
2575
2575
  message: "Enter target feature name:",
2576
2576
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2577
2577
  })
@@ -2584,7 +2584,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2584
2584
  let name = classNameArg?.trim();
2585
2585
  if (!name) {
2586
2586
  name = handleCancel(
2587
- await p14.text({
2587
+ await p16.text({
2588
2588
  message: "Infrastructure class name:",
2589
2589
  placeholder: "e.g. StubUserRepository, PostgresUserRepository, S3FileStorage",
2590
2590
  validate: (v) => v && v.trim() ? void 0 : "Class name cannot be empty"
@@ -2603,7 +2603,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2603
2603
  (i) => className.toLowerCase().includes(removeInterfacePrefix(i.name).toLowerCase())
2604
2604
  );
2605
2605
  const ifaceChoice = handleCancel(
2606
- await p14.select({
2606
+ await p16.select({
2607
2607
  message: "Does this class implement a domain interface or abstract class?",
2608
2608
  options: [
2609
2609
  ...interfaces.map((i) => ({ value: i.name, label: i.name })),
@@ -2615,7 +2615,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2615
2615
  );
2616
2616
  if (ifaceChoice === "__custom__") {
2617
2617
  implementsInterface = handleCancel(
2618
- await p14.text({
2618
+ await p16.text({
2619
2619
  message: "Enter interface or abstract class name:",
2620
2620
  placeholder: "e.g. IUserRepository, BaseStorageAdapter",
2621
2621
  validate: (v) => v && v.trim() ? void 0 : "Name cannot be empty"
@@ -2626,14 +2626,14 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2626
2626
  }
2627
2627
  } else {
2628
2628
  const wantImpl = handleCancel(
2629
- await p14.confirm({
2629
+ await p16.confirm({
2630
2630
  message: "Does this class implement a domain interface or abstract class?",
2631
2631
  initialValue: false
2632
2632
  })
2633
2633
  );
2634
2634
  if (wantImpl) {
2635
2635
  implementsInterface = handleCancel(
2636
- await p14.text({
2636
+ await p16.text({
2637
2637
  message: "Enter interface or abstract class name:",
2638
2638
  placeholder: "e.g. IUserRepository, BaseStorageAdapter",
2639
2639
  validate: (v) => v && v.trim() ? void 0 : "Name cannot be empty"
@@ -2653,7 +2653,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2653
2653
  const allInterfaces = listDomainInterfaces(projectRoot);
2654
2654
  if (allInterfaces.length > 0) {
2655
2655
  const ifaceChoice = handleCancel(
2656
- await p14.select({
2656
+ await p16.select({
2657
2657
  message: "Does this class implement an interface or abstract class?",
2658
2658
  options: [
2659
2659
  ...allInterfaces.map((i) => ({ value: i.name, label: `${i.name} (${i.feature})` })),
@@ -2665,7 +2665,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2665
2665
  );
2666
2666
  if (ifaceChoice === "__custom__") {
2667
2667
  implementsInterface = handleCancel(
2668
- await p14.text({
2668
+ await p16.text({
2669
2669
  message: "Enter interface or abstract class name:",
2670
2670
  placeholder: "e.g. IAuthRepository",
2671
2671
  validate: (v) => v && v.trim() ? void 0 : "Name cannot be empty"
@@ -2678,7 +2678,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2678
2678
  }
2679
2679
  if (!flags.yes && !flags.kind) {
2680
2680
  kind = handleCancel(
2681
- await p14.select({
2681
+ await p16.select({
2682
2682
  message: "Select implementation kind:",
2683
2683
  options: [
2684
2684
  { value: "stub", label: "In-Memory Stub (with in-memory store and test helper methods)" },
@@ -2689,7 +2689,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2689
2689
  );
2690
2690
  if (scope === "feature" && feature && implementsInterface) {
2691
2691
  registerInProvider = handleCancel(
2692
- await p14.confirm({
2692
+ await p16.confirm({
2693
2693
  message: "Register this implementation in feature diProvider.ts?",
2694
2694
  initialValue: true
2695
2695
  })
@@ -2698,8 +2698,8 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2698
2698
  } else if (flags.yes && scope === "feature" && feature && implementsInterface) {
2699
2699
  registerInProvider = !flags.noProvider;
2700
2700
  }
2701
- const s = p14.spinner();
2702
- s.start(`Generating infrastructure class '${pc14.cyan(className)}'...`);
2701
+ const s = p16.spinner();
2702
+ s.start(`Generating infrastructure class '${pc16.cyan(className)}'...`);
2703
2703
  const files = [];
2704
2704
  const options = {
2705
2705
  name: className,
@@ -2734,7 +2734,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2734
2734
  }
2735
2735
  s.stop("Infrastructure class generated successfully!");
2736
2736
  printGeneratedFiles(files, options.dryRun);
2737
- p14.outro(pc14.green(`Infrastructure class '${className}' created!`));
2737
+ p16.outro(pc16.green(`Infrastructure class '${className}' created!`));
2738
2738
  }
2739
2739
  async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2740
2740
  const projectRoot = findProjectRoot();
@@ -2745,14 +2745,14 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2745
2745
  const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
2746
2746
  featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
2747
2747
  const selected = handleCancel(
2748
- await p14.select({
2748
+ await p16.select({
2749
2749
  message: "Select target feature:",
2750
2750
  options: featureOptions
2751
2751
  })
2752
2752
  );
2753
2753
  if (selected === "__custom__") {
2754
2754
  feature = handleCancel(
2755
- await p14.text({
2755
+ await p16.text({
2756
2756
  message: "Enter feature name:",
2757
2757
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2758
2758
  })
@@ -2762,7 +2762,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2762
2762
  }
2763
2763
  } else {
2764
2764
  feature = handleCancel(
2765
- await p14.text({
2765
+ await p16.text({
2766
2766
  message: "Enter target feature name:",
2767
2767
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2768
2768
  })
@@ -2773,7 +2773,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2773
2773
  let name = interfaceNameArg?.trim();
2774
2774
  if (!name) {
2775
2775
  name = handleCancel(
2776
- await p14.text({
2776
+ await p16.text({
2777
2777
  message: "Domain interface name:",
2778
2778
  placeholder: "e.g. IUserRepository, IPaymentService",
2779
2779
  validate: (v) => v && v.trim() ? void 0 : "Interface name cannot be empty"
@@ -2791,7 +2791,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2791
2791
  if (!flags.yes) {
2792
2792
  if (existingEntities.length > 0) {
2793
2793
  const selectedEntity = handleCancel(
2794
- await p14.select({
2794
+ await p16.select({
2795
2795
  message: "Select domain entity associated with this interface:",
2796
2796
  options: [
2797
2797
  ...existingEntities.map((e) => ({ value: e.name, label: e.name })),
@@ -2802,7 +2802,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2802
2802
  );
2803
2803
  if (selectedEntity === "__custom__") {
2804
2804
  entityName = handleCancel(
2805
- await p14.text({
2805
+ await p16.text({
2806
2806
  message: "Enter entity name:",
2807
2807
  defaultValue: removeInterfacePrefix(ifaceName).replace(/Repository$|Service$/, "")
2808
2808
  })
@@ -2812,7 +2812,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2812
2812
  }
2813
2813
  }
2814
2814
  kind = handleCancel(
2815
- await p14.select({
2815
+ await p16.select({
2816
2816
  message: "Interface definition style:",
2817
2817
  options: [
2818
2818
  { value: "abstractClass", label: "Abstract Class (Recommended - acts as DI token & type)" },
@@ -2822,7 +2822,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2822
2822
  })
2823
2823
  );
2824
2824
  methodsPreset = handleCancel(
2825
- await p14.select({
2825
+ await p16.select({
2826
2826
  message: "Method signature preset:",
2827
2827
  options: [
2828
2828
  { value: "crud", label: "Standard CRUD (GetById, List, Create, Update, Delete)" },
@@ -2832,22 +2832,22 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2832
2832
  })
2833
2833
  );
2834
2834
  generateStub = handleCancel(
2835
- await p14.confirm({
2835
+ await p16.confirm({
2836
2836
  message: "Auto-generate Infrastructure Stub implementation?",
2837
2837
  initialValue: true
2838
2838
  })
2839
2839
  );
2840
2840
  if (generateStub) {
2841
2841
  registerInProvider = handleCancel(
2842
- await p14.confirm({
2842
+ await p16.confirm({
2843
2843
  message: "Auto-register interface & stub in feature diProvider.ts?",
2844
2844
  initialValue: true
2845
2845
  })
2846
2846
  );
2847
2847
  }
2848
2848
  }
2849
- const s = p14.spinner();
2850
- s.start(`Generating interface '${pc14.cyan(ifaceName)}'...`);
2849
+ const s = p16.spinner();
2850
+ s.start(`Generating interface '${pc16.cyan(ifaceName)}'...`);
2851
2851
  const files = [];
2852
2852
  const options = {
2853
2853
  name: ifaceName,
@@ -2896,7 +2896,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2896
2896
  }
2897
2897
  s.stop("Interface generated successfully!");
2898
2898
  printGeneratedFiles(files, options.dryRun);
2899
- p14.outro(pc14.green(`Interface '${ifaceName}' created in feature '${feature}'!`));
2899
+ p16.outro(pc16.green(`Interface '${ifaceName}' created in feature '${feature}'!`));
2900
2900
  }
2901
2901
  async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2902
2902
  const projectRoot = findProjectRoot();
@@ -2907,14 +2907,14 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2907
2907
  const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
2908
2908
  featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
2909
2909
  const selected = handleCancel(
2910
- await p14.select({
2910
+ await p16.select({
2911
2911
  message: "Select target feature:",
2912
2912
  options: featureOptions
2913
2913
  })
2914
2914
  );
2915
2915
  if (selected === "__custom__") {
2916
2916
  feature = handleCancel(
2917
- await p14.text({
2917
+ await p16.text({
2918
2918
  message: "Enter feature name:",
2919
2919
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2920
2920
  })
@@ -2924,7 +2924,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2924
2924
  }
2925
2925
  } else {
2926
2926
  feature = handleCancel(
2927
- await p14.text({
2927
+ await p16.text({
2928
2928
  message: "Enter target feature name:",
2929
2929
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2930
2930
  })
@@ -2935,7 +2935,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2935
2935
  let name = handlerNameArg?.trim();
2936
2936
  if (!name) {
2937
2937
  name = handleCancel(
2938
- await p14.text({
2938
+ await p16.text({
2939
2939
  message: "HTTP Route Handler class name:",
2940
2940
  placeholder: "e.g. CreateUserHttp, GetUserHttp, HealthHttp",
2941
2941
  validate: (v) => v && v.trim() ? void 0 : "Handler name cannot be empty"
@@ -2954,7 +2954,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2954
2954
  let validationFields = [];
2955
2955
  if (!flags.yes) {
2956
2956
  method = handleCancel(
2957
- await p14.select({
2957
+ await p16.select({
2958
2958
  message: "HTTP Method:",
2959
2959
  options: [
2960
2960
  { value: "get", label: "GET" },
@@ -2968,7 +2968,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2968
2968
  );
2969
2969
  const defaultPath = method === "get" ? "/" : "/";
2970
2970
  subPath = handleCancel(
2971
- await p14.text({
2971
+ await p16.text({
2972
2972
  message: "Route subpath (relative to feature prefix):",
2973
2973
  initialValue: defaultPath,
2974
2974
  defaultValue: defaultPath
@@ -2979,7 +2979,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2979
2979
  const ucOptions = useCases.map((u) => ({ value: u.name, label: u.name }));
2980
2980
  ucOptions.push({ value: "__none__", label: "None / Standalone Handler" });
2981
2981
  const chosenUc = handleCancel(
2982
- await p14.select({
2982
+ await p16.select({
2983
2983
  message: "Connect this handler to a domain Use Case?",
2984
2984
  options: ucOptions,
2985
2985
  initialValue: ucOptions[0]?.value
@@ -2990,7 +2990,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2990
2990
  }
2991
2991
  }
2992
2992
  includeValidation = handleCancel(
2993
- await p14.confirm({
2993
+ await p16.confirm({
2994
2994
  message: "Include Zod inbound request validation?",
2995
2995
  initialValue: method !== "delete"
2996
2996
  })
@@ -2998,7 +2998,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2998
2998
  if (includeValidation) {
2999
2999
  const defaultSource = method === "get" ? "query" : "body";
3000
3000
  validationSource = handleCancel(
3001
- await p14.select({
3001
+ await p16.select({
3002
3002
  message: "Validation data source:",
3003
3003
  options: [
3004
3004
  { value: "body", label: "req.body (for POST / PUT / PATCH payloads)" },
@@ -3012,8 +3012,8 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
3012
3012
  });
3013
3013
  }
3014
3014
  }
3015
- const s = p14.spinner();
3016
- s.start(`Generating HTTP handler '${pc14.cyan(className)}'...`);
3015
+ const s = p16.spinner();
3016
+ s.start(`Generating HTTP handler '${pc16.cyan(className)}'...`);
3017
3017
  const options = {
3018
3018
  name: className,
3019
3019
  feature,
@@ -3031,7 +3031,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
3031
3031
  const files = generateHttpHandler(options);
3032
3032
  s.stop("HTTP handler generated successfully!");
3033
3033
  printGeneratedFiles(files, options.dryRun);
3034
- p14.outro(pc14.green(`HTTP handler '${className}' created in feature '${feature}'!`));
3034
+ p16.outro(pc16.green(`HTTP handler '${className}' created in feature '${feature}'!`));
3035
3035
  }
3036
3036
  function generateInjectableCode(className, dependencies = [], methods = [], implementsInterface) {
3037
3037
  const formattedDeps = dependencies.map((dep) => ({
@@ -3041,7 +3041,7 @@ function generateInjectableCode(className, dependencies = [], methods = [], impl
3041
3041
  const formattedMethods = methods.map((m) => {
3042
3042
  const isAsync = m.async !== false;
3043
3043
  const retType = m.returnType ? isAsync ? `Promise<${m.returnType}>` : m.returnType : isAsync ? "Promise<void>" : "void";
3044
- const params = m.params && m.params.length > 0 ? m.params.map((p16) => `${p16.name}: ${p16.type}`).join(", ") : "";
3044
+ const params = m.params && m.params.length > 0 ? m.params.map((p19) => `${p19.name}: ${p19.type}`).join(", ") : "";
3045
3045
  return {
3046
3046
  name: m.name,
3047
3047
  isAsync,
@@ -3111,7 +3111,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3111
3111
  let name = classNameArg?.trim();
3112
3112
  if (!name) {
3113
3113
  name = handleCancel(
3114
- await p14.text({
3114
+ await p16.text({
3115
3115
  message: "Injectable class name:",
3116
3116
  placeholder: "e.g. PasswordHasher, TokenService, UserTransformer",
3117
3117
  validate: (v) => v && v.trim() ? void 0 : "Class name cannot be empty"
@@ -3124,7 +3124,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3124
3124
  let customPath;
3125
3125
  if (!flags.yes) {
3126
3126
  location = handleCancel(
3127
- await p14.select({
3127
+ await p16.select({
3128
3128
  message: "Select target architectural location:",
3129
3129
  options: [
3130
3130
  { value: "feature-service", label: "Feature Service (features/<feature>/services/)" },
@@ -3142,14 +3142,14 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3142
3142
  const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
3143
3143
  featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
3144
3144
  const selected = handleCancel(
3145
- await p14.select({
3145
+ await p16.select({
3146
3146
  message: "Select target feature:",
3147
3147
  options: featureOptions
3148
3148
  })
3149
3149
  );
3150
3150
  if (selected === "__custom__") {
3151
3151
  feature = handleCancel(
3152
- await p14.text({
3152
+ await p16.text({
3153
3153
  message: "Enter feature name:",
3154
3154
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
3155
3155
  })
@@ -3159,7 +3159,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3159
3159
  }
3160
3160
  } else {
3161
3161
  feature = handleCancel(
3162
- await p14.text({
3162
+ await p16.text({
3163
3163
  message: "Enter target feature name:",
3164
3164
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
3165
3165
  })
@@ -3169,7 +3169,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3169
3169
  feature = toCamelCase(feature);
3170
3170
  } else if (location === "custom") {
3171
3171
  customPath = handleCancel(
3172
- await p14.text({
3172
+ await p16.text({
3173
3173
  message: "Enter destination directory path:",
3174
3174
  placeholder: "e.g. src/services or lib/utils",
3175
3175
  validate: (v) => v && v.trim() ? void 0 : "Path cannot be empty"
@@ -3180,14 +3180,14 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3180
3180
  let generateInterface2 = false;
3181
3181
  if (!flags.yes) {
3182
3182
  generateInterface2 = handleCancel(
3183
- await p14.confirm({
3183
+ await p16.confirm({
3184
3184
  message: `Generate abstract class interface (I${className}) as runtime DI token?`,
3185
3185
  initialValue: false
3186
3186
  })
3187
3187
  );
3188
3188
  }
3189
- const s = p14.spinner();
3190
- s.start(`Generating injectable '${pc14.cyan(className)}'...`);
3189
+ const s = p16.spinner();
3190
+ s.start(`Generating injectable '${pc16.cyan(className)}'...`);
3191
3191
  const options = {
3192
3192
  name: className,
3193
3193
  feature,
@@ -3201,12 +3201,83 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3201
3201
  const files = generateInjectable(options);
3202
3202
  s.stop("Injectable class generated successfully!");
3203
3203
  printGeneratedFiles(files, options.dryRun);
3204
- p14.outro(pc14.green(`Injectable '${className}' created successfully!`));
3204
+ p16.outro(pc16.green(`Injectable '${className}' created successfully!`));
3205
3205
  }
3206
+ async function runEnvCommand(flags = {}) {
3207
+ const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
3208
+ const s = p16.spinner();
3209
+ s.start("Scanning for .example environment files...");
3210
+ const files = fs11.readdirSync(projectRoot);
3211
+ const exampleFiles = files.filter((file) => file.startsWith(".env") && file.endsWith(".example")).sort();
3212
+ if (exampleFiles.length === 0) {
3213
+ s.stop("No .example files found.");
3214
+ p16.log.info(pc16.yellow(`No files matching '.env*.example' were found in ${projectRoot}`));
3215
+ return [];
3216
+ }
3217
+ s.message(`Found ${exampleFiles.length} template(s). Generating...`);
3218
+ const generatedFiles = [];
3219
+ for (const exampleFile of exampleFiles) {
3220
+ const sourcePath = path2.join(projectRoot, exampleFile);
3221
+ const targetFile = exampleFile.replace(/\.example$/, "");
3222
+ const targetPath = path2.join(projectRoot, targetFile);
3223
+ const content = fs11.readFileSync(sourcePath, "utf-8");
3224
+ const genFile = writeFileSafely(targetPath, content, {
3225
+ projectRoot,
3226
+ dryRun: Boolean(flags.dryRun),
3227
+ overwrite: Boolean(flags.overwrite)
3228
+ });
3229
+ generatedFiles.push(genFile);
3230
+ }
3231
+ s.stop("Environment files processed!");
3232
+ printGeneratedFiles(generatedFiles, Boolean(flags.dryRun));
3233
+ p16.outro(pc16.green("\u2713 Local environment files are ready!"));
3234
+ return generatedFiles;
3235
+ }
3236
+ async function runEnvExamplesCommand(flags = {}) {
3237
+ const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
3238
+ const s = p16.spinner();
3239
+ s.start("Scanning for local environment files...");
3240
+ const files = fs11.readdirSync(projectRoot, { withFileTypes: true });
3241
+ const envFiles = files.filter((dirent) => dirent.isFile() && dirent.name.startsWith(".env") && !dirent.name.endsWith(".example")).map((dirent) => dirent.name).sort();
3242
+ if (envFiles.length === 0) {
3243
+ s.stop("No local .env files found.");
3244
+ p16.log.info(pc16.yellow(`No files matching '.env*' were found in ${projectRoot}`));
3245
+ return [];
3246
+ }
3247
+ s.message(`Found ${envFiles.length} local .env file(s). Generating templates...`);
3248
+ const generatedFiles = [];
3249
+ for (const envFile of envFiles) {
3250
+ const sourcePath = path2.join(projectRoot, envFile);
3251
+ const targetFile = `${envFile}.example`;
3252
+ const targetPath = path2.join(projectRoot, targetFile);
3253
+ const content = fs11.readFileSync(sourcePath, "utf-8");
3254
+ const redactedContent = content.split(/\r?\n/).map((line) => {
3255
+ const trimmed = line.trim();
3256
+ if (trimmed.startsWith("#") || !trimmed) return line;
3257
+ const eqIndex = line.indexOf("=");
3258
+ if (eqIndex !== -1) {
3259
+ return line.substring(0, eqIndex + 1);
3260
+ }
3261
+ return line;
3262
+ }).join("\n");
3263
+ const genFile = writeFileSafely(targetPath, redactedContent, {
3264
+ projectRoot,
3265
+ dryRun: Boolean(flags.dryRun),
3266
+ overwrite: Boolean(flags.overwrite)
3267
+ });
3268
+ generatedFiles.push(genFile);
3269
+ }
3270
+ s.stop("Environment example templates generated!");
3271
+ printGeneratedFiles(generatedFiles, Boolean(flags.dryRun));
3272
+ p16.outro(pc16.green("\u2713 .env*.example files are up to date and safely redacted!"));
3273
+ return generatedFiles;
3274
+ }
3275
+
3276
+ // src/commands/menu.ts
3206
3277
  async function runInteractiveMenu(flags = {}) {
3207
3278
  printBanner();
3208
3279
  const choice = handleCancel(
3209
- await p14.select({
3280
+ await p16.select({
3210
3281
  message: "What would you like to create in your architecture?",
3211
3282
  options: [
3212
3283
  {
@@ -3248,6 +3319,16 @@ async function runInteractiveMenu(flags = {}) {
3248
3319
  value: "injectable",
3249
3320
  label: "Injectable Service",
3250
3321
  hint: "Generic @MakeInjectable class for services, transformers, or modules"
3322
+ },
3323
+ {
3324
+ value: "env",
3325
+ label: "Generate Env Files",
3326
+ hint: "Clone .env*.example files to local .env equivalents (Setup)"
3327
+ },
3328
+ {
3329
+ value: "envExamples",
3330
+ label: "Sync Env Examples",
3331
+ hint: "Safely clone and redact local .env files to .example templates (Sync)"
3251
3332
  }
3252
3333
  ]
3253
3334
  })
@@ -3277,6 +3358,12 @@ async function runInteractiveMenu(flags = {}) {
3277
3358
  case "injectable":
3278
3359
  await runInjectableCommand(void 0, flags);
3279
3360
  break;
3361
+ case "env":
3362
+ await runEnvCommand(flags);
3363
+ break;
3364
+ case "envExamples":
3365
+ await runEnvExamplesCommand({ ...flags, overwrite: flags.overwrite ?? true });
3366
+ break;
3280
3367
  }
3281
3368
  }
3282
3369
 
@@ -3314,8 +3401,8 @@ async function handleCreate(type, name, flags = {}) {
3314
3401
  await runInjectableCommand(name, flags);
3315
3402
  break;
3316
3403
  default:
3317
- p14.log.error(
3318
- pc14.red(
3404
+ p16.log.error(
3405
+ pc16.red(
3319
3406
  `Unknown component type: "${type}". Allowed types: feature, shared, usecase, domain, infrastructure, interface, httpHandler, injectable.`
3320
3407
  )
3321
3408
  );
@@ -3347,7 +3434,7 @@ function findLocalRegistry(startDir = process.cwd()) {
3347
3434
  let current = path2.resolve(startDir);
3348
3435
  while (true) {
3349
3436
  const candidate = path2.join(current, "mason-registry");
3350
- if (fs9.existsSync(candidate) && fs9.existsSync(path2.join(candidate, "index.json"))) {
3437
+ if (fs11.existsSync(candidate) && fs11.existsSync(path2.join(candidate, "index.json"))) {
3351
3438
  return candidate;
3352
3439
  }
3353
3440
  const parent = path2.dirname(current);
@@ -3369,9 +3456,9 @@ function getRegistryBase(projectRoot, override) {
3369
3456
  path2.join(process.cwd(), "mason.config.json")
3370
3457
  ];
3371
3458
  for (const configPath of configLocations) {
3372
- if (fs9.existsSync(configPath)) {
3459
+ if (fs11.existsSync(configPath)) {
3373
3460
  try {
3374
- const config = JSON.parse(fs9.readFileSync(configPath, "utf-8"));
3461
+ const config = JSON.parse(fs11.readFileSync(configPath, "utf-8"));
3375
3462
  if (config.registry && typeof config.registry === "string") {
3376
3463
  return normalizeRegistryUrl(config.registry.trim(), path2.dirname(configPath));
3377
3464
  }
@@ -3384,9 +3471,9 @@ function getRegistryBase(projectRoot, override) {
3384
3471
  path2.join(process.cwd(), "package.json")
3385
3472
  ];
3386
3473
  for (const pkgPath of pkgLocations) {
3387
- if (fs9.existsSync(pkgPath)) {
3474
+ if (fs11.existsSync(pkgPath)) {
3388
3475
  try {
3389
- const pkg = JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
3476
+ const pkg = JSON.parse(fs11.readFileSync(pkgPath, "utf-8"));
3390
3477
  if (pkg.mason?.registry && typeof pkg.mason.registry === "string") {
3391
3478
  return normalizeRegistryUrl(pkg.mason.registry.trim(), path2.dirname(pkgPath));
3392
3479
  }
@@ -3437,15 +3524,15 @@ async function fetchRegistryFile(registryBase, relPath) {
3437
3524
  baseDir = fileURLToPath(baseDir);
3438
3525
  }
3439
3526
  let filePath = path2.resolve(baseDir, cleanRelPath);
3440
- if (!fs9.existsSync(filePath)) {
3527
+ if (!fs11.existsSync(filePath)) {
3441
3528
  const inSrcPath = path2.resolve(baseDir, "src", cleanRelPath);
3442
- if (fs9.existsSync(inSrcPath)) {
3529
+ if (fs11.existsSync(inSrcPath)) {
3443
3530
  filePath = inSrcPath;
3444
3531
  } else {
3445
3532
  throw new Error(`Registry file not found at ${filePath}`);
3446
3533
  }
3447
3534
  }
3448
- return fs9.promises.readFile(filePath, "utf-8");
3535
+ return fs11.promises.readFile(filePath, "utf-8");
3449
3536
  }
3450
3537
  async function fetchRegistryIndex(registryBase) {
3451
3538
  const content = await fetchRegistryFile(registryBase, "index.json");
@@ -3507,26 +3594,26 @@ function findClosestMatch(target, candidates) {
3507
3594
  return null;
3508
3595
  }
3509
3596
  function detectPackageManager(projectRoot) {
3510
- if (fs9.existsSync(path2.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
3511
- if (fs9.existsSync(path2.join(projectRoot, "yarn.lock"))) return "yarn";
3512
- if (fs9.existsSync(path2.join(projectRoot, "bun.lockb")) || fs9.existsSync(path2.join(projectRoot, "bun.lock"))) return "bun";
3597
+ if (fs11.existsSync(path2.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
3598
+ if (fs11.existsSync(path2.join(projectRoot, "yarn.lock"))) return "yarn";
3599
+ if (fs11.existsSync(path2.join(projectRoot, "bun.lockb")) || fs11.existsSync(path2.join(projectRoot, "bun.lock"))) return "bun";
3513
3600
  return "npm";
3514
3601
  }
3515
3602
  function getMissingNpmPackages(projectRoot, requiredPackages) {
3516
3603
  if (!requiredPackages || requiredPackages.length === 0) return [];
3517
3604
  const pkgJsonPath = path2.join(projectRoot, "package.json");
3518
- if (!fs9.existsSync(pkgJsonPath)) {
3605
+ if (!fs11.existsSync(pkgJsonPath)) {
3519
3606
  return requiredPackages;
3520
3607
  }
3521
3608
  try {
3522
- const pkg = JSON.parse(fs9.readFileSync(pkgJsonPath, "utf-8"));
3609
+ const pkg = JSON.parse(fs11.readFileSync(pkgJsonPath, "utf-8"));
3523
3610
  const installed = /* @__PURE__ */ new Set([
3524
3611
  ...Object.keys(pkg.dependencies || {}),
3525
3612
  ...Object.keys(pkg.devDependencies || {}),
3526
3613
  ...Object.keys(pkg.peerDependencies || {})
3527
3614
  ]);
3528
- return requiredPackages.filter((p16) => {
3529
- const nameOnly = p16.startsWith("@") ? `@${p16.slice(1).split("@")[0] || ""}` : p16.split("@")[0] || p16;
3615
+ return requiredPackages.filter((p19) => {
3616
+ const nameOnly = p19.startsWith("@") ? `@${p19.slice(1).split("@")[0] || ""}` : p19.split("@")[0] || p19;
3530
3617
  return !installed.has(nameOnly);
3531
3618
  });
3532
3619
  } catch {
@@ -3551,15 +3638,15 @@ function installNpmPackages(projectRoot, packages, pm) {
3551
3638
  installCmd = `npm install ${packages.join(" ")}`;
3552
3639
  break;
3553
3640
  }
3554
- p14.log.info(`Installing missing dependencies with ${manager}: ${pc14.cyan(packages.join(", "))}`);
3641
+ p16.log.info(`Installing missing dependencies with ${manager}: ${pc16.cyan(packages.join(", "))}`);
3555
3642
  execSync(installCmd, {
3556
3643
  cwd: projectRoot,
3557
3644
  stdio: "inherit"
3558
3645
  });
3559
3646
  }
3560
3647
  function getAllFiles(dir, baseDir = dir) {
3561
- if (!fs9.existsSync(dir)) return [];
3562
- const entries = fs9.readdirSync(dir, { withFileTypes: true });
3648
+ if (!fs11.existsSync(dir)) return [];
3649
+ const entries = fs11.readdirSync(dir, { withFileTypes: true });
3563
3650
  const files = [];
3564
3651
  for (const entry of entries) {
3565
3652
  const fullPath = path2.join(dir, entry.name);
@@ -3579,13 +3666,13 @@ function getAllFiles(dir, baseDir = dir) {
3579
3666
  return files.sort();
3580
3667
  }
3581
3668
  function computeDirectoryHash(dirPath, files) {
3582
- if (!fs9.existsSync(dirPath)) return "";
3669
+ if (!fs11.existsSync(dirPath)) return "";
3583
3670
  const hash = createHash("sha256");
3584
3671
  const fileList = (getAllFiles(dirPath)).sort();
3585
3672
  for (const relativePath of fileList) {
3586
3673
  const absolutePath = path2.join(dirPath, relativePath);
3587
- if (!fs9.existsSync(absolutePath)) continue;
3588
- const content = fs9.readFileSync(absolutePath, "utf8");
3674
+ if (!fs11.existsSync(absolutePath)) continue;
3675
+ const content = fs11.readFileSync(absolutePath, "utf8");
3589
3676
  const normalizedContent = content.replace(/\r\n/g, "\n");
3590
3677
  hash.update(relativePath);
3591
3678
  hash.update(normalizedContent);
@@ -3595,9 +3682,9 @@ function computeDirectoryHash(dirPath, files) {
3595
3682
 
3596
3683
  // src/commands/import.ts
3597
3684
  var importPrompts = {
3598
- select: p14.select,
3599
- text: p14.text,
3600
- confirm: p14.confirm
3685
+ select: p16.select,
3686
+ text: p16.text,
3687
+ confirm: p16.confirm
3601
3688
  };
3602
3689
  function isTestFile(filePath) {
3603
3690
  const normalized = filePath.replace(/\\/g, "/");
@@ -3629,11 +3716,11 @@ function getExistingModuleDir(projectRoot, modType, modName) {
3629
3716
  const folder = modType === "shared" ? "shared" : "features";
3630
3717
  const configuredDir = modType === "shared" ? getConfiguredSharedDir(projectRoot) : getConfiguredFeaturesDir(projectRoot);
3631
3718
  const inConfigured = path2.join(configuredDir, modName);
3632
- if (fs9.existsSync(inConfigured)) return inConfigured;
3719
+ if (fs11.existsSync(inConfigured)) return inConfigured;
3633
3720
  const inSrc = path2.join(projectRoot, "src", folder, modName);
3634
- if (fs9.existsSync(inSrc)) return inSrc;
3721
+ if (fs11.existsSync(inSrc)) return inSrc;
3635
3722
  const inRoot = path2.join(projectRoot, folder, modName);
3636
- if (fs9.existsSync(inRoot)) return inRoot;
3723
+ if (fs11.existsSync(inRoot)) return inRoot;
3637
3724
  return null;
3638
3725
  }
3639
3726
  function checkModuleExists(projectRoot, modType, modName) {
@@ -3649,9 +3736,9 @@ function getAvailableLocalModules(projectRoot, modType) {
3649
3736
  ];
3650
3737
  const set = /* @__PURE__ */ new Set();
3651
3738
  for (const dir of dirs) {
3652
- if (fs9.existsSync(dir)) {
3739
+ if (fs11.existsSync(dir)) {
3653
3740
  try {
3654
- const entries = fs9.readdirSync(dir, { withFileTypes: true });
3741
+ const entries = fs11.readdirSync(dir, { withFileTypes: true });
3655
3742
  for (const e of entries) {
3656
3743
  if (e.isDirectory() && !e.name.startsWith(".")) {
3657
3744
  set.add(e.name);
@@ -3707,19 +3794,19 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3707
3794
  );
3708
3795
  }
3709
3796
  if (type !== "shared" && type !== "feature") {
3710
- p14.log.error(pc14.red(`Invalid module type: "${typeArg}". Must be "shared" or "feature".`));
3797
+ p16.log.error(pc16.red(`Invalid module type: "${typeArg}". Must be "shared" or "feature".`));
3711
3798
  process.exit(1);
3712
3799
  }
3713
3800
  const registryBase = getRegistryBase(projectRoot, flags.registry);
3714
- const s = p14.spinner();
3715
- s.start(`Connecting to registry at ${pc14.dim(registryBase)}...`);
3801
+ const s = p16.spinner();
3802
+ s.start(`Connecting to registry at ${pc16.dim(registryBase)}...`);
3716
3803
  let index;
3717
3804
  try {
3718
3805
  index = await fetchRegistryIndex(registryBase);
3719
3806
  s.stop("Registry index loaded.");
3720
3807
  } catch (err) {
3721
- s.stop(pc14.red("Failed to load registry index."));
3722
- p14.log.error(pc14.red(err.message || String(err)));
3808
+ s.stop(pc16.red("Failed to load registry index."));
3809
+ p16.log.error(pc16.red(err.message || String(err)));
3723
3810
  process.exit(1);
3724
3811
  }
3725
3812
  const availableList = type === "shared" ? index.shared : index.features;
@@ -3727,7 +3814,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3727
3814
  let name = nameArg?.trim();
3728
3815
  if (!name) {
3729
3816
  if (availableList.length === 0) {
3730
- p14.log.warn(`No ${type} modules available in the registry.`);
3817
+ p16.log.warn(`No ${type} modules available in the registry.`);
3731
3818
  return;
3732
3819
  }
3733
3820
  name = handleCancel(
@@ -3743,9 +3830,9 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3743
3830
  }
3744
3831
  if (!candidateNames.includes(name)) {
3745
3832
  const closest = findClosestMatch(name, candidateNames);
3746
- p14.log.error(pc14.red(`\u274C Module '${name}' not found in the ${type} registry.`));
3833
+ p16.log.error(pc16.red(`\u274C Module '${name}' not found in the ${type} registry.`));
3747
3834
  if (closest) {
3748
- p14.log.info(pc14.yellow(`Did you mean '${pc14.cyan(closest)}'?`));
3835
+ p16.log.info(pc16.yellow(`Did you mean '${pc16.cyan(closest)}'?`));
3749
3836
  }
3750
3837
  process.exit(1);
3751
3838
  }
@@ -3757,7 +3844,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3757
3844
  try {
3758
3845
  manifest = await fetchModuleManifest(registryBase, modType, modName);
3759
3846
  } catch (err) {
3760
- p14.log.error(pc14.red(`Failed to fetch manifest for ${modType} '${modName}': ${err.message}`));
3847
+ p16.log.error(pc16.red(`Failed to fetch manifest for ${modType} '${modName}': ${err.message}`));
3761
3848
  process.exit(1);
3762
3849
  }
3763
3850
  nodes.set(key, {
@@ -3797,7 +3884,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3797
3884
  buildTopologicalOrder(rootKey);
3798
3885
  const resolutions = /* @__PURE__ */ new Map();
3799
3886
  const globalAliasMap = /* @__PURE__ */ new Map();
3800
- p14.log.step(`Importing ${type}: ${pc14.cyan(name)}...`);
3887
+ p16.log.step(`Importing ${type}: ${pc16.cyan(name)}...`);
3801
3888
  for (const key of orderedKeys) {
3802
3889
  const node = nodes.get(key);
3803
3890
  const { type: modType, name: registryName, manifest } = node;
@@ -3829,8 +3916,8 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3829
3916
  const programmaticPointTo = pointToMap[registryName] || pointToMap[`${modType}/${registryName}`];
3830
3917
  if (programmaticPointTo) {
3831
3918
  if (!checkModuleExists(projectRoot, modType, programmaticPointTo)) {
3832
- p14.log.error(
3833
- pc14.red(
3919
+ p16.log.error(
3920
+ pc16.red(
3834
3921
  `Module '${programmaticPointTo}' specified in --point-to does not exist in ${folder}.`
3835
3922
  )
3836
3923
  );
@@ -3838,7 +3925,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3838
3925
  }
3839
3926
  globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${programmaticPointTo}/`);
3840
3927
  globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${programmaticPointTo}`);
3841
- p14.log.step(`Pointing '${modType}/${registryName}' to existing '${folder}/${programmaticPointTo}'`);
3928
+ p16.log.step(`Pointing '${modType}/${registryName}' to existing '${folder}/${programmaticPointTo}'`);
3842
3929
  resolutions.set(key, {
3843
3930
  type: modType,
3844
3931
  registryName,
@@ -3854,7 +3941,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3854
3941
  globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${programmaticAlias}`);
3855
3942
  const baseDir = getModuleBaseDir(projectRoot, modType);
3856
3943
  const displayDest = path2.relative(projectRoot, path2.join(baseDir, programmaticAlias));
3857
- p14.log.step(`Importing '${registryName}' into '${displayDest}'...`);
3944
+ p16.log.step(`Importing '${registryName}' into '${displayDest}'...`);
3858
3945
  resolutions.set(key, {
3859
3946
  type: modType,
3860
3947
  registryName,
@@ -3866,7 +3953,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3866
3953
  }
3867
3954
  const existingDir = getExistingModuleDir(projectRoot, modType, registryName);
3868
3955
  if (!existingDir) {
3869
- p14.log.info(` \u21B3 Resolving required ${modType} dependency: ${pc14.cyan(registryName)}`);
3956
+ p16.log.info(` \u21B3 Resolving required ${modType} dependency: ${pc16.cyan(registryName)}`);
3870
3957
  resolutions.set(key, {
3871
3958
  type: modType,
3872
3959
  registryName,
@@ -3882,7 +3969,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3882
3969
  const remoteIntegrity = manifest.integrity || entry?.integrity;
3883
3970
  const localHash = computeDirectoryHash(existingDir);
3884
3971
  if (remoteIntegrity && localHash === remoteIntegrity) {
3885
- p14.log.step(`\u2713 ${modType}/${registryName} is up to date (reusing existing)`);
3972
+ p16.log.step(`\u2713 ${modType}/${registryName} is up to date (reusing existing)`);
3886
3973
  resolutions.set(key, {
3887
3974
  type: modType,
3888
3975
  registryName,
@@ -3893,7 +3980,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3893
3980
  continue;
3894
3981
  }
3895
3982
  if (flags.overwrite) {
3896
- p14.log.step(`Overwriting '${modType}/${registryName}' with registry version (--overwrite)...`);
3983
+ p16.log.step(`Overwriting '${modType}/${registryName}' with registry version (--overwrite)...`);
3897
3984
  resolutions.set(key, {
3898
3985
  type: modType,
3899
3986
  registryName,
@@ -3904,7 +3991,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3904
3991
  continue;
3905
3992
  }
3906
3993
  if (flags.yes) {
3907
- p14.log.info(`Using existing '${modType}/${registryName}'`);
3994
+ p16.log.info(`Using existing '${modType}/${registryName}'`);
3908
3995
  resolutions.set(key, {
3909
3996
  type: modType,
3910
3997
  registryName,
@@ -3938,7 +4025,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3938
4025
  })
3939
4026
  );
3940
4027
  if (choice === "keep_existing" || choice === "use_existing") {
3941
- p14.log.step(`Skipping '${modType}/${registryName}' (keeping existing)`);
4028
+ p16.log.step(`Skipping '${modType}/${registryName}' (keeping existing)`);
3942
4029
  resolutions.set(key, {
3943
4030
  type: modType,
3944
4031
  registryName,
@@ -4010,7 +4097,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
4010
4097
  chosenName = chosenName.replace(/^@\/(shared|features)\//, "").replace(/^(src\/)?(shared|features)\//, "").replace(/^\/+|\/+$/g, "").trim();
4011
4098
  globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${chosenName}/`);
4012
4099
  globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${chosenName}`);
4013
- p14.log.step(`Pointing '${modType}/${registryName}' to existing '${folder}/${chosenName}'`);
4100
+ p16.log.step(`Pointing '${modType}/${registryName}' to existing '${folder}/${chosenName}'`);
4014
4101
  resolutions.set(key, {
4015
4102
  type: modType,
4016
4103
  registryName,
@@ -4042,7 +4129,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
4042
4129
  globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${newName}`);
4043
4130
  const baseDir = getModuleBaseDir(projectRoot, modType);
4044
4131
  const displayDest = path2.relative(projectRoot, path2.join(baseDir, newName));
4045
- p14.log.step(`Importing '${registryName}' into '${displayDest}'...`);
4132
+ p16.log.step(`Importing '${registryName}' into '${displayDest}'...`);
4046
4133
  resolutions.set(key, {
4047
4134
  type: modType,
4048
4135
  registryName,
@@ -4053,7 +4140,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
4053
4140
  continue;
4054
4141
  }
4055
4142
  if (choice === "overwrite") {
4056
- p14.log.step(`Overwriting '${modType}/${registryName}' with registry version...`);
4143
+ p16.log.step(`Overwriting '${modType}/${registryName}' with registry version...`);
4057
4144
  resolutions.set(key, {
4058
4145
  type: modType,
4059
4146
  registryName,
@@ -4096,17 +4183,17 @@ async function handleImport(typeArg, nameArg, flags = {}) {
4096
4183
  const targetDir = path2.join(getModuleBaseDir(projectRoot, res.type), res.targetName);
4097
4184
  const isOverwrite = Boolean(flags.overwrite || res.action === "overwrite");
4098
4185
  if (key !== rootKey) {
4099
- p14.log.step(
4100
- `Importing ${res.type}: ${pc14.cyan(res.targetName)}${res.targetName !== res.registryName ? pc14.dim(` (from ${res.registryName})`) : ""}...`
4186
+ p16.log.step(
4187
+ `Importing ${res.type}: ${pc16.cyan(res.targetName)}${res.targetName !== res.registryName ? pc16.dim(` (from ${res.registryName})`) : ""}...`
4101
4188
  );
4102
4189
  }
4103
4190
  if (res.action === "overwrite" && !flags.dryRun) {
4104
4191
  const existingDir = getExistingModuleDir(projectRoot, res.type, res.targetName);
4105
4192
  if (existingDir && existingDir !== targetDir) {
4106
- fs9.rmSync(existingDir, { recursive: true, force: true });
4193
+ fs11.rmSync(existingDir, { recursive: true, force: true });
4107
4194
  }
4108
- if (fs9.existsSync(targetDir)) {
4109
- fs9.rmSync(targetDir, { recursive: true, force: true });
4195
+ if (fs11.existsSync(targetDir)) {
4196
+ fs11.rmSync(targetDir, { recursive: true, force: true });
4110
4197
  }
4111
4198
  }
4112
4199
  for (const file of res.manifest.files || []) {
@@ -4114,32 +4201,32 @@ async function handleImport(typeArg, nameArg, flags = {}) {
4114
4201
  if (!includeTests && isTestFile(file)) continue;
4115
4202
  const remoteFilePath = `${folder}/${res.registryName}/${file}`;
4116
4203
  const localFilePath = path2.join(targetDir, file);
4117
- if (fs9.existsSync(localFilePath) && !isOverwrite) {
4118
- p14.log.warn(
4119
- ` \u21B3 Skipping existing file: ${pc14.dim(path2.relative(projectRoot, localFilePath))} (use --overwrite to replace)`
4204
+ if (fs11.existsSync(localFilePath) && !isOverwrite) {
4205
+ p16.log.warn(
4206
+ ` \u21B3 Skipping existing file: ${pc16.dim(path2.relative(projectRoot, localFilePath))} (use --overwrite to replace)`
4120
4207
  );
4121
4208
  continue;
4122
4209
  }
4123
4210
  let content = await fetchRegistryFile(registryBase, remoteFilePath);
4124
4211
  content = rewriteImports(content, globalAliasMap);
4125
4212
  if (!flags.dryRun) {
4126
- fs9.mkdirSync(path2.dirname(localFilePath), { recursive: true });
4127
- fs9.writeFileSync(localFilePath, content, "utf-8");
4213
+ fs11.mkdirSync(path2.dirname(localFilePath), { recursive: true });
4214
+ fs11.writeFileSync(localFilePath, content, "utf-8");
4128
4215
  }
4129
4216
  const displayPath = path2.relative(projectRoot, localFilePath);
4130
4217
  totalWrittenFiles.push(displayPath);
4131
- p14.log.message(` ${pc14.green("+")} ${displayPath}`);
4218
+ p16.log.message(` ${pc16.green("+")} ${displayPath}`);
4132
4219
  }
4133
4220
  }
4134
4221
  const missingNpm = getMissingNpmPackages(projectRoot, Array.from(requiredNpmPackages));
4135
4222
  if (missingNpm.length > 0) {
4136
4223
  if (flags.skipInstall || flags.dryRun) {
4137
- p14.log.info(`Required npm dependencies to install: ${pc14.cyan(missingNpm.join(", "))}`);
4224
+ p16.log.info(`Required npm dependencies to install: ${pc16.cyan(missingNpm.join(", "))}`);
4138
4225
  } else {
4139
4226
  try {
4140
4227
  installNpmPackages(projectRoot, missingNpm);
4141
4228
  } catch (err) {
4142
- p14.log.warn(
4229
+ p16.log.warn(
4143
4230
  `Could not automatically install packages (${err.message}). Please run: npm install ${missingNpm.join(
4144
4231
  " "
4145
4232
  )}`
@@ -4148,13 +4235,13 @@ async function handleImport(typeArg, nameArg, flags = {}) {
4148
4235
  }
4149
4236
  }
4150
4237
  if (flags.dryRun) {
4151
- p14.log.info(pc14.yellow("Dry run complete: No files were written to disk."));
4238
+ p16.log.info(pc16.yellow("Dry run complete: No files were written to disk."));
4152
4239
  } else {
4153
4240
  const rootRes = resolutions.get(rootKey);
4154
4241
  const baseDir = getModuleBaseDir(projectRoot, type);
4155
4242
  const displayTarget = path2.relative(projectRoot, path2.join(baseDir, rootRes.targetName));
4156
- p14.log.success(
4157
- pc14.green(
4243
+ p16.log.success(
4244
+ pc16.green(
4158
4245
  `Successfully imported ${type} '${name}' (${totalWrittenFiles.length} file${totalWrittenFiles.length === 1 ? "" : "s"} placed in ${displayTarget})`
4159
4246
  )
4160
4247
  );
@@ -4176,8 +4263,8 @@ function isTestFile2(filePath, projectRoot) {
4176
4263
  return base.endsWith(".test.ts") || base.endsWith(".test.js") || base.endsWith(".spec.ts") || base.endsWith(".spec.js") || base.endsWith(".d.ts") || norm.includes("/__tests__/") || norm.startsWith("__tests__/") || /(?:^|\/)tests?\//.test(norm);
4177
4264
  }
4178
4265
  function scanDirFiles(dir, projectRoot) {
4179
- if (!fs9.existsSync(dir)) return [];
4180
- const entries = fs9.readdirSync(dir, { withFileTypes: true });
4266
+ if (!fs11.existsSync(dir)) return [];
4267
+ const entries = fs11.readdirSync(dir, { withFileTypes: true });
4181
4268
  const files = [];
4182
4269
  for (const entry of entries) {
4183
4270
  if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "__tests__" || entry.name.startsWith(".")) {
@@ -4377,10 +4464,10 @@ var FeatureDependencyGraph = class {
4377
4464
  if (cycle) return cycle;
4378
4465
  } else if (inStack.has(next)) {
4379
4466
  const cycle = [next];
4380
- let p16 = curr;
4381
- while (p16 !== next) {
4382
- cycle.push(p16);
4383
- p16 = parent.get(p16) || next;
4467
+ let p19 = curr;
4468
+ while (p19 !== next) {
4469
+ cycle.push(p19);
4470
+ p19 = parent.get(p19) || next;
4384
4471
  }
4385
4472
  cycle.push(next);
4386
4473
  cycle.reverse();
@@ -4396,8 +4483,8 @@ var FeatureDependencyGraph = class {
4396
4483
  };
4397
4484
  function discoverAllFeatures(projectRoot) {
4398
4485
  const featuresBase = resolveFeaturesDir(projectRoot);
4399
- if (!fs9.existsSync(featuresBase)) return [];
4400
- const entries = fs9.readdirSync(featuresBase, { withFileTypes: true });
4486
+ if (!fs11.existsSync(featuresBase)) return [];
4487
+ const entries = fs11.readdirSync(featuresBase, { withFileTypes: true });
4401
4488
  return entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "__tests__" && e.name !== "tests").map((e) => ({
4402
4489
  name: e.name,
4403
4490
  dir: path2.join(featuresBase, e.name)
@@ -4788,7 +4875,7 @@ Code: ${v.code}`;
4788
4875
  const relPath = path2.relative(projectRoot, file).replace(/\\/g, "/");
4789
4876
  const { layer } = classifyFile(file, projectRoot);
4790
4877
  if (!layer) continue;
4791
- const content = fs9.readFileSync(file, "utf-8");
4878
+ const content = fs11.readFileSync(file, "utf-8");
4792
4879
  const statements = extractImportStatements(content);
4793
4880
  const baseName = path2.basename(file);
4794
4881
  const isBarrel = baseName === "index.ts" || baseName === "index.js";
@@ -5282,15 +5369,15 @@ ${compiledDetails}
5282
5369
  // src/commands/lint.ts
5283
5370
  function formatViolation(v) {
5284
5371
  const lines = [];
5285
- lines.push(pc14.underline(pc14.cyan(v.relativePath)) + pc14.dim(":") + pc14.yellow(String(v.line)));
5372
+ lines.push(pc16.underline(pc16.cyan(v.relativePath)) + pc16.dim(":") + pc16.yellow(String(v.line)));
5286
5373
  const gutter = `${String(v.line).padStart(4, " ")} | `;
5287
- lines.push(pc14.dim(gutter) + v.code);
5374
+ lines.push(pc16.dim(gutter) + v.code);
5288
5375
  const categoryShort = v.category.replace(" Violation", "");
5289
5376
  lines.push(
5290
- " " + pc14.red(pc14.bold("\u2716")) + " " + pc14.red(`[${categoryShort}]`) + " " + v.advice
5377
+ " " + pc16.red(pc16.bold("\u2716")) + " " + pc16.red(`[${categoryShort}]`) + " " + v.advice
5291
5378
  );
5292
5379
  if (v.fixExample?.good) {
5293
- lines.push(" " + pc14.dim("\u2192") + " " + pc14.dim(v.fixExample.good));
5380
+ lines.push(" " + pc16.dim("\u2192") + " " + pc16.dim(v.fixExample.good));
5294
5381
  }
5295
5382
  return lines.join("\n");
5296
5383
  }
@@ -5315,7 +5402,7 @@ async function handleLint(flags = {}) {
5315
5402
  console.log("");
5316
5403
  }
5317
5404
  console.log(
5318
- pc14.red(
5405
+ pc16.red(
5319
5406
  `\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).`
5320
5407
  )
5321
5408
  );
@@ -5334,7 +5421,7 @@ ${compiledDetails}
5334
5421
  }
5335
5422
  if (!quiet) {
5336
5423
  console.log(
5337
- pc14.green(
5424
+ pc16.green(
5338
5425
  `\u2714 Dependency rules verified across ${summary.totalFeatures} feature${summary.totalFeatures === 1 ? "" : "s"}.`
5339
5426
  )
5340
5427
  );
@@ -5349,21 +5436,64 @@ ${compiledDetails}
5349
5436
  console.log(formatViolation(v));
5350
5437
  console.log("");
5351
5438
  }
5352
- console.log(pc14.red(`\u2716 Dependency rules check failed (1 error).`));
5439
+ console.log(pc16.red(`\u2716 Dependency rules check failed (1 error).`));
5353
5440
  console.log("");
5354
5441
  }
5355
5442
  }
5356
5443
  throw err;
5357
5444
  }
5358
5445
  }
5446
+ async function handleInit(type, flags = {}) {
5447
+ if (!type) {
5448
+ const choice = handleCancel(
5449
+ await p16.select({
5450
+ message: "What would you like to initialize?",
5451
+ options: [
5452
+ {
5453
+ value: "env",
5454
+ label: "Environment Files",
5455
+ hint: "Generate local .env files from .env*.example templates"
5456
+ },
5457
+ {
5458
+ value: "env-examples",
5459
+ label: "Sync Env Examples",
5460
+ hint: "Safely clone and redact local .env files to .example templates"
5461
+ }
5462
+ ]
5463
+ })
5464
+ );
5465
+ if (choice === "env") {
5466
+ await runEnvCommand(flags);
5467
+ } else if (choice === "env-examples") {
5468
+ await runEnvExamplesCommand(flags);
5469
+ }
5470
+ return;
5471
+ }
5472
+ const normalized = type.trim().toLowerCase();
5473
+ if (normalized === "env") {
5474
+ await runEnvCommand(flags);
5475
+ } else if (normalized === "env-examples" || normalized === "envexamples") {
5476
+ await runEnvExamplesCommand(flags);
5477
+ } else {
5478
+ p16.log.error(
5479
+ pc16.red(
5480
+ `Unknown init target: "${type}". Allowed targets: env, env-examples.`
5481
+ )
5482
+ );
5483
+ if (process.env.VITEST) {
5484
+ throw new Error(`Unknown init target: "${type}". Allowed targets: env, env-examples.`);
5485
+ }
5486
+ process.exit(1);
5487
+ }
5488
+ }
5359
5489
 
5360
5490
  // src/cli.ts
5361
5491
  function createCli() {
5362
5492
  const cli = cac("mason");
5363
5493
  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) => {
5364
5494
  if (args && args.length > 0) {
5365
- p14.log.error(
5366
- pc14.red(
5495
+ p16.log.error(
5496
+ pc16.red(
5367
5497
  `Unknown command: "${args.join(" ")}". Use --help to list available commands.`
5368
5498
  )
5369
5499
  );
@@ -5374,7 +5504,7 @@ function createCli() {
5374
5504
  const mode = getEffectiveMode(projectRoot, flags);
5375
5505
  if (mode === "registry") {
5376
5506
  const choice2 = handleCancel(
5377
- await p14.select({
5507
+ await p16.select({
5378
5508
  message: "\u{1F3DB}\uFE0F Mason (Registry Mode): What would you like to scaffold?",
5379
5509
  options: [
5380
5510
  {
@@ -5417,7 +5547,7 @@ function createCli() {
5417
5547
  return;
5418
5548
  }
5419
5549
  const choice = handleCancel(
5420
- await p14.select({
5550
+ await p16.select({
5421
5551
  message: "\u{1F9F1} Mason: What would you like to do?",
5422
5552
  options: [
5423
5553
  {
@@ -5434,6 +5564,11 @@ function createCli() {
5434
5564
  value: "lint",
5435
5565
  label: "Lint dependency rules",
5436
5566
  hint: "Scan codebase and verify Clean Architecture dependency rules"
5567
+ },
5568
+ {
5569
+ value: "init",
5570
+ label: "Initialize environment",
5571
+ hint: "Generate local .env files from .env*.example templates"
5437
5572
  }
5438
5573
  ]
5439
5574
  })
@@ -5444,6 +5579,8 @@ function createCli() {
5444
5579
  await handleImport(void 0, void 0, flags);
5445
5580
  } else if (choice === "lint") {
5446
5581
  await handleLint(flags);
5582
+ } else if (choice === "init") {
5583
+ await handleInit("env", flags);
5447
5584
  }
5448
5585
  } catch (err) {
5449
5586
  handleCliError(err);
@@ -5491,6 +5628,26 @@ function createCli() {
5491
5628
  handleCliError(err);
5492
5629
  }
5493
5630
  });
5631
+ cli.command(
5632
+ "init [type]",
5633
+ "Initialize project configurations and resources (e.g. 'mason init env', 'mason init env-examples')"
5634
+ ).option("-d, --dry-run", "Preview changes without writing to disk").option("-o, --overwrite", "Overwrite existing files").example("mason init env").example("mason init env --overwrite").example("mason init env --dry-run").example("mason init env-examples").example("mason init env-examples --overwrite").example("mason init env-examples --dry-run").action(async (type, flags = {}) => {
5635
+ try {
5636
+ await handleInit(type, flags);
5637
+ } catch (err) {
5638
+ handleCliError(err);
5639
+ }
5640
+ });
5641
+ cli.command(
5642
+ "env-examples",
5643
+ "Shorthand for 'mason init env-examples'"
5644
+ ).option("-d, --dry-run", "Preview changes without writing to disk").option("-o, --overwrite", "Overwrite existing .env*.example files").action(async (flags = {}) => {
5645
+ try {
5646
+ await handleInit("env-examples", flags);
5647
+ } catch (err) {
5648
+ handleCliError(err);
5649
+ }
5650
+ });
5494
5651
  const createSubtypes = [
5495
5652
  "feature",
5496
5653
  "shared",
@@ -5522,7 +5679,7 @@ function handleCliError(err) {
5522
5679
  process.exit(0);
5523
5680
  }
5524
5681
  const message = err instanceof Error ? err.message : String(err);
5525
- p14.log.error(pc14.red(`Error: ${message}`));
5682
+ p16.log.error(pc16.red(`Error: ${message}`));
5526
5683
  if (process.env.VITEST) {
5527
5684
  throw err;
5528
5685
  }