@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.js CHANGED
@@ -1,9 +1,10 @@
1
- import fs3 from 'fs';
2
- import path3 from 'path';
1
+ import fs4 from 'fs';
2
+ import path2 from 'path';
3
3
  import * as p4 from '@clack/prompts';
4
- import pc13 from 'picocolors';
4
+ import pc14 from 'picocolors';
5
5
  import { fileURLToPath } from 'url';
6
6
  import { execSync } from 'child_process';
7
+ import { createHash } from 'crypto';
7
8
  import Handlebars from 'handlebars';
8
9
  import { cac } from 'cac';
9
10
 
@@ -75,8 +76,8 @@ function isValidIdentifier(str) {
75
76
  }
76
77
  function writeFileSafely(absolutePath, content, options = {}) {
77
78
  const projectRoot = options.projectRoot || process.cwd();
78
- const relPath = path3.relative(projectRoot, absolutePath).replace(/\\/g, "/");
79
- const existedBefore = fs3.existsSync(absolutePath);
79
+ const relPath = path2.relative(projectRoot, absolutePath).replace(/\\/g, "/");
80
+ const existedBefore = fs4.existsSync(absolutePath);
80
81
  if (options.dryRun) {
81
82
  return {
82
83
  relativePath: relPath,
@@ -95,11 +96,11 @@ function writeFileSafely(absolutePath, content, options = {}) {
95
96
  existedBefore: true
96
97
  };
97
98
  }
98
- const dir = path3.dirname(absolutePath);
99
- if (!fs3.existsSync(dir)) {
100
- fs3.mkdirSync(dir, { recursive: true });
99
+ const dir = path2.dirname(absolutePath);
100
+ if (!fs4.existsSync(dir)) {
101
+ fs4.mkdirSync(dir, { recursive: true });
101
102
  }
102
- fs3.writeFileSync(absolutePath, content, "utf-8");
103
+ fs4.writeFileSync(absolutePath, content, "utf-8");
103
104
  return {
104
105
  relativePath: relPath,
105
106
  absolutePath,
@@ -111,41 +112,114 @@ function writeFileSafely(absolutePath, content, options = {}) {
111
112
  function printGeneratedFiles(files, isDryRun = false) {
112
113
  console.log("");
113
114
  if (isDryRun) {
114
- p4.log.warn(pc13.yellow(pc13.bold("[DRY RUN PREVIEW] No files were written to disk.")));
115
+ p4.log.warn(pc14.yellow(pc14.bold("[DRY RUN PREVIEW] No files were written to disk.")));
115
116
  }
116
117
  for (const f of files) {
117
118
  if (f.action === "created") {
118
- p4.log.success(`${pc13.bold(f.relativePath)} ${pc13.dim("(created)")}`);
119
+ p4.log.success(`${pc14.bold(f.relativePath)} ${pc14.dim("(created)")}`);
119
120
  } else if (f.action === "overwritten") {
120
- p4.log.warn(`${pc13.bold(f.relativePath)} ${pc13.dim("(overwritten)")}`);
121
+ p4.log.warn(`${pc14.bold(f.relativePath)} ${pc14.dim("(overwritten)")}`);
121
122
  } else if (f.action === "skipped") {
122
- p4.log.info(`${pc13.dim(f.relativePath)} ${pc13.dim("(skipped - already exists, pass -o to overwrite)")}`);
123
+ p4.log.info(`${pc14.dim(f.relativePath)} ${pc14.dim("(skipped - already exists, pass -o to overwrite)")}`);
123
124
  } else {
124
- p4.log.info(`${pc13.bold(f.relativePath)} ${pc13.dim("(preview)")}`);
125
+ p4.log.info(`${pc14.bold(f.relativePath)} ${pc14.dim("(preview)")}`);
125
126
  }
126
127
  }
127
128
  }
129
+ function loadMasonConfig(startDir = process.cwd()) {
130
+ let current = path2.resolve(startDir);
131
+ while (true) {
132
+ const configPath = path2.join(current, "mason.config.json");
133
+ if (fs4.existsSync(configPath)) {
134
+ try {
135
+ return JSON.parse(fs4.readFileSync(configPath, "utf-8"));
136
+ } catch {
137
+ return null;
138
+ }
139
+ }
140
+ const parent = path2.dirname(current);
141
+ if (parent === current) break;
142
+ current = parent;
143
+ }
144
+ return null;
145
+ }
146
+ function getEffectiveMode(projectRoot, flags) {
147
+ if (flags?.registry) return "registry";
148
+ const config = loadMasonConfig(projectRoot);
149
+ if (config?.mode) return config.mode;
150
+ if (fs4.existsSync(path2.join(projectRoot, "index.json")) && (fs4.existsSync(path2.join(projectRoot, "features")) || fs4.existsSync(path2.join(projectRoot, "shared"))) && !fs4.existsSync(path2.join(projectRoot, "src"))) {
151
+ return "registry";
152
+ }
153
+ return "app";
154
+ }
155
+ function getConfiguredFeaturesDir(projectRoot, flags) {
156
+ if (flags?.target) return path2.resolve(projectRoot, flags.target);
157
+ if (flags?.dest) return path2.resolve(projectRoot, flags.dest);
158
+ const config = loadMasonConfig(projectRoot);
159
+ if (config?.paths?.features) {
160
+ return path2.resolve(projectRoot, config.paths.features);
161
+ }
162
+ const mode = getEffectiveMode(projectRoot, flags);
163
+ if (mode === "registry") {
164
+ return path2.join(projectRoot, "features");
165
+ }
166
+ if (fs4.existsSync(path2.join(projectRoot, "src/features"))) {
167
+ return path2.join(projectRoot, "src/features");
168
+ }
169
+ return path2.join(projectRoot, "features");
170
+ }
171
+ function getConfiguredSharedDir(projectRoot, flags) {
172
+ if (flags?.target) return path2.resolve(projectRoot, flags.target);
173
+ if (flags?.dest) return path2.resolve(projectRoot, flags.dest);
174
+ const config = loadMasonConfig(projectRoot);
175
+ if (config?.paths?.shared) {
176
+ return path2.resolve(projectRoot, config.paths.shared);
177
+ }
178
+ const mode = getEffectiveMode(projectRoot, flags);
179
+ if (mode === "registry") {
180
+ return path2.join(projectRoot, "shared");
181
+ }
182
+ if (fs4.existsSync(path2.join(projectRoot, "src/shared"))) {
183
+ return path2.join(projectRoot, "src/shared");
184
+ }
185
+ return path2.join(projectRoot, "shared");
186
+ }
187
+
188
+ // src/utils/project.ts
128
189
  function findProjectRoot(startDir = process.cwd()) {
129
- let current = path3.resolve(startDir);
190
+ let current = path2.resolve(startDir);
130
191
  while (true) {
131
- if (fs3.existsSync(path3.join(current, "package.json"))) {
192
+ if (fs4.existsSync(path2.join(current, "package.json"))) {
132
193
  return current;
133
194
  }
134
- const parent = path3.dirname(current);
195
+ const parent = path2.dirname(current);
135
196
  if (parent === current) {
136
- return path3.resolve(startDir);
197
+ return path2.resolve(startDir);
137
198
  }
138
199
  current = parent;
139
200
  }
140
201
  }
141
- function resolveFeaturesDir(projectRoot) {
142
- return path3.join(projectRoot, "features");
202
+ function resolveFeaturesDir(projectRoot, flags) {
203
+ return getConfiguredFeaturesDir(projectRoot, flags);
204
+ }
205
+ function resolveSharedDir(projectRoot, flags) {
206
+ return getConfiguredSharedDir(projectRoot, flags);
207
+ }
208
+ function listExistingShared(projectRoot) {
209
+ const sharedDir = resolveSharedDir(projectRoot);
210
+ if (!fs4.existsSync(sharedDir)) return [];
211
+ try {
212
+ const entries = fs4.readdirSync(sharedDir, { withFileTypes: true });
213
+ return entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name).sort();
214
+ } catch {
215
+ return [];
216
+ }
143
217
  }
144
218
  function listExistingFeatures(projectRoot) {
145
219
  const featuresDir = resolveFeaturesDir(projectRoot);
146
- if (!fs3.existsSync(featuresDir)) return [];
220
+ if (!fs4.existsSync(featuresDir)) return [];
147
221
  try {
148
- const entries = fs3.readdirSync(featuresDir, { withFileTypes: true });
222
+ const entries = fs4.readdirSync(featuresDir, { withFileTypes: true });
149
223
  return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
150
224
  } catch {
151
225
  return [];
@@ -155,14 +229,14 @@ function listDomainInterfaces(projectRoot, featureName) {
155
229
  const results = [];
156
230
  const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
157
231
  for (const feat of features) {
158
- const domainDir = path3.join(resolveFeaturesDir(projectRoot), feat, "domain");
159
- if (!fs3.existsSync(domainDir)) continue;
232
+ const domainDir = path2.join(resolveFeaturesDir(projectRoot), feat, "domain");
233
+ if (!fs4.existsSync(domainDir)) continue;
160
234
  try {
161
- const files = fs3.readdirSync(domainDir);
235
+ const files = fs4.readdirSync(domainDir);
162
236
  for (const file of files) {
163
237
  if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
164
238
  const name = file.replace(/\.ts$/, "");
165
- const fullPath = path3.join(domainDir, file);
239
+ const fullPath = path2.join(domainDir, file);
166
240
  if (/^I[A-Z]/.test(name)) {
167
241
  results.push({
168
242
  name,
@@ -171,7 +245,7 @@ function listDomainInterfaces(projectRoot, featureName) {
171
245
  });
172
246
  } else {
173
247
  try {
174
- const content = fs3.readFileSync(fullPath, "utf-8");
248
+ const content = fs4.readFileSync(fullPath, "utf-8");
175
249
  if (/export\s+(?:abstract\s+class|interface)\s+(\w+)/.test(content)) {
176
250
  results.push({
177
251
  name,
@@ -193,17 +267,17 @@ function listDomainEntities(projectRoot, featureName) {
193
267
  const results = [];
194
268
  const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
195
269
  for (const feat of features) {
196
- const domainDir = path3.join(resolveFeaturesDir(projectRoot), feat, "domain");
197
- if (!fs3.existsSync(domainDir)) continue;
270
+ const domainDir = path2.join(resolveFeaturesDir(projectRoot), feat, "domain");
271
+ if (!fs4.existsSync(domainDir)) continue;
198
272
  try {
199
- const files = fs3.readdirSync(domainDir);
273
+ const files = fs4.readdirSync(domainDir);
200
274
  for (const file of files) {
201
275
  if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
202
276
  const name = file.replace(/\.ts$/, "");
203
277
  if (!/^I[A-Z]/.test(name)) {
204
278
  results.push({
205
279
  name,
206
- fullPath: path3.join(domainDir, file),
280
+ fullPath: path2.join(domainDir, file),
207
281
  feature: feat
208
282
  });
209
283
  }
@@ -218,16 +292,16 @@ function listUseCases(projectRoot, featureName) {
218
292
  const results = [];
219
293
  const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
220
294
  for (const feat of features) {
221
- const useCasesDir = path3.join(resolveFeaturesDir(projectRoot), feat, "useCases");
222
- if (!fs3.existsSync(useCasesDir)) continue;
295
+ const useCasesDir = path2.join(resolveFeaturesDir(projectRoot), feat, "useCases");
296
+ if (!fs4.existsSync(useCasesDir)) continue;
223
297
  try {
224
- const files = fs3.readdirSync(useCasesDir);
298
+ const files = fs4.readdirSync(useCasesDir);
225
299
  for (const file of files) {
226
300
  if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
227
301
  const name = file.replace(/\.ts$/, "");
228
302
  results.push({
229
303
  name,
230
- fullPath: path3.join(useCasesDir, file),
304
+ fullPath: path2.join(useCasesDir, file),
231
305
  feature: feat
232
306
  });
233
307
  }
@@ -252,13 +326,13 @@ function handleCancel(result) {
252
326
  }
253
327
  function printBanner() {
254
328
  console.log("");
255
- p4.intro(pc13.bgCyan(pc13.black(" @solid-stack/mason ")));
329
+ p4.intro(pc14.bgCyan(pc14.black(" @solid-stack/mason ")));
256
330
  }
257
331
  async function promptProperties(options = {}) {
258
332
  const properties = options.defaults ? [...options.defaults] : [];
259
333
  if (properties.length > 0) {
260
- const summary = properties.map((p15) => `${p15.name}: ${p15.type}${p15.optional ? "?" : ""}`).join(", ");
261
- p4.log.info(pc13.dim(`Pre-configured fields: ${summary}`));
334
+ const summary = properties.map((p16) => `${p16.name}: ${p16.type}${p16.optional ? "?" : ""}`).join(", ");
335
+ p4.log.info(pc14.dim(`Pre-configured fields: ${summary}`));
262
336
  const addMore = handleCancel(
263
337
  await p4.confirm({
264
338
  message: "Would you like to add custom fields/properties?",
@@ -269,7 +343,7 @@ async function promptProperties(options = {}) {
269
343
  return properties;
270
344
  }
271
345
  }
272
- p4.log.info(pc13.cyan("Enter properties (leave name blank when finished):"));
346
+ p4.log.info(pc14.cyan("Enter properties (leave name blank when finished):"));
273
347
  while (true) {
274
348
  const name = handleCancel(
275
349
  await p4.text({
@@ -317,7 +391,7 @@ async function promptProperties(options = {}) {
317
391
  optional
318
392
  });
319
393
  p4.log.success(
320
- pc13.green(`Added: ${name.trim()}${optional ? "?" : ""}: ${finalType.trim()}`)
394
+ pc14.green(`Added: ${name.trim()}${optional ? "?" : ""}: ${finalType.trim()}`)
321
395
  );
322
396
  }
323
397
  return properties;
@@ -339,25 +413,25 @@ function normalizeRegistryUrl(urlOrPath, baseDir) {
339
413
  return `https://raw.githubusercontent.com/${owner}/${repo}/${branch}`;
340
414
  }
341
415
  if (baseDir && (trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed === "." || trimmed === "..")) {
342
- return path3.resolve(baseDir, trimmed);
416
+ return path2.resolve(baseDir, trimmed);
343
417
  }
344
418
  return trimmed;
345
419
  }
346
420
  function findLocalRegistry(startDir = process.cwd()) {
347
- let current = path3.resolve(startDir);
421
+ let current = path2.resolve(startDir);
348
422
  while (true) {
349
- const candidate = path3.join(current, "mason-registry");
350
- if (fs3.existsSync(candidate) && fs3.existsSync(path3.join(candidate, "index.json"))) {
423
+ const candidate = path2.join(current, "mason-registry");
424
+ if (fs4.existsSync(candidate) && fs4.existsSync(path2.join(candidate, "index.json"))) {
351
425
  return candidate;
352
426
  }
353
- const parent = path3.dirname(current);
427
+ const parent = path2.dirname(current);
354
428
  if (parent === current) break;
355
429
  current = parent;
356
430
  }
357
431
  return null;
358
432
  }
359
433
  function getRegistryBase(projectRoot, override) {
360
- const rootDir = projectRoot ? path3.resolve(projectRoot) : process.cwd();
434
+ const rootDir = projectRoot ? path2.resolve(projectRoot) : process.cwd();
361
435
  if (override && override.trim()) {
362
436
  return normalizeRegistryUrl(override.trim(), rootDir);
363
437
  }
@@ -365,30 +439,30 @@ function getRegistryBase(projectRoot, override) {
365
439
  return normalizeRegistryUrl(process.env.MASON_REGISTRY_URL.trim(), rootDir);
366
440
  }
367
441
  const configLocations = [
368
- path3.join(rootDir, "mason.config.json"),
369
- path3.join(process.cwd(), "mason.config.json")
442
+ path2.join(rootDir, "mason.config.json"),
443
+ path2.join(process.cwd(), "mason.config.json")
370
444
  ];
371
445
  for (const configPath of configLocations) {
372
- if (fs3.existsSync(configPath)) {
446
+ if (fs4.existsSync(configPath)) {
373
447
  try {
374
- const config = JSON.parse(fs3.readFileSync(configPath, "utf-8"));
448
+ const config = JSON.parse(fs4.readFileSync(configPath, "utf-8"));
375
449
  if (config.registry && typeof config.registry === "string") {
376
- return normalizeRegistryUrl(config.registry.trim(), path3.dirname(configPath));
450
+ return normalizeRegistryUrl(config.registry.trim(), path2.dirname(configPath));
377
451
  }
378
452
  } catch {
379
453
  }
380
454
  }
381
455
  }
382
456
  const pkgLocations = [
383
- path3.join(rootDir, "package.json"),
384
- path3.join(process.cwd(), "package.json")
457
+ path2.join(rootDir, "package.json"),
458
+ path2.join(process.cwd(), "package.json")
385
459
  ];
386
460
  for (const pkgPath of pkgLocations) {
387
- if (fs3.existsSync(pkgPath)) {
461
+ if (fs4.existsSync(pkgPath)) {
388
462
  try {
389
- const pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
463
+ const pkg = JSON.parse(fs4.readFileSync(pkgPath, "utf-8"));
390
464
  if (pkg.mason?.registry && typeof pkg.mason.registry === "string") {
391
- return normalizeRegistryUrl(pkg.mason.registry.trim(), path3.dirname(pkgPath));
465
+ return normalizeRegistryUrl(pkg.mason.registry.trim(), path2.dirname(pkgPath));
392
466
  }
393
467
  } catch {
394
468
  }
@@ -429,11 +503,11 @@ async function fetchRegistryFile(registryBase, relPath) {
429
503
  if (baseDir.startsWith("file://")) {
430
504
  baseDir = fileURLToPath(baseDir);
431
505
  }
432
- const filePath = path3.resolve(baseDir, cleanRelPath);
433
- if (!fs3.existsSync(filePath)) {
506
+ const filePath = path2.resolve(baseDir, cleanRelPath);
507
+ if (!fs4.existsSync(filePath)) {
434
508
  throw new Error(`Registry file not found at ${filePath}`);
435
509
  }
436
- return fs3.promises.readFile(filePath, "utf-8");
510
+ return fs4.promises.readFile(filePath, "utf-8");
437
511
  }
438
512
  async function fetchRegistryIndex(registryBase) {
439
513
  const content = await fetchRegistryFile(registryBase, "index.json");
@@ -495,26 +569,26 @@ function findClosestMatch(target, candidates) {
495
569
  return null;
496
570
  }
497
571
  function detectPackageManager(projectRoot) {
498
- if (fs3.existsSync(path3.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
499
- if (fs3.existsSync(path3.join(projectRoot, "yarn.lock"))) return "yarn";
500
- if (fs3.existsSync(path3.join(projectRoot, "bun.lockb")) || fs3.existsSync(path3.join(projectRoot, "bun.lock"))) return "bun";
572
+ if (fs4.existsSync(path2.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
573
+ if (fs4.existsSync(path2.join(projectRoot, "yarn.lock"))) return "yarn";
574
+ if (fs4.existsSync(path2.join(projectRoot, "bun.lockb")) || fs4.existsSync(path2.join(projectRoot, "bun.lock"))) return "bun";
501
575
  return "npm";
502
576
  }
503
577
  function getMissingNpmPackages(projectRoot, requiredPackages) {
504
578
  if (!requiredPackages || requiredPackages.length === 0) return [];
505
- const pkgJsonPath = path3.join(projectRoot, "package.json");
506
- if (!fs3.existsSync(pkgJsonPath)) {
579
+ const pkgJsonPath = path2.join(projectRoot, "package.json");
580
+ if (!fs4.existsSync(pkgJsonPath)) {
507
581
  return requiredPackages;
508
582
  }
509
583
  try {
510
- const pkg = JSON.parse(fs3.readFileSync(pkgJsonPath, "utf-8"));
584
+ const pkg = JSON.parse(fs4.readFileSync(pkgJsonPath, "utf-8"));
511
585
  const installed = /* @__PURE__ */ new Set([
512
586
  ...Object.keys(pkg.dependencies || {}),
513
587
  ...Object.keys(pkg.devDependencies || {}),
514
588
  ...Object.keys(pkg.peerDependencies || {})
515
589
  ]);
516
- return requiredPackages.filter((p15) => {
517
- const nameOnly = p15.startsWith("@") ? `@${p15.slice(1).split("@")[0] || ""}` : p15.split("@")[0] || p15;
590
+ return requiredPackages.filter((p16) => {
591
+ const nameOnly = p16.startsWith("@") ? `@${p16.slice(1).split("@")[0] || ""}` : p16.split("@")[0] || p16;
518
592
  return !installed.has(nameOnly);
519
593
  });
520
594
  } catch {
@@ -539,12 +613,47 @@ function installNpmPackages(projectRoot, packages, pm) {
539
613
  installCmd = `npm install ${packages.join(" ")}`;
540
614
  break;
541
615
  }
542
- p4.log.info(`Installing missing dependencies with ${manager}: ${pc13.cyan(packages.join(", "))}`);
616
+ p4.log.info(`Installing missing dependencies with ${manager}: ${pc14.cyan(packages.join(", "))}`);
543
617
  execSync(installCmd, {
544
618
  cwd: projectRoot,
545
619
  stdio: "inherit"
546
620
  });
547
621
  }
622
+ function getAllFiles(dir, baseDir = dir) {
623
+ if (!fs4.existsSync(dir)) return [];
624
+ const entries = fs4.readdirSync(dir, { withFileTypes: true });
625
+ const files = [];
626
+ for (const entry of entries) {
627
+ const fullPath = path2.join(dir, entry.name);
628
+ const relPath = path2.relative(baseDir, fullPath).replace(/\\/g, "/");
629
+ if (entry.isDirectory()) {
630
+ if (entry.name === "__tests__" || entry.name.startsWith(".")) {
631
+ continue;
632
+ }
633
+ files.push(...getAllFiles(fullPath, baseDir));
634
+ } else if (entry.isFile()) {
635
+ if (entry.name === "registry.json" || entry.name.endsWith(".test.ts") || entry.name.endsWith(".spec.ts") || entry.name.startsWith(".")) {
636
+ continue;
637
+ }
638
+ files.push(relPath);
639
+ }
640
+ }
641
+ return files.sort();
642
+ }
643
+ function computeDirectoryHash(dirPath, files) {
644
+ if (!fs4.existsSync(dirPath)) return "";
645
+ const hash = createHash("sha256");
646
+ const fileList = (files ?? getAllFiles(dirPath)).sort();
647
+ for (const relativePath of fileList) {
648
+ const absolutePath = path2.join(dirPath, relativePath);
649
+ if (!fs4.existsSync(absolutePath)) continue;
650
+ const content = fs4.readFileSync(absolutePath, "utf8");
651
+ const normalizedContent = content.replace(/\r\n/g, "\n");
652
+ hash.update(relativePath);
653
+ hash.update(normalizedContent);
654
+ }
655
+ return `sha256-${hash.digest("hex")}`;
656
+ }
548
657
  var hbs = Handlebars.create();
549
658
  hbs.registerHelper("eq", (a, b) => a === b);
550
659
  hbs.registerHelper("ne", (a, b) => a !== b);
@@ -561,33 +670,33 @@ hbs.registerHelper("join", (arr, sep) => {
561
670
  });
562
671
  var cachedTemplatesDir = null;
563
672
  function getTemplatesDir() {
564
- if (cachedTemplatesDir && fs3.existsSync(cachedTemplatesDir)) {
673
+ if (cachedTemplatesDir && fs4.existsSync(cachedTemplatesDir)) {
565
674
  return cachedTemplatesDir;
566
675
  }
567
- const currentDir = typeof __dirname !== "undefined" ? __dirname : path3.dirname(fileURLToPath(import.meta.url));
568
- const cand1 = path3.resolve(currentDir, "../../templates");
569
- if (fs3.existsSync(cand1)) {
676
+ const currentDir = typeof __dirname !== "undefined" ? __dirname : path2.dirname(fileURLToPath(import.meta.url));
677
+ const cand1 = path2.resolve(currentDir, "../../templates");
678
+ if (fs4.existsSync(cand1)) {
570
679
  cachedTemplatesDir = cand1;
571
680
  return cand1;
572
681
  }
573
- const cand2 = path3.resolve(currentDir, "../templates");
574
- if (fs3.existsSync(cand2)) {
682
+ const cand2 = path2.resolve(currentDir, "../templates");
683
+ if (fs4.existsSync(cand2)) {
575
684
  cachedTemplatesDir = cand2;
576
685
  return cand2;
577
686
  }
578
- const cand3 = path3.resolve(currentDir, "templates");
579
- if (fs3.existsSync(cand3)) {
687
+ const cand3 = path2.resolve(currentDir, "templates");
688
+ if (fs4.existsSync(cand3)) {
580
689
  cachedTemplatesDir = cand3;
581
690
  return cand3;
582
691
  }
583
692
  let curr = currentDir;
584
693
  while (true) {
585
- const candTemplates = path3.join(curr, "templates");
586
- if (fs3.existsSync(candTemplates)) {
694
+ const candTemplates = path2.join(curr, "templates");
695
+ if (fs4.existsSync(candTemplates)) {
587
696
  cachedTemplatesDir = candTemplates;
588
697
  return candTemplates;
589
698
  }
590
- const parent = path3.dirname(curr);
699
+ const parent = path2.dirname(curr);
591
700
  if (parent === curr) break;
592
701
  curr = parent;
593
702
  }
@@ -595,8 +704,8 @@ function getTemplatesDir() {
595
704
  }
596
705
  var templateCache = /* @__PURE__ */ new Map();
597
706
  function loadTemplate(relativePath) {
598
- const fullPath = path3.isAbsolute(relativePath) ? relativePath : path3.join(getTemplatesDir(), relativePath);
599
- return fs3.readFileSync(fullPath, "utf-8");
707
+ const fullPath = path2.isAbsolute(relativePath) ? relativePath : path2.join(getTemplatesDir(), relativePath);
708
+ return fs4.readFileSync(fullPath, "utf-8");
600
709
  }
601
710
  function renderTemplateFile(relativePath, context) {
602
711
  let compiled = templateCache.get(relativePath);
@@ -681,8 +790,8 @@ function generateDomainEntityCode(name, kind = "interface", properties = []) {
681
790
  function generateDomain(options) {
682
791
  const projectRoot = options.projectRoot || findProjectRoot();
683
792
  const entityName = toPascalCase(options.name);
684
- const targetDir = options.targetDir || path3.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
685
- const filePath = path3.join(targetDir, `${entityName}.ts`);
793
+ const targetDir = options.targetDir || path2.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
794
+ const filePath = path2.join(targetDir, `${entityName}.ts`);
686
795
  const code = generateDomainEntityCode(
687
796
  entityName,
688
797
  options.kind || "interface",
@@ -706,7 +815,7 @@ function generateInterfaceCode(ifaceName, kind = "abstractClass", entityName, me
706
815
  const idKey = `${entityKey}Id`;
707
816
  const listKey = `${entityKey}s`;
708
817
  const formattedCustomMethods = customMethods.map((m) => {
709
- const params = m.params && m.params.length > 0 ? m.params.map((p15) => `${p15.name}: ${p15.type}`).join("; ") : "";
818
+ const params = m.params && m.params.length > 0 ? m.params.map((p16) => `${p16.name}: ${p16.type}`).join("; ") : "";
710
819
  const returnType = m.returnType ? `Promise<${m.returnType}>` : "Promise<void>";
711
820
  return {
712
821
  name: m.name,
@@ -731,8 +840,8 @@ function generateInterfaceCode(ifaceName, kind = "abstractClass", entityName, me
731
840
  function generateInterface(options) {
732
841
  const projectRoot = options.projectRoot || findProjectRoot();
733
842
  const ifaceName = ensureInterfacePrefix(options.name);
734
- const targetDir = options.targetDir || path3.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
735
- const filePath = path3.join(targetDir, `${ifaceName}.ts`);
843
+ const targetDir = options.targetDir || path2.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
844
+ const filePath = path2.join(targetDir, `${ifaceName}.ts`);
736
845
  const code = generateInterfaceCode(
737
846
  ifaceName,
738
847
  options.kind || "abstractClass",
@@ -967,7 +1076,7 @@ function parseInterfaceMethodsAndImports(content, targetIfaceName) {
967
1076
  };
968
1077
  }
969
1078
  function findInterfaceFile(projectRoot, ifaceName, featureName, customPath) {
970
- if (customPath && fs3.existsSync(customPath)) {
1079
+ if (customPath && fs4.existsSync(customPath)) {
971
1080
  return { filePath: customPath, ifaceName };
972
1081
  }
973
1082
  const candidateNames = [
@@ -977,22 +1086,22 @@ function findInterfaceFile(projectRoot, ifaceName, featureName, customPath) {
977
1086
  ];
978
1087
  const features = featureName ? [featureName, ...listExistingFeatures(projectRoot).filter((f) => f !== featureName)] : listExistingFeatures(projectRoot);
979
1088
  for (const feat of features) {
980
- const domainDir = path3.join(resolveFeaturesDir(projectRoot), feat, "domain");
981
- if (!fs3.existsSync(domainDir)) continue;
1089
+ const domainDir = path2.join(resolveFeaturesDir(projectRoot), feat, "domain");
1090
+ if (!fs4.existsSync(domainDir)) continue;
982
1091
  for (const cand of candidateNames) {
983
- const p1 = path3.join(domainDir, `${cand}.ts`);
984
- if (fs3.existsSync(p1)) {
1092
+ const p1 = path2.join(domainDir, `${cand}.ts`);
1093
+ if (fs4.existsSync(p1)) {
985
1094
  return { filePath: p1, ifaceName: cand, feature: feat };
986
1095
  }
987
1096
  }
988
1097
  }
989
1098
  for (const cand of candidateNames) {
990
- const p1 = path3.join(projectRoot, "domain", `${cand}.ts`);
991
- if (fs3.existsSync(p1)) return { filePath: p1, ifaceName: cand };
992
- const p22 = path3.join(projectRoot, "src", "domain", `${cand}.ts`);
993
- if (fs3.existsSync(p22)) return { filePath: p22, ifaceName: cand };
994
- const p32 = path3.join(projectRoot, "interfaces", `${cand}.ts`);
995
- if (fs3.existsSync(p32)) return { filePath: p32, ifaceName: cand };
1099
+ const p1 = path2.join(projectRoot, "domain", `${cand}.ts`);
1100
+ if (fs4.existsSync(p1)) return { filePath: p1, ifaceName: cand };
1101
+ const p22 = path2.join(projectRoot, "src", "domain", `${cand}.ts`);
1102
+ if (fs4.existsSync(p22)) return { filePath: p22, ifaceName: cand };
1103
+ const p32 = path2.join(projectRoot, "interfaces", `${cand}.ts`);
1104
+ if (fs4.existsSync(p32)) return { filePath: p32, ifaceName: cand };
996
1105
  }
997
1106
  return null;
998
1107
  }
@@ -1015,7 +1124,7 @@ function loadInterfaceHierarchy(projectRoot, initialIfaceName, featureName, cust
1015
1124
  while (currentFile && !visited.has(currentFile)) {
1016
1125
  visited.add(currentFile);
1017
1126
  try {
1018
- const content = fs3.readFileSync(currentFile, "utf-8");
1127
+ const content = fs4.readFileSync(currentFile, "utf-8");
1019
1128
  const parsed = parseInterfaceMethodsAndImports(content, currentName);
1020
1129
  for (const m of parsed.methods) {
1021
1130
  if (!allMethods.some((existing) => existing.name === m.name)) {
@@ -1091,7 +1200,7 @@ function generateStubCode(stubClassName, ifaceName, entityName, featureName, met
1091
1200
  const entitiesVar = `${entityVar}s`;
1092
1201
  const idKey = `${entityVar}Id`;
1093
1202
  const impPath = ifaceImportPath || `../domain/${ifaceName}.js`;
1094
- const entityImportPath = ifaceImportPath ? `${path3.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
1203
+ const entityImportPath = ifaceImportPath ? `${path2.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
1095
1204
  const importsByFrom = /* @__PURE__ */ new Map();
1096
1205
  for (const imp of domainImports) {
1097
1206
  if (imp.name === ifaceName || imp.name === baseEntity) continue;
@@ -1140,7 +1249,7 @@ function generateConcreteInfraCode(className, ifaceName, dependencies = [], meth
1140
1249
  if (baseEntity) {
1141
1250
  finalMethods = synthesizeCrudMethods(baseEntity);
1142
1251
  if (!finalDomainImports.some((imp) => imp.name === baseEntity)) {
1143
- const importPath = ifaceImportPath ? `${path3.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
1252
+ const importPath = ifaceImportPath ? `${path2.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
1144
1253
  finalDomainImports.push({
1145
1254
  name: baseEntity,
1146
1255
  from: importPath,
@@ -1185,8 +1294,8 @@ function generateInfrastructure(options) {
1185
1294
  const projectRoot = options.projectRoot || findProjectRoot();
1186
1295
  const className = toPascalCase(options.name);
1187
1296
  const isFeatureScope = options.scope !== "global" && Boolean(options.feature);
1188
- const targetDir = options.targetDir || (isFeatureScope ? path3.join(resolveFeaturesDir(projectRoot), options.feature, "infrastructure") : path3.join(projectRoot, "infrastructure"));
1189
- const filePath = path3.join(targetDir, `${className}.ts`);
1297
+ const targetDir = options.targetDir || (isFeatureScope ? path2.join(resolveFeaturesDir(projectRoot), options.feature, "infrastructure") : path2.join(projectRoot, "infrastructure"));
1298
+ const filePath = path2.join(targetDir, `${className}.ts`);
1190
1299
  let code;
1191
1300
  if (options.implementsInterface) {
1192
1301
  const rawIfaceName = options.implementsInterface;
@@ -1200,7 +1309,7 @@ function generateInfrastructure(options) {
1200
1309
  const ifaceFilePath = hierarchy.ifaceFilePath;
1201
1310
  let ifaceImportPath;
1202
1311
  if (ifaceFilePath) {
1203
- let rel = path3.relative(targetDir, ifaceFilePath).replace(/\\/g, "/").replace(/\.ts$/, ".js");
1312
+ let rel = path2.relative(targetDir, ifaceFilePath).replace(/\\/g, "/").replace(/\.ts$/, ".js");
1204
1313
  if (!rel.startsWith(".")) rel = "./" + rel;
1205
1314
  ifaceImportPath = rel;
1206
1315
  } else if (isFeatureScope) {
@@ -1213,8 +1322,8 @@ function generateInfrastructure(options) {
1213
1322
  if (new RegExp(`\\b${item.name}\\b`).test(allSignatures)) {
1214
1323
  let fromPath;
1215
1324
  if (imp.specifier.startsWith(".") && ifaceFilePath) {
1216
- const absTarget = path3.resolve(path3.dirname(ifaceFilePath), imp.specifier);
1217
- let rel = path3.relative(targetDir, absTarget).replace(/\\/g, "/");
1325
+ const absTarget = path2.resolve(path2.dirname(ifaceFilePath), imp.specifier);
1326
+ let rel = path2.relative(targetDir, absTarget).replace(/\\/g, "/");
1218
1327
  if (!rel.startsWith(".")) rel = "./" + rel;
1219
1328
  fromPath = rel;
1220
1329
  } else {
@@ -1287,22 +1396,22 @@ function generateUseCaseCode(useCaseName, dependencies = [], inputProps = [], ou
1287
1396
  from: /^I[A-Z]/.test(dep) ? `../domain/${dep}.js` : `./${dep}.js`
1288
1397
  }));
1289
1398
  const domainTypesToImport = new Set(domainImports);
1290
- for (const p15 of [...inputProps, ...outputProps]) {
1291
- const rawType = p15.type.trim();
1399
+ for (const p16 of [...inputProps, ...outputProps]) {
1400
+ const rawType = p16.type.trim();
1292
1401
  const baseType = rawType.replace(/\[\]$/, "").trim();
1293
1402
  if (!primitiveTypes.has(baseType) && /^[A-Z][A-Za-z0-9_]*$/.test(baseType) && !dependencies.includes(baseType)) {
1294
1403
  domainTypesToImport.add(baseType);
1295
1404
  }
1296
1405
  }
1297
- const formattedInputProps = inputProps.map((p15) => ({
1298
- name: p15.name,
1299
- optional: p15.optional,
1300
- type: p15.optional && !p15.type.includes("undefined") ? `${p15.type} | undefined` : p15.type
1406
+ const formattedInputProps = inputProps.map((p16) => ({
1407
+ name: p16.name,
1408
+ optional: p16.optional,
1409
+ type: p16.optional && !p16.type.includes("undefined") ? `${p16.type} | undefined` : p16.type
1301
1410
  }));
1302
- const formattedOutputProps = outputProps.map((p15) => ({
1303
- name: p15.name,
1304
- optional: p15.optional,
1305
- type: p15.optional && !p15.type.includes("undefined") ? `${p15.type} | undefined` : p15.type
1411
+ const formattedOutputProps = outputProps.map((p16) => ({
1412
+ name: p16.name,
1413
+ optional: p16.optional,
1414
+ type: p16.optional && !p16.type.includes("undefined") ? `${p16.type} | undefined` : p16.type
1306
1415
  }));
1307
1416
  const formattedDeps = dependencies.map((dep) => ({
1308
1417
  key: toCamelCase(dep),
@@ -1401,8 +1510,8 @@ function generateUseCase(options) {
1401
1510
  const files = [];
1402
1511
  const projectRoot = options.projectRoot || findProjectRoot();
1403
1512
  const ucName = toPascalCase(options.name);
1404
- const targetDir = options.targetDir || path3.join(resolveFeaturesDir(projectRoot), options.feature, "useCases");
1405
- const filePath = path3.join(targetDir, `${ucName}.ts`);
1513
+ const targetDir = options.targetDir || path2.join(resolveFeaturesDir(projectRoot), options.feature, "useCases");
1514
+ const filePath = path2.join(targetDir, `${ucName}.ts`);
1406
1515
  const code = generateUseCaseCode(
1407
1516
  ucName,
1408
1517
  options.dependencies || [],
@@ -1418,7 +1527,7 @@ function generateUseCase(options) {
1418
1527
  })
1419
1528
  );
1420
1529
  if (options.includeTest) {
1421
- const testPath = path3.join(targetDir, `${ucName}.test.ts`);
1530
+ const testPath = path2.join(targetDir, `${ucName}.test.ts`);
1422
1531
  const testCode = generateUseCaseTestCode(ucName, options.dependencies || []);
1423
1532
  files.push(
1424
1533
  writeFileSafely(testPath, testCode, {
@@ -1486,10 +1595,10 @@ function generateHttpHandler(options) {
1486
1595
  const files = [];
1487
1596
  const projectRoot = options.projectRoot || findProjectRoot();
1488
1597
  const className = toPascalCase(options.name);
1489
- const pxDir = path3.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress");
1490
- const handlersDir = path3.join(pxDir, "handlers");
1491
- const pxIndexPath = path3.join(pxDir, "index.ts");
1492
- if (!fs3.existsSync(pxIndexPath)) {
1598
+ const pxDir = path2.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress");
1599
+ const handlersDir = path2.join(pxDir, "handlers");
1600
+ const pxIndexPath = path2.join(pxDir, "index.ts");
1601
+ if (!fs4.existsSync(pxIndexPath)) {
1493
1602
  const featurePath = options.routePrefix || `/${options.feature.toLowerCase()}`;
1494
1603
  const pxIndexContent = renderTemplate(expressIndexTemplate, {
1495
1604
  routePath: featurePath
@@ -1502,7 +1611,7 @@ function generateHttpHandler(options) {
1502
1611
  })
1503
1612
  );
1504
1613
  }
1505
- const handlerFilePath = path3.join(handlersDir, `${className}.ts`);
1614
+ const handlerFilePath = path2.join(handlersDir, `${className}.ts`);
1506
1615
  const code = generateHttpHandlerCode(
1507
1616
  className,
1508
1617
  options.method || "get",
@@ -1523,11 +1632,11 @@ function generateHttpHandler(options) {
1523
1632
  return files;
1524
1633
  }
1525
1634
  function updateOrGenerateDiProvider(featureDir, featureName, bindings = [], options = {}) {
1526
- const providerPath = path3.join(featureDir, "diProvider.ts");
1635
+ const providerPath = path2.join(featureDir, "diProvider.ts");
1527
1636
  const pascalFeature = toPascalCase(featureName);
1528
1637
  const providerModuleName = `${pascalFeature}Provider`;
1529
- if (fs3.existsSync(providerPath) && !options.overwrite) {
1530
- let content = fs3.readFileSync(providerPath, "utf-8");
1638
+ if (fs4.existsSync(providerPath) && !options.overwrite) {
1639
+ let content = fs4.readFileSync(providerPath, "utf-8");
1531
1640
  content = content.replace(/\n*export\s+const\s+ScaffoldProvider\s*=[^;]+;?/g, "");
1532
1641
  for (const b of bindings) {
1533
1642
  const ifaceImp = b.ifaceImportPath || `./domain/${b.ifaceName}.js`;
@@ -1586,7 +1695,7 @@ function generateFeature(options) {
1586
1695
  const projectRoot = options.projectRoot || findProjectRoot();
1587
1696
  const rawName = options.name.trim();
1588
1697
  const featureName = toCamelCase(rawName);
1589
- const targetDir = options.targetDir || path3.join(resolveFeaturesDir(projectRoot), featureName);
1698
+ const targetDir = options.targetDir || path2.join(resolveFeaturesDir(projectRoot), featureName);
1590
1699
  const routePath = options.routePath || `/${featureName}`;
1591
1700
  const rawEntities = [];
1592
1701
  if (options.entityNames && options.entityNames.length > 0) {
@@ -1607,7 +1716,7 @@ function generateFeature(options) {
1607
1716
  const domainFile = generateDomain({
1608
1717
  name: ent,
1609
1718
  feature: featureName,
1610
- targetDir: path3.join(targetDir, "domain"),
1719
+ targetDir: path2.join(targetDir, "domain"),
1611
1720
  projectRoot,
1612
1721
  dryRun: options.dryRun,
1613
1722
  overwrite: options.overwrite,
@@ -1627,7 +1736,7 @@ function generateFeature(options) {
1627
1736
  const ifaceFile = generateInterface({
1628
1737
  name: ifaceName,
1629
1738
  feature: featureName,
1630
- targetDir: path3.join(targetDir, "domain"),
1739
+ targetDir: path2.join(targetDir, "domain"),
1631
1740
  entityName: ent,
1632
1741
  projectRoot,
1633
1742
  dryRun: options.dryRun,
@@ -1644,7 +1753,7 @@ function generateFeature(options) {
1644
1753
  const stubFile = generateInfrastructure({
1645
1754
  name: stubName,
1646
1755
  feature: featureName,
1647
- targetDir: path3.join(targetDir, "infrastructure"),
1756
+ targetDir: path2.join(targetDir, "infrastructure"),
1648
1757
  kind: "stub",
1649
1758
  implementsInterface: ifaceName,
1650
1759
  entityName: ent,
@@ -1686,7 +1795,7 @@ function generateFeature(options) {
1686
1795
  const ucFiles = generateUseCase({
1687
1796
  name: ucName,
1688
1797
  feature: featureName,
1689
- targetDir: path3.join(targetDir, "useCases"),
1798
+ targetDir: path2.join(targetDir, "useCases"),
1690
1799
  dependencies: options.includeInterface !== false ? [ifaceName] : [],
1691
1800
  inputProperties: [{ name: idParam, type: "string" }],
1692
1801
  outputProperties: [{ name: entVar, type: primaryEntity }],
@@ -1731,7 +1840,7 @@ function generateFeature(options) {
1731
1840
  const ucFiles = generateUseCase({
1732
1841
  name: ucName,
1733
1842
  feature: featureName,
1734
- targetDir: path3.join(targetDir, "useCases"),
1843
+ targetDir: path2.join(targetDir, "useCases"),
1735
1844
  dependencies: deps,
1736
1845
  inputProperties: [{ name: entVar, type: ent }],
1737
1846
  outputProperties: [{ name: entVar, type: ent }],
@@ -1764,7 +1873,7 @@ function generateFeature(options) {
1764
1873
  const ucFiles = generateUseCase({
1765
1874
  name: ucName,
1766
1875
  feature: featureName,
1767
- targetDir: path3.join(targetDir, "useCases"),
1876
+ targetDir: path2.join(targetDir, "useCases"),
1768
1877
  dependencies: deps,
1769
1878
  inputProperties: [{ name: idParam, type: "string" }],
1770
1879
  outputProperties: [{ name: entVar, type: ent }],
@@ -1797,7 +1906,7 @@ function generateFeature(options) {
1797
1906
  const ucFiles = generateUseCase({
1798
1907
  name: ucName,
1799
1908
  feature: featureName,
1800
- targetDir: path3.join(targetDir, "useCases"),
1909
+ targetDir: path2.join(targetDir, "useCases"),
1801
1910
  dependencies: deps,
1802
1911
  inputProperties: [
1803
1912
  { name: "page", type: "number", optional: true },
@@ -1841,7 +1950,7 @@ function generateFeature(options) {
1841
1950
  const ucFiles = generateUseCase({
1842
1951
  name: ucName,
1843
1952
  feature: featureName,
1844
- targetDir: path3.join(targetDir, "useCases"),
1953
+ targetDir: path2.join(targetDir, "useCases"),
1845
1954
  dependencies: deps,
1846
1955
  inputProperties: [{ name: entVar, type: ent }],
1847
1956
  outputProperties: [{ name: entVar, type: ent }],
@@ -1874,7 +1983,7 @@ function generateFeature(options) {
1874
1983
  const ucFiles = generateUseCase({
1875
1984
  name: ucName,
1876
1985
  feature: featureName,
1877
- targetDir: path3.join(targetDir, "useCases"),
1986
+ targetDir: path2.join(targetDir, "useCases"),
1878
1987
  dependencies: deps,
1879
1988
  inputProperties: [{ name: idParam, type: "string" }],
1880
1989
  outputProperties: [{ name: "success", type: "boolean" }],
@@ -1911,7 +2020,7 @@ function generateFeature(options) {
1911
2020
  const ucFiles = generateUseCase({
1912
2021
  name: ucName,
1913
2022
  feature: featureName,
1914
- targetDir: path3.join(targetDir, "useCases"),
2023
+ targetDir: path2.join(targetDir, "useCases"),
1915
2024
  dependencies: customUc.dependencies || [],
1916
2025
  inputProperties: customUc.inputProperties || [],
1917
2026
  outputProperties: customUc.outputProperties || [],
@@ -1943,8 +2052,8 @@ function generateFeature(options) {
1943
2052
  }
1944
2053
  }
1945
2054
  if (options.includeExpress !== false) {
1946
- const pxDir = path3.join(targetDir, "pxExpress");
1947
- const pxIndexPath = path3.join(pxDir, "index.ts");
2055
+ const pxDir = path2.join(targetDir, "pxExpress");
2056
+ const pxIndexPath = path2.join(pxDir, "index.ts");
1948
2057
  if (!files.some((f) => f.absolutePath === pxIndexPath)) {
1949
2058
  const pxIndexContent = renderTemplate(expressIndexTemplate, { routePath });
1950
2059
  files.push(
@@ -1970,6 +2079,7 @@ function generateFeature(options) {
1970
2079
  // src/commands/feature.ts
1971
2080
  async function runFeatureCommand(featureNameArg, flags = {}) {
1972
2081
  const projectRoot = findProjectRoot();
2082
+ const isRegistry = getEffectiveMode(projectRoot, flags) === "registry";
1973
2083
  let name = featureNameArg?.trim();
1974
2084
  if (!name && flags.yes) {
1975
2085
  name = "feature";
@@ -1987,9 +2097,10 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1987
2097
  ).trim();
1988
2098
  }
1989
2099
  const normalizedName = toCamelCase(name);
1990
- const defaultDir = path3.relative(projectRoot, path3.join(resolveFeaturesDir(projectRoot), normalizedName));
2100
+ const baseFeaturesDir = resolveFeaturesDir(projectRoot, flags);
2101
+ const defaultDir = path2.relative(projectRoot, path2.join(baseFeaturesDir, normalizedName));
1991
2102
  let targetDirRel = defaultDir;
1992
- if (!flags.yes && !flags.target) {
2103
+ if (!flags.yes && !flags.target && !flags.dest) {
1993
2104
  targetDirRel = handleCancel(
1994
2105
  await p4.text({
1995
2106
  message: "Target directory for the feature:",
@@ -1998,7 +2109,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1998
2109
  })
1999
2110
  ).trim();
2000
2111
  }
2001
- const targetDir = flags.target ? path3.resolve(projectRoot, flags.target) : path3.resolve(projectRoot, targetDirRel);
2112
+ const targetDir = flags.dest ? path2.resolve(projectRoot, flags.dest) : flags.target ? path2.resolve(projectRoot, flags.target) : path2.resolve(projectRoot, targetDirRel);
2002
2113
  let includeExpress = flags.express !== void 0 ? Boolean(flags.express) : true;
2003
2114
  let routePath = `/${normalizedName}`;
2004
2115
  let includeDomain = true;
@@ -2219,7 +2330,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2219
2330
  }
2220
2331
  }
2221
2332
  const s = p4.spinner();
2222
- s.start(`Generating feature '${pc13.cyan(normalizedName)}'...`);
2333
+ s.start(`Generating feature '${pc14.cyan(normalizedName)}'...`);
2223
2334
  const options = {
2224
2335
  name: normalizedName,
2225
2336
  projectRoot,
@@ -2239,9 +2350,207 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2239
2350
  includeDiProvider
2240
2351
  };
2241
2352
  const files = generateFeature(options);
2353
+ if (isRegistry) {
2354
+ const registryPath = path2.join(targetDir, "registry.json");
2355
+ if (!fs4.existsSync(registryPath)) {
2356
+ const manifest = {
2357
+ name: toKebabCase(normalizedName),
2358
+ type: "feature",
2359
+ description: flags.description || `${normalizedName} feature module`,
2360
+ version: "1.0.0",
2361
+ dependencies: {
2362
+ npm: includeExpress ? ["@solid-stack/agnos-express", "express"] : [],
2363
+ shared: [],
2364
+ features: []
2365
+ },
2366
+ files: []
2367
+ };
2368
+ const content = JSON.stringify(manifest, null, 2) + "\n";
2369
+ const genFile = writeFileSafely(registryPath, content, {
2370
+ projectRoot,
2371
+ overwrite: options.overwrite,
2372
+ dryRun: options.dryRun
2373
+ });
2374
+ files.push(genFile);
2375
+ }
2376
+ }
2242
2377
  s.stop("Feature scaffolded successfully!");
2243
2378
  printGeneratedFiles(files, options.dryRun);
2244
- p4.outro(pc13.green(`Feature '${normalizedName}' ready to use!`));
2379
+ p4.outro(
2380
+ pc14.green(
2381
+ `\u2713 Feature '${normalizedName}' initialized in ${path2.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
2382
+ )
2383
+ );
2384
+ }
2385
+ function generateShared(options) {
2386
+ const files = [];
2387
+ const projectRoot = options.projectRoot || findProjectRoot();
2388
+ const rawName = options.name.trim();
2389
+ const pascalName = toPascalCase(rawName);
2390
+ const camelName = toCamelCase(rawName);
2391
+ const kebabName = toKebabCase(rawName);
2392
+ const targetDir = options.targetDir || path2.join(resolveSharedDir(projectRoot), kebabName);
2393
+ const fileDefs = [];
2394
+ const facadeContent = `import { MakeInjectable, type DepsType } from "@solid-stack/di";
2395
+ import { I${pascalName}Engine } from "./ports/I${pascalName}Engine.js";
2396
+
2397
+ @MakeInjectable
2398
+ export class ${pascalName} {
2399
+ public static deps = { ${camelName}Engine: I${pascalName}Engine };
2400
+ constructor(public readonly deps: DepsType<typeof ${pascalName}.deps>) {}
2401
+ }
2402
+ `;
2403
+ fileDefs.push({
2404
+ absolutePath: path2.join(targetDir, `${pascalName}.ts`),
2405
+ content: facadeContent
2406
+ });
2407
+ const portContent = `export abstract class I${pascalName}Engine {
2408
+ // Define abstract port contracts for ${pascalName} here
2409
+ }
2410
+ `;
2411
+ fileDefs.push({
2412
+ absolutePath: path2.join(targetDir, "ports", `I${pascalName}Engine.ts`),
2413
+ content: portContent
2414
+ });
2415
+ const concreteContent = `import { MakeInjectable } from "@solid-stack/di";
2416
+ import { I${pascalName}Engine } from "../ports/I${pascalName}Engine.js";
2417
+
2418
+ @MakeInjectable
2419
+ export class Default${pascalName}Engine implements I${pascalName}Engine {
2420
+ // Default concrete implementation for production
2421
+ }
2422
+ `;
2423
+ fileDefs.push({
2424
+ absolutePath: path2.join(targetDir, "infrastructure", `Default${pascalName}Engine.ts`),
2425
+ content: concreteContent
2426
+ });
2427
+ if (options.includeStub !== false) {
2428
+ const stubContent = `import { MakeInjectable } from "@solid-stack/di";
2429
+ import { I${pascalName}Engine } from "../ports/I${pascalName}Engine.js";
2430
+
2431
+ @MakeInjectable
2432
+ export class Stub${pascalName}Engine implements I${pascalName}Engine {
2433
+ // In-memory or stub implementation for testing
2434
+ }
2435
+ `;
2436
+ fileDefs.push({
2437
+ absolutePath: path2.join(targetDir, "infrastructure", `Stub${pascalName}Engine.ts`),
2438
+ content: stubContent
2439
+ });
2440
+ }
2441
+ const providerContent = `import type { Container } from "@solid-stack/di";
2442
+ import { I${pascalName}Engine } from "./ports/I${pascalName}Engine.js";
2443
+ import { Default${pascalName}Engine } from "./infrastructure/Default${pascalName}Engine.js";
2444
+
2445
+ export const diProvider = (c: Container): void => {
2446
+ c.provide(I${pascalName}Engine, Default${pascalName}Engine);
2447
+ };
2448
+
2449
+ export default diProvider;
2450
+ `;
2451
+ fileDefs.push({
2452
+ absolutePath: path2.join(targetDir, "diProvider.ts"),
2453
+ content: providerContent
2454
+ });
2455
+ if (options.isRegistry) {
2456
+ const registryManifest = {
2457
+ name: kebabName,
2458
+ type: "shared",
2459
+ description: options.description || `${kebabName} shared module`,
2460
+ version: "1.0.0",
2461
+ dependencies: {
2462
+ npm: [],
2463
+ shared: [],
2464
+ features: []
2465
+ },
2466
+ files: []
2467
+ };
2468
+ fileDefs.push({
2469
+ absolutePath: path2.join(targetDir, "registry.json"),
2470
+ content: JSON.stringify(registryManifest, null, 2) + "\n"
2471
+ });
2472
+ }
2473
+ for (const def of fileDefs) {
2474
+ const genFile = writeFileSafely(def.absolutePath, def.content, {
2475
+ projectRoot,
2476
+ overwrite: options.overwrite,
2477
+ dryRun: options.dryRun
2478
+ });
2479
+ files.push(genFile);
2480
+ }
2481
+ return files;
2482
+ }
2483
+
2484
+ // src/commands/shared.ts
2485
+ async function runSharedCommand(sharedNameArg, flags = {}) {
2486
+ const projectRoot = findProjectRoot();
2487
+ const isRegistry = getEffectiveMode(projectRoot, flags) === "registry";
2488
+ let name = sharedNameArg?.trim();
2489
+ if (!name && flags.yes) {
2490
+ name = "shared";
2491
+ }
2492
+ if (!name) {
2493
+ name = handleCancel(
2494
+ await p4.text({
2495
+ message: "What is the shared module name?",
2496
+ placeholder: "e.g. hasher, time, uuid, cache, logger",
2497
+ validate: (val) => {
2498
+ if (!val || !val.trim()) return "Shared module name cannot be empty.";
2499
+ return void 0;
2500
+ }
2501
+ })
2502
+ ).trim();
2503
+ }
2504
+ const normalizedName = toKebabCase(name);
2505
+ const baseSharedDir = resolveSharedDir(projectRoot, flags);
2506
+ const defaultDir = path2.relative(projectRoot, path2.join(baseSharedDir, normalizedName));
2507
+ let targetDirRel = defaultDir;
2508
+ if (!flags.yes && !flags.target && !flags.dest) {
2509
+ targetDirRel = handleCancel(
2510
+ await p4.text({
2511
+ message: "Target directory for the shared module:",
2512
+ initialValue: defaultDir,
2513
+ defaultValue: defaultDir
2514
+ })
2515
+ ).trim();
2516
+ }
2517
+ const targetDir = flags.dest ? path2.resolve(projectRoot, flags.dest) : flags.target ? path2.resolve(projectRoot, flags.target) : path2.resolve(projectRoot, targetDirRel);
2518
+ let description = flags.description || "";
2519
+ let includeStub = true;
2520
+ if (!flags.yes) {
2521
+ description = handleCancel(
2522
+ await p4.text({
2523
+ message: "Module description (optional):",
2524
+ placeholder: "e.g. In-memory caching and Redis port abstraction",
2525
+ initialValue: ""
2526
+ })
2527
+ ).trim();
2528
+ includeStub = handleCancel(
2529
+ await p4.confirm({
2530
+ message: "Include stub implementation in infrastructure/?",
2531
+ initialValue: true
2532
+ })
2533
+ );
2534
+ }
2535
+ const s = p4.spinner();
2536
+ s.start(`Generating shared module '${pc14.cyan(normalizedName)}'...`);
2537
+ const files = generateShared({
2538
+ name: normalizedName,
2539
+ description,
2540
+ projectRoot,
2541
+ targetDir,
2542
+ dryRun: Boolean(flags.dryRun),
2543
+ overwrite: Boolean(flags.overwrite),
2544
+ includeStub,
2545
+ isRegistry
2546
+ });
2547
+ s.stop("Shared module generated successfully!");
2548
+ printGeneratedFiles(files, Boolean(flags.dryRun));
2549
+ p4.outro(
2550
+ pc14.green(
2551
+ `\u2713 Shared module '${normalizedName}' initialized in ${path2.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
2552
+ )
2553
+ );
2245
2554
  }
2246
2555
  async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2247
2556
  const projectRoot = findProjectRoot();
@@ -2366,7 +2675,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2366
2675
  }
2367
2676
  }
2368
2677
  const s = p4.spinner();
2369
- s.start(`Generating use case '${pc13.cyan(ucName)}'...`);
2678
+ s.start(`Generating use case '${pc14.cyan(ucName)}'...`);
2370
2679
  const options = {
2371
2680
  name: ucName,
2372
2681
  feature,
@@ -2398,7 +2707,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2398
2707
  }
2399
2708
  s.stop("Use case generated successfully!");
2400
2709
  printGeneratedFiles(files, options.dryRun);
2401
- p4.outro(pc13.green(`Use case '${ucName}' created in feature '${feature}'!`));
2710
+ p4.outro(pc14.green(`Use case '${ucName}' created in feature '${feature}'!`));
2402
2711
  }
2403
2712
  async function runDomainCommand(domainNameArg, flags = {}) {
2404
2713
  const projectRoot = findProjectRoot();
@@ -2469,7 +2778,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2469
2778
  });
2470
2779
  }
2471
2780
  const s = p4.spinner();
2472
- s.start(`Generating domain model '${pc13.cyan(entityName)}'...`);
2781
+ s.start(`Generating domain model '${pc14.cyan(entityName)}'...`);
2473
2782
  const options = {
2474
2783
  name: entityName,
2475
2784
  feature,
@@ -2482,7 +2791,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2482
2791
  const file = generateDomain(options);
2483
2792
  s.stop("Domain model generated successfully!");
2484
2793
  printGeneratedFiles([file], options.dryRun);
2485
- p4.outro(pc13.green(`Domain model '${entityName}' created in feature '${feature}'!`));
2794
+ p4.outro(pc14.green(`Domain model '${entityName}' created in feature '${feature}'!`));
2486
2795
  }
2487
2796
  async function runInfrastructureCommand(classNameArg, flags = {}) {
2488
2797
  const projectRoot = findProjectRoot();
@@ -2651,7 +2960,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2651
2960
  registerInProvider = !flags.noProvider;
2652
2961
  }
2653
2962
  const s = p4.spinner();
2654
- s.start(`Generating infrastructure class '${pc13.cyan(className)}'...`);
2963
+ s.start(`Generating infrastructure class '${pc14.cyan(className)}'...`);
2655
2964
  const files = [];
2656
2965
  const options = {
2657
2966
  name: className,
@@ -2666,7 +2975,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2666
2975
  const infraFile = generateInfrastructure(options);
2667
2976
  files.push(infraFile);
2668
2977
  if (registerInProvider && scope === "feature" && feature && implementsInterface) {
2669
- const featureDir = path3.join(resolveFeaturesDir(projectRoot), feature);
2978
+ const featureDir = path2.join(resolveFeaturesDir(projectRoot), feature);
2670
2979
  const providerFile = updateOrGenerateDiProvider(
2671
2980
  featureDir,
2672
2981
  feature,
@@ -2686,7 +2995,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2686
2995
  }
2687
2996
  s.stop("Infrastructure class generated successfully!");
2688
2997
  printGeneratedFiles(files, options.dryRun);
2689
- p4.outro(pc13.green(`Infrastructure class '${className}' created!`));
2998
+ p4.outro(pc14.green(`Infrastructure class '${className}' created!`));
2690
2999
  }
2691
3000
  async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2692
3001
  const projectRoot = findProjectRoot();
@@ -2799,7 +3108,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2799
3108
  }
2800
3109
  }
2801
3110
  const s = p4.spinner();
2802
- s.start(`Generating interface '${pc13.cyan(ifaceName)}'...`);
3111
+ s.start(`Generating interface '${pc14.cyan(ifaceName)}'...`);
2803
3112
  const files = [];
2804
3113
  const options = {
2805
3114
  name: ifaceName,
@@ -2828,7 +3137,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2828
3137
  files.push(stubFile);
2829
3138
  }
2830
3139
  if (registerInProvider && generateStub) {
2831
- const featureDir = path3.join(resolveFeaturesDir(projectRoot), feature);
3140
+ const featureDir = path2.join(resolveFeaturesDir(projectRoot), feature);
2832
3141
  const providerFile = updateOrGenerateDiProvider(
2833
3142
  featureDir,
2834
3143
  feature,
@@ -2848,7 +3157,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2848
3157
  }
2849
3158
  s.stop("Interface generated successfully!");
2850
3159
  printGeneratedFiles(files, options.dryRun);
2851
- p4.outro(pc13.green(`Interface '${ifaceName}' created in feature '${feature}'!`));
3160
+ p4.outro(pc14.green(`Interface '${ifaceName}' created in feature '${feature}'!`));
2852
3161
  }
2853
3162
  async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2854
3163
  const projectRoot = findProjectRoot();
@@ -2965,7 +3274,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2965
3274
  }
2966
3275
  }
2967
3276
  const s = p4.spinner();
2968
- s.start(`Generating HTTP handler '${pc13.cyan(className)}'...`);
3277
+ s.start(`Generating HTTP handler '${pc14.cyan(className)}'...`);
2969
3278
  const options = {
2970
3279
  name: className,
2971
3280
  feature,
@@ -2983,7 +3292,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2983
3292
  const files = generateHttpHandler(options);
2984
3293
  s.stop("HTTP handler generated successfully!");
2985
3294
  printGeneratedFiles(files, options.dryRun);
2986
- p4.outro(pc13.green(`HTTP handler '${className}' created in feature '${feature}'!`));
3295
+ p4.outro(pc14.green(`HTTP handler '${className}' created in feature '${feature}'!`));
2987
3296
  }
2988
3297
  function generateInjectableCode(className, dependencies = [], methods = [], implementsInterface) {
2989
3298
  const formattedDeps = dependencies.map((dep) => ({
@@ -2993,7 +3302,7 @@ function generateInjectableCode(className, dependencies = [], methods = [], impl
2993
3302
  const formattedMethods = methods.map((m) => {
2994
3303
  const isAsync = m.async !== false;
2995
3304
  const retType = m.returnType ? isAsync ? `Promise<${m.returnType}>` : m.returnType : isAsync ? "Promise<void>" : "void";
2996
- const params = m.params && m.params.length > 0 ? m.params.map((p15) => `${p15.name}: ${p15.type}`).join(", ") : "";
3305
+ const params = m.params && m.params.length > 0 ? m.params.map((p16) => `${p16.name}: ${p16.type}`).join(", ") : "";
2997
3306
  return {
2998
3307
  name: m.name,
2999
3308
  isAsync,
@@ -3016,21 +3325,21 @@ function generateInjectable(options) {
3016
3325
  const className = toPascalCase(options.name);
3017
3326
  let targetDir = "";
3018
3327
  if (options.customPath) {
3019
- targetDir = path3.isAbsolute(options.customPath) ? options.customPath : path3.join(projectRoot, options.customPath);
3328
+ targetDir = path2.isAbsolute(options.customPath) ? options.customPath : path2.join(projectRoot, options.customPath);
3020
3329
  } else if (options.location === "feature-service" && options.feature) {
3021
- targetDir = path3.join(resolveFeaturesDir(projectRoot), options.feature, "services");
3330
+ targetDir = path2.join(resolveFeaturesDir(projectRoot), options.feature, "services");
3022
3331
  } else if (options.location === "feature-transformer" && options.feature) {
3023
- targetDir = path3.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress", "transformers");
3332
+ targetDir = path2.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress", "transformers");
3024
3333
  } else if (options.location === "module") {
3025
- targetDir = path3.join(projectRoot, "modules", className);
3334
+ targetDir = path2.join(projectRoot, "modules", className);
3026
3335
  } else {
3027
- targetDir = options.feature ? path3.join(resolveFeaturesDir(projectRoot), options.feature, "services") : path3.join(projectRoot, "shared", className.toLowerCase());
3336
+ targetDir = options.feature ? path2.join(resolveFeaturesDir(projectRoot), options.feature, "services") : path2.join(projectRoot, "shared", className.toLowerCase());
3028
3337
  }
3029
- const filePath = path3.join(targetDir, `${className}.ts`);
3338
+ const filePath = path2.join(targetDir, `${className}.ts`);
3030
3339
  let ifaceName;
3031
3340
  if (options.generateInterface) {
3032
3341
  ifaceName = `I${className}`;
3033
- const ifacePath = path3.join(targetDir, `${ifaceName}.ts`);
3342
+ const ifacePath = path2.join(targetDir, `${ifaceName}.ts`);
3034
3343
  const ifaceContent = renderTemplate(injectableInterfaceTemplate, { ifaceName });
3035
3344
  files.push(
3036
3345
  writeFileSafely(ifacePath, ifaceContent, {
@@ -3139,7 +3448,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3139
3448
  );
3140
3449
  }
3141
3450
  const s = p4.spinner();
3142
- s.start(`Generating injectable '${pc13.cyan(className)}'...`);
3451
+ s.start(`Generating injectable '${pc14.cyan(className)}'...`);
3143
3452
  const options = {
3144
3453
  name: className,
3145
3454
  feature,
@@ -3153,7 +3462,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3153
3462
  const files = generateInjectable(options);
3154
3463
  s.stop("Injectable class generated successfully!");
3155
3464
  printGeneratedFiles(files, options.dryRun);
3156
- p4.outro(pc13.green(`Injectable '${className}' created successfully!`));
3465
+ p4.outro(pc14.green(`Injectable '${className}' created successfully!`));
3157
3466
  }
3158
3467
  async function runInteractiveMenu(flags = {}) {
3159
3468
  printBanner();
@@ -3166,6 +3475,11 @@ async function runInteractiveMenu(flags = {}) {
3166
3475
  label: "Feature",
3167
3476
  hint: "Complete vertical slice (domain, infra, usecase, pxExpress, diProvider)"
3168
3477
  },
3478
+ {
3479
+ value: "shared",
3480
+ label: "Shared Module",
3481
+ hint: "Foundational utility or port with ports, infrastructure & diProvider"
3482
+ },
3169
3483
  {
3170
3484
  value: "usecase",
3171
3485
  label: "Use Case",
@@ -3203,6 +3517,9 @@ async function runInteractiveMenu(flags = {}) {
3203
3517
  case "feature":
3204
3518
  await runFeatureCommand(void 0, flags);
3205
3519
  break;
3520
+ case "shared":
3521
+ await runSharedCommand(void 0, flags);
3522
+ break;
3206
3523
  case "usecase":
3207
3524
  await runUseCaseCommand(void 0, flags);
3208
3525
  break;
@@ -3235,6 +3552,9 @@ async function handleCreate(type, name, flags = {}) {
3235
3552
  case "feature":
3236
3553
  await runFeatureCommand(name, flags);
3237
3554
  break;
3555
+ case "shared":
3556
+ await runSharedCommand(name, flags);
3557
+ break;
3238
3558
  case "usecase":
3239
3559
  await runUseCaseCommand(name, flags);
3240
3560
  break;
@@ -3256,20 +3576,171 @@ async function handleCreate(type, name, flags = {}) {
3256
3576
  break;
3257
3577
  default:
3258
3578
  p4.log.error(
3259
- pc13.red(
3260
- `Unknown component type: "${type}". Allowed types: feature, usecase, domain, infrastructure, interface, httpHandler, injectable.`
3579
+ pc14.red(
3580
+ `Unknown component type: "${type}". Allowed types: feature, shared, usecase, domain, infrastructure, interface, httpHandler, injectable.`
3261
3581
  )
3262
3582
  );
3263
3583
  process.exit(1);
3264
3584
  }
3265
3585
  }
3586
+ var importPrompts = {
3587
+ select: p4.select,
3588
+ text: p4.text
3589
+ };
3590
+ function rewriteImports(content, aliasMap) {
3591
+ if (aliasMap.size === 0) return content;
3592
+ let rewritten = content;
3593
+ for (const [oldImport, newImport] of aliasMap.entries()) {
3594
+ const rawOld = oldImport.endsWith("/") ? oldImport.slice(0, -1) : oldImport;
3595
+ const rawNew = newImport.endsWith("/") ? newImport.slice(0, -1) : newImport;
3596
+ const escaped = rawOld.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3597
+ const regex = new RegExp(`${escaped}(?=[\\/'"\`]|$)`, "g");
3598
+ rewritten = rewritten.replace(regex, rawNew);
3599
+ }
3600
+ return rewritten;
3601
+ }
3602
+ function getModuleBaseDir(projectRoot, modType) {
3603
+ const folder = modType === "shared" ? "shared" : "features";
3604
+ return path2.join(projectRoot, "src", folder);
3605
+ }
3606
+ function getExistingModuleDir(projectRoot, modType, modName) {
3607
+ const folder = modType === "shared" ? "shared" : "features";
3608
+ const inSrc = path2.join(projectRoot, "src", folder, modName);
3609
+ if (fs4.existsSync(inSrc)) return inSrc;
3610
+ const inRoot = path2.join(projectRoot, folder, modName);
3611
+ if (fs4.existsSync(inRoot)) return inRoot;
3612
+ return null;
3613
+ }
3614
+ function checkModuleExists(projectRoot, modType, modName) {
3615
+ return getExistingModuleDir(projectRoot, modType, modName) !== null;
3616
+ }
3617
+ async function resolveDependency(depType, depName, aliasMap, projectRoot, flags, importModuleRecursive, importedModules, registryBase, index) {
3618
+ const folder = depType === "shared" ? "shared" : "features";
3619
+ const isAlreadyImportedThisRun = importedModules.has(`${depType}:${depName}`);
3620
+ if (isAlreadyImportedThisRun) return;
3621
+ const programmaticAlias = flags.alias?.[depName] || flags.alias?.[`${depType}/${depName}`];
3622
+ if (programmaticAlias) {
3623
+ aliasMap.set(`@/${folder}/${depName}/`, `@/${folder}/${programmaticAlias}/`);
3624
+ const baseDir = getModuleBaseDir(projectRoot, depType);
3625
+ const displayDest = path2.relative(
3626
+ projectRoot,
3627
+ path2.join(baseDir, programmaticAlias)
3628
+ );
3629
+ p4.log.step(`Importing '${depName}' into '${displayDest}'...`);
3630
+ await importModuleRecursive(depType, depName, programmaticAlias);
3631
+ return;
3632
+ }
3633
+ const existingDir = getExistingModuleDir(projectRoot, depType, depName);
3634
+ if (!existingDir) {
3635
+ p4.log.info(` \u21B3 Resolving required ${depType} dependency: ${pc14.cyan(depName)}`);
3636
+ await importModuleRecursive(depType, depName, depName);
3637
+ return;
3638
+ }
3639
+ const entry = (depType === "shared" ? index?.shared : index?.features)?.find(
3640
+ (item) => item.name === depName
3641
+ );
3642
+ let remoteIntegrity = entry?.integrity;
3643
+ if (!remoteIntegrity) {
3644
+ try {
3645
+ const depManifest = await fetchModuleManifest(registryBase, depType, depName);
3646
+ remoteIntegrity = depManifest.integrity;
3647
+ } catch {
3648
+ }
3649
+ }
3650
+ const localHash = computeDirectoryHash(existingDir);
3651
+ if (remoteIntegrity && localHash === remoteIntegrity) {
3652
+ p4.log.step(`\u2713 ${depType}/${depName} is up to date (reusing existing)`);
3653
+ importedModules.add(`${depType}:${depName}`);
3654
+ return;
3655
+ }
3656
+ if (flags.overwrite) {
3657
+ p4.log.step(`Overwriting '${depType}/${depName}' with registry version (--overwrite)...`);
3658
+ const baseDir = getModuleBaseDir(projectRoot, depType);
3659
+ const targetDir = path2.join(baseDir, depName);
3660
+ if (!flags.dryRun) {
3661
+ if (existingDir !== targetDir) {
3662
+ fs4.rmSync(existingDir, { recursive: true, force: true });
3663
+ }
3664
+ fs4.rmSync(targetDir, { recursive: true, force: true });
3665
+ }
3666
+ await importModuleRecursive(depType, depName, depName, { overwrite: true });
3667
+ return;
3668
+ }
3669
+ if (flags.yes) {
3670
+ p4.log.info(`Using existing '${depType}/${depName}'`);
3671
+ importedModules.add(`${depType}:${depName}`);
3672
+ return;
3673
+ }
3674
+ const choice = handleCancel(
3675
+ await importPrompts.select({
3676
+ message: `Local '${depType}/${depName}' differs from the registry version.`,
3677
+ options: [
3678
+ {
3679
+ value: "keep_existing",
3680
+ label: `Keep existing local version (Recommended)`
3681
+ },
3682
+ {
3683
+ value: "rename",
3684
+ label: "Import fresh copy under a new name (alias)"
3685
+ },
3686
+ {
3687
+ value: "overwrite",
3688
+ label: "Overwrite local version with registry version"
3689
+ }
3690
+ ]
3691
+ })
3692
+ );
3693
+ if (choice === "keep_existing" || choice === "use_existing") {
3694
+ p4.log.step(`Skipping '${depType}/${depName}' (keeping existing)`);
3695
+ importedModules.add(`${depType}:${depName}`);
3696
+ return;
3697
+ }
3698
+ if (choice === "rename") {
3699
+ const newName = handleCancel(
3700
+ await importPrompts.text({
3701
+ message: "What should we name the new copy?",
3702
+ placeholder: `${depName}-copy`,
3703
+ validate(val) {
3704
+ const v = val?.trim();
3705
+ if (!v) return "Name cannot be empty";
3706
+ if (v === depName) return `Name must be different from '${depName}'`;
3707
+ if (!/^[a-zA-Z0-9_-]+$/.test(v)) {
3708
+ return "Name can only contain alphanumeric characters, dashes, and underscores";
3709
+ }
3710
+ if (checkModuleExists(projectRoot, depType, v)) {
3711
+ return `'${v}' already exists in ${folder}, please choose another name`;
3712
+ }
3713
+ }
3714
+ })
3715
+ ).trim();
3716
+ aliasMap.set(`@/${folder}/${depName}/`, `@/${folder}/${newName}/`);
3717
+ const baseDir = getModuleBaseDir(projectRoot, depType);
3718
+ const displayDest = path2.relative(projectRoot, path2.join(baseDir, newName));
3719
+ p4.log.step(`Importing '${depName}' into '${displayDest}'...`);
3720
+ await importModuleRecursive(depType, depName, newName);
3721
+ return;
3722
+ }
3723
+ if (choice === "overwrite") {
3724
+ p4.log.step(`Overwriting '${depType}/${depName}' with registry version...`);
3725
+ const baseDir = getModuleBaseDir(projectRoot, depType);
3726
+ const targetDir = path2.join(baseDir, depName);
3727
+ if (!flags.dryRun) {
3728
+ if (existingDir !== targetDir) {
3729
+ fs4.rmSync(existingDir, { recursive: true, force: true });
3730
+ }
3731
+ fs4.rmSync(targetDir, { recursive: true, force: true });
3732
+ }
3733
+ await importModuleRecursive(depType, depName, depName, { overwrite: true });
3734
+ return;
3735
+ }
3736
+ }
3266
3737
  async function handleImport(typeArg, nameArg, flags = {}) {
3267
3738
  const projectRoot = findProjectRoot();
3268
3739
  let type = typeArg?.trim().toLowerCase();
3269
3740
  if (type === "features") type = "feature";
3270
3741
  if (!type) {
3271
3742
  type = handleCancel(
3272
- await p4.select({
3743
+ await importPrompts.select({
3273
3744
  message: "What kind of module would you like to import?",
3274
3745
  options: [
3275
3746
  {
@@ -3287,19 +3758,19 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3287
3758
  );
3288
3759
  }
3289
3760
  if (type !== "shared" && type !== "feature") {
3290
- p4.log.error(pc13.red(`Invalid module type: "${typeArg}". Must be "shared" or "feature".`));
3761
+ p4.log.error(pc14.red(`Invalid module type: "${typeArg}". Must be "shared" or "feature".`));
3291
3762
  process.exit(1);
3292
3763
  }
3293
3764
  const registryBase = getRegistryBase(projectRoot, flags.registry);
3294
3765
  const s = p4.spinner();
3295
- s.start(`Connecting to registry at ${pc13.dim(registryBase)}...`);
3766
+ s.start(`Connecting to registry at ${pc14.dim(registryBase)}...`);
3296
3767
  let index;
3297
3768
  try {
3298
3769
  index = await fetchRegistryIndex(registryBase);
3299
3770
  s.stop("Registry index loaded.");
3300
3771
  } catch (err) {
3301
- s.stop(pc13.red("Failed to load registry index."));
3302
- p4.log.error(pc13.red(err.message || String(err)));
3772
+ s.stop(pc14.red("Failed to load registry index."));
3773
+ p4.log.error(pc14.red(err.message || String(err)));
3303
3774
  process.exit(1);
3304
3775
  }
3305
3776
  const availableList = type === "shared" ? index.shared : index.features;
@@ -3311,7 +3782,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3311
3782
  return;
3312
3783
  }
3313
3784
  name = handleCancel(
3314
- await p4.select({
3785
+ await importPrompts.select({
3315
3786
  message: `Select a ${type} to import:`,
3316
3787
  options: availableList.map((item) => ({
3317
3788
  value: item.name,
@@ -3323,71 +3794,94 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3323
3794
  }
3324
3795
  if (!candidateNames.includes(name)) {
3325
3796
  const closest = findClosestMatch(name, candidateNames);
3326
- p4.log.error(pc13.red(`\u274C Module '${name}' not found in the ${type} registry.`));
3797
+ p4.log.error(pc14.red(`\u274C Module '${name}' not found in the ${type} registry.`));
3327
3798
  if (closest) {
3328
- p4.log.info(pc13.yellow(`Did you mean '${pc13.cyan(closest)}'?`));
3799
+ p4.log.info(pc14.yellow(`Did you mean '${pc14.cyan(closest)}'?`));
3329
3800
  }
3330
3801
  process.exit(1);
3331
3802
  }
3332
3803
  const importedModules = /* @__PURE__ */ new Set();
3333
3804
  const totalWrittenFiles = [];
3334
3805
  const requiredNpmPackages = /* @__PURE__ */ new Set();
3335
- async function importModuleRecursive(modType, modName) {
3336
- const modKey = `${modType}:${modName}`;
3806
+ async function importModuleRecursive(modType, registryName, targetName = registryName, options) {
3807
+ const modKey = `${modType}:${targetName}`;
3337
3808
  if (importedModules.has(modKey)) return;
3338
- p4.log.step(`Importing ${modType}: ${pc13.cyan(modName)}...`);
3809
+ p4.log.step(
3810
+ `Importing ${modType}: ${pc14.cyan(targetName)}${targetName !== registryName ? pc14.dim(` (from ${registryName})`) : ""}...`
3811
+ );
3339
3812
  let manifest;
3340
3813
  try {
3341
- manifest = await fetchModuleManifest(registryBase, modType, modName);
3814
+ manifest = await fetchModuleManifest(registryBase, modType, registryName);
3342
3815
  } catch (err) {
3343
- p4.log.error(pc13.red(`Failed to fetch manifest for ${modType} '${modName}': ${err.message}`));
3816
+ p4.log.error(pc14.red(`Failed to fetch manifest for ${modType} '${registryName}': ${err.message}`));
3344
3817
  process.exit(1);
3345
3818
  }
3819
+ const aliasMap = /* @__PURE__ */ new Map();
3820
+ if (targetName !== registryName) {
3821
+ const folder2 = modType === "shared" ? "shared" : "features";
3822
+ aliasMap.set(`@/${folder2}/${registryName}/`, `@/${folder2}/${targetName}/`);
3823
+ }
3346
3824
  const sharedDeps = manifest.dependencies?.shared || [];
3347
3825
  for (const dep of sharedDeps) {
3348
- const depPath = path3.join(projectRoot, "src", "shared", dep);
3349
- if (!fs3.existsSync(depPath) && !importedModules.has(`shared:${dep}`)) {
3350
- p4.log.info(` \u21B3 Resolving required shared dependency: ${pc13.cyan(dep)}`);
3351
- await importModuleRecursive("shared", dep);
3352
- }
3826
+ await resolveDependency(
3827
+ "shared",
3828
+ dep,
3829
+ aliasMap,
3830
+ projectRoot,
3831
+ flags,
3832
+ importModuleRecursive,
3833
+ importedModules,
3834
+ registryBase,
3835
+ index
3836
+ );
3353
3837
  }
3354
3838
  const featureDeps = manifest.dependencies?.features || [];
3355
3839
  for (const dep of featureDeps) {
3356
- const depPath = path3.join(projectRoot, "src", "features", dep);
3357
- if (!fs3.existsSync(depPath) && !importedModules.has(`feature:${dep}`)) {
3358
- p4.log.info(` \u21B3 Resolving required feature dependency: ${pc13.cyan(dep)}`);
3359
- await importModuleRecursive("feature", dep);
3360
- }
3840
+ await resolveDependency(
3841
+ "feature",
3842
+ dep,
3843
+ aliasMap,
3844
+ projectRoot,
3845
+ flags,
3846
+ importModuleRecursive,
3847
+ importedModules,
3848
+ registryBase,
3849
+ index
3850
+ );
3361
3851
  }
3362
3852
  for (const pkg of manifest.dependencies?.npm || []) {
3363
3853
  requiredNpmPackages.add(pkg);
3364
3854
  }
3365
3855
  const folder = modType === "shared" ? "shared" : "features";
3366
- const targetDir = path3.join(projectRoot, "src", folder, modName);
3856
+ const targetDir = path2.join(getModuleBaseDir(projectRoot, modType), targetName);
3857
+ const isOverwrite = Boolean(flags.overwrite || options?.overwrite);
3367
3858
  for (const file of manifest.files || []) {
3368
3859
  if (file === "registry.json") continue;
3369
- const remoteFilePath = `${folder}/${modName}/${file}`;
3370
- const localFilePath = path3.join(targetDir, file);
3371
- if (fs3.existsSync(localFilePath) && !flags.overwrite) {
3372
- p4.log.warn(` \u21B3 Skipping existing file: ${pc13.dim(path3.relative(projectRoot, localFilePath))} (use --overwrite to replace)`);
3860
+ const remoteFilePath = `${folder}/${registryName}/${file}`;
3861
+ const localFilePath = path2.join(targetDir, file);
3862
+ if (fs4.existsSync(localFilePath) && !isOverwrite) {
3863
+ p4.log.warn(
3864
+ ` \u21B3 Skipping existing file: ${pc14.dim(path2.relative(projectRoot, localFilePath))} (use --overwrite to replace)`
3865
+ );
3373
3866
  continue;
3374
3867
  }
3375
- const content = await fetchRegistryFile(registryBase, remoteFilePath);
3868
+ let content = await fetchRegistryFile(registryBase, remoteFilePath);
3869
+ content = rewriteImports(content, aliasMap);
3376
3870
  if (!flags.dryRun) {
3377
- fs3.mkdirSync(path3.dirname(localFilePath), { recursive: true });
3378
- fs3.writeFileSync(localFilePath, content, "utf-8");
3871
+ fs4.mkdirSync(path2.dirname(localFilePath), { recursive: true });
3872
+ fs4.writeFileSync(localFilePath, content, "utf-8");
3379
3873
  }
3380
- const displayPath = path3.relative(projectRoot, localFilePath);
3874
+ const displayPath = path2.relative(projectRoot, localFilePath);
3381
3875
  totalWrittenFiles.push(displayPath);
3382
- p4.log.message(` ${pc13.green("+")} ${displayPath}`);
3876
+ p4.log.message(` ${pc14.green("+")} ${displayPath}`);
3383
3877
  }
3384
3878
  importedModules.add(modKey);
3385
3879
  }
3386
- await importModuleRecursive(type, name);
3880
+ await importModuleRecursive(type, name, name);
3387
3881
  const missingNpm = getMissingNpmPackages(projectRoot, Array.from(requiredNpmPackages));
3388
3882
  if (missingNpm.length > 0) {
3389
3883
  if (flags.skipInstall || flags.dryRun) {
3390
- p4.log.info(`Required npm dependencies to install: ${pc13.cyan(missingNpm.join(", "))}`);
3884
+ p4.log.info(`Required npm dependencies to install: ${pc14.cyan(missingNpm.join(", "))}`);
3391
3885
  } else {
3392
3886
  try {
3393
3887
  installNpmPackages(projectRoot, missingNpm);
@@ -3397,11 +3891,13 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3397
3891
  }
3398
3892
  }
3399
3893
  if (flags.dryRun) {
3400
- p4.log.info(pc13.yellow("Dry run complete: No files were written to disk."));
3894
+ p4.log.info(pc14.yellow("Dry run complete: No files were written to disk."));
3401
3895
  } else {
3896
+ const baseDir = getModuleBaseDir(projectRoot, type);
3897
+ const displayTarget = path2.relative(projectRoot, path2.join(baseDir, name));
3402
3898
  p4.log.success(
3403
- pc13.green(
3404
- `Successfully imported ${type} '${name}' (${totalWrittenFiles.length} file${totalWrittenFiles.length === 1 ? "" : "s"} placed in src/${type === "shared" ? "shared" : "features"}/${name})`
3899
+ pc14.green(
3900
+ `Successfully imported ${type} '${name}' (${totalWrittenFiles.length} file${totalWrittenFiles.length === 1 ? "" : "s"} placed in ${displayTarget})`
3405
3901
  )
3406
3902
  );
3407
3903
  }
@@ -3410,24 +3906,70 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3410
3906
  // src/cli.ts
3411
3907
  function createCli() {
3412
3908
  const cli = cac("mason");
3413
- 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) => {
3909
+ 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) => {
3414
3910
  if (args && args.length > 0) {
3415
3911
  p4.log.error(
3416
- pc13.red(
3912
+ pc14.red(
3417
3913
  `Unknown command: "${args.join(" ")}". Use --help to list available commands.`
3418
3914
  )
3419
3915
  );
3420
3916
  process.exit(1);
3421
3917
  }
3422
3918
  try {
3919
+ const projectRoot = findProjectRoot();
3920
+ const mode = getEffectiveMode(projectRoot, flags);
3921
+ if (mode === "registry") {
3922
+ const choice2 = handleCancel(
3923
+ await p4.select({
3924
+ message: "\u{1F3DB}\uFE0F Mason (Registry Mode): What would you like to scaffold?",
3925
+ options: [
3926
+ {
3927
+ value: "create_feature",
3928
+ label: "Initialize New Feature",
3929
+ hint: "Scaffold a new feature slice under features/"
3930
+ },
3931
+ {
3932
+ value: "create_shared",
3933
+ label: "Initialize New Shared Module",
3934
+ hint: "Scaffold a new shared utility/port under shared/"
3935
+ },
3936
+ {
3937
+ value: "create_usecase",
3938
+ label: "Create New Use Case",
3939
+ hint: "Add a new use case to an existing feature"
3940
+ },
3941
+ {
3942
+ value: "import",
3943
+ label: "Import from Remote Registry",
3944
+ hint: "Download a module from upstream"
3945
+ }
3946
+ ]
3947
+ })
3948
+ );
3949
+ switch (choice2) {
3950
+ case "create_feature":
3951
+ await handleCreate("feature", void 0, flags);
3952
+ break;
3953
+ case "create_shared":
3954
+ await handleCreate("shared", void 0, flags);
3955
+ break;
3956
+ case "create_usecase":
3957
+ await handleCreate("usecase", void 0, flags);
3958
+ break;
3959
+ case "import":
3960
+ await handleImport(void 0, void 0, flags);
3961
+ break;
3962
+ }
3963
+ return;
3964
+ }
3423
3965
  const choice = handleCancel(
3424
3966
  await p4.select({
3425
3967
  message: "\u{1F9F1} Mason: What would you like to do?",
3426
3968
  options: [
3427
3969
  {
3428
3970
  value: "create",
3429
- label: "Create local component",
3430
- hint: "Scaffold a new feature, use case, domain entity, interface, etc."
3971
+ label: "Create component",
3972
+ hint: "Scaffold a new feature, shared module, use case, domain entity, etc."
3431
3973
  },
3432
3974
  {
3433
3975
  value: "import",
@@ -3448,8 +3990,8 @@ function createCli() {
3448
3990
  });
3449
3991
  cli.command(
3450
3992
  "create [type] [name]",
3451
- "Scaffold local Clean Architecture components (feature, usecase, domain, etc.)"
3452
- ).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 = {}) => {
3993
+ "Scaffold local Clean Architecture components (feature, shared, usecase, domain, etc.)"
3994
+ ).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 = {}) => {
3453
3995
  try {
3454
3996
  await handleCreate(type, name, flags);
3455
3997
  } catch (err) {
@@ -3468,6 +4010,7 @@ function createCli() {
3468
4010
  });
3469
4011
  const createSubtypes = [
3470
4012
  "feature",
4013
+ "shared",
3471
4014
  "usecase",
3472
4015
  "domain",
3473
4016
  "infrastructure",
@@ -3488,7 +4031,7 @@ function createCli() {
3488
4031
  });
3489
4032
  }
3490
4033
  cli.help();
3491
- cli.version("1.0.10");
4034
+ cli.version("1.0.14");
3492
4035
  return cli;
3493
4036
  }
3494
4037
  function handleCliError(err) {
@@ -3496,7 +4039,7 @@ function handleCliError(err) {
3496
4039
  process.exit(0);
3497
4040
  }
3498
4041
  const message = err instanceof Error ? err.message : String(err);
3499
- p4.log.error(pc13.red(`Error: ${message}`));
4042
+ p4.log.error(pc14.red(`Error: ${message}`));
3500
4043
  process.exit(1);
3501
4044
  }
3502
4045
  async function runCli(argv = process.argv) {
@@ -3504,6 +4047,6 @@ async function runCli(argv = process.argv) {
3504
4047
  cli.parse(argv, { run: true });
3505
4048
  }
3506
4049
 
3507
- export { DEFAULT_REGISTRY_BASE, PromptCancelledError, concreteInfraTemplate, createCli, detectPackageManager, diProviderTemplate, domainClassTemplate, domainInterfaceTemplate, domainValueObjectTemplate, ensureInterfacePrefix, expressIndexTemplate, fetchModuleManifest, fetchRegistryFile, fetchRegistryIndex, findClosestMatch, findInterfaceFile, findLocalRegistry, findProjectRoot, generateConcreteInfraCode, generateDomain, generateDomainEntityCode, generateFeature, generateHttpHandler, generateHttpHandlerCode, generateInfrastructure, generateInjectable, generateInjectableCode, generateInterface, generateInterfaceCode, generateStubCode, generateUseCase, generateUseCaseCode, generateUseCaseTestCode, getMissingNpmPackages, getRegistryBase, getTemplatesDir, handleCancel, handleCreate, handleImport, hbs, httpHandlerTemplate, injectableInterfaceTemplate, injectableTemplate, installNpmPackages, interfaceTemplate, isValidIdentifier, levenshteinDistance, listDomainEntities, listDomainInterfaces, listExistingFeatures, listUseCases, loadInterfaceHierarchy, loadTemplate, normalizeRegistryUrl, parseInterfaceMethodsAndImports, printBanner, printGeneratedFiles, promptProperties, removeInterfacePrefix, renderTemplate, renderTemplateFile, resolveFeaturesDir, runCli, stubInfraTemplate, synthesizeCrudMethods, toCamelCase, toKebabCase, toPascalCase, toPlural, toSingular, toStubName, updateOrGenerateDiProvider, useCaseTemplate, useCaseTestTemplate, writeFileSafely };
4050
+ export { DEFAULT_REGISTRY_BASE, PromptCancelledError, computeDirectoryHash, concreteInfraTemplate, createCli, detectPackageManager, diProviderTemplate, domainClassTemplate, domainInterfaceTemplate, domainValueObjectTemplate, ensureInterfacePrefix, expressIndexTemplate, fetchModuleManifest, fetchRegistryFile, fetchRegistryIndex, findClosestMatch, findInterfaceFile, findLocalRegistry, findProjectRoot, generateConcreteInfraCode, generateDomain, generateDomainEntityCode, generateFeature, generateHttpHandler, generateHttpHandlerCode, generateInfrastructure, generateInjectable, generateInjectableCode, generateInterface, generateInterfaceCode, generateShared, generateStubCode, generateUseCase, generateUseCaseCode, generateUseCaseTestCode, getAllFiles, getConfiguredFeaturesDir, getConfiguredSharedDir, getEffectiveMode, getMissingNpmPackages, getRegistryBase, getTemplatesDir, handleCancel, handleCreate, handleImport, hbs, httpHandlerTemplate, injectableInterfaceTemplate, injectableTemplate, installNpmPackages, interfaceTemplate, isValidIdentifier, levenshteinDistance, listDomainEntities, listDomainInterfaces, listExistingFeatures, listExistingShared, listUseCases, loadInterfaceHierarchy, loadMasonConfig, loadTemplate, normalizeRegistryUrl, parseInterfaceMethodsAndImports, printBanner, printGeneratedFiles, promptProperties, removeInterfacePrefix, renderTemplate, renderTemplateFile, resolveFeaturesDir, resolveSharedDir, runCli, runSharedCommand, stubInfraTemplate, synthesizeCrudMethods, toCamelCase, toKebabCase, toPascalCase, toPlural, toSingular, toStubName, updateOrGenerateDiProvider, useCaseTemplate, useCaseTestTemplate, writeFileSafely };
3508
4051
  //# sourceMappingURL=index.js.map
3509
4052
  //# sourceMappingURL=index.js.map