@solid-stack/mason 1.0.12 → 1.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,11 +1,12 @@
1
1
  'use strict';
2
2
 
3
- var fs3 = require('fs');
4
- var path3 = require('path');
5
- var p4 = require('@clack/prompts');
6
- var pc13 = require('picocolors');
3
+ var fs12 = require('fs');
4
+ var path2 = require('path');
5
+ var p15 = require('@clack/prompts');
6
+ var pc15 = 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);
35
- var p4__namespace = /*#__PURE__*/_interopNamespace(p4);
36
- var pc13__default = /*#__PURE__*/_interopDefault(pc13);
34
+ var fs12__default = /*#__PURE__*/_interopDefault(fs12);
35
+ var path2__default = /*#__PURE__*/_interopDefault(path2);
36
+ var p15__namespace = /*#__PURE__*/_interopNamespace(p15);
37
+ var pc15__default = /*#__PURE__*/_interopDefault(pc15);
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 = fs12__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 (!fs12__default.default.existsSync(dir)) {
130
+ fs12__default.default.mkdirSync(dir, { recursive: true });
130
131
  }
131
- fs3__default.default.writeFileSync(absolutePath, content, "utf-8");
132
+ fs12__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
+ p15__namespace.log.warn(pc15__default.default.yellow(pc15__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
+ p15__namespace.log.success(`${pc15__default.default.bold(f.relativePath)} ${pc15__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
+ p15__namespace.log.warn(`${pc15__default.default.bold(f.relativePath)} ${pc15__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
+ p15__namespace.log.info(`${pc15__default.default.dim(f.relativePath)} ${pc15__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
+ p15__namespace.log.info(`${pc15__default.default.bold(f.relativePath)} ${pc15__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 (fs12__default.default.existsSync(configPath)) {
163
+ try {
164
+ return JSON.parse(fs12__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 (fs12__default.default.existsSync(path2__default.default.join(projectRoot, "index.json")) && (fs12__default.default.existsSync(path2__default.default.join(projectRoot, "features")) || fs12__default.default.existsSync(path2__default.default.join(projectRoot, "shared"))) && !fs12__default.default.existsSync(path2__default.default.join(projectRoot, "src"))) {
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 (fs12__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 (fs12__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 (fs12__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 (!fs12__default.default.existsSync(sharedDir)) return [];
240
+ try {
241
+ const entries = fs12__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 (!fs12__default.default.existsSync(featuresDir)) return [];
176
250
  try {
177
- const entries = fs3__default.default.readdirSync(featuresDir, { withFileTypes: true });
251
+ const entries = fs12__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 (!fs12__default.default.existsSync(domainDir)) continue;
189
263
  try {
190
- const files = fs3__default.default.readdirSync(domainDir);
264
+ const files = fs12__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 = fs12__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 (!fs12__default.default.existsSync(domainDir)) continue;
227
301
  try {
228
- const files = fs3__default.default.readdirSync(domainDir);
302
+ const files = fs12__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 (!fs12__default.default.existsSync(useCasesDir)) continue;
252
326
  try {
253
- const files = fs3__default.default.readdirSync(useCasesDir);
327
+ const files = fs12__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
  }
@@ -273,23 +347,23 @@ var PromptCancelledError = class extends Error {
273
347
  }
274
348
  };
275
349
  function handleCancel(result) {
276
- if (p4__namespace.isCancel(result)) {
277
- p4__namespace.cancel("Operation cancelled.");
350
+ if (p15__namespace.isCancel(result)) {
351
+ p15__namespace.cancel("Operation cancelled.");
278
352
  throw new PromptCancelledError();
279
353
  }
280
354
  return 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
+ p15__namespace.intro(pc15__default.default.bgCyan(pc15__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((p17) => `${p17.name}: ${p17.type}${p17.optional ? "?" : ""}`).join(", ");
364
+ p15__namespace.log.info(pc15__default.default.dim(`Pre-configured fields: ${summary}`));
291
365
  const addMore = handleCancel(
292
- await p4__namespace.confirm({
366
+ await p15__namespace.confirm({
293
367
  message: "Would you like to add custom fields/properties?",
294
368
  initialValue: false
295
369
  })
@@ -298,10 +372,10 @@ 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
+ p15__namespace.log.info(pc15__default.default.cyan("Enter properties (leave name blank when finished):"));
302
376
  while (true) {
303
377
  const name = handleCancel(
304
- await p4__namespace.text({
378
+ await p15__namespace.text({
305
379
  message: "Property name (or press Enter to finish):",
306
380
  placeholder: "e.g. title, email, status"
307
381
  })
@@ -310,7 +384,7 @@ async function promptProperties(options = {}) {
310
384
  break;
311
385
  }
312
386
  const type = handleCancel(
313
- await p4__namespace.select({
387
+ await p15__namespace.select({
314
388
  message: `Type for property "${name.trim()}":`,
315
389
  options: [
316
390
  { value: "string", label: "string" },
@@ -327,7 +401,7 @@ async function promptProperties(options = {}) {
327
401
  let finalType = type;
328
402
  if (type === "custom") {
329
403
  finalType = handleCancel(
330
- await p4__namespace.text({
404
+ await p15__namespace.text({
331
405
  message: `Enter custom type for "${name.trim()}":`,
332
406
  placeholder: "e.g. UserRole, string | null",
333
407
  validate: (val) => val && val.trim() ? void 0 : "Type cannot be empty"
@@ -335,7 +409,7 @@ async function promptProperties(options = {}) {
335
409
  );
336
410
  }
337
411
  const optional = handleCancel(
338
- await p4__namespace.confirm({
412
+ await p15__namespace.confirm({
339
413
  message: `Is "${name.trim()}" optional?`,
340
414
  initialValue: false
341
415
  })
@@ -345,8 +419,8 @@ async function promptProperties(options = {}) {
345
419
  type: finalType.trim(),
346
420
  optional
347
421
  });
348
- p4__namespace.log.success(
349
- pc13__default.default.green(`Added: ${name.trim()}${optional ? "?" : ""}: ${finalType.trim()}`)
422
+ p15__namespace.log.success(
423
+ pc15__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 (fs12__default.default.existsSync(candidate) && fs12__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 (fs12__default.default.existsSync(configPath)) {
402
476
  try {
403
- const config = JSON.parse(fs3__default.default.readFileSync(configPath, "utf-8"));
477
+ const config = JSON.parse(fs12__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 (fs12__default.default.existsSync(pkgPath)) {
417
491
  try {
418
- const pkg = JSON.parse(fs3__default.default.readFileSync(pkgPath, "utf-8"));
492
+ const pkg = JSON.parse(fs12__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 (!fs12__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 fs12__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 (fs12__default.default.existsSync(path2__default.default.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
602
+ if (fs12__default.default.existsSync(path2__default.default.join(projectRoot, "yarn.lock"))) return "yarn";
603
+ if (fs12__default.default.existsSync(path2__default.default.join(projectRoot, "bun.lockb")) || fs12__default.default.existsSync(path2__default.default.join(projectRoot, "bun.lock"))) return "bun";
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 (!fs12__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(fs12__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((p17) => {
620
+ const nameOnly = p17.startsWith("@") ? `@${p17.slice(1).split("@")[0] || ""}` : p17.split("@")[0] || p17;
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
+ p15__namespace.log.info(`Installing missing dependencies with ${manager}: ${pc15__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 (!fs12__default.default.existsSync(dir)) return [];
653
+ const entries = fs12__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 (!fs12__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 (!fs12__default.default.existsSync(absolutePath)) continue;
679
+ const content = fs12__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 && fs12__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 (fs12__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 (fs12__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 (fs12__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 (fs12__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 fs12__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((p17) => `${p17.name}: ${p17.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 && fs12__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 (!fs12__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 (fs12__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 (fs12__default.default.existsSync(p1)) return { filePath: p1, ifaceName: cand };
1130
+ const p22 = path2__default.default.join(projectRoot, "src", "domain", `${cand}.ts`);
1131
+ if (fs12__default.default.existsSync(p22)) return { filePath: p22, ifaceName: cand };
1132
+ const p32 = path2__default.default.join(projectRoot, "interfaces", `${cand}.ts`);
1133
+ if (fs12__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 = fs12__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 p17 of [...inputProps, ...outputProps]) {
1429
+ const rawType = p17.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((p17) => ({
1436
+ name: p17.name,
1437
+ optional: p17.optional,
1438
+ type: p17.optional && !p17.type.includes("undefined") ? `${p17.type} | undefined` : p17.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((p17) => ({
1441
+ name: p17.name,
1442
+ optional: p17.optional,
1443
+ type: p17.optional && !p17.type.includes("undefined") ? `${p17.type} | undefined` : p17.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 (!fs12__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 (fs12__default.default.existsSync(providerPath) && !options.overwrite) {
1668
+ let content = fs12__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,13 +2108,14 @@ 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";
2005
2115
  }
2006
2116
  if (!name) {
2007
2117
  name = handleCancel(
2008
- await p4__namespace.text({
2118
+ await p15__namespace.text({
2009
2119
  message: "What is the feature name?",
2010
2120
  placeholder: "e.g. users, auth, orders, billing",
2011
2121
  validate: (val) => {
@@ -2016,18 +2126,19 @@ 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
- await p4__namespace.text({
2134
+ await p15__namespace.text({
2024
2135
  message: "Target directory for the feature:",
2025
2136
  initialValue: defaultDir,
2026
2137
  defaultValue: defaultDir
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;
@@ -2042,14 +2153,14 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2042
2153
  const customUseCases = [];
2043
2154
  if (!flags.yes) {
2044
2155
  includeExpress = handleCancel(
2045
- await p4__namespace.confirm({
2156
+ await p15__namespace.confirm({
2046
2157
  message: "Include Express Presentation layer (pxExpress routes & handlers)?",
2047
2158
  initialValue: true
2048
2159
  })
2049
2160
  );
2050
2161
  if (includeExpress) {
2051
2162
  routePath = handleCancel(
2052
- await p4__namespace.text({
2163
+ await p15__namespace.text({
2053
2164
  message: "Express route prefix:",
2054
2165
  initialValue: `/${normalizedName}`,
2055
2166
  defaultValue: `/${normalizedName}`
@@ -2057,14 +2168,14 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2057
2168
  ).trim();
2058
2169
  }
2059
2170
  includeDomain = handleCancel(
2060
- await p4__namespace.confirm({
2171
+ await p15__namespace.confirm({
2061
2172
  message: "Include Domain Entity/Entities?",
2062
2173
  initialValue: true
2063
2174
  })
2064
2175
  );
2065
2176
  if (includeDomain) {
2066
2177
  const domainInput = handleCancel(
2067
- await p4__namespace.text({
2178
+ await p15__namespace.text({
2068
2179
  message: "Domain entity name(s) (comma-separated):",
2069
2180
  initialValue: defaultEntity,
2070
2181
  defaultValue: defaultEntity,
@@ -2083,21 +2194,21 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2083
2194
  entityNames = [defaultEntity];
2084
2195
  }
2085
2196
  includeInterface = handleCancel(
2086
- await p4__namespace.confirm({
2197
+ await p15__namespace.confirm({
2087
2198
  message: `Include Domain Repository interface(s) for ${entityNames.join(", ")}?`,
2088
2199
  initialValue: true
2089
2200
  })
2090
2201
  );
2091
2202
  if (includeInterface) {
2092
2203
  includeInfra = handleCancel(
2093
- await p4__namespace.confirm({
2204
+ await p15__namespace.confirm({
2094
2205
  message: `Include Infrastructure Stub implementation(s) for ${entityNames.join(", ")}?`,
2095
2206
  initialValue: true
2096
2207
  })
2097
2208
  );
2098
2209
  }
2099
2210
  includeUseCase = handleCancel(
2100
- await p4__namespace.confirm({
2211
+ await p15__namespace.confirm({
2101
2212
  message: "Include Domain Use Cases?",
2102
2213
  initialValue: true
2103
2214
  })
@@ -2111,7 +2222,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2111
2222
  { value: "delete", label: "Delete (Delete<Entity>)", hint: "DELETE /:id" }
2112
2223
  ];
2113
2224
  crudOperations = handleCancel(
2114
- await p4__namespace.multiselect({
2225
+ await p15__namespace.multiselect({
2115
2226
  message: "Select standard CRUD use cases to generate:",
2116
2227
  options: crudChoices,
2117
2228
  initialValues: ["create", "read", "list", "update", "delete"],
@@ -2119,13 +2230,13 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2119
2230
  })
2120
2231
  );
2121
2232
  includeTests = handleCancel(
2122
- await p4__namespace.confirm({
2233
+ await p15__namespace.confirm({
2123
2234
  message: "Generate Vitest unit tests for use cases?",
2124
2235
  initialValue: true
2125
2236
  })
2126
2237
  );
2127
2238
  const wantCustom = handleCancel(
2128
- await p4__namespace.confirm({
2239
+ await p15__namespace.confirm({
2129
2240
  message: "Do you want to add any custom use cases to this feature?",
2130
2241
  initialValue: false
2131
2242
  })
@@ -2135,7 +2246,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2135
2246
  const availableIfaces = entityNames.map((e) => `I${e}Repository`);
2136
2247
  while (addMore) {
2137
2248
  const customName = handleCancel(
2138
- await p4__namespace.text({
2249
+ await p15__namespace.text({
2139
2250
  message: "Custom Use Case name:",
2140
2251
  placeholder: "e.g. ResetPassword, VerifyEmail, AssignRole",
2141
2252
  validate: (v) => v && v.trim() ? void 0 : "Use Case name cannot be empty"
@@ -2145,7 +2256,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2145
2256
  let selectedDeps = [];
2146
2257
  if (availableIfaces.length > 0) {
2147
2258
  selectedDeps = handleCancel(
2148
- await p4__namespace.multiselect({
2259
+ await p15__namespace.multiselect({
2149
2260
  message: `Select domain interfaces to inject into ${customUcName}:`,
2150
2261
  options: availableIfaces.map((i) => ({ value: i, label: i })),
2151
2262
  required: false
@@ -2154,7 +2265,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2154
2265
  }
2155
2266
  let customInputProps = [];
2156
2267
  const hasInput = handleCancel(
2157
- await p4__namespace.confirm({
2268
+ await p15__namespace.confirm({
2158
2269
  message: `Does ${customUcName} accept input arguments?`,
2159
2270
  initialValue: true
2160
2271
  })
@@ -2165,7 +2276,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2165
2276
  }
2166
2277
  let customOutputProps = [];
2167
2278
  const hasCustomOutput = handleCancel(
2168
- await p4__namespace.confirm({
2279
+ await p15__namespace.confirm({
2169
2280
  message: `Does ${customUcName} return custom output data?`,
2170
2281
  initialValue: false
2171
2282
  })
@@ -2175,7 +2286,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2175
2286
  });
2176
2287
  }
2177
2288
  const customIncludeTest = handleCancel(
2178
- await p4__namespace.confirm({
2289
+ await p15__namespace.confirm({
2179
2290
  message: `Generate Vitest unit test for ${customUcName}?`,
2180
2291
  initialValue: true
2181
2292
  })
@@ -2185,14 +2296,14 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2185
2296
  let customHttpPath = `/${toKebabCase(customUcName)}`;
2186
2297
  if (includeExpress) {
2187
2298
  customCreateHttp = handleCancel(
2188
- await p4__namespace.confirm({
2299
+ await p15__namespace.confirm({
2189
2300
  message: `Generate a matching ExpressRoute HTTP handler for ${customUcName}?`,
2190
2301
  initialValue: true
2191
2302
  })
2192
2303
  );
2193
2304
  if (customCreateHttp) {
2194
2305
  customHttpMethod = handleCancel(
2195
- await p4__namespace.select({
2306
+ await p15__namespace.select({
2196
2307
  message: "HTTP method for route:",
2197
2308
  options: [
2198
2309
  { value: "post", label: "POST" },
@@ -2205,7 +2316,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2205
2316
  })
2206
2317
  );
2207
2318
  customHttpPath = handleCancel(
2208
- await p4__namespace.text({
2319
+ await p15__namespace.text({
2209
2320
  message: "Route subpath (relative to feature prefix):",
2210
2321
  initialValue: `/${toKebabCase(customUcName)}`,
2211
2322
  defaultValue: `/${toKebabCase(customUcName)}`
@@ -2226,7 +2337,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2226
2337
  validationFields: customInputProps
2227
2338
  });
2228
2339
  addMore = handleCancel(
2229
- await p4__namespace.confirm({
2340
+ await p15__namespace.confirm({
2230
2341
  message: "Add another custom use case?",
2231
2342
  initialValue: false
2232
2343
  })
@@ -2235,7 +2346,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2235
2346
  }
2236
2347
  }
2237
2348
  includeDiProvider = handleCancel(
2238
- await p4__namespace.confirm({
2349
+ await p15__namespace.confirm({
2239
2350
  message: "Generate / update feature DI provider (diProvider.ts)?",
2240
2351
  initialValue: true
2241
2352
  })
@@ -2247,8 +2358,8 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2247
2358
  entityNames = [defaultEntity];
2248
2359
  }
2249
2360
  }
2250
- const s = p4__namespace.spinner();
2251
- s.start(`Generating feature '${pc13__default.default.cyan(normalizedName)}'...`);
2361
+ const s = p15__namespace.spinner();
2362
+ s.start(`Generating feature '${pc15__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 (!fs12__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
+ p15__namespace.outro(
2409
+ pc15__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 p15__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 p15__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 p15__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 p15__namespace.confirm({
2559
+ message: "Include stub implementation in infrastructure/?",
2560
+ initialValue: true
2561
+ })
2562
+ );
2563
+ }
2564
+ const s = p15__namespace.spinner();
2565
+ s.start(`Generating shared module '${pc15__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
+ p15__namespace.outro(
2579
+ pc15__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();
@@ -2281,14 +2590,14 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2281
2590
  const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
2282
2591
  featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
2283
2592
  const selected = handleCancel(
2284
- await p4__namespace.select({
2593
+ await p15__namespace.select({
2285
2594
  message: "Select target feature:",
2286
2595
  options: featureOptions
2287
2596
  })
2288
2597
  );
2289
2598
  if (selected === "__custom__") {
2290
2599
  feature = handleCancel(
2291
- await p4__namespace.text({
2600
+ await p15__namespace.text({
2292
2601
  message: "Enter feature name:",
2293
2602
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2294
2603
  })
@@ -2298,7 +2607,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2298
2607
  }
2299
2608
  } else {
2300
2609
  feature = handleCancel(
2301
- await p4__namespace.text({
2610
+ await p15__namespace.text({
2302
2611
  message: "Enter target feature name:",
2303
2612
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2304
2613
  })
@@ -2309,7 +2618,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2309
2618
  let name = useCaseNameArg?.trim();
2310
2619
  if (!name) {
2311
2620
  name = handleCancel(
2312
- await p4__namespace.text({
2621
+ await p15__namespace.text({
2313
2622
  message: "Use Case name:",
2314
2623
  placeholder: "e.g. CreateUser, GetUser, CancelOrder",
2315
2624
  validate: (v) => v && v.trim() ? void 0 : "Use Case name cannot be empty"
@@ -2325,7 +2634,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2325
2634
  label: i.name
2326
2635
  }));
2327
2636
  selectedDeps = handleCancel(
2328
- await p4__namespace.multiselect({
2637
+ await p15__namespace.multiselect({
2329
2638
  message: "Select domain interfaces to inject into use case:",
2330
2639
  options: multiOptions,
2331
2640
  required: false
@@ -2340,7 +2649,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2340
2649
  let httpPath = "/";
2341
2650
  if (!flags.yes) {
2342
2651
  const hasInput = handleCancel(
2343
- await p4__namespace.confirm({
2652
+ await p15__namespace.confirm({
2344
2653
  message: "Does this use case accept input arguments?",
2345
2654
  initialValue: true
2346
2655
  })
@@ -2350,7 +2659,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2350
2659
  });
2351
2660
  }
2352
2661
  const hasCustomOutput = handleCancel(
2353
- await p4__namespace.confirm({
2662
+ await p15__namespace.confirm({
2354
2663
  message: "Does this use case return custom output data?",
2355
2664
  initialValue: false
2356
2665
  })
@@ -2360,20 +2669,20 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2360
2669
  });
2361
2670
  }
2362
2671
  includeTest = handleCancel(
2363
- await p4__namespace.confirm({
2672
+ await p15__namespace.confirm({
2364
2673
  message: "Generate Vitest unit test skeleton (.test.ts)?",
2365
2674
  initialValue: true
2366
2675
  })
2367
2676
  );
2368
2677
  createHttp = handleCancel(
2369
- await p4__namespace.confirm({
2678
+ await p15__namespace.confirm({
2370
2679
  message: "Generate a matching ExpressRoute HTTP handler?",
2371
2680
  initialValue: false
2372
2681
  })
2373
2682
  );
2374
2683
  if (createHttp) {
2375
2684
  httpMethod = handleCancel(
2376
- await p4__namespace.select({
2685
+ await p15__namespace.select({
2377
2686
  message: "HTTP method for route:",
2378
2687
  options: [
2379
2688
  { value: "post", label: "POST" },
@@ -2386,7 +2695,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2386
2695
  })
2387
2696
  );
2388
2697
  httpPath = handleCancel(
2389
- await p4__namespace.text({
2698
+ await p15__namespace.text({
2390
2699
  message: "Route subpath (relative to feature prefix):",
2391
2700
  initialValue: "/",
2392
2701
  defaultValue: "/"
@@ -2394,8 +2703,8 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2394
2703
  ).trim();
2395
2704
  }
2396
2705
  }
2397
- const s = p4__namespace.spinner();
2398
- s.start(`Generating use case '${pc13__default.default.cyan(ucName)}'...`);
2706
+ const s = p15__namespace.spinner();
2707
+ s.start(`Generating use case '${pc15__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
+ p15__namespace.outro(pc15__default.default.green(`Use case '${ucName}' created in feature '${feature}'!`));
2431
2740
  }
2432
2741
  async function runDomainCommand(domainNameArg, flags = {}) {
2433
2742
  const projectRoot = findProjectRoot();
@@ -2438,14 +2747,14 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2438
2747
  const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
2439
2748
  featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
2440
2749
  const selected = handleCancel(
2441
- await p4__namespace.select({
2750
+ await p15__namespace.select({
2442
2751
  message: "Select target feature:",
2443
2752
  options: featureOptions
2444
2753
  })
2445
2754
  );
2446
2755
  if (selected === "__custom__") {
2447
2756
  feature = handleCancel(
2448
- await p4__namespace.text({
2757
+ await p15__namespace.text({
2449
2758
  message: "Enter feature name:",
2450
2759
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2451
2760
  })
@@ -2455,7 +2764,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2455
2764
  }
2456
2765
  } else {
2457
2766
  feature = handleCancel(
2458
- await p4__namespace.text({
2767
+ await p15__namespace.text({
2459
2768
  message: "Enter target feature name:",
2460
2769
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2461
2770
  })
@@ -2466,7 +2775,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2466
2775
  let name = domainNameArg?.trim();
2467
2776
  if (!name) {
2468
2777
  name = handleCancel(
2469
- await p4__namespace.text({
2778
+ await p15__namespace.text({
2470
2779
  message: "Domain model / entity name:",
2471
2780
  placeholder: "e.g. User, Order, Product, Address",
2472
2781
  validate: (v) => v && v.trim() ? void 0 : "Domain name cannot be empty"
@@ -2478,7 +2787,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2478
2787
  let properties = [];
2479
2788
  if (!flags.yes) {
2480
2789
  kind = handleCancel(
2481
- await p4__namespace.select({
2790
+ await p15__namespace.select({
2482
2791
  message: "Select domain model type:",
2483
2792
  options: [
2484
2793
  { value: "interface", label: "TypeScript Interface (pure data structure)" },
@@ -2497,8 +2806,8 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2497
2806
  defaults: kind === "valueObject" ? [{ name: "value", type: "string" }] : defaultProps
2498
2807
  });
2499
2808
  }
2500
- const s = p4__namespace.spinner();
2501
- s.start(`Generating domain model '${pc13__default.default.cyan(entityName)}'...`);
2809
+ const s = p15__namespace.spinner();
2810
+ s.start(`Generating domain model '${pc15__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
+ p15__namespace.outro(pc15__default.default.green(`Domain model '${entityName}' created in feature '${feature}'!`));
2515
2824
  }
2516
2825
  async function runInfrastructureCommand(classNameArg, flags = {}) {
2517
2826
  const projectRoot = findProjectRoot();
@@ -2520,7 +2829,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2520
2829
  let feature = flags.feature?.trim();
2521
2830
  if (!flags.yes && !flags.global && !flags.feature) {
2522
2831
  scope = handleCancel(
2523
- await p4__namespace.select({
2832
+ await p15__namespace.select({
2524
2833
  message: "Where should this infrastructure implementation be placed?",
2525
2834
  options: [
2526
2835
  { value: "feature", label: "Feature-level (features/<feature>/infrastructure)" },
@@ -2535,14 +2844,14 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2535
2844
  const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
2536
2845
  featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
2537
2846
  const selected = handleCancel(
2538
- await p4__namespace.select({
2847
+ await p15__namespace.select({
2539
2848
  message: "Select target feature:",
2540
2849
  options: featureOptions
2541
2850
  })
2542
2851
  );
2543
2852
  if (selected === "__custom__") {
2544
2853
  feature = handleCancel(
2545
- await p4__namespace.text({
2854
+ await p15__namespace.text({
2546
2855
  message: "Enter feature name:",
2547
2856
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2548
2857
  })
@@ -2552,7 +2861,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2552
2861
  }
2553
2862
  } else {
2554
2863
  feature = handleCancel(
2555
- await p4__namespace.text({
2864
+ await p15__namespace.text({
2556
2865
  message: "Enter target feature name:",
2557
2866
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2558
2867
  })
@@ -2565,7 +2874,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2565
2874
  let name = classNameArg?.trim();
2566
2875
  if (!name) {
2567
2876
  name = handleCancel(
2568
- await p4__namespace.text({
2877
+ await p15__namespace.text({
2569
2878
  message: "Infrastructure class name:",
2570
2879
  placeholder: "e.g. StubUserRepository, PostgresUserRepository, S3FileStorage",
2571
2880
  validate: (v) => v && v.trim() ? void 0 : "Class name cannot be empty"
@@ -2584,7 +2893,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2584
2893
  (i) => className.toLowerCase().includes(removeInterfacePrefix(i.name).toLowerCase())
2585
2894
  );
2586
2895
  const ifaceChoice = handleCancel(
2587
- await p4__namespace.select({
2896
+ await p15__namespace.select({
2588
2897
  message: "Does this class implement a domain interface or abstract class?",
2589
2898
  options: [
2590
2899
  ...interfaces.map((i) => ({ value: i.name, label: i.name })),
@@ -2596,7 +2905,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2596
2905
  );
2597
2906
  if (ifaceChoice === "__custom__") {
2598
2907
  implementsInterface = handleCancel(
2599
- await p4__namespace.text({
2908
+ await p15__namespace.text({
2600
2909
  message: "Enter interface or abstract class name:",
2601
2910
  placeholder: "e.g. IUserRepository, BaseStorageAdapter",
2602
2911
  validate: (v) => v && v.trim() ? void 0 : "Name cannot be empty"
@@ -2607,14 +2916,14 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2607
2916
  }
2608
2917
  } else {
2609
2918
  const wantImpl = handleCancel(
2610
- await p4__namespace.confirm({
2919
+ await p15__namespace.confirm({
2611
2920
  message: "Does this class implement a domain interface or abstract class?",
2612
2921
  initialValue: false
2613
2922
  })
2614
2923
  );
2615
2924
  if (wantImpl) {
2616
2925
  implementsInterface = handleCancel(
2617
- await p4__namespace.text({
2926
+ await p15__namespace.text({
2618
2927
  message: "Enter interface or abstract class name:",
2619
2928
  placeholder: "e.g. IUserRepository, BaseStorageAdapter",
2620
2929
  validate: (v) => v && v.trim() ? void 0 : "Name cannot be empty"
@@ -2634,7 +2943,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2634
2943
  const allInterfaces = listDomainInterfaces(projectRoot);
2635
2944
  if (allInterfaces.length > 0) {
2636
2945
  const ifaceChoice = handleCancel(
2637
- await p4__namespace.select({
2946
+ await p15__namespace.select({
2638
2947
  message: "Does this class implement an interface or abstract class?",
2639
2948
  options: [
2640
2949
  ...allInterfaces.map((i) => ({ value: i.name, label: `${i.name} (${i.feature})` })),
@@ -2646,7 +2955,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2646
2955
  );
2647
2956
  if (ifaceChoice === "__custom__") {
2648
2957
  implementsInterface = handleCancel(
2649
- await p4__namespace.text({
2958
+ await p15__namespace.text({
2650
2959
  message: "Enter interface or abstract class name:",
2651
2960
  placeholder: "e.g. IAuthRepository",
2652
2961
  validate: (v) => v && v.trim() ? void 0 : "Name cannot be empty"
@@ -2659,7 +2968,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2659
2968
  }
2660
2969
  if (!flags.yes && !flags.kind) {
2661
2970
  kind = handleCancel(
2662
- await p4__namespace.select({
2971
+ await p15__namespace.select({
2663
2972
  message: "Select implementation kind:",
2664
2973
  options: [
2665
2974
  { value: "stub", label: "In-Memory Stub (with in-memory store and test helper methods)" },
@@ -2670,7 +2979,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2670
2979
  );
2671
2980
  if (scope === "feature" && feature && implementsInterface) {
2672
2981
  registerInProvider = handleCancel(
2673
- await p4__namespace.confirm({
2982
+ await p15__namespace.confirm({
2674
2983
  message: "Register this implementation in feature diProvider.ts?",
2675
2984
  initialValue: true
2676
2985
  })
@@ -2679,8 +2988,8 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2679
2988
  } else if (flags.yes && scope === "feature" && feature && implementsInterface) {
2680
2989
  registerInProvider = !flags.noProvider;
2681
2990
  }
2682
- const s = p4__namespace.spinner();
2683
- s.start(`Generating infrastructure class '${pc13__default.default.cyan(className)}'...`);
2991
+ const s = p15__namespace.spinner();
2992
+ s.start(`Generating infrastructure class '${pc15__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
+ p15__namespace.outro(pc15__default.default.green(`Infrastructure class '${className}' created!`));
2719
3028
  }
2720
3029
  async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2721
3030
  const projectRoot = findProjectRoot();
@@ -2726,14 +3035,14 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2726
3035
  const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
2727
3036
  featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
2728
3037
  const selected = handleCancel(
2729
- await p4__namespace.select({
3038
+ await p15__namespace.select({
2730
3039
  message: "Select target feature:",
2731
3040
  options: featureOptions
2732
3041
  })
2733
3042
  );
2734
3043
  if (selected === "__custom__") {
2735
3044
  feature = handleCancel(
2736
- await p4__namespace.text({
3045
+ await p15__namespace.text({
2737
3046
  message: "Enter feature name:",
2738
3047
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2739
3048
  })
@@ -2743,7 +3052,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2743
3052
  }
2744
3053
  } else {
2745
3054
  feature = handleCancel(
2746
- await p4__namespace.text({
3055
+ await p15__namespace.text({
2747
3056
  message: "Enter target feature name:",
2748
3057
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2749
3058
  })
@@ -2754,7 +3063,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2754
3063
  let name = interfaceNameArg?.trim();
2755
3064
  if (!name) {
2756
3065
  name = handleCancel(
2757
- await p4__namespace.text({
3066
+ await p15__namespace.text({
2758
3067
  message: "Domain interface name:",
2759
3068
  placeholder: "e.g. IUserRepository, IPaymentService",
2760
3069
  validate: (v) => v && v.trim() ? void 0 : "Interface name cannot be empty"
@@ -2772,7 +3081,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2772
3081
  if (!flags.yes) {
2773
3082
  if (existingEntities.length > 0) {
2774
3083
  const selectedEntity = handleCancel(
2775
- await p4__namespace.select({
3084
+ await p15__namespace.select({
2776
3085
  message: "Select domain entity associated with this interface:",
2777
3086
  options: [
2778
3087
  ...existingEntities.map((e) => ({ value: e.name, label: e.name })),
@@ -2783,7 +3092,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2783
3092
  );
2784
3093
  if (selectedEntity === "__custom__") {
2785
3094
  entityName = handleCancel(
2786
- await p4__namespace.text({
3095
+ await p15__namespace.text({
2787
3096
  message: "Enter entity name:",
2788
3097
  defaultValue: removeInterfacePrefix(ifaceName).replace(/Repository$|Service$/, "")
2789
3098
  })
@@ -2793,7 +3102,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2793
3102
  }
2794
3103
  }
2795
3104
  kind = handleCancel(
2796
- await p4__namespace.select({
3105
+ await p15__namespace.select({
2797
3106
  message: "Interface definition style:",
2798
3107
  options: [
2799
3108
  { value: "abstractClass", label: "Abstract Class (Recommended - acts as DI token & type)" },
@@ -2803,7 +3112,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2803
3112
  })
2804
3113
  );
2805
3114
  methodsPreset = handleCancel(
2806
- await p4__namespace.select({
3115
+ await p15__namespace.select({
2807
3116
  message: "Method signature preset:",
2808
3117
  options: [
2809
3118
  { value: "crud", label: "Standard CRUD (GetById, List, Create, Update, Delete)" },
@@ -2813,22 +3122,22 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2813
3122
  })
2814
3123
  );
2815
3124
  generateStub = handleCancel(
2816
- await p4__namespace.confirm({
3125
+ await p15__namespace.confirm({
2817
3126
  message: "Auto-generate Infrastructure Stub implementation?",
2818
3127
  initialValue: true
2819
3128
  })
2820
3129
  );
2821
3130
  if (generateStub) {
2822
3131
  registerInProvider = handleCancel(
2823
- await p4__namespace.confirm({
3132
+ await p15__namespace.confirm({
2824
3133
  message: "Auto-register interface & stub in feature diProvider.ts?",
2825
3134
  initialValue: true
2826
3135
  })
2827
3136
  );
2828
3137
  }
2829
3138
  }
2830
- const s = p4__namespace.spinner();
2831
- s.start(`Generating interface '${pc13__default.default.cyan(ifaceName)}'...`);
3139
+ const s = p15__namespace.spinner();
3140
+ s.start(`Generating interface '${pc15__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
+ p15__namespace.outro(pc15__default.default.green(`Interface '${ifaceName}' created in feature '${feature}'!`));
2881
3190
  }
2882
3191
  async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2883
3192
  const projectRoot = findProjectRoot();
@@ -2888,14 +3197,14 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2888
3197
  const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
2889
3198
  featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
2890
3199
  const selected = handleCancel(
2891
- await p4__namespace.select({
3200
+ await p15__namespace.select({
2892
3201
  message: "Select target feature:",
2893
3202
  options: featureOptions
2894
3203
  })
2895
3204
  );
2896
3205
  if (selected === "__custom__") {
2897
3206
  feature = handleCancel(
2898
- await p4__namespace.text({
3207
+ await p15__namespace.text({
2899
3208
  message: "Enter feature name:",
2900
3209
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2901
3210
  })
@@ -2905,7 +3214,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2905
3214
  }
2906
3215
  } else {
2907
3216
  feature = handleCancel(
2908
- await p4__namespace.text({
3217
+ await p15__namespace.text({
2909
3218
  message: "Enter target feature name:",
2910
3219
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
2911
3220
  })
@@ -2916,7 +3225,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2916
3225
  let name = handlerNameArg?.trim();
2917
3226
  if (!name) {
2918
3227
  name = handleCancel(
2919
- await p4__namespace.text({
3228
+ await p15__namespace.text({
2920
3229
  message: "HTTP Route Handler class name:",
2921
3230
  placeholder: "e.g. CreateUserHttp, GetUserHttp, HealthHttp",
2922
3231
  validate: (v) => v && v.trim() ? void 0 : "Handler name cannot be empty"
@@ -2935,7 +3244,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2935
3244
  let validationFields = [];
2936
3245
  if (!flags.yes) {
2937
3246
  method = handleCancel(
2938
- await p4__namespace.select({
3247
+ await p15__namespace.select({
2939
3248
  message: "HTTP Method:",
2940
3249
  options: [
2941
3250
  { value: "get", label: "GET" },
@@ -2949,7 +3258,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2949
3258
  );
2950
3259
  const defaultPath = method === "get" ? "/" : "/";
2951
3260
  subPath = handleCancel(
2952
- await p4__namespace.text({
3261
+ await p15__namespace.text({
2953
3262
  message: "Route subpath (relative to feature prefix):",
2954
3263
  initialValue: defaultPath,
2955
3264
  defaultValue: defaultPath
@@ -2960,7 +3269,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2960
3269
  const ucOptions = useCases.map((u) => ({ value: u.name, label: u.name }));
2961
3270
  ucOptions.push({ value: "__none__", label: "None / Standalone Handler" });
2962
3271
  const chosenUc = handleCancel(
2963
- await p4__namespace.select({
3272
+ await p15__namespace.select({
2964
3273
  message: "Connect this handler to a domain Use Case?",
2965
3274
  options: ucOptions,
2966
3275
  initialValue: ucOptions[0]?.value
@@ -2971,7 +3280,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2971
3280
  }
2972
3281
  }
2973
3282
  includeValidation = handleCancel(
2974
- await p4__namespace.confirm({
3283
+ await p15__namespace.confirm({
2975
3284
  message: "Include Zod inbound request validation?",
2976
3285
  initialValue: method !== "delete"
2977
3286
  })
@@ -2979,7 +3288,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2979
3288
  if (includeValidation) {
2980
3289
  const defaultSource = method === "get" ? "query" : "body";
2981
3290
  validationSource = handleCancel(
2982
- await p4__namespace.select({
3291
+ await p15__namespace.select({
2983
3292
  message: "Validation data source:",
2984
3293
  options: [
2985
3294
  { value: "body", label: "req.body (for POST / PUT / PATCH payloads)" },
@@ -2993,8 +3302,8 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2993
3302
  });
2994
3303
  }
2995
3304
  }
2996
- const s = p4__namespace.spinner();
2997
- s.start(`Generating HTTP handler '${pc13__default.default.cyan(className)}'...`);
3305
+ const s = p15__namespace.spinner();
3306
+ s.start(`Generating HTTP handler '${pc15__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
+ p15__namespace.outro(pc15__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((p17) => `${p17.name}: ${p17.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, {
@@ -3092,7 +3401,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3092
3401
  let name = classNameArg?.trim();
3093
3402
  if (!name) {
3094
3403
  name = handleCancel(
3095
- await p4__namespace.text({
3404
+ await p15__namespace.text({
3096
3405
  message: "Injectable class name:",
3097
3406
  placeholder: "e.g. PasswordHasher, TokenService, UserTransformer",
3098
3407
  validate: (v) => v && v.trim() ? void 0 : "Class name cannot be empty"
@@ -3105,7 +3414,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3105
3414
  let customPath;
3106
3415
  if (!flags.yes) {
3107
3416
  location = handleCancel(
3108
- await p4__namespace.select({
3417
+ await p15__namespace.select({
3109
3418
  message: "Select target architectural location:",
3110
3419
  options: [
3111
3420
  { value: "feature-service", label: "Feature Service (features/<feature>/services/)" },
@@ -3123,14 +3432,14 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3123
3432
  const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
3124
3433
  featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
3125
3434
  const selected = handleCancel(
3126
- await p4__namespace.select({
3435
+ await p15__namespace.select({
3127
3436
  message: "Select target feature:",
3128
3437
  options: featureOptions
3129
3438
  })
3130
3439
  );
3131
3440
  if (selected === "__custom__") {
3132
3441
  feature = handleCancel(
3133
- await p4__namespace.text({
3442
+ await p15__namespace.text({
3134
3443
  message: "Enter feature name:",
3135
3444
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
3136
3445
  })
@@ -3140,7 +3449,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3140
3449
  }
3141
3450
  } else {
3142
3451
  feature = handleCancel(
3143
- await p4__namespace.text({
3452
+ await p15__namespace.text({
3144
3453
  message: "Enter target feature name:",
3145
3454
  validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
3146
3455
  })
@@ -3150,7 +3459,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3150
3459
  feature = toCamelCase(feature);
3151
3460
  } else if (location === "custom") {
3152
3461
  customPath = handleCancel(
3153
- await p4__namespace.text({
3462
+ await p15__namespace.text({
3154
3463
  message: "Enter destination directory path:",
3155
3464
  placeholder: "e.g. src/services or lib/utils",
3156
3465
  validate: (v) => v && v.trim() ? void 0 : "Path cannot be empty"
@@ -3161,14 +3470,14 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3161
3470
  let generateInterface2 = false;
3162
3471
  if (!flags.yes) {
3163
3472
  generateInterface2 = handleCancel(
3164
- await p4__namespace.confirm({
3473
+ await p15__namespace.confirm({
3165
3474
  message: `Generate abstract class interface (I${className}) as runtime DI token?`,
3166
3475
  initialValue: false
3167
3476
  })
3168
3477
  );
3169
3478
  }
3170
- const s = p4__namespace.spinner();
3171
- s.start(`Generating injectable '${pc13__default.default.cyan(className)}'...`);
3479
+ const s = p15__namespace.spinner();
3480
+ s.start(`Generating injectable '${pc15__default.default.cyan(className)}'...`);
3172
3481
  const options = {
3173
3482
  name: className,
3174
3483
  feature,
@@ -3182,12 +3491,12 @@ 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
+ p15__namespace.outro(pc15__default.default.green(`Injectable '${className}' created successfully!`));
3186
3495
  }
3187
3496
  async function runInteractiveMenu(flags = {}) {
3188
3497
  printBanner();
3189
3498
  const choice = handleCancel(
3190
- await p4__namespace.select({
3499
+ await p15__namespace.select({
3191
3500
  message: "What would you like to create in your architecture?",
3192
3501
  options: [
3193
3502
  {
@@ -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;
@@ -3284,21 +3604,172 @@ async function handleCreate(type, name, flags = {}) {
3284
3604
  await runInjectableCommand(name, flags);
3285
3605
  break;
3286
3606
  default:
3287
- p4__namespace.log.error(
3288
- pc13__default.default.red(
3289
- `Unknown component type: "${type}". Allowed types: feature, usecase, domain, infrastructure, interface, httpHandler, injectable.`
3607
+ p15__namespace.log.error(
3608
+ pc15__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: p15__namespace.select,
3617
+ text: p15__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 (fs12__default.default.existsSync(inSrc)) return inSrc;
3639
+ const inRoot = path2__default.default.join(projectRoot, folder, modName);
3640
+ if (fs12__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
+ p15__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
+ p15__namespace.log.info(` \u21B3 Resolving required ${depType} dependency: ${pc15__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
+ p15__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
+ p15__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
+ fs12__default.default.rmSync(existingDir, { recursive: true, force: true });
3692
+ }
3693
+ fs12__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
+ p15__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
+ p15__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
+ p15__namespace.log.step(`Importing '${depName}' into '${displayDest}'...`);
3749
+ await importModuleRecursive(depType, depName, newName);
3750
+ return;
3751
+ }
3752
+ if (choice === "overwrite") {
3753
+ p15__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
+ fs12__default.default.rmSync(existingDir, { recursive: true, force: true });
3759
+ }
3760
+ fs12__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
+ p15__namespace.log.error(pc15__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
- const s = p4__namespace.spinner();
3324
- s.start(`Connecting to registry at ${pc13__default.default.dim(registryBase)}...`);
3794
+ const s = p15__namespace.spinner();
3795
+ s.start(`Connecting to registry at ${pc15__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(pc15__default.default.red("Failed to load registry index."));
3802
+ p15__namespace.log.error(pc15__default.default.red(err.message || String(err)));
3332
3803
  process.exit(1);
3333
3804
  }
3334
3805
  const availableList = type === "shared" ? index.shared : index.features;
@@ -3336,11 +3807,11 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3336
3807
  let name = nameArg?.trim();
3337
3808
  if (!name) {
3338
3809
  if (availableList.length === 0) {
3339
- p4__namespace.log.warn(`No ${type} modules available in the registry.`);
3810
+ p15__namespace.log.warn(`No ${type} modules available in the registry.`);
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,122 +3823,769 @@ 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
+ p15__namespace.log.error(pc15__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
+ p15__namespace.log.info(pc15__default.default.yellow(`Did you mean '${pc15__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
+ p15__namespace.log.step(
3839
+ `Importing ${modType}: ${pc15__default.default.cyan(targetName)}${targetName !== registryName ? pc15__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
+ p15__namespace.log.error(pc15__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 (fs12__default.default.existsSync(localFilePath) && !isOverwrite) {
3892
+ p15__namespace.log.warn(
3893
+ ` \u21B3 Skipping existing file: ${pc15__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
+ fs12__default.default.mkdirSync(path2__default.default.dirname(localFilePath), { recursive: true });
3901
+ fs12__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
+ p15__namespace.log.message(` ${pc15__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
+ p15__namespace.log.info(`Required npm dependencies to install: ${pc15__default.default.cyan(missingNpm.join(", "))}`);
3420
3914
  } else {
3421
3915
  try {
3422
3916
  installNpmPackages(projectRoot, missingNpm);
3423
3917
  } catch (err) {
3424
- p4__namespace.log.warn(`Could not automatically install packages (${err.message}). Please run: npm install ${missingNpm.join(" ")}`);
3918
+ p15__namespace.log.warn(`Could not automatically install packages (${err.message}). Please run: npm install ${missingNpm.join(" ")}`);
3425
3919
  }
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
+ p15__namespace.log.info(pc15__default.default.yellow("Dry run complete: No files were written to disk."));
3430
3924
  } else {
3431
- 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})`
3925
+ const baseDir = getModuleBaseDir(projectRoot, type);
3926
+ const displayTarget = path2__default.default.relative(projectRoot, path2__default.default.join(baseDir, name));
3927
+ p15__namespace.log.success(
3928
+ pc15__default.default.green(
3929
+ `Successfully imported ${type} '${name}' (${totalWrittenFiles.length} file${totalWrittenFiles.length === 1 ? "" : "s"} placed in ${displayTarget})`
3434
3930
  )
3435
3931
  );
3436
3932
  }
3437
3933
  }
3934
+ function loadMasterIndex(projectRoot) {
3935
+ const indexPath = path2__default.default.join(projectRoot, "index.json");
3936
+ if (!fs12__default.default.existsSync(indexPath)) {
3937
+ const initialIndex = { shared: [], features: [] };
3938
+ fs12__default.default.writeFileSync(indexPath, JSON.stringify(initialIndex, null, 2) + "\n", "utf-8");
3939
+ return initialIndex;
3940
+ }
3941
+ return JSON.parse(fs12__default.default.readFileSync(indexPath, "utf-8"));
3942
+ }
3943
+ function saveMasterIndex(projectRoot, index) {
3944
+ const indexPath = path2__default.default.join(projectRoot, "index.json");
3945
+ index.shared.sort((a, b) => a.name.localeCompare(b.name));
3946
+ index.features.sort((a, b) => a.name.localeCompare(b.name));
3947
+ fs12__default.default.writeFileSync(indexPath, JSON.stringify(index, null, 2) + "\n", "utf-8");
3948
+ }
3949
+ function discoverDiskModules(projectRoot) {
3950
+ const results = [];
3951
+ const sharedDir = resolveSharedDir(projectRoot);
3952
+ if (fs12__default.default.existsSync(sharedDir)) {
3953
+ const entries = fs12__default.default.readdirSync(sharedDir, { withFileTypes: true });
3954
+ for (const entry of entries) {
3955
+ if (entry.isDirectory() && !entry.name.startsWith(".")) {
3956
+ results.push({ name: entry.name, type: "shared", dir: path2__default.default.join(sharedDir, entry.name) });
3957
+ }
3958
+ }
3959
+ }
3960
+ const featuresDir = resolveFeaturesDir(projectRoot);
3961
+ if (fs12__default.default.existsSync(featuresDir)) {
3962
+ const entries = fs12__default.default.readdirSync(featuresDir, { withFileTypes: true });
3963
+ for (const entry of entries) {
3964
+ if (entry.isDirectory() && !entry.name.startsWith(".")) {
3965
+ results.push({ name: entry.name, type: "feature", dir: path2__default.default.join(featuresDir, entry.name) });
3966
+ }
3967
+ }
3968
+ }
3969
+ return results;
3970
+ }
3971
+ function checkGitStatusForDir(projectRoot, dirPath) {
3972
+ try {
3973
+ const relPath = path2__default.default.relative(projectRoot, dirPath);
3974
+ const output = child_process.execSync(`git status --porcelain -- "${relPath}"`, {
3975
+ cwd: projectRoot,
3976
+ stdio: ["ignore", "pipe", "ignore"],
3977
+ encoding: "utf-8"
3978
+ }).trim();
3979
+ if (!output) {
3980
+ return { hasChanges: false, filesCount: 0 };
3981
+ }
3982
+ const lines = output.split("\n").filter((l) => l.trim().length > 0);
3983
+ return { hasChanges: true, filesCount: lines.length };
3984
+ } catch {
3985
+ return { hasChanges: false, filesCount: 0 };
3986
+ }
3987
+ }
3988
+ function getRegistryDiff(projectRoot) {
3989
+ const index = loadMasterIndex(projectRoot);
3990
+ const diskModules = discoverDiskModules(projectRoot);
3991
+ const diskMap = /* @__PURE__ */ new Map();
3992
+ for (const mod of diskModules) {
3993
+ diskMap.set(`${mod.type}:${mod.name}`, mod);
3994
+ }
3995
+ const allKeys = /* @__PURE__ */ new Set();
3996
+ for (const item of index.shared) {
3997
+ allKeys.add(`shared:${item.name}`);
3998
+ }
3999
+ for (const item of index.features) {
4000
+ allKeys.add(`feature:${item.name}`);
4001
+ }
4002
+ for (const mod of diskModules) {
4003
+ allKeys.add(`${mod.type}:${mod.name}`);
4004
+ }
4005
+ const statuses = [];
4006
+ for (const key of allKeys) {
4007
+ const [typeStr, name] = key.split(":");
4008
+ const type = typeStr;
4009
+ const registeredEntry = (type === "shared" ? index.shared : index.features).find(
4010
+ (e) => e.name === name
4011
+ );
4012
+ const isRegistered = Boolean(registeredEntry);
4013
+ const diskInfo = diskMap.get(key);
4014
+ const existsOnDisk = Boolean(diskInfo && fs12__default.default.existsSync(diskInfo.dir));
4015
+ let hasUncommittedGitChanges = false;
4016
+ let uncommittedFilesCount = 0;
4017
+ let hasIntegrityDrift = false;
4018
+ let localIntegrity;
4019
+ if (existsOnDisk && diskInfo) {
4020
+ const git = checkGitStatusForDir(projectRoot, diskInfo.dir);
4021
+ hasUncommittedGitChanges = git.hasChanges;
4022
+ uncommittedFilesCount = git.filesCount;
4023
+ localIntegrity = computeDirectoryHash(diskInfo.dir);
4024
+ if (isRegistered && registeredEntry?.integrity) {
4025
+ hasIntegrityDrift = localIntegrity !== registeredEntry.integrity;
4026
+ }
4027
+ }
4028
+ statuses.push({
4029
+ name,
4030
+ type,
4031
+ isRegistered,
4032
+ existsOnDisk,
4033
+ hasUncommittedGitChanges,
4034
+ uncommittedFilesCount,
4035
+ hasIntegrityDrift,
4036
+ localIntegrity,
4037
+ registeredIntegrity: registeredEntry?.integrity
4038
+ });
4039
+ }
4040
+ return statuses.sort((a, b) => a.type.localeCompare(b.type) || a.name.localeCompare(b.name));
4041
+ }
4042
+ async function registerModule(typeArg, nameArg, flags = {}) {
4043
+ const projectRoot = findProjectRoot();
4044
+ const index = loadMasterIndex(projectRoot);
4045
+ let type = typeArg?.trim();
4046
+ if (!type) {
4047
+ type = handleCancel(
4048
+ await p15__namespace.select({
4049
+ message: "Module type to register:",
4050
+ options: [
4051
+ { value: "feature", label: "Feature" },
4052
+ { value: "shared", label: "Shared Module" }
4053
+ ]
4054
+ })
4055
+ );
4056
+ }
4057
+ const diskModules = discoverDiskModules(projectRoot).filter((m) => m.type === type);
4058
+ const registeredNames = new Set((type === "shared" ? index.shared : index.features).map((m) => m.name));
4059
+ const unregistered = diskModules.filter((m) => !registeredNames.has(m.name));
4060
+ let name = nameArg?.trim();
4061
+ if (!name) {
4062
+ if (unregistered.length > 0) {
4063
+ const opts = unregistered.map((m) => ({ value: m.name, label: m.name, hint: "unregistered on disk" }));
4064
+ opts.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom name...", hint: "" });
4065
+ const choice = handleCancel(
4066
+ await p15__namespace.select({
4067
+ message: `Select a ${type} to register:`,
4068
+ options: opts
4069
+ })
4070
+ );
4071
+ if (choice === "__custom__") {
4072
+ name = handleCancel(
4073
+ await p15__namespace.text({
4074
+ message: `Enter ${type} name:`,
4075
+ validate: (v) => v && v.trim() ? void 0 : "Name cannot be empty"
4076
+ })
4077
+ ).trim();
4078
+ } else {
4079
+ name = choice;
4080
+ }
4081
+ } else {
4082
+ name = handleCancel(
4083
+ await p15__namespace.text({
4084
+ message: `Enter ${type} name to register:`,
4085
+ validate: (v) => v && v.trim() ? void 0 : "Name cannot be empty"
4086
+ })
4087
+ ).trim();
4088
+ }
4089
+ }
4090
+ const folder = type === "shared" ? resolveSharedDir(projectRoot) : resolveFeaturesDir(projectRoot);
4091
+ const modDir = path2__default.default.join(folder, name);
4092
+ if (!fs12__default.default.existsSync(modDir)) {
4093
+ p15__namespace.log.error(pc15__default.default.red(`Module directory does not exist: ${path2__default.default.relative(projectRoot, modDir)}`));
4094
+ process.exit(1);
4095
+ }
4096
+ const files = getAllFiles(modDir);
4097
+ const integrity = computeDirectoryHash(modDir, files);
4098
+ const manifestPath = path2__default.default.join(modDir, "registry.json");
4099
+ let manifest;
4100
+ if (fs12__default.default.existsSync(manifestPath)) {
4101
+ manifest = JSON.parse(fs12__default.default.readFileSync(manifestPath, "utf-8"));
4102
+ } else {
4103
+ manifest = {
4104
+ name,
4105
+ type,
4106
+ description: `${name} ${type} module`,
4107
+ version: "1.0.0",
4108
+ dependencies: {
4109
+ npm: [],
4110
+ shared: [],
4111
+ features: []
4112
+ },
4113
+ files: []
4114
+ };
4115
+ }
4116
+ manifest.name = name;
4117
+ manifest.type = type;
4118
+ manifest.files = files;
4119
+ manifest.integrity = integrity;
4120
+ fs12__default.default.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n", "utf-8");
4121
+ const targetList = type === "shared" ? index.shared : index.features;
4122
+ const existingIdx = targetList.findIndex((item) => item.name === name);
4123
+ if (existingIdx >= 0) {
4124
+ targetList[existingIdx] = {
4125
+ name,
4126
+ description: manifest.description,
4127
+ version: manifest.version,
4128
+ integrity
4129
+ };
4130
+ } else {
4131
+ targetList.push({
4132
+ name,
4133
+ description: manifest.description,
4134
+ version: manifest.version,
4135
+ integrity
4136
+ });
4137
+ }
4138
+ saveMasterIndex(projectRoot, index);
4139
+ p15__namespace.log.success(
4140
+ pc15__default.default.green(
4141
+ `\u2713 Module '${type}/${name}' registered in index.json (${files.length} files, ${integrity.slice(0, 18)}...)`
4142
+ )
4143
+ );
4144
+ }
4145
+ async function commitModule(typeArg, nameArg, flags = {}) {
4146
+ const projectRoot = findProjectRoot();
4147
+ let type = typeArg?.trim();
4148
+ if (!type) {
4149
+ type = handleCancel(
4150
+ await p15__namespace.select({
4151
+ message: "Module type to commit:",
4152
+ options: [
4153
+ { value: "feature", label: "Feature" },
4154
+ { value: "shared", label: "Shared Module" }
4155
+ ]
4156
+ })
4157
+ );
4158
+ }
4159
+ let name = nameArg?.trim();
4160
+ if (!name) {
4161
+ const diskModules = discoverDiskModules(projectRoot).filter((m) => m.type === type);
4162
+ const opts = diskModules.map((m) => {
4163
+ const git = checkGitStatusForDir(projectRoot, m.dir);
4164
+ return {
4165
+ value: m.name,
4166
+ label: m.name,
4167
+ hint: git.hasChanges ? `${git.filesCount} uncommitted files` : "clean working tree"
4168
+ };
4169
+ });
4170
+ name = handleCancel(
4171
+ await p15__namespace.select({
4172
+ message: `Select ${type} to commit:`,
4173
+ options: opts
4174
+ })
4175
+ );
4176
+ }
4177
+ const folder = type === "shared" ? resolveSharedDir(projectRoot) : resolveFeaturesDir(projectRoot);
4178
+ const modDir = path2__default.default.join(folder, name);
4179
+ if (!fs12__default.default.existsSync(modDir)) {
4180
+ p15__namespace.log.error(pc15__default.default.red(`Module directory does not exist: ${path2__default.default.relative(projectRoot, modDir)}`));
4181
+ process.exit(1);
4182
+ }
4183
+ const relModDir = path2__default.default.relative(projectRoot, modDir);
4184
+ const gitStatus = checkGitStatusForDir(projectRoot, modDir);
4185
+ if (!gitStatus.hasChanges) {
4186
+ p15__namespace.log.warn(pc15__default.default.yellow(`No uncommitted git changes found in '${type}/${name}'.`));
4187
+ return;
4188
+ }
4189
+ let message = flags.message?.trim();
4190
+ if (!message) {
4191
+ message = handleCancel(
4192
+ await p15__namespace.text({
4193
+ message: `Commit message for ${type}/${name}:`,
4194
+ placeholder: `e.g. feat(${name}): update use cases and dependencies`,
4195
+ validate: (v) => v && v.trim() ? void 0 : "Commit message cannot be empty"
4196
+ })
4197
+ ).trim();
4198
+ }
4199
+ const s = p15__namespace.spinner();
4200
+ s.start(`Committing '${type}/${name}' to git repository...`);
4201
+ try {
4202
+ child_process.execSync(`git add "${relModDir}"`, { cwd: projectRoot, stdio: ["ignore", "pipe", "pipe"] });
4203
+ child_process.execSync(`git commit -m "${message.replace(/"/g, '\\"')}" -- "${relModDir}"`, {
4204
+ cwd: projectRoot,
4205
+ stdio: ["ignore", "pipe", "pipe"]
4206
+ });
4207
+ s.stop(`Changes for '${type}/${name}' committed to git.`);
4208
+ p15__namespace.log.success(pc15__default.default.green(`\u2713 Committed '${type}/${name}': "${message}"`));
4209
+ } catch (err) {
4210
+ s.stop(pc15__default.default.red("Git commit failed."));
4211
+ p15__namespace.log.error(pc15__default.default.red(err.message || String(err)));
4212
+ process.exit(1);
4213
+ }
4214
+ }
4215
+ async function registerModuleChanges(typeArg, nameArg, flags = {}) {
4216
+ const projectRoot = findProjectRoot();
4217
+ const index = loadMasterIndex(projectRoot);
4218
+ let type = typeArg?.trim();
4219
+ if (!type) {
4220
+ type = handleCancel(
4221
+ await p15__namespace.select({
4222
+ message: "Module type to update:",
4223
+ options: [
4224
+ { value: "feature", label: "Feature" },
4225
+ { value: "shared", label: "Shared Module" }
4226
+ ]
4227
+ })
4228
+ );
4229
+ }
4230
+ let name = nameArg?.trim();
4231
+ if (!name) {
4232
+ const diskModules = discoverDiskModules(projectRoot).filter((m) => m.type === type);
4233
+ const diffs = getRegistryDiff(projectRoot).filter((d) => d.type === type && d.isRegistered);
4234
+ const opts = diskModules.map((m) => {
4235
+ const d = diffs.find((item) => item.name === m.name);
4236
+ return {
4237
+ value: m.name,
4238
+ label: m.name,
4239
+ hint: d?.hasIntegrityDrift ? "drift detected (needs register)" : "up to date"
4240
+ };
4241
+ });
4242
+ name = handleCancel(
4243
+ await p15__namespace.select({
4244
+ message: `Select ${type} to register changes:`,
4245
+ options: opts
4246
+ })
4247
+ );
4248
+ }
4249
+ const folder = type === "shared" ? resolveSharedDir(projectRoot) : resolveFeaturesDir(projectRoot);
4250
+ const modDir = path2__default.default.join(folder, name);
4251
+ if (!fs12__default.default.existsSync(modDir)) {
4252
+ p15__namespace.log.error(pc15__default.default.red(`Module directory does not exist: ${path2__default.default.relative(projectRoot, modDir)}`));
4253
+ process.exit(1);
4254
+ }
4255
+ const files = getAllFiles(modDir);
4256
+ const integrity = computeDirectoryHash(modDir, files);
4257
+ const manifestPath = path2__default.default.join(modDir, "registry.json");
4258
+ let manifest;
4259
+ if (fs12__default.default.existsSync(manifestPath)) {
4260
+ manifest = JSON.parse(fs12__default.default.readFileSync(manifestPath, "utf-8"));
4261
+ } else {
4262
+ manifest = {
4263
+ name,
4264
+ type,
4265
+ description: `${name} ${type} module`,
4266
+ version: "1.0.0",
4267
+ dependencies: { npm: [], shared: [], features: [] },
4268
+ files: []
4269
+ };
4270
+ }
4271
+ manifest.files = files;
4272
+ manifest.integrity = integrity;
4273
+ if (flags.version) {
4274
+ manifest.version = flags.version;
4275
+ }
4276
+ fs12__default.default.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n", "utf-8");
4277
+ const targetList = type === "shared" ? index.shared : index.features;
4278
+ const existingIdx = targetList.findIndex((item) => item.name === name);
4279
+ if (existingIdx >= 0) {
4280
+ targetList[existingIdx] = {
4281
+ name,
4282
+ description: manifest.description,
4283
+ version: manifest.version,
4284
+ integrity
4285
+ };
4286
+ } else {
4287
+ targetList.push({
4288
+ name,
4289
+ description: manifest.description,
4290
+ version: manifest.version,
4291
+ integrity
4292
+ });
4293
+ }
4294
+ saveMasterIndex(projectRoot, index);
4295
+ p15__namespace.log.success(
4296
+ pc15__default.default.green(`\u2713 Registered changes for '${type}/${name}' (new integrity: ${integrity.slice(0, 18)}...)`)
4297
+ );
4298
+ }
4299
+ async function unregisterModule(typeArg, nameArg, flags = {}) {
4300
+ const projectRoot = findProjectRoot();
4301
+ const index = loadMasterIndex(projectRoot);
4302
+ let type = typeArg?.trim();
4303
+ if (!type) {
4304
+ type = handleCancel(
4305
+ await p15__namespace.select({
4306
+ message: "Module type to unregister:",
4307
+ options: [
4308
+ { value: "feature", label: "Feature" },
4309
+ { value: "shared", label: "Shared Module" }
4310
+ ]
4311
+ })
4312
+ );
4313
+ }
4314
+ const registeredList = type === "shared" ? index.shared : index.features;
4315
+ if (registeredList.length === 0) {
4316
+ p15__namespace.log.warn(`No registered ${type} modules found in index.json.`);
4317
+ return;
4318
+ }
4319
+ let name = nameArg?.trim();
4320
+ if (!name) {
4321
+ name = handleCancel(
4322
+ await p15__namespace.select({
4323
+ message: `Select ${type} to unregister from index.json:`,
4324
+ options: registeredList.map((item) => ({
4325
+ value: item.name,
4326
+ label: item.name,
4327
+ hint: item.description
4328
+ }))
4329
+ })
4330
+ );
4331
+ }
4332
+ if (type === "shared") {
4333
+ index.shared = index.shared.filter((item) => item.name !== name);
4334
+ } else {
4335
+ index.features = index.features.filter((item) => item.name !== name);
4336
+ }
4337
+ saveMasterIndex(projectRoot, index);
4338
+ p15__namespace.log.success(
4339
+ pc15__default.default.green(
4340
+ `\u2713 Unregistered module '${type}/${name}' from index.json (source code retained on disk in ${type === "shared" ? "shared" : "features"}/${name})`
4341
+ )
4342
+ );
4343
+ }
4344
+ function diffRegistry(flags = {}) {
4345
+ const projectRoot = findProjectRoot();
4346
+ const diffs = getRegistryDiff(projectRoot);
4347
+ p15__namespace.log.step(pc15__default.default.bold("\u{1F4CB} Mason Registry Diff & Status Summary"));
4348
+ const unregistered = diffs.filter((d) => d.existsOnDisk && !d.isRegistered);
4349
+ const uncommitted = diffs.filter((d) => d.existsOnDisk && d.hasUncommittedGitChanges);
4350
+ const committedDrift = diffs.filter(
4351
+ (d) => d.existsOnDisk && d.isRegistered && !d.hasUncommittedGitChanges && d.hasIntegrityDrift
4352
+ );
4353
+ const upToDate = diffs.filter(
4354
+ (d) => d.existsOnDisk && d.isRegistered && !d.hasUncommittedGitChanges && !d.hasIntegrityDrift
4355
+ );
4356
+ const missing = diffs.filter((d) => !d.existsOnDisk && d.isRegistered);
4357
+ if (unregistered.length > 0) {
4358
+ p15__namespace.log.message(pc15__default.default.yellow(`
4359
+ \u{1F4E6} Unregistered Modules (${unregistered.length}):`));
4360
+ for (const mod of unregistered) {
4361
+ p15__namespace.log.message(` ${pc15__default.default.red("\u25CF")} ${pc15__default.default.bold(`${mod.type}/${mod.name}`)} ${pc15__default.default.dim("(not registered in index.json)")}`);
4362
+ }
4363
+ }
4364
+ if (uncommitted.length > 0) {
4365
+ p15__namespace.log.message(pc15__default.default.yellow(`
4366
+ \u{1F4BE} Uncommitted Changes in Git (${uncommitted.length}):`));
4367
+ for (const mod of uncommitted) {
4368
+ p15__namespace.log.message(
4369
+ ` ${pc15__default.default.yellow("\u25B2")} ${pc15__default.default.bold(`${mod.type}/${mod.name}`)} ${pc15__default.default.dim(`(${mod.uncommittedFilesCount} modified/untracked files)`)}`
4370
+ );
4371
+ }
4372
+ }
4373
+ if (committedDrift.length > 0) {
4374
+ p15__namespace.log.message(pc15__default.default.cyan(`
4375
+ \u{1F680} Committed But Unregistered Changes (${committedDrift.length}):`));
4376
+ for (const mod of committedDrift) {
4377
+ p15__namespace.log.message(
4378
+ ` ${pc15__default.default.cyan("\u25C6")} ${pc15__default.default.bold(`${mod.type}/${mod.name}`)} ${pc15__default.default.dim(
4379
+ `local: ${mod.localIntegrity?.slice(0, 16)}... != registered: ${mod.registeredIntegrity?.slice(0, 16)}...`
4380
+ )}`
4381
+ );
4382
+ }
4383
+ }
4384
+ if (missing.length > 0) {
4385
+ p15__namespace.log.message(pc15__default.default.red(`
4386
+ \u26A0\uFE0F Missing on Disk (${missing.length}):`));
4387
+ for (const mod of missing) {
4388
+ p15__namespace.log.message(` ${pc15__default.default.red("\u2715")} ${pc15__default.default.bold(`${mod.type}/${mod.name}`)} ${pc15__default.default.dim("(in index.json, but folder missing)")}`);
4389
+ }
4390
+ }
4391
+ if (upToDate.length > 0) {
4392
+ p15__namespace.log.message(pc15__default.default.green(`
4393
+ \u2713 Up to Date & Registered (${upToDate.length}):`));
4394
+ for (const mod of upToDate) {
4395
+ p15__namespace.log.message(` ${pc15__default.default.green("\u2714")} ${mod.type}/${mod.name} ${pc15__default.default.dim(`(${mod.registeredIntegrity?.slice(0, 18)}...)`)}`);
4396
+ }
4397
+ }
4398
+ p15__namespace.log.message("");
4399
+ }
4400
+ async function handleRegistry(actionArg, typeArg, nameArg, flags = {}) {
4401
+ let action = actionArg?.trim().toLowerCase();
4402
+ if (!action) {
4403
+ action = handleCancel(
4404
+ await p15__namespace.select({
4405
+ message: "\u{1F3DB}\uFE0F Mason Registry: What would you like to do?",
4406
+ options: [
4407
+ {
4408
+ value: "diff",
4409
+ label: "Inspect Diff & Status",
4410
+ hint: "Summary of unregistered, uncommitted, and drifted modules"
4411
+ },
4412
+ {
4413
+ value: "register",
4414
+ label: "Register Module",
4415
+ hint: "Add an existing module to index.json with integrity checksum"
4416
+ },
4417
+ {
4418
+ value: "commit",
4419
+ label: "Commit Changes",
4420
+ hint: "Commit a module's working changes to git in the background"
4421
+ },
4422
+ {
4423
+ value: "register-changes",
4424
+ label: "Register Changes",
4425
+ hint: "Update index.json & registry.json with latest checksums"
4426
+ },
4427
+ {
4428
+ value: "unregister",
4429
+ label: "Unregister Module",
4430
+ hint: "Remove module from index.json (source code stays on disk)"
4431
+ }
4432
+ ]
4433
+ })
4434
+ );
4435
+ }
4436
+ switch (action) {
4437
+ case "diff":
4438
+ case "status":
4439
+ diffRegistry(flags);
4440
+ break;
4441
+ case "register":
4442
+ await registerModule(typeArg, nameArg, flags);
4443
+ break;
4444
+ case "commit":
4445
+ await commitModule(typeArg, nameArg, flags);
4446
+ break;
4447
+ case "register-changes":
4448
+ case "update":
4449
+ await registerModuleChanges(typeArg, nameArg, flags);
4450
+ break;
4451
+ case "unregister":
4452
+ await unregisterModule(typeArg, nameArg, flags);
4453
+ break;
4454
+ default:
4455
+ p15__namespace.log.error(
4456
+ pc15__default.default.red(
4457
+ `Unknown registry action: "${action}". Valid actions: diff, register, commit, register-changes, unregister.`
4458
+ )
4459
+ );
4460
+ process.exit(1);
4461
+ }
4462
+ }
3438
4463
 
3439
4464
  // src/cli.ts
3440
4465
  function createCli() {
3441
4466
  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) => {
4467
+ 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
4468
  if (args && args.length > 0) {
3444
- p4__namespace.log.error(
3445
- pc13__default.default.red(
4469
+ p15__namespace.log.error(
4470
+ pc15__default.default.red(
3446
4471
  `Unknown command: "${args.join(" ")}". Use --help to list available commands.`
3447
4472
  )
3448
4473
  );
3449
4474
  process.exit(1);
3450
4475
  }
3451
4476
  try {
4477
+ const projectRoot = findProjectRoot();
4478
+ const mode = getEffectiveMode(projectRoot, flags);
4479
+ if (mode === "registry") {
4480
+ const choice2 = handleCancel(
4481
+ await p15__namespace.select({
4482
+ message: "\u{1F3DB}\uFE0F Mason Registry Maintainer: What would you like to do?",
4483
+ options: [
4484
+ {
4485
+ value: "diff",
4486
+ label: "Inspect Diff & Status",
4487
+ hint: "Overview of unregistered, uncommitted, or drifted modules"
4488
+ },
4489
+ {
4490
+ value: "create_feature",
4491
+ label: "Initialize New Feature",
4492
+ hint: "Scaffold a new feature slice under features/"
4493
+ },
4494
+ {
4495
+ value: "create_shared",
4496
+ label: "Initialize New Shared Module",
4497
+ hint: "Scaffold a new shared utility/port under shared/"
4498
+ },
4499
+ {
4500
+ value: "create_usecase",
4501
+ label: "Create New Use Case",
4502
+ hint: "Add a new use case to an existing feature"
4503
+ },
4504
+ {
4505
+ value: "register",
4506
+ label: "Register Module",
4507
+ hint: "Add a module to index.json with integrity checksum"
4508
+ },
4509
+ {
4510
+ value: "commit",
4511
+ label: "Commit Module Changes",
4512
+ hint: "Commit working changes in a module to git"
4513
+ },
4514
+ {
4515
+ value: "register_changes",
4516
+ label: "Register Changes",
4517
+ hint: "Recompute checksums and update index.json"
4518
+ },
4519
+ {
4520
+ value: "unregister",
4521
+ label: "Unregister Module",
4522
+ hint: "Remove from index.json but keep source on disk"
4523
+ },
4524
+ {
4525
+ value: "import",
4526
+ label: "Import from Remote Registry",
4527
+ hint: "Download a module from upstream"
4528
+ }
4529
+ ]
4530
+ })
4531
+ );
4532
+ switch (choice2) {
4533
+ case "diff":
4534
+ await handleRegistry("diff", void 0, void 0, flags);
4535
+ break;
4536
+ case "create_feature":
4537
+ await handleCreate("feature", void 0, flags);
4538
+ break;
4539
+ case "create_shared":
4540
+ await handleCreate("shared", void 0, flags);
4541
+ break;
4542
+ case "create_usecase":
4543
+ await handleCreate("usecase", void 0, flags);
4544
+ break;
4545
+ case "register":
4546
+ await handleRegistry("register", void 0, void 0, flags);
4547
+ break;
4548
+ case "commit":
4549
+ await handleRegistry("commit", void 0, void 0, flags);
4550
+ break;
4551
+ case "register_changes":
4552
+ await handleRegistry("register-changes", void 0, void 0, flags);
4553
+ break;
4554
+ case "unregister":
4555
+ await handleRegistry("unregister", void 0, void 0, flags);
4556
+ break;
4557
+ case "import":
4558
+ await handleImport(void 0, void 0, flags);
4559
+ break;
4560
+ }
4561
+ return;
4562
+ }
3452
4563
  const choice = handleCancel(
3453
- await p4__namespace.select({
4564
+ await p15__namespace.select({
3454
4565
  message: "\u{1F9F1} Mason: What would you like to do?",
3455
4566
  options: [
3456
4567
  {
3457
4568
  value: "create",
3458
4569
  label: "Create local component",
3459
- hint: "Scaffold a new feature, use case, domain entity, interface, etc."
4570
+ hint: "Scaffold a new feature, shared module, use case, domain entity, etc."
3460
4571
  },
3461
4572
  {
3462
4573
  value: "import",
3463
4574
  label: "Import from registry",
3464
4575
  hint: "Download and link pre-built shared modules or features from the registry"
4576
+ },
4577
+ {
4578
+ value: "registry",
4579
+ label: "Registry management",
4580
+ hint: "Inspect diff, register modules, or commit module changes"
3465
4581
  }
3466
4582
  ]
3467
4583
  })
3468
4584
  );
3469
4585
  if (choice === "create") {
3470
4586
  await handleCreate(void 0, void 0, flags);
4587
+ } else if (choice === "registry") {
4588
+ await handleRegistry(void 0, void 0, void 0, flags);
3471
4589
  } else {
3472
4590
  await handleImport(void 0, void 0, flags);
3473
4591
  }
@@ -3477,8 +4595,8 @@ function createCli() {
3477
4595
  });
3478
4596
  cli.command(
3479
4597
  "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 = {}) => {
4598
+ "Scaffold local Clean Architecture components (feature, shared, usecase, domain, etc.)"
4599
+ ).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
4600
  try {
3483
4601
  await handleCreate(type, name, flags);
3484
4602
  } catch (err) {
@@ -3495,8 +4613,28 @@ function createCli() {
3495
4613
  handleCliError(err);
3496
4614
  }
3497
4615
  });
4616
+ cli.command(
4617
+ "registry [action] [type] [name]",
4618
+ "Registry & version management (diff, register, commit, register-changes, unregister)"
4619
+ ).option("-m, --message <message>", "Commit message for 'commit' command").option("-y, --yes", "Accept defaults and skip interactive prompts").example("mason registry diff").example("mason registry register feature authn").example("mason registry commit shared hasher -m 'Add bcrypt engine'").example("mason registry register-changes shared hasher").example("mason registry unregister feature oldFeature").action(
4620
+ async (action, type, name, flags = {}) => {
4621
+ try {
4622
+ await handleRegistry(action, type, name, flags);
4623
+ } catch (err) {
4624
+ handleCliError(err);
4625
+ }
4626
+ }
4627
+ );
4628
+ cli.command("diff", "Inspect registry status and diff (alias for 'mason registry diff')").allowUnknownOptions().action(async (flags = {}) => {
4629
+ try {
4630
+ await handleRegistry("diff", void 0, void 0, flags);
4631
+ } catch (err) {
4632
+ handleCliError(err);
4633
+ }
4634
+ });
3498
4635
  const createSubtypes = [
3499
4636
  "feature",
4637
+ "shared",
3500
4638
  "usecase",
3501
4639
  "domain",
3502
4640
  "infrastructure",
@@ -3517,7 +4655,7 @@ function createCli() {
3517
4655
  });
3518
4656
  }
3519
4657
  cli.help();
3520
- cli.version("1.0.10");
4658
+ cli.version("1.0.13");
3521
4659
  return cli;
3522
4660
  }
3523
4661
  function handleCliError(err) {
@@ -3525,7 +4663,7 @@ function handleCliError(err) {
3525
4663
  process.exit(0);
3526
4664
  }
3527
4665
  const message = err instanceof Error ? err.message : String(err);
3528
- p4__namespace.log.error(pc13__default.default.red(`Error: ${message}`));
4666
+ p15__namespace.log.error(pc15__default.default.red(`Error: ${message}`));
3529
4667
  process.exit(1);
3530
4668
  }
3531
4669
  async function runCli(argv = process.argv) {
@@ -3535,6 +4673,7 @@ async function runCli(argv = process.argv) {
3535
4673
 
3536
4674
  exports.DEFAULT_REGISTRY_BASE = DEFAULT_REGISTRY_BASE;
3537
4675
  exports.PromptCancelledError = PromptCancelledError;
4676
+ exports.computeDirectoryHash = computeDirectoryHash;
3538
4677
  exports.concreteInfraTemplate = concreteInfraTemplate;
3539
4678
  exports.createCli = createCli;
3540
4679
  exports.detectPackageManager = detectPackageManager;
@@ -3562,16 +4701,22 @@ exports.generateInjectable = generateInjectable;
3562
4701
  exports.generateInjectableCode = generateInjectableCode;
3563
4702
  exports.generateInterface = generateInterface;
3564
4703
  exports.generateInterfaceCode = generateInterfaceCode;
4704
+ exports.generateShared = generateShared;
3565
4705
  exports.generateStubCode = generateStubCode;
3566
4706
  exports.generateUseCase = generateUseCase;
3567
4707
  exports.generateUseCaseCode = generateUseCaseCode;
3568
4708
  exports.generateUseCaseTestCode = generateUseCaseTestCode;
4709
+ exports.getAllFiles = getAllFiles;
4710
+ exports.getConfiguredFeaturesDir = getConfiguredFeaturesDir;
4711
+ exports.getConfiguredSharedDir = getConfiguredSharedDir;
4712
+ exports.getEffectiveMode = getEffectiveMode;
3569
4713
  exports.getMissingNpmPackages = getMissingNpmPackages;
3570
4714
  exports.getRegistryBase = getRegistryBase;
3571
4715
  exports.getTemplatesDir = getTemplatesDir;
3572
4716
  exports.handleCancel = handleCancel;
3573
4717
  exports.handleCreate = handleCreate;
3574
4718
  exports.handleImport = handleImport;
4719
+ exports.handleRegistry = handleRegistry;
3575
4720
  exports.hbs = hbs;
3576
4721
  exports.httpHandlerTemplate = httpHandlerTemplate;
3577
4722
  exports.injectableInterfaceTemplate = injectableInterfaceTemplate;
@@ -3583,8 +4728,10 @@ exports.levenshteinDistance = levenshteinDistance;
3583
4728
  exports.listDomainEntities = listDomainEntities;
3584
4729
  exports.listDomainInterfaces = listDomainInterfaces;
3585
4730
  exports.listExistingFeatures = listExistingFeatures;
4731
+ exports.listExistingShared = listExistingShared;
3586
4732
  exports.listUseCases = listUseCases;
3587
4733
  exports.loadInterfaceHierarchy = loadInterfaceHierarchy;
4734
+ exports.loadMasonConfig = loadMasonConfig;
3588
4735
  exports.loadTemplate = loadTemplate;
3589
4736
  exports.normalizeRegistryUrl = normalizeRegistryUrl;
3590
4737
  exports.parseInterfaceMethodsAndImports = parseInterfaceMethodsAndImports;
@@ -3595,7 +4742,9 @@ exports.removeInterfacePrefix = removeInterfacePrefix;
3595
4742
  exports.renderTemplate = renderTemplate;
3596
4743
  exports.renderTemplateFile = renderTemplateFile;
3597
4744
  exports.resolveFeaturesDir = resolveFeaturesDir;
4745
+ exports.resolveSharedDir = resolveSharedDir;
3598
4746
  exports.runCli = runCli;
4747
+ exports.runSharedCommand = runSharedCommand;
3599
4748
  exports.stubInfraTemplate = stubInfraTemplate;
3600
4749
  exports.synthesizeCrudMethods = synthesizeCrudMethods;
3601
4750
  exports.toCamelCase = toCamelCase;