@solid-stack/mason 1.0.12 → 1.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,11 +1,12 @@
1
1
  'use strict';
2
2
 
3
- var fs3 = require('fs');
4
- var path3 = require('path');
3
+ var fs4 = require('fs');
4
+ var path2 = require('path');
5
5
  var p4 = require('@clack/prompts');
6
- var pc13 = require('picocolors');
6
+ var pc14 = require('picocolors');
7
7
  var url = require('url');
8
8
  var child_process = require('child_process');
9
+ var crypto = require('crypto');
9
10
  var Handlebars = require('handlebars');
10
11
  var cac = require('cac');
11
12
 
@@ -30,10 +31,10 @@ function _interopNamespace(e) {
30
31
  return Object.freeze(n);
31
32
  }
32
33
 
33
- var fs3__default = /*#__PURE__*/_interopDefault(fs3);
34
- var path3__default = /*#__PURE__*/_interopDefault(path3);
34
+ var fs4__default = /*#__PURE__*/_interopDefault(fs4);
35
+ var path2__default = /*#__PURE__*/_interopDefault(path2);
35
36
  var p4__namespace = /*#__PURE__*/_interopNamespace(p4);
36
- var pc13__default = /*#__PURE__*/_interopDefault(pc13);
37
+ var pc14__default = /*#__PURE__*/_interopDefault(pc14);
37
38
  var Handlebars__default = /*#__PURE__*/_interopDefault(Handlebars);
38
39
 
39
40
  // src/utils/formatting.ts
@@ -104,8 +105,8 @@ function isValidIdentifier(str) {
104
105
  }
