@atlashub/smartstack-mcp 1.9.0 → 1.13.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
@@ -1822,6 +1822,10 @@ var scaffoldExtensionTool = {
1822
1822
  type: "boolean",
1823
1823
  description: "For feature type: generate repository pattern"
1824
1824
  },
1825
+ withSeedData: {
1826
+ type: "boolean",
1827
+ description: "For entity type: generate centralized SeedData file in Seeding/Data/{Domain}/"
1828
+ },
1825
1829
  entityProperties: {
1826
1830
  type: "array",
1827
1831
  items: {
@@ -1986,8 +1990,12 @@ async function scaffoldFeature(name, options, structure, config, result, dryRun
1986
1990
  result.instructions.push("---");
1987
1991
  result.instructions.push(`## Summary: Generated ${generated.join(" + ")}`);
1988
1992
  result.instructions.push("");
1993
+ const schema = options?.schema || (isClientExtension ? "extensions" : "core");
1994
+ const dbContextName = schema === "extensions" ? "ExtensionsDbContext" : "CoreDbContext";
1995
+ const dbContextInterface = schema === "extensions" ? "IExtensionsDbContext" : "ICoreDbContext";
1996
+ const migrationPrefix = schema === "extensions" ? "ext" : "core";
1989
1997
  result.instructions.push("### Next Steps:");
1990
- result.instructions.push(`1. Add DbSet to ApplicationDbContext: \`public DbSet<${name}> ${name}s => Set<${name}>();\``);
1998
+ result.instructions.push(`1. Add DbSet to ${dbContextInterface} and ${dbContextName}: \`public DbSet<${name}> ${name}s => Set<${name}>();\``);
1991
1999
  if (withRepository) {
1992
2000
  result.instructions.push(`2. Register repository: \`services.AddScoped<I${name}Repository, ${name}Repository>();\``);
1993
2001
  }
@@ -1995,8 +2003,8 @@ async function scaffoldFeature(name, options, structure, config, result, dryRun
1995
2003
  if (withValidation) {
1996
2004
  result.instructions.push(`${withRepository ? "4" : "3"}. Register validators: \`services.AddValidatorsFromAssemblyContaining<Create${name}DtoValidator>();\``);
1997
2005
  }
1998
- result.instructions.push(`${withRepository ? withValidation ? "5" : "4" : withValidation ? "4" : "3"}. Create migration: \`dotnet ef migrations add ${options?.tablePrefix || "ref_"}vX.X.X_XXX_Add${name}\``);
1999
- result.instructions.push(`${withRepository ? withValidation ? "6" : "5" : withValidation ? "5" : "4"}. Run migration: \`dotnet ef database update\``);
2006
+ 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}\``);
2007
+ result.instructions.push(`${withRepository ? withValidation ? "6" : "5" : withValidation ? "5" : "4"}. Run migration: \`dotnet ef database update --context ${dbContextName}\``);
2000
2008
  if (!skipComponent) {
2001
2009
  result.instructions.push(`Import component: \`import { ${name} } from './components/${name}';\``);
2002
2010
  }
@@ -2325,17 +2333,136 @@ public class {{name}}Configuration : IEntityTypeConfiguration<{{name}}>
2325
2333
  }
2326
2334
  result.files.push({ path: entityFilePath, content: entityContent, type: "created" });
2327
2335
  result.files.push({ path: configFilePath, content: configContent, type: "created" });
2328
- result.instructions.push(`Add DbSet to ApplicationDbContext:`);
2336
+ const dbContextName = schema === "extensions" ? "ExtensionsDbContext" : "CoreDbContext";
2337
+ const dbContextInterface = schema === "extensions" ? "IExtensionsDbContext" : "ICoreDbContext";
2338
+ const migrationPrefix = schema === "extensions" ? "ext" : "core";
2339
+ result.instructions.push(`Add DbSet to ${dbContextInterface}:`);
2340
+ result.instructions.push(`public DbSet<${name}> ${name}s => Set<${name}>();`);
2341
+ result.instructions.push("");
2342
+ result.instructions.push(`And implementation in ${dbContextName}:`);
2329
2343
  result.instructions.push(`public DbSet<${name}> ${name}s => Set<${name}>();`);
2330
2344
  result.instructions.push("");
2331
2345
  result.instructions.push("Create migration:");
2332
- result.instructions.push(`dotnet ef migrations add core_vX.X.X_XXX_Add${name} --context ApplicationDbContext`);
2346
+ result.instructions.push(`dotnet ef migrations add ${migrationPrefix}_vX.X.X_XXX_Add${name} --context ${dbContextName}`);
2333
2347
  result.instructions.push("");
2334
2348
  result.instructions.push("Required fields from BaseEntity:");
2335
2349
  result.instructions.push(`- Id (Guid), ${isSystemEntity ? "" : "TenantId (Guid), "}Code (string, lowercase)`);
2336
2350
  result.instructions.push("- CreatedAt, UpdatedAt, CreatedBy, UpdatedBy (audit)");
2337
2351
  result.instructions.push("- IsDeleted, DeletedAt, DeletedBy (soft delete)");
2338
2352
  result.instructions.push("- RowVersion (concurrency)");
2353
+ if (options?.withSeedData) {
2354
+ result.instructions.push("");
2355
+ result.instructions.push("### Seed Data");
2356
+ await scaffoldSeedData(name, options, structure, config, result, dryRun);
2357
+ }
2358
+ }
2359
+ async function scaffoldSeedData(name, options, structure, config, result, dryRun = false) {
2360
+ const tablePrefix = options?.tablePrefix || "ref_";
2361
+ const domainMap = {
2362
+ "auth_": "Authorization",
2363
+ "nav_": "Navigation",
2364
+ "usr_": "User",
2365
+ "wkf_": "Communications",
2366
+ "cfg_": "Configuration",
2367
+ "ai_": "AI",
2368
+ "entra_": "Entra",
2369
+ "ref_": "Reference",
2370
+ "support_": "Support",
2371
+ "loc_": "Localization"
2372
+ };
2373
+ const domain = options?.seedDataDomain || domainMap[tablePrefix] || "Reference";
2374
+ const isSystemEntity = options?.isSystemEntity || false;
2375
+ const seedDataTemplate = `using SmartStack.Domain.{{domainNamespace}};
2376
+
2377
+ namespace SmartStack.Infrastructure.Persistence.Seeding.Data.{{domain}};
2378
+
2379
+ /// <summary>
2380
+ /// Donnees seed pour {{name}}.
2381
+ /// Centralise les IDs et donnees d'initialisation.
2382
+ /// </summary>
2383
+ public static class {{name}}SeedData
2384
+ {
2385
+ // ============================================================
2386
+ // IDs - Documenter chaque ID avec son role
2387
+ // ============================================================
2388
+
2389
+ /// <summary>ID exemple - A remplacer par vos IDs</summary>
2390
+ public static readonly Guid ExampleId = Guid.Parse("{{exampleGuid}}");
2391
+
2392
+ // ============================================================
2393
+ // CODES / CONSTANTS
2394
+ // ============================================================
2395
+
2396
+ public const string ExampleCode = "example";
2397
+
2398
+ // ============================================================
2399
+ // SEED DATA
2400
+ // ============================================================
2401
+
2402
+ /// <summary>
2403
+ /// Retourne toutes les donnees seed pour {{name}}.
2404
+ /// Appel\xE9 depuis {{name}}Configuration.HasData()
2405
+ /// </summary>
2406
+ public static object[] GetSeedData()
2407
+ {
2408
+ var seedDate = SeedConstants.SeedDate;
2409
+
2410
+ return new object[]
2411
+ {
2412
+ // Exemple - A remplacer par vos donnees
2413
+ new
2414
+ {
2415
+ Id = ExampleId,
2416
+ {{#unless isSystemEntity}}
2417
+ TenantId = (Guid?)null, // Seed data systeme sans tenant
2418
+ {{/unless}}
2419
+ Code = ExampleCode,
2420
+ // TODO: Ajouter les proprietes specifiques
2421
+ IsDeleted = false,
2422
+ CreatedAt = seedDate
2423
+ }
2424
+ };
2425
+ }
2426
+ }
2427
+ `;
2428
+ const exampleGuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
2429
+ const r = Math.random() * 16 | 0;
2430
+ const v = c === "x" ? r : r & 3 | 8;
2431
+ return v.toString(16);
2432
+ });
2433
+ const context = {
2434
+ name,
2435
+ domain,
2436
+ domainNamespace: domain,
2437
+ isSystemEntity,
2438
+ exampleGuid
2439
+ };
2440
+ const seedDataContent = Handlebars.compile(seedDataTemplate)(context);
2441
+ const projectRoot = config.smartstack.projectPath;
2442
+ const infraPath = structure.infrastructure || path8.join(projectRoot, "Infrastructure");
2443
+ const seedDataPath = path8.join(infraPath, "Persistence", "Seeding", "Data", domain);
2444
+ const seedDataFilePath = path8.join(seedDataPath, `${name}SeedData.cs`);
2445
+ validatePathSecurity(seedDataFilePath, projectRoot);
2446
+ if (!dryRun) {
2447
+ await ensureDirectory(seedDataPath);
2448
+ await writeText(seedDataFilePath, seedDataContent);
2449
+ }
2450
+ result.files.push({ path: seedDataFilePath, content: seedDataContent, type: "created" });
2451
+ result.instructions.push(`SeedData file generated in Seeding/Data/${domain}/`);
2452
+ result.instructions.push("");
2453
+ result.instructions.push("Update your Configuration to use centralized SeedData:");
2454
+ result.instructions.push("```csharp");
2455
+ result.instructions.push(`// In ${name}Configuration.cs`);
2456
+ result.instructions.push(`using SmartStack.Infrastructure.Persistence.Seeding.Data.${domain};`);
2457
+ result.instructions.push("");
2458
+ result.instructions.push(`builder.HasData(${name}SeedData.GetSeedData());`);
2459
+ result.instructions.push("```");
2460
+ result.instructions.push("");
2461
+ result.instructions.push("Pattern to follow:");
2462
+ result.instructions.push("1. Define public static readonly Guid IDs");
2463
+ result.instructions.push("2. Define const string codes");
2464
+ result.instructions.push("3. Implement GetSeedData() returning object[]");
2465
+ result.instructions.push("4. Reference other SeedData IDs for foreign keys");
2339
2466
  }
