@atlashub/smartstack-cli 4.14.0 → 4.17.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 +4 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-entry.mjs +256 -101
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -1
- package/templates/mcp-scaffolding/frontend/api-client.ts.hbs +1 -0
- package/templates/skills/apex/references/agent-teams-protocol.md +28 -5
- package/templates/skills/apex/references/frontend-route-wiring-app-tsx.md +1 -1
- package/templates/skills/apex/steps/step-02-plan.md +132 -0
- package/templates/skills/apex/steps/step-03-execute.md +71 -0
- package/templates/skills/apex/steps/step-04-examine.md +21 -0
- package/templates/skills/apex/steps/step-05-deep-review.md +11 -0
- package/templates/skills/apex/steps/step-06-resolve.md +24 -5
- package/templates/skills/apex/steps/step-07-tests.md +11 -0
- package/templates/skills/application/references/frontend-route-wiring-app-tsx.md +36 -4
- package/templates/skills/application/references/nav-fallback-procedure.md +6 -4
- package/templates/skills/application/templates-frontend.md +3 -3
- package/templates/skills/controller/steps/step-01-analyze.md +8 -2
- package/templates/skills/documentation/steps/step-03-validate.md +47 -16
- package/templates/mcp-scaffolding/frontend/nav-routes.ts.hbs +0 -133
- package/templates/mcp-scaffolding/frontend/routes.tsx.hbs +0 -126
|
@@ -25,7 +25,9 @@ Validate that generated documentation is complete and accurate, then integrate i
|
|
|
25
25
|
- [ ] Mock UI faithfully reproduces the real page interface (not generic KPI/table)
|
|
26
26
|
- [ ] Every Mock UI section has `Annotation` components explaining each visual element
|
|
27
27
|
- [ ] i18n JSON is FLAT (no root key) — `t('title')` resolves directly
|
|
28
|
-
- [ ] Route
|
|
28
|
+
- [ ] Route added as **child** of `/docs/business` group (not standalone) in App.tsx
|
|
29
|
+
- [ ] Route added in `smartstackRoutes.tsx` (locked routes)
|
|
30
|
+
- [ ] DocsLayout sidebar updated (if applicable)
|
|
29
31
|
- [ ] Module appears in UserIndexPage.tsx (if applicable)
|
|
30
32
|
- [ ] DocPanelContext.tsx mapping updated for all module routes
|
|
31
33
|
- [ ] i18n/config.ts updated with new namespace
|
|
@@ -36,7 +38,9 @@ Validate that generated documentation is complete and accurate, then integrate i
|
|
|
36
38
|
- [ ] i18n JSON uses root key matching namespace
|
|
37
39
|
- [ ] DocRenderer wrapper (index.tsx) passes correct `i18nNamespace` prop
|
|
38
40
|
- [ ] doc-data.ts contains structured data (~50 lines)
|
|
39
|
-
- [ ] Route
|
|
41
|
+
- [ ] Route added as **child** of `/system/docs` group (not standalone) in App.tsx
|
|
42
|
+
- [ ] Route added in `smartstackRoutes.tsx` (locked routes)
|
|
43
|
+
- [ ] DocsLayout sidebar updated (if applicable)
|
|
40
44
|
|
|
41
45
|
#### For `update` Type
|
|
42
46
|
|
|
@@ -111,34 +115,59 @@ detects tenant-prefixed routes (`/t/:slug/...`), and builds lookup keys as `{app
|
|
|
111
115
|
|
|
112
116
|
#### App.tsx Routing
|
|
113
117
|
|
|
114
|
-
**
|
|
118
|
+
Documentation pages use a **separate routing system** from business pages. They render inside `DocsLayout` (sidebar navigation), NOT `AppLayout`. There are two route groups:
|
|
119
|
+
|
|
120
|
+
- **`/docs/business`** → Business module docs (user type) — uses `<DocsLayout />`
|
|
121
|
+
- **`/system/docs`** → Developer/database/testing docs — uses `<DocsLayout />`
|
|
122
|
+
|
|
123
|
+
> **Note:** The `<Suspense fallback={<PageLoader />}>` is already in place at the global level (wrapping the entire Routes tree). Do NOT add per-route `<Suspense>` wrappers.
|
|
124
|
+
|
|
125
|
+
**For `user` type:** Add lazy import and **child route** inside the existing `/docs/business` group:
|
|
115
126
|
|
|
116
127
|
```tsx
|
|
128
|
+
// 1. Add lazy import at top of App.tsx
|
|
117
129
|
const {Module}DocPage = lazy(() =>
|
|
118
130
|
import('@/pages/docs/business/{application}/{module}')
|
|
119
131
|
);
|
|
120
132
|
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
133
|
+
// 2. Find the existing <Route path="/docs/business" element={<DocsLayout />}> group
|
|
134
|
+
// Add as CHILD route (not standalone):
|
|
135
|
+
<Route path="/docs/business" element={<DocsLayout />}>
|
|
136
|
+
{/* ... existing doc routes ... */}
|
|
137
|
+
<Route path="{application}/{module}" element={<{Module}DocPage />} />
|
|
138
|
+
</Route>
|
|
126
139
|
```
|
|
127
140
|
|
|
128
|
-
**For `developer|database|testing` types:** Add
|
|
141
|
+
**For `developer|database|testing` types:** Add **child route** inside the existing `/system/docs` group:
|
|
129
142
|
|
|
130
143
|
```tsx
|
|
144
|
+
// 1. Add lazy import at top of App.tsx
|
|
131
145
|
const {Tool}DocPage = lazy(() =>
|
|
132
|
-
import('@/pages/docs/
|
|
146
|
+
import('@/pages/docs/{category}/{tool}')
|
|
133
147
|
);
|
|
134
148
|
|
|
135
|
-
//
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
149
|
+
// 2. Find the existing <Route path="/system/docs" element={<DocsLayout />}> group
|
|
150
|
+
// Add as CHILD route:
|
|
151
|
+
<Route path="/system/docs" element={<DocsLayout />}>
|
|
152
|
+
{/* ... existing doc routes ... */}
|
|
153
|
+
<Route path="{category}/{tool}" element={<{Tool}DocPage />} />
|
|
154
|
+
</Route>
|
|
140
155
|
```
|
|
141
156
|
|
|
157
|
+
#### smartstackRoutes.tsx Registration
|
|
158
|
+
|
|
159
|
+
**MANDATORY** for all doc types: Add the route in `smartstackRoutes.tsx` under the corresponding group (`/docs/business` or `/system/docs`). Documentation routes are part of `LOCKED_PREFIXES` and cannot be overridden by client extensions.
|
|
160
|
+
|
|
161
|
+
#### DocsLayout Sidebar Update
|
|
162
|
+
|
|
163
|
+
If the new documentation page should appear in the sidebar navigation, update the `navigation` array in `DocsLayout.tsx` with the new entry (label, path, icon).
|
|
164
|
+
|
|
165
|
+
#### Important Notes
|
|
166
|
+
|
|
167
|
+
- Documentation routes do **NOT** use `AppLayout`, `RouteGuard`, or `LicenseGuard`
|
|
168
|
+
- Documentation routes have **no tenant prefix** (`/t/:slug/...`) — they are globally accessible
|
|
169
|
+
- Documentation routes require **no specific permission** to access
|
|
170
|
+
|
|
142
171
|
#### i18n Config Update
|
|
143
172
|
|
|
144
173
|
**File:** `web/smartstack-web/src/i18n/config.ts`
|
|
@@ -230,7 +259,9 @@ Add or update entry with metadata:
|
|
|
230
259
|
- [x] Mock UI faithfully reproduces the real page interface (user type)
|
|
231
260
|
- [x] Every Mock UI section has `Annotation` components (user type)
|
|
232
261
|
- [x] i18n files created in ALL 4 languages (FR, EN, DE, IT) with correct format
|
|
233
|
-
- [x] Route
|
|
262
|
+
- [x] Route added as child of correct DocsLayout group in App.tsx
|
|
263
|
+
- [x] Route registered in smartstackRoutes.tsx (locked routes)
|
|
264
|
+
- [x] DocsLayout sidebar updated (if applicable)
|
|
234
265
|
- [x] docs-manifest.json updated
|
|
235
266
|
- [x] i18n/config.ts updated with new namespace
|
|
236
267
|
- [x] Module appears in UserIndexPage (if type = user)
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
{{!-- SmartStack NavRoute Registry Template --}}
|
|
2
|
-
{{!-- Generates centralized route registry for frontend --}}
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* NavRoute Registry
|
|
6
|
-
*
|
|
7
|
-
* Auto-generated by SmartStack MCP - DO NOT EDIT MANUALLY
|
|
8
|
-
* Generated: {{generatedAt}}
|
|
9
|
-
* Source: {{source}}
|
|
10
|
-
* Routes: {{routeCount}}
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
export interface NavRoute {
|
|
14
|
-
/** NavRoute path (e.g., "administration.users") */
|
|
15
|
-
navRoute: string;
|
|
16
|
-
/** API endpoint path (e.g., "/api/administration/users") */
|
|
17
|
-
api: string;
|
|
18
|
-
/** Frontend route path (e.g., "/administration/users") */
|
|
19
|
-
web: string;
|
|
20
|
-
/** Required permissions to access this route */
|
|
21
|
-
permissions: string[];
|
|
22
|
-
/** Backend controller name */
|
|
23
|
-
controller?: string;
|
|
24
|
-
/** Supported HTTP methods */
|
|
25
|
-
methods: string[];
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* All registered NavRoutes
|
|
30
|
-
*/
|
|
31
|
-
export const ROUTES: Record<string, NavRoute> = {
|
|
32
|
-
{{#each routes}}
|
|
33
|
-
'{{this.navRoute}}': {
|
|
34
|
-
navRoute: '{{this.navRoute}}',
|
|
35
|
-
api: '{{this.apiPath}}',
|
|
36
|
-
web: '{{this.webPath}}',
|
|
37
|
-
permissions: [{{#each this.permissions}}'{{this}}'{{#unless @last}}, {{/unless}}{{/each}}],
|
|
38
|
-
{{#if this.controller}}
|
|
39
|
-
controller: '{{this.controller}}',
|
|
40
|
-
{{/if}}
|
|
41
|
-
methods: [{{#each this.methods}}'{{this}}'{{#unless @last}}, {{/unless}}{{/each}}],
|
|
42
|
-
},
|
|
43
|
-
{{/each}}
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Get route configuration by NavRoute path
|
|
48
|
-
* @throws Error if route not found
|
|
49
|
-
*/
|
|
50
|
-
export function getRoute(navRoute: string): NavRoute {
|
|
51
|
-
const route = ROUTES[navRoute];
|
|
52
|
-
if (!route) {
|
|
53
|
-
throw new Error(`Route not found: ${navRoute}. Run scaffold_routes to regenerate.`);
|
|
54
|
-
}
|
|
55
|
-
return route;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Safely get route configuration, returns undefined if not found
|
|
60
|
-
*/
|
|
61
|
-
export function tryGetRoute(navRoute: string): NavRoute | undefined {
|
|
62
|
-
return ROUTES[navRoute];
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Check if user has permission for route
|
|
67
|
-
*/
|
|
68
|
-
export function hasRoutePermission(navRoute: string, userPermissions: string[]): boolean {
|
|
69
|
-
const route = ROUTES[navRoute];
|
|
70
|
-
if (!route || route.permissions.length === 0) return true;
|
|
71
|
-
return route.permissions.some(p => userPermissions.includes(p));
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Get all routes for an application (e.g., "administration")
|
|
76
|
-
*/
|
|
77
|
-
export function getRoutesByApplication(application: string): NavRoute[] {
|
|
78
|
-
return Object.values(ROUTES).filter(r => r.navRoute.startsWith(`${application}.`));
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Get all routes for an application and module (e.g., "administration.users")
|
|
83
|
-
*/
|
|
84
|
-
export function getRoutesByModule(application: string, module: string): NavRoute[] {
|
|
85
|
-
const prefix = `${application}.${module}`;
|
|
86
|
-
return Object.values(ROUTES).filter(r => r.navRoute.startsWith(`${prefix}.`) || r.navRoute === prefix);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Get all unique applications
|
|
91
|
-
*/
|
|
92
|
-
export function getApplications(): string[] {
|
|
93
|
-
const applications = new Set<string>();
|
|
94
|
-
for (const route of Object.values(ROUTES)) {
|
|
95
|
-
applications.add(route.navRoute.split('.')[0]);
|
|
96
|
-
}
|
|
97
|
-
return Array.from(applications);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Get all unique modules within an application
|
|
102
|
-
*/
|
|
103
|
-
export function getModules(application: string): string[] {
|
|
104
|
-
const modules = new Set<string>();
|
|
105
|
-
for (const route of Object.values(ROUTES)) {
|
|
106
|
-
const parts = route.navRoute.split('.');
|
|
107
|
-
if (parts[0] === application && parts.length >= 2) {
|
|
108
|
-
modules.add(parts[1]);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
return Array.from(modules);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Build API URL from NavRoute with optional path segments
|
|
116
|
-
*/
|
|
117
|
-
export function buildApiUrl(navRoute: string, ...segments: string[]): string {
|
|
118
|
-
const route = getRoute(navRoute);
|
|
119
|
-
if (segments.length === 0) return route.api;
|
|
120
|
-
return `${route.api}/${segments.join('/')}`;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Build frontend URL from NavRoute with optional path segments
|
|
125
|
-
*/
|
|
126
|
-
export function buildWebUrl(navRoute: string, ...segments: string[]): string {
|
|
127
|
-
const route = getRoute(navRoute);
|
|
128
|
-
if (segments.length === 0) return route.web;
|
|
129
|
-
return `${route.web}/${segments.join('/')}`;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Route count for debugging
|
|
133
|
-
export const ROUTE_COUNT = {{routeCount}};
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
{{!-- SmartStack React Router Configuration Template --}}
|
|
2
|
-
{{!-- Generates router configuration from NavRoute registry --}}
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* React Router Configuration
|
|
6
|
-
*
|
|
7
|
-
* Auto-generated by SmartStack MCP - DO NOT EDIT MANUALLY
|
|
8
|
-
* Generated: {{generatedAt}}
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import React, { Suspense, lazy } from 'react';
|
|
12
|
-
import { createBrowserRouter, RouteObject, Navigate } from 'react-router-dom';
|
|
13
|
-
import { ROUTES } from './navRoutes.generated';
|
|
14
|
-
{{#if includeGuards}}
|
|
15
|
-
import { ProtectedRoute, PermissionGuard } from './guards';
|
|
16
|
-
{{/if}}
|
|
17
|
-
|
|
18
|
-
// ============================================================================
|
|
19
|
-
// Layouts
|
|
20
|
-
// ============================================================================
|
|
21
|
-
|
|
22
|
-
{{#each applications}}
|
|
23
|
-
import { {{capitalize this}}Layout } from '../layouts/{{capitalize this}}Layout';
|
|
24
|
-
{{/each}}
|
|
25
|
-
|
|
26
|
-
// ============================================================================
|
|
27
|
-
// Pages (lazy loaded for code splitting)
|
|
28
|
-
// ============================================================================
|
|
29
|
-
|
|
30
|
-
{{#each routes}}
|
|
31
|
-
const {{pageName this.navRoute}}Page = lazy(() => import('../pages/{{pagePath this.navRoute}}'));
|
|
32
|
-
{{/each}}
|
|
33
|
-
|
|
34
|
-
// ============================================================================
|
|
35
|
-
// Loading Component
|
|
36
|
-
// ============================================================================
|
|
37
|
-
|
|
38
|
-
const PageLoader: React.FC = () => (
|
|
39
|
-
<div className="flex items-center justify-center h-64">
|
|
40
|
-
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500" />
|
|
41
|
-
</div>
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
// ============================================================================
|
|
45
|
-
// Route Configuration
|
|
46
|
-
// ============================================================================
|
|
47
|
-
|
|
48
|
-
const routes: RouteObject[] = [
|
|
49
|
-
// Root redirect
|
|
50
|
-
{
|
|
51
|
-
path: '/',
|
|
52
|
-
element: <Navigate to="{{defaultPath}}" replace />,
|
|
53
|
-
},
|
|
54
|
-
|
|
55
|
-
{{#each applicationTree}}
|
|
56
|
-
// {{uppercase @key}} Application
|
|
57
|
-
{
|
|
58
|
-
path: '{{@key}}',
|
|
59
|
-
element: <{{capitalize @key}}Layout />,
|
|
60
|
-
children: [
|
|
61
|
-
{{#each this}}
|
|
62
|
-
{
|
|
63
|
-
path: '{{modulePath this.navRoute}}',
|
|
64
|
-
{{#if this.permissions.length}}
|
|
65
|
-
element: (
|
|
66
|
-
<PermissionGuard permissions={ROUTES['{{this.navRoute}}'].permissions}>
|
|
67
|
-
<Suspense fallback={<PageLoader />}>
|
|
68
|
-
<{{pageName this.navRoute}}Page />
|
|
69
|
-
</Suspense>
|
|
70
|
-
</PermissionGuard>
|
|
71
|
-
),
|
|
72
|
-
{{else}}
|
|
73
|
-
element: (
|
|
74
|
-
<Suspense fallback={<PageLoader />}>
|
|
75
|
-
<{{pageName this.navRoute}}Page />
|
|
76
|
-
</Suspense>
|
|
77
|
-
),
|
|
78
|
-
{{/if}}
|
|
79
|
-
},
|
|
80
|
-
{{/each}}
|
|
81
|
-
],
|
|
82
|
-
},
|
|
83
|
-
{{/each}}
|
|
84
|
-
|
|
85
|
-
// 404 Not Found
|
|
86
|
-
{
|
|
87
|
-
path: '*',
|
|
88
|
-
element: (
|
|
89
|
-
<div className="flex flex-col items-center justify-center h-screen">
|
|
90
|
-
<h1 className="text-4xl font-bold text-gray-900">404</h1>
|
|
91
|
-
<p className="mt-2 text-gray-600">Page not found</p>
|
|
92
|
-
<a href="{{defaultPath}}" className="mt-4 text-blue-600 hover:underline">
|
|
93
|
-
Return to home
|
|
94
|
-
</a>
|
|
95
|
-
</div>
|
|
96
|
-
),
|
|
97
|
-
},
|
|
98
|
-
];
|
|
99
|
-
|
|
100
|
-
// ============================================================================
|
|
101
|
-
// Router Export
|
|
102
|
-
// ============================================================================
|
|
103
|
-
|
|
104
|
-
{{#if includeGuards}}
|
|
105
|
-
export const router = createBrowserRouter([
|
|
106
|
-
{
|
|
107
|
-
element: <ProtectedRoute />,
|
|
108
|
-
children: routes,
|
|
109
|
-
},
|
|
110
|
-
]);
|
|
111
|
-
{{else}}
|
|
112
|
-
export const router = createBrowserRouter(routes);
|
|
113
|
-
{{/if}}
|
|
114
|
-
|
|
115
|
-
export default router;
|
|
116
|
-
|
|
117
|
-
// ============================================================================
|
|
118
|
-
// Type Exports
|
|
119
|
-
// ============================================================================
|
|
120
|
-
|
|
121
|
-
export type { RouteObject };
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Get route configuration by NavRoute path
|
|
125
|
-
*/
|
|
126
|
-
export const getRouteConfig = (navRoute: string) => ROUTES[navRoute];
|