@atlashub/smartstack-mcp 1.21.0 → 1.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2438,6 +2438,27 @@ Handlebars.registerHelper("camelCase", (str) => {
2438
2438
  Handlebars.registerHelper("kebabCase", (str) => {
2439
2439
  return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
2440
2440
  });
2441
+ function resolveHierarchy(navRoute) {
2442
+ if (!navRoute) {
2443
+ return { context: "", application: "", module: "", domainPath: "", infraPath: "", controllerArea: "" };
2444
+ }
2445
+ const segments = navRoute.split(".");
2446
+ const toPascal = (s) => s.split("-").map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join("");
2447
+ const context = segments[0] ? toPascal(segments[0]) : "";
2448
+ const application = segments[1] ? toPascal(segments[1]) : "";
2449
+ const module = segments[2] ? toPascal(segments[2]) : segments[1] ? toPascal(segments[1]) : "";
2450
+ let domainPath = "";
2451
+ if (segments.length >= 3) {
2452
+ domainPath = path8.join(context, application, module);
2453
+ } else if (segments.length === 2) {
2454
+ domainPath = path8.join(context, module);
2455
+ } else if (segments.length === 1) {
2456
+ domainPath = context;
2457
+ }
2458
+ const infraPath = module || "";
2459
+ const controllerArea = context;
2460
+ return { context, application, module, domainPath, infraPath, controllerArea };
2461
+ }
2441
2462
  async function handleScaffoldExtension(args, config) {
2442
2463
  const input = ScaffoldExtensionInputSchema.parse(args);
2443
2464
  const dryRun = input.options?.dryRun || false;
@@ -2582,17 +2603,21 @@ async function scaffoldFeature(name, options, structure, config, result, dryRun
2582
2603
  result.instructions.push(`${withRepository ? withValidation ? "5" : "4" : withValidation ? "4" : "3"}. Create migration: \`dotnet ef migrations add ${migrationPrefix}_vX.X.X_XXX_Add${name} --context ${dbContextName}\``);
2583
2604
  result.instructions.push(`${withRepository ? withValidation ? "6" : "5" : withValidation ? "5" : "4"}. Run migration: \`dotnet ef database update --context ${dbContextName}\``);
2584
2605
  if (!skipComponent) {
2585
- result.instructions.push(`Import component: \`import { ${name} } from './components/${name}';\``);
2606
+ const featureHierarchy = resolveHierarchy(options?.navRoute);
2607
+ const featureComponentPath = featureHierarchy.context && featureHierarchy.module ? `@/components/${featureHierarchy.context.toLowerCase()}/${featureHierarchy.module.toLowerCase()}/${name}` : `./components/${name}`;
2608
+ result.instructions.push(`Import component: \`import { ${name} } from '${featureComponentPath}';\``);
2586
2609
  }
2587
2610
  }
2588
2611
  async function scaffoldService(name, options, structure, config, result, dryRun = false) {
2589
- const namespace = options?.namespace || `${config.conventions.namespaces.application}.Services`;
2612
+ const hierarchy = resolveHierarchy(options?.navRoute);
2613
+ const interfaceNamespace = options?.namespace || `${config.conventions.namespaces.application}.Common.Interfaces`;
2614
+ const implNamespace = hierarchy.infraPath ? `${config.conventions.namespaces.infrastructure}.Services.${hierarchy.infraPath}` : `${config.conventions.namespaces.infrastructure}.Services`;
2590
2615
  const methods = options?.methods || ["GetByIdAsync", "GetAllAsync", "CreateAsync", "UpdateAsync", "DeleteAsync"];
2591
2616
  const interfaceTemplate = `using System.Threading;
2592
2617
  using System.Threading.Tasks;
2593
2618
  using System.Collections.Generic;
2594
2619
 
2595
- namespace {{namespace}};
2620
+ namespace {{interfaceNamespace}};
2596
2621
 
2597
2622
  /// <summary>
2598
2623
  /// Service interface for {{name}} operations
@@ -2613,7 +2638,7 @@ using System.Threading.Tasks;
2613
2638
  using System.Collections.Generic;
2614
2639
  using Microsoft.Extensions.Logging;
2615
2640
 
2616
- namespace {{namespace}};
2641
+ namespace {{implNamespace}};
2617
2642
 
2618
2643
  /// <summary>
2619
2644
  /// Service implementation for {{name}} operations
@@ -2643,19 +2668,22 @@ public class {{name}}Service : I{{name}}Service
2643
2668
  const diTemplate = `// Add to DependencyInjection.cs or ServiceCollectionExtensions.cs:
2644
2669
  services.AddScoped<I{{name}}Service, {{name}}Service>();
2645
2670
  `;
2646
- const context = { namespace, name, methods };
2671
+ const context = { interfaceNamespace, implNamespace, name, methods };
2647
2672
  const interfaceContent = Handlebars.compile(interfaceTemplate)(context);
2648
2673
  const implementationContent = Handlebars.compile(implementationTemplate)(context);
2649
2674
  const diContent = Handlebars.compile(diTemplate)(context);
2650
2675
  const projectRoot = config.smartstack.projectPath;
2651
- const basePath = structure.application || projectRoot;
2652
- const servicesPath = path8.join(basePath, "Services");
2653
- const interfacePath = path8.join(servicesPath, `I${name}Service.cs`);
2654
- const implementationPath = path8.join(servicesPath, `${name}Service.cs`);
2676
+ const appPath = structure.application || projectRoot;
2677
+ const infraPath = structure.infrastructure || path8.join(projectRoot, "Infrastructure");
2678
+ const interfacesDir = path8.join(appPath, "Common", "Interfaces");
2679
+ const implDir = hierarchy.infraPath ? path8.join(infraPath, "Services", hierarchy.infraPath) : path8.join(infraPath, "Services");
2680
+ const interfacePath = path8.join(interfacesDir, `I${name}Service.cs`);
2681
+ const implementationPath = path8.join(implDir, `${name}Service.cs`);
2655
2682
  validatePathSecurity(interfacePath, projectRoot);
2656
2683
  validatePathSecurity(implementationPath, projectRoot);
2657
2684
  if (!dryRun) {
2658
- await ensureDirectory(servicesPath);
2685
+ await ensureDirectory(interfacesDir);
2686
+ await ensureDirectory(implDir);
2659
2687
  await writeText(interfacePath, interfaceContent);
2660
2688
  await writeText(implementationPath, implementationContent);
2661
2689
  }
@@ -2665,7 +2693,8 @@ services.AddScoped<I{{name}}Service, {{name}}Service>();
2665
2693
  result.instructions.push(diContent);
2666
2694
  }
2667
2695
  async function scaffoldEntity(name, options, structure, config, result, dryRun = false) {
2668
- const namespace = options?.namespace || config.conventions.namespaces.domain;
2696
+ const hierarchy = resolveHierarchy(options?.navRoute);
2697
+ const namespace = options?.namespace || (hierarchy.domainPath ? `${config.conventions.namespaces.domain}.${hierarchy.domainPath.replace(/[\\/]/g, ".")}` : config.conventions.namespaces.domain);
2669
2698
  const baseEntity = options?.baseEntity;
2670
2699
  const isSystemEntity = options?.isSystemEntity || false;
2671
2700
  const tablePrefix = options?.tablePrefix || "ref_";
@@ -2895,15 +2924,17 @@ public class {{name}}Configuration : IEntityTypeConfiguration<{{name}}>
2895
2924
  const entityContent = Handlebars.compile(entityTemplate)(context);
2896
2925
  const configContent = Handlebars.compile(configTemplate)(context);
2897
2926
  const projectRoot = config.smartstack.projectPath;
2898
- const domainPath = structure.domain || path8.join(projectRoot, "Domain");
2899
- const infraPath = structure.infrastructure || path8.join(projectRoot, "Infrastructure");
2900
- const entityFilePath = path8.join(domainPath, `${name}.cs`);
2901
- const configFilePath = path8.join(infraPath, "Persistence", "Configurations", `${name}Configuration.cs`);
2927
+ const domainBase = structure.domain || path8.join(projectRoot, "Domain");
2928
+ const infraBase = structure.infrastructure || path8.join(projectRoot, "Infrastructure");
2929
+ const entityDir = hierarchy.domainPath ? path8.join(domainBase, hierarchy.domainPath) : domainBase;
2930
+ const configDir = hierarchy.infraPath ? path8.join(infraBase, "Persistence", "Configurations", hierarchy.infraPath) : path8.join(infraBase, "Persistence", "Configurations");
2931
+ const entityFilePath = path8.join(entityDir, `${name}.cs`);
2932
+ const configFilePath = path8.join(configDir, `${name}Configuration.cs`);
2902
2933
  validatePathSecurity(entityFilePath, projectRoot);
2903
2934
  validatePathSecurity(configFilePath, projectRoot);
2904
2935
  if (!dryRun) {
2905
- await ensureDirectory(domainPath);
2906
- await ensureDirectory(path8.join(infraPath, "Persistence", "Configurations"));
2936
+ await ensureDirectory(entityDir);
2937
+ await ensureDirectory(configDir);
2907
2938
  await writeText(entityFilePath, entityContent);
2908
2939
  await writeText(configFilePath, configContent);
2909
2940
  }
@@ -3220,7 +3251,8 @@ GO
3220
3251
  result.instructions.push("- Group memberships (user belongs to parent groups)");
3221
3252
  }
3222
3253
  async function scaffoldController(name, options, structure, config, result, dryRun = false) {
3223
- const namespace = options?.namespace || `${config.conventions.namespaces.api}.Controllers`;
3254
+ const hierarchy = resolveHierarchy(options?.navRoute);
3255
+ const namespace = options?.namespace || (hierarchy.controllerArea ? `${config.conventions.namespaces.api}.Controllers.${hierarchy.controllerArea}` : `${config.conventions.namespaces.api}.Controllers`);
3224
3256
  const navRoute = options?.navRoute;
3225
3257
  const navRouteSuffix = options?.navRouteSuffix;
3226
3258
  const routeAttribute = navRoute ? navRouteSuffix ? `[NavRoute("${navRoute}", Suffix = "${navRouteSuffix}")]` : `[NavRoute("${navRoute}")]` : `[Route("api/[controller]")]`;
@@ -3316,11 +3348,11 @@ public record Update{{name}}Request();
3316
3348
  const controllerContent = Handlebars.compile(controllerTemplate)(context);
3317
3349
  const projectRoot = config.smartstack.projectPath;
3318
3350
  const apiPath = structure.api || path8.join(projectRoot, "Api");
3319
- const controllersPath = path8.join(apiPath, "Controllers");
3320
- const controllerFilePath = path8.join(controllersPath, `${name}Controller.cs`);
3351
+ const controllersDir = hierarchy.controllerArea ? path8.join(apiPath, "Controllers", hierarchy.controllerArea) : path8.join(apiPath, "Controllers");
3352
+ const controllerFilePath = path8.join(controllersDir, `${name}Controller.cs`);
3321
3353
  validatePathSecurity(controllerFilePath, projectRoot);
3322
3354
  if (!dryRun) {
3323
- await ensureDirectory(controllersPath);
3355
+ await ensureDirectory(controllersDir);
3324
3356
  await writeText(controllerFilePath, controllerContent);
3325
3357
  }
3326
3358
  result.files.push({ path: controllerFilePath, content: controllerContent, type: "created" });
@@ -3346,6 +3378,7 @@ public record Update{{name}}Request();
3346
3378
  }
3347
3379
  }
3348
3380
  async function scaffoldComponent(name, options, structure, config, result, dryRun = false) {
3381
+ const hierarchy = resolveHierarchy(options?.navRoute);
3349
3382
  const componentTemplate = `import React, { useState, useEffect } from 'react';
3350
3383
 
3351
3384
  interface {{name}}Props {
@@ -3498,7 +3531,8 @@ export function use{{name}}(options: Use{{name}}Options = {}) {
3498
3531
  const hookContent = Handlebars.compile(hookTemplate)(context);
3499
3532
  const projectRoot = config.smartstack.projectPath;
3500
3533
  const webPath = structure.web || path8.join(projectRoot, "web");
3501
- const componentsPath = options?.outputPath || path8.join(webPath, "src", "components");
3534
+ const componentsBase = path8.join(webPath, "src", "components");
3535
+ const componentsPath = options?.outputPath ? options.outputPath : hierarchy.context && hierarchy.module ? path8.join(componentsBase, hierarchy.context.toLowerCase(), hierarchy.module.toLowerCase()) : componentsBase;
3502
3536
  const hooksPath = path8.join(webPath, "src", "hooks");
3503
3537
  const componentFilePath = path8.join(componentsPath, `${name}.tsx`);
3504
3538
  const hookFilePath = path8.join(hooksPath, `use${name}.ts`);
@@ -3513,8 +3547,9 @@ export function use{{name}}(options: Use{{name}}Options = {}) {
3513
3547
  result.files.push({ path: componentFilePath, content: componentContent, type: "created" });
3514
3548
  result.files.push({ path: hookFilePath, content: hookContent, type: "created" });
3515
3549
  result.instructions.push("Import and use the component:");
3516
- result.instructions.push(`import { ${name} } from './components/${name}';`);
3517
- result.instructions.push(`import { use${name} } from './hooks/use${name}';`);
3550
+ const componentImportPath = hierarchy.context && hierarchy.module ? `@/components/${hierarchy.context.toLowerCase()}/${hierarchy.module.toLowerCase()}/${name}` : `./components/${name}`;
3551
+ result.instructions.push(`import { ${name} } from '${componentImportPath}';`);
3552
+ result.instructions.push(`import { use${name} } from '@/hooks/use${name}';`);
3518
3553
  }
3519
3554
  async function scaffoldTest(name, options, structure, config, result, dryRun = false) {
3520
3555
  const isSystemEntity = options?.isSystemEntity || false;
@@ -3642,7 +3677,8 @@ public class {{name}}ServiceTests
3642
3677
  result.instructions.push("- FluentAssertions");
3643
3678
  }
3644
3679
  async function scaffoldDtos(name, options, structure, config, result, dryRun = false) {
3645
- const namespace = options?.namespace || `${config.conventions.namespaces.application}.DTOs`;
3680
+ const hierarchy = resolveHierarchy(options?.navRoute);
3681
+ const namespace = options?.namespace || (hierarchy.domainPath ? `${config.conventions.namespaces.application}.${hierarchy.domainPath.replace(/[\\/]/g, ".")}.DTOs` : `${config.conventions.namespaces.application}.DTOs`);
3646
3682
  const isSystemEntity = options?.isSystemEntity || false;
3647
3683
  const properties = options?.entityProperties || [
3648
3684
  { name: "Name", type: "string", required: true, maxLength: 200 },
@@ -3759,7 +3795,7 @@ public record Update{{name}}Dto
3759
3795
  const createContent = Handlebars.compile(createDtoTemplate)(context);
3760
3796
  const updateContent = Handlebars.compile(updateDtoTemplate)(context);
3761
3797
  const basePath = structure.application || config.smartstack.projectPath;
3762
- const dtosPath = path8.join(basePath, "DTOs", name);
3798
+ const dtosPath = hierarchy.domainPath ? path8.join(basePath, hierarchy.domainPath, "DTOs") : path8.join(basePath, "DTOs", name);
3763
3799
  const responseFilePath = path8.join(dtosPath, `${name}ResponseDto.cs`);
3764
3800
  const createFilePath = path8.join(dtosPath, `Create${name}Dto.cs`);
3765
3801
  const updateFilePath = path8.join(dtosPath, `Update${name}Dto.cs`);
@@ -3778,7 +3814,8 @@ public record Update{{name}}Dto
3778
3814
  result.instructions.push(`- Update${name}Dto: For PUT requests`);
3779
3815
  }
3780
3816
  async function scaffoldValidator(name, options, structure, config, result, dryRun = false) {
3781
- const namespace = options?.namespace || `${config.conventions.namespaces.application}.Validators`;
3817
+ const hierarchy = resolveHierarchy(options?.navRoute);
3818
+ const namespace = options?.namespace || (hierarchy.domainPath ? `${config.conventions.namespaces.application}.${hierarchy.domainPath.replace(/[\\/]/g, ".")}.Validators` : `${config.conventions.namespaces.application}.Validators`);
3782
3819
  const properties = options?.entityProperties || [
3783
3820
  { name: "Name", type: "string", required: true, maxLength: 200 },
3784
3821
  { name: "Description", type: "string?", required: false, maxLength: 500 }
@@ -3856,7 +3893,7 @@ public class Update{{name}}DtoValidator : AbstractValidator<Update{{name}}Dto>
3856
3893
  const createValidatorContent = Handlebars.compile(createValidatorTemplate)(context);
3857
3894
  const updateValidatorContent = Handlebars.compile(updateValidatorTemplate)(context);
3858
3895
  const basePath = structure.application || config.smartstack.projectPath;
3859
- const validatorsPath = path8.join(basePath, "Validators");
3896
+ const validatorsPath = hierarchy.domainPath ? path8.join(basePath, hierarchy.domainPath, "Validators") : path8.join(basePath, "Validators");
3860
3897
  const createValidatorFilePath = path8.join(validatorsPath, `Create${name}DtoValidator.cs`);
3861
3898
  const updateValidatorFilePath = path8.join(validatorsPath, `Update${name}DtoValidator.cs`);
3862
3899
  if (!dryRun) {
@@ -3872,6 +3909,7 @@ public class Update{{name}}DtoValidator : AbstractValidator<Update{{name}}Dto>
3872
3909
  result.instructions.push("Required package: FluentValidation.DependencyInjectionExtensions");
3873
3910
  }
3874
3911
  async function scaffoldRepository(name, options, structure, config, result, dryRun = false) {
3912
+ const hierarchy = resolveHierarchy(options?.navRoute);
3875
3913
  const isSystemEntity = options?.isSystemEntity || false;
3876
3914
  const schema = options?.schema || config.conventions.schemas.platform;
3877
3915
  const dbContextName = schema === "extensions" ? "ExtensionsDbContext" : "CoreDbContext";
@@ -3995,11 +4033,13 @@ public class {{name}}Repository : I{{name}}Repository
3995
4033
  const implementationContent = Handlebars.compile(implementationTemplate)(context);
3996
4034
  const appPath = structure.application || config.smartstack.projectPath;
3997
4035
  const infraPath = structure.infrastructure || path8.join(config.smartstack.projectPath, "Infrastructure");
3998
- const interfaceFilePath = path8.join(appPath, "Repositories", `I${name}Repository.cs`);
3999
- const implementationFilePath = path8.join(infraPath, "Repositories", `${name}Repository.cs`);
4036
+ const appRepoDir = hierarchy.infraPath ? path8.join(appPath, "Repositories", hierarchy.infraPath) : path8.join(appPath, "Repositories");
4037
+ const infraRepoDir = hierarchy.infraPath ? path8.join(infraPath, "Repositories", hierarchy.infraPath) : path8.join(infraPath, "Repositories");
4038
+ const interfaceFilePath = path8.join(appRepoDir, `I${name}Repository.cs`);
4039
+ const implementationFilePath = path8.join(infraRepoDir, `${name}Repository.cs`);
4000
4040
  if (!dryRun) {
4001
- await ensureDirectory(path8.join(appPath, "Repositories"));
4002
- await ensureDirectory(path8.join(infraPath, "Repositories"));
4041
+ await ensureDirectory(appRepoDir);
4042
+ await ensureDirectory(infraRepoDir);
4003
4043
  await writeText(interfaceFilePath, interfaceContent);
4004
4044
  await writeText(implementationFilePath, implementationContent);
4005
4045
  }