2340
2467
  async function scaffoldController(name, options, structure, config, result, dryRun = false) {
2341
2468
  const namespace = options?.namespace || `${config.conventions.namespaces.api}.Controllers`;
@@ -2991,6 +3118,8 @@ public class Update{{name}}DtoValidator : AbstractValidator<Update{{name}}Dto>
2991
3118
  }
2992
3119
  async function scaffoldRepository(name, options, structure, config, result, dryRun = false) {
2993
3120
  const isSystemEntity = options?.isSystemEntity || false;
3121
+ const schema = options?.schema || config.conventions.schemas.platform;
3122
+ const dbContextName = schema === "extensions" ? "ExtensionsDbContext" : "CoreDbContext";
2994
3123
  const interfaceTemplate = `using System;
2995
3124
  using System.Collections.Generic;
2996
3125
  using System.Threading;
@@ -3042,9 +3171,9 @@ namespace ${config.conventions.namespaces.infrastructure}.Repositories;
3042
3171
  /// </summary>
3043
3172
  public class {{name}}Repository : I{{name}}Repository
3044
3173
  {
3045
- private readonly ApplicationDbContext _context;
3174
+ private readonly {{dbContextName}} _context;
3046
3175
 
3047
- public {{name}}Repository(ApplicationDbContext context)
3176
+ public {{name}}Repository({{dbContextName}} context)
3048
3177
  {
3049
3178
  _context = context;
3050
3179
  }
@@ -3104,7 +3233,8 @@ public class {{name}}Repository : I{{name}}Repository
3104
3233
  `;
3105
3234
  const context = {
3106
3235
  name,
3107
- isSystemEntity
3236
+ isSystemEntity,
3237
+ dbContextName
3108
3238
  };
3109
3239
  const interfaceContent = Handlebars.compile(interfaceTemplate)(context);
3110
3240
  const implementationContent = Handlebars.compile(implementationTemplate)(context);
@@ -3602,7 +3732,8 @@ async function handleSuggestMigration(args, config) {
3602
3732
  const pascalDescription = toPascalCase(input.description);
3603
3733
  const sequenceStr = sequence.toString().padStart(3, "0");
3604
3734
  const migrationName = `${context}_v${version}_${sequenceStr}_${pascalDescription}`;
3605
- const command = `dotnet ef migrations add ${migrationName} --context ApplicationDbContext`;
3735
+ const dbContextName = context === "core" ? "CoreDbContext" : "ExtensionsDbContext";
3736
+ const command = `dotnet ef migrations add ${migrationName} --context ${dbContextName}`;
3606
3737
  const lines = [];
3607
3738
  lines.push("# Migration Name Suggestion");
3608
3739
  lines.push("");
@@ -3620,11 +3751,17 @@ async function handleSuggestMigration(args, config) {
3620
3751
  lines.push("");
3621
3752
  lines.push(`| Part | Value | Description |`);
3622
3753
  lines.push(`|------|-------|-------------|`);
3623
- lines.push(`| Context | \`${context}\` | DbContext name |`);
3754
+ lines.push(`| Context | \`${context}\` | Migration prefix (core/extensions) |`);
3755
+ lines.push(`| DbContext | \`${dbContextName}\` | EF Core DbContext to use |`);
3756
+ lines.push(`| Schema | \`${context}\` | Database schema for tables |`);
3624
3757
  lines.push(`| Version | \`v${version}\` | Semver version |`);
3625
3758
  lines.push(`| Sequence | \`${sequenceStr}\` | Order in version |`);
3626
3759
  lines.push(`| Description | \`${pascalDescription}\` | Migration description |`);
3627
3760
  lines.push("");
3761
+ lines.push("> **Note**: Migrations are stored in separate history tables:");
3762
+ lines.push(`> - Core: \`core.__EFMigrationsHistory\``);
3763
+ lines.push(`> - Extensions: \`extensions.__EFMigrationsHistory\``);
3764
+ lines.push("");
3628
3765
  if (existingMigrations.length > 0) {
3629
3766
  lines.push("## Existing Migrations (last 5)");
3630
3767
  lines.push("");
@@ -3638,7 +3775,7 @@ async function handleSuggestMigration(args, config) {
3638
3775
  async function findExistingMigrations(structure, config, context) {
3639
3776
  const migrations = [];
3640
3777
  const infraPath = structure.infrastructure || path10.join(config.smartstack.projectPath, "Infrastructure");
3641
- const migrationsPath = path10.join(infraPath, "Migrations");
3778
+ const migrationsPath = path10.join(infraPath, "Persistence", "Migrations");
3642
3779
  try {
3643
3780
  const migrationFiles = await findFiles("*.cs", { cwd: migrationsPath });
3644
3781
  const migrationPattern = /^(\w+)_v(\d+\.\d+\.\d+)_(\d+)_(\w+)\.cs$/;
@@ -3685,7 +3822,6 @@ function compareVersions2(a, b) {
3685
3822
  }
3686
3823
 
3687
3824
  // src/tools/generate-permissions.ts
3688
- import Handlebars2 from "handlebars";
3689
3825
  import path11 from "path";
3690
3826
  var HTTP_METHOD_TO_ACTION = {
3691
3827
  "GET": "read",
@@ -3702,17 +3838,21 @@ var generatePermissionsTool = {
3702
3838
  name: "generate_permissions",
3703
3839
  description: `Generate RBAC permissions for SmartStack controllers.
3704
3840
 
3705
- Automatically creates permissions following the convention: {navRoute}.{action}
3841
+ Analyzes NavRoute attributes and outputs HasData() C# code to add to PermissionConfiguration.cs.
3842
+
3843
+ IMPORTANT: This tool does NOT generate migrations with raw SQL (forbidden by SmartStack conventions).
3844
+ Instead, it outputs HasData() code that must be manually added to the Configuration file.
3706
3845
 
3707
3846
  Example:
3708
3847
  navRoute: "platform.administration.entra"
3709
- Generates:
3848
+ Outputs HasData() code for:
3849
+ - platform.administration.entra.*
3710
3850
  - platform.administration.entra.read
3711
3851
  - platform.administration.entra.create
3712
3852
  - platform.administration.entra.update
3713
3853
  - platform.administration.entra.delete
3714
3854
 
3715
- Can also generate EF Core migration to seed permissions in database.`,
3855
+ After adding to PermissionConfiguration.cs, run: dotnet ef migrations add <MigrationName>`,
3716
3856
  inputSchema: {
3717
3857
  type: "object",
3718
3858
  properties: {
@@ -3730,15 +3870,10 @@ Can also generate EF Core migration to seed permissions in database.`,
3730
3870
  default: true,
3731
3871
  description: "Include standard CRUD actions (read, create, update, delete)"
3732
3872
  },
3733
- generateMigration: {
3873
+ includeWildcard: {
3734
3874
  type: "boolean",
3735
3875
  default: true,
3736
- description: "Generate EF Core migration to seed permissions in database"
3737
- },
3738
- dryRun: {
3739
- type: "boolean",
3740
- default: false,
3741
- description: "Preview without writing files or creating migration"
3876
+ description: "Include wildcard permission (e.g., personal.myspace.tenants.*)"
3742
3877
  }
3743
3878
  }
3744
3879
  }
@@ -3749,8 +3884,7 @@ async function handleGeneratePermissions(args, config) {
3749
3884
  navRoute: args.navRoute,
3750
3885
  actions: args.actions || [],
3751
3886
  includeStandardActions: args.includeStandardActions !== false,
3752
- generateMigration: args.generateMigration !== false,
3753
- dryRun: args.dryRun === true
3887
+ includeWildcard: args.includeWildcard !== false
3754
3888
  };
3755
3889
  const structure = await findSmartStackStructure(config.smartstack.projectPath);
3756
3890
  if (!structure) {
@@ -3762,16 +3896,17 @@ async function handleGeneratePermissions(args, config) {
3762
3896
  permissions = generatePermissionsForNavRoute(
3763
3897
  options.navRoute,
3764
3898
  options.actions || [],
3765
- options.includeStandardActions || false
3899
+ options.includeStandardActions || false,
3900
+ options.includeWildcard || false
3766
3901
  );
3767
- report = `## Permissions Generated for NavRoute: ${options.navRoute}
3902
+ report = `## Permissions for NavRoute: ${options.navRoute}
3768
3903
 
3769
3904
  `;
3770
3905
  report += formatPermissionsReport(permissions);
3771
3906
  } else {
3772
3907
  const scannedPermissions = await scanControllersForPermissions(structure.api || structure.root);
3773
3908
  permissions = scannedPermissions;
3774
- report = `## Permissions Generated from All Controllers
3909
+ report = `## Permissions from All Controllers
3775
3910
 
3776
3911
  `;
3777
3912
  report += `Total controllers scanned: ${getUniqueNavRouteCount(permissions)}
@@ -3781,53 +3916,51 @@ async function handleGeneratePermissions(args, config) {
3781
3916
  `;
3782
3917
  report += formatPermissionsReport(permissions);
3783
3918
  }
3784
- if (options.generateMigration && !options.dryRun) {
3785
- const migrationResult = await generatePermissionMigration(
3786
- structure.api || structure.root,
3787
- permissions,
3788
- config
3789
- );
3790
- report += `
3919
+ report += `
3791
3920
 
3792
- ## Migration Generated
3921
+ ## HasData() Code for PermissionConfiguration.cs
3793
3922
 
3794
3923
  `;
3795
- report += `File: ${migrationResult.filePath}
3924
+ report += `\u26A0\uFE0F **IMPORTANT**: Do NOT use migrationBuilder.Sql() - it's forbidden by SmartStack conventions.
3796
3925
  `;
3797
- report += `Migration name: ${migrationResult.migrationName}
3926
+ report += `Add this code to \`PermissionConfiguration.cs\` in the HasData() section, then create a migration.
3798
3927
 
3799
3928
  `;
3800
- report += `### Next Steps
3929
+ report += generateHasDataCode(permissions, options.navRoute || "custom");
3930
+ report += `
3931
+
3932
+ ### Next Steps
3801
3933
 
3802
3934
  `;
3803
- report += `1. Review the generated migration
3804
- `;
3805
- report += `2. Run: \`dotnet ef database update\`
3935
+ report += `1. Add the module ID variable in GetSeedData() method
3806
3936
  `;
3807
- report += `3. Verify permissions in auth_Permissions table
3937
+ report += `2. Add the HasData entries to the return array
3808
3938
  `;
3809
- } else if (options.dryRun) {
3810
- report += `
3811
-
3812
- ## Dry Run Mode
3813
-
3939
+ report += `3. Run: \`dotnet ef migrations add <MigrationName> -o Persistence/Migrations\`
3814
3940
  `;
3815
- report += `No files were created. Remove 'dryRun: true' to generate migration.
3941
+ report += `4. Run: \`dotnet ef database update\`
3816
3942
  `;
3817
- }
3818
3943
  return report;
3819
3944
  } catch (error) {
3820
3945
  logger.error("Error generating permissions:", error);
3821
3946
  throw error;
3822
3947
  }
3823
3948
  }
3824
- function generatePermissionsForNavRoute(navRoute, customActions, includeStandardActions) {
3949
+ function generatePermissionsForNavRoute(navRoute, customActions, includeStandardActions, includeWildcard = true) {
3825
3950
  const permissions = [];
3826
3951
  const parts = navRoute.split(".");
3827
3952
  const context = parts[0];
3828
3953
  if (parts.length < 3) {
3829
3954
  throw new Error(`Invalid NavRoute format: ${navRoute}. Expected format: context.application.module`);
3830
3955
  }
3956
+ if (includeWildcard) {
3957
+ permissions.push({
3958
+ code: `${navRoute}.*`,
3959
+ name: formatPermissionName(navRoute, "Full Access"),
3960
+ description: `Full ${parts[parts.length - 1]} management`,
3961
+ category: context
3962
+ });
3963
+ }
3831
3964
  const actions = includeStandardActions ? [...STANDARD_ACTIONS, ...customActions] : customActions;
3832
3965
  for (const action of actions) {
3833
3966
  const code = `${navRoute}.${action}`;
@@ -3966,87 +4099,54 @@ function getUniqueNavRouteCount(permissions) {
3966
4099
  );
3967
4100
  return navRoutes.size;
3968
4101
  }
3969
- async function generatePermissionMigration(backendRoot, permissions, config) {
3970
- const templatePath = path11.join(
3971
- process.cwd(),
3972
- "templates",
3973
- "migrations",
3974
- "seed-permissions.cs.hbs"
3975
- );
3976
- let template;
3977
- try {
3978
- template = await readText(templatePath);
3979
- } catch {
3980
- template = getSeedPermissionsTemplate();
3981
- }
3982
- const handlebars = Handlebars2.create();
3983
- const compiled = handlebars.compile(template);
3984
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:T.]/g, "").slice(0, 14);
3985
- const migrationName = `SeedPermissions_${timestamp}`;
3986
- const content = compiled({
3987
- migrationName,
3988
- permissions,
3989
- timestamp
3990
- });
3991
- const migrationsPath = path11.join(
3992
- backendRoot,
3993
- "Infrastructure",
3994
- "Data",
3995
- "Migrations",
3996
- "Core"
3997
- );
3998
- await ensureDirectory(migrationsPath);
3999
- const filePath = path11.join(migrationsPath, `${migrationName}.cs`);
4000
- validatePathSecurity(filePath, config.smartstack.projectPath);
4001
- await writeText(filePath, content);
4002
- logger.info(`Generated permission migration: ${filePath}`);
4003
- return {
4004
- filePath,
4005
- migrationName
4006
- };
4007
- }
4008
- function getSeedPermissionsTemplate() {
4009
- return `using Microsoft.EntityFrameworkCore.Migrations;
4010
-
4011
- namespace SmartStack.Infrastructure.Data.Migrations.Core;
4102
+ function generateHasDataCode(permissions, navRoute) {
4103
+ const parts = navRoute.split(".");
4104
+ const moduleName = parts.length >= 3 ? parts[parts.length - 1] : "custom";
4105
+ const moduleVarName = `${moduleName}ModuleId`;
4106
+ let code = "```csharp\n";
4107
+ code += `// 1. Add module ID variable (get from NavigationModuleSeedData.cs)
4108
+ `;
4109
+ code += `var ${moduleVarName} = Guid.Parse("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); // TODO: Replace with actual ID
4012
4110
 
4013
- /// <summary>
4014
- /// Seed permissions generated from NavRoute controllers
4015
- /// Generated on {{timestamp}}
4016
- /// </summary>
4017
- public partial class {{migrationName}} : Migration
4018
- {
4019
- protected override void Up(MigrationBuilder migrationBuilder)
4020
- {
4021
- // Insert permissions
4022
- {{#each permissions}}
4023
- migrationBuilder.Sql(@"
4024
- IF NOT EXISTS (SELECT 1 FROM core.auth_Permissions WHERE Code = '{{code}}')
4025
- BEGIN
4026
- INSERT INTO core.auth_Permissions (Id, Code, Name, Description, IsDeleted, CreatedAt, UpdatedAt)
4027
- VALUES (
4028
- NEWID(),
4029
- '{{code}}',
4030
- '{{name}}',
4031
- '{{description}}',
4032
- 0,
4033
- GETUTCDATE(),
4034
- GETUTCDATE()
4035
- );
4036
- END
4037
- ");
4038
- {{/each}}
4039
- }
4040
-
4041
- protected override void Down(MigrationBuilder migrationBuilder)
4042
- {
4043
- // Remove seeded permissions
4044
- {{#each permissions}}
4045
- migrationBuilder.Sql(@"DELETE FROM core.auth_Permissions WHERE Code = '{{code}}'");
4046
- {{/each}}
4111
+ `;
4112
+ code += `// 2. Add these entries to the HasData() return array:
4113
+ `;
4114
+ for (const perm of permissions) {
4115
+ const isWildcard = perm.code.endsWith(".*");
4116
+ const action = perm.code.split(".").pop();
4117
+ const guidPlaceholder = generatePlaceholderGuid();
4118
+ if (isWildcard) {
4119
+ code += `new { Id = Guid.Parse("${guidPlaceholder}"), Path = "${perm.code}", Level = PermissionLevel.Module, IsWildcard = true, ModuleId = ${moduleVarName}, Description = "${perm.description}", CreatedAt = seedDate },
4120
+ `;
4121
+ } else {
4122
+ const actionEnum = getActionEnum(action || "read");
4123
+ code += `new { Id = Guid.Parse("${guidPlaceholder}"), Path = "${perm.code}", Level = PermissionLevel.Module, Action = PermissionAction.${actionEnum}, IsWildcard = false, ModuleId = ${moduleVarName}, Description = "${perm.description}", CreatedAt = seedDate },
4124
+ `;
4047
4125
  }
4048
- }
4126
+ }
4127
+ code += "```\n";
4128
+ code += "\n\u26A0\uFE0F **IMPORTANT**: Replace all GUIDs with randomly generated ones using:\n";
4129
+ code += "```powershell\n";
4130
+ code += `1..${permissions.length} | ForEach-Object { [guid]::NewGuid().ToString() }
4049
4131
  `;
4132
+ code += "```\n";
4133
+ return code;
4134
+ }
4135
+ function generatePlaceholderGuid() {
4136
+ return "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX";
4137
+ }
4138
+ function getActionEnum(action) {
4139
+ const actionMap = {
4140
+ "read": "Read",
4141
+ "create": "Create",
4142
+ "update": "Update",
4143
+ "delete": "Delete",
4144
+ "assign": "Assign",
4145
+ "execute": "Execute",
4146
+ "export": "Export",
4147
+ "import": "Import"
4148
+ };
4149
+ return actionMap[action.toLowerCase()] || action.charAt(0).toUpperCase() + action.slice(1);
4050
4150
  }
4051
4151
  async function readDirectoryRecursive(dir) {
4052
4152
  const files = [];
@@ -4066,7 +4166,7 @@ async function readDirectoryRecursive(dir) {
4066
4166
  }
4067
4167
 
4068
4168
  // src/tools/scaffold-tests.ts
4069
- import Handlebars3 from "handlebars";
4169
+ import Handlebars2 from "handlebars";
4070
4170
  import path12 from "path";
4071
4171
  var scaffoldTestsTool = {
4072
4172
  name: "scaffold_tests",
@@ -4146,15 +4246,15 @@ var scaffoldTestsTool = {
4146
4246
  required: ["target", "name"]
4147
4247
  }
4148
4248
  };
4149
- Handlebars3.registerHelper("pascalCase", (str) => {
4249
+ Handlebars2.registerHelper("pascalCase", (str) => {
4150
4250
  if (!str) return "";
4151
4251
  return str.charAt(0).toUpperCase() + str.slice(1);
4152
4252
  });
4153
- Handlebars3.registerHelper("camelCase", (str) => {
4253
+ Handlebars2.registerHelper("camelCase", (str) => {
4154
4254
  if (!str) return "";
4155
4255
  return str.charAt(0).toLowerCase() + str.slice(1);
4156
4256
  });
4157
- Handlebars3.registerHelper("unless", function(conditional, options) {
4257
+ Handlebars2.registerHelper("unless", function(conditional, options) {
4158
4258
  if (!conditional) {
4159
4259
  return options.fn(this);
4160
4260
  }
@@ -5496,7 +5596,7 @@ async function scaffoldEntityTests(name, options, testTypes, structure, config,
5496
5596
  ...options
5497
5597
  };
5498
5598
  if (testTypes.includes("unit")) {
5499
- const content = Handlebars3.compile(entityTestTemplate)(context);
5599
+ const content = Handlebars2.compile(entityTestTemplate)(context);
5500
5600
  const testPath = path12.join(structure.root, "Tests", "Unit", "Domain", `${name}Tests.cs`);
5501
5601
  validatePathSecurity(testPath, structure.root);
5502
5602
  if (!dryRun) {
@@ -5510,7 +5610,7 @@ async function scaffoldEntityTests(name, options, testTypes, structure, config,
5510
5610
  });
5511
5611
  }
5512
5612
  if (testTypes.includes("security")) {
5513
- const securityContent = Handlebars3.compile(securityTestTemplate)({
5613
+ const securityContent = Handlebars2.compile(securityTestTemplate)({
5514
5614
  ...context,
5515
5615
  nameLower: name.charAt(0).toLowerCase() + name.slice(1),
5516
5616
  apiNamespace: config.conventions.namespaces.api
@@ -5542,7 +5642,7 @@ async function scaffoldServiceTests(name, options, testTypes, structure, config,
5542
5642
  ...options
5543
5643
  };
5544
5644
  if (testTypes.includes("unit")) {
5545
- const content = Handlebars3.compile(serviceTestTemplate)(context);
5645
+ const content = Handlebars2.compile(serviceTestTemplate)(context);
5546
5646
  const testPath = path12.join(structure.root, "Tests", "Unit", "Services", `${name}ServiceTests.cs`);
5547
5647
  validatePathSecurity(testPath, structure.root);
5548
5648
  if (!dryRun) {
@@ -5572,7 +5672,7 @@ async function scaffoldControllerTests(name, options, testTypes, structure, conf
5572
5672
  ...options
5573
5673
  };
5574
5674
  if (testTypes.includes("integration")) {
5575
- const content = Handlebars3.compile(controllerTestTemplate)(context);
5675
+ const content = Handlebars2.compile(controllerTestTemplate)(context);
5576
5676
  const testPath = path12.join(structure.root, "Tests", "Integration", "Controllers", `${name}ControllerTests.cs`);
5577
5677
  validatePathSecurity(testPath, structure.root);
5578
5678
  if (!dryRun) {
@@ -5586,7 +5686,7 @@ async function scaffoldControllerTests(name, options, testTypes, structure, conf
5586
5686
  });
5587
5687
  }
5588
5688
  if (testTypes.includes("security")) {
5589
- const securityContent = Handlebars3.compile(securityTestTemplate)(context);
5689
+ const securityContent = Handlebars2.compile(securityTestTemplate)(context);
5590
5690
  const securityPath = path12.join(structure.root, "Tests", "Security", `${name}SecurityTests.cs`);
5591
5691
  validatePathSecurity(securityPath, structure.root);
5592
5692
  if (!dryRun) {
@@ -5611,7 +5711,7 @@ async function scaffoldValidatorTests(name, options, testTypes, structure, confi
5611
5711
  ...options
5612
5712
  };
5613
5713
  if (testTypes.includes("unit")) {
5614
- const content = Handlebars3.compile(validatorTestTemplate)(context);
5714
+ const content = Handlebars2.compile(validatorTestTemplate)(context);
5615
5715
  const testPath = path12.join(structure.root, "Tests", "Unit", "Validators", `${name}ValidatorTests.cs`);
5616
5716
  validatePathSecurity(testPath, structure.root);
5617
5717
  if (!dryRun) {
@@ -5638,7 +5738,7 @@ async function scaffoldRepositoryTests(name, options, testTypes, structure, conf
5638
5738
  ...options
5639
5739
  };
5640
5740
  if (testTypes.includes("integration")) {
5641
- const content = Handlebars3.compile(repositoryTestTemplate)(context);
5741
+ const content = Handlebars2.compile(repositoryTestTemplate)(context);
5642
5742
  const testPath = path12.join(structure.root, "Tests", "Integration", "Repositories", `${name}RepositoryTests.cs`);
5643
5743
  validatePathSecurity(testPath, structure.root);
5644
5744
  if (!dryRun) {
@@ -11337,7 +11437,7 @@ async function createServer() {
11337
11437
  const server = new Server(
11338
11438
  {
11339
11439
  name: "smartstack-mcp",
11340
- version: "1.9.0"
11440
+ version: "1.10.0"
11341
11441
  },
11342
11442
  {
11343
11443
  capabilities: {