@solid-stack/mason 1.0.11 → 1.0.13

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
@@ -2,13 +2,14 @@
2
2
  'use strict';
3
3
 
4
4
  var cac = require('cac');
5
- var p3 = require('@clack/prompts');
6
- var pc13 = require('picocolors');
7
- var path6 = require('path');
8
- var fs7 = require('fs');
5
+ var p15 = require('@clack/prompts');
6
+ var pc15 = require('picocolors');
7
+ var fs12 = 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 }; }
@@ -31,16 +32,16 @@ function _interopNamespace(e) {
31
32
  return Object.freeze(n);
32
33
  }
33
34
 
34
- var p3__namespace = /*#__PURE__*/_interopNamespace(p3);
35
- var pc13__default = /*#__PURE__*/_interopDefault(pc13);
36
- var path6__default = /*#__PURE__*/_interopDefault(path6);
37
- var fs7__default = /*#__PURE__*/_interopDefault(fs7);
35
+ var p15__namespace = /*#__PURE__*/_interopNamespace(p15);
36
+ var pc15__default = /*#__PURE__*/_interopDefault(pc15);
37
+ var fs12__default = /*#__PURE__*/_interopDefault(fs12);
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 = path6__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 = fs12__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 = path6__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 (!fs12__default.default.existsSync(dir)) {
65
+ fs12__default.default.mkdirSync(dir, { recursive: true });
65
66
  }
