@solid-stack/mason 1.0.22 → 1.0.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.cjs CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  var cac = require('cac');
5
5
  var p14 = require('@clack/prompts');
6
- var pc15 = require('picocolors');
6
+ var pc14 = require('picocolors');
7
7
  var fs9 = require('fs');
8
8
  var path2 = require('path');
9
9
  var url = require('url');
@@ -33,7 +33,7 @@ function _interopNamespace(e) {
33
33
  }
34
34
 
35
35
  var p14__namespace = /*#__PURE__*/_interopNamespace(p14);
36
- var pc15__default = /*#__PURE__*/_interopDefault(pc15);
36
+ var pc14__default = /*#__PURE__*/_interopDefault(pc14);
37
37
  var fs9__default = /*#__PURE__*/_interopDefault(fs9);
38
38
  var path2__default = /*#__PURE__*/_interopDefault(path2);
39
39
  var Handlebars__default = /*#__PURE__*/_interopDefault(Handlebars);
@@ -76,17 +76,17 @@ function writeFileSafely(absolutePath, content, options = {}) {
76
76
  function printGeneratedFiles(files, isDryRun = false) {
77
77
  console.log("");
78
78
  if (isDryRun) {
79
- p14__namespace.log.warn(pc15__default.default.yellow(pc15__default.default.bold("[DRY RUN PREVIEW] No files were written to disk.")));
79
+ p14__namespace.log.warn(pc14__default.default.yellow(pc14__default.default.bold("[DRY RUN PREVIEW] No files were written to disk.")));
80
80
  }
81
81
  for (const f of files) {
82
82
  if (f.action === "created") {
83
- p14__namespace.log.success(`${pc15__default.default.bold(f.relativePath)} ${pc15__default.default.dim("(created)")}`);
83
+ p14__namespace.log.success(`${pc14__default.default.bold(f.relativePath)} ${pc14__default.default.dim("(created)")}`);
84
84
  } else if (f.action === "overwritten") {
85
- p14__namespace.log.warn(`${pc15__default.default.bold(f.relativePath)} ${pc15__default.default.dim("(overwritten)")}`);
85
+ p14__namespace.log.warn(`${pc14__default.default.bold(f.relativePath)} ${pc14__default.default.dim("(overwritten)")}`);
86
86
  } else if (f.action === "skipped") {
87
- p14__namespace.log.info(`${pc15__default.default.dim(f.relativePath)} ${pc15__default.default.dim("(skipped - already exists, pass -o to overwrite)")}`);
87
+ p14__namespace.log.info(`${pc14__default.default.dim(f.relativePath)} ${pc14__default.default.dim("(skipped - already exists, pass -o to overwrite)")}`);
88
88
  } else {
89
- p14__namespace.log.info(`${pc15__default.default.bold(f.relativePath)} ${pc15__default.default.dim("(preview)")}`);
89
+ p14__namespace.log.info(`${pc14__default.default.bold(f.relativePath)} ${pc14__default.default.dim("(preview)")}`);
90
90
  }
91
91
  }
92
92
  }
@@ -1773,13 +1773,13 @@ function handleCancel(result) {
1773
1773
  }
1774
1774
  function printBanner() {
1775
1775
  console.log("");
1776
- p14__namespace.intro(pc15__default.default.bgCyan(pc15__default.default.black(" @solid-stack/mason ")));
1776
+ p14__namespace.intro(pc14__default.default.bgCyan(pc14__default.default.black(" @solid-stack/mason ")));
1777
1777
  }
1778
1778
  async function promptProperties(options = {}) {
1779
1779
  const properties = options.defaults ? [...options.defaults] : [];
1780
1780
  if (properties.length > 0) {
1781
1781
  const summary = properties.map((p16) => `${p16.name}: ${p16.type}${p16.optional ? "?" : ""}`).join(", ");
1782
- p14__namespace.log.info(pc15__default.default.dim(`Pre-configured fields: ${summary}`));
1782
+ p14__namespace.log.info(pc14__default.default.dim(`Pre-configured fields: ${summary}`));
1783
1783
  const addMore = handleCancel(
1784
1784
  await p14__namespace.confirm({
1785
1785
  message: "Would you like to add custom fields/properties?",
@@ -1790,7 +1790,7 @@ async function promptProperties(options = {}) {
1790
1790
  return properties;
1791
1791
  }
1792
1792
  }
1793
- p14__namespace.log.info(pc15__default.default.cyan("Enter properties (leave name blank when finished):"));
1793
+ p14__namespace.log.info(pc14__default.default.cyan("Enter properties (leave name blank when finished):"));
1794
1794
  while (true) {
1795
1795
  const name = handleCancel(
1796
1796
  await p14__namespace.text({
@@ -1838,7 +1838,7 @@ async function promptProperties(options = {}) {
1838
1838
  optional
1839
1839
  });
1840
1840
  p14__namespace.log.success(
1841
- pc15__default.default.green(`Added: ${name.trim()}${optional ? "?" : ""}: ${finalType.trim()}`)
1841
+ pc14__default.default.green(`Added: ${name.trim()}${optional ? "?" : ""}: ${finalType.trim()}`)
1842
1842
  );
1843
1843
  }
1844
1844
  return properties;
@@ -2098,7 +2098,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2098
2098
  }
2099
2099
  }
2100
2100
  const s = p14__namespace.spinner();
2101
- s.start(`Generating feature '${pc15__default.default.cyan(normalizedName)}'...`);
2101
+ s.start(`Generating feature '${pc14__default.default.cyan(normalizedName)}'...`);
2102
2102
  const options = {
2103
2103
  name: normalizedName,
2104
2104
  projectRoot,
@@ -2145,7 +2145,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2145
2145
  s.stop("Feature scaffolded successfully!");
2146
2146
  printGeneratedFiles(files, options.dryRun);
2147
2147
  p14__namespace.outro(
2148
- pc15__default.default.green(
2148
+ pc14__default.default.green(
2149
2149
  `\u2713 Feature '${normalizedName}' initialized in ${path2__default.default.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
2150
2150
  )
2151
2151
  );
@@ -2301,7 +2301,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
2301
2301
  );
2302
2302
  }
2303
2303
  const s = p14__namespace.spinner();
2304
- s.start(`Generating shared module '${pc15__default.default.cyan(normalizedName)}'...`);
2304
+ s.start(`Generating shared module '${pc14__default.default.cyan(normalizedName)}'...`);
2305
2305
  const files = generateShared({
2306
2306
  name: normalizedName,
2307
2307
  description,
@@ -2315,7 +2315,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
2315
2315
  s.stop("Shared module generated successfully!");
2316
2316
  printGeneratedFiles(files, Boolean(flags.dryRun));
2317
2317
  p14__namespace.outro(
2318
- pc15__default.default.green(
2318
+ pc14__default.default.green(
2319
2319
  `\u2713 Shared module '${normalizedName}' initialized in ${path2__default.default.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
2320
2320
  )
2321
2321
  );
@@ -2443,7 +2443,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2443
2443
  }
2444
2444
  }
2445
2445
  const s = p14__namespace.spinner();
2446
- s.start(`Generating use case '${pc15__default.default.cyan(ucName)}'...`);
2446
+ s.start(`Generating use case '${pc14__default.default.cyan(ucName)}'...`);
2447
2447
  const options = {
2448
2448
  name: ucName,
2449
2449
  feature,
@@ -2475,7 +2475,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
2475
2475
  }
2476
2476
  s.stop("Use case generated successfully!");
2477
2477
  printGeneratedFiles(files, options.dryRun);
2478
- p14__namespace.outro(pc15__default.default.green(`Use case '${ucName}' created in feature '${feature}'!`));
2478
+ p14__namespace.outro(pc14__default.default.green(`Use case '${ucName}' created in feature '${feature}'!`));
2479
2479
  }
2480
2480
  async function runDomainCommand(domainNameArg, flags = {}) {
2481
2481
  const projectRoot = findProjectRoot();
@@ -2546,7 +2546,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2546
2546
  });
2547
2547
  }
2548
2548
  const s = p14__namespace.spinner();
2549
- s.start(`Generating domain model '${pc15__default.default.cyan(entityName)}'...`);
2549
+ s.start(`Generating domain model '${pc14__default.default.cyan(entityName)}'...`);
2550
2550
  const options = {
2551
2551
  name: entityName,
2552
2552
  feature,
@@ -2559,7 +2559,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
2559
2559
  const file = generateDomain(options);
2560
2560
  s.stop("Domain model generated successfully!");
2561
2561
  printGeneratedFiles([file], options.dryRun);
2562
- p14__namespace.outro(pc15__default.default.green(`Domain model '${entityName}' created in feature '${feature}'!`));
2562
+ p14__namespace.outro(pc14__default.default.green(`Domain model '${entityName}' created in feature '${feature}'!`));
2563
2563
  }
2564
2564
  async function runInfrastructureCommand(classNameArg, flags = {}) {
2565
2565
  const projectRoot = findProjectRoot();
@@ -2728,7 +2728,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2728
2728
  registerInProvider = !flags.noProvider;
2729
2729
  }
2730
2730
  const s = p14__namespace.spinner();
2731
- s.start(`Generating infrastructure class '${pc15__default.default.cyan(className)}'...`);
2731
+ s.start(`Generating infrastructure class '${pc14__default.default.cyan(className)}'...`);
2732
2732
  const files = [];
2733
2733
  const options = {
2734
2734
  name: className,
@@ -2763,7 +2763,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2763
2763
  }
2764
2764
  s.stop("Infrastructure class generated successfully!");
2765
2765
  printGeneratedFiles(files, options.dryRun);
2766
- p14__namespace.outro(pc15__default.default.green(`Infrastructure class '${className}' created!`));
2766
+ p14__namespace.outro(pc14__default.default.green(`Infrastructure class '${className}' created!`));
2767
2767
  }
2768
2768
  async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2769
2769
  const projectRoot = findProjectRoot();
@@ -2876,7 +2876,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2876
2876
  }
2877
2877
  }
