@atlashub/smartstack-cli 4.5.0 → 4.7.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 +26 -8
- package/dist/index.js.map +1 -1
- package/dist/mcp-entry.mjs +24 -0
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -1
- package/templates/project/DependencyInjection.Application.cs.template +2 -2
- package/templates/project/Program.cs.template +91 -36
- package/templates/skills/apex/references/frontend-route-wiring-app-tsx.md +139 -0
- package/templates/skills/apex/references/person-extension-pattern.md +7 -5
- package/templates/skills/apex/references/smartstack-frontend.md +278 -103
- package/templates/skills/apex/references/smartstack-layers.md +2 -2
- package/templates/skills/apex/steps/step-03-execute.md +18 -1
- package/templates/skills/apex/steps/step-05-deep-review.md +1 -1
- package/templates/skills/cli-app-sync/SKILL.md +272 -0
- package/templates/skills/cli-app-sync/references/comparison-map.md +228 -0
- package/templates/skills/gitflow/SKILL.md +5 -0
- package/templates/skills/gitflow/steps/step-init.md +19 -0
- package/templates/skills/ralph-loop/references/task-transform-legacy.md +2 -2
- package/templates/skills/ui-components/SKILL.md +310 -0
- package/templates/skills/ui-components/patterns/data-table.md +136 -0
package/package.json
CHANGED
|
@@ -17,8 +17,8 @@ public static class DependencyInjection
|
|
|
17
17
|
this IServiceCollection services)
|
|
18
18
|
{
|
|
19
19
|
// TODO: Register your application services here
|
|
20
|
-
//
|
|
21
|
-
// services.
|
|
20
|
+
// NOTE: MediatR is already registered by AddSmartStack() — do NOT register it again here.
|
|
21
|
+
// Only add client-specific services (e.g., custom validators, domain services).
|
|
22
22
|
|
|
23
23
|
return services;
|
|
24
24
|
}
|
|
@@ -1,46 +1,101 @@
|
|
|
1
|
+
using Serilog;
|
|
2
|
+
using Serilog.Events;
|
|
1
3
|
using Microsoft.EntityFrameworkCore;
|
|
2
4
|
using SmartStack.Api.Extensions;
|
|
3
5
|
using {{ProjectName}}.Infrastructure;
|
|
4
6
|
using {{ProjectName}}.Infrastructure.Persistence;
|
|
5
7
|
using {{ProjectName}}.Application;
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
// Bootstrap logger for startup errors
|
|
10
|
+
Log.Logger = new LoggerConfiguration()
|
|
11
|
+
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
|
|
12
|
+
.Enrich.FromLogContext()
|
|
13
|
+
.WriteTo.Console()
|
|
14
|
+
.CreateBootstrapLogger();
|
|
8
15
|
|
|
9
|
-
|
|
10
|
-
// 1. Add SmartStack Core services (from NuGet package)
|
|
11
|
-
// ===================================================================
|
|
12
|
-
builder.Services.AddSmartStack(builder.Configuration, options =>
|
|
16
|
+
try
|
|
13
17
|
{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
18
|
+
Log.Information("Starting {{ProjectName}} API");
|
|
19
|
+
|
|
20
|
+
var builder = WebApplication.CreateBuilder(args);
|
|
21
|
+
|
|
22
|
+
// Configure Kestrel to use HTTP/1.1 on all configured endpoints
|
|
23
|
+
builder.WebHost.ConfigureKestrel((context, options) =>
|
|
24
|
+
{
|
|
25
|
+
options.ConfigureEndpointDefaults(listenOptions =>
|
|
26
|
+
{
|
|
27
|
+
listenOptions.Protocols = Microsoft.AspNetCore.Server.Kestrel.Core.HttpProtocols.Http1;
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Load appsettings.Local.json if it exists (for local development overrides)
|
|
32
|
+
builder.Configuration.AddJsonFile("appsettings.Local.json", optional: true, reloadOnChange: true);
|
|
33
|
+
|
|
34
|
+
// Serilog configuration from appsettings
|
|
35
|
+
builder.Host.UseSmartStackSerilog();
|
|
36
|
+
|
|
37
|
+
// Application Insights SDK (must be added before Serilog runs)
|
|
38
|
+
var aiConnectionString = builder.Configuration["Logging:Sinks:ApplicationInsights:ConnectionString"];
|
|
39
|
+
var aiEnabled = builder.Configuration.GetValue<bool>("Logging:Sinks:ApplicationInsights:Enabled");
|
|
40
|
+
if (!string.IsNullOrEmpty(aiConnectionString) && aiEnabled)
|
|
41
|
+
{
|
|
42
|
+
builder.Services.AddApplicationInsightsTelemetry(options =>
|
|
43
|
+
{
|
|
44
|
+
options.ConnectionString = aiConnectionString;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ===================================================================
|
|
49
|
+
// 1. Add SmartStack Core services (from NuGet package)
|
|
50
|
+
// ===================================================================
|
|
51
|
+
builder.Services.AddSmartStack(builder.Configuration, options =>
|
|
52
|
+
{
|
|
53
|
+
options.EnableDevSeeding = builder.Environment.IsDevelopment();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// ===================================================================
|
|
57
|
+
// 2. Add client-specific services (Dual-DbContext pattern)
|
|
58
|
+
// ===================================================================
|
|
59
|
+
builder.Services.Add{{ProjectName}}Infrastructure(builder.Configuration);
|
|
60
|
+
builder.Services.Add{{ProjectName}}Application();
|
|
61
|
+
|
|
62
|
+
var app = builder.Build();
|
|
39
63
|
|
|
40
|
-
// ===================================================================
|
|
41
|
-
//
|
|
42
|
-
// ===================================================================
|
|
43
|
-
|
|
44
|
-
app.
|
|
64
|
+
// ===================================================================
|
|
65
|
+
// 3. Initialize SmartStack + apply migrations (in correct order!)
|
|
66
|
+
// ===================================================================
|
|
67
|
+
// This initializes SmartStack and applies Core migrations automatically
|
|
68
|
+
await app.InitializeSmartStackAsync();
|
|
45
69
|
|
|
46
|
-
|
|
70
|
+
// Apply Extensions migrations AFTER Core migrations
|
|
71
|
+
// Your client-specific tables may have FK references to Core tables
|
|
72
|
+
if (app.Environment.IsDevelopment())
|
|
73
|
+
{
|
|
74
|
+
using var scope = app.Services.CreateScope();
|
|
75
|
+
var extDb = scope.ServiceProvider.GetRequiredService<ExtensionsDbContext>();
|
|
76
|
+
await extDb.Database.MigrateAsync();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ===================================================================
|
|
80
|
+
// 4. SmartStack middleware & endpoints
|
|
81
|
+
// ===================================================================
|
|
82
|
+
// Swagger UI (development only)
|
|
83
|
+
app.UseSmartStackSwagger();
|
|
84
|
+
|
|
85
|
+
// SmartStack middleware pipeline
|
|
86
|
+
app.UseSmartStack();
|
|
87
|
+
|
|
88
|
+
// Map controllers and SignalR hubs
|
|
89
|
+
app.MapSmartStack();
|
|
90
|
+
|
|
91
|
+
Log.Information("{{ProjectName}} API started successfully");
|
|
92
|
+
app.Run();
|
|
93
|
+
}
|
|
94
|
+
catch (Exception ex)
|
|
95
|
+
{
|
|
96
|
+
Log.Fatal(ex, "Application terminated unexpectedly");
|
|
97
|
+
}
|
|
98
|
+
finally
|
|
99
|
+
{
|
|
100
|
+
Log.CloseAndFlush();
|
|
101
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Frontend: Route Wiring in App.tsx
|
|
2
|
+
|
|
3
|
+
> Referenced from `steps/step-03-execute.md` — Detailed route wiring patterns and verification.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Step 4: Wire Routes to App.tsx (BLOCKING)
|
|
8
|
+
|
|
9
|
+
**CRITICAL:** This step is MANDATORY. Without it, routes exist as files but are invisible to the React Router. The page will be BLANK.
|
|
10
|
+
|
|
11
|
+
After `scaffold_routes` generates the route files, you MUST manually insert the routes into `App.tsx`.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Step 4a: Import Page Components
|
|
16
|
+
|
|
17
|
+
At the top of App.tsx:
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { {EntityName}Page } from '@/pages/{Application}/{Module}/{EntityName}Page';
|
|
21
|
+
// Or lazy-loaded:
|
|
22
|
+
const {EntityName}Page = lazy(() => import('@/pages/{Application}/{Module}/{EntityName}Page'));
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Step 4b: Detect App.tsx Routing Pattern
|
|
28
|
+
|
|
29
|
+
Read App.tsx and detect which pattern is used:
|
|
30
|
+
|
|
31
|
+
### Pattern A: applicationRoutes Object
|
|
32
|
+
|
|
33
|
+
**If App.tsx contains:** `applicationRoutes: ApplicationRouteExtensions`
|
|
34
|
+
|
|
35
|
+
→ Add routes to `applicationRoutes.{application}[]` with **RELATIVE** paths (no leading `/`)
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
const applicationRoutes: ApplicationRouteExtensions = {
|
|
39
|
+
'human-resources': [
|
|
40
|
+
// existing routes...
|
|
41
|
+
{ path: '{module_kebab}/{section_kebab}', element: <{EntityName}ListPage /> },
|
|
42
|
+
{ path: '{module_kebab}/{section_kebab}/new', element: <Create{EntityName}Page /> },
|
|
43
|
+
{ path: '{module_kebab}/{section_kebab}/:id', element: <{EntityName}DetailPage /> },
|
|
44
|
+
{ path: '{module_kebab}/{section_kebab}/:id/edit', element: <Create{EntityName}Page /> },
|
|
45
|
+
|
|
46
|
+
// Parent redirect routes (MANDATORY — prevents /login redirect on parent navigation)
|
|
47
|
+
{ path: '{module_kebab}', element: <Navigate to="{module_kebab}/{first_section_kebab}" replace /> },
|
|
48
|
+
{ path: '', element: <Navigate to="{first_module_kebab}/{first_section_kebab}" replace /> },
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Routes are automatically injected into BOTH standard (`/{application}/...`) and tenant-prefixed (`/t/:slug/{application}/...`) route trees by `mergeRoutes()`. No manual duplication needed.
|
|
54
|
+
|
|
55
|
+
### Pattern B: JSX Routes
|
|
56
|
+
|
|
57
|
+
**If App.tsx contains:** `<Route path="/{application}" element={<{Layout} />}>`
|
|
58
|
+
|
|
59
|
+
→ Insert `<Route>` children inside the Layout wrapper:
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
<Route path="/human-resources" element={<AppLayout />}>
|
|
63
|
+
{/* ... existing routes ... */}
|
|
64
|
+
<Route path="{module_kebab}" element={<{EntityName}Page />} />
|
|
65
|
+
</Route>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**ALSO add the same routes inside the tenant-prefixed block:**
|
|
69
|
+
|
|
70
|
+
Find `<Route path="/t/:slug">` and add the **same route entries** there.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Step 4c: Application-to-Layout Mapping
|
|
75
|
+
|
|
76
|
+
| Application prefix | Layout Component | Route path |
|
|
77
|
+
|---------|------------------|------------|
|
|
78
|
+
| `administration.*` | `AppLayout` | `/administration` |
|
|
79
|
+
| `*` (business apps) | `AppLayout` | `/{application}` |
|
|
80
|
+
| `myspace.*` | `AppLayout` | `/myspace` |
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Step 4d: Verify Wiring
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
Tool: mcp__smartstack__validate_frontend_routes
|
|
88
|
+
Args:
|
|
89
|
+
scope: "routes"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
If `appWiring.issues` is not empty, fix the wiring before proceeding.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Step 4e: Parent Redirect Routes (MANDATORY)
|
|
97
|
+
|
|
98
|
+
**CRITICAL:** Without parent redirects, navigating to an application or module URL (e.g., `/human-resources` or `/human-resources/employees`) will cause a redirect to `/login` because no route matches.
|
|
99
|
+
|
|
100
|
+
For each application, you MUST add:
|
|
101
|
+
|
|
102
|
+
1. **Application root redirect** — redirects `/{application}` to the first module/section:
|
|
103
|
+
```tsx
|
|
104
|
+
{ path: '', element: <Navigate to="{first_module}/{first_section}" replace /> }
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
2. **Module redirect** (if modules have sections) — redirects `/{application}/{module}` to first section:
|
|
108
|
+
```tsx
|
|
109
|
+
{ path: '{module}', element: <Navigate to="{module}/{first_section}" replace /> }
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Example:** For NavRoutes `human-resources.employees.management` and `human-resources.employees.departments`:
|
|
113
|
+
```tsx
|
|
114
|
+
{ path: 'employees', element: <Navigate to="employees/management" replace /> },
|
|
115
|
+
{ path: '', element: <Navigate to="employees/management" replace /> },
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The `to` prop is resolved relative to the **parent route** (`/{application}`), so always use the full path from the application root.
|
|
119
|
+
|
|
120
|
+
> Note: `scaffold_routes` with `outputFormat: "clientRoutes"` generates these redirects automatically.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Forbidden Patterns (BOTH patterns)
|
|
125
|
+
|
|
126
|
+
- Adding application routes to `clientRoutes[]` with absolute paths — `clientRoutes` is ONLY for routes outside SmartStack applications (e.g., `/about`, `/pricing`)
|
|
127
|
+
- Adding routes OUTSIDE the Layout wrapper (shell will not render)
|
|
128
|
+
- Using `createBrowserRouter` (SmartStack uses `useRoutes()` + `mergeRoutes()`)
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Verification Checklist
|
|
133
|
+
|
|
134
|
+
- [ ] Routes are inside the AppLayout wrapper
|
|
135
|
+
- [ ] Routes use NESTED structure (not flat)
|
|
136
|
+
- [ ] Application kebab-case matches navigation seed data
|
|
137
|
+
- [ ] Both standard and tenant-prefixed blocks have routes (if using Pattern B)
|
|
138
|
+
- [ ] Page components are imported at top of App.tsx
|
|
139
|
+
- [ ] `mcp__smartstack__validate_frontend_routes` returns no issues
|
|
@@ -542,15 +542,17 @@ public record CustomerResponseDto(
|
|
|
542
542
|
|
|
543
543
|
```tsx
|
|
544
544
|
// Composite columns from response DTO (already resolved by backend)
|
|
545
|
-
<
|
|
545
|
+
<DataTable
|
|
546
546
|
columns={[
|
|
547
|
-
{ key: 'code', label: t('module:columns.code', 'Code') },
|
|
548
|
-
{ key: 'displayFirstName', label: t('module:columns.firstName', 'First Name') },
|
|
549
|
-
{ key: 'displayLastName', label: t('module:columns.lastName', 'Last Name') },
|
|
550
|
-
{ key: 'displayEmail', label: t('module:columns.email', 'Email') },
|
|
547
|
+
{ key: 'code', label: t('module:columns.code', 'Code'), sortable: true },
|
|
548
|
+
{ key: 'displayFirstName', label: t('module:columns.firstName', 'First Name'), sortable: true },
|
|
549
|
+
{ key: 'displayLastName', label: t('module:columns.lastName', 'Last Name'), sortable: true },
|
|
550
|
+
{ key: 'displayEmail', label: t('module:columns.email', 'Email'), sortable: true },
|
|
551
551
|
{ key: 'status', label: t('module:columns.status', 'Status') },
|
|
552
552
|
]}
|
|
553
553
|
data={items}
|
|
554
|
+
searchable
|
|
555
|
+
pagination={{ pageSize: 10 }}
|
|
554
556
|
/>
|
|
555
557
|
```
|
|
556
558
|
|