@atlashub/smartstack-cli 3.24.0 → 3.26.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.
Files changed (33) hide show
  1. package/dist/index.js +5 -0
  2. package/dist/index.js.map +1 -1
  3. package/dist/mcp-entry.mjs +51 -14
  4. package/dist/mcp-entry.mjs.map +1 -1
  5. package/package.json +1 -1
  6. package/templates/skills/apex/SKILL.md +26 -5
  7. package/templates/skills/apex/_shared.md +3 -3
  8. package/templates/skills/apex/references/agent-teams-protocol.md +8 -8
  9. package/templates/skills/apex/references/challenge-questions.md +165 -0
  10. package/templates/skills/apex/references/post-checks.md +457 -0
  11. package/templates/skills/apex/references/smartstack-api.md +234 -14
  12. package/templates/skills/apex/references/smartstack-frontend.md +20 -0
  13. package/templates/skills/apex/references/smartstack-layers.md +16 -4
  14. package/templates/skills/apex/steps/step-00-init.md +84 -56
  15. package/templates/skills/apex/steps/step-01-analyze.md +73 -87
  16. package/templates/skills/apex/steps/step-03-execute.md +6 -4
  17. package/templates/skills/apex/steps/step-04-examine.md +198 -0
  18. package/templates/skills/apex/steps/{step-05-examine.md → step-05-deep-review.md} +6 -6
  19. package/templates/skills/apex/steps/step-06-resolve.md +2 -2
  20. package/templates/skills/business-analyse/SKILL.md +28 -0
  21. package/templates/skills/business-analyse/references/agent-module-prompt.md +255 -0
  22. package/templates/skills/business-analyse/references/agent-pooling-best-practices.md +26 -10
  23. package/templates/skills/business-analyse/references/team-orchestration.md +437 -0
  24. package/templates/skills/business-analyse/steps/step-02-decomposition.md +31 -4
  25. package/templates/skills/business-analyse/steps/step-03a1-setup.md +21 -0
  26. package/templates/skills/business-analyse/steps/step-03d-validate.md +84 -0
  27. package/templates/skills/efcore/steps/migration/step-02-create.md +14 -1
  28. package/templates/skills/ralph-loop/references/category-rules.md +26 -2
  29. package/templates/skills/ralph-loop/references/compact-loop.md +1 -1
  30. package/templates/skills/ralph-loop/references/core-seed-data.md +45 -10
  31. package/templates/skills/ralph-loop/steps/step-02-execute.md +128 -1
  32. package/templates/skills/validate-feature/steps/step-01-compile.md +4 -1
  33. package/templates/skills/apex/steps/step-04-validate.md +0 -448
@@ -34504,28 +34504,32 @@ using System.Linq;
34504
34504
  using Microsoft.EntityFrameworkCore;
34505
34505
  using Microsoft.Extensions.Logging;
34506
34506
  using SmartStack.Application.Common.Interfaces.Identity;
34507
+ using SmartStack.Application.Common.Interfaces.Tenants;
34507
34508
  using SmartStack.Application.Common.Interfaces.Persistence;
34508
34509
 
34509
34510
  namespace {{implNamespace}};
34510
34511
 
34511
34512
  /// <summary>
34512
34513
  /// Service implementation for {{name}} operations.
34513
- /// IMPORTANT: All queries MUST filter by _currentUser.TenantId for multi-tenant isolation.
34514
+ /// IMPORTANT: All queries MUST filter by tenant ID for multi-tenant isolation.
34514
34515
  /// IMPORTANT: GetAllAsync MUST support search parameter for frontend EntityLookup component.
34515
34516
  /// </summary>
34516
34517
  public class {{name}}Service : I{{name}}Service
