@solid-stack/mason 1.0.12 → 1.0.14

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.cjs CHANGED
@@ -3,12 +3,13 @@
3
3
 
4
4
  var cac = require('cac');
5
5
  var p3 = require('@clack/prompts');
6
- var pc13 = require('picocolors');
7
- var path15 = require('path');
8
- var fs7 = require('fs');
6
+ var pc14 = require('picocolors');
7
+ var fs9 = require('fs');
8
+ var path2 = require('path');
9
9
  var url = require('url');
10
10
  var Handlebars = require('handlebars');
11
11
  var child_process = require('child_process');
12
+ var crypto = require('crypto');
12
13
 
13
14
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
14
15
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -32,15 +33,15 @@ function _interopNamespace(e) {
32
33
  }
33
34
 
34
35
  var p3__namespace = /*#__PURE__*/_interopNamespace(p3);
35
- var pc13__default = /*#__PURE__*/_interopDefault(pc13);
36
- var path15__default = /*#__PURE__*/_interopDefault(path15);
37
- var fs7__default = /*#__PURE__*/_interopDefault(fs7);
36
+ var pc14__default = /*#__PURE__*/_interopDefault(pc14);
37
+ var fs9__default = /*#__PURE__*/_interopDefault(fs9);
38
+ var path2__default = /*#__PURE__*/_interopDefault(path2);
38
39
  var Handlebars__default = /*#__PURE__*/_interopDefault(Handlebars);
39
40
 