2878
2878
  const s = p14__namespace.spinner();
2879
- s.start(`Generating interface '${pc15__default.default.cyan(ifaceName)}'...`);
2879
+ s.start(`Generating interface '${pc14__default.default.cyan(ifaceName)}'...`);
2880
2880
  const files = [];
2881
2881
  const options = {
2882
2882
  name: ifaceName,
@@ -2925,7 +2925,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2925
2925
  }
2926
2926
  s.stop("Interface generated successfully!");
2927
2927
  printGeneratedFiles(files, options.dryRun);
2928
- p14__namespace.outro(pc15__default.default.green(`Interface '${ifaceName}' created in feature '${feature}'!`));
2928
+ p14__namespace.outro(pc14__default.default.green(`Interface '${ifaceName}' created in feature '${feature}'!`));
2929
2929
  }
2930
2930
  async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
2931
2931
  const projectRoot = findProjectRoot();
@@ -3042,7 +3042,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
3042
3042
  }
3043
3043
  }
3044
3044
  const s = p14__namespace.spinner();
3045
- s.start(`Generating HTTP handler '${pc15__default.default.cyan(className)}'...`);
3045
+ s.start(`Generating HTTP handler '${pc14__default.default.cyan(className)}'...`);
3046
3046
  const options = {
3047
3047
  name: className,
3048
3048
  feature,
@@ -3060,7 +3060,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
3060
3060
  const files = generateHttpHandler(options);
3061
3061
  s.stop("HTTP handler generated successfully!");
3062
3062
  printGeneratedFiles(files, options.dryRun);
3063
- p14__namespace.outro(pc15__default.default.green(`HTTP handler '${className}' created in feature '${feature}'!`));
3063
+ p14__namespace.outro(pc14__default.default.green(`HTTP handler '${className}' created in feature '${feature}'!`));
3064
3064
  }