66
- fs7__default.default.writeFileSync(absolutePath, content, "utf-8");
67
+ fs12__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
+ p15__namespace.log.warn(pc15__default.default.yellow(pc15__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
+ p15__namespace.log.success(`${pc15__default.default.bold(f.relativePath)} ${pc15__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
+ p15__namespace.log.warn(`${pc15__default.default.bold(f.relativePath)} ${pc15__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
+ p15__namespace.log.info(`${pc15__default.default.dim(f.relativePath)} ${pc15__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
+ p15__namespace.log.info(`${pc15__default.default.bold(f.relativePath)} ${pc15__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 (fs12__default.default.existsSync(configPath)) {
162
+ try {
163
+ return JSON.parse(fs12__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 (fs12__default.default.existsSync(path2__default.default.join(projectRoot, "index.json")) && (fs12__default.default.existsSync(path2__default.default.join(projectRoot, "features")) || fs12__default.default.existsSync(path2__default.default.join(projectRoot, "shared"))) && !fs12__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 (fs12__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 (fs12__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 = path6__default.default.resolve(startDir);
218
+ let current = path2__default.default.resolve(startDir);
158
219
  while (true) {
159
- if (fs7__default.default.existsSync(path6__default.default.join(current, "package.json"))) {
220
+ if (fs12__default.default.existsSync(path2__default.default.join(current, "package.json"))) {
160
221
  return current;
161
222
  }
162
- const parent = path6__default.default.dirname(current);
223
+ const parent = path2__default.default.dirname(current);
163
224
  if (parent === current) {
164
- return path6__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 path6__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 (!fs12__default.default.existsSync(featuresDir)) return [];
175
239
  try {
176
- const entries = fs7__default.default.readdirSync(featuresDir, { withFileTypes: true });
240
+ const entries = fs12__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 = path6__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 (!fs12__default.default.existsSync(domainDir)) continue;
188
252
  try {
189
- const files = fs7__default.default.readdirSync(domainDir);
253
+ const files = fs12__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 = path6__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 = fs12__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 = path6__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 (!fs12__default.default.existsSync(domainDir)) continue;
226
290
  try {
227
- const files = fs7__default.default.readdirSync(domainDir);
291
+ const files = fs12__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: path6__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 = path6__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 (!fs12__default.default.existsSync(useCasesDir)) continue;
251
315
  try {
252
- const files = fs7__default.default.readdirSync(useCasesDir);
316
+ const files = fs12__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: path6__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 && fs12__default.default.existsSync(cachedTemplatesDir)) {
285
349
  return cachedTemplatesDir;
286
350
  }
287
- const currentDir = typeof __dirname !== "undefined" ? __dirname : path6__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 = path6__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 (fs12__default.default.existsSync(cand1)) {
290
354
  cachedTemplatesDir = cand1;
291
355
  return cand1;
292
356
  }
293
- const cand2 = path6__default.default.resolve(currentDir, "../templates");
294
- if (fs7__default.default.existsSync(cand2)) {
357
+ const cand2 = path2__default.default.resolve(currentDir, "../templates");
358
+ if (fs12__default.default.existsSync(cand2)) {
295
359
  cachedTemplatesDir = cand2;
296
360
  return cand2;
297
361
  }
298
- const cand3 = path6__default.default.resolve(currentDir, "templates");
299
- if (fs7__default.default.existsSync(cand3)) {
362
+ const cand3 = path2__default.default.resolve(currentDir, "templates");
363
+ if (fs12__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 = path6__default.default.join(curr, "templates");
306
- if (fs7__default.default.existsSync(candTemplates)) {
369
+ const candTemplates = path2__default.default.join(curr, "templates");
370
+ if (fs12__default.default.existsSync(candTemplates)) {
307
371
  cachedTemplatesDir = candTemplates;
308
372
  return candTemplates;
309
373
  }
310
- const parent = path6__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 = path6__default.default.isAbsolute(relativePath) ? relativePath : path6__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 fs12__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 || path6__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
407
- const filePath = path6__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((p17) => `${p17.name}: ${p17.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 || path6__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
457
- const filePath = path6__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 && fs12__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 = path6__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 (!fs12__default.default.existsSync(domainDir)) continue;
704
768
  for (const cand of candidateNames) {
705
- const p1 = path6__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 (fs12__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 = path6__default.default.join(projectRoot, "domain", `${cand}.ts`);
713
- if (fs7__default.default.existsSync(p1)) return { filePath: p1, ifaceName: cand };
714
- const p22 = path6__default.default.join(projectRoot, "src", "domain", `${cand}.ts`);
715
- if (fs7__default.default.existsSync(p22)) return { filePath: p22, ifaceName: cand };
716
- const p32 = path6__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 (fs12__default.default.existsSync(p1)) return { filePath: p1, ifaceName: cand };
778
+ const p22 = path2__default.default.join(projectRoot, "src", "domain", `${cand}.ts`);
779
+ if (fs12__default.default.existsSync(p22)) return { filePath: p22, ifaceName: cand };
780
+ const p32 = path2__default.default.join(projectRoot, "interfaces", `${cand}.ts`);
781
+ if (fs12__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 = fs12__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 ? `${path6__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 ? `${path6__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 ? path6__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "infrastructure") : path6__default.default.join(projectRoot, "infrastructure"));
911
- const filePath = path6__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 = path6__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 = path6__default.default.resolve(path6__default.default.dirname(ifaceFilePath), imp.specifier);
939
- let rel = path6__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 p17 of [...inputProps, ...outputProps]) {
1077
+ const rawType = p17.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((p17) => ({
1084
+ name: p17.name,
1085
+ optional: p17.optional,
1086
+ type: p17.optional && !p17.type.includes("undefined") ? `${p17.type} | undefined` : p17.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((p17) => ({
1089
+ name: p17.name,
1090
+ optional: p17.optional,
1091
+ type: p17.optional && !p17.type.includes("undefined") ? `${p17.type} | undefined` : p17.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 || path6__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "useCases");
1127
- const filePath = path6__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 = path6__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 = path6__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress");
1212
- const handlersDir = path6__default.default.join(pxDir, "handlers");
1213
- const pxIndexPath = path6__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 (!fs12__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 = path6__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 = path6__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 (fs12__default.default.existsSync(providerPath) && !options.overwrite) {
1316
+ let content = fs12__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 || path6__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: path6__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: path6__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: path6__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: path6__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: path6__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: path6__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: path6__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: path6__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: path6__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: path6__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 = path6__default.default.join(targetDir, "pxExpress");
1669
- const pxIndexPath = path6__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(
@@ -1695,23 +1759,23 @@ var PromptCancelledError = class extends Error {
1695
1759
  }
1696
1760
  };
1697
1761
  function handleCancel(result) {
1698
- if (p3__namespace.isCancel(result)) {
1699
- p3__namespace.cancel("Operation cancelled.");
1762
+ if (p15__namespace.isCancel(result)) {
1763
+ p15__namespace.cancel("Operation cancelled.");
1700
1764
  throw new PromptCancelledError();
1701
1765
  }
1702
1766
  return 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
+ p15__namespace.intro(pc15__default.default.bgCyan(pc15__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((p17) => `${p17.name}: ${p17.type}${p17.optional ? "?" : ""}`).join(", ");
1776
+ p15__namespace.log.info(pc15__default.default.dim(`Pre-configured fields: ${summary}`));
1713
1777
  const addMore = handleCancel(
1714
- await p3__namespace.confirm({
1778
+ await p15__namespace.confirm({
1715
1779
  message: "Would you like to add custom fields/properties?",
1716
1780
  initialValue: false
1717
1781
  })
@@ -1720,10 +1784,10 @@ 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
+ p15__namespace.log.info(pc15__default.default.cyan("Enter properties (leave name blank when finished):"));
1724
1788
  while (true) {
1725
1789
  const name = handleCancel(
1726
- await p3__namespace.text({
1790
+ await p15__namespace.text({
1727
1791
  message: "Property name (or press Enter to finish):",
1728
1792
  placeholder: "e.g. title, email, status"
1729
1793
  })
@@ -1732,7 +1796,7 @@ async function promptProperties(options = {}) {
1732
1796
  break;
1733
1797
  }
1734
1798
  const type = handleCancel(
1735
- await p3__namespace.select({
1799
+ await p15__namespace.select({
1736
1800
  message: `Type for property "${name.trim()}":`,
1737
1801
  options: [
1738
1802
  { value: "string", label: "string" },
@@ -1749,7 +1813,7 @@ async function promptProperties(options = {}) {
1749
1813
  let finalType = type;
1750
1814
  if (type === "custom") {
1751
1815
  finalType = handleCancel(
1752
- await p3__namespace.text({
1816
+ await p15__namespace.text({
1753
1817
  message: `Enter custom type for "${name.trim()}":`,
1754
1818
  placeholder: "e.g. UserRole, string | null",
1755
1819
  validate: (val) => val && val.trim() ? void 0 : "Type cannot be empty"
@@ -1757,7 +1821,7 @@ async function promptProperties(options = {}) {
1757
1821
  );
1758
1822
  }
1759
1823
  const optional = handleCancel(
1760
- await p3__namespace.confirm({
1824
+ await p15__namespace.confirm({
1761
1825
  message: `Is "${name.trim()}" optional?`,
1762
1826
  initialValue: false
1763
1827
  })
@@ -1767,8 +1831,8 @@ async function promptProperties(options = {}) {
1767
1831
  type: finalType.trim(),
1768
1832
  optional
1769
1833
  });
1770
- p3__namespace.log.success(
1771
- pc13__default.default.green(`Added: ${name.trim()}${optional ? "?" : ""}: ${finalType.trim()}`)
1834
+ p15__namespace.log.success(
1835
+ pc15__default.default.green(`Added: ${name.trim()}${optional ? "?" : ""}: ${finalType.trim()}`)
1772
1836
  );
1773
1837
  }
1774
1838
  return properties;
@@ -1777,13 +1841,14 @@ 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";
1783
1848
  }
1784
1849
  if (!name) {
1785
1850
  name = handleCancel(
1786
- await p3__namespace.text({
1851
+ await p15__namespace.text({
1787
1852
  message: "What is the feature name?",
1788
1853
  placeholder: "e.g. users, auth, orders, billing",
1789
1854
  validate: (val) => {
@@ -1794,18 +1859,19 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1794
1859
  ).trim();
1795
1860
  }
1796
1861
  const normalizedName = toCamelCase(name);
1797
- const defaultDir = path6__default.default.relative(projectRoot, path6__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
- await p3__namespace.text({
1867
+ await p15__namespace.text({
1802
1868
  message: "Target directory for the feature:",
1803
1869
  initialValue: defaultDir,
1804
1870
  defaultValue: defaultDir
1805
1871
  })
1806
1872
  ).trim();
1807
1873
  }
1808
- const targetDir = flags.target ? path6__default.default.resolve(projectRoot, flags.target) : path6__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;
@@ -1820,14 +1886,14 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1820
1886
  const customUseCases = [];
1821
1887
  if (!flags.yes) {
1822
1888
  includeExpress = handleCancel(
1823
- await p3__namespace.confirm({
1889
+ await p15__namespace.confirm({
1824
1890
  message: "Include Express Presentation layer (pxExpress routes & handlers)?",
1825
1891
  initialValue: true
1826
1892
  })
1827
1893
  );
1828
1894
  if (includeExpress) {
1829
1895
  routePath = handleCancel(
1830
- await p3__namespace.text({
1896
+ await p15__namespace.text({
1831
1897
  message: "Express route prefix:",
1832
1898
  initialValue: `/${normalizedName}`,
1833
1899
  defaultValue: `/${normalizedName}`
@@ -1835,14 +1901,14 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1835
1901
  ).trim();
1836
1902
  }
1837
1903
  includeDomain = handleCancel(
1838
- await p3__namespace.confirm({
1904
+ await p15__namespace.confirm({
1839
1905
  message: "Include Domain Entity/Entities?",
1840
1906
  initialValue: true
1841
1907
  })
1842
1908
  );
1843
1909
  if (includeDomain) {
1844
1910
  const domainInput = handleCancel(
1845
- await p3__namespace.text({
1911
+ await p15__namespace.text({
1846
1912
  message: "Domain entity name(s) (comma-separated):",
1847
1913
  initialValue: defaultEntity,
1848
1914
  defaultValue: defaultEntity,
@@ -1861,21 +1927,21 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1861
1927
  entityNames = [defaultEntity];
1862
1928
  }
1863
1929
  includeInterface = handleCancel(
1864
- await p3__namespace.confirm({
1930
+ await p15__namespace.confirm({
1865
1931
  message: `Include Domain Repository interface(s) for ${entityNames.join(", ")}?`,
1866
1932
  initialValue: true
1867
1933
  })
1868
1934
  );
1869
1935
  if (includeInterface) {
1870
1936
  includeInfra = handleCancel(
1871
- await p3__namespace.confirm({
1937
+ await p15__namespace.confirm({
1872
1938
  message: `Include Infrastructure Stub implementation(s) for ${entityNames.join(", ")}?`,
1873
1939
  initialValue: true
1874
1940
  })
1875
1941
  );
1876
1942
  }
1877
1943
  includeUseCase = handleCancel(
1878
- await p3__namespace.confirm({
1944
+ await p15__namespace.confirm({
1879
1945
  message: "Include Domain Use Cases?",
1880
1946
  initialValue: true
1881
1947
  })
@@ -1889,7 +1955,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1889
1955
  { value: "delete", label: "Delete (Delete<Entity>)", hint: "DELETE /:id" }
1890
1956
  ];
1891
1957
  crudOperations = handleCancel(
1892
- await p3__namespace.multiselect({
1958
+ await p15__namespace.multiselect({
1893
1959
  message: "Select standard CRUD use cases to generate:",
1894
1960
  options: crudChoices,
1895
1961
  initialValues: ["create", "read", "list", "update", "delete"],
@@ -1897,13 +1963,13 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1897
1963
  })
1898
1964
  );
1899
1965
  includeTests = handleCancel(
1900
- await p3__namespace.confirm({
1966
+ await p15__namespace.confirm({
1901
1967
  message: "Generate Vitest unit tests for use cases?",
1902
1968
  initialValue: true
1903
1969
  })
1904
1970
  );
1905
1971
  const wantCustom = handleCancel(
1906
- await p3__namespace.confirm({
1972
+ await p15__namespace.confirm({
1907
1973
  message: "Do you want to add any custom use cases to this feature?",
1908
1974
  initialValue: false
1909
1975
  })
@@ -1913,7 +1979,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1913
1979
  const availableIfaces = entityNames.map((e) => `I${e}Repository`);
1914
1980
  while (addMore) {
1915
1981
  const customName = handleCancel(
1916
- await p3__namespace.text({
1982
+ await p15__namespace.text({
1917
1983
  message: "Custom Use Case name:",
1918
1984
  placeholder: "e.g. ResetPassword, VerifyEmail, AssignRole",
1919
1985
  validate: (v) => v && v.trim() ? void 0 : "Use Case name cannot be empty"
@@ -1923,7 +1989,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1923
1989
  let selectedDeps = [];
1924
1990
  if (availableIfaces.length > 0) {
1925
1991
  selectedDeps = handleCancel(
1926
- await p3__namespace.multiselect({
1992
+ await p15__namespace.multiselect({
1927
1993
  message: `Select domain interfaces to inject into ${customUcName}:`,
1928
1994
  options: availableIfaces.map((i) => ({ value: i, label: i })),
1929
1995
  required: false
@@ -1932,7 +1998,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1932
1998
  }
1933
1999
  let customInputProps = [];
1934
2000
  const hasInput = handleCancel(
1935
- await p3__namespace.confirm({
2001
+ await p15__namespace.confirm({
1936
2002
  message: `Does ${customUcName} accept input arguments?`,
1937
2003
  initialValue: true
1938
2004
  })
@@ -1943,7 +2009,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1943
2009
  }
1944
2010
  let customOutputProps = [];
1945
2011
  const hasCustomOutput = handleCancel(
1946
- await p3__namespace.confirm({
2012
+ await p15__namespace.confirm({
1947
2013
  message: `Does ${customUcName} return custom output data?`,
1948
2014
  initialValue: false
1949
2015
  })
@@ -1953,7 +2019,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1953
2019
  });
1954
2020
  }
1955
2021
  const customIncludeTest = handleCancel(
1956
- await p3__namespace.confirm({
2022
+ await p15__namespace.confirm({
1957
2023
  message: `Generate Vitest unit test for ${customUcName}?`,
1958
2024
  initialValue: true
1959
2025
  })
@@ -1963,14 +2029,14 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1963
2029
  let customHttpPath = `/${toKebabCase(customUcName)}`;
1964
2030
  if (includeExpress) {
1965
2031
  customCreateHttp = handleCancel(
1966
- await p3__namespace.confirm({
2032
+ await p15__namespace.confirm({
1967
2033
  message: `Generate a matching ExpressRoute HTTP handler for ${customUcName}?`,
1968
2034
  initialValue: true
1969
2035
  })
1970
2036
  );
1971
2037
  if (customCreateHttp) {
1972
2038
  customHttpMethod = handleCancel(
1973
- await p3__namespace.select({
2039
+ await p15__namespace.select({
1974
2040
  message: "HTTP method for route:",
1975
2041
  options: [
1976
2042
  { value: "post", label: "POST" },
@@ -1983,7 +2049,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1983
2049
  })
1984
2050
  );
1985
2051
  customHttpPath = handleCancel(
1986
- await p3__namespace.text({
2052
+ await p15__namespace.text({
1987
2053
  message: "Route subpath (relative to feature prefix):",
1988
2054
  initialValue: `/${toKebabCase(customUcName)}`,
1989
2055
  defaultValue: `/${toKebabCase(customUcName)}`
@@ -2004,7 +2070,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2004
2070
  validationFields: customInputProps
2005
2071
  });
2006
2072
  addMore = handleCancel(
2007
- await p3__namespace.confirm({
2073
+ await p15__namespace.confirm({
2008
2074
  message: "Add another custom use case?",
2009
2075
  initialValue: false
2010
2076
  })
@@ -2013,7 +2079,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2013
2079
  }
2014
2080
  }
2015
2081
  includeDiProvider = handleCancel(
2016
- await p3__namespace.confirm({
2082
+ await p15__namespace.confirm({
2017
2083
  message: "Generate / update feature DI provider (diProvider.ts)?",
2018
2084
  initialValue: true
2019
2085
  })
@@ -2025,8 +2091,8 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2025
2091
  entityNames = [defaultEntity];
2026
2092
  }
2027
2093
  }
2028
- const s = p3__namespace.spinner();
2029
- s.start(`Generating feature '${pc13__default.default.cyan(normalizedName)}'...`);
2094
+ const s = p15__namespace.spinner();
2095
+ s.start(`Generating feature '${pc15__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 (!fs12__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
+ p15__namespace.outro(
2142
+ pc15__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 p15__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 p15__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 p15__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 p15__namespace.confirm({
2292
+ message: "Include stub implementation in infrastructure/?",
2293
+ initialValue: true
2294
+ })
2295
+ );
2296
+ }
2297
+ const s = p15__namespace.spinner();
2298
+ s.start(`Generating shared module '${pc15__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
+ p15__namespace.outro(
2312
+ pc15__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();
@@ -2059,14 +2323,14 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2059
2323
  const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
2060
2324
  featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
2061
2325
  const selected = handleCancel(
2062
- await p3__namespace.select({
2326
+ await p15__namespace.select({
2063
2327
  message: "Select target feature:",
2064
2328
  options: featureOptions
2065
2329
  })
2066
2330
  );
2067
2331
  if (selected === "__custom__") {
2068
2332
  feature = handleCancel(
2069
- await p3__namespace.text({
2333
+ await p15__namespace.text({
2070
2334
  message: "Enter feature name:",
2071
2335
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2072
2336
  })
@@ -2076,7 +2340,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2076
2340
  }
2077
2341
  } else {
2078
2342
  feature = handleCancel(
2079
- await p3__namespace.text({
2343
+ await p15__namespace.text({
2080
2344
  message: "Enter target feature name:",
2081
2345
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2082
2346
  })
@@ -2087,7 +2351,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2087
2351
  let name = useCaseNameArg?.trim();
2088
2352
  if (!name) {
2089
2353
  name = handleCancel(
2090
- await p3__namespace.text({
2354
+ await p15__namespace.text({
2091
2355
  message: "Use Case name:",
2092
2356
  placeholder: "e.g. CreateUser, GetUser, CancelOrder",
2093
2357
  validate: (v) => v && v.trim() ? void 0 : "Use Case name cannot be empty"
@@ -2103,7 +2367,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2103
2367
  label: i.name
2104
2368
  }));
2105
2369
  selectedDeps = handleCancel(
2106
- await p3__namespace.multiselect({
2370
+ await p15__namespace.multiselect({
2107
2371
  message: "Select domain interfaces to inject into use case:",
2108
2372
  options: multiOptions,
2109
2373
  required: false
@@ -2118,7 +2382,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2118
2382
  let httpPath = "/";
2119
2383
  if (!flags.yes) {
2120
2384
  const hasInput = handleCancel(
2121
- await p3__namespace.confirm({
2385
+ await p15__namespace.confirm({
2122
2386
  message: "Does this use case accept input arguments?",
2123
2387
  initialValue: true
2124
2388
  })
@@ -2128,7 +2392,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2128
2392
  });
2129
2393
  }
2130
2394
  const hasCustomOutput = handleCancel(
2131
- await p3__namespace.confirm({
2395
+ await p15__namespace.confirm({
2132
2396
  message: "Does this use case return custom output data?",
2133
2397
  initialValue: false
2134
2398
  })
@@ -2138,20 +2402,20 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2138
2402
  });
2139
2403
  }
2140
2404
  includeTest = handleCancel(
2141
- await p3__namespace.confirm({
2405
+ await p15__namespace.confirm({
2142
2406
  message: "Generate Vitest unit test skeleton (.test.ts)?",
2143
2407
  initialValue: true
2144
2408
  })
2145
2409
  );
2146
2410
  createHttp = handleCancel(
2147
- await p3__namespace.confirm({
2411
+ await p15__namespace.confirm({
2148
2412
  message: "Generate a matching ExpressRoute HTTP handler?",
2149
2413
  initialValue: false
2150
2414
  })
2151
2415
  );
2152
2416
  if (createHttp) {
2153
2417
  httpMethod = handleCancel(
2154
- await p3__namespace.select({
2418
+ await p15__namespace.select({
2155
2419
  message: "HTTP method for route:",
2156
2420
  options: [
2157
2421
  { value: "post", label: "POST" },
@@ -2164,7 +2428,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2164
2428
  })
2165
2429
  );
2166
2430
  httpPath = handleCancel(
2167
- await p3__namespace.text({
2431
+ await p15__namespace.text({
2168
2432
  message: "Route subpath (relative to feature prefix):",
2169
2433
  initialValue: "/",
2170
2434
  defaultValue: "/"
@@ -2172,8 +2436,8 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2172
2436
  ).trim();
2173
2437
  }
2174
2438
  }
2175
- const s = p3__namespace.spinner();
2176
- s.start(`Generating use case '${pc13__default.default.cyan(ucName)}'...`);
2439
+ const s = p15__namespace.spinner();
2440
+ s.start(`Generating use case '${pc15__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
+ p15__namespace.outro(pc15__default.default.green(`Use case '${ucName}' created in feature '${feature}'!`));
2209
2473
  }
2210
2474
  async function runDomainCommand(domainNameArg, flags = {}) {
2211
2475
  const projectRoot = findProjectRoot();
@@ -2216,14 +2480,14 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2216
2480
  const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
2217
2481
  featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
2218
2482
  const selected = handleCancel(
2219
- await p3__namespace.select({
2483
+ await p15__namespace.select({
2220
2484
  message: "Select target feature:",
2221
2485
  options: featureOptions
2222
2486
  })
2223
2487
  );
2224
2488
  if (selected === "__custom__") {
2225
2489
  feature = handleCancel(
2226
- await p3__namespace.text({
2490
+ await p15__namespace.text({
2227
2491
  message: "Enter feature name:",
2228
2492
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2229
2493
  })
@@ -2233,7 +2497,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2233
2497
  }
2234
2498
  } else {
2235
2499
  feature = handleCancel(
2236
- await p3__namespace.text({
2500
+ await p15__namespace.text({
2237
2501
  message: "Enter target feature name:",
2238
2502
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2239
2503
  })
@@ -2244,7 +2508,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2244
2508
  let name = domainNameArg?.trim();
2245
2509
  if (!name) {
2246
2510
  name = handleCancel(
2247
- await p3__namespace.text({
2511
+ await p15__namespace.text({
2248
2512
  message: "Domain model / entity name:",
2249
2513
  placeholder: "e.g. User, Order, Product, Address",
2250
2514
  validate: (v) => v && v.trim() ? void 0 : "Domain name cannot be empty"
@@ -2256,7 +2520,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2256
2520
  let properties = [];
2257
2521
  if (!flags.yes) {
2258
2522
  kind = handleCancel(
2259
- await p3__namespace.select({
2523
+ await p15__namespace.select({
2260
2524
  message: "Select domain model type:",
2261
2525
  options: [
2262
2526
  { value: "interface", label: "TypeScript Interface (pure data structure)" },
@@ -2275,8 +2539,8 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2275
2539
  defaults: kind === "valueObject" ? [{ name: "value", type: "string" }] : defaultProps
2276
2540
  });
2277
2541
  }
2278
- const s = p3__namespace.spinner();
2279
- s.start(`Generating domain model '${pc13__default.default.cyan(entityName)}'...`);
2542
+ const s = p15__namespace.spinner();
2543
+ s.start(`Generating domain model '${pc15__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
+ p15__namespace.outro(pc15__default.default.green(`Domain model '${entityName}' created in feature '${feature}'!`));
2293
2557
  }
2294
2558
  async function runInfrastructureCommand(classNameArg, flags = {}) {
2295
2559
  const projectRoot = findProjectRoot();
@@ -2298,7 +2562,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2298
2562
  let feature = flags.feature?.trim();
2299
2563
  if (!flags.yes && !flags.global && !flags.feature) {
2300
2564
  scope = handleCancel(
2301
- await p3__namespace.select({
2565
+ await p15__namespace.select({
2302
2566
  message: "Where should this infrastructure implementation be placed?",
2303
2567
  options: [
2304
2568
  { value: "feature", label: "Feature-level (features/<feature>/infrastructure)" },
@@ -2313,14 +2577,14 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2313
2577
  const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
2314
2578
  featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
2315
2579
  const selected = handleCancel(
2316
- await p3__namespace.select({
2580
+ await p15__namespace.select({
2317
2581
  message: "Select target feature:",
2318
2582
  options: featureOptions
2319
2583
  })
2320
2584
  );
2321
2585
  if (selected === "__custom__") {
2322
2586
  feature = handleCancel(
2323
- await p3__namespace.text({
2587
+ await p15__namespace.text({
2324
2588
  message: "Enter feature name:",
2325
2589
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2326
2590
  })
@@ -2330,7 +2594,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2330
2594
  }
2331
2595
  } else {
2332
2596
  feature = handleCancel(
2333
- await p3__namespace.text({
2597
+ await p15__namespace.text({
2334
2598
  message: "Enter target feature name:",
2335
2599
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2336
2600
  })
@@ -2343,7 +2607,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2343
2607
  let name = classNameArg?.trim();
2344
2608
  if (!name) {
2345
2609
  name = handleCancel(
2346
- await p3__namespace.text({
2610
+ await p15__namespace.text({
2347
2611
  message: "Infrastructure class name:",
2348
2612
  placeholder: "e.g. StubUserRepository, PostgresUserRepository, S3FileStorage",
2349
2613
  validate: (v) => v && v.trim() ? void 0 : "Class name cannot be empty"
@@ -2362,7 +2626,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2362
2626
  (i) => className.toLowerCase().includes(removeInterfacePrefix(i.name).toLowerCase())
2363
2627
  );
2364
2628
  const ifaceChoice = handleCancel(
2365
- await p3__namespace.select({
2629
+ await p15__namespace.select({
2366
2630
  message: "Does this class implement a domain interface or abstract class?",
2367
2631
  options: [
2368
2632
  ...interfaces.map((i) => ({ value: i.name, label: i.name })),
@@ -2374,7 +2638,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2374
2638
  );
2375
2639
  if (ifaceChoice === "__custom__") {
2376
2640
  implementsInterface = handleCancel(
2377
- await p3__namespace.text({
2641
+ await p15__namespace.text({
2378
2642
  message: "Enter interface or abstract class name:",
2379
2643
  placeholder: "e.g. IUserRepository, BaseStorageAdapter",
2380
2644
  validate: (v) => v && v.trim() ? void 0 : "Name cannot be empty"
@@ -2385,14 +2649,14 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2385
2649
  }
2386
2650
  } else {
2387
2651
  const wantImpl = handleCancel(
2388
- await p3__namespace.confirm({
2652
+ await p15__namespace.confirm({
2389
2653
  message: "Does this class implement a domain interface or abstract class?",
2390
2654
  initialValue: false
2391
2655
  })
2392
2656
  );
2393
2657
  if (wantImpl) {
2394
2658
  implementsInterface = handleCancel(
2395
- await p3__namespace.text({
2659
+ await p15__namespace.text({
2396
2660
  message: "Enter interface or abstract class name:",
2397
2661
  placeholder: "e.g. IUserRepository, BaseStorageAdapter",
2398
2662
  validate: (v) => v && v.trim() ? void 0 : "Name cannot be empty"
@@ -2412,7 +2676,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2412
2676
  const allInterfaces = listDomainInterfaces(projectRoot);
2413
2677
  if (allInterfaces.length > 0) {
2414
2678
  const ifaceChoice = handleCancel(
2415
- await p3__namespace.select({
2679
+ await p15__namespace.select({
2416
2680
  message: "Does this class implement an interface or abstract class?",
2417
2681
  options: [
2418
2682
  ...allInterfaces.map((i) => ({ value: i.name, label: `${i.name} (${i.feature})` })),
@@ -2424,7 +2688,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2424
2688
  );
2425
2689
  if (ifaceChoice === "__custom__") {
2426
2690
  implementsInterface = handleCancel(
2427
- await p3__namespace.text({
2691
+ await p15__namespace.text({
2428
2692
  message: "Enter interface or abstract class name:",
2429
2693
  placeholder: "e.g. IAuthRepository",
2430
2694
  validate: (v) => v && v.trim() ? void 0 : "Name cannot be empty"
@@ -2437,7 +2701,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2437
2701
  }
2438
2702
  if (!flags.yes && !flags.kind) {
2439
2703
  kind = handleCancel(
2440
- await p3__namespace.select({
2704
+ await p15__namespace.select({
2441
2705
  message: "Select implementation kind:",
2442
2706
  options: [
2443
2707
  { value: "stub", label: "In-Memory Stub (with in-memory store and test helper methods)" },
@@ -2448,7 +2712,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2448
2712
  );
2449
2713
  if (scope === "feature" && feature && implementsInterface) {
2450
2714
  registerInProvider = handleCancel(
2451
- await p3__namespace.confirm({
2715
+ await p15__namespace.confirm({
2452
2716
  message: "Register this implementation in feature diProvider.ts?",
2453
2717
  initialValue: true
2454
2718
  })
@@ -2457,8 +2721,8 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2457
2721
  } else if (flags.yes && scope === "feature" && feature && implementsInterface) {
2458
2722
  registerInProvider = !flags.noProvider;
2459
2723
  }
2460
- const s = p3__namespace.spinner();
2461
- s.start(`Generating infrastructure class '${pc13__default.default.cyan(className)}'...`);
2724
+ const s = p15__namespace.spinner();
2725
+ s.start(`Generating infrastructure class '${pc15__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 = path6__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
+ p15__namespace.outro(pc15__default.default.green(`Infrastructure class '${className}' created!`));
2497
2761
  }
2498
2762
  async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2499
2763
  const projectRoot = findProjectRoot();
@@ -2504,14 +2768,14 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2504
2768
  const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
2505
2769
  featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
2506
2770
  const selected = handleCancel(
2507
- await p3__namespace.select({
2771
+ await p15__namespace.select({
2508
2772
  message: "Select target feature:",
2509
2773
  options: featureOptions
2510
2774
  })
2511
2775
  );
2512
2776
  if (selected === "__custom__") {
2513
2777
  feature = handleCancel(
2514
- await p3__namespace.text({
2778
+ await p15__namespace.text({
2515
2779
  message: "Enter feature name:",
2516
2780
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2517
2781
  })
@@ -2521,7 +2785,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2521
2785
  }
2522
2786
  } else {
2523
2787
  feature = handleCancel(
2524
- await p3__namespace.text({
2788
+ await p15__namespace.text({
2525
2789
  message: "Enter target feature name:",
2526
2790
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2527
2791
  })
@@ -2532,7 +2796,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2532
2796
  let name = interfaceNameArg?.trim();
2533
2797
  if (!name) {
2534
2798
  name = handleCancel(
2535
- await p3__namespace.text({
2799
+ await p15__namespace.text({
2536
2800
  message: "Domain interface name:",
2537
2801
  placeholder: "e.g. IUserRepository, IPaymentService",
2538
2802
  validate: (v) => v && v.trim() ? void 0 : "Interface name cannot be empty"
@@ -2550,7 +2814,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2550
2814
  if (!flags.yes) {
2551
2815
  if (existingEntities.length > 0) {
2552
2816
  const selectedEntity = handleCancel(
2553
- await p3__namespace.select({
2817
+ await p15__namespace.select({
2554
2818
  message: "Select domain entity associated with this interface:",
2555
2819
  options: [
2556
2820
  ...existingEntities.map((e) => ({ value: e.name, label: e.name })),
@@ -2561,7 +2825,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2561
2825
  );
2562
2826
  if (selectedEntity === "__custom__") {
2563
2827
  entityName = handleCancel(
2564
- await p3__namespace.text({
2828
+ await p15__namespace.text({
2565
2829
  message: "Enter entity name:",
2566
2830
  defaultValue: removeInterfacePrefix(ifaceName).replace(/Repository$|Service$/, "")
2567
2831
  })
@@ -2571,7 +2835,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2571
2835
  }
2572
2836
  }
2573
2837
  kind = handleCancel(
2574
- await p3__namespace.select({
2838
+ await p15__namespace.select({
2575
2839
  message: "Interface definition style:",
2576
2840
  options: [
2577
2841
  { value: "abstractClass", label: "Abstract Class (Recommended - acts as DI token & type)" },
@@ -2581,7 +2845,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2581
2845
  })
2582
2846
  );
2583
2847
  methodsPreset = handleCancel(
2584
- await p3__namespace.select({
2848
+ await p15__namespace.select({
2585
2849
  message: "Method signature preset:",
2586
2850
  options: [
2587
2851
  { value: "crud", label: "Standard CRUD (GetById, List, Create, Update, Delete)" },
@@ -2591,22 +2855,22 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2591
2855
  })
2592
2856
  );
2593
2857
  generateStub = handleCancel(
2594
- await p3__namespace.confirm({
2858
+ await p15__namespace.confirm({
2595
2859
  message: "Auto-generate Infrastructure Stub implementation?",
2596
2860
  initialValue: true
2597
2861
  })
2598
2862
  );
2599
2863
  if (generateStub) {
2600
2864
  registerInProvider = handleCancel(
2601
- await p3__namespace.confirm({
2865
+ await p15__namespace.confirm({
2602
2866
  message: "Auto-register interface & stub in feature diProvider.ts?",
2603
2867
  initialValue: true
2604
2868
  })
2605
2869
  );
2606
2870
  }
2607
2871
  }
2608
- const s = p3__namespace.spinner();
2609
- s.start(`Generating interface '${pc13__default.default.cyan(ifaceName)}'...`);
2872
+ const s = p15__namespace.spinner();
2873
+ s.start(`Generating interface '${pc15__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 = path6__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
+ p15__namespace.outro(pc15__default.default.green(`Interface '${ifaceName}' created in feature '${feature}'!`));
2659
2923
  }
2660
2924
  async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2661
2925
  const projectRoot = findProjectRoot();
@@ -2666,14 +2930,14 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2666
2930
  const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
2667
2931
  featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
2668
2932
  const selected = handleCancel(
2669
- await p3__namespace.select({
2933
+ await p15__namespace.select({
2670
2934
  message: "Select target feature:",
2671
2935
  options: featureOptions
2672
2936
  })
2673
2937
  );
2674
2938
  if (selected === "__custom__") {
2675
2939
  feature = handleCancel(
2676
- await p3__namespace.text({
2940
+ await p15__namespace.text({
2677
2941
  message: "Enter feature name:",
2678
2942
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2679
2943
  })
@@ -2683,7 +2947,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2683
2947
  }
2684
2948
  } else {
2685
2949
  feature = handleCancel(
2686
- await p3__namespace.text({
2950
+ await p15__namespace.text({
2687
2951
  message: "Enter target feature name:",
2688
2952
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2689
2953
  })
@@ -2694,7 +2958,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2694
2958
  let name = handlerNameArg?.trim();
2695
2959
  if (!name) {
2696
2960
  name = handleCancel(
2697
- await p3__namespace.text({
2961
+ await p15__namespace.text({
2698
2962
  message: "HTTP Route Handler class name:",
2699
2963
  placeholder: "e.g. CreateUserHttp, GetUserHttp, HealthHttp",
2700
2964
  validate: (v) => v && v.trim() ? void 0 : "Handler name cannot be empty"
@@ -2713,7 +2977,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2713
2977
  let validationFields = [];
2714
2978
  if (!flags.yes) {
2715
2979
  method = handleCancel(
2716
- await p3__namespace.select({
2980
+ await p15__namespace.select({
2717
2981
  message: "HTTP Method:",
2718
2982
  options: [
2719
2983
  { value: "get", label: "GET" },
@@ -2727,7 +2991,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2727
2991
  );
2728
2992
  const defaultPath = method === "get" ? "/" : "/";
2729
2993
  subPath = handleCancel(
2730
- await p3__namespace.text({
2994
+ await p15__namespace.text({
2731
2995
  message: "Route subpath (relative to feature prefix):",
2732
2996
  initialValue: defaultPath,
2733
2997
  defaultValue: defaultPath
@@ -2738,7 +3002,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2738
3002
  const ucOptions = useCases.map((u) => ({ value: u.name, label: u.name }));
2739
3003
  ucOptions.push({ value: "__none__", label: "None / Standalone Handler" });
2740
3004
  const chosenUc = handleCancel(
2741
- await p3__namespace.select({
3005
+ await p15__namespace.select({
2742
3006
  message: "Connect this handler to a domain Use Case?",
2743
3007
  options: ucOptions,
2744
3008
  initialValue: ucOptions[0]?.value
@@ -2749,7 +3013,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2749
3013
  }
2750
3014
  }
2751
3015
  includeValidation = handleCancel(
2752
- await p3__namespace.confirm({
3016
+ await p15__namespace.confirm({
2753
3017
  message: "Include Zod inbound request validation?",
2754
3018
  initialValue: method !== "delete"
2755
3019
  })
@@ -2757,7 +3021,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2757
3021
  if (includeValidation) {
2758
3022
  const defaultSource = method === "get" ? "query" : "body";
2759
3023
  validationSource = handleCancel(
2760
- await p3__namespace.select({
3024
+ await p15__namespace.select({
2761
3025
  message: "Validation data source:",
2762
3026
  options: [
2763
3027
  { value: "body", label: "req.body (for POST / PUT / PATCH payloads)" },
@@ -2771,8 +3035,8 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2771
3035
  });
2772
3036
  }
2773
3037
  }
2774
- const s = p3__namespace.spinner();
2775
- s.start(`Generating HTTP handler '${pc13__default.default.cyan(className)}'...`);
3038
+ const s = p15__namespace.spinner();
3039
+ s.start(`Generating HTTP handler '${pc15__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
+ p15__namespace.outro(pc15__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((p17) => `${p17.name}: ${p17.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 = path6__default.default.isAbsolute(options.customPath) ? options.customPath : path6__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 = path6__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 = path6__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 = path6__default.default.join(projectRoot, "modules", className);
3096
+ targetDir = path2__default.default.join(projectRoot, "modules", className);
2833
3097
  } else {
2834
- targetDir = options.feature ? path6__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "services") : path6__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 = path6__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 = path6__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, {
@@ -2870,7 +3134,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
2870
3134
  let name = classNameArg?.trim();
2871
3135
  if (!name) {
2872
3136
  name = handleCancel(
2873
- await p3__namespace.text({
3137
+ await p15__namespace.text({
2874
3138
  message: "Injectable class name:",
2875
3139
  placeholder: "e.g. PasswordHasher, TokenService, UserTransformer",
2876
3140
  validate: (v) => v && v.trim() ? void 0 : "Class name cannot be empty"
@@ -2883,7 +3147,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
2883
3147
  let customPath;
2884
3148
  if (!flags.yes) {
2885
3149
  location = handleCancel(
2886
- await p3__namespace.select({
3150
+ await p15__namespace.select({
2887
3151
  message: "Select target architectural location:",
2888
3152
  options: [
2889
3153
  { value: "feature-service", label: "Feature Service (features/<feature>/services/)" },
@@ -2901,14 +3165,14 @@ async function runInjectableCommand(classNameArg, flags = {}) {
2901
3165
  const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
2902
3166
  featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
2903
3167
  const selected = handleCancel(
2904
- await p3__namespace.select({
3168
+ await p15__namespace.select({
2905
3169
  message: "Select target feature:",
2906
3170
  options: featureOptions
2907
3171
  })
2908
3172
  );
2909
3173
  if (selected === "__custom__") {
2910
3174
  feature = handleCancel(
2911
- await p3__namespace.text({
3175
+ await p15__namespace.text({
2912
3176
  message: "Enter feature name:",
2913
3177
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2914
3178
  })
@@ -2918,7 +3182,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
2918
3182
  }
2919
3183
  } else {
2920
3184
  feature = handleCancel(
2921
- await p3__namespace.text({
3185
+ await p15__namespace.text({
2922
3186
  message: "Enter target feature name:",
2923
3187
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2924
3188
  })
@@ -2928,7 +3192,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
2928
3192
  feature = toCamelCase(feature);
2929
3193
  } else if (location === "custom") {
2930
3194
  customPath = handleCancel(
2931
- await p3__namespace.text({
3195
+ await p15__namespace.text({
2932
3196
  message: "Enter destination directory path:",
2933
3197
  placeholder: "e.g. src/services or lib/utils",
2934
3198
  validate: (v) => v && v.trim() ? void 0 : "Path cannot be empty"
@@ -2939,14 +3203,14 @@ async function runInjectableCommand(classNameArg, flags = {}) {
2939
3203
  let generateInterface2 = false;
2940
3204
  if (!flags.yes) {
2941
3205
  generateInterface2 = handleCancel(
2942
- await p3__namespace.confirm({
3206
+ await p15__namespace.confirm({
2943
3207
  message: `Generate abstract class interface (I${className}) as runtime DI token?`,
2944
3208
  initialValue: false
2945
3209
  })
2946
3210
  );
2947
3211
  }
2948
- const s = p3__namespace.spinner();
2949
- s.start(`Generating injectable '${pc13__default.default.cyan(className)}'...`);
3212
+ const s = p15__namespace.spinner();
3213
+ s.start(`Generating injectable '${pc15__default.default.cyan(className)}'...`);
2950
3214
  const options = {
2951
3215
  name: className,
2952
3216
  feature,
@@ -2960,12 +3224,12 @@ 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
+ p15__namespace.outro(pc15__default.default.green(`Injectable '${className}' created successfully!`));
2964
3228
  }
2965
3229
  async function runInteractiveMenu(flags = {}) {
2966
3230
  printBanner();
2967
3231
  const choice = handleCancel(
2968
- await p3__namespace.select({
3232
+ await p15__namespace.select({
2969
3233
  message: "What would you like to create in your architecture?",
2970
3234
  options: [
2971
3235
  {
@@ -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;
@@ -3062,49 +3337,114 @@ async function handleCreate(type, name, flags = {}) {
3062
3337
  await runInjectableCommand(name, flags);
3063
3338
  break;
3064
3339
  default:
3065
- p3__namespace.log.error(
3066
- pc13__default.default.red(
3067
- `Unknown component type: "${type}". Allowed types: feature, usecase, domain, infrastructure, interface, httpHandler, injectable.`
3340
+ p15__namespace.log.error(
3341
+ pc15__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);
3071
3346
  }
3072
3347
  }
3073
3348
  var DEFAULT_REGISTRY_BASE = "https://raw.githubusercontent.com/solid-stack-digital/mason-registry/main";
3349
+ function normalizeRegistryUrl(urlOrPath, baseDir) {
3350
+ let trimmed = urlOrPath.trim();
3351
+ const gitSshRegex = /^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/;
3352
+ const sshMatch = trimmed.match(gitSshRegex);
3353
+ if (sshMatch && sshMatch[1] && sshMatch[2]) {
3354
+ return `https://raw.githubusercontent.com/${sshMatch[1]}/${sshMatch[2]}/main`;
3355
+ }
3356
+ const githubWebRegex = /^https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?(?:\/tree\/([^/]+))?\/?$/;
3357
+ const webMatch = trimmed.match(githubWebRegex);
3358
+ if (webMatch && webMatch[1] && webMatch[2]) {
3359
+ const owner = webMatch[1];
3360
+ const repo = webMatch[2];
3361
+ const branch = webMatch[3] || "main";
3362
+ return `https://raw.githubusercontent.com/${owner}/${repo}/${branch}`;
3363
+ }
3364
+ if (baseDir && (trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed === "." || trimmed === "..")) {
3365
+ return path2__default.default.resolve(baseDir, trimmed);
3366
+ }
3367
+ return trimmed;
3368
+ }
3369
+ function findLocalRegistry(startDir = process.cwd()) {
3370
+ let current = path2__default.default.resolve(startDir);
3371
+ while (true) {
3372
+ const candidate = path2__default.default.join(current, "mason-registry");
3373
+ if (fs12__default.default.existsSync(candidate) && fs12__default.default.existsSync(path2__default.default.join(candidate, "index.json"))) {
3374
+ return candidate;
3375
+ }
3376
+ const parent = path2__default.default.dirname(current);
3377
+ if (parent === current) break;
3378
+ current = parent;
3379
+ }
3380
+ return null;
3381
+ }
3074
3382
  function getRegistryBase(projectRoot, override) {
3383
+ const rootDir = projectRoot ? path2__default.default.resolve(projectRoot) : process.cwd();
3075
3384
  if (override && override.trim()) {
3076
- return override.trim();
3385
+ return normalizeRegistryUrl(override.trim(), rootDir);
3077
3386
  }
3078
3387
  if (process.env.MASON_REGISTRY_URL && process.env.MASON_REGISTRY_URL.trim()) {
3079
- return process.env.MASON_REGISTRY_URL.trim();
3388
+ return normalizeRegistryUrl(process.env.MASON_REGISTRY_URL.trim(), rootDir);
3080
3389
  }
3081
- if (projectRoot) {
3082
- const configPath = path6__default.default.join(projectRoot, "mason.config.json");
3083
- if (fs7__default.default.existsSync(configPath)) {
3390
+ const configLocations = [
3391
+ path2__default.default.join(rootDir, "mason.config.json"),
3392
+ path2__default.default.join(process.cwd(), "mason.config.json")
3393
+ ];
3394
+ for (const configPath of configLocations) {
3395
+ if (fs12__default.default.existsSync(configPath)) {
3084
3396
  try {
3085
- const config = JSON.parse(fs7__default.default.readFileSync(configPath, "utf-8"));
3397
+ const config = JSON.parse(fs12__default.default.readFileSync(configPath, "utf-8"));
3086
3398
  if (config.registry && typeof config.registry === "string") {
3087
- return config.registry.trim();
3399
+ return normalizeRegistryUrl(config.registry.trim(), path2__default.default.dirname(configPath));
3088
3400
  }
3089
3401
  } catch {
3090
3402
  }
3091
3403
  }
3092
3404
  }
3093
- if (projectRoot) {
3094
- const siblingRegistry = path6__default.default.resolve(projectRoot, "../mason-registry");
3095
- if (fs7__default.default.existsSync(siblingRegistry) && fs7__default.default.existsSync(path6__default.default.join(siblingRegistry, "index.json"))) {
3096
- return siblingRegistry;
3405
+ const pkgLocations = [
3406
+ path2__default.default.join(rootDir, "package.json"),
3407
+ path2__default.default.join(process.cwd(), "package.json")
3408
+ ];
3409
+ for (const pkgPath of pkgLocations) {
3410
+ if (fs12__default.default.existsSync(pkgPath)) {
3411
+ try {
3412
+ const pkg = JSON.parse(fs12__default.default.readFileSync(pkgPath, "utf-8"));
3413
+ if (pkg.mason?.registry && typeof pkg.mason.registry === "string") {
3414
+ return normalizeRegistryUrl(pkg.mason.registry.trim(), path2__default.default.dirname(pkgPath));
3415
+ }
3416
+ } catch {
3417
+ }
3097
3418
  }
3098
3419
  }
3420
+ const localRegistry = findLocalRegistry(rootDir);
3421
+ if (localRegistry) {
3422
+ return localRegistry;
3423
+ }
3099
3424
  return DEFAULT_REGISTRY_BASE;
3100
3425
  }
3101
3426
  async function fetchRegistryFile(registryBase, relPath) {
3102
3427
  const cleanRelPath = relPath.replace(/^\/+/, "");
3103
3428
  if (registryBase.startsWith("http://") || registryBase.startsWith("https://")) {
3104
3429
  const url = `${registryBase.replace(/\/+$/, "")}/${cleanRelPath}`;
3105
- const res = await fetch(url);
3430
+ const headers = {};
3431
+ const token = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
3432
+ if (token) {
3433
+ headers["Authorization"] = `Bearer ${token}`;
3434
+ }
3435
+ const res = await fetch(url, { headers });
3106
3436
  if (!res.ok) {
3107
- throw new Error(`Failed to fetch ${cleanRelPath} from ${url} (HTTP ${res.status}: ${res.statusText})`);
3437
+ let extraHint = "";
3438
+ if (res.status === 404) {
3439
+ extraHint = `
3440
+ Possible reasons:
3441
+ \u2022 If using a private GitHub repository, ensure GITHUB_TOKEN is set in your environment.
3442
+ \u2022 If your registry has not yet been pushed to GitHub, point to your local directory:
3443
+ - CLI flag: mason import ... --registry /path/to/mason-registry
3444
+ - Environment: export MASON_REGISTRY_URL=/path/to/mason-registry
3445
+ - Config file: Create mason.config.json with { "registry": "/path/to/mason-registry" }`;
3446
+ }
3447
+ throw new Error(`Failed to fetch ${cleanRelPath} from ${url} (HTTP ${res.status}: ${res.statusText})${extraHint}`);
3108
3448
  }
3109
3449
  return await res.text();
3110
3450
  }
@@ -3112,11 +3452,11 @@ async function fetchRegistryFile(registryBase, relPath) {
3112
3452
  if (baseDir.startsWith("file://")) {
3113
3453
  baseDir = url.fileURLToPath(baseDir);
3114
3454
  }
3115
- const filePath = path6__default.default.resolve(baseDir, cleanRelPath);
3116
- if (!fs7__default.default.existsSync(filePath)) {
3455
+ const filePath = path2__default.default.resolve(baseDir, cleanRelPath);
3456
+ if (!fs12__default.default.existsSync(filePath)) {
3117
3457
  throw new Error(`Registry file not found at ${filePath}`);
3118
3458
  }
3119
- return fs7__default.default.promises.readFile(filePath, "utf-8");
3459
+ return fs12__default.default.promises.readFile(filePath, "utf-8");
3120
3460
  }
3121
3461
  async function fetchRegistryIndex(registryBase) {
3122
3462
  const content = await fetchRegistryFile(registryBase, "index.json");
@@ -3178,26 +3518,26 @@ function findClosestMatch(target, candidates) {
3178
3518
  return null;
3179
3519
  }
3180
3520
  function detectPackageManager(projectRoot) {
3181
- if (fs7__default.default.existsSync(path6__default.default.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
3182
- if (fs7__default.default.existsSync(path6__default.default.join(projectRoot, "yarn.lock"))) return "yarn";
3183
- if (fs7__default.default.existsSync(path6__default.default.join(projectRoot, "bun.lockb")) || fs7__default.default.existsSync(path6__default.default.join(projectRoot, "bun.lock"))) return "bun";
3521
+ if (fs12__default.default.existsSync(path2__default.default.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
3522
+ if (fs12__default.default.existsSync(path2__default.default.join(projectRoot, "yarn.lock"))) return "yarn";
3523
+ if (fs12__default.default.existsSync(path2__default.default.join(projectRoot, "bun.lockb")) || fs12__default.default.existsSync(path2__default.default.join(projectRoot, "bun.lock"))) return "bun";
3184
3524
  return "npm";
3185
3525
  }
3186
3526
  function getMissingNpmPackages(projectRoot, requiredPackages) {
3187
3527
  if (!requiredPackages || requiredPackages.length === 0) return [];
3188
- const pkgJsonPath = path6__default.default.join(projectRoot, "package.json");
3189
- if (!fs7__default.default.existsSync(pkgJsonPath)) {
3528
+ const pkgJsonPath = path2__default.default.join(projectRoot, "package.json");
3529
+ if (!fs12__default.default.existsSync(pkgJsonPath)) {
3190
3530
  return requiredPackages;
3191
3531
  }
3192
3532
  try {
3193
- const pkg = JSON.parse(fs7__default.default.readFileSync(pkgJsonPath, "utf-8"));
3533
+ const pkg = JSON.parse(fs12__default.default.readFileSync(pkgJsonPath, "utf-8"));
3194
3534
  const installed = /* @__PURE__ */ new Set([
3195
3535
  ...Object.keys(pkg.dependencies || {}),
3196
3536
  ...Object.keys(pkg.devDependencies || {}),
3197
3537
  ...Object.keys(pkg.peerDependencies || {})
3198
3538
  ]);
3199
- return requiredPackages.filter((p15) => {
3200
- const nameOnly = p15.startsWith("@") ? `@${p15.slice(1).split("@")[0] || ""}` : p15.split("@")[0] || p15;
3539
+ return requiredPackages.filter((p17) => {
3540
+ const nameOnly = p17.startsWith("@") ? `@${p17.slice(1).split("@")[0] || ""}` : p17.split("@")[0] || p17;
3201
3541
  return !installed.has(nameOnly);
3202
3542
  });
3203
3543
  } catch {
@@ -3222,21 +3562,207 @@ function installNpmPackages(projectRoot, packages, pm) {
3222
3562
  installCmd = `npm install ${packages.join(" ")}`;
3223
3563
  break;
3224
3564
  }
3225
- p3__namespace.log.info(`Installing missing dependencies with ${manager}: ${pc13__default.default.cyan(packages.join(", "))}`);
3565
+ p15__namespace.log.info(`Installing missing dependencies with ${manager}: ${pc15__default.default.cyan(packages.join(", "))}`);
3226
3566
  child_process.execSync(installCmd, {
3227
3567
  cwd: projectRoot,
3228
3568
  stdio: "inherit"
3229
3569
  });
3230
3570
  }
3571
+ function getAllFiles(dir, baseDir = dir) {
3572
+ if (!fs12__default.default.existsSync(dir)) return [];
3573
+ const entries = fs12__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 (!fs12__default.default.existsSync(dirPath)) return "";
3594
+ const hash = crypto.createHash("sha256");
3595
+ const fileList = (files ?? getAllFiles(dirPath)).sort();
3596
+ for (const relativePath of fileList) {
3597
+ const absolutePath = path2__default.default.join(dirPath, relativePath);
3598
+ if (!fs12__default.default.existsSync(absolutePath)) continue;
3599
+ const content = fs12__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
+ }
3231
3606
 
3232
3607
  // src/commands/import.ts
3608
+ var importPrompts = {
3609
+ select: p15__namespace.select,
3610
+ text: p15__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 (fs12__default.default.existsSync(inSrc)) return inSrc;
3632
+ const inRoot = path2__default.default.join(projectRoot, folder, modName);
3633
+ if (fs12__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
+ p15__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
+ p15__namespace.log.info(` \u21B3 Resolving required ${depType} dependency: ${pc15__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
+ p15__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
+ p15__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
+ fs12__default.default.rmSync(existingDir, { recursive: true, force: true });
3685
+ }
3686
+ fs12__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
+ p15__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
+ p15__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
+ p15__namespace.log.step(`Importing '${depName}' into '${displayDest}'...`);
3742
+ await importModuleRecursive(depType, depName, newName);
3743
+ return;
3744
+ }
3745
+ if (choice === "overwrite") {
3746
+ p15__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
+ fs12__default.default.rmSync(existingDir, { recursive: true, force: true });
3752
+ }
3753
+ fs12__default.default.rmSync(targetDir, { recursive: true, force: true });
3754
+ }
3755
+ await importModuleRecursive(depType, depName, depName, { overwrite: true });
3756
+ return;
3757
+ }
3758
+ }
3233
3759
  async function handleImport(typeArg, nameArg, flags = {}) {
3234
3760
  const projectRoot = findProjectRoot();
3235
3761
  let type = typeArg?.trim().toLowerCase();
3236
3762
  if (type === "features") type = "feature";
3237
3763
  if (!type) {
3238
3764
  type = handleCancel(
3239
- await p3__namespace.select({
3765
+ await importPrompts.select({
3240
3766
  message: "What kind of module would you like to import?",
3241
3767
  options: [
3242
3768
  {
@@ -3254,19 +3780,19 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3254
3780
  );
3255
3781
  }
3256
3782
  if (type !== "shared" && type !== "feature") {
3257
- p3__namespace.log.error(pc13__default.default.red(`Invalid module type: "${typeArg}". Must be "shared" or "feature".`));
3783
+ p15__namespace.log.error(pc15__default.default.red(`Invalid module type: "${typeArg}". Must be "shared" or "feature".`));
3258
3784
  process.exit(1);
3259
3785
  }
3260
3786
  const registryBase = getRegistryBase(projectRoot, flags.registry);
3261
- const s = p3__namespace.spinner();
3262
- s.start(`Connecting to registry at ${pc13__default.default.dim(registryBase)}...`);
3787
+ const s = p15__namespace.spinner();
3788
+ s.start(`Connecting to registry at ${pc15__default.default.dim(registryBase)}...`);
3263
3789
  let index;
3264
3790
  try {
3265
3791
  index = await fetchRegistryIndex(registryBase);
3266
3792
  s.stop("Registry index loaded.");
3267
3793
  } catch (err) {
3268
- s.stop(pc13__default.default.red("Failed to load registry index."));
3269
- p3__namespace.log.error(pc13__default.default.red(err.message || String(err)));
3794
+ s.stop(pc15__default.default.red("Failed to load registry index."));
3795
+ p15__namespace.log.error(pc15__default.default.red(err.message || String(err)));
3270
3796
  process.exit(1);
3271
3797
  }
3272
3798
  const availableList = type === "shared" ? index.shared : index.features;
@@ -3274,11 +3800,11 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3274
3800
  let name = nameArg?.trim();
3275
3801
  if (!name) {
3276
3802
  if (availableList.length === 0) {
3277
- p3__namespace.log.warn(`No ${type} modules available in the registry.`);
3803
+ p15__namespace.log.warn(`No ${type} modules available in the registry.`);
3278
3804
  return;
3279
3805
  }
3280
3806
  name = handleCancel(
3281
- await p3__namespace.select({
3807
+ await importPrompts.select({
3282
3808
  message: `Select a ${type} to import:`,
3283
3809
  options: availableList.map((item) => ({
3284
3810
  value: item.name,
@@ -3290,122 +3816,769 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3290
3816
  }
3291
3817
  if (!candidateNames.includes(name)) {
3292
3818
  const closest = findClosestMatch(name, candidateNames);
3293
- p3__namespace.log.error(pc13__default.default.red(`\u274C Module '${name}' not found in the ${type} registry.`));
3819
+ p15__namespace.log.error(pc15__default.default.red(`\u274C Module '${name}' not found in the ${type} registry.`));
3294
3820
  if (closest) {
3295
- p3__namespace.log.info(pc13__default.default.yellow(`Did you mean '${pc13__default.default.cyan(closest)}'?`));
3821
+ p15__namespace.log.info(pc15__default.default.yellow(`Did you mean '${pc15__default.default.cyan(closest)}'?`));
3296
3822
  }
3297
3823
  process.exit(1);
3298
3824
  }
3299
3825
  const importedModules = /* @__PURE__ */ new Set();
3300
3826
  const totalWrittenFiles = [];
3301
3827
  const requiredNpmPackages = /* @__PURE__ */ new Set();
3302
- async function importModuleRecursive(modType, modName) {
3303
- const modKey = `${modType}:${modName}`;
3828
+ async function importModuleRecursive(modType, registryName, targetName = registryName, options) {
3829
+ const modKey = `${modType}:${targetName}`;
3304
3830
  if (importedModules.has(modKey)) return;
3305
- p3__namespace.log.step(`Importing ${modType}: ${pc13__default.default.cyan(modName)}...`);
3831
+ p15__namespace.log.step(
3832
+ `Importing ${modType}: ${pc15__default.default.cyan(targetName)}${targetName !== registryName ? pc15__default.default.dim(` (from ${registryName})`) : ""}...`
3833
+ );
3306
3834
  let manifest;
3307
3835
  try {
3308
- manifest = await fetchModuleManifest(registryBase, modType, modName);
3836
+ manifest = await fetchModuleManifest(registryBase, modType, registryName);
3309
3837
  } catch (err) {
3310
- p3__namespace.log.error(pc13__default.default.red(`Failed to fetch manifest for ${modType} '${modName}': ${err.message}`));
3838
+ p15__namespace.log.error(pc15__default.default.red(`Failed to fetch manifest for ${modType} '${registryName}': ${err.message}`));
3311
3839
  process.exit(1);
3312
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
+ }
3313
3846
  const sharedDeps = manifest.dependencies?.shared || [];
3314
3847
  for (const dep of sharedDeps) {
3315
- const depPath = path6__default.default.join(projectRoot, "src", "shared", dep);
3316
- if (!fs7__default.default.existsSync(depPath) && !importedModules.has(`shared:${dep}`)) {
3317
- p3__namespace.log.info(` \u21B3 Resolving required shared dependency: ${pc13__default.default.cyan(dep)}`);
3318
- await importModuleRecursive("shared", dep);
3319
- }
3848
+ await resolveDependency(
3849
+ "shared",
3850
+ dep,
3851
+ aliasMap,
3852
+ projectRoot,
3853
+ flags,
3854
+ importModuleRecursive,
3855
+ importedModules,
3856
+ registryBase,
3857
+ index
3858
+ );
3320
3859
  }
3321
3860
  const featureDeps = manifest.dependencies?.features || [];
3322
3861
  for (const dep of featureDeps) {
3323
- const depPath = path6__default.default.join(projectRoot, "src", "features", dep);
3324
- if (!fs7__default.default.existsSync(depPath) && !importedModules.has(`feature:${dep}`)) {
3325
- p3__namespace.log.info(` \u21B3 Resolving required feature dependency: ${pc13__default.default.cyan(dep)}`);
3326
- await importModuleRecursive("feature", dep);
3327
- }
3862
+ await resolveDependency(
3863
+ "feature",
3864
+ dep,
3865
+ aliasMap,
3866
+ projectRoot,
3867
+ flags,
3868
+ importModuleRecursive,
3869
+ importedModules,
3870
+ registryBase,
3871
+ index
3872
+ );
3328
3873
  }
3329
3874
  for (const pkg of manifest.dependencies?.npm || []) {
3330
3875
  requiredNpmPackages.add(pkg);
3331
3876
  }
3332
3877
  const folder = modType === "shared" ? "shared" : "features";
3333
- const targetDir = path6__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);
3334
3880
  for (const file of manifest.files || []) {
3335
3881
  if (file === "registry.json") continue;
3336
- const remoteFilePath = `${folder}/${modName}/${file}`;
3337
- const localFilePath = path6__default.default.join(targetDir, file);
3338
- if (fs7__default.default.existsSync(localFilePath) && !flags.overwrite) {
3339
- p3__namespace.log.warn(` \u21B3 Skipping existing file: ${pc13__default.default.dim(path6__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 (fs12__default.default.existsSync(localFilePath) && !isOverwrite) {
3885
+ p15__namespace.log.warn(
3886
+ ` \u21B3 Skipping existing file: ${pc15__default.default.dim(path2__default.default.relative(projectRoot, localFilePath))} (use --overwrite to replace)`
3887
+ );
3340
3888
  continue;
3341
3889
  }
3342
- const content = await fetchRegistryFile(registryBase, remoteFilePath);
3890
+ let content = await fetchRegistryFile(registryBase, remoteFilePath);
3891
+ content = rewriteImports(content, aliasMap);
3343
3892
  if (!flags.dryRun) {
3344
- fs7__default.default.mkdirSync(path6__default.default.dirname(localFilePath), { recursive: true });
3345
- fs7__default.default.writeFileSync(localFilePath, content, "utf-8");
3893
+ fs12__default.default.mkdirSync(path2__default.default.dirname(localFilePath), { recursive: true });
3894
+ fs12__default.default.writeFileSync(localFilePath, content, "utf-8");
3346
3895
  }
3347
- const displayPath = path6__default.default.relative(projectRoot, localFilePath);
3896
+ const displayPath = path2__default.default.relative(projectRoot, localFilePath);
3348
3897
  totalWrittenFiles.push(displayPath);
3349
- p3__namespace.log.message(` ${pc13__default.default.green("+")} ${displayPath}`);
3898
+ p15__namespace.log.message(` ${pc15__default.default.green("+")} ${displayPath}`);
3350
3899
  }
3351
3900
  importedModules.add(modKey);
3352
3901
  }
3353
- await importModuleRecursive(type, name);
3902
+ await importModuleRecursive(type, name, name);
3354
3903
  const missingNpm = getMissingNpmPackages(projectRoot, Array.from(requiredNpmPackages));
3355
3904
  if (missingNpm.length > 0) {
3356
3905
  if (flags.skipInstall || flags.dryRun) {
3357
- p3__namespace.log.info(`Required npm dependencies to install: ${pc13__default.default.cyan(missingNpm.join(", "))}`);
3906
+ p15__namespace.log.info(`Required npm dependencies to install: ${pc15__default.default.cyan(missingNpm.join(", "))}`);
3358
3907
  } else {
3359
3908
  try {
3360
3909
  installNpmPackages(projectRoot, missingNpm);
3361
3910
  } catch (err) {
3362
- p3__namespace.log.warn(`Could not automatically install packages (${err.message}). Please run: npm install ${missingNpm.join(" ")}`);
3911
+ p15__namespace.log.warn(`Could not automatically install packages (${err.message}). Please run: npm install ${missingNpm.join(" ")}`);
3363
3912
  }
3364
3913
  }
3365
3914
  }
3366
3915
  if (flags.dryRun) {
3367
- p3__namespace.log.info(pc13__default.default.yellow("Dry run complete: No files were written to disk."));
3916
+ p15__namespace.log.info(pc15__default.default.yellow("Dry run complete: No files were written to disk."));
3368
3917
  } else {
3369
- p3__namespace.log.success(
3370
- pc13__default.default.green(
3371
- `Successfully imported ${type} '${name}' (${totalWrittenFiles.length} file${totalWrittenFiles.length === 1 ? "" : "s"} placed in src/${type === "shared" ? "shared" : "features"}/${name})`
3918
+ const baseDir = getModuleBaseDir(projectRoot, type);
3919
+ const displayTarget = path2__default.default.relative(projectRoot, path2__default.default.join(baseDir, name));
3920
+ p15__namespace.log.success(
3921
+ pc15__default.default.green(
3922
+ `Successfully imported ${type} '${name}' (${totalWrittenFiles.length} file${totalWrittenFiles.length === 1 ? "" : "s"} placed in ${displayTarget})`
3372
3923
  )
3373
3924
  );
3374
3925
  }
3375
3926
  }
3927
+ function loadMasterIndex(projectRoot) {
3928
+ const indexPath = path2__default.default.join(projectRoot, "index.json");
3929
+ if (!fs12__default.default.existsSync(indexPath)) {
3930
+ const initialIndex = { shared: [], features: [] };
3931
+ fs12__default.default.writeFileSync(indexPath, JSON.stringify(initialIndex, null, 2) + "\n", "utf-8");
3932
+ return initialIndex;
3933
+ }
3934
+ return JSON.parse(fs12__default.default.readFileSync(indexPath, "utf-8"));
3935
+ }
3936
+ function saveMasterIndex(projectRoot, index) {
3937
+ const indexPath = path2__default.default.join(projectRoot, "index.json");
3938
+ index.shared.sort((a, b) => a.name.localeCompare(b.name));
3939
+ index.features.sort((a, b) => a.name.localeCompare(b.name));
3940
+ fs12__default.default.writeFileSync(indexPath, JSON.stringify(index, null, 2) + "\n", "utf-8");
3941
+ }
3942
+ function discoverDiskModules(projectRoot) {
3943
+ const results = [];
3944
+ const sharedDir = resolveSharedDir(projectRoot);
3945
+ if (fs12__default.default.existsSync(sharedDir)) {
3946
+ const entries = fs12__default.default.readdirSync(sharedDir, { withFileTypes: true });
3947
+ for (const entry of entries) {
3948
+ if (entry.isDirectory() && !entry.name.startsWith(".")) {
3949
+ results.push({ name: entry.name, type: "shared", dir: path2__default.default.join(sharedDir, entry.name) });
3950
+ }
3951
+ }
3952
+ }
3953
+ const featuresDir = resolveFeaturesDir(projectRoot);
3954
+ if (fs12__default.default.existsSync(featuresDir)) {
3955
+ const entries = fs12__default.default.readdirSync(featuresDir, { withFileTypes: true });
3956
+ for (const entry of entries) {
3957
+ if (entry.isDirectory() && !entry.name.startsWith(".")) {
3958
+ results.push({ name: entry.name, type: "feature", dir: path2__default.default.join(featuresDir, entry.name) });
3959
+ }
3960
+ }
3961
+ }
3962
+ return results;
3963
+ }
3964
+ function checkGitStatusForDir(projectRoot, dirPath) {
3965
+ try {
3966
+ const relPath = path2__default.default.relative(projectRoot, dirPath);
3967
+ const output = child_process.execSync(`git status --porcelain -- "${relPath}"`, {
3968
+ cwd: projectRoot,
3969
+ stdio: ["ignore", "pipe", "ignore"],
3970
+ encoding: "utf-8"
3971
+ }).trim();
3972
+ if (!output) {
3973
+ return { hasChanges: false, filesCount: 0 };
3974
+ }
3975
+ const lines = output.split("\n").filter((l) => l.trim().length > 0);
3976
+ return { hasChanges: true, filesCount: lines.length };
3977
+ } catch {
3978
+ return { hasChanges: false, filesCount: 0 };
3979
+ }
3980
+ }
3981
+ function getRegistryDiff(projectRoot) {
3982
+ const index = loadMasterIndex(projectRoot);
3983
+ const diskModules = discoverDiskModules(projectRoot);
3984
+ const diskMap = /* @__PURE__ */ new Map();
3985
+ for (const mod of diskModules) {
3986
+ diskMap.set(`${mod.type}:${mod.name}`, mod);
3987
+ }
3988
+ const allKeys = /* @__PURE__ */ new Set();
3989
+ for (const item of index.shared) {
3990
+ allKeys.add(`shared:${item.name}`);
3991
+ }
3992
+ for (const item of index.features) {
3993
+ allKeys.add(`feature:${item.name}`);
3994
+ }
3995
+ for (const mod of diskModules) {
3996
+ allKeys.add(`${mod.type}:${mod.name}`);
3997
+ }
3998
+ const statuses = [];
3999
+ for (const key of allKeys) {
4000
+ const [typeStr, name] = key.split(":");
4001
+ const type = typeStr;
4002
+ const registeredEntry = (type === "shared" ? index.shared : index.features).find(
4003
+ (e) => e.name === name
4004
+ );
4005
+ const isRegistered = Boolean(registeredEntry);
4006
+ const diskInfo = diskMap.get(key);
4007
+ const existsOnDisk = Boolean(diskInfo && fs12__default.default.existsSync(diskInfo.dir));
4008
+ let hasUncommittedGitChanges = false;
4009
+ let uncommittedFilesCount = 0;
4010
+ let hasIntegrityDrift = false;
4011
+ let localIntegrity;
4012
+ if (existsOnDisk && diskInfo) {
4013
+ const git = checkGitStatusForDir(projectRoot, diskInfo.dir);
4014
+ hasUncommittedGitChanges = git.hasChanges;
4015
+ uncommittedFilesCount = git.filesCount;
4016
+ localIntegrity = computeDirectoryHash(diskInfo.dir);
4017
+ if (isRegistered && registeredEntry?.integrity) {
4018
+ hasIntegrityDrift = localIntegrity !== registeredEntry.integrity;
4019
+ }
4020
+ }
4021
+ statuses.push({
4022
+ name,
4023
+ type,
4024
+ isRegistered,
4025
+ existsOnDisk,
4026
+ hasUncommittedGitChanges,
4027
+ uncommittedFilesCount,
4028
+ hasIntegrityDrift,
4029
+ localIntegrity,
4030
+ registeredIntegrity: registeredEntry?.integrity
4031
+ });
4032
+ }
4033
+ return statuses.sort((a, b) => a.type.localeCompare(b.type) || a.name.localeCompare(b.name));
4034
+ }
4035
+ async function registerModule(typeArg, nameArg, flags = {}) {
4036
+ const projectRoot = findProjectRoot();
4037
+ const index = loadMasterIndex(projectRoot);
4038
+ let type = typeArg?.trim();
4039
+ if (!type) {
4040
+ type = handleCancel(
4041
+ await p15__namespace.select({
4042
+ message: "Module type to register:",
4043
+ options: [
4044
+ { value: "feature", label: "Feature" },
4045
+ { value: "shared", label: "Shared Module" }
4046
+ ]
4047
+ })
4048
+ );
4049
+ }
4050
+ const diskModules = discoverDiskModules(projectRoot).filter((m) => m.type === type);
4051
+ const registeredNames = new Set((type === "shared" ? index.shared : index.features).map((m) => m.name));
4052
+ const unregistered = diskModules.filter((m) => !registeredNames.has(m.name));
4053
+ let name = nameArg?.trim();
4054
+ if (!name) {
4055
+ if (unregistered.length > 0) {
4056
+ const opts = unregistered.map((m) => ({ value: m.name, label: m.name, hint: "unregistered on disk" }));
4057
+ opts.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom name...", hint: "" });
4058
+ const choice = handleCancel(
4059
+ await p15__namespace.select({
4060
+ message: `Select a ${type} to register:`,
4061
+ options: opts
4062
+ })
4063
+ );
4064
+ if (choice === "__custom__") {
4065
+ name = handleCancel(
4066
+ await p15__namespace.text({
4067
+ message: `Enter ${type} name:`,
4068
+ validate: (v) => v && v.trim() ? void 0 : "Name cannot be empty"
4069
+ })
4070
+ ).trim();
4071
+ } else {
4072
+ name = choice;
4073
+ }
4074
+ } else {
4075
+ name = handleCancel(
4076
+ await p15__namespace.text({
4077
+ message: `Enter ${type} name to register:`,
4078
+ validate: (v) => v && v.trim() ? void 0 : "Name cannot be empty"
4079
+ })
4080
+ ).trim();
4081
+ }
4082
+ }
4083
+ const folder = type === "shared" ? resolveSharedDir(projectRoot) : resolveFeaturesDir(projectRoot);
4084
+ const modDir = path2__default.default.join(folder, name);
4085
+ if (!fs12__default.default.existsSync(modDir)) {
4086
+ p15__namespace.log.error(pc15__default.default.red(`Module directory does not exist: ${path2__default.default.relative(projectRoot, modDir)}`));
4087
+ process.exit(1);
4088
+ }
4089
+ const files = getAllFiles(modDir);
4090
+ const integrity = computeDirectoryHash(modDir, files);
4091
+ const manifestPath = path2__default.default.join(modDir, "registry.json");
4092
+ let manifest;
4093
+ if (fs12__default.default.existsSync(manifestPath)) {
4094
+ manifest = JSON.parse(fs12__default.default.readFileSync(manifestPath, "utf-8"));
4095
+ } else {
4096
+ manifest = {
4097
+ name,
4098
+ type,
4099
+ description: `${name} ${type} module`,
4100
+ version: "1.0.0",
4101
+ dependencies: {
4102
+ npm: [],
4103
+ shared: [],
4104
+ features: []
4105
+ },
4106
+ files: []
4107
+ };
4108
+ }
4109
+ manifest.name = name;
4110
+ manifest.type = type;
4111
+ manifest.files = files;
4112
+ manifest.integrity = integrity;
4113
+ fs12__default.default.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n", "utf-8");
4114
+ const targetList = type === "shared" ? index.shared : index.features;
4115
+ const existingIdx = targetList.findIndex((item) => item.name === name);
4116
+ if (existingIdx >= 0) {
4117
+ targetList[existingIdx] = {
4118
+ name,
4119
+ description: manifest.description,
4120
+ version: manifest.version,
4121
+ integrity
4122
+ };
4123
+ } else {
4124
+ targetList.push({
4125
+ name,
4126
+ description: manifest.description,
4127
+ version: manifest.version,
4128
+ integrity
4129
+ });
4130
+ }
4131
+ saveMasterIndex(projectRoot, index);
4132
+ p15__namespace.log.success(
4133
+ pc15__default.default.green(
4134
+ `\u2713 Module '${type}/${name}' registered in index.json (${files.length} files, ${integrity.slice(0, 18)}...)`
4135
+ )
4136
+ );
4137
+ }
4138
+ async function commitModule(typeArg, nameArg, flags = {}) {
4139
+ const projectRoot = findProjectRoot();
4140
+ let type = typeArg?.trim();
4141
+ if (!type) {
4142
+ type = handleCancel(
4143
+ await p15__namespace.select({
4144
+ message: "Module type to commit:",
4145
+ options: [
4146
+ { value: "feature", label: "Feature" },
4147
+ { value: "shared", label: "Shared Module" }
4148
+ ]
4149
+ })
4150
+ );
4151
+ }
4152
+ let name = nameArg?.trim();
4153
+ if (!name) {
4154
+ const diskModules = discoverDiskModules(projectRoot).filter((m) => m.type === type);
4155
+ const opts = diskModules.map((m) => {
4156
+ const git = checkGitStatusForDir(projectRoot, m.dir);
4157
+ return {
4158
+ value: m.name,
4159
+ label: m.name,
4160
+ hint: git.hasChanges ? `${git.filesCount} uncommitted files` : "clean working tree"
4161
+ };
4162
+ });
4163
+ name = handleCancel(
4164
+ await p15__namespace.select({
4165
+ message: `Select ${type} to commit:`,
4166
+ options: opts
4167
+ })
4168
+ );
4169
+ }
4170
+ const folder = type === "shared" ? resolveSharedDir(projectRoot) : resolveFeaturesDir(projectRoot);
4171
+ const modDir = path2__default.default.join(folder, name);
4172
+ if (!fs12__default.default.existsSync(modDir)) {
4173
+ p15__namespace.log.error(pc15__default.default.red(`Module directory does not exist: ${path2__default.default.relative(projectRoot, modDir)}`));
4174
+ process.exit(1);
4175
+ }
4176
+ const relModDir = path2__default.default.relative(projectRoot, modDir);
4177
+ const gitStatus = checkGitStatusForDir(projectRoot, modDir);
4178
+ if (!gitStatus.hasChanges) {
4179
+ p15__namespace.log.warn(pc15__default.default.yellow(`No uncommitted git changes found in '${type}/${name}'.`));
4180
+ return;
4181
+ }
4182
+ let message = flags.message?.trim();
4183
+ if (!message) {
4184
+ message = handleCancel(
4185
+ await p15__namespace.text({
4186
+ message: `Commit message for ${type}/${name}:`,
4187
+ placeholder: `e.g. feat(${name}): update use cases and dependencies`,
4188
+ validate: (v) => v && v.trim() ? void 0 : "Commit message cannot be empty"
4189
+ })
4190
+ ).trim();
4191
+ }
4192
+ const s = p15__namespace.spinner();
4193
+ s.start(`Committing '${type}/${name}' to git repository...`);
4194
+ try {
4195
+ child_process.execSync(`git add "${relModDir}"`, { cwd: projectRoot, stdio: ["ignore", "pipe", "pipe"] });
4196
+ child_process.execSync(`git commit -m "${message.replace(/"/g, '\\"')}" -- "${relModDir}"`, {
4197
+ cwd: projectRoot,
4198
+ stdio: ["ignore", "pipe", "pipe"]
4199
+ });
4200
+ s.stop(`Changes for '${type}/${name}' committed to git.`);
4201
+ p15__namespace.log.success(pc15__default.default.green(`\u2713 Committed '${type}/${name}': "${message}"`));
4202
+ } catch (err) {
4203
+ s.stop(pc15__default.default.red("Git commit failed."));
4204
+ p15__namespace.log.error(pc15__default.default.red(err.message || String(err)));
4205
+ process.exit(1);
4206
+ }
4207
+ }
4208
+ async function registerModuleChanges(typeArg, nameArg, flags = {}) {
4209
+ const projectRoot = findProjectRoot();
4210
+ const index = loadMasterIndex(projectRoot);
4211
+ let type = typeArg?.trim();
4212
+ if (!type) {
4213
+ type = handleCancel(
4214
+ await p15__namespace.select({
4215
+ message: "Module type to update:",
4216
+ options: [
4217
+ { value: "feature", label: "Feature" },
4218
+ { value: "shared", label: "Shared Module" }
4219
+ ]
4220
+ })
4221
+ );
4222
+ }
4223
+ let name = nameArg?.trim();
4224
+ if (!name) {
4225
+ const diskModules = discoverDiskModules(projectRoot).filter((m) => m.type === type);
4226
+ const diffs = getRegistryDiff(projectRoot).filter((d) => d.type === type && d.isRegistered);
4227
+ const opts = diskModules.map((m) => {
4228
+ const d = diffs.find((item) => item.name === m.name);
4229
+ return {
4230
+ value: m.name,
4231
+ label: m.name,
4232
+ hint: d?.hasIntegrityDrift ? "drift detected (needs register)" : "up to date"
4233
+ };
4234
+ });
4235
+ name = handleCancel(
4236
+ await p15__namespace.select({
4237
+ message: `Select ${type} to register changes:`,
4238
+ options: opts
4239
+ })
4240
+ );
4241
+ }
4242
+ const folder = type === "shared" ? resolveSharedDir(projectRoot) : resolveFeaturesDir(projectRoot);
4243
+ const modDir = path2__default.default.join(folder, name);
4244
+ if (!fs12__default.default.existsSync(modDir)) {
4245
+ p15__namespace.log.error(pc15__default.default.red(`Module directory does not exist: ${path2__default.default.relative(projectRoot, modDir)}`));
4246
+ process.exit(1);
4247
+ }
4248
+ const files = getAllFiles(modDir);
4249
+ const integrity = computeDirectoryHash(modDir, files);
4250
+ const manifestPath = path2__default.default.join(modDir, "registry.json");
4251
+ let manifest;
4252
+ if (fs12__default.default.existsSync(manifestPath)) {
4253
+ manifest = JSON.parse(fs12__default.default.readFileSync(manifestPath, "utf-8"));
4254
+ } else {
4255
+ manifest = {
4256
+ name,
4257
+ type,
4258
+ description: `${name} ${type} module`,
4259
+ version: "1.0.0",
4260
+ dependencies: { npm: [], shared: [], features: [] },
4261
+ files: []
4262
+ };
4263
+ }
4264
+ manifest.files = files;
4265
+ manifest.integrity = integrity;
4266
+ if (flags.version) {
4267
+ manifest.version = flags.version;
4268
+ }
4269
+ fs12__default.default.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n", "utf-8");
4270
+ const targetList = type === "shared" ? index.shared : index.features;
4271
+ const existingIdx = targetList.findIndex((item) => item.name === name);
4272
+ if (existingIdx >= 0) {
4273
+ targetList[existingIdx] = {
4274
+ name,
4275
+ description: manifest.description,
4276
+ version: manifest.version,
4277
+ integrity
4278
+ };
4279
+ } else {
4280
+ targetList.push({
4281
+ name,
4282
+ description: manifest.description,
4283
+ version: manifest.version,
4284
+ integrity
4285
+ });
4286
+ }
4287
+ saveMasterIndex(projectRoot, index);
4288
+ p15__namespace.log.success(
4289
+ pc15__default.default.green(`\u2713 Registered changes for '${type}/${name}' (new integrity: ${integrity.slice(0, 18)}...)`)
4290
+ );
4291
+ }
4292
+ async function unregisterModule(typeArg, nameArg, flags = {}) {
4293
+ const projectRoot = findProjectRoot();
4294
+ const index = loadMasterIndex(projectRoot);
4295
+ let type = typeArg?.trim();
4296
+ if (!type) {
4297
+ type = handleCancel(
4298
+ await p15__namespace.select({
4299
+ message: "Module type to unregister:",
4300
+ options: [
4301
+ { value: "feature", label: "Feature" },
4302
+ { value: "shared", label: "Shared Module" }
4303
+ ]
4304
+ })
4305
+ );
4306
+ }
4307
+ const registeredList = type === "shared" ? index.shared : index.features;
4308
+ if (registeredList.length === 0) {
4309
+ p15__namespace.log.warn(`No registered ${type} modules found in index.json.`);
4310
+ return;
4311
+ }
4312
+ let name = nameArg?.trim();
4313
+ if (!name) {
4314
+ name = handleCancel(
4315
+ await p15__namespace.select({
4316
+ message: `Select ${type} to unregister from index.json:`,
4317
+ options: registeredList.map((item) => ({
4318
+ value: item.name,
4319
+ label: item.name,
4320
+ hint: item.description
4321
+ }))
4322
+ })
4323
+ );
4324
+ }
4325
+ if (type === "shared") {
4326
+ index.shared = index.shared.filter((item) => item.name !== name);
4327
+ } else {
4328
+ index.features = index.features.filter((item) => item.name !== name);
4329
+ }
4330
+ saveMasterIndex(projectRoot, index);
4331
+ p15__namespace.log.success(
4332
+ pc15__default.default.green(
4333
+ `\u2713 Unregistered module '${type}/${name}' from index.json (source code retained on disk in ${type === "shared" ? "shared" : "features"}/${name})`
4334
+ )
4335
+ );
4336
+ }
4337
+ function diffRegistry(flags = {}) {
4338
+ const projectRoot = findProjectRoot();
4339
+ const diffs = getRegistryDiff(projectRoot);
4340
+ p15__namespace.log.step(pc15__default.default.bold("\u{1F4CB} Mason Registry Diff & Status Summary"));
4341
+ const unregistered = diffs.filter((d) => d.existsOnDisk && !d.isRegistered);
4342
+ const uncommitted = diffs.filter((d) => d.existsOnDisk && d.hasUncommittedGitChanges);
4343
+ const committedDrift = diffs.filter(
4344
+ (d) => d.existsOnDisk && d.isRegistered && !d.hasUncommittedGitChanges && d.hasIntegrityDrift
4345
+ );
4346
+ const upToDate = diffs.filter(
4347
+ (d) => d.existsOnDisk && d.isRegistered && !d.hasUncommittedGitChanges && !d.hasIntegrityDrift
4348
+ );
4349
+ const missing = diffs.filter((d) => !d.existsOnDisk && d.isRegistered);
4350
+ if (unregistered.length > 0) {
4351
+ p15__namespace.log.message(pc15__default.default.yellow(`
4352
+ \u{1F4E6} Unregistered Modules (${unregistered.length}):`));
4353
+ for (const mod of unregistered) {
4354
+ p15__namespace.log.message(` ${pc15__default.default.red("\u25CF")} ${pc15__default.default.bold(`${mod.type}/${mod.name}`)} ${pc15__default.default.dim("(not registered in index.json)")}`);
4355
+ }
4356
+ }
4357
+ if (uncommitted.length > 0) {
4358
+ p15__namespace.log.message(pc15__default.default.yellow(`
4359
+ \u{1F4BE} Uncommitted Changes in Git (${uncommitted.length}):`));
4360
+ for (const mod of uncommitted) {
4361
+ p15__namespace.log.message(
4362
+ ` ${pc15__default.default.yellow("\u25B2")} ${pc15__default.default.bold(`${mod.type}/${mod.name}`)} ${pc15__default.default.dim(`(${mod.uncommittedFilesCount} modified/untracked files)`)}`
4363
+ );
4364
+ }
4365
+ }
4366
+ if (committedDrift.length > 0) {
4367
+ p15__namespace.log.message(pc15__default.default.cyan(`
4368
+ \u{1F680} Committed But Unregistered Changes (${committedDrift.length}):`));
4369
+ for (const mod of committedDrift) {
4370
+ p15__namespace.log.message(
4371
+ ` ${pc15__default.default.cyan("\u25C6")} ${pc15__default.default.bold(`${mod.type}/${mod.name}`)} ${pc15__default.default.dim(
4372
+ `local: ${mod.localIntegrity?.slice(0, 16)}... != registered: ${mod.registeredIntegrity?.slice(0, 16)}...`
4373
+ )}`
4374
+ );
4375
+ }
4376
+ }
4377
+ if (missing.length > 0) {
4378
+ p15__namespace.log.message(pc15__default.default.red(`
4379
+ \u26A0\uFE0F Missing on Disk (${missing.length}):`));
4380
+ for (const mod of missing) {
4381
+ p15__namespace.log.message(` ${pc15__default.default.red("\u2715")} ${pc15__default.default.bold(`${mod.type}/${mod.name}`)} ${pc15__default.default.dim("(in index.json, but folder missing)")}`);
4382
+ }
4383
+ }
4384
+ if (upToDate.length > 0) {
4385
+ p15__namespace.log.message(pc15__default.default.green(`
4386
+ \u2713 Up to Date & Registered (${upToDate.length}):`));
4387
+ for (const mod of upToDate) {
4388
+ p15__namespace.log.message(` ${pc15__default.default.green("\u2714")} ${mod.type}/${mod.name} ${pc15__default.default.dim(`(${mod.registeredIntegrity?.slice(0, 18)}...)`)}`);
4389
+ }
4390
+ }
4391
+ p15__namespace.log.message("");
4392
+ }
4393
+ async function handleRegistry(actionArg, typeArg, nameArg, flags = {}) {
4394
+ let action = actionArg?.trim().toLowerCase();
4395
+ if (!action) {
4396
+ action = handleCancel(
4397
+ await p15__namespace.select({
4398
+ message: "\u{1F3DB}\uFE0F Mason Registry: What would you like to do?",
4399
+ options: [
4400
+ {
4401
+ value: "diff",
4402
+ label: "Inspect Diff & Status",
4403
+ hint: "Summary of unregistered, uncommitted, and drifted modules"
4404
+ },
4405
+ {
4406
+ value: "register",
4407
+ label: "Register Module",
4408
+ hint: "Add an existing module to index.json with integrity checksum"
4409
+ },
4410
+ {
4411
+ value: "commit",
4412
+ label: "Commit Changes",
4413
+ hint: "Commit a module's working changes to git in the background"
4414
+ },
4415
+ {
4416
+ value: "register-changes",
4417
+ label: "Register Changes",
4418
+ hint: "Update index.json & registry.json with latest checksums"
4419
+ },
4420
+ {
4421
+ value: "unregister",
4422
+ label: "Unregister Module",
4423
+ hint: "Remove module from index.json (source code stays on disk)"
4424
+ }
4425
+ ]
4426
+ })
4427
+ );
4428
+ }
4429
+ switch (action) {
4430
+ case "diff":
4431
+ case "status":
4432
+ diffRegistry(flags);
4433
+ break;
4434
+ case "register":
4435
+ await registerModule(typeArg, nameArg, flags);
4436
+ break;
4437
+ case "commit":
4438
+ await commitModule(typeArg, nameArg, flags);
4439
+ break;
4440
+ case "register-changes":
4441
+ case "update":
4442
+ await registerModuleChanges(typeArg, nameArg, flags);
4443
+ break;
4444
+ case "unregister":
4445
+ await unregisterModule(typeArg, nameArg, flags);
4446
+ break;
4447
+ default:
4448
+ p15__namespace.log.error(
4449
+ pc15__default.default.red(
4450
+ `Unknown registry action: "${action}". Valid actions: diff, register, commit, register-changes, unregister.`
4451
+ )
4452
+ );
4453
+ process.exit(1);
4454
+ }
4455
+ }
3376
4456
 
3377
4457
  // src/cli.ts
3378
4458
  function createCli() {
3379
4459
  const cli = cac.cac("mason");
3380
- 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) => {
4460
+ 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) => {
3381
4461
  if (args && args.length > 0) {
3382
- p3__namespace.log.error(
3383
- pc13__default.default.red(
4462
+ p15__namespace.log.error(
4463
+ pc15__default.default.red(
3384
4464
  `Unknown command: "${args.join(" ")}". Use --help to list available commands.`
3385
4465
  )
3386
4466
  );
3387
4467
  process.exit(1);
3388
4468
  }
3389
4469
  try {
4470
+ const projectRoot = findProjectRoot();
4471
+ const mode = getEffectiveMode(projectRoot, flags);
4472
+ if (mode === "registry") {
4473
+ const choice2 = handleCancel(
4474
+ await p15__namespace.select({
4475
+ message: "\u{1F3DB}\uFE0F Mason Registry Maintainer: What would you like to do?",
4476
+ options: [
4477
+ {
4478
+ value: "diff",
4479
+ label: "Inspect Diff & Status",
4480
+ hint: "Overview of unregistered, uncommitted, or drifted modules"
4481
+ },
4482
+ {
4483
+ value: "create_feature",
4484
+ label: "Initialize New Feature",
4485
+ hint: "Scaffold a new feature slice under features/"
4486
+ },
4487
+ {
4488
+ value: "create_shared",
4489
+ label: "Initialize New Shared Module",
4490
+ hint: "Scaffold a new shared utility/port under shared/"
4491
+ },
4492
+ {
4493
+ value: "create_usecase",
4494
+ label: "Create New Use Case",
4495
+ hint: "Add a new use case to an existing feature"
4496
+ },
4497
+ {
4498
+ value: "register",
4499
+ label: "Register Module",
4500
+ hint: "Add a module to index.json with integrity checksum"
4501
+ },
4502
+ {
4503
+ value: "commit",
4504
+ label: "Commit Module Changes",
4505
+ hint: "Commit working changes in a module to git"
4506
+ },
4507
+ {
4508
+ value: "register_changes",
4509
+ label: "Register Changes",
4510
+ hint: "Recompute checksums and update index.json"
4511
+ },
4512
+ {
4513
+ value: "unregister",
4514
+ label: "Unregister Module",
4515
+ hint: "Remove from index.json but keep source on disk"
4516
+ },
4517
+ {
4518
+ value: "import",
4519
+ label: "Import from Remote Registry",
4520
+ hint: "Download a module from upstream"
4521
+ }
4522
+ ]
4523
+ })
4524
+ );
4525
+ switch (choice2) {
4526
+ case "diff":
4527
+ await handleRegistry("diff", void 0, void 0, flags);
4528
+ break;
4529
+ case "create_feature":
4530
+ await handleCreate("feature", void 0, flags);
4531
+ break;
4532
+ case "create_shared":
4533
+ await handleCreate("shared", void 0, flags);
4534
+ break;
4535
+ case "create_usecase":
4536
+ await handleCreate("usecase", void 0, flags);
4537
+ break;
4538
+ case "register":
4539
+ await handleRegistry("register", void 0, void 0, flags);
4540
+ break;
4541
+ case "commit":
4542
+ await handleRegistry("commit", void 0, void 0, flags);
4543
+ break;
4544
+ case "register_changes":
4545
+ await handleRegistry("register-changes", void 0, void 0, flags);
4546
+ break;
4547
+ case "unregister":
4548
+ await handleRegistry("unregister", void 0, void 0, flags);
4549
+ break;
4550
+ case "import":
4551
+ await handleImport(void 0, void 0, flags);
4552
+ break;
4553
+ }
4554
+ return;
4555
+ }
3390
4556
  const choice = handleCancel(
3391
- await p3__namespace.select({
4557
+ await p15__namespace.select({
3392
4558
  message: "\u{1F9F1} Mason: What would you like to do?",
3393
4559
  options: [
3394
4560
  {
3395
4561
  value: "create",
3396
4562
  label: "Create local component",
3397
- hint: "Scaffold a new feature, use case, domain entity, interface, etc."
4563
+ hint: "Scaffold a new feature, shared module, use case, domain entity, etc."
3398
4564
  },
3399
4565
  {
3400
4566
  value: "import",
3401
4567
  label: "Import from registry",
3402
4568
  hint: "Download and link pre-built shared modules or features from the registry"
4569
+ },
4570
+ {
4571
+ value: "registry",
4572
+ label: "Registry management",
4573
+ hint: "Inspect diff, register modules, or commit module changes"
3403
4574
  }
3404
4575
  ]
3405
4576
  })
3406
4577
  );
3407
4578
  if (choice === "create") {
3408
4579
  await handleCreate(void 0, void 0, flags);
4580
+ } else if (choice === "registry") {
4581
+ await handleRegistry(void 0, void 0, void 0, flags);
3409
4582
  } else {
3410
4583
  await handleImport(void 0, void 0, flags);
3411
4584
  }
@@ -3415,8 +4588,8 @@ function createCli() {
3415
4588
  });
3416
4589
  cli.command(
3417
4590
  "create [type] [name]",
3418
- "Scaffold local Clean Architecture components (feature, usecase, domain, etc.)"
3419
- ).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 = {}) => {
4591
+ "Scaffold local Clean Architecture components (feature, shared, usecase, domain, etc.)"
4592
+ ).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 = {}) => {
3420
4593
  try {
3421
4594
  await handleCreate(type, name, flags);
3422
4595
  } catch (err) {
@@ -3433,8 +4606,28 @@ function createCli() {
3433
4606
  handleCliError(err);
3434
4607
  }
3435
4608
  });
4609
+ cli.command(
4610
+ "registry [action] [type] [name]",
4611
+ "Registry & version management (diff, register, commit, register-changes, unregister)"
4612
+ ).option("-m, --message <message>", "Commit message for 'commit' command").option("-y, --yes", "Accept defaults and skip interactive prompts").example("mason registry diff").example("mason registry register feature authn").example("mason registry commit shared hasher -m 'Add bcrypt engine'").example("mason registry register-changes shared hasher").example("mason registry unregister feature oldFeature").action(
4613
+ async (action, type, name, flags = {}) => {
4614
+ try {
4615
+ await handleRegistry(action, type, name, flags);
4616
+ } catch (err) {
4617
+ handleCliError(err);
4618
+ }
4619
+ }
4620
+ );
4621
+ cli.command("diff", "Inspect registry status and diff (alias for 'mason registry diff')").allowUnknownOptions().action(async (flags = {}) => {
4622
+ try {
4623
+ await handleRegistry("diff", void 0, void 0, flags);
4624
+ } catch (err) {
4625
+ handleCliError(err);
4626
+ }
4627
+ });
3436
4628
  const createSubtypes = [
3437
4629
  "feature",
4630
+ "shared",
3438
4631
  "usecase",
3439
4632
  "domain",
3440
4633
  "infrastructure",
@@ -3455,7 +4648,7 @@ function createCli() {
3455
4648
  });
3456
4649
  }
3457
4650
  cli.help();
3458
- cli.version("1.0.10");
4651
+ cli.version("1.0.13");
3459
4652
  return cli;
3460
4653
  }
3461
4654
  function handleCliError(err) {
@@ -3463,7 +4656,7 @@ function handleCliError(err) {
3463
4656
  process.exit(0);
3464
4657
  }
3465
4658
  const message = err instanceof Error ? err.message : String(err);
3466
- p3__namespace.log.error(pc13__default.default.red(`Error: ${message}`));
4659
+ p15__namespace.log.error(pc15__default.default.red(`Error: ${message}`));
3467
4660
  process.exit(1);
3468
4661
  }
3469
4662
  async function runCli(argv = process.argv) {