34517
34518
  {
34518
34519
  private readonly IExtensionsDbContext _db;
34519
- private readonly ICurrentUser _currentUser;
34520
+ private readonly ICurrentUserService _currentUser;
34521
+ private readonly ICurrentTenantService _currentTenant;
34520
34522
  private readonly ILogger<{{name}}Service> _logger;
34521
34523
 
34522
34524
  public {{name}}Service(
34523
34525
  IExtensionsDbContext db,
34524
- ICurrentUser currentUser,
34526
+ ICurrentUserService currentUser,
34527
+ ICurrentTenantService currentTenant,
34525
34528
  ILogger<{{name}}Service> logger)
34526
34529
  {
34527
34530
  _db = db;
34528
34531
  _currentUser = currentUser;
34532
+ _currentTenant = currentTenant;
34529
34533
  _logger = logger;
34530
34534
  }
34531
34535
 
@@ -34533,8 +34537,10 @@ public class {{name}}Service : I{{name}}Service
34533
34537
  /// <inheritdoc />
34534
34538
  public async Task<object> {{this}}(CancellationToken cancellationToken = default)
34535
34539
  {
34536
- _logger.LogInformation("Executing {{this}} for tenant {TenantId}", _currentUser.TenantId);
34537
- // TODO: Implement {{this}} \u2014 ALL queries must filter by _currentUser.TenantId
34540
+ var tenantId = _currentTenant.TenantId
34541
+ ?? throw new UnauthorizedAccessException("Tenant context is required");
34542
+ _logger.LogInformation("Executing {{this}} for tenant {TenantId}", tenantId);
34543
+ // TODO: Implement {{this}} \u2014 ALL queries must filter by tenantId
34538
34544
  // IMPORTANT: GetAllAsync MUST accept (string? search, int page, int pageSize) parameters
34539
34545
  // to enable EntityLookup search on the frontend. Example:
34540
34546
  // if (!string.IsNullOrWhiteSpace(search))
@@ -57016,12 +57022,37 @@ async function scaffoldRoutes(input, config2) {
57016
57022
  result.instructions.push("import { lazy, Suspense } from 'react';");
57017
57023
  result.instructions.push("import { PageLoader } from '@/components/ui/PageLoader';");
57018
57024
  result.instructions.push("");
57025
+ const importedComponents = /* @__PURE__ */ new Set();
57026
+ for (const [context, applications] of Object.entries(routeTree)) {
57027
+ for (const [, modules] of Object.entries(applications)) {
57028
+ for (const route of modules) {
57029
+ const pageEntry = pageFiles.get(route.navRoute);
57030
+ if (pageEntry) {
57031
+ for (const entry of pageEntry) {
57032
+ if (!importedComponents.has(entry.componentName)) {
57033
+ importedComponents.add(entry.componentName);
57034
+ result.instructions.push(`const ${entry.componentName} = lazy(() =>`);
57035
+ result.instructions.push(` import('${entry.importPath}').then(m => ({ default: m.${entry.componentName} }))`);
57036
+ result.instructions.push(");");
57037
+ }
57038
+ }
57039
+ } else {
57040
+ const component = route.navRoute.split(".").map(capitalize).join("") + "Page";
57041
+ if (!importedComponents.has(component)) {
57042
+ importedComponents.add(component);
57043
+ result.instructions.push(`// TODO: const ${component} = lazy(() => import('@/pages/...'));`);
57044
+ }
57045
+ }
57046
+ }
57047
+ }
57048
+ }
57049
+ result.instructions.push("");
57019
57050
  result.instructions.push("const contextRoutes: ContextRouteExtensions = {");
57020
57051
  for (const [context, applications] of Object.entries(routeTree)) {
57021
57052
  result.instructions.push(` ${context}: [`);
57022
57053
  for (const [, modules] of Object.entries(applications)) {
57023
57054
  for (const route of modules) {
57024
- const modulePath = route.navRoute.split(".").slice(1).join("/");
57055
+ const modulePath = route.navRoute.split(".").slice(1).map(toKebabCase).join("/");
57025
57056
  const pageEntry = pageFiles.get(route.navRoute);
57026
57057
  const component = pageEntry?.[0]?.componentName || `${route.navRoute.split(".").map(capitalize).join("")}Page`;
57027
57058
  result.instructions.push(` { path: '${modulePath}', element: <Suspense fallback={<PageLoader />}><${component} /></Suspense> },`);
@@ -57047,7 +57078,7 @@ async function scaffoldRoutes(input, config2) {
57047
57078
  result.instructions.push("```tsx");
57048
57079
  for (const [, modules] of Object.entries(applications)) {
57049
57080
  for (const route of modules) {
57050
- const modulePath = route.navRoute.split(".").slice(1).join("/");
57081
+ const modulePath = route.navRoute.split(".").slice(1).map(toKebabCase).join("/");
57051
57082
  const pageEntry = pageFiles.get(route.navRoute);
57052
57083
  const component = pageEntry?.[0]?.componentName || `${route.navRoute.split(".").map(capitalize).join("")}Page`;
57053
57084
  result.instructions.push(`<Route path="${modulePath}" element={<Suspense fallback={<PageLoader />}><${component} /></Suspense>} />`);
@@ -57150,11 +57181,11 @@ async function discoverNavRoutes(structure, scope, warnings) {
57150
57181
  permissions.push(match2[1]);
57151
57182
  }
57152
57183
  const fullNavRoute = suffix ? `${navRoute}.${suffix}` : navRoute;
57153
- const expectedRoute = `api/${navRoute.replace(/\./g, "/")}${suffix ? `/${suffix}` : ""}`;
57184
+ const expectedRoute = `api/${navRouteToUrlPath(navRoute)}${suffix ? `/${toKebabCase(suffix)}` : ""}`;
57154
57185
  routes.push({
57155
57186
  navRoute: fullNavRoute,
57156
- apiPath: `/api/${navRoute.replace(/\./g, "/")}${suffix ? `/${suffix}` : ""}`,
57157
- webPath: `/${navRoute.replace(/\./g, "/")}${suffix ? `/${suffix}` : ""}`,
57187
+ apiPath: `/api/${navRouteToUrlPath(navRoute)}${suffix ? `/${toKebabCase(suffix)}` : ""}`,
57188
+ webPath: `/${navRouteToUrlPath(navRoute)}${suffix ? `/${toKebabCase(suffix)}` : ""}`,
57158
57189
  permissions,
57159
57190
  controller: controllerName,
57160
57191
  methods
@@ -57319,7 +57350,7 @@ function generateRouterConfig(routes, includeGuards) {
57319
57350
  lines.push(` path: '${app}',`);
57320
57351
  lines.push(" children: [");
57321
57352
  for (const route of modules) {
57322
- const modulePath = route.navRoute.split(".").slice(2).join("/");
57353
+ const modulePath = route.navRoute.split(".").slice(2).map(toKebabCase).join("/");
57323
57354
  const pageName = route.navRoute.split(".").map(capitalize).join("");
57324
57355
  if (includeGuards && route.permissions.length > 0) {
57325
57356
  lines.push(" {");
@@ -57583,10 +57614,10 @@ function generateClientRoutesConfig(routes, pageFiles, includeGuards) {
57583
57614
  lines.push(" {");
57584
57615
  lines.push(` path: '${app}',`);
57585
57616
  lines.push(" children: [");
57586
- const firstModulePath = modules[0].navRoute.split(".").slice(2).join("/");
57617
+ const firstModulePath = modules[0].navRoute.split(".").slice(2).map(toKebabCase).join("/");
57587
57618
  lines.push(` { index: true, element: <Navigate to="${firstModulePath}" replace /> },`);
57588
57619
  for (const route of modules) {
57589
- const modulePath = route.navRoute.split(".").slice(2).join("/");
57620
+ const modulePath = route.navRoute.split(".").slice(2).map(toKebabCase).join("/");
57590
57621
  const pageName = route.navRoute.split(".").map(capitalize).join("") + "Page";
57591
57622
  const pageEntry = pageFiles.get(route.navRoute);
57592
57623
  const component = pageEntry?.[0]?.componentName || pageName;
@@ -57611,7 +57642,7 @@ function generateClientRoutesConfig(routes, pageFiles, includeGuards) {
57611
57642
  lines.push(" },");
57612
57643
  } else {
57613
57644
  for (const route of modules) {
57614
- const fullPath = route.navRoute.split(".").slice(1).join("/");
57645
+ const fullPath = route.navRoute.split(".").slice(1).map(toKebabCase).join("/");
57615
57646
  const pageName = route.navRoute.split(".").map(capitalize).join("") + "Page";
57616
57647
  const pageEntry = pageFiles.get(route.navRoute);
57617
57648
  const component = pageEntry?.[0]?.componentName || pageName;
@@ -57673,6 +57704,12 @@ function getLayoutName(context) {
57673
57704
  };
57674
57705
  return layoutMap[context] || `${capitalize(context)}Layout`;
57675
57706
  }
57707
+ function toKebabCase(segment) {
57708
+ return segment.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
57709
+ }
57710
+ function navRouteToUrlPath(navRoute) {
57711
+ return navRoute.split(".").map(toKebabCase).join("/");
57712
+ }
57676
57713
  function capitalize(str) {
57677
57714
  return str.charAt(0).toUpperCase() + str.slice(1);
57678
57715
  }