105
106
  function writeFileSafely(absolutePath, content, options = {}) {
106
107
  const projectRoot = options.projectRoot || process.cwd();
107
- const relPath = path3__default.default.relative(projectRoot, absolutePath).replace(/\\/g, "/");
108
- const existedBefore = fs3__default.default.existsSync(absolutePath);
108
+ const relPath = path2__default.default.relative(projectRoot, absolutePath).replace(/\\/g, "/");
109
+ const existedBefore = fs4__default.default.existsSync(absolutePath);
109
110
  if (options.dryRun) {
110
111
  return {
111
112
  relativePath: relPath,
@@ -124,11 +125,11 @@ function writeFileSafely(absolutePath, content, options = {}) {
124
125
  existedBefore: true
125
126
  };
126
127
  }
127
- const dir = path3__default.default.dirname(absolutePath);
128
- if (!fs3__default.default.existsSync(dir)) {
129
- fs3__default.default.mkdirSync(dir, { recursive: true });
128
+ const dir = path2__default.default.dirname(absolutePath);
129
+ if (!fs4__default.default.existsSync(dir)) {
130
+ fs4__default.default.mkdirSync(dir, { recursive: true });
130
131
  }
131
- fs3__default.default.writeFileSync(absolutePath, content, "utf-8");
132
+ fs4__default.default.writeFileSync(absolutePath, content, "utf-8");
132
133
  return {
133
134
  relativePath: relPath,
134
135
  absolutePath,
@@ -140,41 +141,114 @@ function writeFileSafely(absolutePath, content, options = {}) {
140
141
  function printGeneratedFiles(files, isDryRun = false) {
141
142
  console.log("");
142
143
  if (isDryRun) {
143
- p4__namespace.log.warn(pc13__default.default.yellow(pc13__default.default.bold("[DRY RUN PREVIEW] No files were written to disk.")));
144
+ p4__namespace.log.warn(pc14__default.default.yellow(pc14__default.default.bold("[DRY RUN PREVIEW] No files were written to disk.")));
144
145
  }
145
146
  for (const f of files) {
146
147
  if (f.action === "created") {
147
- p4__namespace.log.success(`${pc13__default.default.bold(f.relativePath)} ${pc13__default.default.dim("(created)")}`);
148
+ p4__namespace.log.success(`${pc14__default.default.bold(f.relativePath)} ${pc14__default.default.dim("(created)")}`);
148
149
  } else if (f.action === "overwritten") {
149
- p4__namespace.log.warn(`${pc13__default.default.bold(f.relativePath)} ${pc13__default.default.dim("(overwritten)")}`);
150
+ p4__namespace.log.warn(`${pc14__default.default.bold(f.relativePath)} ${pc14__default.default.dim("(overwritten)")}`);
150
151
  } else if (f.action === "skipped") {
151
- p4__namespace.log.info(`${pc13__default.default.dim(f.relativePath)} ${pc13__default.default.dim("(skipped - already exists, pass -o to overwrite)")}`);
152
+ p4__namespace.log.info(`${pc14__default.default.dim(f.relativePath)} ${pc14__default.default.dim("(skipped - already exists, pass -o to overwrite)")}`);
152
153
  } else {
153
- p4__namespace.log.info(`${pc13__default.default.bold(f.relativePath)} ${pc13__default.default.dim("(preview)")}`);
154
+ p4__namespace.log.info(`${pc14__default.default.bold(f.relativePath)} ${pc14__default.default.dim("(preview)")}`);
154
155
  }
155
156
  }
156
157
  }
158
+ function loadMasonConfig(startDir = process.cwd()) {
159
+ let current = path2__default.default.resolve(startDir);
160
+ while (true) {
161
+ const configPath = path2__default.default.join(current, "mason.config.json");
162
+ if (fs4__default.default.existsSync(configPath)) {
163
+ try {
164
+ return JSON.parse(fs4__default.default.readFileSync(configPath, "utf-8"));
165
+ } catch {
166
+ return null;
167
+ }
168
+ }
169
+ const parent = path2__default.default.dirname(current);
170
+ if (parent === current) break;
171
+ current = parent;
172
+ }
173
+ return null;
174
+ }
175
+ function getEffectiveMode(projectRoot, flags) {
176
+ if (flags?.registry) return "registry";
177
+ const config = loadMasonConfig(projectRoot);
178
+ if (config?.mode) return config.mode;
179
+ if (fs4__default.default.existsSync(path2__default.default.join(projectRoot, "index.json")) && (fs4__default.default.existsSync(path2__default.default.join(projectRoot, "features")) || fs4__default.default.existsSync(path2__default.default.join(projectRoot, "shared"))) && !fs4__default.default.existsSync(path2__default.default.join(projectRoot, "src"))) {
180
+ return "registry";
181
+ }
182
+ return "app";
183
+ }
184
+ function getConfiguredFeaturesDir(projectRoot, flags) {
185
+ if (flags?.target) return path2__default.default.resolve(projectRoot, flags.target);
186
+ if (flags?.dest) return path2__default.default.resolve(projectRoot, flags.dest);
187
+ const config = loadMasonConfig(projectRoot);
188
+ if (config?.paths?.features) {
189
+ return path2__default.default.resolve(projectRoot, config.paths.features);
190
+ }
191
+ const mode = getEffectiveMode(projectRoot, flags);
192
+ if (mode === "registry") {
193
+ return path2__default.default.join(projectRoot, "features");
194
+ }
195
+ if (fs4__default.default.existsSync(path2__default.default.join(projectRoot, "src/features"))) {
196
+ return path2__default.default.join(projectRoot, "src/features");
197
+ }
198
+ return path2__default.default.join(projectRoot, "features");
199
+ }
200
+ function getConfiguredSharedDir(projectRoot, flags) {
201
+ if (flags?.target) return path2__default.default.resolve(projectRoot, flags.target);
202
+ if (flags?.dest) return path2__default.default.resolve(projectRoot, flags.dest);
203
+ const config = loadMasonConfig(projectRoot);
204
+ if (config?.paths?.shared) {
205
+ return path2__default.default.resolve(projectRoot, config.paths.shared);
206
+ }
207
+ const mode = getEffectiveMode(projectRoot, flags);
208
+ if (mode === "registry") {
209
+ return path2__default.default.join(projectRoot, "shared");
210
+ }
211
+ if (fs4__default.default.existsSync(path2__default.default.join(projectRoot, "src/shared"))) {
212
+ return path2__default.default.join(projectRoot, "src/shared");
213
+ }
214
+ return path2__default.default.join(projectRoot, "shared");
215
+ }
216
+
217
+ // src/utils/project.ts
157
218
  function findProjectRoot(startDir = process.cwd()) {
158
- let current = path3__default.default.resolve(startDir);
219
+ let current = path2__default.default.resolve(startDir);
159
220
  while (true) {
160
- if (fs3__default.default.existsSync(path3__default.default.join(current, "package.json"))) {
221
+ if (fs4__default.default.existsSync(path2__default.default.join(current, "package.json"))) {
161
222
  return current;
162
223
  }
163
- const parent = path3__default.default.dirname(current);
224
+ const parent = path2__default.default.dirname(current);
164
225
  if (parent === current) {
165
- return path3__default.default.resolve(startDir);
226
+ return path2__default.default.resolve(startDir);
166
227
  }
167
228
  current = parent;
168
229
  }
169
230
  }
170
- function resolveFeaturesDir(projectRoot) {
171
- return path3__default.default.join(projectRoot, "features");
231
+ function resolveFeaturesDir(projectRoot, flags) {
232
+ return getConfiguredFeaturesDir(projectRoot, flags);
233
+ }
234
+ function resolveSharedDir(projectRoot, flags) {
235
+ return getConfiguredSharedDir(projectRoot, flags);
236
+ }
237
+ function listExistingShared(projectRoot) {
238
+ const sharedDir = resolveSharedDir(projectRoot);
239
+ if (!fs4__default.default.existsSync(sharedDir)) return [];
240
+ try {
241
+ const entries = fs4__default.default.readdirSync(sharedDir, { withFileTypes: true });
242
+ return entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name).sort();
243
+ } catch {
244
+ return [];
245
+ }
172
246
  }
173
247
  function listExistingFeatures(projectRoot) {
174
248
  const featuresDir = resolveFeaturesDir(projectRoot);
175
- if (!fs3__default.default.existsSync(featuresDir)) return [];
249
+ if (!fs4__default.default.existsSync(featuresDir)) return [];
176
250
  try {
177
- const entries = fs3__default.default.readdirSync(featuresDir, { withFileTypes: true });
251
+ const entries = fs4__default.default.readdirSync(featuresDir, { withFileTypes: true });
178
252
  return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
179
253
  } catch {
180
254
  return [];
@@ -184,14 +258,14 @@ function listDomainInterfaces(projectRoot, featureName) {
184
258
  const results = [];
185
259
  const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
186
260
  for (const feat of features) {
187
- const domainDir = path3__default.default.join(resolveFeaturesDir(projectRoot), feat, "domain");
188
- if (!fs3__default.default.existsSync(domainDir)) continue;
261
+ const domainDir = path2__default.default.join(resolveFeaturesDir(projectRoot), feat, "domain");
262
+ if (!fs4__default.default.existsSync(domainDir)) continue;
189
263
  try {
190
- const files = fs3__default.default.readdirSync(domainDir);
264
+ const files = fs4__default.default.readdirSync(domainDir);
191
265
  for (const file of files) {
192
266
  if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
193
267
  const name = file.replace(/\.ts$/, "");
194
- const fullPath = path3__default.default.join(domainDir, file);
268
+ const fullPath = path2__default.default.join(domainDir, file);
195
269
  if (/^I[A-Z]/.test(name)) {
196
270
  results.push({
197
271
  name,
@@ -200,7 +274,7 @@ function listDomainInterfaces(projectRoot, featureName) {
200
274
  });
201
275
  } else {
202
276
  try {
203
- const content = fs3__default.default.readFileSync(fullPath, "utf-8");
277
+ const content = fs4__default.default.readFileSync(fullPath, "utf-8");
204
278
  if (/export\s+(?:abstract\s+class|interface)\s+(\w+)/.test(content)) {
205
279
  results.push({
206
280
  name,
@@ -222,17 +296,17 @@ function listDomainEntities(projectRoot, featureName) {
222
296
  const results = [];
223
297
  const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
224
298
  for (const feat of features) {
225
- const domainDir = path3__default.default.join(resolveFeaturesDir(projectRoot), feat, "domain");
226
- if (!fs3__default.default.existsSync(domainDir)) continue;
299
+ const domainDir = path2__default.default.join(resolveFeaturesDir(projectRoot), feat, "domain");
300
+ if (!fs4__default.default.existsSync(domainDir)) continue;
227
301
  try {
228
- const files = fs3__default.default.readdirSync(domainDir);
302
+ const files = fs4__default.default.readdirSync(domainDir);
229
303
  for (const file of files) {
230
304
  if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
231
305
  const name = file.replace(/\.ts$/, "");
232
306
  if (!/^I[A-Z]/.test(name)) {
233
307
  results.push({
234
308
  name,
235
- fullPath: path3__default.default.join(domainDir, file),
309
+ fullPath: path2__default.default.join(domainDir, file),
236
310
  feature: feat
237
311
  });
238
312
  }
@@ -247,16 +321,16 @@ function listUseCases(projectRoot, featureName) {
247
321
  const results = [];
248
322
  const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
249
323
  for (const feat of features) {
250
- const useCasesDir = path3__default.default.join(resolveFeaturesDir(projectRoot), feat, "useCases");
251
- if (!fs3__default.default.existsSync(useCasesDir)) continue;
324
+ const useCasesDir = path2__default.default.join(resolveFeaturesDir(projectRoot), feat, "useCases");
325
+ if (!fs4__default.default.existsSync(useCasesDir)) continue;
252
326
  try {
253
- const files = fs3__default.default.readdirSync(useCasesDir);
327
+ const files = fs4__default.default.readdirSync(useCasesDir);
254
328
  for (const file of files) {
255
329
  if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
256
330
  const name = file.replace(/\.ts$/, "");
257
331
  results.push({
258
332
  name,
259
- fullPath: path3__default.default.join(useCasesDir, file),
333
+ fullPath: path2__default.default.join(useCasesDir, file),
260
334
  feature: feat
261
335
  });
262
336
  }
@@ -281,13 +355,13 @@ function handleCancel(result) {
281
355
  }
282
356
  function printBanner() {
283
357
  console.log("");
284
- p4__namespace.intro(pc13__default.default.bgCyan(pc13__default.default.black(" @solid-stack/mason ")));
358
+ p4__namespace.intro(pc14__default.default.bgCyan(pc14__default.default.black(" @solid-stack/mason ")));
285
359
  }
286
360
  async function promptProperties(options = {}) {
287
361
  const properties = options.defaults ? [...options.defaults] : [];
288
362
  if (properties.length > 0) {
289
- const summary = properties.map((p15) => `${p15.name}: ${p15.type}${p15.optional ? "?" : ""}`).join(", ");
290
- p4__namespace.log.info(pc13__default.default.dim(`Pre-configured fields: ${summary}`));
363
+ const summary = properties.map((p16) => `${p16.name}: ${p16.type}${p16.optional ? "?" : ""}`).join(", ");
364
+ p4__namespace.log.info(pc14__default.default.dim(`Pre-configured fields: ${summary}`));
291
365
  const addMore = handleCancel(
292
366
  await p4__namespace.confirm({
293
367
  message: "Would you like to add custom fields/properties?",
@@ -298,7 +372,7 @@ async function promptProperties(options = {}) {
298
372
  return properties;
299
373
  }
300
374
  }
301
- p4__namespace.log.info(pc13__default.default.cyan("Enter properties (leave name blank when finished):"));
375
+ p4__namespace.log.info(pc14__default.default.cyan("Enter properties (leave name blank when finished):"));
302
376
  while (true) {
303
377
  const name = handleCancel(
304
378
  await p4__namespace.text({
@@ -346,7 +420,7 @@ async function promptProperties(options = {}) {
346
420
  optional
347
421
  });
348
422
  p4__namespace.log.success(
349
- pc13__default.default.green(`Added: ${name.trim()}${optional ? "?" : ""}: ${finalType.trim()}`)
423
+ pc14__default.default.green(`Added: ${name.trim()}${optional ? "?" : ""}: ${finalType.trim()}`)
350
424
  );
351
425
  }
352
426
  return properties;
@@ -368,25 +442,25 @@ function normalizeRegistryUrl(urlOrPath, baseDir) {
368
442
  return `https://raw.githubusercontent.com/${owner}/${repo}/${branch}`;
369
443
  }
370
444
  if (baseDir && (trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed === "." || trimmed === "..")) {
371
- return path3__default.default.resolve(baseDir, trimmed);
445
+ return path2__default.default.resolve(baseDir, trimmed);
372
446
  }
373
447
  return trimmed;
374
448
  }
375
449
  function findLocalRegistry(startDir = process.cwd()) {
376
- let current = path3__default.default.resolve(startDir);
450
+ let current = path2__default.default.resolve(startDir);
377
451
  while (true) {
378
- const candidate = path3__default.default.join(current, "mason-registry");
379
- if (fs3__default.default.existsSync(candidate) && fs3__default.default.existsSync(path3__default.default.join(candidate, "index.json"))) {
452
+ const candidate = path2__default.default.join(current, "mason-registry");
453
+ if (fs4__default.default.existsSync(candidate) && fs4__default.default.existsSync(path2__default.default.join(candidate, "index.json"))) {
380
454
  return candidate;
381
455
  }
382
- const parent = path3__default.default.dirname(current);
456
+ const parent = path2__default.default.dirname(current);
383
457
  if (parent === current) break;
384
458
  current = parent;
385
459
  }
386
460
  return null;
387
461
  }
388
462
  function getRegistryBase(projectRoot, override) {
389
- const rootDir = projectRoot ? path3__default.default.resolve(projectRoot) : process.cwd();
463
+ const rootDir = projectRoot ? path2__default.default.resolve(projectRoot) : process.cwd();
390
464
  if (override && override.trim()) {
391
465
  return normalizeRegistryUrl(override.trim(), rootDir);
392
466
  }
@@ -394,30 +468,30 @@ function getRegistryBase(projectRoot, override) {
394
468
  return normalizeRegistryUrl(process.env.MASON_REGISTRY_URL.trim(), rootDir);
395
469
  }
396
470
  const configLocations = [
397
- path3__default.default.join(rootDir, "mason.config.json"),
398
- path3__default.default.join(process.cwd(), "mason.config.json")
471
+ path2__default.default.join(rootDir, "mason.config.json"),
472
+ path2__default.default.join(process.cwd(), "mason.config.json")
399
473
  ];
400
474
  for (const configPath of configLocations) {
401
- if (fs3__default.default.existsSync(configPath)) {
475
+ if (fs4__default.default.existsSync(configPath)) {
402
476
  try {
403
- const config = JSON.parse(fs3__default.default.readFileSync(configPath, "utf-8"));
477
+ const config = JSON.parse(fs4__default.default.readFileSync(configPath, "utf-8"));
404
478
  if (config.registry && typeof config.registry === "string") {
405
- return normalizeRegistryUrl(config.registry.trim(), path3__default.default.dirname(configPath));
479
+ return normalizeRegistryUrl(config.registry.trim(), path2__default.default.dirname(configPath));
406
480
  }
407
481
  } catch {
408
482
  }
409
483
  }
410
484
  }
411
485
  const pkgLocations = [
412
- path3__default.default.join(rootDir, "package.json"),
413
- path3__default.default.join(process.cwd(), "package.json")
486
+ path2__default.default.join(rootDir, "package.json"),
487
+ path2__default.default.join(process.cwd(), "package.json")
414
488
  ];
415
489
  for (const pkgPath of pkgLocations) {
416
- if (fs3__default.default.existsSync(pkgPath)) {
490
+ if (fs4__default.default.existsSync(pkgPath)) {
417
491
  try {
418
- const pkg = JSON.parse(fs3__default.default.readFileSync(pkgPath, "utf-8"));
492
+ const pkg = JSON.parse(fs4__default.default.readFileSync(pkgPath, "utf-8"));
419
493
  if (pkg.mason?.registry && typeof pkg.mason.registry === "string") {
420
- return normalizeRegistryUrl(pkg.mason.registry.trim(), path3__default.default.dirname(pkgPath));
494
+ return normalizeRegistryUrl(pkg.mason.registry.trim(), path2__default.default.dirname(pkgPath));
421
495
  }
422
496
  } catch {
423
497
  }
@@ -458,11 +532,11 @@ async function fetchRegistryFile(registryBase, relPath) {
458
532
  if (baseDir.startsWith("file://")) {
459
533
  baseDir = url.fileURLToPath(baseDir);
460
534
  }
461
- const filePath = path3__default.default.resolve(baseDir, cleanRelPath);
462
- if (!fs3__default.default.existsSync(filePath)) {
535
+ const filePath = path2__default.default.resolve(baseDir, cleanRelPath);
536
+ if (!fs4__default.default.existsSync(filePath)) {
463
537
  throw new Error(`Registry file not found at ${filePath}`);
464
538
  }
465
- return fs3__default.default.promises.readFile(filePath, "utf-8");
539
+ return fs4__default.default.promises.readFile(filePath, "utf-8");
466
540
  }
467
541
  async function fetchRegistryIndex(registryBase) {
468
542
  const content = await fetchRegistryFile(registryBase, "index.json");
@@ -524,26 +598,26 @@ function findClosestMatch(target, candidates) {
524
598
  return null;
525
599
  }
526
600
  function detectPackageManager(projectRoot) {
527
- if (fs3__default.default.existsSync(path3__default.default.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
528
- if (fs3__default.default.existsSync(path3__default.default.join(projectRoot, "yarn.lock"))) return "yarn";
529
- if (fs3__default.default.existsSync(path3__default.default.join(projectRoot, "bun.lockb")) || fs3__default.default.existsSync(path3__default.default.join(projectRoot, "bun.lock"))) return "bun";
601
+ if (fs4__default.default.existsSync(path2__default.default.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
602
+ if (fs4__default.default.existsSync(path2__default.default.join(projectRoot, "yarn.lock"))) return "yarn";
603
+ if (fs4__default.default.existsSync(path2__default.default.join(projectRoot, "bun.lockb")) || fs4__default.default.existsSync(path2__default.default.join(projectRoot, "bun.lock"))) return "bun";
530
604
  return "npm";
531
605
  }
532
606
  function getMissingNpmPackages(projectRoot, requiredPackages) {
533
607
  if (!requiredPackages || requiredPackages.length === 0) return [];
534
- const pkgJsonPath = path3__default.default.join(projectRoot, "package.json");
535
- if (!fs3__default.default.existsSync(pkgJsonPath)) {
608
+ const pkgJsonPath = path2__default.default.join(projectRoot, "package.json");
609
+ if (!fs4__default.default.existsSync(pkgJsonPath)) {
536
610
  return requiredPackages;
537
611
  }
538
612
  try {
539
- const pkg = JSON.parse(fs3__default.default.readFileSync(pkgJsonPath, "utf-8"));
613
+ const pkg = JSON.parse(fs4__default.default.readFileSync(pkgJsonPath, "utf-8"));
540
614
  const installed = /* @__PURE__ */ new Set([
541
615
  ...Object.keys(pkg.dependencies || {}),
542
616
  ...Object.keys(pkg.devDependencies || {}),
543
617
  ...Object.keys(pkg.peerDependencies || {})
544
618
  ]);
545
- return requiredPackages.filter((p15) => {
546
- const nameOnly = p15.startsWith("@") ? `@${p15.slice(1).split("@")[0] || ""}` : p15.split("@")[0] || p15;
619
+ return requiredPackages.filter((p16) => {
620
+ const nameOnly = p16.startsWith("@") ? `@${p16.slice(1).split("@")[0] || ""}` : p16.split("@")[0] || p16;
547
621
  return !installed.has(nameOnly);
548
622
  });
549
623
  } catch {
@@ -568,12 +642,47 @@ function installNpmPackages(projectRoot, packages, pm) {
568
642
  installCmd = `npm install ${packages.join(" ")}`;
569
643
  break;
570
644
  }
571
- p4__namespace.log.info(`Installing missing dependencies with ${manager}: ${pc13__default.default.cyan(packages.join(", "))}`);
645
+ p4__namespace.log.info(`Installing missing dependencies with ${manager}: ${pc14__default.default.cyan(packages.join(", "))}`);
572
646
  child_process.execSync(installCmd, {
573
647
  cwd: projectRoot,
574
648
  stdio: "inherit"
575
649
  });
576
650
  }
651
+ function getAllFiles(dir, baseDir = dir) {
652
+ if (!fs4__default.default.existsSync(dir)) return [];
653
+ const entries = fs4__default.default.readdirSync(dir, { withFileTypes: true });
654
+ const files = [];
655
+ for (const entry of entries) {
656
+ const fullPath = path2__default.default.join(dir, entry.name);
657
+ const relPath = path2__default.default.relative(baseDir, fullPath).replace(/\\/g, "/");
658
+ if (entry.isDirectory()) {
659
+ if (entry.name === "__tests__" || entry.name.startsWith(".")) {
660
+ continue;
661
+ }
662
+ files.push(...getAllFiles(fullPath, baseDir));
663
+ } else if (entry.isFile()) {
664
+ if (entry.name === "registry.json" || entry.name.endsWith(".test.ts") || entry.name.endsWith(".spec.ts") || entry.name.startsWith(".")) {
665
+ continue;
666
+ }
667
+ files.push(relPath);
668
+ }
669
+ }
670
+ return files.sort();
671
+ }
672
+ function computeDirectoryHash(dirPath, files) {
673
+ if (!fs4__default.default.existsSync(dirPath)) return "";
674
+ const hash = crypto.createHash("sha256");
675
+ const fileList = (files ?? getAllFiles(dirPath)).sort();
676
+ for (const relativePath of fileList) {
677
+ const absolutePath = path2__default.default.join(dirPath, relativePath);
678
+ if (!fs4__default.default.existsSync(absolutePath)) continue;
679
+ const content = fs4__default.default.readFileSync(absolutePath, "utf8");
680
+ const normalizedContent = content.replace(/\r\n/g, "\n");
681
+ hash.update(relativePath);
682
+ hash.update(normalizedContent);
683
+ }
684
+ return `sha256-${hash.digest("hex")}`;
685
+ }
577
686
  var hbs = Handlebars__default.default.create();
578
687
  hbs.registerHelper("eq", (a, b) => a === b);
579
688
  hbs.registerHelper("ne", (a, b) => a !== b);
@@ -590,33 +699,33 @@ hbs.registerHelper("join", (arr, sep) => {
590
699
  });
591
700
  var cachedTemplatesDir = null;
592
701
  function getTemplatesDir() {
593
- if (cachedTemplatesDir && fs3__default.default.existsSync(cachedTemplatesDir)) {
702
+ if (cachedTemplatesDir && fs4__default.default.existsSync(cachedTemplatesDir)) {
594
703
  return cachedTemplatesDir;
595
704
  }
596
- const currentDir = typeof __dirname !== "undefined" ? __dirname : path3__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
597
- const cand1 = path3__default.default.resolve(currentDir, "../../templates");
598
- if (fs3__default.default.existsSync(cand1)) {
705
+ 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('index.cjs', document.baseURI).href))));
706
+ const cand1 = path2__default.default.resolve(currentDir, "../../templates");
707
+ if (fs4__default.default.existsSync(cand1)) {
599
708
  cachedTemplatesDir = cand1;
600
709
  return cand1;
601
710
  }
602
- const cand2 = path3__default.default.resolve(currentDir, "../templates");
603
- if (fs3__default.default.existsSync(cand2)) {
711
+ const cand2 = path2__default.default.resolve(currentDir, "../templates");
712
+ if (fs4__default.default.existsSync(cand2)) {
604
713
  cachedTemplatesDir = cand2;
605
714
  return cand2;
606
715
  }
607
- const cand3 = path3__default.default.resolve(currentDir, "templates");
608
- if (fs3__default.default.existsSync(cand3)) {
716
+ const cand3 = path2__default.default.resolve(currentDir, "templates");
717
+ if (fs4__default.default.existsSync(cand3)) {
609
718
  cachedTemplatesDir = cand3;
610
719
  return cand3;
611
720
  }
612
721
  let curr = currentDir;
613
722
  while (true) {
614
- const candTemplates = path3__default.default.join(curr, "templates");
615
- if (fs3__default.default.existsSync(candTemplates)) {
723
+ const candTemplates = path2__default.default.join(curr, "templates");
724
+ if (fs4__default.default.existsSync(candTemplates)) {
616
725
  cachedTemplatesDir = candTemplates;
617
726
  return candTemplates;
618
727
  }
619
- const parent = path3__default.default.dirname(curr);
728
+ const parent = path2__default.default.dirname(curr);
620
729
  if (parent === curr) break;
621
730
  curr = parent;
622
731
  }
@@ -624,8 +733,8 @@ function getTemplatesDir() {
624
733
  }
625
734
  var templateCache = /* @__PURE__ */ new Map();
626
735
  function loadTemplate(relativePath) {
627
- const fullPath = path3__default.default.isAbsolute(relativePath) ? relativePath : path3__default.default.join(getTemplatesDir(), relativePath);
628
- return fs3__default.default.readFileSync(fullPath, "utf-8");
736
+ const fullPath = path2__default.default.isAbsolute(relativePath) ? relativePath : path2__default.default.join(getTemplatesDir(), relativePath);
737
+ return fs4__default.default.readFileSync(fullPath, "utf-8");
629
738
  }
630
739
  function renderTemplateFile(relativePath, context) {
631
740
  let compiled = templateCache.get(relativePath);
@@ -710,8 +819,8 @@ function generateDomainEntityCode(name, kind = "interface", properties = []) {
710
819
  function generateDomain(options) {
711
820
  const projectRoot = options.projectRoot || findProjectRoot();
712
821
  const entityName = toPascalCase(options.name);
713
- const targetDir = options.targetDir || path3__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
714
- const filePath = path3__default.default.join(targetDir, `${entityName}.ts`);
822
+ const targetDir = options.targetDir || path2__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
823
+ const filePath = path2__default.default.join(targetDir, `${entityName}.ts`);
715
824
  const code = generateDomainEntityCode(
716
825
  entityName,
717
826
  options.kind || "interface",
@@ -735,7 +844,7 @@ function generateInterfaceCode(ifaceName, kind = "abstractClass", entityName, me
735
844
  const idKey = `${entityKey}Id`;
736
845
  const listKey = `${entityKey}s`;
737
846
  const formattedCustomMethods = customMethods.map((m) => {
738
- const params = m.params && m.params.length > 0 ? m.params.map((p15) => `${p15.name}: ${p15.type}`).join("; ") : "";
847
+ const params = m.params && m.params.length > 0 ? m.params.map((p16) => `${p16.name}: ${p16.type}`).join("; ") : "";
739
848
  const returnType = m.returnType ? `Promise<${m.returnType}>` : "Promise<void>";
740
849
  return {
741
850
  name: m.name,
@@ -760,8 +869,8 @@ function generateInterfaceCode(ifaceName, kind = "abstractClass", entityName, me
760
869
  function generateInterface(options) {
761
870
  const projectRoot = options.projectRoot || findProjectRoot();
762
871
  const ifaceName = ensureInterfacePrefix(options.name);
763
- const targetDir = options.targetDir || path3__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
764
- const filePath = path3__default.default.join(targetDir, `${ifaceName}.ts`);
872
+ const targetDir = options.targetDir || path2__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
873
+ const filePath = path2__default.default.join(targetDir, `${ifaceName}.ts`);
765
874
  const code = generateInterfaceCode(
766
875
  ifaceName,
767
876
  options.kind || "abstractClass",
@@ -996,7 +1105,7 @@ function parseInterfaceMethodsAndImports(content, targetIfaceName) {
996
1105
  };
997
1106
  }
998
1107
  function findInterfaceFile(projectRoot, ifaceName, featureName, customPath) {
999
- if (customPath && fs3__default.default.existsSync(customPath)) {
1108
+ if (customPath && fs4__default.default.existsSync(customPath)) {
1000
1109
  return { filePath: customPath, ifaceName };
1001
1110
  }
1002
1111
  const candidateNames = [
@@ -1006,22 +1115,22 @@ function findInterfaceFile(projectRoot, ifaceName, featureName, customPath) {
1006
1115
  ];
1007
1116
  const features = featureName ? [featureName, ...listExistingFeatures(projectRoot).filter((f) => f !== featureName)] : listExistingFeatures(projectRoot);
1008
1117
  for (const feat of features) {
1009
- const domainDir = path3__default.default.join(resolveFeaturesDir(projectRoot), feat, "domain");
1010
- if (!fs3__default.default.existsSync(domainDir)) continue;
1118
+ const domainDir = path2__default.default.join(resolveFeaturesDir(projectRoot), feat, "domain");
1119
+ if (!fs4__default.default.existsSync(domainDir)) continue;
1011
1120
  for (const cand of candidateNames) {
1012
- const p1 = path3__default.default.join(domainDir, `${cand}.ts`);
1013
- if (fs3__default.default.existsSync(p1)) {
1121
+ const p1 = path2__default.default.join(domainDir, `${cand}.ts`);
1122
+ if (fs4__default.default.existsSync(p1)) {
1014
1123
  return { filePath: p1, ifaceName: cand, feature: feat };
1015
1124
  }
1016
1125
  }
1017
1126
  }
1018
1127
  for (const cand of candidateNames) {
1019
- const p1 = path3__default.default.join(projectRoot, "domain", `${cand}.ts`);
1020
- if (fs3__default.default.existsSync(p1)) return { filePath: p1, ifaceName: cand };
1021
- const p22 = path3__default.default.join(projectRoot, "src", "domain", `${cand}.ts`);
1022
- if (fs3__default.default.existsSync(p22)) return { filePath: p22, ifaceName: cand };
1023
- const p32 = path3__default.default.join(projectRoot, "interfaces", `${cand}.ts`);
1024
- if (fs3__default.default.existsSync(p32)) return { filePath: p32, ifaceName: cand };
1128
+ const p1 = path2__default.default.join(projectRoot, "domain", `${cand}.ts`);
1129
+ if (fs4__default.default.existsSync(p1)) return { filePath: p1, ifaceName: cand };
1130
+ const p22 = path2__default.default.join(projectRoot, "src", "domain", `${cand}.ts`);
1131
+ if (fs4__default.default.existsSync(p22)) return { filePath: p22, ifaceName: cand };
1132
+ const p32 = path2__default.default.join(projectRoot, "interfaces", `${cand}.ts`);
1133
+ if (fs4__default.default.existsSync(p32)) return { filePath: p32, ifaceName: cand };
1025
1134
  }
1026
1135
  return null;
1027
1136
  }
@@ -1044,7 +1153,7 @@ function loadInterfaceHierarchy(projectRoot, initialIfaceName, featureName, cust
1044
1153
  while (currentFile && !visited.has(currentFile)) {
1045
1154
  visited.add(currentFile);
1046
1155
  try {
1047
- const content = fs3__default.default.readFileSync(currentFile, "utf-8");
1156
+ const content = fs4__default.default.readFileSync(currentFile, "utf-8");
1048
1157
  const parsed = parseInterfaceMethodsAndImports(content, currentName);
1049
1158
  for (const m of parsed.methods) {
1050
1159
  if (!allMethods.some((existing) => existing.name === m.name)) {
@@ -1120,7 +1229,7 @@ function generateStubCode(stubClassName, ifaceName, entityName, featureName, met
1120
1229
  const entitiesVar = `${entityVar}s`;
1121
1230
  const idKey = `${entityVar}Id`;
1122
1231
  const impPath = ifaceImportPath || `../domain/${ifaceName}.js`;
1123
- const entityImportPath = ifaceImportPath ? `${path3__default.default.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
1232
+ const entityImportPath = ifaceImportPath ? `${path2__default.default.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
1124
1233
  const importsByFrom = /* @__PURE__ */ new Map();
1125
1234
  for (const imp of domainImports) {
1126
1235
  if (imp.name === ifaceName || imp.name === baseEntity) continue;
@@ -1169,7 +1278,7 @@ function generateConcreteInfraCode(className, ifaceName, dependencies = [], meth
1169
1278
  if (baseEntity) {
1170
1279
  finalMethods = synthesizeCrudMethods(baseEntity);
1171
1280
  if (!finalDomainImports.some((imp) => imp.name === baseEntity)) {
1172
- const importPath = ifaceImportPath ? `${path3__default.default.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
1281
+ const importPath = ifaceImportPath ? `${path2__default.default.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
1173
1282
  finalDomainImports.push({
1174
1283
  name: baseEntity,
1175
1284
  from: importPath,
@@ -1214,8 +1323,8 @@ function generateInfrastructure(options) {
1214
1323
  const projectRoot = options.projectRoot || findProjectRoot();
1215
1324
  const className = toPascalCase(options.name);
1216
1325
  const isFeatureScope = options.scope !== "global" && Boolean(options.feature);
1217
- const targetDir = options.targetDir || (isFeatureScope ? path3__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "infrastructure") : path3__default.default.join(projectRoot, "infrastructure"));
1218
- const filePath = path3__default.default.join(targetDir, `${className}.ts`);
1326
+ const targetDir = options.targetDir || (isFeatureScope ? path2__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "infrastructure") : path2__default.default.join(projectRoot, "infrastructure"));
1327
+ const filePath = path2__default.default.join(targetDir, `${className}.ts`);
1219
1328
  let code;
1220
1329
  if (options.implementsInterface) {
1221
1330
  const rawIfaceName = options.implementsInterface;
@@ -1229,7 +1338,7 @@ function generateInfrastructure(options) {
1229
1338
  const ifaceFilePath = hierarchy.ifaceFilePath;
1230
1339
  let ifaceImportPath;
1231
1340
  if (ifaceFilePath) {
1232
- let rel = path3__default.default.relative(targetDir, ifaceFilePath).replace(/\\/g, "/").replace(/\.ts$/, ".js");
1341
+ let rel = path2__default.default.relative(targetDir, ifaceFilePath).replace(/\\/g, "/").replace(/\.ts$/, ".js");
1233
1342
  if (!rel.startsWith(".")) rel = "./" + rel;
1234
1343
  ifaceImportPath = rel;
1235
1344
  } else if (isFeatureScope) {
@@ -1242,8 +1351,8 @@ function generateInfrastructure(options) {
1242
1351
  if (new RegExp(`\\b${item.name}\\b`).test(allSignatures)) {
1243
1352
  let fromPath;
1244
1353
  if (imp.specifier.startsWith(".") && ifaceFilePath) {
1245
- const absTarget = path3__default.default.resolve(path3__default.default.dirname(ifaceFilePath), imp.specifier);
1246
- let rel = path3__default.default.relative(targetDir, absTarget).replace(/\\/g, "/");
1354
+ const absTarget = path2__default.default.resolve(path2__default.default.dirname(ifaceFilePath), imp.specifier);
1355
+ let rel = path2__default.default.relative(targetDir, absTarget).replace(/\\/g, "/");
1247
1356
  if (!rel.startsWith(".")) rel = "./" + rel;
1248
1357
  fromPath = rel;
1249
1358
  } else {
@@ -1316,22 +1425,22 @@ function generateUseCaseCode(useCaseName, dependencies = [], inputProps = [], ou
1316
1425
  from: /^I[A-Z]/.test(dep) ? `../domain/${dep}.js` : `./${dep}.js`
1317
1426
  }));
1318
1427
  const domainTypesToImport = new Set(domainImports);
1319
- for (const p15 of [...inputProps, ...outputProps]) {
1320
- const rawType = p15.type.trim();
1428
+ for (const p16 of [...inputProps, ...outputProps]) {
1429
+ const rawType = p16.type.trim();
1321
1430
  const baseType = rawType.replace(/\[\]$/, "").trim();
1322
1431
  if (!primitiveTypes.has(baseType) && /^[A-Z][A-Za-z0-9_]*$/.test(baseType) && !dependencies.includes(baseType)) {
1323
1432
  domainTypesToImport.add(baseType);
1324
1433
  }
1325
1434
  }
1326
- const formattedInputProps = inputProps.map((p15) => ({
1327
- name: p15.name,
1328
- optional: p15.optional,
1329
- type: p15.optional && !p15.type.includes("undefined") ? `${p15.type} | undefined` : p15.type
1435
+ const formattedInputProps = inputProps.map((p16) => ({
1436
+ name: p16.name,
1437
+ optional: p16.optional,
1438
+ type: p16.optional && !p16.type.includes("undefined") ? `${p16.type} | undefined` : p16.type
1330
1439
  }));
1331
- const formattedOutputProps = outputProps.map((p15) => ({
1332
- name: p15.name,
1333
- optional: p15.optional,
1334
- type: p15.optional && !p15.type.includes("undefined") ? `${p15.type} | undefined` : p15.type
1440
+ const formattedOutputProps = outputProps.map((p16) => ({
1441
+ name: p16.name,
1442
+ optional: p16.optional,
1443
+ type: p16.optional && !p16.type.includes("undefined") ? `${p16.type} | undefined` : p16.type
1335
1444
  }));
1336
1445
  const formattedDeps = dependencies.map((dep) => ({
1337
1446
  key: toCamelCase(dep),
@@ -1430,8 +1539,8 @@ function generateUseCase(options) {
1430
1539
  const files = [];
1431
1540
  const projectRoot = options.projectRoot || findProjectRoot();
1432
1541
  const ucName = toPascalCase(options.name);
1433
- const targetDir = options.targetDir || path3__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "useCases");
1434
- const filePath = path3__default.default.join(targetDir, `${ucName}.ts`);
1542
+ const targetDir = options.targetDir || path2__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "useCases");
1543
+ const filePath = path2__default.default.join(targetDir, `${ucName}.ts`);
1435
1544
  const code = generateUseCaseCode(
1436
1545
  ucName,
1437
1546
  options.dependencies || [],
@@ -1447,7 +1556,7 @@ function generateUseCase(options) {
1447
1556
  })
1448
1557
  );
1449
1558
  if (options.includeTest) {
1450
- const testPath = path3__default.default.join(targetDir, `${ucName}.test.ts`);
1559
+ const testPath = path2__default.default.join(targetDir, `${ucName}.test.ts`);
1451
1560
  const testCode = generateUseCaseTestCode(ucName, options.dependencies || []);
1452
1561
  files.push(
1453
1562
  writeFileSafely(testPath, testCode, {
@@ -1515,10 +1624,10 @@ function generateHttpHandler(options) {
1515
1624
  const files = [];
1516
1625
  const projectRoot = options.projectRoot || findProjectRoot();
1517
1626
  const className = toPascalCase(options.name);
1518
- const pxDir = path3__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress");
1519
- const handlersDir = path3__default.default.join(pxDir, "handlers");
1520
- const pxIndexPath = path3__default.default.join(pxDir, "index.ts");
1521
- if (!fs3__default.default.existsSync(pxIndexPath)) {
1627
+ const pxDir = path2__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress");
1628
+ const handlersDir = path2__default.default.join(pxDir, "handlers");
1629
+ const pxIndexPath = path2__default.default.join(pxDir, "index.ts");
1630
+ if (!fs4__default.default.existsSync(pxIndexPath)) {
1522
1631
  const featurePath = options.routePrefix || `/${options.feature.toLowerCase()}`;
1523
1632
  const pxIndexContent = renderTemplate(expressIndexTemplate, {
1524
1633
  routePath: featurePath
@@ -1531,7 +1640,7 @@ function generateHttpHandler(options) {
1531
1640
  })
1532
1641
  );
1533
1642
  }
1534
- const handlerFilePath = path3__default.default.join(handlersDir, `${className}.ts`);
1643
+ const handlerFilePath = path2__default.default.join(handlersDir, `${className}.ts`);
1535
1644
  const code = generateHttpHandlerCode(
1536
1645
  className,
1537
1646
  options.method || "get",
@@ -1552,11 +1661,11 @@ function generateHttpHandler(options) {
1552
1661
  return files;
1553
1662
  }
1554
1663
  function updateOrGenerateDiProvider(featureDir, featureName, bindings = [], options = {}) {
1555
- const providerPath = path3__default.default.join(featureDir, "diProvider.ts");
1664
+ const providerPath = path2__default.default.join(featureDir, "diProvider.ts");
1556
1665
  const pascalFeature = toPascalCase(featureName);
1557
1666
  const providerModuleName = `${pascalFeature}Provider`;
1558
- if (fs3__default.default.existsSync(providerPath) && !options.overwrite) {
1559
- let content = fs3__default.default.readFileSync(providerPath, "utf-8");
1667
+ if (fs4__default.default.existsSync(providerPath) && !options.overwrite) {
1668
+ let content = fs4__default.default.readFileSync(providerPath, "utf-8");
1560
1669
  content = content.replace(/\n*export\s+const\s+ScaffoldProvider\s*=[^;]+;?/g, "");
1561
1670
  for (const b of bindings) {
1562
1671
  const ifaceImp = b.ifaceImportPath || `./domain/${b.ifaceName}.js`;
@@ -1615,7 +1724,7 @@ function generateFeature(options) {
1615
1724
  const projectRoot = options.projectRoot || findProjectRoot();
1616
1725
  const rawName = options.name.trim();
1617
1726
  const featureName = toCamelCase(rawName);
1618
- const targetDir = options.targetDir || path3__default.default.join(resolveFeaturesDir(projectRoot), featureName);
1727
+ const targetDir = options.targetDir || path2__default.default.join(resolveFeaturesDir(projectRoot), featureName);
1619
1728
  const routePath = options.routePath || `/${featureName}`;
1620
1729
  const rawEntities = [];
1621
1730
  if (options.entityNames && options.entityNames.length > 0) {
@@ -1636,7 +1745,7 @@ function generateFeature(options) {
1636
1745
  const domainFile = generateDomain({
1637
1746
  name: ent,
1638
1747
  feature: featureName,
1639
- targetDir: path3__default.default.join(targetDir, "domain"),
1748
+ targetDir: path2__default.default.join(targetDir, "domain"),
1640
1749
  projectRoot,
1641
1750
  dryRun: options.dryRun,
1642
1751
  overwrite: options.overwrite,
@@ -1656,7 +1765,7 @@ function generateFeature(options) {
1656
1765
  const ifaceFile = generateInterface({
1657
1766
  name: ifaceName,
1658
1767
  feature: featureName,
1659
- targetDir: path3__default.default.join(targetDir, "domain"),
1768
+ targetDir: path2__default.default.join(targetDir, "domain"),
1660
1769
  entityName: ent,
1661
1770
  projectRoot,
1662
1771
  dryRun: options.dryRun,
@@ -1673,7 +1782,7 @@ function generateFeature(options) {
1673
1782
  const stubFile = generateInfrastructure({
1674
1783
  name: stubName,
1675
1784
  feature: featureName,
1676
- targetDir: path3__default.default.join(targetDir, "infrastructure"),
1785
+ targetDir: path2__default.default.join(targetDir, "infrastructure"),
1677
1786
  kind: "stub",
1678
1787
  implementsInterface: ifaceName,
1679
1788
  entityName: ent,
@@ -1715,7 +1824,7 @@ function generateFeature(options) {
1715
1824
  const ucFiles = generateUseCase({
1716
1825
  name: ucName,
1717
1826
  feature: featureName,
1718
- targetDir: path3__default.default.join(targetDir, "useCases"),
1827
+ targetDir: path2__default.default.join(targetDir, "useCases"),
1719
1828
  dependencies: options.includeInterface !== false ? [ifaceName] : [],
1720
1829
  inputProperties: [{ name: idParam, type: "string" }],
1721
1830
  outputProperties: [{ name: entVar, type: primaryEntity }],
@@ -1760,7 +1869,7 @@ function generateFeature(options) {
1760
1869
  const ucFiles = generateUseCase({
1761
1870
  name: ucName,
1762
1871
  feature: featureName,
1763
- targetDir: path3__default.default.join(targetDir, "useCases"),
1872
+ targetDir: path2__default.default.join(targetDir, "useCases"),
1764
1873
  dependencies: deps,
1765
1874
  inputProperties: [{ name: entVar, type: ent }],
1766
1875
  outputProperties: [{ name: entVar, type: ent }],
@@ -1793,7 +1902,7 @@ function generateFeature(options) {
1793
1902
  const ucFiles = generateUseCase({
1794
1903
  name: ucName,
1795
1904
  feature: featureName,
1796
- targetDir: path3__default.default.join(targetDir, "useCases"),
1905
+ targetDir: path2__default.default.join(targetDir, "useCases"),
1797
1906
  dependencies: deps,
1798
1907
  inputProperties: [{ name: idParam, type: "string" }],
1799
1908
  outputProperties: [{ name: entVar, type: ent }],
@@ -1826,7 +1935,7 @@ function generateFeature(options) {
1826
1935
  const ucFiles = generateUseCase({
1827
1936
  name: ucName,
1828
1937
  feature: featureName,
1829
- targetDir: path3__default.default.join(targetDir, "useCases"),
1938
+ targetDir: path2__default.default.join(targetDir, "useCases"),
1830
1939
  dependencies: deps,
1831
1940
  inputProperties: [
1832
1941
  { name: "page", type: "number", optional: true },
@@ -1870,7 +1979,7 @@ function generateFeature(options) {
1870
1979
  const ucFiles = generateUseCase({
1871
1980
  name: ucName,
1872
1981
  feature: featureName,
1873
- targetDir: path3__default.default.join(targetDir, "useCases"),
1982
+ targetDir: path2__default.default.join(targetDir, "useCases"),
1874
1983
  dependencies: deps,
1875
1984
  inputProperties: [{ name: entVar, type: ent }],
1876
1985
  outputProperties: [{ name: entVar, type: ent }],
@@ -1903,7 +2012,7 @@ function generateFeature(options) {
1903
2012
  const ucFiles = generateUseCase({
1904
2013
  name: ucName,
1905
2014
  feature: featureName,
1906
- targetDir: path3__default.default.join(targetDir, "useCases"),
2015
+ targetDir: path2__default.default.join(targetDir, "useCases"),
1907
2016
  dependencies: deps,
1908
2017
  inputProperties: [{ name: idParam, type: "string" }],
1909
2018
  outputProperties: [{ name: "success", type: "boolean" }],
@@ -1940,7 +2049,7 @@ function generateFeature(options) {
1940
2049
  const ucFiles = generateUseCase({
1941
2050
  name: ucName,
1942
2051
  feature: featureName,
1943
- targetDir: path3__default.default.join(targetDir, "useCases"),
2052
+ targetDir: path2__default.default.join(targetDir, "useCases"),
1944
2053
  dependencies: customUc.dependencies || [],
1945
2054
  inputProperties: customUc.inputProperties || [],
1946
2055
  outputProperties: customUc.outputProperties || [],
@@ -1972,8 +2081,8 @@ function generateFeature(options) {
1972
2081
  }
1973
2082
  }
1974
2083
  if (options.includeExpress !== false) {
1975
- const pxDir = path3__default.default.join(targetDir, "pxExpress");
1976
- const pxIndexPath = path3__default.default.join(pxDir, "index.ts");
2084
+ const pxDir = path2__default.default.join(targetDir, "pxExpress");
2085
+ const pxIndexPath = path2__default.default.join(pxDir, "index.ts");
1977
2086
  if (!files.some((f) => f.absolutePath === pxIndexPath)) {
1978
2087
  const pxIndexContent = renderTemplate(expressIndexTemplate, { routePath });
1979
2088
  files.push(
@@ -1999,6 +2108,7 @@ function generateFeature(options) {
1999
2108
  // src/commands/feature.ts
2000
2109
  async function runFeatureCommand(featureNameArg, flags = {}) {
2001
2110
  const projectRoot = findProjectRoot();
2111
+ const isRegistry = getEffectiveMode(projectRoot, flags) === "registry";
2002
2112
  let name = featureNameArg?.trim();
2003
2113
  if (!name && flags.yes) {
2004
2114
  name = "feature";
@@ -2016,9 +2126,10 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2016
2126
  ).trim();
2017
2127
  }
2018
2128
  const normalizedName = toCamelCase(name);
2019
- const defaultDir = path3__default.default.relative(projectRoot, path3__default.default.join(resolveFeaturesDir(projectRoot), normalizedName));
2129
+ const baseFeaturesDir = resolveFeaturesDir(projectRoot, flags);
2130
+ const defaultDir = path2__default.default.relative(projectRoot, path2__default.default.join(baseFeaturesDir, normalizedName));
2020
2131
  let targetDirRel = defaultDir;
2021
- if (!flags.yes && !flags.target) {
2132
+ if (!flags.yes && !flags.target && !flags.dest) {
2022
2133
  targetDirRel = handleCancel(
2023
2134
  await p4__namespace.text({
2024
2135
  message: "Target directory for the feature:",
@@ -2027,7 +2138,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2027
2138
  })
2028
2139
  ).trim();
2029
2140
  }
2030
- const targetDir = flags.target ? path3__default.default.resolve(projectRoot, flags.target) : path3__default.default.resolve(projectRoot, targetDirRel);
2141
+ 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);
2031
2142
  let includeExpress = flags.express !== void 0 ? Boolean(flags.express) : true;
2032
2143
  let routePath = `/${normalizedName}`;
2033
2144
  let includeDomain = true;
@@ -2248,7 +2359,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2248
2359
  }
2249
2360
  }
2250
2361
  const s = p4__namespace.spinner();
2251
- s.start(`Generating feature '${pc13__default.default.cyan(normalizedName)}'...`);
2362
+ s.start(`Generating feature '${pc14__default.default.cyan(normalizedName)}'...`);
2252
2363
  const options = {
2253
2364
  name: normalizedName,
2254
2365
  projectRoot,
@@ -2268,9 +2379,207 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2268
2379
  includeDiProvider
2269
2380
  };
2270
2381
  const files = generateFeature(options);
2382
+ if (isRegistry) {
2383
+ const registryPath = path2__default.default.join(targetDir, "registry.json");
2384
+ if (!fs4__default.default.existsSync(registryPath)) {
2385
+ const manifest = {
2386
+ name: toKebabCase(normalizedName),
2387
+ type: "feature",
2388
+ description: flags.description || `${normalizedName} feature module`,
2389
+ version: "1.0.0",
2390
+ dependencies: {
2391
+ npm: includeExpress ? ["@solid-stack/agnos-express", "express"] : [],
2392
+ shared: [],
2393
+ features: []
2394
+ },
2395
+ files: []
2396
+ };
2397
+ const content = JSON.stringify(manifest, null, 2) + "\n";
2398
+ const genFile = writeFileSafely(registryPath, content, {
2399
+ projectRoot,
2400
+ overwrite: options.overwrite,
2401
+ dryRun: options.dryRun
2402
+ });
2403
+ files.push(genFile);
2404
+ }
2405
+ }
2271
2406
  s.stop("Feature scaffolded successfully!");
2272
2407
  printGeneratedFiles(files, options.dryRun);
2273
- p4__namespace.outro(pc13__default.default.green(`Feature '${normalizedName}' ready to use!`));
2408
+ p4__namespace.outro(
2409
+ pc14__default.default.green(
2410
+ `\u2713 Feature '${normalizedName}' initialized in ${path2__default.default.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
2411
+ )
2412
+ );
2413
+ }
2414
+ function generateShared(options) {
2415
+ const files = [];
2416
+ const projectRoot = options.projectRoot || findProjectRoot();
2417
+ const rawName = options.name.trim();
2418
+ const pascalName = toPascalCase(rawName);
2419
+ const camelName = toCamelCase(rawName);
2420
+ const kebabName = toKebabCase(rawName);
2421
+ const targetDir = options.targetDir || path2__default.default.join(resolveSharedDir(projectRoot), kebabName);
2422
+ const fileDefs = [];
2423
+ const facadeContent = `import { MakeInjectable, type DepsType } from "@solid-stack/di";
2424
+ import { I${pascalName}Engine } from "./ports/I${pascalName}Engine.js";
2425
+
2426
+ @MakeInjectable
2427
+ export class ${pascalName} {
2428
+ public static deps = { ${camelName}Engine: I${pascalName}Engine };
2429
+ constructor(public readonly deps: DepsType<typeof ${pascalName}.deps>) {}
2430
+ }
2431
+ `;
2432
+ fileDefs.push({
2433
+ absolutePath: path2__default.default.join(targetDir, `${pascalName}.ts`),
2434
+ content: facadeContent
2435
+ });
2436
+ const portContent = `export abstract class I${pascalName}Engine {
2437
+ // Define abstract port contracts for ${pascalName} here
2438
+ }
2439
+ `;
2440
+ fileDefs.push({
2441
+ absolutePath: path2__default.default.join(targetDir, "ports", `I${pascalName}Engine.ts`),
2442
+ content: portContent
2443
+ });
2444
+ const concreteContent = `import { MakeInjectable } from "@solid-stack/di";
2445
+ import { I${pascalName}Engine } from "../ports/I${pascalName}Engine.js";
2446
+
2447
+ @MakeInjectable
2448
+ export class Default${pascalName}Engine implements I${pascalName}Engine {
2449
+ // Default concrete implementation for production
2450
+ }
2451
+ `;
2452
+ fileDefs.push({
2453
+ absolutePath: path2__default.default.join(targetDir, "infrastructure", `Default${pascalName}Engine.ts`),
2454
+ content: concreteContent
2455
+ });
2456
+ if (options.includeStub !== false) {
2457
+ const stubContent = `import { MakeInjectable } from "@solid-stack/di";
2458
+ import { I${pascalName}Engine } from "../ports/I${pascalName}Engine.js";
2459
+
2460
+ @MakeInjectable
2461
+ export class Stub${pascalName}Engine implements I${pascalName}Engine {
2462
+ // In-memory or stub implementation for testing
2463
+ }
2464
+ `;
2465
+ fileDefs.push({
2466
+ absolutePath: path2__default.default.join(targetDir, "infrastructure", `Stub${pascalName}Engine.ts`),
2467
+ content: stubContent
2468
+ });
2469
+ }
2470
+ const providerContent = `import type { Container } from "@solid-stack/di";
2471
+ import { I${pascalName}Engine } from "./ports/I${pascalName}Engine.js";
2472
+ import { Default${pascalName}Engine } from "./infrastructure/Default${pascalName}Engine.js";
2473
+
2474
+ export const diProvider = (c: Container): void => {
2475
+ c.provide(I${pascalName}Engine, Default${pascalName}Engine);
2476
+ };
2477
+
2478
+ export default diProvider;
2479
+ `;
2480
+ fileDefs.push({
2481
+ absolutePath: path2__default.default.join(targetDir, "diProvider.ts"),
2482
+ content: providerContent
2483
+ });
2484
+ if (options.isRegistry) {
2485
+ const registryManifest = {
2486
+ name: kebabName,
2487
+ type: "shared",
2488
+ description: options.description || `${kebabName} shared module`,
2489
+ version: "1.0.0",
2490
+ dependencies: {
2491
+ npm: [],
2492
+ shared: [],
2493
+ features: []
2494
+ },
2495
+ files: []
2496
+ };
2497
+ fileDefs.push({
2498
+ absolutePath: path2__default.default.join(targetDir, "registry.json"),
2499
+ content: JSON.stringify(registryManifest, null, 2) + "\n"
2500
+ });
2501
+ }
2502
+ for (const def of fileDefs) {
2503
+ const genFile = writeFileSafely(def.absolutePath, def.content, {
2504
+ projectRoot,
2505
+ overwrite: options.overwrite,
2506
+ dryRun: options.dryRun
2507
+ });
2508
+ files.push(genFile);
2509
+ }
2510
+ return files;
2511
+ }
2512
+
2513
+ // src/commands/shared.ts
2514
+ async function runSharedCommand(sharedNameArg, flags = {}) {
2515
+ const projectRoot = findProjectRoot();
2516
+ const isRegistry = getEffectiveMode(projectRoot, flags) === "registry";
2517
+ let name = sharedNameArg?.trim();
2518
+ if (!name && flags.yes) {
2519
+ name = "shared";
2520
+ }
2521
+ if (!name) {
2522
+ name = handleCancel(
2523
+ await p4__namespace.text({
2524
+ message: "What is the shared module name?",
2525
+ placeholder: "e.g. hasher, time, uuid, cache, logger",
2526
+ validate: (val) => {
2527
+ if (!val || !val.trim()) return "Shared module name cannot be empty.";
2528
+ return void 0;
2529
+ }
2530
+ })
2531
+ ).trim();
2532
+ }
2533
+ const normalizedName = toKebabCase(name);
2534
+ const baseSharedDir = resolveSharedDir(projectRoot, flags);
2535
+ const defaultDir = path2__default.default.relative(projectRoot, path2__default.default.join(baseSharedDir, normalizedName));
2536
+ let targetDirRel = defaultDir;
2537
+ if (!flags.yes && !flags.target && !flags.dest) {
2538
+ targetDirRel = handleCancel(
2539
+ await p4__namespace.text({
2540
+ message: "Target directory for the shared module:",
2541
+ initialValue: defaultDir,
2542
+ defaultValue: defaultDir
2543
+ })
2544
+ ).trim();
2545
+ }
2546
+ 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);
2547
+ let description = flags.description || "";
2548
+ let includeStub = true;
2549
+ if (!flags.yes) {
2550
+ description = handleCancel(
2551
+ await p4__namespace.text({
2552
+ message: "Module description (optional):",
2553
+ placeholder: "e.g. In-memory caching and Redis port abstraction",
2554
+ initialValue: ""
2555
+ })
2556
+ ).trim();
2557
+ includeStub = handleCancel(
2558
+ await p4__namespace.confirm({
2559
+ message: "Include stub implementation in infrastructure/?",
2560
+ initialValue: true
2561
+ })
2562
+ );
2563
+ }
2564
+ const s = p4__namespace.spinner();
2565
+ s.start(`Generating shared module '${pc14__default.default.cyan(normalizedName)}'...`);
2566
+ const files = generateShared({
2567
+ name: normalizedName,
2568
+ description,
2569
+ projectRoot,
2570
+ targetDir,
2571
+ dryRun: Boolean(flags.dryRun),
2572
+ overwrite: Boolean(flags.overwrite),
2573
+ includeStub,
2574
+ isRegistry
2575
+ });
2576
+ s.stop("Shared module generated successfully!");
2577
+ printGeneratedFiles(files, Boolean(flags.dryRun));
2578
+ p4__namespace.outro(
2579
+ pc14__default.default.green(
2580
+ `\u2713 Shared module '${normalizedName}' initialized in ${path2__default.default.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
2581
+ )
2582
+ );
2274
2583
  }
2275
2584
  async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2276
2585
  const projectRoot = findProjectRoot();
@@ -2395,7 +2704,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2395
2704
  }
2396
2705
  }
2397
2706
  const s = p4__namespace.spinner();
2398
- s.start(`Generating use case '${pc13__default.default.cyan(ucName)}'...`);
2707
+ s.start(`Generating use case '${pc14__default.default.cyan(ucName)}'...`);
2399
2708
  const options = {
2400
2709
  name: ucName,
2401
2710
  feature,
@@ -2427,7 +2736,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2427
2736
  }
2428
2737
  s.stop("Use case generated successfully!");
2429
2738
  printGeneratedFiles(files, options.dryRun);
2430
- p4__namespace.outro(pc13__default.default.green(`Use case '${ucName}' created in feature '${feature}'!`));
2739
+ p4__namespace.outro(pc14__default.default.green(`Use case '${ucName}' created in feature '${feature}'!`));
2431
2740
  }
2432
2741
  async function runDomainCommand(domainNameArg, flags = {}) {
2433
2742
  const projectRoot = findProjectRoot();
@@ -2498,7 +2807,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2498
2807
  });
2499
2808
  }
2500
2809
  const s = p4__namespace.spinner();
2501
- s.start(`Generating domain model '${pc13__default.default.cyan(entityName)}'...`);
2810
+ s.start(`Generating domain model '${pc14__default.default.cyan(entityName)}'...`);
2502
2811
  const options = {
2503
2812
  name: entityName,
2504
2813
  feature,
@@ -2511,7 +2820,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2511
2820
  const file = generateDomain(options);
2512
2821
  s.stop("Domain model generated successfully!");
2513
2822
  printGeneratedFiles([file], options.dryRun);
2514
- p4__namespace.outro(pc13__default.default.green(`Domain model '${entityName}' created in feature '${feature}'!`));
2823
+ p4__namespace.outro(pc14__default.default.green(`Domain model '${entityName}' created in feature '${feature}'!`));
2515
2824
  }
2516
2825
  async function runInfrastructureCommand(classNameArg, flags = {}) {
2517
2826
  const projectRoot = findProjectRoot();
@@ -2680,7 +2989,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2680
2989
  registerInProvider = !flags.noProvider;
2681
2990
  }
2682
2991
  const s = p4__namespace.spinner();
2683
- s.start(`Generating infrastructure class '${pc13__default.default.cyan(className)}'...`);
2992
+ s.start(`Generating infrastructure class '${pc14__default.default.cyan(className)}'...`);
2684
2993
  const files = [];
2685
2994
  const options = {
2686
2995
  name: className,
@@ -2695,7 +3004,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2695
3004
  const infraFile = generateInfrastructure(options);
2696
3005
  files.push(infraFile);
2697
3006
  if (registerInProvider && scope === "feature" && feature && implementsInterface) {
2698
- const featureDir = path3__default.default.join(resolveFeaturesDir(projectRoot), feature);
3007
+ const featureDir = path2__default.default.join(resolveFeaturesDir(projectRoot), feature);
2699
3008
  const providerFile = updateOrGenerateDiProvider(
2700
3009
  featureDir,
2701
3010
  feature,
@@ -2715,7 +3024,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2715
3024
  }
2716
3025
  s.stop("Infrastructure class generated successfully!");
2717
3026
  printGeneratedFiles(files, options.dryRun);
2718
- p4__namespace.outro(pc13__default.default.green(`Infrastructure class '${className}' created!`));
3027
+ p4__namespace.outro(pc14__default.default.green(`Infrastructure class '${className}' created!`));
2719
3028
  }
2720
3029
  async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2721
3030
  const projectRoot = findProjectRoot();
@@ -2828,7 +3137,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2828
3137
  }
2829
3138
  }
2830
3139
  const s = p4__namespace.spinner();
2831
- s.start(`Generating interface '${pc13__default.default.cyan(ifaceName)}'...`);
3140
+ s.start(`Generating interface '${pc14__default.default.cyan(ifaceName)}'...`);
2832
3141
  const files = [];
2833
3142
  const options = {
2834
3143
  name: ifaceName,
@@ -2857,7 +3166,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2857
3166
  files.push(stubFile);
2858
3167
  }
2859
3168
  if (registerInProvider && generateStub) {
2860
- const featureDir = path3__default.default.join(resolveFeaturesDir(projectRoot), feature);
3169
+ const featureDir = path2__default.default.join(resolveFeaturesDir(projectRoot), feature);
2861
3170
  const providerFile = updateOrGenerateDiProvider(
2862
3171
  featureDir,
2863
3172
  feature,
@@ -2877,7 +3186,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2877
3186
  }
2878
3187
  s.stop("Interface generated successfully!");
2879
3188
  printGeneratedFiles(files, options.dryRun);
2880
- p4__namespace.outro(pc13__default.default.green(`Interface '${ifaceName}' created in feature '${feature}'!`));
3189
+ p4__namespace.outro(pc14__default.default.green(`Interface '${ifaceName}' created in feature '${feature}'!`));
2881
3190
  }
2882
3191
  async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2883
3192
  const projectRoot = findProjectRoot();
@@ -2994,7 +3303,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2994
3303
  }
2995
3304
  }
2996
3305
  const s = p4__namespace.spinner();
2997
- s.start(`Generating HTTP handler '${pc13__default.default.cyan(className)}'...`);
3306
+ s.start(`Generating HTTP handler '${pc14__default.default.cyan(className)}'...`);
2998
3307
  const options = {
2999
3308
  name: className,
3000
3309
  feature,
@@ -3012,7 +3321,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
3012
3321
  const files = generateHttpHandler(options);
3013
3322
  s.stop("HTTP handler generated successfully!");
3014
3323
  printGeneratedFiles(files, options.dryRun);
3015
- p4__namespace.outro(pc13__default.default.green(`HTTP handler '${className}' created in feature '${feature}'!`));
3324
+ p4__namespace.outro(pc14__default.default.green(`HTTP handler '${className}' created in feature '${feature}'!`));
3016
3325
  }
3017
3326
  function generateInjectableCode(className, dependencies = [], methods = [], implementsInterface) {
3018
3327
  const formattedDeps = dependencies.map((dep) => ({
@@ -3022,7 +3331,7 @@ function generateInjectableCode(className, dependencies = [], methods = [], impl
3022
3331
  const formattedMethods = methods.map((m) => {
3023
3332
  const isAsync = m.async !== false;
3024
3333
  const retType = m.returnType ? isAsync ? `Promise<${m.returnType}>` : m.returnType : isAsync ? "Promise<void>" : "void";
3025
- const params = m.params && m.params.length > 0 ? m.params.map((p15) => `${p15.name}: ${p15.type}`).join(", ") : "";
3334
+ const params = m.params && m.params.length > 0 ? m.params.map((p16) => `${p16.name}: ${p16.type}`).join(", ") : "";
3026
3335
  return {
3027
3336
  name: m.name,
3028
3337
  isAsync,
@@ -3045,21 +3354,21 @@ function generateInjectable(options) {
3045
3354
  const className = toPascalCase(options.name);
3046
3355
  let targetDir = "";
3047
3356
  if (options.customPath) {
3048
- targetDir = path3__default.default.isAbsolute(options.customPath) ? options.customPath : path3__default.default.join(projectRoot, options.customPath);
3357
+ targetDir = path2__default.default.isAbsolute(options.customPath) ? options.customPath : path2__default.default.join(projectRoot, options.customPath);
3049
3358
  } else if (options.location === "feature-service" && options.feature) {
3050
- targetDir = path3__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "services");
3359
+ targetDir = path2__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "services");
3051
3360
  } else if (options.location === "feature-transformer" && options.feature) {
3052
- targetDir = path3__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress", "transformers");
3361
+ targetDir = path2__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress", "transformers");
3053
3362
  } else if (options.location === "module") {
3054
- targetDir = path3__default.default.join(projectRoot, "modules", className);
3363
+ targetDir = path2__default.default.join(projectRoot, "modules", className);
3055
3364
  } else {
3056
- targetDir = options.feature ? path3__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "services") : path3__default.default.join(projectRoot, "shared", className.toLowerCase());
3365
+ targetDir = options.feature ? path2__default.default.join(resolveFeaturesDir(projectRoot), options.feature, "services") : path2__default.default.join(projectRoot, "shared", className.toLowerCase());
3057
3366
  }
3058
- const filePath = path3__default.default.join(targetDir, `${className}.ts`);
3367
+ const filePath = path2__default.default.join(targetDir, `${className}.ts`);
3059
3368
  let ifaceName;
3060
3369
  if (options.generateInterface) {
3061
3370
  ifaceName = `I${className}`;
3062
- const ifacePath = path3__default.default.join(targetDir, `${ifaceName}.ts`);
3371
+ const ifacePath = path2__default.default.join(targetDir, `${ifaceName}.ts`);
3063
3372
  const ifaceContent = renderTemplate(injectableInterfaceTemplate, { ifaceName });
3064
3373
  files.push(
3065
3374
  writeFileSafely(ifacePath, ifaceContent, {
@@ -3168,7 +3477,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3168
3477
  );
3169
3478
  }
3170
3479
  const s = p4__namespace.spinner();
3171
- s.start(`Generating injectable '${pc13__default.default.cyan(className)}'...`);
3480
+ s.start(`Generating injectable '${pc14__default.default.cyan(className)}'...`);
3172
3481
  const options = {
3173
3482
  name: className,
3174
3483
  feature,
@@ -3182,7 +3491,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3182
3491
  const files = generateInjectable(options);
3183
3492
  s.stop("Injectable class generated successfully!");
3184
3493
  printGeneratedFiles(files, options.dryRun);
3185
- p4__namespace.outro(pc13__default.default.green(`Injectable '${className}' created successfully!`));
3494
+ p4__namespace.outro(pc14__default.default.green(`Injectable '${className}' created successfully!`));
3186
3495
  }
3187
3496
  async function runInteractiveMenu(flags = {}) {
3188
3497
  printBanner();
@@ -3195,6 +3504,11 @@ async function runInteractiveMenu(flags = {}) {
3195
3504
  label: "Feature",
3196
3505
  hint: "Complete vertical slice (domain, infra, usecase, pxExpress, diProvider)"
3197
3506
  },
3507
+ {
3508
+ value: "shared",
3509
+ label: "Shared Module",
3510
+ hint: "Foundational utility or port with ports, infrastructure & diProvider"
3511
+ },
3198
3512
  {
3199
3513
  value: "usecase",
3200
3514
  label: "Use Case",
@@ -3232,6 +3546,9 @@ async function runInteractiveMenu(flags = {}) {
3232
3546
  case "feature":
3233
3547
  await runFeatureCommand(void 0, flags);
3234
3548
  break;
3549
+ case "shared":
3550
+ await runSharedCommand(void 0, flags);
3551
+ break;
3235
3552
  case "usecase":
3236
3553
  await runUseCaseCommand(void 0, flags);
3237
3554
  break;
@@ -3264,6 +3581,9 @@ async function handleCreate(type, name, flags = {}) {
3264
3581
  case "feature":
3265
3582
  await runFeatureCommand(name, flags);
3266
3583
  break;
3584
+ case "shared":
3585
+ await runSharedCommand(name, flags);
3586
+ break;
3267
3587
  case "usecase":
3268
3588
  await runUseCaseCommand(name, flags);
3269
3589
  break;
@@ -3285,20 +3605,171 @@ async function handleCreate(type, name, flags = {}) {
3285
3605
  break;
3286
3606
  default:
3287
3607
  p4__namespace.log.error(
3288
- pc13__default.default.red(
3289
- `Unknown component type: "${type}". Allowed types: feature, usecase, domain, infrastructure, interface, httpHandler, injectable.`
3608
+ pc14__default.default.red(
3609
+ `Unknown component type: "${type}". Allowed types: feature, shared, usecase, domain, infrastructure, interface, httpHandler, injectable.`
3290
3610
  )
3291
3611
  );
3292
3612
  process.exit(1);
3293
3613
  }
3294
3614
  }
3615
+ var importPrompts = {
3616
+ select: p4__namespace.select,
3617
+ text: p4__namespace.text
3618
+ };
3619
+ function rewriteImports(content, aliasMap) {
3620
+ if (aliasMap.size === 0) return content;
3621
+ let rewritten = content;
3622
+ for (const [oldImport, newImport] of aliasMap.entries()) {
3623
+ const rawOld = oldImport.endsWith("/") ? oldImport.slice(0, -1) : oldImport;
3624
+ const rawNew = newImport.endsWith("/") ? newImport.slice(0, -1) : newImport;
3625
+ const escaped = rawOld.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3626
+ const regex = new RegExp(`${escaped}(?=[\\/'"\`]|$)`, "g");
3627
+ rewritten = rewritten.replace(regex, rawNew);
3628
+ }
3629
+ return rewritten;
3630
+ }
3631
+ function getModuleBaseDir(projectRoot, modType) {
3632
+ const folder = modType === "shared" ? "shared" : "features";
3633
+ return path2__default.default.join(projectRoot, "src", folder);
3634
+ }
3635
+ function getExistingModuleDir(projectRoot, modType, modName) {
3636
+ const folder = modType === "shared" ? "shared" : "features";
3637
+ const inSrc = path2__default.default.join(projectRoot, "src", folder, modName);
3638
+ if (fs4__default.default.existsSync(inSrc)) return inSrc;
3639
+ const inRoot = path2__default.default.join(projectRoot, folder, modName);
3640
+ if (fs4__default.default.existsSync(inRoot)) return inRoot;
3641
+ return null;
3642
+ }
3643
+ function checkModuleExists(projectRoot, modType, modName) {
3644
+ return getExistingModuleDir(projectRoot, modType, modName) !== null;
3645
+ }
3646
+ async function resolveDependency(depType, depName, aliasMap, projectRoot, flags, importModuleRecursive, importedModules, registryBase, index) {
3647
+ const folder = depType === "shared" ? "shared" : "features";
3648
+ const isAlreadyImportedThisRun = importedModules.has(`${depType}:${depName}`);
3649
+ if (isAlreadyImportedThisRun) return;
3650
+ const programmaticAlias = flags.alias?.[depName] || flags.alias?.[`${depType}/${depName}`];
3651
+ if (programmaticAlias) {
3652
+ aliasMap.set(`@/${folder}/${depName}/`, `@/${folder}/${programmaticAlias}/`);
3653
+ const baseDir = getModuleBaseDir(projectRoot, depType);
3654
+ const displayDest = path2__default.default.relative(
3655
+ projectRoot,
3656
+ path2__default.default.join(baseDir, programmaticAlias)
3657
+ );
3658
+ p4__namespace.log.step(`Importing '${depName}' into '${displayDest}'...`);
3659
+ await importModuleRecursive(depType, depName, programmaticAlias);
3660
+ return;
3661
+ }
3662
+ const existingDir = getExistingModuleDir(projectRoot, depType, depName);
3663
+ if (!existingDir) {
3664
+ p4__namespace.log.info(` \u21B3 Resolving required ${depType} dependency: ${pc14__default.default.cyan(depName)}`);
3665
+ await importModuleRecursive(depType, depName, depName);
3666
+ return;
3667
+ }
3668
+ const entry = (depType === "shared" ? index?.shared : index?.features)?.find(
3669
+ (item) => item.name === depName
3670
+ );
3671
+ let remoteIntegrity = entry?.integrity;
3672
+ if (!remoteIntegrity) {
3673
+ try {
3674
+ const depManifest = await fetchModuleManifest(registryBase, depType, depName);
3675
+ remoteIntegrity = depManifest.integrity;
3676
+ } catch {
3677
+ }
3678
+ }
3679
+ const localHash = computeDirectoryHash(existingDir);
3680
+ if (remoteIntegrity && localHash === remoteIntegrity) {
3681
+ p4__namespace.log.step(`\u2713 ${depType}/${depName} is up to date (reusing existing)`);
3682
+ importedModules.add(`${depType}:${depName}`);
3683
+ return;
3684
+ }
3685
+ if (flags.overwrite) {
3686
+ p4__namespace.log.step(`Overwriting '${depType}/${depName}' with registry version (--overwrite)...`);
3687
+ const baseDir = getModuleBaseDir(projectRoot, depType);
3688
+ const targetDir = path2__default.default.join(baseDir, depName);
3689
+ if (!flags.dryRun) {
3690
+ if (existingDir !== targetDir) {
3691
+ fs4__default.default.rmSync(existingDir, { recursive: true, force: true });
3692
+ }
3693
+ fs4__default.default.rmSync(targetDir, { recursive: true, force: true });
3694
+ }
3695
+ await importModuleRecursive(depType, depName, depName, { overwrite: true });
3696
+ return;
3697
+ }
3698
+ if (flags.yes) {
3699
+ p4__namespace.log.info(`Using existing '${depType}/${depName}'`);
3700
+ importedModules.add(`${depType}:${depName}`);
3701
+ return;
3702
+ }
3703
+ const choice = handleCancel(
3704
+ await importPrompts.select({
3705
+ message: `Local '${depType}/${depName}' differs from the registry version.`,
3706
+ options: [
3707
+ {
3708
+ value: "keep_existing",
3709
+ label: `Keep existing local version (Recommended)`
3710
+ },
3711
+ {
3712
+ value: "rename",
3713
+ label: "Import fresh copy under a new name (alias)"
3714
+ },
3715
+ {
3716
+ value: "overwrite",
3717
+ label: "Overwrite local version with registry version"
3718
+ }
3719
+ ]
3720
+ })
3721
+ );
3722
+ if (choice === "keep_existing" || choice === "use_existing") {
3723
+ p4__namespace.log.step(`Skipping '${depType}/${depName}' (keeping existing)`);
3724
+ importedModules.add(`${depType}:${depName}`);
3725
+ return;
3726
+ }
3727
+ if (choice === "rename") {
3728
+ const newName = handleCancel(
3729
+ await importPrompts.text({
3730
+ message: "What should we name the new copy?",
3731
+ placeholder: `${depName}-copy`,
3732
+ validate(val) {
3733
+ const v = val?.trim();
3734
+ if (!v) return "Name cannot be empty";
3735
+ if (v === depName) return `Name must be different from '${depName}'`;
3736
+ if (!/^[a-zA-Z0-9_-]+$/.test(v)) {
3737
+ return "Name can only contain alphanumeric characters, dashes, and underscores";
3738
+ }
3739
+ if (checkModuleExists(projectRoot, depType, v)) {
3740
+ return `'${v}' already exists in ${folder}, please choose another name`;
3741
+ }
3742
+ }
3743
+ })
3744
+ ).trim();
3745
+ aliasMap.set(`@/${folder}/${depName}/`, `@/${folder}/${newName}/`);
3746
+ const baseDir = getModuleBaseDir(projectRoot, depType);
3747
+ const displayDest = path2__default.default.relative(projectRoot, path2__default.default.join(baseDir, newName));
3748
+ p4__namespace.log.step(`Importing '${depName}' into '${displayDest}'...`);
3749
+ await importModuleRecursive(depType, depName, newName);
3750
+ return;
3751
+ }
3752
+ if (choice === "overwrite") {
3753
+ p4__namespace.log.step(`Overwriting '${depType}/${depName}' with registry version...`);
3754
+ const baseDir = getModuleBaseDir(projectRoot, depType);
3755
+ const targetDir = path2__default.default.join(baseDir, depName);
3756
+ if (!flags.dryRun) {
3757
+ if (existingDir !== targetDir) {
3758
+ fs4__default.default.rmSync(existingDir, { recursive: true, force: true });
3759
+ }
3760
+ fs4__default.default.rmSync(targetDir, { recursive: true, force: true });
3761
+ }
3762
+ await importModuleRecursive(depType, depName, depName, { overwrite: true });
3763
+ return;
3764
+ }
3765
+ }
3295
3766
  async function handleImport(typeArg, nameArg, flags = {}) {
3296
3767
  const projectRoot = findProjectRoot();
3297
3768
  let type = typeArg?.trim().toLowerCase();
3298
3769
  if (type === "features") type = "feature";
3299
3770
  if (!type) {
3300
3771
  type = handleCancel(
3301
- await p4__namespace.select({
3772
+ await importPrompts.select({
3302
3773
  message: "What kind of module would you like to import?",
3303
3774
  options: [
3304
3775
  {
@@ -3316,19 +3787,19 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3316
3787
  );
3317
3788
  }
3318
3789
  if (type !== "shared" && type !== "feature") {
3319
- p4__namespace.log.error(pc13__default.default.red(`Invalid module type: "${typeArg}". Must be "shared" or "feature".`));
3790
+ p4__namespace.log.error(pc14__default.default.red(`Invalid module type: "${typeArg}". Must be "shared" or "feature".`));
3320
3791
  process.exit(1);
3321
3792
  }
3322
3793
  const registryBase = getRegistryBase(projectRoot, flags.registry);
3323
3794
  const s = p4__namespace.spinner();
3324
- s.start(`Connecting to registry at ${pc13__default.default.dim(registryBase)}...`);
3795
+ s.start(`Connecting to registry at ${pc14__default.default.dim(registryBase)}...`);
3325
3796
  let index;
3326
3797
  try {
3327
3798
  index = await fetchRegistryIndex(registryBase);
3328
3799
  s.stop("Registry index loaded.");
3329
3800
  } catch (err) {
3330
- s.stop(pc13__default.default.red("Failed to load registry index."));
3331
- p4__namespace.log.error(pc13__default.default.red(err.message || String(err)));
3801
+ s.stop(pc14__default.default.red("Failed to load registry index."));
3802
+ p4__namespace.log.error(pc14__default.default.red(err.message || String(err)));
3332
3803
  process.exit(1);
3333
3804
  }
3334
3805
  const availableList = type === "shared" ? index.shared : index.features;
@@ -3340,7 +3811,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3340
3811
  return;
3341
3812
  }
3342
3813
  name = handleCancel(
3343
- await p4__namespace.select({
3814
+ await importPrompts.select({
3344
3815
  message: `Select a ${type} to import:`,
3345
3816
  options: availableList.map((item) => ({
3346
3817
  value: item.name,
@@ -3352,71 +3823,94 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3352
3823
  }
3353
3824
  if (!candidateNames.includes(name)) {
3354
3825
  const closest = findClosestMatch(name, candidateNames);
3355
- p4__namespace.log.error(pc13__default.default.red(`\u274C Module '${name}' not found in the ${type} registry.`));
3826
+ p4__namespace.log.error(pc14__default.default.red(`\u274C Module '${name}' not found in the ${type} registry.`));
3356
3827
  if (closest) {
3357
- p4__namespace.log.info(pc13__default.default.yellow(`Did you mean '${pc13__default.default.cyan(closest)}'?`));
3828
+ p4__namespace.log.info(pc14__default.default.yellow(`Did you mean '${pc14__default.default.cyan(closest)}'?`));
3358
3829
  }
3359
3830
  process.exit(1);
3360
3831
  }
3361
3832
  const importedModules = /* @__PURE__ */ new Set();
3362
3833
  const totalWrittenFiles = [];
3363
3834
  const requiredNpmPackages = /* @__PURE__ */ new Set();
3364
- async function importModuleRecursive(modType, modName) {
3365
- const modKey = `${modType}:${modName}`;
3835
+ async function importModuleRecursive(modType, registryName, targetName = registryName, options) {
3836
+ const modKey = `${modType}:${targetName}`;
3366
3837
  if (importedModules.has(modKey)) return;
3367
- p4__namespace.log.step(`Importing ${modType}: ${pc13__default.default.cyan(modName)}...`);
3838
+ p4__namespace.log.step(
3839
+ `Importing ${modType}: ${pc14__default.default.cyan(targetName)}${targetName !== registryName ? pc14__default.default.dim(` (from ${registryName})`) : ""}...`
3840
+ );
3368
3841
  let manifest;
3369
3842
  try {
3370
- manifest = await fetchModuleManifest(registryBase, modType, modName);
3843
+ manifest = await fetchModuleManifest(registryBase, modType, registryName);
3371
3844
  } catch (err) {
3372
- p4__namespace.log.error(pc13__default.default.red(`Failed to fetch manifest for ${modType} '${modName}': ${err.message}`));
3845
+ p4__namespace.log.error(pc14__default.default.red(`Failed to fetch manifest for ${modType} '${registryName}': ${err.message}`));
3373
3846
  process.exit(1);
3374
3847
  }
3848
+ const aliasMap = /* @__PURE__ */ new Map();
3849
+ if (targetName !== registryName) {
3850
+ const folder2 = modType === "shared" ? "shared" : "features";
3851
+ aliasMap.set(`@/${folder2}/${registryName}/`, `@/${folder2}/${targetName}/`);
3852
+ }
3375
3853
  const sharedDeps = manifest.dependencies?.shared || [];
3376
3854
  for (const dep of sharedDeps) {
3377
- const depPath = path3__default.default.join(projectRoot, "src", "shared", dep);
3378
- if (!fs3__default.default.existsSync(depPath) && !importedModules.has(`shared:${dep}`)) {
3379
- p4__namespace.log.info(` \u21B3 Resolving required shared dependency: ${pc13__default.default.cyan(dep)}`);
3380
- await importModuleRecursive("shared", dep);
3381
- }
3855
+ await resolveDependency(
3856
+ "shared",
3857
+ dep,
3858
+ aliasMap,
3859
+ projectRoot,
3860
+ flags,
3861
+ importModuleRecursive,
3862
+ importedModules,
3863
+ registryBase,
3864
+ index
3865
+ );
3382
3866
  }
3383
3867
  const featureDeps = manifest.dependencies?.features || [];
3384
3868
  for (const dep of featureDeps) {
3385
- const depPath = path3__default.default.join(projectRoot, "src", "features", dep);
3386
- if (!fs3__default.default.existsSync(depPath) && !importedModules.has(`feature:${dep}`)) {
3387
- p4__namespace.log.info(` \u21B3 Resolving required feature dependency: ${pc13__default.default.cyan(dep)}`);
3388
- await importModuleRecursive("feature", dep);
3389
- }
3869
+ await resolveDependency(
3870
+ "feature",
3871
+ dep,
3872
+ aliasMap,
3873
+ projectRoot,
3874
+ flags,
3875
+ importModuleRecursive,
3876
+ importedModules,
3877
+ registryBase,
3878
+ index
3879
+ );
3390
3880
  }
3391
3881
  for (const pkg of manifest.dependencies?.npm || []) {
3392
3882
  requiredNpmPackages.add(pkg);
3393
3883
  }
3394
3884
  const folder = modType === "shared" ? "shared" : "features";
3395
- const targetDir = path3__default.default.join(projectRoot, "src", folder, modName);
3885
+ const targetDir = path2__default.default.join(getModuleBaseDir(projectRoot, modType), targetName);
3886
+ const isOverwrite = Boolean(flags.overwrite || options?.overwrite);
3396
3887
  for (const file of manifest.files || []) {
3397
3888
  if (file === "registry.json") continue;
3398
- const remoteFilePath = `${folder}/${modName}/${file}`;
3399
- const localFilePath = path3__default.default.join(targetDir, file);
3400
- if (fs3__default.default.existsSync(localFilePath) && !flags.overwrite) {
3401
- p4__namespace.log.warn(` \u21B3 Skipping existing file: ${pc13__default.default.dim(path3__default.default.relative(projectRoot, localFilePath))} (use --overwrite to replace)`);
3889
+ const remoteFilePath = `${folder}/${registryName}/${file}`;
3890
+ const localFilePath = path2__default.default.join(targetDir, file);
3891
+ if (fs4__default.default.existsSync(localFilePath) && !isOverwrite) {
3892
+ p4__namespace.log.warn(
3893
+ ` \u21B3 Skipping existing file: ${pc14__default.default.dim(path2__default.default.relative(projectRoot, localFilePath))} (use --overwrite to replace)`
3894
+ );
3402
3895
  continue;
3403
3896
  }
3404
- const content = await fetchRegistryFile(registryBase, remoteFilePath);
3897
+ let content = await fetchRegistryFile(registryBase, remoteFilePath);
3898
+ content = rewriteImports(content, aliasMap);
3405
3899
  if (!flags.dryRun) {
3406
- fs3__default.default.mkdirSync(path3__default.default.dirname(localFilePath), { recursive: true });
3407
- fs3__default.default.writeFileSync(localFilePath, content, "utf-8");
3900
+ fs4__default.default.mkdirSync(path2__default.default.dirname(localFilePath), { recursive: true });
3901
+ fs4__default.default.writeFileSync(localFilePath, content, "utf-8");
3408
3902
  }
3409
- const displayPath = path3__default.default.relative(projectRoot, localFilePath);
3903
+ const displayPath = path2__default.default.relative(projectRoot, localFilePath);
3410
3904
  totalWrittenFiles.push(displayPath);
3411
- p4__namespace.log.message(` ${pc13__default.default.green("+")} ${displayPath}`);
3905
+ p4__namespace.log.message(` ${pc14__default.default.green("+")} ${displayPath}`);
3412
3906
  }
3413
3907
  importedModules.add(modKey);
3414
3908
  }
3415
- await importModuleRecursive(type, name);
3909
+ await importModuleRecursive(type, name, name);
3416
3910
  const missingNpm = getMissingNpmPackages(projectRoot, Array.from(requiredNpmPackages));
3417
3911
  if (missingNpm.length > 0) {
3418
3912
  if (flags.skipInstall || flags.dryRun) {
3419
- p4__namespace.log.info(`Required npm dependencies to install: ${pc13__default.default.cyan(missingNpm.join(", "))}`);
3913
+ p4__namespace.log.info(`Required npm dependencies to install: ${pc14__default.default.cyan(missingNpm.join(", "))}`);
3420
3914
  } else {
3421
3915
  try {
3422
3916
  installNpmPackages(projectRoot, missingNpm);
@@ -3426,11 +3920,13 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3426
3920
  }
3427
3921
  }
3428
3922
  if (flags.dryRun) {
3429
- p4__namespace.log.info(pc13__default.default.yellow("Dry run complete: No files were written to disk."));
3923
+ p4__namespace.log.info(pc14__default.default.yellow("Dry run complete: No files were written to disk."));
3430
3924
  } else {
3925
+ const baseDir = getModuleBaseDir(projectRoot, type);
3926
+ const displayTarget = path2__default.default.relative(projectRoot, path2__default.default.join(baseDir, name));
3431
3927
  p4__namespace.log.success(
3432
- pc13__default.default.green(
3433
- `Successfully imported ${type} '${name}' (${totalWrittenFiles.length} file${totalWrittenFiles.length === 1 ? "" : "s"} placed in src/${type === "shared" ? "shared" : "features"}/${name})`
3928
+ pc14__default.default.green(
3929
+ `Successfully imported ${type} '${name}' (${totalWrittenFiles.length} file${totalWrittenFiles.length === 1 ? "" : "s"} placed in ${displayTarget})`
3434
3930
  )
3435
3931
  );
3436
3932
  }
@@ -3439,24 +3935,70 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3439
3935
  // src/cli.ts
3440
3936
  function createCli() {
3441
3937
  const cli = cac.cac("mason");
3442
- 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) => {
3938
+ 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) => {
3443
3939
  if (args && args.length > 0) {
3444
3940
  p4__namespace.log.error(
3445
- pc13__default.default.red(
3941
+ pc14__default.default.red(
3446
3942
  `Unknown command: "${args.join(" ")}". Use --help to list available commands.`
3447
3943
  )
3448
3944
  );
3449
3945
  process.exit(1);
3450
3946
  }
3451
3947
  try {
3948
+ const projectRoot = findProjectRoot();
3949
+ const mode = getEffectiveMode(projectRoot, flags);
3950
+ if (mode === "registry") {
3951
+ const choice2 = handleCancel(
3952
+ await p4__namespace.select({
3953
+ message: "\u{1F3DB}\uFE0F Mason (Registry Mode): What would you like to scaffold?",
3954
+ options: [
3955
+ {
3956
+ value: "create_feature",
3957
+ label: "Initialize New Feature",
3958
+ hint: "Scaffold a new feature slice under features/"
3959
+ },
3960
+ {
3961
+ value: "create_shared",
3962
+ label: "Initialize New Shared Module",
3963
+ hint: "Scaffold a new shared utility/port under shared/"
3964
+ },
3965
+ {
3966
+ value: "create_usecase",
3967
+ label: "Create New Use Case",
3968
+ hint: "Add a new use case to an existing feature"
3969
+ },
3970
+ {
3971
+ value: "import",
3972
+ label: "Import from Remote Registry",
3973
+ hint: "Download a module from upstream"
3974
+ }
3975
+ ]
3976
+ })
3977
+ );
3978
+ switch (choice2) {
3979
+ case "create_feature":
3980
+ await handleCreate("feature", void 0, flags);
3981
+ break;
3982
+ case "create_shared":
3983
+ await handleCreate("shared", void 0, flags);
3984
+ break;
3985
+ case "create_usecase":
3986
+ await handleCreate("usecase", void 0, flags);
3987
+ break;
3988
+ case "import":
3989
+ await handleImport(void 0, void 0, flags);
3990
+ break;
3991
+ }
3992
+ return;
3993
+ }
3452
3994
  const choice = handleCancel(
3453
3995
  await p4__namespace.select({
3454
3996
  message: "\u{1F9F1} Mason: What would you like to do?",
3455
3997
  options: [
3456
3998
  {
3457
3999
  value: "create",
3458
- label: "Create local component",
3459
- hint: "Scaffold a new feature, use case, domain entity, interface, etc."
4000
+ label: "Create component",
4001
+ hint: "Scaffold a new feature, shared module, use case, domain entity, etc."
3460
4002
  },
3461
4003
  {
3462
4004
  value: "import",
@@ -3477,8 +4019,8 @@ function createCli() {
3477
4019
  });
3478
4020
  cli.command(
3479
4021
  "create [type] [name]",
3480
- "Scaffold local Clean Architecture components (feature, usecase, domain, etc.)"
3481
- ).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 = {}) => {
4022
+ "Scaffold local Clean Architecture components (feature, shared, usecase, domain, etc.)"
4023
+ ).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 = {}) => {
3482
4024
  try {
3483
4025
  await handleCreate(type, name, flags);
3484
4026
  } catch (err) {
@@ -3497,6 +4039,7 @@ function createCli() {
3497
4039
  });
3498
4040
  const createSubtypes = [
3499
4041
  "feature",
4042
+ "shared",
3500
4043
  "usecase",
3501
4044
  "domain",
3502
4045
  "infrastructure",
@@ -3517,7 +4060,7 @@ function createCli() {
3517
4060
  });
3518
4061
  }
3519
4062
  cli.help();
3520
- cli.version("1.0.10");
4063
+ cli.version("1.0.14");
3521
4064
  return cli;
3522
4065
  }
3523
4066
  function handleCliError(err) {
@@ -3525,7 +4068,7 @@ function handleCliError(err) {
3525
4068
  process.exit(0);
3526
4069
  }
3527
4070
  const message = err instanceof Error ? err.message : String(err);
3528
- p4__namespace.log.error(pc13__default.default.red(`Error: ${message}`));
4071
+ p4__namespace.log.error(pc14__default.default.red(`Error: ${message}`));
3529
4072
  process.exit(1);
3530
4073
  }
3531
4074
  async function runCli(argv = process.argv) {
@@ -3535,6 +4078,7 @@ async function runCli(argv = process.argv) {
3535
4078
 
3536
4079
  exports.DEFAULT_REGISTRY_BASE = DEFAULT_REGISTRY_BASE;
3537
4080
  exports.PromptCancelledError = PromptCancelledError;
4081
+ exports.computeDirectoryHash = computeDirectoryHash;
3538
4082
  exports.concreteInfraTemplate = concreteInfraTemplate;
3539
4083
  exports.createCli = createCli;
3540
4084
  exports.detectPackageManager = detectPackageManager;
@@ -3562,10 +4106,15 @@ exports.generateInjectable = generateInjectable;
3562
4106
  exports.generateInjectableCode = generateInjectableCode;
3563
4107
  exports.generateInterface = generateInterface;
3564
4108
  exports.generateInterfaceCode = generateInterfaceCode;
4109
+ exports.generateShared = generateShared;
3565
4110
  exports.generateStubCode = generateStubCode;
3566
4111
  exports.generateUseCase = generateUseCase;
3567
4112
  exports.generateUseCaseCode = generateUseCaseCode;
3568
4113
  exports.generateUseCaseTestCode = generateUseCaseTestCode;
4114
+ exports.getAllFiles = getAllFiles;
4115
+ exports.getConfiguredFeaturesDir = getConfiguredFeaturesDir;
4116
+ exports.getConfiguredSharedDir = getConfiguredSharedDir;
4117
+ exports.getEffectiveMode = getEffectiveMode;
3569
4118
  exports.getMissingNpmPackages = getMissingNpmPackages;
3570
4119
  exports.getRegistryBase = getRegistryBase;
3571
4120
  exports.getTemplatesDir = getTemplatesDir;
@@ -3583,8 +4132,10 @@ exports.levenshteinDistance = levenshteinDistance;
3583
4132
  exports.listDomainEntities = listDomainEntities;
3584
4133
  exports.listDomainInterfaces = listDomainInterfaces;
3585
4134
  exports.listExistingFeatures = listExistingFeatures;
4135
+ exports.listExistingShared = listExistingShared;
3586
4136
  exports.listUseCases = listUseCases;
3587
4137
  exports.loadInterfaceHierarchy = loadInterfaceHierarchy;
4138
+ exports.loadMasonConfig = loadMasonConfig;
3588
4139
  exports.loadTemplate = loadTemplate;
3589
4140
  exports.normalizeRegistryUrl = normalizeRegistryUrl;
3590
4141
  exports.parseInterfaceMethodsAndImports = parseInterfaceMethodsAndImports;
@@ -3595,7 +4146,9 @@ exports.removeInterfacePrefix = removeInterfacePrefix;
3595
4146
  exports.renderTemplate = renderTemplate;
3596
4147
  exports.renderTemplateFile = renderTemplateFile;
3597
4148
  exports.resolveFeaturesDir = resolveFeaturesDir;
4149
+ exports.resolveSharedDir = resolveSharedDir;
3598
4150
  exports.runCli = runCli;
4151
+ exports.runSharedCommand = runSharedCommand;
3599
4152
  exports.stubInfraTemplate = stubInfraTemplate;
3600
4153
  exports.synthesizeCrudMethods = synthesizeCrudMethods;
3601
4154
  exports.toCamelCase = toCamelCase;