3065
3065
  function generateInjectableCode(className, dependencies = [], methods = [], implementsInterface) {
3066
3066
  const formattedDeps = dependencies.map((dep) => ({
@@ -3216,7 +3216,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3216
3216
  );
3217
3217
  }
3218
3218
  const s = p14__namespace.spinner();
3219
- s.start(`Generating injectable '${pc15__default.default.cyan(className)}'...`);
3219
+ s.start(`Generating injectable '${pc14__default.default.cyan(className)}'...`);
3220
3220
  const options = {
3221
3221
  name: className,
3222
3222
  feature,
@@ -3230,7 +3230,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
3230
3230
  const files = generateInjectable(options);
3231
3231
  s.stop("Injectable class generated successfully!");
3232
3232
  printGeneratedFiles(files, options.dryRun);
3233
- p14__namespace.outro(pc15__default.default.green(`Injectable '${className}' created successfully!`));
3233
+ p14__namespace.outro(pc14__default.default.green(`Injectable '${className}' created successfully!`));
3234
3234
  }
3235
3235
  async function runInteractiveMenu(flags = {}) {
3236
3236
  printBanner();
@@ -3344,7 +3344,7 @@ async function handleCreate(type, name, flags = {}) {
3344
3344
  break;
3345
3345
  default:
3346
3346
  p14__namespace.log.error(
3347
- pc15__default.default.red(
3347
+ pc14__default.default.red(
3348
3348
  `Unknown component type: "${type}". Allowed types: feature, shared, usecase, domain, infrastructure, interface, httpHandler, injectable.`
3349
3349
  )
3350
3350
  );
@@ -3580,7 +3580,7 @@ function installNpmPackages(projectRoot, packages, pm) {
3580
3580
  installCmd = `npm install ${packages.join(" ")}`;
3581
3581
  break;
3582
3582
  }
3583
- p14__namespace.log.info(`Installing missing dependencies with ${manager}: ${pc15__default.default.cyan(packages.join(", "))}`);
3583
+ p14__namespace.log.info(`Installing missing dependencies with ${manager}: ${pc14__default.default.cyan(packages.join(", "))}`);
3584
3584
  child_process.execSync(installCmd, {
3585
3585
  cwd: projectRoot,
3586
3586
  stdio: "inherit"
@@ -3736,19 +3736,19 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3736
3736
  );
3737
3737
  }
3738
3738
  if (type !== "shared" && type !== "feature") {
3739
- p14__namespace.log.error(pc15__default.default.red(`Invalid module type: "${typeArg}". Must be "shared" or "feature".`));
3739
+ p14__namespace.log.error(pc14__default.default.red(`Invalid module type: "${typeArg}". Must be "shared" or "feature".`));
3740
3740
  process.exit(1);
3741
3741
  }
3742
3742
  const registryBase = getRegistryBase(projectRoot, flags.registry);
3743
3743
  const s = p14__namespace.spinner();
3744
- s.start(`Connecting to registry at ${pc15__default.default.dim(registryBase)}...`);
3744
+ s.start(`Connecting to registry at ${pc14__default.default.dim(registryBase)}...`);
3745
3745
  let index;
3746
3746
  try {
3747
3747
  index = await fetchRegistryIndex(registryBase);
3748
3748
  s.stop("Registry index loaded.");
3749
3749
  } catch (err) {
3750
- s.stop(pc15__default.default.red("Failed to load registry index."));
3751
- p14__namespace.log.error(pc15__default.default.red(err.message || String(err)));
3750
+ s.stop(pc14__default.default.red("Failed to load registry index."));
3751
+ p14__namespace.log.error(pc14__default.default.red(err.message || String(err)));
3752
3752
  process.exit(1);
3753
3753
  }
3754
3754
  const availableList = type === "shared" ? index.shared : index.features;
@@ -3772,9 +3772,9 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3772
3772
  }
3773
3773
  if (!candidateNames.includes(name)) {
3774
3774
  const closest = findClosestMatch(name, candidateNames);
3775
- p14__namespace.log.error(pc15__default.default.red(`\u274C Module '${name}' not found in the ${type} registry.`));
3775
+ p14__namespace.log.error(pc14__default.default.red(`\u274C Module '${name}' not found in the ${type} registry.`));
3776
3776
  if (closest) {
3777
- p14__namespace.log.info(pc15__default.default.yellow(`Did you mean '${pc15__default.default.cyan(closest)}'?`));
3777
+ p14__namespace.log.info(pc14__default.default.yellow(`Did you mean '${pc14__default.default.cyan(closest)}'?`));
3778
3778
  }
3779
3779
  process.exit(1);
3780
3780
  }
@@ -3786,7 +3786,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3786
3786
  try {
3787
3787
  manifest = await fetchModuleManifest(registryBase, modType, modName);
3788
3788
  } catch (err) {
3789
- p14__namespace.log.error(pc15__default.default.red(`Failed to fetch manifest for ${modType} '${modName}': ${err.message}`));
3789
+ p14__namespace.log.error(pc14__default.default.red(`Failed to fetch manifest for ${modType} '${modName}': ${err.message}`));
3790
3790
  process.exit(1);
3791
3791
  }
3792
3792
  nodes.set(key, {
@@ -3826,7 +3826,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3826
3826
  buildTopologicalOrder(rootKey);
3827
3827
  const resolutions = /* @__PURE__ */ new Map();
3828
3828
  const globalAliasMap = /* @__PURE__ */ new Map();
3829
- p14__namespace.log.step(`Importing ${type}: ${pc15__default.default.cyan(name)}...`);
3829
+ p14__namespace.log.step(`Importing ${type}: ${pc14__default.default.cyan(name)}...`);
3830
3830
  for (const key of orderedKeys) {
3831
3831
  const node = nodes.get(key);
3832
3832
  const { type: modType, name: registryName, manifest } = node;
@@ -3859,7 +3859,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3859
3859
  if (programmaticPointTo) {
3860
3860
  if (!checkModuleExists(projectRoot, modType, programmaticPointTo)) {
3861
3861
  p14__namespace.log.error(
3862
- pc15__default.default.red(
3862
+ pc14__default.default.red(
3863
3863
  `Module '${programmaticPointTo}' specified in --point-to does not exist in ${folder}.`
3864
3864
  )
3865
3865
  );
@@ -3895,7 +3895,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3895
3895
  }
3896
3896
  const existingDir = getExistingModuleDir(projectRoot, modType, registryName);
3897
3897
  if (!existingDir) {
3898
- p14__namespace.log.info(` \u21B3 Resolving required ${modType} dependency: ${pc15__default.default.cyan(registryName)}`);
3898
+ p14__namespace.log.info(` \u21B3 Resolving required ${modType} dependency: ${pc14__default.default.cyan(registryName)}`);
3899
3899
  resolutions.set(key, {
3900
3900
  type: modType,
3901
3901
  registryName,
@@ -4126,7 +4126,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
4126
4126
  const isOverwrite = Boolean(flags.overwrite || res.action === "overwrite");
4127
4127
  if (key !== rootKey) {
4128
4128
  p14__namespace.log.step(
4129
- `Importing ${res.type}: ${pc15__default.default.cyan(res.targetName)}${res.targetName !== res.registryName ? pc15__default.default.dim(` (from ${res.registryName})`) : ""}...`
4129
+ `Importing ${res.type}: ${pc14__default.default.cyan(res.targetName)}${res.targetName !== res.registryName ? pc14__default.default.dim(` (from ${res.registryName})`) : ""}...`
4130
4130
  );
4131
4131
  }
4132
4132
  if (res.action === "overwrite" && !flags.dryRun) {
@@ -4145,7 +4145,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
4145
4145
  const localFilePath = path2__default.default.join(targetDir, file);
4146
4146
  if (fs9__default.default.existsSync(localFilePath) && !isOverwrite) {
4147
4147
  p14__namespace.log.warn(
4148
- ` \u21B3 Skipping existing file: ${pc15__default.default.dim(path2__default.default.relative(projectRoot, localFilePath))} (use --overwrite to replace)`
4148
+ ` \u21B3 Skipping existing file: ${pc14__default.default.dim(path2__default.default.relative(projectRoot, localFilePath))} (use --overwrite to replace)`
4149
4149
  );
4150
4150
  continue;
4151
4151
  }
@@ -4157,13 +4157,13 @@ async function handleImport(typeArg, nameArg, flags = {}) {
4157
4157
  }
4158
4158
  const displayPath = path2__default.default.relative(projectRoot, localFilePath);
4159
4159
  totalWrittenFiles.push(displayPath);
4160
- p14__namespace.log.message(` ${pc15__default.default.green("+")} ${displayPath}`);
4160
+ p14__namespace.log.message(` ${pc14__default.default.green("+")} ${displayPath}`);
4161
4161
  }
4162
4162
  }
4163
4163
  const missingNpm = getMissingNpmPackages(projectRoot, Array.from(requiredNpmPackages));
4164
4164
  if (missingNpm.length > 0) {
4165
4165
  if (flags.skipInstall || flags.dryRun) {
4166
- p14__namespace.log.info(`Required npm dependencies to install: ${pc15__default.default.cyan(missingNpm.join(", "))}`);
4166
+ p14__namespace.log.info(`Required npm dependencies to install: ${pc14__default.default.cyan(missingNpm.join(", "))}`);
4167
4167
  } else {
4168
4168
  try {
4169
4169
  installNpmPackages(projectRoot, missingNpm);
@@ -4177,13 +4177,13 @@ async function handleImport(typeArg, nameArg, flags = {}) {
4177
4177
  }
4178
4178
  }
4179
4179
  if (flags.dryRun) {
4180
- p14__namespace.log.info(pc15__default.default.yellow("Dry run complete: No files were written to disk."));
4180
+ p14__namespace.log.info(pc14__default.default.yellow("Dry run complete: No files were written to disk."));
4181
4181
  } else {
4182
4182
  const rootRes = resolutions.get(rootKey);
4183
4183
  const baseDir = getModuleBaseDir(projectRoot, type);
4184
4184
  const displayTarget = path2__default.default.relative(projectRoot, path2__default.default.join(baseDir, rootRes.targetName));
4185
4185
  p14__namespace.log.success(
4186
- pc15__default.default.green(
4186
+ pc14__default.default.green(
4187
4187
  `Successfully imported ${type} '${name}' (${totalWrittenFiles.length} file${totalWrittenFiles.length === 1 ? "" : "s"} placed in ${displayTarget})`
4188
4188
  )
4189
4189
  );
@@ -4432,6 +4432,350 @@ function discoverAllFeatures(projectRoot) {
4432
4432
  dir: path2__default.default.join(featuresBase, e.name)
4433
4433
  })).sort((a, b) => a.name.localeCompare(b.name));
4434
4434
  }
4435
+ function extractClassMethods(content, className) {
4436
+ let clean = "";
4437
+ let inLineComment = false;
4438
+ let inBlockComment = false;
4439
+ let inString = null;
4440
+ let escaped = false;
4441
+ for (let i = 0; i < content.length; i++) {
4442
+ const c = content[i];
4443
+ const next = content[i + 1];
4444
+ if (inLineComment) {
4445
+ if (c === "\n") {
4446
+ inLineComment = false;
4447
+ clean += "\n";
4448
+ } else {
4449
+ clean += " ";
4450
+ }
4451
+ continue;
4452
+ }
4453
+ if (inBlockComment) {
4454
+ if (c === "*" && next === "/") {
4455
+ inBlockComment = false;
4456
+ clean += " ";
4457
+ i++;
4458
+ } else {
4459
+ clean += c === "\n" ? "\n" : " ";
4460
+ }
4461
+ continue;
4462
+ }
4463
+ if (inString) {
4464
+ clean += c === "\n" ? "\n" : " ";
4465
+ if (escaped) {
4466
+ escaped = false;
4467
+ } else if (c === "\\") {
4468
+ escaped = true;
4469
+ } else if (c === inString) {
4470
+ inString = null;
4471
+ }
4472
+ continue;
4473
+ }
4474
+ if (c === "/" && next === "/") {
4475
+ inLineComment = true;
4476
+ clean += " ";
4477
+ i++;
4478
+ continue;
4479
+ }
4480
+ if (c === "/" && next === "*") {
4481
+ inBlockComment = true;
4482
+ clean += " ";
4483
+ i++;
4484
+ continue;
4485
+ }
4486
+ if (c === '"' || c === "'" || c === "`") {
4487
+ inString = c;
4488
+ clean += " ";
4489
+ continue;
4490
+ }
4491
+ clean += c;
4492
+ }
4493
+ const classDeclRegex = new RegExp(`(?:export\\s+(?:default\\s+)?)?class\\s+${className}\\b[^{]*\\{`);
4494
+ const match = classDeclRegex.exec(clean);
4495
+ if (!match) return null;
4496
+ const classBodyStart = match.index + match[0].length - 1;
4497
+ const methods = [];
4498
+ let depth = 1;
4499
+ let parenDepth = 0;
4500
+ let atMemberStart = true;
4501
+ const methodRegex = /^\s*(?:@[a-zA-Z0-9_$]+(?:\([^)]*\))?\s+)*(?:(public|private|protected|static)\s+)*(?:(async)\s+)?(?:(get|set)\s+)?(#?[a-zA-Z0-9_$]+)\s*(?:<[^>]+>)?\s*\(/;
4502
+ const arrowRegex = /^\s*(?:(public|private|protected|static)\s+)?(#?[a-zA-Z0-9_$]+)\s*=\s*(?:async\s*)?\(/;
4503
+ for (let i = classBodyStart + 1; i < clean.length && depth > 0; i++) {
4504
+ const c = clean[i];
4505
+ if (c === "{") {
4506
+ depth++;
4507
+ atMemberStart = false;
4508
+ continue;
4509
+ }
4510
+ if (c === "}") {
4511
+ depth--;
4512
+ if (depth === 1) {
4513
+ atMemberStart = true;
4514
+ }
4515
+ continue;
4516
+ }
4517
+ if (depth === 1) {
4518
+ if (c === ";") {
4519
+ atMemberStart = true;
4520
+ parenDepth = 0;
4521
+ continue;
4522
+ }
4523
+ if (c === "(") {
4524
+ parenDepth++;
4525
+ } else if (c === ")") {
4526
+ parenDepth = Math.max(0, parenDepth - 1);
4527
+ }
4528
+ if (atMemberStart && parenDepth === 0) {
4529
+ if (/\s/.test(c)) continue;
4530
+ const slice = clean.slice(i);
4531
+ const methodMatch = slice.match(methodRegex);
4532
+ if (methodMatch) {
4533
+ const fullMatch = methodMatch[0];
4534
+ const beforeParen = fullMatch.slice(0, fullMatch.lastIndexOf("(")).trim();
4535
+ const tokens = beforeParen.split(/\s+/).filter(Boolean);
4536
+ const rawName = tokens[tokens.length - 1] || "";
4537
+ const methodName = rawName.replace(/<.*$/, "");
4538
+ const controlKeywords = ["if", "for", "while", "switch", "catch", "with"];
4539
+ if (!controlKeywords.includes(methodName)) {
4540
+ const isPrivate = tokens.includes("private") || tokens.includes("protected") || methodName.startsWith("#");
4541
+ const isAsync = tokens.includes("async");
4542
+ const isPublic = !isPrivate;
4543
+ const lineNum = content.slice(0, i).split("\n").length;
4544
+ const lineCode = content.split("\n")[lineNum - 1]?.trim() || "";
4545
+ methods.push({
4546
+ name: methodName,
4547
+ isAsync,
4548
+ isPublic,
4549
+ line: lineNum,
4550
+ code: lineCode
4551
+ });
4552
+ i += fullMatch.length - 1;
4553
+ parenDepth = 1;
4554
+ atMemberStart = false;
4555
+ continue;
4556
+ }
4557
+ }
4558
+ const arrowMatch = slice.match(arrowRegex);
4559
+ if (arrowMatch) {
4560
+ const fullMatch = arrowMatch[0];
4561
+ const beforeEquals = fullMatch.split("=")[0].trim();
4562
+ const tokens = beforeEquals.split(/\s+/).filter(Boolean);
4563
+ const rawName = tokens[tokens.length - 1] || "";
4564
+ const isPrivate = tokens.includes("private") || tokens.includes("protected") || rawName.startsWith("#");
4565
+ const isAsync = fullMatch.includes("async");
4566
+ const isPublic = !isPrivate;
4567
+ const lineNum = content.slice(0, i).split("\n").length;
4568
+ const lineCode = content.split("\n")[lineNum - 1]?.trim() || "";
4569
+ methods.push({
4570
+ name: rawName,
4571
+ isAsync,
4572
+ isPublic,
4573
+ line: lineNum,
4574
+ code: lineCode
4575
+ });
4576
+ i += fullMatch.length - 1;
4577
+ parenDepth = 1;
4578
+ atMemberStart = false;
4579
+ continue;
4580
+ }
4581
+ atMemberStart = false;
4582
+ }
4583
+ }
4584
+ }
4585
+ return methods;
4586
+ }
4587
+ function checkUseCaseShape(featureName, filePath, relativePath, content, addViolation) {
4588
+ const fileName = path2__default.default.basename(filePath);
4589
+ const lines = content.split(/\r?\n/);
4590
+ const classMatch = content.match(/(?:export\s+(?:default\s+)?)?class\s+([A-Za-z0-9_]+)/);
4591
+ if (!classMatch) {
4592
+ addViolation({
4593
+ feature: featureName,
4594
+ layer: "useCases",
4595
+ file: filePath,
4596
+ relativePath,
4597
+ line: 1,
4598
+ category: "Use Case Shape Violation",
4599
+ code: lines[0]?.trim() || "",
4600
+ ruleId: "U_SHAPE_CLASS",
4601
+ severity: "error",
4602
+ advice: `Use case file '${fileName}' must define and export a class.`,
4603
+ fixExample: {
4604
+ bad: "// Missing class definition",
4605
+ good: `export class ${fileName.replace(/\.(ts|js)$/, "")} { ... }`
4606
+ }
4607
+ });
4608
+ return;
4609
+ }
4610
+ const className = classMatch[1];
4611
+ let classLine = 1;
4612
+ for (let i = 0; i < lines.length; i++) {
4613
+ if (new RegExp(`(?:export\\s+(?:default\\s+)?)?class\\s+${className}\\b`).test(lines[i])) {
4614
+ classLine = i + 1;
4615
+ break;
4616
+ }
4617
+ }
4618
+ if (!/@MakeInjectable\b/.test(content)) {
4619
+ addViolation({
4620
+ feature: featureName,
4621
+ layer: "useCases",
4622
+ file: filePath,
4623
+ relativePath,
4624
+ line: classLine,
4625
+ category: "Use Case Shape Violation",
4626
+ code: lines[classLine - 1]?.trim() || `class ${className}`,
4627
+ ruleId: "U_SHAPE_DECORATOR",
4628
+ severity: "error",
4629
+ advice: `Use case class '${className}' in '${fileName}' must be decorated with @MakeInjectable.`,
4630
+ fixExample: {
4631
+ bad: lines[classLine - 1]?.trim() || `class ${className}`,
4632
+ good: `@MakeInjectable
4633
+ export class ${className} { ... }`
4634
+ }
4635
+ });
4636
+ }
4637
+ const hasAsyncExecute = /(?:public\s+)?async\s+execute\s*(?:<[^>]+>)?\s*\(/.test(content);
4638
+ if (!hasAsyncExecute) {
4639
+ const hasSyncExecute = /(?:public\s+)?execute\s*(?:<[^>]+>)?\s*\(/.test(content);
4640
+ addViolation({
4641
+ feature: featureName,
4642
+ layer: "useCases",
4643
+ file: filePath,
4644
+ relativePath,
4645
+ line: classLine,
4646
+ category: "Use Case Shape Violation",
4647
+ code: lines[classLine - 1]?.trim() || `class ${className}`,
4648
+ ruleId: "U_SHAPE_EXECUTE",
4649
+ severity: "error",
4650
+ advice: hasSyncExecute ? `Use case class '${className}' in '${fileName}' must have an 'async execute' method (found synchronous 'execute').` : `Use case class '${className}' in '${fileName}' must implement an 'async execute(...)' method.`,
4651
+ fixExample: {
4652
+ bad: hasSyncExecute ? "execute(props: ...) { ... }" : `class ${className} { ... }`,
4653
+ good: "async execute(props: ...): Promise<...> { ... }"
4654
+ }
4655
+ });
4656
+ }
4657
+ const classMethods = extractClassMethods(content, className);
4658
+ if (classMethods) {
4659
+ const extraPublicMethods = classMethods.filter(
4660
+ (m) => m.isPublic && m.name !== "constructor" && m.name !== "execute"
4661
+ );
4662
+ for (const m of extraPublicMethods) {
4663
+ addViolation({
4664
+ feature: featureName,
4665
+ layer: "useCases",
4666
+ file: filePath,
4667
+ relativePath,
4668
+ line: m.line,
4669
+ category: "Use Case Shape Violation",
4670
+ code: m.code || `public ${m.name}(...)`,
4671
+ ruleId: "U_SHAPE_ONLY_EXECUTE",
4672
+ severity: "error",
4673
+ advice: `Use case class '${className}' in '${fileName}' has disallowed public method '${m.name}'. The only allowed public methods are constructor and async execute. Extract extra methods into their own use cases, or make them private.`,
4674
+ fixExample: {
4675
+ bad: m.code || `${m.name}(...) { ... }`,
4676
+ good: `// Extract '${m.name}' into its own dedicated Use Case, or make it private if it is internal helper logic:
4677
+ private ${m.name}(...) { ... }`
4678
+ }
4679
+ });
4680
+ }
4681
+ }
4682
+ }
4683
+ function checkServiceShape(featureName, filePath, relativePath, content, addViolation) {
4684
+ const fileName = path2__default.default.basename(filePath);
4685
+ const lines = content.split(/\r?\n/);
4686
+ const classMatch = content.match(/(?:export\s+(?:default\s+)?)?class\s+([A-Za-z0-9_]+)/);
4687
+ if (!classMatch) {
4688
+ addViolation({
4689
+ feature: featureName,
4690
+ layer: "services",
4691
+ file: filePath,
4692
+ relativePath,
4693
+ line: 1,
4694
+ category: "Service Shape Violation",
4695
+ code: lines[0]?.trim() || "",
4696
+ ruleId: "S_SHAPE_CLASS",
4697
+ severity: "error",
4698
+ advice: `Service file '${fileName}' must define and export a class.`,
4699
+ fixExample: {
4700
+ bad: "// Missing class definition",
4701
+ good: `export class ${fileName.replace(/\.(ts|js)$/, "")} { ... }`
4702
+ }
4703
+ });
4704
+ return;
4705
+ }
4706
+ const className = classMatch[1];
4707
+ let classLine = 1;
4708
+ for (let i = 0; i < lines.length; i++) {
4709
+ if (new RegExp(`(?:export\\s+(?:default\\s+)?)?class\\s+${className}\\b`).test(lines[i])) {
4710
+ classLine = i + 1;
4711
+ break;
4712
+ }
4713
+ }
4714
+ if (!/@MakeInjectable\b/.test(content)) {
4715
+ addViolation({
4716
+ feature: featureName,
4717
+ layer: "services",
4718
+ file: filePath,
4719
+ relativePath,
4720
+ line: classLine,
4721
+ category: "Service Shape Violation",
4722
+ code: lines[classLine - 1]?.trim() || `class ${className}`,
4723
+ ruleId: "S_SHAPE_DECORATOR",
4724
+ severity: "error",
4725
+ advice: `Service class '${className}' in '${fileName}' must be decorated with @MakeInjectable.`,
4726
+ fixExample: {
4727
+ bad: lines[classLine - 1]?.trim() || `class ${className}`,
4728
+ good: `@MakeInjectable
4729
+ export class ${className} { ... }`
4730
+ }
4731
+ });
4732
+ }
4733
+ const hasAsyncExecute = /(?:public\s+)?async\s+execute\s*(?:<[^>]+>)?\s*\(/.test(content);
4734
+ if (!hasAsyncExecute) {
4735
+ const hasSyncExecute = /(?:public\s+)?execute\s*(?:<[^>]+>)?\s*\(/.test(content);
4736
+ addViolation({
4737
+ feature: featureName,
4738
+ layer: "services",
4739
+ file: filePath,
4740
+ relativePath,
4741
+ line: classLine,
4742
+ category: "Service Shape Violation",
4743
+ code: lines[classLine - 1]?.trim() || `class ${className}`,
4744
+ ruleId: "S_SHAPE_EXECUTE",
4745
+ severity: "error",
4746
+ advice: hasSyncExecute ? `Service class '${className}' in '${fileName}' must have an 'async execute' method (found synchronous 'execute').` : `Service class '${className}' in '${fileName}' must implement an 'async execute(...)' method.`,
4747
+ fixExample: {
4748
+ bad: hasSyncExecute ? "execute(props: ...) { ... }" : `class ${className} { ... }`,
4749
+ good: "async execute(props: ...): Promise<...> { ... }"
4750
+ }
4751
+ });
4752
+ }
4753
+ const classMethods = extractClassMethods(content, className);
4754
+ if (classMethods) {
4755
+ const extraPublicMethods = classMethods.filter(
4756
+ (m) => m.isPublic && m.name !== "constructor" && m.name !== "execute"
4757
+ );
4758
+ for (const m of extraPublicMethods) {
4759
+ addViolation({
4760
+ feature: featureName,
4761
+ layer: "services",
4762
+ file: filePath,
4763
+ relativePath,
4764
+ line: m.line,
4765
+ category: "Service Shape Violation",
4766
+ code: m.code || `public ${m.name}(...)`,
4767
+ ruleId: "S_SHAPE_ONLY_EXECUTE",
4768
+ severity: "error",
4769
+ advice: `Service class '${className}' in '${fileName}' has disallowed public method '${m.name}'. The only allowed public methods are constructor and async execute. If a service has multiple public methods, those should be on their own services instead of being cramped in a single service.`,
4770
+ fixExample: {
4771
+ bad: m.code || `${m.name}(...) { ... }`,
4772
+ good: `// Extract '${m.name}' into its own dedicated Service, or make it private if it is internal helper logic:
4773
+ private ${m.name}(...) { ... }`
4774
+ }
4775
+ });
4776
+ }
4777
+ }
4778
+ }
4435
4779
  function lintCodebase(options = {}) {
4436
4780
  const startTime = Date.now();
4437
4781
  const projectRoot = options.projectRoot || findProjectRoot(options.targetDir || process.cwd());
@@ -4449,13 +4793,9 @@ function lintCodebase(options = {}) {
4449
4793
  featureResult.passed = false;
4450
4794
  }
4451
4795
  if (failFast) {
4452
- const errorMsg = `[FAIL FAST] Dependency rule violation detected:
4453
- Feature: ${v.feature}
4454
- Category: ${v.category}
4455
- Rule: ${v.ruleId}
4456
- Location: ${v.relativePath}:${v.line}
4457
- Code: ${v.code}
4458
- Explanation: ${v.advice}`;
4796
+ const errorMsg = `Dependency rule violation detected in ${v.relativePath}:${v.line}:
4797
+ [${v.category}] ${v.advice}
4798
+ Code: ${v.code}`;
4459
4799
  throw new DependencyRuleError(errorMsg, [v], true);
4460
4800
  }
4461
4801
  };
@@ -4479,6 +4819,14 @@ function lintCodebase(options = {}) {
4479
4819
  if (!layer) continue;
4480
4820
  const content = fs9__default.default.readFileSync(file, "utf-8");
4481
4821
  const statements = extractImportStatements(content);
4822
+ const baseName = path2__default.default.basename(file);
4823
+ const isBarrel = baseName === "index.ts" || baseName === "index.js";
4824
+ if (layer === "useCases" && !isBarrel) {
4825
+ checkUseCaseShape(feat.name, file, relPath, content, addViolation);
4826
+ }
4827
+ if (layer === "services" && !isBarrel) {
4828
+ checkServiceShape(feat.name, file, relPath, content, addViolation);
4829
+ }
4482
4830
  if (layer === "domain") {
4483
4831
  const lines = content.split(/\r?\n/);
4484
4832
  for (let i = 0; i < lines.length; i++) {
@@ -4889,6 +5237,24 @@ function lintCodebase(options = {}) {
4889
5237
  }
4890
5238
  });
4891
5239
  }
5240
+ if (target.targetLayer === "services") {
5241
+ addViolation({
5242
+ feature: feat.name,
5243
+ layer,
5244
+ file,
5245
+ relativePath: relPath,
5246
+ line: stmt.line,
5247
+ category: "Presentation Layer Violation",
5248
+ code: stmt.rawCode,
5249
+ ruleId: "P4_DIRECT_SERVICE",
5250
+ severity: "error",
5251
+ advice: "Presentation handlers must not depend on services directly. They must use use cases, and use cases will then depend on the service.",
5252
+ fixExample: {
5253
+ bad: stmt.rawCode,
5254
+ good: "// Inject a Use Case via static deps instead of a Service."
5255
+ }
5256
+ });
5257
+ }
4892
5258
  }
4893
5259
  }
4894
5260
  }
@@ -4943,87 +5309,23 @@ ${compiledDetails}
4943
5309
  }
4944
5310
 
4945
5311
  // src/commands/lint.ts
4946
- function printLintBanner(mode) {
4947
- console.log("");
4948
- const title = " Mason Clean Architecture Dependency Linter ";
4949
- const line = "\u2501".repeat(title.length + 2);
4950
- console.log(pc15__default.default.cyan(` \u250F${line}\u2513`));
4951
- console.log(` ${pc15__default.default.cyan("\u2503")} ${pc15__default.default.bgCyan(pc15__default.default.black(title))} ${pc15__default.default.cyan("\u2503")}`);
4952
- console.log(pc15__default.default.cyan(` \u2517${line}\u251B`));
4953
- const modeText = mode === "failFast" ? `${pc15__default.default.yellow("Fail-Fast")} ${pc15__default.default.dim("(default: stops at first error)")}` : `${pc15__default.default.magenta("Complete")} ${pc15__default.default.dim("(--complete: compiles all errors)")}`;
4954
- console.log(` Mode: ${modeText}`);
4955
- console.log("");
4956
- }
4957
- function formatViolation(v, showFixHint = true) {
5312
+ function formatViolation(v) {
4958
5313
  const lines = [];
4959
- const categoryBadge = pc15__default.default.red(pc15__default.default.bold(`[${v.category}]`));
4960
- const location = pc15__default.default.white(`${v.relativePath}:${pc15__default.default.yellow(String(v.line))}`);
4961
- lines.push(` ${categoryBadge} ${location}`);
4962
- lines.push(` ${pc15__default.default.dim("Rule ID:")} ${pc15__default.default.yellow(v.ruleId)}`);
4963
- lines.push(` ${pc15__default.default.dim("Code:")} ${pc15__default.default.magenta(v.code)}`);
4964
- lines.push(` ${pc15__default.default.dim("Advice:")} ${pc15__default.default.cyan(v.advice)}`);
4965
- if (showFixHint && v.fixExample) {
4966
- lines.push(` ${pc15__default.default.dim("Example:")}`);
4967
- lines.push(` ${pc15__default.default.red(`- ${v.fixExample.bad}`)}`);
4968
- lines.push(` ${pc15__default.default.green(`+ ${v.fixExample.good}`)}`);
4969
- }
4970
- return lines.join("\n");
4971
- }
4972
- function formatFeatureResult(res, options = {}) {
4973
- const lines = [];
4974
- if (res.passed) {
4975
- lines.push(
4976
- ` ${pc15__default.default.green("\u2714")} ${pc15__default.default.bold(res.featureName)} ${pc15__default.default.dim(
4977
- `(${res.scannedFilesCount} files checked - clean)`
4978
- )}`
4979
- );
4980
- return lines.join("\n");
4981
- }
4982
- const countBadge = pc15__default.default.red(
4983
- `(${res.violations.length} violation${res.violations.length > 1 ? "s" : ""})`
5314
+ lines.push(pc14__default.default.underline(pc14__default.default.cyan(v.relativePath)) + pc14__default.default.dim(":") + pc14__default.default.yellow(String(v.line)));
5315
+ const gutter = `${String(v.line).padStart(4, " ")} | `;
5316
+ lines.push(pc14__default.default.dim(gutter) + v.code);
5317
+ const categoryShort = v.category.replace(" Violation", "");
5318
+ lines.push(
5319
+ " " + pc14__default.default.red(pc14__default.default.bold("\u2716")) + " " + pc14__default.default.red(`[${categoryShort}]`) + " " + v.advice
4984
5320
  );
4985
- lines.push(`
4986
- ${pc15__default.default.red("\u2716")} ${pc15__default.default.bold(pc15__default.default.white(res.featureName))} ${countBadge}`);
4987
- lines.push(` ${pc15__default.default.dim("Path:")} ${res.featureDir}`);
4988
- lines.push("");
4989
- for (const v of res.violations) {
4990
- lines.push(formatViolation(v, options.fixHint !== false));
4991
- lines.push("");
5321
+ if (v.fixExample?.good) {
5322
+ lines.push(" " + pc14__default.default.dim("\u2192") + " " + pc14__default.default.dim(v.fixExample.good));
4992
5323
  }
4993
5324
  return lines.join("\n");
4994
5325
  }
4995
- function printTerminalReport(summary, options = {}) {
4996
- for (const res of summary.results) {
4997
- const formatted = formatFeatureResult(res, options);
4998
- if (formatted.trim()) {
4999
- console.log(formatted);
5000
- }
5001
- }
5002
- console.log("\u2501".repeat(70));
5003
- const statusIcon = summary.totalViolations === 0 ? pc15__default.default.green("\u2714 PASS") : pc15__default.default.red("\u2716 FAIL");
5004
- console.log(
5005
- ` ${statusIcon} ${pc15__default.default.bold("Dependency Rules Audit Result")} ${pc15__default.default.dim(
5006
- `(${summary.durationMs}ms)`
5007
- )}`
5008
- );
5009
- console.log(
5010
- ` ${pc15__default.default.bold("Features:")} ${pc15__default.default.cyan(String(summary.totalFeatures))} total | ${pc15__default.default.green(
5011
- String(summary.cleanFeatures)
5012
- )} clean | ${summary.violatedFeatures > 0 ? pc15__default.default.red(String(summary.violatedFeatures)) : pc15__default.default.green("0")} with violations`
5013
- );
5014
- console.log(
5015
- ` ${pc15__default.default.bold("Violations:")} ${summary.totalViolations > 0 ? pc15__default.default.red(pc15__default.default.bold(String(summary.totalViolations))) : pc15__default.default.green("0")} rule violations found`
5016
- );
5017
- console.log("\u2501".repeat(70));
5018
- console.log("");
5019
- }
5020
5326
  async function handleLint(flags = {}) {
5021
5327
  const complete = Boolean(flags.complete);
5022
- const mode = complete ? "complete" : "failFast";
5023
5328
  const quiet = Boolean(flags.quiet);
5024
- if (!quiet) {
5025
- printLintBanner(mode);
5026
- }
5027
5329
  const options = {
5028
5330
  complete,
5029
5331
  quiet,
@@ -5034,14 +5336,23 @@ async function handleLint(flags = {}) {
5034
5336
  };
5035
5337
  try {
5036
5338
  const summary = lintCodebase(options);
5037
- if (!quiet) {
5038
- printTerminalReport(summary, options);
5039
- }
5040
5339
  if (summary.totalViolations > 0) {
5340
+ if (!quiet) {
5341
+ console.log("");
5342
+ for (const v of summary.allViolations) {
5343
+ console.log(formatViolation(v));
5344
+ console.log("");
5345
+ }
5346
+ console.log(
5347
+ pc14__default.default.red(
5348
+ `\u2716 Dependency rules check failed with ${summary.totalViolations} error${summary.totalViolations > 1 ? "s" : ""} across ${summary.violatedFeatures} feature${summary.violatedFeatures > 1 ? "s" : ""} (${summary.totalFeatures} features scanned).`
5349
+ )
5350
+ );
5351
+ console.log("");
5352
+ }
5041
5353
  const errorHeader = `Dependency rule audit failed with ${summary.totalViolations} violation(s) across ${summary.violatedFeatures} feature(s):`;
5042
5354
  const compiledDetails = summary.allViolations.map(
5043
5355
  (v, i) => `${i + 1}. [${v.category}] ${v.relativePath}:${v.line}
5044
- Rule: ${v.ruleId}
5045
5356
  Code: ${v.code}
5046
5357
  Advice: ${v.advice}`
5047
5358
  ).join("\n\n");
@@ -5052,20 +5363,22 @@ ${compiledDetails}
5052
5363
  }
5053
5364
  if (!quiet) {
5054
5365
  console.log(
5055
- pc15__default.default.green(`\u2714 All dependency rules are being followed! (${summary.totalFeatures} features checked)`)
5366
+ pc14__default.default.green(
5367
+ `\u2714 Dependency rules verified across ${summary.totalFeatures} feature${summary.totalFeatures === 1 ? "" : "s"}.`
5368
+ )
5056
5369
  );
5057
- console.log("");
5058
5370
  }
5059
5371
  return summary;
5060
5372
  } catch (err) {
5061
5373
  if (err instanceof DependencyRuleError && err.isFailFast) {
5062
5374
  if (!quiet) {
5063
- console.log(pc15__default.default.red(`
5064
- \u2716 [FAIL FAST] Terminating on first detected dependency rule violation:`));
5375
+ console.log("");
5065
5376
  const v = err.violations[0];
5066
5377
  if (v) {
5067
- console.log(formatViolation(v, options.fixHint !== false));
5378
+ console.log(formatViolation(v));
5379
+ console.log("");
5068
5380
  }
5381
+ console.log(pc14__default.default.red(`\u2716 Dependency rules check failed (1 error).`));
5069
5382
  console.log("");
5070
5383
  }
5071
5384
  }
@@ -5079,7 +5392,7 @@ function createCli() {
5079
5392
  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").option("--point-to <mapping>", "Point a dependency to an existing local module (e.g. --point-to time:mytime)").option("--alias <mapping>", "Import a dependency fresh under an alias name (e.g. --alias time:time-mason)").option("--tests", "Include test files (use --no-tests to exclude)").action(async (args, flags) => {
5080
5393
  if (args && args.length > 0) {
5081
5394
  p14__namespace.log.error(
5082
- pc15__default.default.red(
5395
+ pc14__default.default.red(
5083
5396
  `Unknown command: "${args.join(" ")}". Use --help to list available commands.`
5084
5397
  )
5085
5398
  );
@@ -5198,6 +5511,12 @@ function createCli() {
5198
5511
  targetDir: target || flags.target || flags.dest
5199
5512
  });
5200
5513
  } catch (err) {
5514
+ if (err instanceof DependencyRuleError) {
5515
+ if (process.env.VITEST) {
5516
+ throw err;
5517
+ }
5518
+ process.exit(1);
5519
+ }
5201
5520
  handleCliError(err);
5202
5521
  }
5203
5522
  });
@@ -5232,7 +5551,7 @@ function handleCliError(err) {
5232
5551
  process.exit(0);
5233
5552
  }
5234
5553
  const message = err instanceof Error ? err.message : String(err);
5235
- p14__namespace.log.error(pc15__default.default.red(`Error: ${message}`));
5554
+ p14__namespace.log.error(pc14__default.default.red(`Error: ${message}`));
5236
5555
  if (process.env.VITEST) {
5237
5556
  throw err;
5238
5557
  }