40
41
  function writeFileSafely(absolutePath, content, options = {}) {
41
42
  const projectRoot = options.projectRoot || process.cwd();
42
- const relPath = path15__default.default.relative(projectRoot, absolutePath).replace(/\\/g, "/");
43
- const existedBefore = fs7__default.default.existsSync(absolutePath);
43
+ const relPath = path2__default.default.relative(projectRoot, absolutePath).replace(/\\/g, "/");
44
+ const existedBefore = fs9__default.default.existsSync(absolutePath);
44
45
  if (options.dryRun) {
45
46
  return {
46
47
  relativePath: relPath,
@@ -59,11 +60,11 @@ function writeFileSafely(absolutePath, content, options = {}) {
59
60
  existedBefore: true
60
61
  };
61
62
  }
62
- const dir = path15__default.default.dirname(absolutePath);
63
- if (!fs7__default.default.existsSync(dir)) {
64
- fs7__default.default.mkdirSync(dir, { recursive: true });
63
+ const dir = path2__default.default.dirname(absolutePath);
64
+ if (!fs9__default.default.existsSync(dir)) {
65
+ fs9__default.default.mkdirSync(dir, { recursive: true });
65
66
  }
66
- fs7__default.default.writeFileSync(absolutePath, content, "utf-8");
67
+ fs9__default.default.writeFileSync(absolutePath, content, "utf-8");
67
68
  return {
68
69
  relativePath: relPath,
69
70
  absolutePath,
@@ -75,17 +76,17 @@ function writeFileSafely(absolutePath, content, options = {}) {
75
76
  function printGeneratedFiles(files, isDryRun = false) {
76
77
  console.log("");
77
78
  if (isDryRun) {
78
- p3__namespace.log.warn(pc13__default.default.yellow(pc13__default.default.bold("[DRY RUN PREVIEW] No files were written to disk.")));
79
+ p3__namespace.log.warn(pc14__default.default.yellow(pc14__default.default.bold("[DRY RUN PREVIEW] No files were written to disk.")));
79
80
  }
80
81
  for (const f of files) {
81
82
  if (f.action === "created") {
82
- p3__namespace.log.success(`${pc13__default.default.bold(f.relativePath)} ${pc13__default.default.dim("(created)")}`);
83
+ p3__namespace.log.success(`${pc14__default.default.bold(f.relativePath)} ${pc14__default.default.dim("(created)")}`);
83
84
  } else if (f.action === "overwritten") {
84
- p3__namespace.log.warn(`${pc13__default.default.bold(f.relativePath)} ${pc13__default.default.dim("(overwritten)")}`);
85
+ p3__namespace.log.warn(`${pc14__default.default.bold(f.relativePath)} ${pc14__default.default.dim("(overwritten)")}`);
85
86
  } else if (f.action === "skipped") {
86
- p3__namespace.log.info(`${pc13__default.default.dim(f.relativePath)} ${pc13__default.default.dim("(skipped - already exists, pass -o to overwrite)")}`);
87
+ p3__namespace.log.info(`${pc14__default.default.dim(f.relativePath)} ${pc14__default.default.dim("(skipped - already exists, pass -o to overwrite)")}`);
87
88
  } else {
88
- p3__namespace.log.info(`${pc13__default.default.bold(f.relativePath)} ${pc13__default.default.dim("(preview)")}`);
89
+ p3__namespace.log.info(`${pc14__default.default.bold(f.relativePath)} ${pc14__default.default.dim("(preview)")}`);
89
90
  }
90
91
  }
91
92
  }
@@ -153,27 +154,90 @@ function toStubName(ifaceName) {
153
154
  }
154
155
  return `Stub${base}`;
155
156
  }
157
+ function loadMasonConfig(startDir = process.cwd()) {
158
+ let current = path2__default.default.resolve(startDir);
159
+ while (true) {
160
+ const configPath = path2__default.default.join(current, "mason.config.json");
161
+ if (fs9__default.default.existsSync(configPath)) {
162
+ try {
163
+ return JSON.parse(fs9__default.default.readFileSync(configPath, "utf-8"));
164
+ } catch {
165
+ return null;
166
+ }
167
+ }
168
+ const parent = path2__default.default.dirname(current);
169
+ if (parent === current) break;
170
+ current = parent;
171
+ }
172
+ return null;
173
+ }
174
+ function getEffectiveMode(projectRoot, flags) {
175
+ if (flags?.registry) return "registry";
176
+ const config = loadMasonConfig(projectRoot);
177
+ if (config?.mode) return config.mode;
178
+ if (fs9__default.default.existsSync(path2__default.default.join(projectRoot, "index.json")) && (fs9__default.default.existsSync(path2__default.default.join(projectRoot, "features")) || fs9__default.default.existsSync(path2__default.default.join(projectRoot, "shared"))) && !fs9__default.default.existsSync(path2__default.default.join(projectRoot, "src"))) {
179
+ return "registry";
180
+ }
181
+ return "app";
182
+ }
183
+ function getConfiguredFeaturesDir(projectRoot, flags) {
184
+ if (flags?.target) return path2__default.default.resolve(projectRoot, flags.target);
185
+ if (flags?.dest) return path2__default.default.resolve(projectRoot, flags.dest);
186
+ const config = loadMasonConfig(projectRoot);
187
+ if (config?.paths?.features) {
188
+ return path2__default.default.resolve(projectRoot, config.paths.features);
189
+ }
190
+ const mode = getEffectiveMode(projectRoot, flags);
191
+ if (mode === "registry") {
192
+ return path2__default.default.join(projectRoot, "features");
193
+ }
194
+ if (fs9__default.default.existsSync(path2__default.default.join(projectRoot, "src/features"))) {
195
+ return path2__default.default.join(projectRoot, "src/features");
196
+ }
197
+ return path2__default.default.join(projectRoot, "features");
198
+ }
199
+ function getConfiguredSharedDir(projectRoot, flags) {
200
+ if (flags?.target) return path2__default.default.resolve(projectRoot, flags.target);
201
+ if (flags?.dest) return path2__default.default.resolve(projectRoot, flags.dest);
202
+ const config = loadMasonConfig(projectRoot);
203
+ if (config?.paths?.shared) {
204
+ return path2__default.default.resolve(projectRoot, config.paths.shared);
205
+ }
206
+ const mode = getEffectiveMode(projectRoot, flags);
207
+ if (mode === "registry") {
208
+ return path2__default.default.join(projectRoot, "shared");
209
+ }
210
+ if (fs9__default.default.existsSync(path2__default.default.join(projectRoot, "src/shared"))) {
211
+ return path2__default.default.join(projectRoot, "src/shared");
212
+ }
213
+ return path2__default.default.join(projectRoot, "shared");
214
+ }
215
+
216
+ // src/utils/project.ts
156
217
  function findProjectRoot(startDir = process.cwd()) {
157
- let current = path15__default.default.resolve(startDir);
218
+ let current = path2__default.default.resolve(startDir);
158
219
  while (true) {
159
- if (fs7__default.default.existsSync(path15__default.default.join(current, "package.json"))) {
220
+ if (fs9__default.default.existsSync(path2__default.default.join(current, "package.json"))) {
160
221
  return current;
161
222
  }
162
- const parent = path15__default.default.dirname(current);
223
+ const parent = path2__default.default.dirname(current);
163
224
  if (parent === current) {
164
- return path15__default.default.resolve(startDir);
225
+ return path2__default.default.resolve(startDir);
165
226
  }
166
227
  current = parent;
167
228
  }
168
229
  }
169
- function resolveFeaturesDir(projectRoot) {
170
- return path15__default.default.join(projectRoot, "features");
230
+ function resolveFeaturesDir(projectRoot, flags) {
231
+ return getConfiguredFeaturesDir(projectRoot, flags);
232
+ }
233
+ function resolveSharedDir(projectRoot, flags) {
234
+ return getConfiguredSharedDir(projectRoot, flags);
171
235
  }
172
236
  function listExistingFeatures(projectRoot) {
173
237
  const featuresDir = resolveFeaturesDir(projectRoot);
174
- if (!fs7__default.default.existsSync(featuresDir)) return [];
238
+ if (!fs9__default.default.existsSync(featuresDir)) return [];
175
239
  try {
176
- const entries = fs7__default.default.readdirSync(featuresDir, { withFileTypes: true });
240
+ const entries = fs9__default.default.readdirSync(featuresDir, { withFileTypes: true });
177
241
  return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
178
242
  } catch {
179
243
  return [];
@@ -183,14 +247,14 @@ function listDomainInterfaces(projectRoot, featureName) {
183
247
  const results = [];
184
248
  const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
185
249
  for (const feat of features) {
186
- const domainDir = path15__default.default.join(resolveFeaturesDir(projectRoot), feat, "domain");
187
- if (!fs7__default.default.existsSync(domainDir)) continue;
250
+ const domainDir = path2__default.default.join(resolveFeaturesDir(projectRoot), feat, "domain");
251
+ if (!fs9__default.default.existsSync(domainDir)) continue;
188
252
  try {
189
- const files = fs7__default.default.readdirSync(domainDir);
253
+ const files = fs9__default.default.readdirSync(domainDir);
190
254
  for (const file of files) {
191
255
  if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
192
256
  const name = file.replace(/\.ts$/, "");
193
- const fullPath = path15__default.default.join(domainDir, file);
257
+ const fullPath = path2__default.default.join(domainDir, file);
194
258
  if (/^I[A-Z]/.test(name)) {
195
259
  results.push({
196
260
  name,
@@ -199,7 +263,7 @@ function listDomainInterfaces(projectRoot, featureName) {
199
263
  });
200
264
  } else {
201
265
  try {
202
- const content = fs7__default.default.readFileSync(fullPath, "utf-8");
266
+ const content = fs9__default.default.readFileSync(fullPath, "utf-8");
203
267
  if (/export\s+(?:abstract\s+class|interface)\s+(\w+)/.test(content)) {
204
268
  results.push({
205
269
  name,
@@ -221,17 +285,17 @@ function listDomainEntities(projectRoot, featureName) {
221
285
  const results = [];
222
286
  const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
223
287
  for (const feat of features) {
224
- const domainDir = path15__default.default.join(resolveFeaturesDir(projectRoot), feat, "domain");
225
- if (!fs7__default.default.existsSync(domainDir)) continue;
288
+ const domainDir = path2__default.default.join(resolveFeaturesDir(projectRoot), feat, "domain");
289
+ if (!fs9__default.default.existsSync(domainDir)) continue;
226
290
  try {
227
- const files = fs7__default.default.readdirSync(domainDir);
291
+ const files = fs9__default.default.readdirSync(domainDir);
228
292
  for (const file of files) {
229
293
  if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
230
294
  const name = file.replace(/\.ts$/, "");
231
295
  if (!/^I[A-Z]/.test(name)) {
232
296
  results.push({
233
297
  name,
234
- fullPath: path15__default.default.join(domainDir, file),
298
+ fullPath: path2__default.default.join(domainDir, file),
235
299
  feature: feat
236
300
  });
237
301
  }
@@ -246,16 +310,16 @@ function listUseCases(projectRoot, featureName) {
246
310
  const results = [];
247
311
  const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
248
312
  for (const feat of features) {
249
- const useCasesDir = path15__default.default.join(resolveFeaturesDir(projectRoot), feat, "useCases");
250
- if (!fs7__default.default.existsSync(useCasesDir)) continue;
313
+ const useCasesDir = path2__default.default.join(resolveFeaturesDir(projectRoot), feat, "useCases");
314
+ if (!fs9__default.default.existsSync(useCasesDir)) continue;
251
315
  try {
252
- const files = fs7__default.default.readdirSync(useCasesDir);
316
+ const files = fs9__default.default.readdirSync(useCasesDir);
253
317
  for (const file of files) {
254
318
  if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
255
319
  const name = file.replace(/\.ts$/, "");
256
320
  results.push({
257
321
  name,
258
- fullPath: path15__default.default.join(useCasesDir, file),
322
+ fullPath: path2__default.default.join(useCasesDir, file),
259
323
  feature: feat
260
324
  });
261
325
  }
@@ -281,33 +345,33 @@ hbs.registerHelper("join", (arr, sep) => {
281
345
  });
282
346
  var cachedTemplatesDir = null;
283
347
  function getTemplatesDir() {
284
- if (cachedTemplatesDir && fs7__default.default.existsSync(cachedTemplatesDir)) {
348
+ if (cachedTemplatesDir && fs9__default.default.existsSync(cachedTemplatesDir)) {
285
349
  return cachedTemplatesDir;
286
350
  }
287
- const currentDir = typeof __dirname !== "undefined" ? __dirname : path15__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('bin.cjs', document.baseURI).href))));
288
- const cand1 = path15__default.default.resolve(currentDir, "../../templates");
289
- if (fs7__default.default.existsSync(cand1)) {
351
+ const currentDir = typeof __dirname !== "undefined" ? __dirname : path2__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('bin.cjs', document.baseURI).href))));
352
+ const cand1 = path2__default.default.resolve(currentDir, "../../templates");
353
+ if (fs9__default.default.existsSync(cand1)) {
290
354
  cachedTemplatesDir = cand1;
291
355
  return cand1;
292
356
  }
293
- const cand2 = path15__default.default.resolve(currentDir, "../templates");
294
- if (fs7__default.default.existsSync(cand2)) {
357
+ const cand2 = path2__default.default.resolve(currentDir, "../templates");
358
+ if (fs9__default.default.existsSync(cand2)) {
295
359
  cachedTemplatesDir = cand2;
296
360
  return cand2;
297
361
  }
298
- const cand3 = path15__default.default.resolve(currentDir, "templates");
299
- if (fs7__default.default.existsSync(cand3)) {
362
+ const cand3 = path2__default.default.resolve(currentDir, "templates");
363
+ if (fs9__default.default.existsSync(cand3)) {
300
364
  cachedTemplatesDir = cand3;
301
365
  return cand3;
302
366
  }
303
367
  let curr = currentDir;
304
368
  while (true) {
305
- const candTemplates = path15__default.default.join(curr, "templates");
306
- if (fs7__default.default.existsSync(candTemplates)) {
369
+ const candTemplates = path2__default.default.join(curr, "templates");
370
+ if (fs9__default.default.existsSync(candTemplates)) {
307
371
  cachedTemplatesDir = candTemplates;
308
372
  return candTemplates;
309
373
  }
310
- const parent = path15__default.default.dirname(curr);
374
+ const parent = path2__default.default.dirname(curr);
311
375
  if (parent === curr) break;
312
376
  curr = parent;
313
377
  }
@@ -315,8 +379,8 @@ function getTemplatesDir() {
315
379
  }
316
380
  var templateCache = /* @__PURE__ */ new Map();
317
381
  function loadTemplate(relativePath) {
318
- const fullPath = path15__default.default.isAbsolute(relativePath) ? relativePath : path15__default.default.join(getTemplatesDir(), relativePath);
319
- return fs7__default.default.readFileSync(fullPath, "utf-8");
382
+ const fullPath = path2__default.default.isAbsolute(relativePath) ? relativePath : path2__default.default.join(getTemplatesDir(), relativePath);
383
+ return fs9__default.default.readFileSync(fullPath, "utf-8");
320
384
  }
321
385
  function renderTemplateFile(relativePath, context) {
322
386
  let compiled = templateCache.get(relativePath);
@@ -403,8 +467,8 @@ function generateDomainEntityCode(name, kind = "interface", properties = []) {
403
467
  function generateDomain(options) {
404
468
  const projectRoot = options.projectRoot || findProjectRoot();
405
469
  const entityName = toPascalCase(options.name);
406
- const targetDir = options.targetDir || path15__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
407
- const filePath = path15__default.default.join(targetDir, `${entityName}.ts`);
470
+ const targetDir = options.targetDir || path2__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
471
+ const filePath = path2__default.default.join(targetDir, `${entityName}.ts`);
408
472
  const code = generateDomainEntityCode(
409
473
  entityName,
410
474
  options.kind || "interface",
@@ -428,7 +492,7 @@ function generateInterfaceCode(ifaceName, kind = "abstractClass", entityName, me
428
492
  const idKey = `${entityKey}Id`;
429
493
  const listKey = `${entityKey}s`;
430
494
  const formattedCustomMethods = customMethods.map((m) => {
431
- const params = m.params && m.params.length > 0 ? m.params.map((p15) => `${p15.name}: ${p15.type}`).join("; ") : "";
495
+ const params = m.params && m.params.length > 0 ? m.params.map((p16) => `${p16.name}: ${p16.type}`).join("; ") : "";
432
496
  const returnType = m.returnType ? `Promise<${m.returnType}>` : "Promise<void>";
433
497
  return {
434
498
  name: m.name,
@@ -453,8 +517,8 @@ function generateInterfaceCode(ifaceName, kind = "abstractClass", entityName, me
453
517
  function generateInterface(options) {
454
518
  const projectRoot = options.projectRoot || findProjectRoot();
455
519
  const ifaceName = ensureInterfacePrefix(options.name);
456
- const targetDir = options.targetDir || path15__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
457
- const filePath = path15__default.default.join(targetDir, `${ifaceName}.ts`);
520
+ const targetDir = options.targetDir || path2__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
521
+ const filePath = path2__default.default.join(targetDir, `${ifaceName}.ts`);
458
522
  const code = generateInterfaceCode(
459
523
  ifaceName,
460
524
  options.kind || "abstractClass",
@@ -689,7 +753,7 @@ function parseInterfaceMethodsAndImports(content, targetIfaceName) {
689
753
  };
690
754
  }
691
755
  function findInterfaceFile(projectRoot, ifaceName, featureName, customPath) {
692
- if (customPath && fs7__default.default.existsSync(customPath)) {
756
+ if (customPath && fs9__default.default.existsSync(customPath)) {
693
757
  return { filePath: customPath, ifaceName };
694
758
  }
695
759
  const candidateNames = [
@@ -699,22 +763,22 @@ function findInterfaceFile(projectRoot, ifaceName, featureName, customPath) {
699
763
  ];
700
764
  const features = featureName ? [featureName, ...listExistingFeatures(projectRoot).filter((f) => f !== featureName)] : listExistingFeatures(projectRoot);
701
765
  for (const feat of features) {
702
- const domainDir = path15__default.default.join(resolveFeaturesDir(projectRoot), feat, "domain");
703
- if (!fs7__default.default.existsSync(domainDir)) continue;
766
+ const domainDir = path2__default.default.join(resolveFeaturesDir(projectRoot), feat, "domain");
767
+ if (!fs9__default.default.existsSync(domainDir)) continue;
704
768
  for (const cand of candidateNames) {
705
- const p1 = path15__default.default.join(domainDir, `${cand}.ts`);
706
- if (fs7__default.default.existsSync(p1)) {
769
+ const p1 = path2__default.default.join(domainDir, `${cand}.ts`);
770
+ if (fs9__default.default.existsSync(p1)) {
707
771
  return { filePath: p1, ifaceName: cand, feature: feat };
708
772
  }
709
773
  }
710
774
  }
711
775
  for (const cand of candidateNames) {
712
- const p1 = path15__default.default.join(projectRoot, "domain", `${cand}.ts`);
713
- if (fs7__default.default.existsSync(p1)) return { filePath: p1, ifaceName: cand };
714
- const p22 = path15__default.default.join(projectRoot, "src", "domain", `${cand}.ts`);
715
- if (fs7__default.default.existsSync(p22)) return { filePath: p22, ifaceName: cand };
716
- const p32 = path15__default.default.join(projectRoot, "interfaces", `${cand}.ts`);
717
- if (fs7__default.default.existsSync(p32)) return { filePath: p32, ifaceName: cand };
776
+ const p1 = path2__default.default.join(projectRoot, "domain", `${cand}.ts`);
777
+ if (fs9__default.default.existsSync(p1)) return { filePath: p1, ifaceName: cand };
778
+ const p22 = path2__default.default.join(projectRoot, "src", "domain", `${cand}.ts`);
779
+ if (fs9__default.default.existsSync(p22)) return { filePath: p22, ifaceName: cand };
780
+ const p32 = path2__default.default.join(projectRoot, "interfaces", `${cand}.ts`);
781
+ if (fs9__default.default.existsSync(p32)) return { filePath: p32, ifaceName: cand };
718
782
  }
719
783
  return null;
720
784
  }
@@ -737,7 +801,7 @@ function loadInterfaceHierarchy(projectRoot, initialIfaceName, featureName, cust
737
801
  while (currentFile && !visited.has(currentFile)) {
738
802
  visited.add(currentFile);
739
803
  try {
740
- const content = fs7__default.default.readFileSync(currentFile, "utf-8");
804
+ const content = fs9__default.default.readFileSync(currentFile, "utf-8");
741
805
  const parsed = parseInterfaceMethodsAndImports(content, currentName);
742
806
  for (const m of parsed.methods) {
743
807
  if (!allMethods.some((existing) => existing.name === m.name)) {
@@ -813,7 +877,7 @@ function generateStubCode(stubClassName, ifaceName, entityName, featureName, met
813
877
  const entitiesVar = `${entityVar}s`;
814
878
  const idKey = `${entityVar}Id`;
815
879
  const impPath = ifaceImportPath || `../domain/${ifaceName}.js`;
816
- const entityImportPath = ifaceImportPath ? `${path15__default.default.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
880
+ const entityImportPath = ifaceImportPath ? `${path2__default.default.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
817
881
  const importsByFrom = /* @__PURE__ */ new Map();
818
882
  for (const imp of domainImports) {
819
883
  if (imp.name === ifaceName || imp.name === baseEntity) continue;
@@ -862,7 +926,7 @@ function generateConcreteInfraCode(className, ifaceName, dependencies = [], meth
862
926
  if (baseEntity) {
863
927
  finalMethods = synthesizeCrudMethods(baseEntity);
864
928
  if (!finalDomainImports.some((imp) => imp.name === baseEntity)) {
865
- const importPath = ifaceImportPath ? `${path15__default.default.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
929
+ const importPath = ifaceImportPath ? `${path2__default.default.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
866
930
  finalDomainImports.push({
867
931
  name: baseEntity,
868
932
  from: importPath,
@@ -907,8 +971,8 @@ function generateInfrastructure(options) {
907
971
  const projectRoot = options.projectRoot || findProjectRoot();
908
972
  const className = toPascalCase(options.name);
909
973
  const isFeatureScope = options.scope !== "global" && Boolean(options.feature);
910
- const targetDir = options.targetDir || (isFeatureScope ? path15__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "infrastructure") : path15__default.default.join(projectRoot, "infrastructure"));
911
- const filePath = path15__default.default.join(targetDir, `${className}.ts`);
974
+ const targetDir = options.targetDir || (isFeatureScope ? path2__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "infrastructure") : path2__default.default.join(projectRoot, "infrastructure"));
975
+ const filePath = path2__default.default.join(targetDir, `${className}.ts`);
912
976
  let code;
913
977
  if (options.implementsInterface) {
914
978
  const rawIfaceName = options.implementsInterface;
@@ -922,7 +986,7 @@ function generateInfrastructure(options) {
922
986
  const ifaceFilePath = hierarchy.ifaceFilePath;
923
987
  let ifaceImportPath;
924
988
  if (ifaceFilePath) {
925
- let rel = path15__default.default.relative(targetDir, ifaceFilePath).replace(/\\/g, "/").replace(/\.ts$/, ".js");
989
+ let rel = path2__default.default.relative(targetDir, ifaceFilePath).replace(/\\/g, "/").replace(/\.ts$/, ".js");
926
990
  if (!rel.startsWith(".")) rel = "./" + rel;
927
991
  ifaceImportPath = rel;
928
992
  } else if (isFeatureScope) {
@@ -935,8 +999,8 @@ function generateInfrastructure(options) {
935
999
  if (new RegExp(`\\b${item.name}\\b`).test(allSignatures)) {
936
1000
  let fromPath;
937
1001
  if (imp.specifier.startsWith(".") && ifaceFilePath) {
938
- const absTarget = path15__default.default.resolve(path15__default.default.dirname(ifaceFilePath), imp.specifier);
939
- let rel = path15__default.default.relative(targetDir, absTarget).replace(/\\/g, "/");
1002
+ const absTarget = path2__default.default.resolve(path2__default.default.dirname(ifaceFilePath), imp.specifier);
1003
+ let rel = path2__default.default.relative(targetDir, absTarget).replace(/\\/g, "/");
940
1004
  if (!rel.startsWith(".")) rel = "./" + rel;
941
1005
  fromPath = rel;
942
1006
  } else {
@@ -1009,22 +1073,22 @@ function generateUseCaseCode(useCaseName, dependencies = [], inputProps = [], ou
1009
1073
  from: /^I[A-Z]/.test(dep) ? `../domain/${dep}.js` : `./${dep}.js`
1010
1074
  }));
1011
1075
  const domainTypesToImport = new Set(domainImports);
1012
- for (const p15 of [...inputProps, ...outputProps]) {
1013
- const rawType = p15.type.trim();
1076
+ for (const p16 of [...inputProps, ...outputProps]) {
1077
+ const rawType = p16.type.trim();
1014
1078
  const baseType = rawType.replace(/\[\]$/, "").trim();
1015
1079
  if (!primitiveTypes.has(baseType) && /^[A-Z][A-Za-z0-9_]*$/.test(baseType) && !dependencies.includes(baseType)) {
1016
1080
  domainTypesToImport.add(baseType);
1017
1081
  }
1018
1082
  }
1019
- const formattedInputProps = inputProps.map((p15) => ({
1020
- name: p15.name,
1021
- optional: p15.optional,
1022
- type: p15.optional && !p15.type.includes("undefined") ? `${p15.type} | undefined` : p15.type
1083
+ const formattedInputProps = inputProps.map((p16) => ({
1084
+ name: p16.name,
1085
+ optional: p16.optional,
1086
+ type: p16.optional && !p16.type.includes("undefined") ? `${p16.type} | undefined` : p16.type
1023
1087
  }));
1024
- const formattedOutputProps = outputProps.map((p15) => ({
1025
- name: p15.name,
1026
- optional: p15.optional,
1027
- type: p15.optional && !p15.type.includes("undefined") ? `${p15.type} | undefined` : p15.type
1088
+ const formattedOutputProps = outputProps.map((p16) => ({
1089
+ name: p16.name,
1090
+ optional: p16.optional,
1091
+ type: p16.optional && !p16.type.includes("undefined") ? `${p16.type} | undefined` : p16.type
1028
1092
  }));
1029
1093
  const formattedDeps = dependencies.map((dep) => ({
1030
1094
  key: toCamelCase(dep),
@@ -1123,8 +1187,8 @@ function generateUseCase(options) {
1123
1187
  const files = [];
1124
1188
  const projectRoot = options.projectRoot || findProjectRoot();
1125
1189
  const ucName = toPascalCase(options.name);
1126
- const targetDir = options.targetDir || path15__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "useCases");
1127
- const filePath = path15__default.default.join(targetDir, `${ucName}.ts`);
1190
+ const targetDir = options.targetDir || path2__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "useCases");
1191
+ const filePath = path2__default.default.join(targetDir, `${ucName}.ts`);
1128
1192
  const code = generateUseCaseCode(
1129
1193
  ucName,
1130
1194
  options.dependencies || [],
@@ -1140,7 +1204,7 @@ function generateUseCase(options) {
1140
1204
  })
1141
1205
  );
1142
1206
  if (options.includeTest) {
1143
- const testPath = path15__default.default.join(targetDir, `${ucName}.test.ts`);
1207
+ const testPath = path2__default.default.join(targetDir, `${ucName}.test.ts`);
1144
1208
  const testCode = generateUseCaseTestCode(ucName, options.dependencies || []);
1145
1209
  files.push(
1146
1210
  writeFileSafely(testPath, testCode, {
@@ -1208,10 +1272,10 @@ function generateHttpHandler(options) {
1208
1272
  const files = [];
1209
1273
  const projectRoot = options.projectRoot || findProjectRoot();
1210
1274
  const className = toPascalCase(options.name);
1211
- const pxDir = path15__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress");
1212
- const handlersDir = path15__default.default.join(pxDir, "handlers");
1213
- const pxIndexPath = path15__default.default.join(pxDir, "index.ts");
1214
- if (!fs7__default.default.existsSync(pxIndexPath)) {
1275
+ const pxDir = path2__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress");
1276
+ const handlersDir = path2__default.default.join(pxDir, "handlers");
1277
+ const pxIndexPath = path2__default.default.join(pxDir, "index.ts");
1278
+ if (!fs9__default.default.existsSync(pxIndexPath)) {
1215
1279
  const featurePath = options.routePrefix || `/${options.feature.toLowerCase()}`;
1216
1280
  const pxIndexContent = renderTemplate(expressIndexTemplate, {
1217
1281
  routePath: featurePath
@@ -1224,7 +1288,7 @@ function generateHttpHandler(options) {
1224
1288
  })
1225
1289
  );
1226
1290
  }
1227
- const handlerFilePath = path15__default.default.join(handlersDir, `${className}.ts`);
1291
+ const handlerFilePath = path2__default.default.join(handlersDir, `${className}.ts`);
1228
1292
  const code = generateHttpHandlerCode(
1229
1293
  className,
1230
1294
  options.method || "get",
@@ -1245,11 +1309,11 @@ function generateHttpHandler(options) {
1245
1309
  return files;
1246
1310
  }
1247
1311
  function updateOrGenerateDiProvider(featureDir, featureName, bindings = [], options = {}) {
1248
- const providerPath = path15__default.default.join(featureDir, "diProvider.ts");
1312
+ const providerPath = path2__default.default.join(featureDir, "diProvider.ts");
1249
1313
  const pascalFeature = toPascalCase(featureName);
1250
1314
  const providerModuleName = `${pascalFeature}Provider`;
1251
- if (fs7__default.default.existsSync(providerPath) && !options.overwrite) {
1252
- let content = fs7__default.default.readFileSync(providerPath, "utf-8");
1315
+ if (fs9__default.default.existsSync(providerPath) && !options.overwrite) {
1316
+ let content = fs9__default.default.readFileSync(providerPath, "utf-8");
1253
1317
  content = content.replace(/\n*export\s+const\s+ScaffoldProvider\s*=[^;]+;?/g, "");
1254
1318
  for (const b of bindings) {
1255
1319
  const ifaceImp = b.ifaceImportPath || `./domain/${b.ifaceName}.js`;
@@ -1308,7 +1372,7 @@ function generateFeature(options) {
1308
1372
  const projectRoot = options.projectRoot || findProjectRoot();
1309
1373
  const rawName = options.name.trim();
1310
1374
  const featureName = toCamelCase(rawName);
1311
- const targetDir = options.targetDir || path15__default.default.join(resolveFeaturesDir(projectRoot), featureName);
1375
+ const targetDir = options.targetDir || path2__default.default.join(resolveFeaturesDir(projectRoot), featureName);
1312
1376
  const routePath = options.routePath || `/${featureName}`;
1313
1377
  const rawEntities = [];
1314
1378
  if (options.entityNames && options.entityNames.length > 0) {
@@ -1329,7 +1393,7 @@ function generateFeature(options) {
1329
1393
  const domainFile = generateDomain({
1330
1394
  name: ent,
1331
1395
  feature: featureName,
1332
- targetDir: path15__default.default.join(targetDir, "domain"),
1396
+ targetDir: path2__default.default.join(targetDir, "domain"),
1333
1397
  projectRoot,
1334
1398
  dryRun: options.dryRun,
1335
1399
  overwrite: options.overwrite,
@@ -1349,7 +1413,7 @@ function generateFeature(options) {
1349
1413
  const ifaceFile = generateInterface({
1350
1414
  name: ifaceName,
1351
1415
  feature: featureName,
1352
- targetDir: path15__default.default.join(targetDir, "domain"),
1416
+ targetDir: path2__default.default.join(targetDir, "domain"),
1353
1417
  entityName: ent,
1354
1418
  projectRoot,
1355
1419
  dryRun: options.dryRun,
@@ -1366,7 +1430,7 @@ function generateFeature(options) {
1366
1430
  const stubFile = generateInfrastructure({
1367
1431
  name: stubName,
1368
1432
  feature: featureName,
1369
- targetDir: path15__default.default.join(targetDir, "infrastructure"),
1433
+ targetDir: path2__default.default.join(targetDir, "infrastructure"),
1370
1434
  kind: "stub",
1371
1435
  implementsInterface: ifaceName,
1372
1436
  entityName: ent,
@@ -1408,7 +1472,7 @@ function generateFeature(options) {
1408
1472
  const ucFiles = generateUseCase({
1409
1473
  name: ucName,
1410
1474
  feature: featureName,
1411
- targetDir: path15__default.default.join(targetDir, "useCases"),
1475
+ targetDir: path2__default.default.join(targetDir, "useCases"),
1412
1476
  dependencies: options.includeInterface !== false ? [ifaceName] : [],
1413
1477
  inputProperties: [{ name: idParam, type: "string" }],
1414
1478
  outputProperties: [{ name: entVar, type: primaryEntity }],
@@ -1453,7 +1517,7 @@ function generateFeature(options) {
1453
1517
  const ucFiles = generateUseCase({
1454
1518
  name: ucName,
1455
1519
  feature: featureName,
1456
- targetDir: path15__default.default.join(targetDir, "useCases"),
1520
+ targetDir: path2__default.default.join(targetDir, "useCases"),
1457
1521
  dependencies: deps,
1458
1522
  inputProperties: [{ name: entVar, type: ent }],
1459
1523
  outputProperties: [{ name: entVar, type: ent }],
@@ -1486,7 +1550,7 @@ function generateFeature(options) {
1486
1550
  const ucFiles = generateUseCase({
1487
1551
  name: ucName,
1488
1552
  feature: featureName,
1489
- targetDir: path15__default.default.join(targetDir, "useCases"),
1553
+ targetDir: path2__default.default.join(targetDir, "useCases"),
1490
1554
  dependencies: deps,
1491
1555
  inputProperties: [{ name: idParam, type: "string" }],
1492
1556
  outputProperties: [{ name: entVar, type: ent }],
@@ -1519,7 +1583,7 @@ function generateFeature(options) {
1519
1583
  const ucFiles = generateUseCase({
1520
1584
  name: ucName,
1521
1585
  feature: featureName,
1522
- targetDir: path15__default.default.join(targetDir, "useCases"),
1586
+ targetDir: path2__default.default.join(targetDir, "useCases"),
1523
1587
  dependencies: deps,
1524
1588
  inputProperties: [
1525
1589
  { name: "page", type: "number", optional: true },
@@ -1563,7 +1627,7 @@ function generateFeature(options) {
1563
1627
  const ucFiles = generateUseCase({
1564
1628
  name: ucName,
1565
1629
  feature: featureName,
1566
- targetDir: path15__default.default.join(targetDir, "useCases"),
1630
+ targetDir: path2__default.default.join(targetDir, "useCases"),
1567
1631
  dependencies: deps,
1568
1632
  inputProperties: [{ name: entVar, type: ent }],
1569
1633
  outputProperties: [{ name: entVar, type: ent }],
@@ -1596,7 +1660,7 @@ function generateFeature(options) {
1596
1660
  const ucFiles = generateUseCase({
1597
1661
  name: ucName,
1598
1662
  feature: featureName,
1599
- targetDir: path15__default.default.join(targetDir, "useCases"),
1663
+ targetDir: path2__default.default.join(targetDir, "useCases"),
1600
1664
  dependencies: deps,
1601
1665
  inputProperties: [{ name: idParam, type: "string" }],
1602
1666
  outputProperties: [{ name: "success", type: "boolean" }],
@@ -1633,7 +1697,7 @@ function generateFeature(options) {
1633
1697
  const ucFiles = generateUseCase({
1634
1698
  name: ucName,
1635
1699
  feature: featureName,
1636
- targetDir: path15__default.default.join(targetDir, "useCases"),
1700
+ targetDir: path2__default.default.join(targetDir, "useCases"),
1637
1701
  dependencies: customUc.dependencies || [],
1638
1702
  inputProperties: customUc.inputProperties || [],
1639
1703
  outputProperties: customUc.outputProperties || [],
@@ -1665,8 +1729,8 @@ function generateFeature(options) {
1665
1729
  }
1666
1730
  }
1667
1731
  if (options.includeExpress !== false) {
1668
- const pxDir = path15__default.default.join(targetDir, "pxExpress");
1669
- const pxIndexPath = path15__default.default.join(pxDir, "index.ts");
1732
+ const pxDir = path2__default.default.join(targetDir, "pxExpress");
1733
+ const pxIndexPath = path2__default.default.join(pxDir, "index.ts");
1670
1734
  if (!files.some((f) => f.absolutePath === pxIndexPath)) {
1671
1735
  const pxIndexContent = renderTemplate(expressIndexTemplate, { routePath });
1672
1736
  files.push(
@@ -1703,13 +1767,13 @@ function handleCancel(result) {
1703
1767
  }
1704
1768
  function printBanner() {
1705
1769
  console.log("");
1706
- p3__namespace.intro(pc13__default.default.bgCyan(pc13__default.default.black(" @solid-stack/mason ")));
1770
+ p3__namespace.intro(pc14__default.default.bgCyan(pc14__default.default.black(" @solid-stack/mason ")));
1707
1771
  }
1708
1772
  async function promptProperties(options = {}) {
1709
1773
  const properties = options.defaults ? [...options.defaults] : [];
1710
1774
  if (properties.length > 0) {
1711
- const summary = properties.map((p15) => `${p15.name}: ${p15.type}${p15.optional ? "?" : ""}`).join(", ");
1712
- p3__namespace.log.info(pc13__default.default.dim(`Pre-configured fields: ${summary}`));
1775
+ const summary = properties.map((p16) => `${p16.name}: ${p16.type}${p16.optional ? "?" : ""}`).join(", ");
1776
+ p3__namespace.log.info(pc14__default.default.dim(`Pre-configured fields: ${summary}`));
1713
1777
  const addMore = handleCancel(
1714
1778
  await p3__namespace.confirm({
1715
1779
  message: "Would you like to add custom fields/properties?",
@@ -1720,7 +1784,7 @@ async function promptProperties(options = {}) {
1720
1784
  return properties;
1721
1785
  }
1722
1786
  }
1723
- p3__namespace.log.info(pc13__default.default.cyan("Enter properties (leave name blank when finished):"));
1787
+ p3__namespace.log.info(pc14__default.default.cyan("Enter properties (leave name blank when finished):"));
1724
1788
  while (true) {
1725
1789
  const name = handleCancel(
1726
1790
  await p3__namespace.text({
@@ -1768,7 +1832,7 @@ async function promptProperties(options = {}) {
1768
1832
  optional
1769
1833
  });
1770
1834
  p3__namespace.log.success(
1771
- pc13__default.default.green(`Added: ${name.trim()}${optional ? "?" : ""}: ${finalType.trim()}`)
1835
+ pc14__default.default.green(`Added: ${name.trim()}${optional ? "?" : ""}: ${finalType.trim()}`)
1772
1836
  );
1773
1837
  }
1774
1838
  return properties;
@@ -1777,6 +1841,7 @@ async function promptProperties(options = {}) {
1777
1841
  // src/commands/feature.ts
1778
1842
  async function runFeatureCommand(featureNameArg, flags = {}) {
1779
1843
  const projectRoot = findProjectRoot();
1844
+ const isRegistry = getEffectiveMode(projectRoot, flags) === "registry";
1780
1845
  let name = featureNameArg?.trim();
1781
1846
  if (!name && flags.yes) {
1782
1847
  name = "feature";
@@ -1794,9 +1859,10 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1794
1859
  ).trim();
1795
1860
  }
1796
1861
  const normalizedName = toCamelCase(name);
1797
- const defaultDir = path15__default.default.relative(projectRoot, path15__default.default.join(resolveFeaturesDir(projectRoot), normalizedName));
1862
+ const baseFeaturesDir = resolveFeaturesDir(projectRoot, flags);
1863
+ const defaultDir = path2__default.default.relative(projectRoot, path2__default.default.join(baseFeaturesDir, normalizedName));
1798
1864
  let targetDirRel = defaultDir;
1799
- if (!flags.yes && !flags.target) {
1865
+ if (!flags.yes && !flags.target && !flags.dest) {
1800
1866
  targetDirRel = handleCancel(
1801
1867
  await p3__namespace.text({
1802
1868
  message: "Target directory for the feature:",
@@ -1805,7 +1871,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1805
1871
  })
1806
1872
  ).trim();
1807
1873
  }
1808
- const targetDir = flags.target ? path15__default.default.resolve(projectRoot, flags.target) : path15__default.default.resolve(projectRoot, targetDirRel);
1874
+ const targetDir = flags.dest ? path2__default.default.resolve(projectRoot, flags.dest) : flags.target ? path2__default.default.resolve(projectRoot, flags.target) : path2__default.default.resolve(projectRoot, targetDirRel);
1809
1875
  let includeExpress = flags.express !== void 0 ? Boolean(flags.express) : true;
1810
1876
  let routePath = `/${normalizedName}`;
1811
1877
  let includeDomain = true;
@@ -2026,7 +2092,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2026
2092
  }
2027
2093
  }
2028
2094
  const s = p3__namespace.spinner();
2029
- s.start(`Generating feature '${pc13__default.default.cyan(normalizedName)}'...`);
2095
+ s.start(`Generating feature '${pc14__default.default.cyan(normalizedName)}'...`);
2030
2096
  const options = {
2031
2097
  name: normalizedName,
2032
2098
  projectRoot,
@@ -2046,9 +2112,207 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2046
2112
  includeDiProvider
2047
2113
  };
2048
2114
  const files = generateFeature(options);
2115
+ if (isRegistry) {
2116
+ const registryPath = path2__default.default.join(targetDir, "registry.json");
2117
+ if (!fs9__default.default.existsSync(registryPath)) {
2118
+ const manifest = {
2119
+ name: toKebabCase(normalizedName),
2120
+ type: "feature",
2121
+ description: flags.description || `${normalizedName} feature module`,
2122
+ version: "1.0.0",
2123
+ dependencies: {
2124
+ npm: includeExpress ? ["@solid-stack/agnos-express", "express"] : [],
2125
+ shared: [],
2126
+ features: []
2127
+ },
2128
+ files: []
2129
+ };
2130
+ const content = JSON.stringify(manifest, null, 2) + "\n";
2131
+ const genFile = writeFileSafely(registryPath, content, {
2132
+ projectRoot,
2133
+ overwrite: options.overwrite,
2134
+ dryRun: options.dryRun
2135
+ });
2136
+ files.push(genFile);
2137
+ }
2138
+ }
2049
2139
  s.stop("Feature scaffolded successfully!");
2050
2140
  printGeneratedFiles(files, options.dryRun);
2051
- p3__namespace.outro(pc13__default.default.green(`Feature '${normalizedName}' ready to use!`));
2141
+ p3__namespace.outro(
2142
+ pc14__default.default.green(
2143
+ `\u2713 Feature '${normalizedName}' initialized in ${path2__default.default.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
2144
+ )
2145
+ );
2146
+ }
2147
+ function generateShared(options) {
2148
+ const files = [];
2149
+ const projectRoot = options.projectRoot || findProjectRoot();
2150
+ const rawName = options.name.trim();
2151
+ const pascalName = toPascalCase(rawName);
2152
+ const camelName = toCamelCase(rawName);
2153
+ const kebabName = toKebabCase(rawName);
2154
+ const targetDir = options.targetDir || path2__default.default.join(resolveSharedDir(projectRoot), kebabName);
2155
+ const fileDefs = [];
2156
+ const facadeContent = `import { MakeInjectable, type DepsType } from "@solid-stack/di";
2157
+ import { I${pascalName}Engine } from "./ports/I${pascalName}Engine.js";
2158
+
2159
+ @MakeInjectable
2160
+ export class ${pascalName} {
2161
+ public static deps = { ${camelName}Engine: I${pascalName}Engine };
2162
+ constructor(public readonly deps: DepsType<typeof ${pascalName}.deps>) {}
2163
+ }
2164
+ `;
2165
+ fileDefs.push({
2166
+ absolutePath: path2__default.default.join(targetDir, `${pascalName}.ts`),
2167
+ content: facadeContent
2168
+ });
2169
+ const portContent = `export abstract class I${pascalName}Engine {
2170
+ // Define abstract port contracts for ${pascalName} here
2171
+ }
2172
+ `;
2173
+ fileDefs.push({
2174
+ absolutePath: path2__default.default.join(targetDir, "ports", `I${pascalName}Engine.ts`),
2175
+ content: portContent
2176
+ });
2177
+ const concreteContent = `import { MakeInjectable } from "@solid-stack/di";
2178
+ import { I${pascalName}Engine } from "../ports/I${pascalName}Engine.js";
2179
+
2180
+ @MakeInjectable
2181
+ export class Default${pascalName}Engine implements I${pascalName}Engine {
2182
+ // Default concrete implementation for production
2183
+ }
2184
+ `;
2185
+ fileDefs.push({
2186
+ absolutePath: path2__default.default.join(targetDir, "infrastructure", `Default${pascalName}Engine.ts`),
2187
+ content: concreteContent
2188
+ });
2189
+ if (options.includeStub !== false) {
2190
+ const stubContent = `import { MakeInjectable } from "@solid-stack/di";
2191
+ import { I${pascalName}Engine } from "../ports/I${pascalName}Engine.js";
2192
+
2193
+ @MakeInjectable
2194
+ export class Stub${pascalName}Engine implements I${pascalName}Engine {
2195
+ // In-memory or stub implementation for testing
2196
+ }
2197
+ `;
2198
+ fileDefs.push({
2199
+ absolutePath: path2__default.default.join(targetDir, "infrastructure", `Stub${pascalName}Engine.ts`),
2200
+ content: stubContent
2201
+ });
2202
+ }
2203
+ const providerContent = `import type { Container } from "@solid-stack/di";
2204
+ import { I${pascalName}Engine } from "./ports/I${pascalName}Engine.js";
2205
+ import { Default${pascalName}Engine } from "./infrastructure/Default${pascalName}Engine.js";
2206
+
2207
+ export const diProvider = (c: Container): void => {
2208
+ c.provide(I${pascalName}Engine, Default${pascalName}Engine);
2209
+ };
2210
+
2211
+ export default diProvider;
2212
+ `;
2213
+ fileDefs.push({
2214
+ absolutePath: path2__default.default.join(targetDir, "diProvider.ts"),
2215
+ content: providerContent
2216
+ });
2217
+ if (options.isRegistry) {
2218
+ const registryManifest = {
2219
+ name: kebabName,
2220
+ type: "shared",
2221
+ description: options.description || `${kebabName} shared module`,
2222
+ version: "1.0.0",
2223
+ dependencies: {
2224
+ npm: [],
2225
+ shared: [],
2226
+ features: []
2227
+ },
2228
+ files: []
2229
+ };
2230
+ fileDefs.push({
2231
+ absolutePath: path2__default.default.join(targetDir, "registry.json"),
2232
+ content: JSON.stringify(registryManifest, null, 2) + "\n"
2233
+ });
2234
+ }
2235
+ for (const def of fileDefs) {
2236
+ const genFile = writeFileSafely(def.absolutePath, def.content, {
2237
+ projectRoot,
2238
+ overwrite: options.overwrite,
2239
+ dryRun: options.dryRun
2240
+ });
2241
+ files.push(genFile);
2242
+ }
2243
+ return files;
2244
+ }
2245
+
2246
+ // src/commands/shared.ts
2247
+ async function runSharedCommand(sharedNameArg, flags = {}) {
2248
+ const projectRoot = findProjectRoot();
2249
+ const isRegistry = getEffectiveMode(projectRoot, flags) === "registry";
2250
+ let name = sharedNameArg?.trim();
2251
+ if (!name && flags.yes) {
2252
+ name = "shared";
2253
+ }
2254
+ if (!name) {
2255
+ name = handleCancel(
2256
+ await p3__namespace.text({
2257
+ message: "What is the shared module name?",
2258
+ placeholder: "e.g. hasher, time, uuid, cache, logger",
2259
+ validate: (val) => {
2260
+ if (!val || !val.trim()) return "Shared module name cannot be empty.";
2261
+ return void 0;
2262
+ }
2263
+ })
2264
+ ).trim();
2265
+ }
2266
+ const normalizedName = toKebabCase(name);
2267
+ const baseSharedDir = resolveSharedDir(projectRoot, flags);
2268
+ const defaultDir = path2__default.default.relative(projectRoot, path2__default.default.join(baseSharedDir, normalizedName));
2269
+ let targetDirRel = defaultDir;
2270
+ if (!flags.yes && !flags.target && !flags.dest) {
2271
+ targetDirRel = handleCancel(
2272
+ await p3__namespace.text({
2273
+ message: "Target directory for the shared module:",
2274
+ initialValue: defaultDir,
2275
+ defaultValue: defaultDir
2276
+ })
2277
+ ).trim();
2278
+ }
2279
+ const targetDir = flags.dest ? path2__default.default.resolve(projectRoot, flags.dest) : flags.target ? path2__default.default.resolve(projectRoot, flags.target) : path2__default.default.resolve(projectRoot, targetDirRel);
2280
+ let description = flags.description || "";
2281
+ let includeStub = true;
2282
+ if (!flags.yes) {
2283
+ description = handleCancel(
2284
+ await p3__namespace.text({
2285
+ message: "Module description (optional):",
2286
+ placeholder: "e.g. In-memory caching and Redis port abstraction",
2287
+ initialValue: ""
2288
+ })
2289
+ ).trim();
2290
+ includeStub = handleCancel(
2291
+ await p3__namespace.confirm({
2292
+ message: "Include stub implementation in infrastructure/?",
2293
+ initialValue: true
2294
+ })
2295
+ );
2296
+ }
2297
+ const s = p3__namespace.spinner();
2298
+ s.start(`Generating shared module '${pc14__default.default.cyan(normalizedName)}'...`);
2299
+ const files = generateShared({
2300
+ name: normalizedName,
2301
+ description,
2302
+ projectRoot,
2303
+ targetDir,
2304
+ dryRun: Boolean(flags.dryRun),
2305
+ overwrite: Boolean(flags.overwrite),
2306
+ includeStub,
2307
+ isRegistry
2308
+ });
2309
+ s.stop("Shared module generated successfully!");
2310
+ printGeneratedFiles(files, Boolean(flags.dryRun));
2311
+ p3__namespace.outro(
2312
+ pc14__default.default.green(
2313
+ `\u2713 Shared module '${normalizedName}' initialized in ${path2__default.default.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
2314
+ )
2315
+ );
2052
2316
  }
2053
2317
  async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2054
2318
  const projectRoot = findProjectRoot();
@@ -2173,7 +2437,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2173
2437
  }
2174
2438
  }
2175
2439
  const s = p3__namespace.spinner();
2176
- s.start(`Generating use case '${pc13__default.default.cyan(ucName)}'...`);
2440
+ s.start(`Generating use case '${pc14__default.default.cyan(ucName)}'...`);
2177
2441
  const options = {
2178
2442
  name: ucName,
2179
2443
  feature,
@@ -2205,7 +2469,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2205
2469
  }
2206
2470
  s.stop("Use case generated successfully!");
2207
2471
  printGeneratedFiles(files, options.dryRun);
2208
- p3__namespace.outro(pc13__default.default.green(`Use case '${ucName}' created in feature '${feature}'!`));
2472
+ p3__namespace.outro(pc14__default.default.green(`Use case '${ucName}' created in feature '${feature}'!`));
2209
2473
  }
2210
2474
  async function runDomainCommand(domainNameArg, flags = {}) {
2211
2475
  const projectRoot = findProjectRoot();
@@ -2276,7 +2540,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2276
2540
  });
2277
2541
  }
2278
2542
  const s = p3__namespace.spinner();
2279
- s.start(`Generating domain model '${pc13__default.default.cyan(entityName)}'...`);
2543
+ s.start(`Generating domain model '${pc14__default.default.cyan(entityName)}'...`);
2280
2544
  const options = {
2281
2545
  name: entityName,
2282
2546
  feature,
@@ -2289,7 +2553,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2289
2553
  const file = generateDomain(options);
2290
2554
  s.stop("Domain model generated successfully!");
2291
2555
  printGeneratedFiles([file], options.dryRun);
2292
- p3__namespace.outro(pc13__default.default.green(`Domain model '${entityName}' created in feature '${feature}'!`));
2556
+ p3__namespace.outro(pc14__default.default.green(`Domain model '${entityName}' created in feature '${feature}'!`));
2293
2557
  }
2294
2558
  async function runInfrastructureCommand(classNameArg, flags = {}) {
2295
2559
  const projectRoot = findProjectRoot();
@@ -2458,7 +2722,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2458
2722
  registerInProvider = !flags.noProvider;
2459
2723
  }
2460
2724
  const s = p3__namespace.spinner();
2461
- s.start(`Generating infrastructure class '${pc13__default.default.cyan(className)}'...`);
2725
+ s.start(`Generating infrastructure class '${pc14__default.default.cyan(className)}'...`);
2462
2726
  const files = [];
2463
2727
  const options = {
2464
2728
  name: className,
@@ -2473,7 +2737,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2473
2737
  const infraFile = generateInfrastructure(options);
2474
2738
  files.push(infraFile);
2475
2739
  if (registerInProvider && scope === "feature" && feature && implementsInterface) {
2476
- const featureDir = path15__default.default.join(resolveFeaturesDir(projectRoot), feature);
2740
+ const featureDir = path2__default.default.join(resolveFeaturesDir(projectRoot), feature);
2477
2741
  const providerFile = updateOrGenerateDiProvider(
2478
2742
  featureDir,
2479
2743
  feature,
@@ -2493,7 +2757,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2493
2757
  }
2494
2758
  s.stop("Infrastructure class generated successfully!");
2495
2759
  printGeneratedFiles(files, options.dryRun);
2496
- p3__namespace.outro(pc13__default.default.green(`Infrastructure class '${className}' created!`));
2760
+ p3__namespace.outro(pc14__default.default.green(`Infrastructure class '${className}' created!`));
2497
2761
  }
2498
2762
  async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2499
2763
  const projectRoot = findProjectRoot();
@@ -2606,7 +2870,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2606
2870
  }
2607
2871
  }
2608
2872
  const s = p3__namespace.spinner();
2609
- s.start(`Generating interface '${pc13__default.default.cyan(ifaceName)}'...`);
2873
+ s.start(`Generating interface '${pc14__default.default.cyan(ifaceName)}'...`);
2610
2874
  const files = [];
2611
2875
  const options = {
2612
2876
  name: ifaceName,
@@ -2635,7 +2899,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2635
2899
  files.push(stubFile);
2636
2900
  }
2637
2901
  if (registerInProvider && generateStub) {
2638
- const featureDir = path15__default.default.join(resolveFeaturesDir(projectRoot), feature);
2902
+ const featureDir = path2__default.default.join(resolveFeaturesDir(projectRoot), feature);
2639
2903
  const providerFile = updateOrGenerateDiProvider(
2640
2904
  featureDir,
2641
2905
  feature,
@@ -2655,7 +2919,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2655
2919
  }
2656
2920
  s.stop("Interface generated successfully!");
2657
2921
  printGeneratedFiles(files, options.dryRun);
2658
- p3__namespace.outro(pc13__default.default.green(`Interface '${ifaceName}' created in feature '${feature}'!`));
2922
+ p3__namespace.outro(pc14__default.default.green(`Interface '${ifaceName}' created in feature '${feature}'!`));
2659
2923
  }
2660
2924
  async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2661
2925
  const projectRoot = findProjectRoot();
@@ -2772,7 +3036,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2772
3036
  }
2773
3037
  }
2774
3038
  const s = p3__namespace.spinner();
2775
- s.start(`Generating HTTP handler '${pc13__default.default.cyan(className)}'...`);
3039
+ s.start(`Generating HTTP handler '${pc14__default.default.cyan(className)}'...`);
2776
3040
  const options = {
2777
3041
  name: className,
2778
3042
  feature,
@@ -2790,7 +3054,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2790
3054
  const files = generateHttpHandler(options);
2791
3055
  s.stop("HTTP handler generated successfully!");
2792
3056
  printGeneratedFiles(files, options.dryRun);
2793
- p3__namespace.outro(pc13__default.default.green(`HTTP handler '${className}' created in feature '${feature}'!`));
3057
+ p3__namespace.outro(pc14__default.default.green(`HTTP handler '${className}' created in feature '${feature}'!`));
2794
3058
  }
2795
3059
  function generateInjectableCode(className, dependencies = [], methods = [], implementsInterface) {
2796
3060
  const formattedDeps = dependencies.map((dep) => ({
@@ -2800,7 +3064,7 @@ function generateInjectableCode(className, dependencies = [], methods = [], impl
2800
3064
  const formattedMethods = methods.map((m) => {
2801
3065
  const isAsync = m.async !== false;
2802
3066
  const retType = m.returnType ? isAsync ? `Promise<${m.returnType}>` : m.returnType : isAsync ? "Promise<void>" : "void";
2803
- const params = m.params && m.params.length > 0 ? m.params.map((p15) => `${p15.name}: ${p15.type}`).join(", ") : "";
3067
+ const params = m.params && m.params.length > 0 ? m.params.map((p16) => `${p16.name}: ${p16.type}`).join(", ") : "";
2804
3068
  return {
2805
3069
  name: m.name,
2806
3070
  isAsync,
@@ -2823,21 +3087,21 @@ function generateInjectable(options) {
2823
3087
  const className = toPascalCase(options.name);
2824
3088
  let targetDir = "";
2825
3089
  if (options.customPath) {
2826
- targetDir = path15__default.default.isAbsolute(options.customPath) ? options.customPath : path15__default.default.join(projectRoot, options.customPath);
3090
+ targetDir = path2__default.default.isAbsolute(options.customPath) ? options.customPath : path2__default.default.join(projectRoot, options.customPath);
2827
3091
  } else if (options.location === "feature-service" && options.feature) {
2828
- targetDir = path15__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "services");
3092
+ targetDir = path2__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "services");
2829
3093
  } else if (options.location === "feature-transformer" && options.feature) {
2830
- targetDir = path15__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress", "transformers");
3094
+ targetDir = path2__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress", "transformers");
2831
3095
  } else if (options.location === "module") {
2832
- targetDir = path15__default.default.join(projectRoot, "modules", className);
3096
+ targetDir = path2__default.default.join(projectRoot, "modules", className);
2833
3097
  } else {
2834
- targetDir = options.feature ? path15__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "services") : path15__default.default.join(projectRoot, "shared", className.toLowerCase());
3098
+ targetDir = options.feature ? path2__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "services") : path2__default.default.join(projectRoot, "shared", className.toLowerCase());
2835
3099
  }
2836
- const filePath = path15__default.default.join(targetDir, `${className}.ts`);
3100
+ const filePath = path2__default.default.join(targetDir, `${className}.ts`);
2837
3101
  let ifaceName;
2838
3102
  if (options.generateInterface) {
2839
3103
  ifaceName = `I${className}`;
2840
- const ifacePath = path15__default.default.join(targetDir, `${ifaceName}.ts`);
3104
+ const ifacePath = path2__default.default.join(targetDir, `${ifaceName}.ts`);
2841
3105
  const ifaceContent = renderTemplate(injectableInterfaceTemplate, { ifaceName });
2842
3106
  files.push(
2843
3107
  writeFileSafely(ifacePath, ifaceContent, {
@@ -2946,7 +3210,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
2946
3210
  );
2947
3211
  }
2948
3212
  const s = p3__namespace.spinner();
2949
- s.start(`Generating injectable '${pc13__default.default.cyan(className)}'...`);
3213
+ s.start(`Generating injectable '${pc14__default.default.cyan(className)}'...`);
2950
3214
  const options = {
2951
3215
  name: className,
2952
3216
  feature,
@@ -2960,7 +3224,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
2960
3224
  const files = generateInjectable(options);
2961
3225
  s.stop("Injectable class generated successfully!");
2962
3226
  printGeneratedFiles(files, options.dryRun);
2963
- p3__namespace.outro(pc13__default.default.green(`Injectable '${className}' created successfully!`));
3227
+ p3__namespace.outro(pc14__default.default.green(`Injectable '${className}' created successfully!`));
2964
3228
  }
2965
3229
  async function runInteractiveMenu(flags = {}) {
2966
3230
  printBanner();
@@ -2973,6 +3237,11 @@ async function runInteractiveMenu(flags = {}) {
2973
3237
  label: "Feature",
2974
3238
  hint: "Complete vertical slice (domain, infra, usecase, pxExpress, diProvider)"
2975
3239
  },
3240
+ {
3241
+ value: "shared",
3242
+ label: "Shared Module",
3243
+ hint: "Foundational utility or port with ports, infrastructure & diProvider"
3244
+ },
2976
3245
  {
2977
3246
  value: "usecase",
2978
3247
  label: "Use Case",
@@ -3010,6 +3279,9 @@ async function runInteractiveMenu(flags = {}) {
3010
3279
  case "feature":
3011
3280
  await runFeatureCommand(void 0, flags);
3012
3281
  break;
3282
+ case "shared":
3283
+ await runSharedCommand(void 0, flags);
3284
+ break;
3013
3285
  case "usecase":
3014
3286
  await runUseCaseCommand(void 0, flags);
3015
3287
  break;
@@ -3042,6 +3314,9 @@ async function handleCreate(type, name, flags = {}) {
3042
3314
  case "feature":
3043
3315
  await runFeatureCommand(name, flags);
3044
3316
  break;
3317
+ case "shared":
3318
+ await runSharedCommand(name, flags);
3319
+ break;
3045
3320
  case "usecase":
3046
3321
  await runUseCaseCommand(name, flags);
3047
3322
  break;
@@ -3063,8 +3338,8 @@ async function handleCreate(type, name, flags = {}) {
3063
3338
  break;
3064
3339
  default:
3065
3340
  p3__namespace.log.error(
3066
- pc13__default.default.red(
3067
- `Unknown component type: "${type}". Allowed types: feature, usecase, domain, infrastructure, interface, httpHandler, injectable.`
3341
+ pc14__default.default.red(
3342
+ `Unknown component type: "${type}". Allowed types: feature, shared, usecase, domain, infrastructure, interface, httpHandler, injectable.`
3068
3343
  )
3069
3344
  );
3070
3345
  process.exit(1);
@@ -3087,25 +3362,25 @@ function normalizeRegistryUrl(urlOrPath, baseDir) {
3087
3362
  return `https://raw.githubusercontent.com/${owner}/${repo}/${branch}`;
3088
3363
  }
3089
3364
  if (baseDir && (trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed === "." || trimmed === "..")) {
3090
- return path15__default.default.resolve(baseDir, trimmed);
3365
+ return path2__default.default.resolve(baseDir, trimmed);
3091
3366
  }
3092
3367
  return trimmed;
3093
3368
  }
3094
3369
  function findLocalRegistry(startDir = process.cwd()) {
3095
- let current = path15__default.default.resolve(startDir);
3370
+ let current = path2__default.default.resolve(startDir);
3096
3371
  while (true) {
3097
- const candidate = path15__default.default.join(current, "mason-registry");
3098
- if (fs7__default.default.existsSync(candidate) && fs7__default.default.existsSync(path15__default.default.join(candidate, "index.json"))) {
3372
+ const candidate = path2__default.default.join(current, "mason-registry");
3373
+ if (fs9__default.default.existsSync(candidate) && fs9__default.default.existsSync(path2__default.default.join(candidate, "index.json"))) {
3099
3374
  return candidate;
3100
3375
  }
3101
- const parent = path15__default.default.dirname(current);
3376
+ const parent = path2__default.default.dirname(current);
3102
3377
  if (parent === current) break;
3103
3378
  current = parent;
3104
3379
  }
3105
3380
  return null;
3106
3381
  }
3107
3382
  function getRegistryBase(projectRoot, override) {
3108
- const rootDir = projectRoot ? path15__default.default.resolve(projectRoot) : process.cwd();
3383
+ const rootDir = projectRoot ? path2__default.default.resolve(projectRoot) : process.cwd();
3109
3384
  if (override && override.trim()) {
3110
3385
  return normalizeRegistryUrl(override.trim(), rootDir);
3111
3386
  }
@@ -3113,30 +3388,30 @@ function getRegistryBase(projectRoot, override) {
3113
3388
  return normalizeRegistryUrl(process.env.MASON_REGISTRY_URL.trim(), rootDir);
3114
3389
  }
3115
3390
  const configLocations = [
3116
- path15__default.default.join(rootDir, "mason.config.json"),
3117
- path15__default.default.join(process.cwd(), "mason.config.json")
3391
+ path2__default.default.join(rootDir, "mason.config.json"),
3392
+ path2__default.default.join(process.cwd(), "mason.config.json")
3118
3393
  ];
3119
3394
  for (const configPath of configLocations) {
3120
- if (fs7__default.default.existsSync(configPath)) {
3395
+ if (fs9__default.default.existsSync(configPath)) {
3121
3396
  try {
3122
- const config = JSON.parse(fs7__default.default.readFileSync(configPath, "utf-8"));
3397
+ const config = JSON.parse(fs9__default.default.readFileSync(configPath, "utf-8"));
3123
3398
  if (config.registry && typeof config.registry === "string") {
3124
- return normalizeRegistryUrl(config.registry.trim(), path15__default.default.dirname(configPath));
3399
+ return normalizeRegistryUrl(config.registry.trim(), path2__default.default.dirname(configPath));
3125
3400
  }
3126
3401
  } catch {
3127
3402
  }
3128
3403
  }
3129
3404
  }
3130
3405
  const pkgLocations = [
3131
- path15__default.default.join(rootDir, "package.json"),
3132
- path15__default.default.join(process.cwd(), "package.json")
3406
+ path2__default.default.join(rootDir, "package.json"),
3407
+ path2__default.default.join(process.cwd(), "package.json")
3133
3408
  ];
3134
3409
  for (const pkgPath of pkgLocations) {
3135
- if (fs7__default.default.existsSync(pkgPath)) {
3410
+ if (fs9__default.default.existsSync(pkgPath)) {
3136
3411
  try {
3137
- const pkg = JSON.parse(fs7__default.default.readFileSync(pkgPath, "utf-8"));
3412
+ const pkg = JSON.parse(fs9__default.default.readFileSync(pkgPath, "utf-8"));
3138
3413
  if (pkg.mason?.registry && typeof pkg.mason.registry === "string") {
3139
- return normalizeRegistryUrl(pkg.mason.registry.trim(), path15__default.default.dirname(pkgPath));
3414
+ return normalizeRegistryUrl(pkg.mason.registry.trim(), path2__default.default.dirname(pkgPath));
3140
3415
  }
3141
3416
  } catch {
3142
3417
  }
@@ -3177,11 +3452,11 @@ async function fetchRegistryFile(registryBase, relPath) {
3177
3452
  if (baseDir.startsWith("file://")) {
3178
3453
  baseDir = url.fileURLToPath(baseDir);
3179
3454
  }
3180
- const filePath = path15__default.default.resolve(baseDir, cleanRelPath);
3181
- if (!fs7__default.default.existsSync(filePath)) {
3455
+ const filePath = path2__default.default.resolve(baseDir, cleanRelPath);
3456
+ if (!fs9__default.default.existsSync(filePath)) {
3182
3457
  throw new Error(`Registry file not found at ${filePath}`);
3183
3458
  }
3184
- return fs7__default.default.promises.readFile(filePath, "utf-8");
3459
+ return fs9__default.default.promises.readFile(filePath, "utf-8");
3185
3460
  }
3186
3461
  async function fetchRegistryIndex(registryBase) {
3187
3462
  const content = await fetchRegistryFile(registryBase, "index.json");
@@ -3243,26 +3518,26 @@ function findClosestMatch(target, candidates) {
3243
3518
  return null;
3244
3519
  }
3245
3520
  function detectPackageManager(projectRoot) {
3246
- if (fs7__default.default.existsSync(path15__default.default.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
3247
- if (fs7__default.default.existsSync(path15__default.default.join(projectRoot, "yarn.lock"))) return "yarn";
3248
- if (fs7__default.default.existsSync(path15__default.default.join(projectRoot, "bun.lockb")) || fs7__default.default.existsSync(path15__default.default.join(projectRoot, "bun.lock"))) return "bun";
3521
+ if (fs9__default.default.existsSync(path2__default.default.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
3522
+ if (fs9__default.default.existsSync(path2__default.default.join(projectRoot, "yarn.lock"))) return "yarn";
3523
+ if (fs9__default.default.existsSync(path2__default.default.join(projectRoot, "bun.lockb")) || fs9__default.default.existsSync(path2__default.default.join(projectRoot, "bun.lock"))) return "bun";
3249
3524
  return "npm";
3250
3525
  }
3251
3526
  function getMissingNpmPackages(projectRoot, requiredPackages) {
3252
3527
  if (!requiredPackages || requiredPackages.length === 0) return [];
3253
- const pkgJsonPath = path15__default.default.join(projectRoot, "package.json");
3254
- if (!fs7__default.default.existsSync(pkgJsonPath)) {
3528
+ const pkgJsonPath = path2__default.default.join(projectRoot, "package.json");
3529
+ if (!fs9__default.default.existsSync(pkgJsonPath)) {
3255
3530
  return requiredPackages;
3256
3531
  }
3257
3532
  try {
3258
- const pkg = JSON.parse(fs7__default.default.readFileSync(pkgJsonPath, "utf-8"));
3533
+ const pkg = JSON.parse(fs9__default.default.readFileSync(pkgJsonPath, "utf-8"));
3259
3534
  const installed = /* @__PURE__ */ new Set([
3260
3535
  ...Object.keys(pkg.dependencies || {}),
3261
3536
  ...Object.keys(pkg.devDependencies || {}),
3262
3537
  ...Object.keys(pkg.peerDependencies || {})
3263
3538
  ]);
3264
- return requiredPackages.filter((p15) => {
3265
- const nameOnly = p15.startsWith("@") ? `@${p15.slice(1).split("@")[0] || ""}` : p15.split("@")[0] || p15;
3539
+ return requiredPackages.filter((p16) => {
3540
+ const nameOnly = p16.startsWith("@") ? `@${p16.slice(1).split("@")[0] || ""}` : p16.split("@")[0] || p16;
3266
3541
  return !installed.has(nameOnly);
3267
3542
  });
3268
3543
  } catch {
@@ -3287,21 +3562,207 @@ function installNpmPackages(projectRoot, packages, pm) {
3287
3562
  installCmd = `npm install ${packages.join(" ")}`;
3288
3563
  break;
3289
3564
  }
3290
- p3__namespace.log.info(`Installing missing dependencies with ${manager}: ${pc13__default.default.cyan(packages.join(", "))}`);
3565
+ p3__namespace.log.info(`Installing missing dependencies with ${manager}: ${pc14__default.default.cyan(packages.join(", "))}`);
3291
3566
  child_process.execSync(installCmd, {
3292
3567
  cwd: projectRoot,
3293
3568
  stdio: "inherit"
3294
3569
  });
3295
3570
  }
3571
+ function getAllFiles(dir, baseDir = dir) {
3572
+ if (!fs9__default.default.existsSync(dir)) return [];
3573
+ const entries = fs9__default.default.readdirSync(dir, { withFileTypes: true });
3574
+ const files = [];
3575
+ for (const entry of entries) {
3576
+ const fullPath = path2__default.default.join(dir, entry.name);
3577
+ const relPath = path2__default.default.relative(baseDir, fullPath).replace(/\\/g, "/");
3578
+ if (entry.isDirectory()) {
3579
+ if (entry.name === "__tests__" || entry.name.startsWith(".")) {
3580
+ continue;
3581
+ }
3582
+ files.push(...getAllFiles(fullPath, baseDir));
3583
+ } else if (entry.isFile()) {
3584
+ if (entry.name === "registry.json" || entry.name.endsWith(".test.ts") || entry.name.endsWith(".spec.ts") || entry.name.startsWith(".")) {
3585
+ continue;
3586
+ }
3587
+ files.push(relPath);
3588
+ }
3589
+ }
3590
+ return files.sort();
3591
+ }
3592
+ function computeDirectoryHash(dirPath, files) {
3593
+ if (!fs9__default.default.existsSync(dirPath)) return "";
3594
+ const hash = crypto.createHash("sha256");
3595
+ const fileList = (getAllFiles(dirPath)).sort();
3596
+ for (const relativePath of fileList) {
3597
+ const absolutePath = path2__default.default.join(dirPath, relativePath);
3598
+ if (!fs9__default.default.existsSync(absolutePath)) continue;
3599
+ const content = fs9__default.default.readFileSync(absolutePath, "utf8");
3600
+ const normalizedContent = content.replace(/\r\n/g, "\n");
3601
+ hash.update(relativePath);
3602
+ hash.update(normalizedContent);
3603
+ }
3604
+ return `sha256-${hash.digest("hex")}`;
3605
+ }
3296
3606
 
3297
3607
  // src/commands/import.ts
3608
+ var importPrompts = {
3609
+ select: p3__namespace.select,
3610
+ text: p3__namespace.text
3611
+ };
3612
+ function rewriteImports(content, aliasMap) {
3613
+ if (aliasMap.size === 0) return content;
3614
+ let rewritten = content;
3615
+ for (const [oldImport, newImport] of aliasMap.entries()) {
3616
+ const rawOld = oldImport.endsWith("/") ? oldImport.slice(0, -1) : oldImport;
3617
+ const rawNew = newImport.endsWith("/") ? newImport.slice(0, -1) : newImport;
3618
+ const escaped = rawOld.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3619
+ const regex = new RegExp(`${escaped}(?=[\\/'"\`]|$)`, "g");
3620
+ rewritten = rewritten.replace(regex, rawNew);
3621
+ }
3622
+ return rewritten;
3623
+ }
3624
+ function getModuleBaseDir(projectRoot, modType) {
3625
+ const folder = modType === "shared" ? "shared" : "features";
3626
+ return path2__default.default.join(projectRoot, "src", folder);
3627
+ }
3628
+ function getExistingModuleDir(projectRoot, modType, modName) {
3629
+ const folder = modType === "shared" ? "shared" : "features";
3630
+ const inSrc = path2__default.default.join(projectRoot, "src", folder, modName);
3631
+ if (fs9__default.default.existsSync(inSrc)) return inSrc;
3632
+ const inRoot = path2__default.default.join(projectRoot, folder, modName);
3633
+ if (fs9__default.default.existsSync(inRoot)) return inRoot;
3634
+ return null;
3635
+ }
3636
+ function checkModuleExists(projectRoot, modType, modName) {
3637
+ return getExistingModuleDir(projectRoot, modType, modName) !== null;
3638
+ }
3639
+ async function resolveDependency(depType, depName, aliasMap, projectRoot, flags, importModuleRecursive, importedModules, registryBase, index) {
3640
+ const folder = depType === "shared" ? "shared" : "features";
3641
+ const isAlreadyImportedThisRun = importedModules.has(`${depType}:${depName}`);
3642
+ if (isAlreadyImportedThisRun) return;
3643
+ const programmaticAlias = flags.alias?.[depName] || flags.alias?.[`${depType}/${depName}`];
3644
+ if (programmaticAlias) {
3645
+ aliasMap.set(`@/${folder}/${depName}/`, `@/${folder}/${programmaticAlias}/`);
3646
+ const baseDir = getModuleBaseDir(projectRoot, depType);
3647
+ const displayDest = path2__default.default.relative(
3648
+ projectRoot,
3649
+ path2__default.default.join(baseDir, programmaticAlias)
3650
+ );
3651
+ p3__namespace.log.step(`Importing '${depName}' into '${displayDest}'...`);
3652
+ await importModuleRecursive(depType, depName, programmaticAlias);
3653
+ return;
3654
+ }
3655
+ const existingDir = getExistingModuleDir(projectRoot, depType, depName);
3656
+ if (!existingDir) {
3657
+ p3__namespace.log.info(` \u21B3 Resolving required ${depType} dependency: ${pc14__default.default.cyan(depName)}`);
3658
+ await importModuleRecursive(depType, depName, depName);
3659
+ return;
3660
+ }
3661
+ const entry = (depType === "shared" ? index?.shared : index?.features)?.find(
3662
+ (item) => item.name === depName
3663
+ );
3664
+ let remoteIntegrity = entry?.integrity;
3665
+ if (!remoteIntegrity) {
3666
+ try {
3667
+ const depManifest = await fetchModuleManifest(registryBase, depType, depName);
3668
+ remoteIntegrity = depManifest.integrity;
3669
+ } catch {
3670
+ }
3671
+ }
3672
+ const localHash = computeDirectoryHash(existingDir);
3673
+ if (remoteIntegrity && localHash === remoteIntegrity) {
3674
+ p3__namespace.log.step(`\u2713 ${depType}/${depName} is up to date (reusing existing)`);
3675
+ importedModules.add(`${depType}:${depName}`);
3676
+ return;
3677
+ }
3678
+ if (flags.overwrite) {
3679
+ p3__namespace.log.step(`Overwriting '${depType}/${depName}' with registry version (--overwrite)...`);
3680
+ const baseDir = getModuleBaseDir(projectRoot, depType);
3681
+ const targetDir = path2__default.default.join(baseDir, depName);
3682
+ if (!flags.dryRun) {
3683
+ if (existingDir !== targetDir) {
3684
+ fs9__default.default.rmSync(existingDir, { recursive: true, force: true });
3685
+ }
3686
+ fs9__default.default.rmSync(targetDir, { recursive: true, force: true });
3687
+ }
3688
+ await importModuleRecursive(depType, depName, depName, { overwrite: true });
3689
+ return;
3690
+ }
3691
+ if (flags.yes) {
3692
+ p3__namespace.log.info(`Using existing '${depType}/${depName}'`);
3693
+ importedModules.add(`${depType}:${depName}`);
3694
+ return;
3695
+ }
3696
+ const choice = handleCancel(
3697
+ await importPrompts.select({
3698
+ message: `Local '${depType}/${depName}' differs from the registry version.`,
3699
+ options: [
3700
+ {
3701
+ value: "keep_existing",
3702
+ label: `Keep existing local version (Recommended)`
3703
+ },
3704
+ {
3705
+ value: "rename",
3706
+ label: "Import fresh copy under a new name (alias)"
3707
+ },
3708
+ {
3709
+ value: "overwrite",
3710
+ label: "Overwrite local version with registry version"
3711
+ }
3712
+ ]
3713
+ })
3714
+ );
3715
+ if (choice === "keep_existing" || choice === "use_existing") {
3716
+ p3__namespace.log.step(`Skipping '${depType}/${depName}' (keeping existing)`);
3717
+ importedModules.add(`${depType}:${depName}`);
3718
+ return;
3719
+ }
3720
+ if (choice === "rename") {
3721
+ const newName = handleCancel(
3722
+ await importPrompts.text({
3723
+ message: "What should we name the new copy?",
3724
+ placeholder: `${depName}-copy`,
3725
+ validate(val) {
3726
+ const v = val?.trim();
3727
+ if (!v) return "Name cannot be empty";
3728
+ if (v === depName) return `Name must be different from '${depName}'`;
3729
+ if (!/^[a-zA-Z0-9_-]+$/.test(v)) {
3730
+ return "Name can only contain alphanumeric characters, dashes, and underscores";
3731
+ }
3732
+ if (checkModuleExists(projectRoot, depType, v)) {
3733
+ return `'${v}' already exists in ${folder}, please choose another name`;
3734
+ }
3735
+ }
3736
+ })
3737
+ ).trim();
3738
+ aliasMap.set(`@/${folder}/${depName}/`, `@/${folder}/${newName}/`);
3739
+ const baseDir = getModuleBaseDir(projectRoot, depType);
3740
+ const displayDest = path2__default.default.relative(projectRoot, path2__default.default.join(baseDir, newName));
3741
+ p3__namespace.log.step(`Importing '${depName}' into '${displayDest}'...`);
3742
+ await importModuleRecursive(depType, depName, newName);
3743
+ return;
3744
+ }
3745
+ if (choice === "overwrite") {
3746
+ p3__namespace.log.step(`Overwriting '${depType}/${depName}' with registry version...`);
3747
+ const baseDir = getModuleBaseDir(projectRoot, depType);
3748
+ const targetDir = path2__default.default.join(baseDir, depName);
3749
+ if (!flags.dryRun) {
3750
+ if (existingDir !== targetDir) {
3751
+ fs9__default.default.rmSync(existingDir, { recursive: true, force: true });
3752
+ }
3753
+ fs9__default.default.rmSync(targetDir, { recursive: true, force: true });
3754
+ }
3755
+ await importModuleRecursive(depType, depName, depName, { overwrite: true });
3756
+ return;
3757
+ }
3758
+ }
3298
3759
  async function handleImport(typeArg, nameArg, flags = {}) {
3299
3760
  const projectRoot = findProjectRoot();
3300
3761
  let type = typeArg?.trim().toLowerCase();
3301
3762
  if (type === "features") type = "feature";
3302
3763
  if (!type) {
3303
3764
  type = handleCancel(
3304
- await p3__namespace.select({
3765
+ await importPrompts.select({
3305
3766
  message: "What kind of module would you like to import?",
3306
3767
  options: [
3307
3768
  {
@@ -3319,19 +3780,19 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3319
3780
  );
3320
3781
  }
3321
3782
  if (type !== "shared" && type !== "feature") {
3322
- p3__namespace.log.error(pc13__default.default.red(`Invalid module type: "${typeArg}". Must be "shared" or "feature".`));
3783
+ p3__namespace.log.error(pc14__default.default.red(`Invalid module type: "${typeArg}". Must be "shared" or "feature".`));
3323
3784
  process.exit(1);
3324
3785
  }
3325
3786
  const registryBase = getRegistryBase(projectRoot, flags.registry);
3326
3787
  const s = p3__namespace.spinner();
3327
- s.start(`Connecting to registry at ${pc13__default.default.dim(registryBase)}...`);
3788
+ s.start(`Connecting to registry at ${pc14__default.default.dim(registryBase)}...`);
3328
3789
  let index;
3329
3790
  try {
3330
3791
  index = await fetchRegistryIndex(registryBase);
3331
3792
  s.stop("Registry index loaded.");
3332
3793
  } catch (err) {
3333
- s.stop(pc13__default.default.red("Failed to load registry index."));
3334
- p3__namespace.log.error(pc13__default.default.red(err.message || String(err)));
3794
+ s.stop(pc14__default.default.red("Failed to load registry index."));
3795
+ p3__namespace.log.error(pc14__default.default.red(err.message || String(err)));
3335
3796
  process.exit(1);
3336
3797
  }
3337
3798
  const availableList = type === "shared" ? index.shared : index.features;
@@ -3343,7 +3804,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3343
3804
  return;
3344
3805
  }
3345
3806
  name = handleCancel(
3346
- await p3__namespace.select({
3807
+ await importPrompts.select({
3347
3808
  message: `Select a ${type} to import:`,
3348
3809
  options: availableList.map((item) => ({
3349
3810
  value: item.name,
@@ -3355,71 +3816,94 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3355
3816
  }
3356
3817
  if (!candidateNames.includes(name)) {
3357
3818
  const closest = findClosestMatch(name, candidateNames);
3358
- p3__namespace.log.error(pc13__default.default.red(`\u274C Module '${name}' not found in the ${type} registry.`));
3819
+ p3__namespace.log.error(pc14__default.default.red(`\u274C Module '${name}' not found in the ${type} registry.`));
3359
3820
  if (closest) {
3360
- p3__namespace.log.info(pc13__default.default.yellow(`Did you mean '${pc13__default.default.cyan(closest)}'?`));
3821
+ p3__namespace.log.info(pc14__default.default.yellow(`Did you mean '${pc14__default.default.cyan(closest)}'?`));
3361
3822
  }
3362
3823
  process.exit(1);
3363
3824
  }
3364
3825
  const importedModules = /* @__PURE__ */ new Set();
3365
3826
  const totalWrittenFiles = [];
3366
3827
  const requiredNpmPackages = /* @__PURE__ */ new Set();
3367
- async function importModuleRecursive(modType, modName) {
3368
- const modKey = `${modType}:${modName}`;
3828
+ async function importModuleRecursive(modType, registryName, targetName = registryName, options) {
3829
+ const modKey = `${modType}:${targetName}`;
3369
3830
  if (importedModules.has(modKey)) return;
3370
- p3__namespace.log.step(`Importing ${modType}: ${pc13__default.default.cyan(modName)}...`);
3831
+ p3__namespace.log.step(
3832
+ `Importing ${modType}: ${pc14__default.default.cyan(targetName)}${targetName !== registryName ? pc14__default.default.dim(` (from ${registryName})`) : ""}...`
3833
+ );
3371
3834
  let manifest;
3372
3835
  try {
3373
- manifest = await fetchModuleManifest(registryBase, modType, modName);
3836
+ manifest = await fetchModuleManifest(registryBase, modType, registryName);
3374
3837
  } catch (err) {
3375
- p3__namespace.log.error(pc13__default.default.red(`Failed to fetch manifest for ${modType} '${modName}': ${err.message}`));
3838
+ p3__namespace.log.error(pc14__default.default.red(`Failed to fetch manifest for ${modType} '${registryName}': ${err.message}`));
3376
3839
  process.exit(1);
3377
3840
  }
3841
+ const aliasMap = /* @__PURE__ */ new Map();
3842
+ if (targetName !== registryName) {
3843
+ const folder2 = modType === "shared" ? "shared" : "features";
3844
+ aliasMap.set(`@/${folder2}/${registryName}/`, `@/${folder2}/${targetName}/`);
3845
+ }
3378
3846
  const sharedDeps = manifest.dependencies?.shared || [];
3379
3847
  for (const dep of sharedDeps) {
3380
- const depPath = path15__default.default.join(projectRoot, "src", "shared", dep);
3381
- if (!fs7__default.default.existsSync(depPath) && !importedModules.has(`shared:${dep}`)) {
3382
- p3__namespace.log.info(` \u21B3 Resolving required shared dependency: ${pc13__default.default.cyan(dep)}`);
3383
- await importModuleRecursive("shared", dep);
3384
- }
3848
+ await resolveDependency(
3849
+ "shared",
3850
+ dep,
3851
+ aliasMap,
3852
+ projectRoot,
3853
+ flags,
3854
+ importModuleRecursive,
3855
+ importedModules,
3856
+ registryBase,
3857
+ index
3858
+ );
3385
3859
  }
3386
3860
  const featureDeps = manifest.dependencies?.features || [];
3387
3861
  for (const dep of featureDeps) {
3388
- const depPath = path15__default.default.join(projectRoot, "src", "features", dep);
3389
- if (!fs7__default.default.existsSync(depPath) && !importedModules.has(`feature:${dep}`)) {
3390
- p3__namespace.log.info(` \u21B3 Resolving required feature dependency: ${pc13__default.default.cyan(dep)}`);
3391
- await importModuleRecursive("feature", dep);
3392
- }
3862
+ await resolveDependency(
3863
+ "feature",
3864
+ dep,
3865
+ aliasMap,
3866
+ projectRoot,
3867
+ flags,
3868
+ importModuleRecursive,
3869
+ importedModules,
3870
+ registryBase,
3871
+ index
3872
+ );
3393
3873
  }
3394
3874
  for (const pkg of manifest.dependencies?.npm || []) {
3395
3875
  requiredNpmPackages.add(pkg);
3396
3876
  }
3397
3877
  const folder = modType === "shared" ? "shared" : "features";
3398
- const targetDir = path15__default.default.join(projectRoot, "src", folder, modName);
3878
+ const targetDir = path2__default.default.join(getModuleBaseDir(projectRoot, modType), targetName);
3879
+ const isOverwrite = Boolean(flags.overwrite || options?.overwrite);
3399
3880
  for (const file of manifest.files || []) {
3400
3881
  if (file === "registry.json") continue;
3401
- const remoteFilePath = `${folder}/${modName}/${file}`;
3402
- const localFilePath = path15__default.default.join(targetDir, file);
3403
- if (fs7__default.default.existsSync(localFilePath) && !flags.overwrite) {
3404
- p3__namespace.log.warn(` \u21B3 Skipping existing file: ${pc13__default.default.dim(path15__default.default.relative(projectRoot, localFilePath))} (use --overwrite to replace)`);
3882
+ const remoteFilePath = `${folder}/${registryName}/${file}`;
3883
+ const localFilePath = path2__default.default.join(targetDir, file);
3884
+ if (fs9__default.default.existsSync(localFilePath) && !isOverwrite) {
3885
+ p3__namespace.log.warn(
3886
+ ` \u21B3 Skipping existing file: ${pc14__default.default.dim(path2__default.default.relative(projectRoot, localFilePath))} (use --overwrite to replace)`
3887
+ );
3405
3888
  continue;
3406
3889
  }
3407
- const content = await fetchRegistryFile(registryBase, remoteFilePath);
3890
+ let content = await fetchRegistryFile(registryBase, remoteFilePath);
3891
+ content = rewriteImports(content, aliasMap);
3408
3892
  if (!flags.dryRun) {
3409
- fs7__default.default.mkdirSync(path15__default.default.dirname(localFilePath), { recursive: true });
3410
- fs7__default.default.writeFileSync(localFilePath, content, "utf-8");
3893
+ fs9__default.default.mkdirSync(path2__default.default.dirname(localFilePath), { recursive: true });
3894
+ fs9__default.default.writeFileSync(localFilePath, content, "utf-8");
3411
3895
  }
3412
- const displayPath = path15__default.default.relative(projectRoot, localFilePath);
3896
+ const displayPath = path2__default.default.relative(projectRoot, localFilePath);
3413
3897
  totalWrittenFiles.push(displayPath);
3414
- p3__namespace.log.message(` ${pc13__default.default.green("+")} ${displayPath}`);
3898
+ p3__namespace.log.message(` ${pc14__default.default.green("+")} ${displayPath}`);
3415
3899
  }
3416
3900
  importedModules.add(modKey);
3417
3901
  }
3418
- await importModuleRecursive(type, name);
3902
+ await importModuleRecursive(type, name, name);
3419
3903
  const missingNpm = getMissingNpmPackages(projectRoot, Array.from(requiredNpmPackages));
3420
3904
  if (missingNpm.length > 0) {
3421
3905
  if (flags.skipInstall || flags.dryRun) {
3422
- p3__namespace.log.info(`Required npm dependencies to install: ${pc13__default.default.cyan(missingNpm.join(", "))}`);
3906
+ p3__namespace.log.info(`Required npm dependencies to install: ${pc14__default.default.cyan(missingNpm.join(", "))}`);
3423
3907
  } else {
3424
3908
  try {
3425
3909
  installNpmPackages(projectRoot, missingNpm);
@@ -3429,11 +3913,13 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3429
3913
  }
3430
3914
  }
3431
3915
  if (flags.dryRun) {
3432
- p3__namespace.log.info(pc13__default.default.yellow("Dry run complete: No files were written to disk."));
3916
+ p3__namespace.log.info(pc14__default.default.yellow("Dry run complete: No files were written to disk."));
3433
3917
  } else {
3918
+ const baseDir = getModuleBaseDir(projectRoot, type);
3919
+ const displayTarget = path2__default.default.relative(projectRoot, path2__default.default.join(baseDir, name));
3434
3920
  p3__namespace.log.success(
3435
- pc13__default.default.green(
3436
- `Successfully imported ${type} '${name}' (${totalWrittenFiles.length} file${totalWrittenFiles.length === 1 ? "" : "s"} placed in src/${type === "shared" ? "shared" : "features"}/${name})`
3921
+ pc14__default.default.green(
3922
+ `Successfully imported ${type} '${name}' (${totalWrittenFiles.length} file${totalWrittenFiles.length === 1 ? "" : "s"} placed in ${displayTarget})`
3437
3923
  )
3438
3924
  );
3439
3925
  }
@@ -3442,24 +3928,70 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3442
3928
  // src/cli.ts
3443
3929
  function createCli() {
3444
3930
  const cli = cac.cac("mason");
3445
- 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").action(async (args, flags) => {
3931
+ 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").action(async (args, flags) => {
3446
3932
  if (args && args.length > 0) {
3447
3933
  p3__namespace.log.error(
3448
- pc13__default.default.red(
3934
+ pc14__default.default.red(
3449
3935
  `Unknown command: "${args.join(" ")}". Use --help to list available commands.`
3450
3936
  )
3451
3937
  );
3452
3938
  process.exit(1);
3453
3939
  }
3454
3940
  try {
3941
+ const projectRoot = findProjectRoot();
3942
+ const mode = getEffectiveMode(projectRoot, flags);
3943
+ if (mode === "registry") {
3944
+ const choice2 = handleCancel(
3945
+ await p3__namespace.select({
3946
+ message: "\u{1F3DB}\uFE0F Mason (Registry Mode): What would you like to scaffold?",
3947
+ options: [
3948
+ {
3949
+ value: "create_feature",
3950
+ label: "Initialize New Feature",
3951
+ hint: "Scaffold a new feature slice under features/"
3952
+ },
3953
+ {
3954
+ value: "create_shared",
3955
+ label: "Initialize New Shared Module",
3956
+ hint: "Scaffold a new shared utility/port under shared/"
3957
+ },
3958
+ {
3959
+ value: "create_usecase",
3960
+ label: "Create New Use Case",
3961
+ hint: "Add a new use case to an existing feature"
3962
+ },
3963
+ {
3964
+ value: "import",
3965
+ label: "Import from Remote Registry",
3966
+ hint: "Download a module from upstream"
3967
+ }
3968
+ ]
3969
+ })
3970
+ );
3971
+ switch (choice2) {
3972
+ case "create_feature":
3973
+ await handleCreate("feature", void 0, flags);
3974
+ break;
3975
+ case "create_shared":
3976
+ await handleCreate("shared", void 0, flags);
3977
+ break;
3978
+ case "create_usecase":
3979
+ await handleCreate("usecase", void 0, flags);
3980
+ break;
3981
+ case "import":
3982
+ await handleImport(void 0, void 0, flags);
3983
+ break;
3984
+ }
3985
+ return;
3986
+ }
3455
3987
  const choice = handleCancel(
3456
3988
  await p3__namespace.select({
3457
3989
  message: "\u{1F9F1} Mason: What would you like to do?",
3458
3990
  options: [
3459
3991
  {
3460
3992
  value: "create",
3461
- label: "Create local component",
3462
- hint: "Scaffold a new feature, use case, domain entity, interface, etc."
3993
+ label: "Create component",
3994
+ hint: "Scaffold a new feature, shared module, use case, domain entity, etc."
3463
3995
  },
3464
3996
  {
3465
3997
  value: "import",
@@ -3480,8 +4012,8 @@ function createCli() {
3480
4012
  });
3481
4013
  cli.command(
3482
4014
  "create [type] [name]",
3483
- "Scaffold local Clean Architecture components (feature, usecase, domain, etc.)"
3484
- ).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("-f, --feature <feature>", "Target feature name").option("-t, --target <dir>", "Override target directory").option("--express", "Include Express presentation layer (pxExpress)").option("--no-express", "Skip Express presentation layer").option("-i, --interface <interface>", "Interface or abstract class to implement").option("-k, --kind <kind>", "Implementation kind ('stub' or 'concrete')").option("-g, --global", "Place in global infrastructure/ instead of feature").option("-m, --method <method>", "HTTP method (get, post, put, patch, delete)").option("-p, --path <path>", "Route subpath (e.g. /:id)").option("-u, --usecase <usecase>", "Connected use case name").option("--message <message>", "Custom success message").option("--domainNames <names>", "Comma-separated domain entity names").example("mason create").example("mason create feature users").example("mason create usecase PlaceOrder -f orders").example("mason create domain User -f users").example("mason create infrastructure PostgresUserRepository -f users -i IUserRepository").example("mason create interface IUserRepository -f users").example("mason create httpHandler GetUserHttp -f users -m get -p /:id").example("mason create injectable PasswordHasher").action(async (type, name, flags = {}) => {
4015
+ "Scaffold local Clean Architecture components (feature, shared, usecase, domain, etc.)"
4016
+ ).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("-f, --feature <feature>", "Target feature name").option("-t, --target <dir>", "Override target directory").option("--dest <dir>", "Override target directory (alias for --target)").option("--registry", "Scaffold in registry mode (stamps registry.json)").option("--express", "Include Express presentation layer (pxExpress)").option("--no-express", "Skip Express presentation layer").option("-i, --interface <interface>", "Interface or abstract class to implement").option("-k, --kind <kind>", "Implementation kind ('stub' or 'concrete')").option("-g, --global", "Place in global infrastructure/ instead of feature").option("-m, --method <method>", "HTTP method (get, post, put, patch, delete)").option("-p, --path <path>", "Route subpath (e.g. /:id)").option("-u, --usecase <usecase>", "Connected use case name").option("--message <message>", "Custom success message").option("--domainNames <names>", "Comma-separated domain entity names").example("mason create").example("mason create feature users").example("mason create usecase PlaceOrder -f orders").example("mason create domain User -f users").example("mason create infrastructure PostgresUserRepository -f users -i IUserRepository").example("mason create interface IUserRepository -f users").example("mason create httpHandler GetUserHttp -f users -m get -p /:id").example("mason create injectable PasswordHasher").action(async (type, name, flags = {}) => {
3485
4017
  try {
3486
4018
  await handleCreate(type, name, flags);
3487
4019
  } catch (err) {
@@ -3500,6 +4032,7 @@ function createCli() {
3500
4032
  });
3501
4033
  const createSubtypes = [
3502
4034
  "feature",
4035
+ "shared",
3503
4036
  "usecase",
3504
4037
  "domain",
3505
4038
  "infrastructure",
@@ -3520,7 +4053,7 @@ function createCli() {
3520
4053
  });
3521
4054
  }
3522
4055
  cli.help();
3523
- cli.version("1.0.10");
4056
+ cli.version("1.0.14");
3524
4057
  return cli;
3525
4058
  }
3526
4059
  function handleCliError(err) {
@@ -3528,7 +4061,7 @@ function handleCliError(err) {
3528
4061
  process.exit(0);
3529
4062
  }
3530
4063
  const message = err instanceof Error ? err.message : String(err);
3531
- p3__namespace.log.error(pc13__default.default.red(`Error: ${message}`));
4064
+ p3__namespace.log.error(pc14__default.default.red(`Error: ${message}`));
3532
4065
  process.exit(1);
3533
4066
  }
3534
4067
  async function runCli(argv = process.argv) {