@atlashub/smartstack-cli 3.31.0 → 3.32.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/.documentation/installation.html +7 -2
- package/README.md +7 -1
- package/dist/index.js +33 -37
- package/dist/index.js.map +1 -1
- package/dist/mcp-entry.mjs +547 -97
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -1
- package/scripts/health-check.sh +2 -1
- package/templates/mcp-scaffolding/controller.cs.hbs +10 -7
- package/templates/mcp-scaffolding/entity-extension.cs.hbs +132 -124
- package/templates/mcp-scaffolding/frontend/api-client.ts.hbs +4 -4
- package/templates/mcp-scaffolding/tests/controller.test.cs.hbs +38 -15
- package/templates/mcp-scaffolding/tests/service.test.cs.hbs +20 -8
- package/templates/skills/apex/SKILL.md +7 -9
- package/templates/skills/apex/_shared.md +9 -2
- package/templates/skills/apex/references/code-generation.md +412 -0
- package/templates/skills/apex/references/post-checks.md +377 -37
- package/templates/skills/apex/references/smartstack-api.md +229 -5
- package/templates/skills/apex/references/smartstack-frontend.md +368 -11
- package/templates/skills/apex/references/smartstack-layers.md +54 -7
- package/templates/skills/apex/steps/step-00-init.md +1 -2
- package/templates/skills/apex/steps/step-01-analyze.md +45 -2
- package/templates/skills/apex/steps/step-02-plan.md +23 -2
- package/templates/skills/apex/steps/step-03-execute.md +195 -5
- package/templates/skills/apex/steps/step-04-examine.md +18 -5
- package/templates/skills/apex/steps/step-05-deep-review.md +9 -11
- package/templates/skills/apex/steps/step-06-resolve.md +5 -9
- package/templates/skills/apex/steps/step-07-tests.md +66 -1
- package/templates/skills/apex/steps/step-08-run-tests.md +12 -3
- package/templates/skills/application/references/provider-template.md +62 -39
- package/templates/skills/application/templates-backend.md +3 -3
- package/templates/skills/application/templates-frontend.md +12 -12
- package/templates/skills/application/templates-seed.md +14 -4
- package/templates/skills/business-analyse/SKILL.md +9 -6
- package/templates/skills/business-analyse/questionnaire/04-data.md +8 -0
- package/templates/skills/business-analyse/references/agent-module-prompt.md +84 -5
- package/templates/skills/business-analyse/references/agent-pooling-best-practices.md +83 -19
- package/templates/skills/business-analyse/references/consolidation-structural-checks.md +6 -2
- package/templates/skills/business-analyse/references/team-orchestration.md +443 -110
- package/templates/skills/business-analyse/references/validation-checklist.md +5 -4
- package/templates/skills/business-analyse/schemas/sections/analysis-schema.json +44 -0
- package/templates/skills/business-analyse/steps/step-03a2-analysis.md +72 -1
- package/templates/skills/business-analyse/steps/step-03c-compile.md +93 -7
- package/templates/skills/business-analyse/steps/step-03d-validate.md +34 -2
- package/templates/skills/business-analyse/steps/step-04b-analyze.md +40 -0
- package/templates/skills/controller/references/controller-code-templates.md +2 -2
- package/templates/skills/controller/templates.md +12 -12
- package/templates/skills/feature-full/steps/step-01-implementation.md +4 -4
- package/templates/skills/ralph-loop/references/category-rules.md +44 -2
- package/templates/skills/ralph-loop/references/compact-loop.md +37 -0
- package/templates/skills/ralph-loop/references/core-seed-data.md +51 -20
- package/templates/skills/review-code/references/owasp-api-top10.md +1 -1
|
@@ -264,6 +264,43 @@ Batch: {batch.length} [{firstCategory}] → {batch.map(t => `[${t.id}] ${t.descr
|
|
|
264
264
|
done
|
|
265
265
|
fi
|
|
266
266
|
```
|
|
267
|
+
# BLOCKING: No custom entity hooks wrapping services (test-v4-014 root cause)
|
|
268
|
+
if [ -n "$HOOK_DIR" ]; then
|
|
269
|
+
CUSTOM_HOOKS=$(find "$HOOK_DIR" -name "use*.ts" -o -name "use*.tsx" 2>/dev/null | grep -iv "Preferences" | grep -v "node_modules")
|
|
270
|
+
if [ -n "$CUSTOM_HOOKS" ]; then
|
|
271
|
+
echo "BLOCKING: Custom entity hooks detected — pages MUST call services directly"
|
|
272
|
+
echo "$CUSTOM_HOOKS"
|
|
273
|
+
echo "Pattern: page useCallback → service.getAll() → setData(result.items)"
|
|
274
|
+
echo "FORBIDDEN: useEmployees(), useProjects() etc. wrapping services with useState/useEffect"
|
|
275
|
+
# FIX REQUIRED: delete custom hooks, move API calls into page components
|
|
276
|
+
fi
|
|
277
|
+
fi
|
|
278
|
+
|
|
279
|
+
# BLOCKING: No direct array types in services — MUST use PaginatedResult<T>
|
|
280
|
+
if [ -n "$WEB_SRC" ]; then
|
|
281
|
+
SVC_DIR=$(find "$WEB_SRC" -path "*/services" -not -path "*/node_modules/*" -type d 2>/dev/null | head -1)
|
|
282
|
+
if [ -n "$SVC_DIR" ]; then
|
|
283
|
+
DIRECT_ARRAYS=$(grep -rPn 'api\.get<[A-Z]\w+\[\]>' "$SVC_DIR" --include="*.ts" 2>/dev/null)
|
|
284
|
+
if [ -n "$DIRECT_ARRAYS" ]; then
|
|
285
|
+
echo "BLOCKING: Direct array response types — MUST use PaginatedResult<T>"
|
|
286
|
+
echo "$DIRECT_ARRAYS"
|
|
287
|
+
echo "Pattern: api.get<PaginatedResult<Employee>>(url) then .items in page"
|
|
288
|
+
# FIX REQUIRED before commit
|
|
289
|
+
fi
|
|
290
|
+
fi
|
|
291
|
+
fi
|
|
292
|
+
|
|
293
|
+
# BLOCKING: lazy() imports MUST have Suspense boundary in App.tsx
|
|
294
|
+
if [ -n "$APP_TSX" ]; then
|
|
295
|
+
HAS_LAZY=$(grep -c 'lazy(' "$APP_TSX" 2>/dev/null)
|
|
296
|
+
HAS_SUSPENSE=$(grep -c 'Suspense' "$APP_TSX" 2>/dev/null)
|
|
297
|
+
if [ "$HAS_LAZY" -gt 0 ] && [ "$HAS_SUSPENSE" -eq 0 ]; then
|
|
298
|
+
echo "BLOCKING: lazy() imports without <Suspense> boundary in App.tsx"
|
|
299
|
+
echo "Every lazy() page MUST be wrapped: <Suspense fallback={<PageLoader />}>"
|
|
300
|
+
# FIX REQUIRED before commit
|
|
301
|
+
fi
|
|
302
|
+
fi
|
|
303
|
+
|
|
267
304
|
If ANY POST-CHECK fails → fix the code → re-run ALL checks → loop until ALL pass.
|
|
268
305
|
**NEVER commit with failing POST-CHECKs.**
|
|
269
306
|
|
|
@@ -1104,47 +1104,78 @@ public class {AppPascalName}SeedDataProvider : IClientSeedDataProvider
|
|
|
1104
1104
|
.FirstAsync(m => m.Code == "{module1Code}" && m.ApplicationId == app.Id, ct);
|
|
1105
1105
|
// Repeat for each module...
|
|
1106
1106
|
|
|
1107
|
-
// --- Module translations ---
|
|
1108
|
-
|
|
1107
|
+
// --- Module translations (idempotent — unique index IX_nav_Translations_EntityType_EntityId_LanguageCode) ---
|
|
1108
|
+
// CRITICAL: Always check existence before inserting translations to avoid duplicate key errors
|
|
1109
|
+
// on re-runs, partial failures, or DB reset scenarios.
|
|
1110
|
+
if (!await context.NavigationTranslations.AnyAsync(
|
|
1111
|
+
t => t.EntityId == {Module1Pascal}NavigationSeedData.{Module1Pascal}ModuleId
|
|
1112
|
+
&& t.EntityType == NavigationEntityType.Module, ct))
|
|
1109
1113
|
{
|
|
1110
|
-
|
|
1111
|
-
|
|
1114
|
+
foreach (var t in {Module1Pascal}NavigationSeedData.GetTranslationEntries())
|
|
1115
|
+
{
|
|
1116
|
+
context.NavigationTranslations.Add(
|
|
1117
|
+
NavigationTranslation.Create(t.EntityType, t.EntityId, t.LanguageCode, t.Label, t.Description));
|
|
1118
|
+
}
|
|
1112
1119
|
}
|
|
1113
1120
|
// Repeat for each module...
|
|
1114
1121
|
await ((DbContext)context).SaveChangesAsync(ct);
|
|
1115
1122
|
|
|
1116
|
-
// --- Sections (
|
|
1123
|
+
// --- Sections (idempotent — check each section before inserting) ---
|
|
1117
1124
|
// Module 1 sections
|
|
1118
1125
|
foreach (var secEntry in {Module1Pascal}NavigationSeedData.GetSectionEntries(mod1Entity.Id))
|
|
1119
1126
|
{
|
|
1120
|
-
var
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1127
|
+
var secExists = await context.NavigationSections
|
|
1128
|
+
.AnyAsync(s => s.Code == secEntry.Code && s.ModuleId == mod1Entity.Id, ct);
|
|
1129
|
+
if (!secExists)
|
|
1130
|
+
{
|
|
1131
|
+
var sec = NavigationSection.Create(
|
|
1132
|
+
secEntry.ModuleId, secEntry.Code, secEntry.Label,
|
|
1133
|
+
secEntry.Description, secEntry.Icon, secEntry.IconType,
|
|
1134
|
+
secEntry.Route, secEntry.DisplayOrder);
|
|
1135
|
+
context.NavigationSections.Add(sec);
|
|
1136
|
+
}
|
|
1125
1137
|
}
|
|
1126
1138
|
// Repeat for each module that has sections...
|
|
1127
1139
|
await ((DbContext)context).SaveChangesAsync(ct);
|
|
1128
1140
|
|
|
1129
|
-
// --- Section translations ---
|
|
1130
|
-
foreach (var
|
|
1141
|
+
// --- Section translations (idempotent — check before inserting) ---
|
|
1142
|
+
foreach (var secEntry in {Module1Pascal}NavigationSeedData.GetSectionEntries(mod1Entity.Id))
|
|
1131
1143
|
{
|
|
1132
|
-
context.NavigationTranslations.
|
|
1133
|
-
|
|
1144
|
+
if (!await context.NavigationTranslations.AnyAsync(
|
|
1145
|
+
t => t.EntityId == secEntry.Id && t.EntityType == NavigationEntityType.Section, ct))
|
|
1146
|
+
{
|
|
1147
|
+
foreach (var t in {Module1Pascal}NavigationSeedData.GetSectionTranslationEntries()
|
|
1148
|
+
.Where(st => st.EntityId == secEntry.Id))
|
|
1149
|
+
{
|
|
1150
|
+
context.NavigationTranslations.Add(
|
|
1151
|
+
NavigationTranslation.Create(t.EntityType, t.EntityId, t.LanguageCode, t.Label, t.Description));
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1134
1154
|
}
|
|
1135
1155
|
// Repeat for each module that has sections...
|
|
1136
1156
|
await ((DbContext)context).SaveChangesAsync(ct);
|
|
1137
1157
|
|
|
1138
|
-
// --- Resources (
|
|
1139
|
-
//
|
|
1158
|
+
// --- Resources (idempotent — use ACTUAL section IDs from DB, not deterministic seed IDs) ---
|
|
1159
|
+
// CRITICAL: NavigationSection.Create() generates its own ID in DB.
|
|
1160
|
+
// The deterministic GUID from GetSectionEntries() is NOT the actual SectionId.
|
|
1161
|
+
// We MUST query the real section by Code+ModuleId to get the actual DB ID,
|
|
1162
|
+
// otherwise FK_nav_Resources_nav_Sections_SectionId will fail.
|
|
1140
1163
|
foreach (var secEntry in {Module1Pascal}NavigationSeedData.GetSectionEntries(mod1Entity.Id))
|
|
1141
1164
|
{
|
|
1165
|
+
var actualSection = await context.NavigationSections
|
|
1166
|
+
.FirstAsync(s => s.Code == secEntry.Code && s.ModuleId == mod1Entity.Id, ct);
|
|
1167
|
+
|
|
1142
1168
|
foreach (var resEntry in {Module1Pascal}NavigationSeedData.GetResourceEntries(secEntry.Id))
|
|
1143
1169
|
{
|
|
1144
|
-
var
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1170
|
+
var resExists = await context.NavigationResources
|
|
1171
|
+
.AnyAsync(r => r.Code == resEntry.Code && r.SectionId == actualSection.Id, ct);
|
|
1172
|
+
if (!resExists)
|
|
1173
|
+
{
|
|
1174
|
+
var res = NavigationResource.Create(
|
|
1175
|
+
actualSection.Id, resEntry.Code, resEntry.Label,
|
|
1176
|
+
resEntry.EntityType, resEntry.Route, resEntry.DisplayOrder);
|
|
1177
|
+
context.NavigationResources.Add(res);
|
|
1178
|
+
}
|
|
1148
1179
|
}
|
|
1149
1180
|
}
|
|
1150
1181
|
// Repeat for each module that has resources...
|
|
@@ -101,7 +101,7 @@ public async Task<ActionResult<List<OrderDto>>> GetAll([FromQuery] int pageSize
|
|
|
101
101
|
|
|
102
102
|
// GOOD: Enforced max page size
|
|
103
103
|
[HttpGet]
|
|
104
|
-
public async Task<ActionResult<
|
|
104
|
+
public async Task<ActionResult<PaginatedResult<OrderDto>>> GetAll(
|
|
105
105
|
[FromQuery] int page = 1,
|
|
106
106
|
[FromQuery] int pageSize = 20)
|
|
107
107
|
{
|