@atlashub/smartstack-cli 4.29.0 → 4.31.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/business-analyse.html +217 -0
- package/dist/index.js +17 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/skills/apex/references/code-generation.md +1 -1
- package/templates/skills/apex/references/person-extension-pattern.md +23 -2
- package/templates/skills/apex/references/post-checks.md +52 -0
- package/templates/skills/apex/references/smartstack-api.md +111 -0
- package/templates/skills/apex/references/smartstack-frontend.md +25 -2
- package/templates/skills/apex/references/smartstack-layers.md +1 -0
- package/templates/skills/apex/steps/step-03-execute.md +110 -7
- package/templates/skills/application/templates-frontend.md +1 -1
- package/templates/skills/ba-generate-html/SKILL.md +1 -1
- package/templates/skills/ba-generate-html/html/ba-interactive.html +234 -262
- package/templates/skills/ba-generate-html/html/src/partials/cadrage-context.html +9 -9
- package/templates/skills/ba-generate-html/html/src/partials/cadrage-scope.html +17 -39
- package/templates/skills/ba-generate-html/html/src/partials/cadrage-stakeholders.html +7 -7
- package/templates/skills/ba-generate-html/html/src/partials/cadrage-success.html +13 -13
- package/templates/skills/ba-generate-html/html/src/partials/consol-datamodel.html +4 -4
- package/templates/skills/ba-generate-html/html/src/partials/consol-flows.html +5 -5
- package/templates/skills/ba-generate-html/html/src/partials/consol-interactions.html +2 -2
- package/templates/skills/ba-generate-html/html/src/partials/consol-permissions.html +4 -4
- package/templates/skills/ba-generate-html/html/src/partials/decomp-dependencies.html +11 -11
- package/templates/skills/ba-generate-html/html/src/partials/decomp-modules.html +8 -16
- package/templates/skills/ba-generate-html/html/src/partials/handoff-summary.html +5 -5
- package/templates/skills/ba-generate-html/html/src/scripts/01-data-init.js +29 -21
- package/templates/skills/ba-generate-html/html/src/scripts/02-navigation.js +10 -10
- package/templates/skills/ba-generate-html/html/src/scripts/03-render-cadrage.js +5 -4
- package/templates/skills/ba-generate-html/html/src/scripts/04-render-modules.js +4 -6
- package/templates/skills/ba-generate-html/html/src/scripts/05-render-specs.js +57 -57
- package/templates/skills/ba-generate-html/html/src/scripts/06-render-consolidation.js +4 -4
- package/templates/skills/ba-generate-html/html/src/scripts/06-render-mockups.js +5 -5
- package/templates/skills/ba-generate-html/html/src/scripts/07-render-handoff.js +9 -12
- package/templates/skills/ba-generate-html/html/src/scripts/08-editing.js +3 -3
- package/templates/skills/ba-generate-html/html/src/scripts/09-export.js +2 -2
- package/templates/skills/ba-generate-html/html/src/scripts/10-comments.js +3 -3
- package/templates/skills/ba-generate-html/html/src/scripts/11-review-panel.js +8 -8
- package/templates/skills/ba-generate-html/html/src/styles/03-navigation.css +1 -1
- package/templates/skills/ba-generate-html/html/src/styles/04-cards.css +2 -4
- package/templates/skills/ba-generate-html/html/src/template.html +93 -123
- package/templates/skills/ba-generate-html/references/data-build.md +4 -9
- package/templates/skills/ba-generate-html/references/data-mapping.md +2 -7
- package/templates/skills/ba-generate-html/references/output-modes.md +1 -1
- package/templates/skills/ba-generate-html/steps/step-02-build-data.md +8 -7
- package/templates/skills/ba-generate-html/steps/step-04-verify.md +2 -2
- package/templates/skills/ba-review/references/review-data-mapping.md +4 -6
- package/templates/skills/ba-review/steps/step-01-apply.md +2 -4
- package/templates/skills/business-analyse/patterns/suggestion-catalog.md +4 -4
- package/templates/skills/business-analyse/questionnaire.md +1 -1
- package/templates/skills/business-analyse/react/schema.md +2 -7
- package/templates/skills/business-analyse/schemas/application-schema.json +2 -9
- package/templates/skills/business-analyse/schemas/project-schema.json +4 -8
- package/templates/skills/business-analyse/schemas/sections/discovery-schema.json +1 -3
- package/templates/skills/business-analyse/steps/step-01-cadrage.md +5 -12
- package/templates/skills/business-analyse/steps/step-02-structure.md +3 -5
- package/templates/skills/dev-start/SKILL.md +242 -0
- package/templates/skills/ui-components/SKILL.md +1 -1
- package/templates/skills/ui-components/patterns/data-table.md +1 -1
|
@@ -50,7 +50,11 @@ context: {
|
|
|
50
50
|
stakeholders: master.cadrage.stakeholders.map(s => ({
|
|
51
51
|
role: s.role,
|
|
52
52
|
function: s.function || "",
|
|
53
|
-
tasks: s.tasks
|
|
53
|
+
tasks: Array.isArray(s.tasks)
|
|
54
|
+
? s.tasks
|
|
55
|
+
: typeof s.tasks === 'string'
|
|
56
|
+
? s.tasks.split(',').map(t => t.trim()).filter(Boolean)
|
|
57
|
+
: [],
|
|
54
58
|
frequency: mapFrequency(s.frequency), // "Quotidien"→"daily", "Hebdomadaire"→"weekly", "Mensuel"→"monthly"
|
|
55
59
|
access: mapAccess(s.involvement), // "decision-maker"→"admin", "end-user"→"contributor", "observer"→"viewer"
|
|
56
60
|
frustrations: (s.painPoints || []).join("\n")
|
|
@@ -60,10 +64,8 @@ stakeholders: master.cadrage.stakeholders.map(s => ({
|
|
|
60
64
|
**cadrage.scope** (KEY CONVERSION — CRITICAL):
|
|
61
65
|
```javascript
|
|
62
66
|
scope: {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
optional: (master.cadrage.globalScope?.couldHave || []).map(item => ({ name: item, description: "" })),
|
|
66
|
-
excluded: (master.cadrage.globalScope?.outOfScope || []).map(item => ({ name: item, description: "" }))
|
|
67
|
+
inscope: (master.cadrage.globalScope?.inScope || []).map(item => ({ name: item, description: "" })),
|
|
68
|
+
outofscope: (master.cadrage.globalScope?.outOfScope || []).map(item => ({ name: item, description: "" }))
|
|
67
69
|
}
|
|
68
70
|
```
|
|
69
71
|
|
|
@@ -77,7 +79,6 @@ modules: master.modules.map(m => ({
|
|
|
77
79
|
name: m.name || m.code, // display name (MANDATORY)
|
|
78
80
|
description: m.description || "",
|
|
79
81
|
featureType: m.featureType || "data-centric",
|
|
80
|
-
priority: m.priority || "must",
|
|
81
82
|
entities: m.entities || [],
|
|
82
83
|
anticipatedSections: m.anticipatedSections || [],
|
|
83
84
|
dependencies: m.dependencies || [],
|
|
@@ -235,7 +236,7 @@ dependencyGraph: {
|
|
|
235
236
|
## VALIDATION
|
|
236
237
|
|
|
237
238
|
- FEATURE_DATA.moduleSpecs must have ONE entry per module
|
|
238
|
-
- cadrage.scope uses HTML keys (
|
|
239
|
+
- cadrage.scope uses HTML keys (inscope/outofscope)
|
|
239
240
|
- Wireframe fields use format/content (NOT mockupFormat/mockup)
|
|
240
241
|
|
|
241
242
|
## NEXT STEP
|
|
@@ -30,9 +30,9 @@ if ! grep -q '"moduleSpecs"' ba-interactive.html; then
|
|
|
30
30
|
fi
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
**Check 3 — cadrage.scope uses HTML keys (
|
|
33
|
+
**Check 3 — cadrage.scope uses HTML keys (inscope/outofscope, NOT inScope):**
|
|
34
34
|
```
|
|
35
|
-
if grep -q '"
|
|
35
|
+
if grep -q '"inScope"' ba-interactive.html; then
|
|
36
36
|
BLOCKING_ERROR("cadrage.scope still has JSON keys — conversion failed")
|
|
37
37
|
fi
|
|
38
38
|
```
|
|
@@ -17,15 +17,13 @@ The ba-review.json is exported from the interactive HTML with the same structure
|
|
|
17
17
|
### Scope (HTML → index.json)
|
|
18
18
|
|
|
19
19
|
```javascript
|
|
20
|
-
// HTML format: {
|
|
21
|
-
// index.json format: {
|
|
20
|
+
// HTML format: { inscope: [{name, description}], outofscope: [{name, description}] }
|
|
21
|
+
// index.json format: { inScope: [string], outOfScope: [string] }
|
|
22
22
|
|
|
23
23
|
reverseScope(reviewCadrage) {
|
|
24
24
|
return {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
couldHave: (reviewCadrage.scope?.optional || []).map(s => s.name),
|
|
28
|
-
outOfScope: (reviewCadrage.scope?.excluded || []).map(s => s.name)
|
|
25
|
+
inScope: (reviewCadrage.scope?.inscope || []).map(s => s.name),
|
|
26
|
+
outOfScope: (reviewCadrage.scope?.outofscope || []).map(s => s.name)
|
|
29
27
|
};
|
|
30
28
|
}
|
|
31
29
|
```
|
|
@@ -91,10 +91,8 @@ Read the new version's cadrage.json and apply reverse mapping from review data:
|
|
|
91
91
|
|
|
92
92
|
```
|
|
93
93
|
1. Scope mapping (review → cadrage.json):
|
|
94
|
-
- cadrage.scope.
|
|
95
|
-
- cadrage.scope.
|
|
96
|
-
- cadrage.scope.optional[] → cadrage.globalScope.couldHave[]
|
|
97
|
-
- cadrage.scope.excluded[] → cadrage.globalScope.outOfScope[]
|
|
94
|
+
- cadrage.scope.inscope[] → cadrage.globalScope.inScope[]
|
|
95
|
+
- cadrage.scope.outofscope[] → cadrage.globalScope.outOfScope[]
|
|
98
96
|
(Extract name from {name, description} objects)
|
|
99
97
|
|
|
100
98
|
2. Stakeholders mapping:
|
|
@@ -75,7 +75,7 @@ Suggests system integrations based on requirements:
|
|
|
75
75
|
|
|
76
76
|
### 4.1 Trigger Point
|
|
77
77
|
|
|
78
|
-
After step-01-cadrage completes scope definition (scope.
|
|
78
|
+
After step-01-cadrage completes scope definition (scope.inScope populated):
|
|
79
79
|
|
|
80
80
|
```
|
|
81
81
|
Step-01: Analyse → Define Scope → Load Suggestion Catalog → Present Suggestions
|
|
@@ -86,10 +86,10 @@ Step-01: Analyse → Define Scope → Load Suggestion Catalog → Present Sugges
|
|
|
86
86
|
```
|
|
87
87
|
Input: index.json (module name, keywords, must-haves, should-haves)
|
|
88
88
|
|
|
89
|
-
1. Read scope.
|
|
89
|
+
1. Read scope.inScope (user-defined features)
|
|
90
90
|
2. Extract primary keywords (module type) from scope.description
|
|
91
91
|
3. Match against Module Patterns table → Suggested Modules
|
|
92
|
-
4. Analyze scope.
|
|
92
|
+
4. Analyze scope.inScope for conditions → Suggested Sections
|
|
93
93
|
5. Detect mentions of integrations → Suggested Integrations
|
|
94
94
|
6. Filter out already-present modules/sections
|
|
95
95
|
7. Rank by relevance (keyword match > condition confidence)
|
|
@@ -207,7 +207,7 @@ Select all that apply: [ ] [ ] [ ] [ ]
|
|
|
207
207
|
|
|
208
208
|
When user selects suggestions:
|
|
209
209
|
|
|
210
|
-
1. **Add Selected Modules:** Update scope.
|
|
210
|
+
1. **Add Selected Modules:** Update scope.inScope with new modules
|
|
211
211
|
2. **Add Selected Sections:** Update specification.sections (in step-02)
|
|
212
212
|
3. **Add Selected Integrations:** Update specification.integrations
|
|
213
213
|
4. **Store Decisions:** Log in index.json.suggestions[]
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
| File | Questions | Phase | Focus |
|
|
36
36
|
|------|-----------|-------|-------|
|
|
37
37
|
| `questionnaire/01-context.md` | 3 | step-01-cadrage | Business process, friction points, vision |
|
|
38
|
-
| `questionnaire/02-stakeholders-scope.md` | 10 | step-01-cadrage | User profiles, access levels, scope boundaries
|
|
38
|
+
| `questionnaire/02-stakeholders-scope.md` | 10 | step-01-cadrage | User profiles, access levels, scope boundaries |
|
|
39
39
|
| `questionnaire/03-data-ui.md` | ~15 | step-03-specify (per module) | Data entities, UI expectations, dashboards |
|
|
40
40
|
| `questionnaire/04-risks-metrics.md` | — | NOT USED | Risks/metrics out of BA scope |
|
|
41
41
|
| `questionnaire/05-cross-module.md` | ~8 | step-03-specify (conditional) | Cross-module dependencies, integration impact |
|
|
@@ -69,9 +69,7 @@ export interface ApplicationCadrage {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
export interface GlobalScope {
|
|
72
|
-
|
|
73
|
-
shouldHave: string[];
|
|
74
|
-
couldHave: string[];
|
|
72
|
+
inScope: string[];
|
|
75
73
|
outOfScope: string[];
|
|
76
74
|
}
|
|
77
75
|
|
|
@@ -88,7 +86,6 @@ export interface ApplicationModule {
|
|
|
88
86
|
description: string;
|
|
89
87
|
featureType: string;
|
|
90
88
|
entities: string[];
|
|
91
|
-
priority: 'must' | 'should' | 'could';
|
|
92
89
|
estimatedComplexity: 'simple' | 'medium' | 'complex';
|
|
93
90
|
status: 'pending' | 'in-progress' | 'specified' | 'validated';
|
|
94
91
|
featureJsonPath?: string;
|
|
@@ -246,9 +243,7 @@ export interface Stakeholder {
|
|
|
246
243
|
}
|
|
247
244
|
|
|
248
245
|
export interface FeatureScope {
|
|
249
|
-
|
|
250
|
-
shouldHave: string[];
|
|
251
|
-
couldHave: string[];
|
|
246
|
+
inScope: string[];
|
|
252
247
|
outOfScope: string[];
|
|
253
248
|
mainFlow: string[];
|
|
254
249
|
alternativeFlows: AlternativeFlow[];
|
|
@@ -133,9 +133,7 @@
|
|
|
133
133
|
"globalScope": {
|
|
134
134
|
"type": "object",
|
|
135
135
|
"properties": {
|
|
136
|
-
"
|
|
137
|
-
"shouldHave": { "type": "array", "items": { "type": "string" } },
|
|
138
|
-
"couldHave": { "type": "array", "items": { "type": "string" } },
|
|
136
|
+
"inScope": { "type": "array", "items": { "type": "string" } },
|
|
139
137
|
"outOfScope": { "type": "array", "items": { "type": "string" } }
|
|
140
138
|
}
|
|
141
139
|
},
|
|
@@ -193,7 +191,7 @@
|
|
|
193
191
|
"required": ["item", "category", "module"],
|
|
194
192
|
"properties": {
|
|
195
193
|
"item": { "type": "string", "description": "Requirement from the original brief" },
|
|
196
|
-
"category": { "type": "string", "enum": ["
|
|
194
|
+
"category": { "type": "string", "enum": ["inScope", "outOfScope", "implicit"] },
|
|
197
195
|
"module": { "type": ["string", "null"], "description": "Module code that covers this requirement (null if cross-cutting)" },
|
|
198
196
|
"ucRef": { "type": ["string", "null"], "description": "UC ID if already assigned" },
|
|
199
197
|
"notes": { "type": "string" },
|
|
@@ -246,11 +244,6 @@
|
|
|
246
244
|
"type": ["string", "null"],
|
|
247
245
|
"description": "Path to the module-level index.json once created"
|
|
248
246
|
},
|
|
249
|
-
"priority": {
|
|
250
|
-
"type": "string",
|
|
251
|
-
"enum": ["must", "should", "could"],
|
|
252
|
-
"description": "Priority level from global scope"
|
|
253
|
-
},
|
|
254
247
|
"sortOrder": {
|
|
255
248
|
"type": "integer",
|
|
256
249
|
"description": "Position in topological order (0-based)"
|
|
@@ -118,11 +118,9 @@
|
|
|
118
118
|
},
|
|
119
119
|
"globalScope": {
|
|
120
120
|
"type": "object",
|
|
121
|
-
"description": "Project-wide
|
|
121
|
+
"description": "Project-wide binary scope",
|
|
122
122
|
"properties": {
|
|
123
|
-
"
|
|
124
|
-
"shouldHave": { "type": "array", "items": { "type": "string" } },
|
|
125
|
-
"couldHave": { "type": "array", "items": { "type": "string" } },
|
|
123
|
+
"inScope": { "type": "array", "items": { "type": "string" } },
|
|
126
124
|
"outOfScope": { "type": "array", "items": { "type": "string" } }
|
|
127
125
|
}
|
|
128
126
|
},
|
|
@@ -189,7 +187,7 @@
|
|
|
189
187
|
"required": ["item", "category"],
|
|
190
188
|
"properties": {
|
|
191
189
|
"item": { "type": "string", "description": "Requirement from the original brief" },
|
|
192
|
-
"category": { "type": "string", "enum": ["
|
|
190
|
+
"category": { "type": "string", "enum": ["inScope", "outOfScope", "implicit"] },
|
|
193
191
|
"application": { "type": ["string", "null"], "description": "Application code (null if cross-cutting)" },
|
|
194
192
|
"module": { "type": ["string", "null"], "description": "Module code (null if application-level)" },
|
|
195
193
|
"notes": { "type": "string" },
|
|
@@ -251,9 +249,7 @@
|
|
|
251
249
|
"type": "object",
|
|
252
250
|
"description": "Application-specific scope (subset of global scope)",
|
|
253
251
|
"properties": {
|
|
254
|
-
"
|
|
255
|
-
"shouldHave": { "type": "array", "items": { "type": "string" } },
|
|
256
|
-
"couldHave": { "type": "array", "items": { "type": "string" } }
|
|
252
|
+
"inScope": { "type": "array", "items": { "type": "string" } }
|
|
257
253
|
}
|
|
258
254
|
},
|
|
259
255
|
"modules": {
|
|
@@ -27,9 +27,7 @@
|
|
|
27
27
|
"scope": {
|
|
28
28
|
"type": "object",
|
|
29
29
|
"properties": {
|
|
30
|
-
"
|
|
31
|
-
"shouldHave": { "type": "array", "items": { "type": "string" } },
|
|
32
|
-
"couldHave": { "type": "array", "items": { "type": "string" } },
|
|
30
|
+
"inScope": { "type": "array", "items": { "type": "string" } },
|
|
33
31
|
"outOfScope": { "type": "array", "items": { "type": "string" } },
|
|
34
32
|
"mainFlow": { "type": "string" },
|
|
35
33
|
"decisionPoints": { "type": "array", "items": { "type": "string" } },
|
|
@@ -232,10 +232,9 @@ Ask in 1-2 batches. After each batch:
|
|
|
232
232
|
|
|
233
233
|
#### 4c. Functional Scope (ALWAYS — from `questionnaire/02-stakeholders-scope.md`)
|
|
234
234
|
|
|
235
|
-
**Mandatory minimum:** Q2.13 (
|
|
235
|
+
**Mandatory minimum:** Q2.13 (in-scope), Q2.15 (exclusions), Q2.16 (main journey).
|
|
236
236
|
|
|
237
237
|
Ask in 1-2 batches. After each batch:
|
|
238
|
-
- If everything is "must-have" → **BLOCKING**: apply MoSCoW prioritization question
|
|
239
238
|
- If no exclusions → probe: "What should this system explicitly NOT do?"
|
|
240
239
|
- If journey is too simple → detail: "What happens when something goes wrong? When someone cancels?"
|
|
241
240
|
|
|
@@ -252,10 +251,6 @@ Ask in 1-2 batches. After each batch:
|
|
|
252
251
|
> ```
|
|
253
252
|
>
|
|
254
253
|
> These error flows become **Business Rules** in step-03 (BR-WF category: workflow/guard conditions).
|
|
255
|
-
>
|
|
256
|
-
> **BLOCKING RULE — MoSCoW DISTRIBUTION:**
|
|
257
|
-
> If the client classifies ALL features as "must-have", you MUST challenge this BEFORE proceeding.
|
|
258
|
-
> If client moves items → update categories. If client confirms ALL mustHave → accept but log in changelog.
|
|
259
254
|
|
|
260
255
|
#### 4d. Challenge Implicit Assumptions (CRITICAL)
|
|
261
256
|
|
|
@@ -435,7 +430,7 @@ options:
|
|
|
435
430
|
| Choice | Action |
|
|
436
431
|
|--------|--------|
|
|
437
432
|
| **Géré dans l'app** | Entity becomes a sub-entity of the parent module. Add as a **tab in the detail page** of the referencing module. Note in coverageMatrix with `parentModule` field. |
|
|
438
|
-
| **Nouveau module dédié** | **Add new module to `{pre_analysis}.detected_modules[]`** with the proposed architecture (sections, detail tabs, dependencies). Add to `coverageMatrix` as
|
|
433
|
+
| **Nouveau module dédié** | **Add new module to `{pre_analysis}.detected_modules[]`** with the proposed architecture (sections, detail tabs, dependencies). Add to `coverageMatrix` as inScope. The `list` section is ALWAYS the main section. Additional sections ONLY for distinct functional zones (dashboard, approve, import...). `create` and `edit` are ACTIONS within `list` and detail pages, NEVER standalone sections. |
|
|
439
434
|
| **Système externe** | Flag for integration specification. Add to `coverageMatrix` as integration. Load questionnaire materials on integrations if not already covered. |
|
|
440
435
|
| **Liste simple en config** | Entity becomes a lookup/reference table (no dedicated module, no section). Note in coverageMatrix as config data. |
|
|
441
436
|
|
|
@@ -586,7 +581,7 @@ BEFORE transitioning to step-02:
|
|
|
586
581
|
2. Re-read ALL answers collected during phases 2-4
|
|
587
582
|
3. For EACH distinct feature/requirement identified:
|
|
588
583
|
- Create an entry in `cadrage.coverageMatrix[]`
|
|
589
|
-
- Assign it to
|
|
584
|
+
- Assign it to inScope or outOfScope
|
|
590
585
|
- Assign the module that will cover it (or null if cross-cutting)
|
|
591
586
|
- List anticipated sections (Level 4) — ONLY functional zones
|
|
592
587
|
- List anticipated resources (Level 5) for each section
|
|
@@ -644,9 +639,7 @@ ba-writer.enrichSection({
|
|
|
644
639
|
toBe: {from Phase 3, section 4a — Q1.8 answer},
|
|
645
640
|
stakeholders: [{from Phase 3, section 4b}],
|
|
646
641
|
globalScope: {
|
|
647
|
-
|
|
648
|
-
shouldHave: [{from coverage matrix}],
|
|
649
|
-
couldHave: [{from coverage matrix}],
|
|
642
|
+
inScope: [{from Phase 3, section 4c + Phase 4 accepted suggestions + coverage matrix}],
|
|
650
643
|
outOfScope: [{from Phase 3, section 4c — Q2.15 exclusions}]
|
|
651
644
|
},
|
|
652
645
|
applicationRoles: [{from Phase 5, section 6}],
|
|
@@ -667,7 +660,7 @@ ba-writer.updateStatus({feature_id}, "framed")
|
|
|
667
660
|
| Aspect | Count |
|
|
668
661
|
|--------|-------|
|
|
669
662
|
| Stakeholders | {count} |
|
|
670
|
-
|
|
|
663
|
+
| In-scope items | {count} |
|
|
671
664
|
| Application roles | {count} |
|
|
672
665
|
| Suggestions accepted | {count} |
|
|
673
666
|
| Anticipated sections | {total across all modules} |
|
|
@@ -60,7 +60,6 @@ For each module:
|
|
|
60
60
|
- ApplicationCode (parent app)
|
|
61
61
|
- Description
|
|
62
62
|
- FeatureType (data-centric | workflow | reporting | integration | full-module)
|
|
63
|
-
- Priority (must | should | could)
|
|
64
63
|
- Entities (preliminary list)
|
|
65
64
|
```
|
|
66
65
|
|
|
@@ -137,14 +136,14 @@ Display the complete hierarchy for validation:
|
|
|
137
136
|
|
|
138
137
|
```
|
|
139
138
|
[App: HumanResources]
|
|
140
|
-
├── [Module: Employees]
|
|
139
|
+
├── [Module: Employees]
|
|
141
140
|
│ ├── list → SmartTable (employees-grid)
|
|
142
141
|
│ └── detail → SmartForm (employee-form)
|
|
143
|
-
├── [Module: Absences]
|
|
142
|
+
├── [Module: Absences]
|
|
144
143
|
│ ├── list → SmartTable (absences-grid)
|
|
145
144
|
│ ├── detail → SmartForm (absence-form)
|
|
146
145
|
│ └── calendar → SmartKanban (absences-calendar)
|
|
147
|
-
└── [Module: Reports]
|
|
146
|
+
└── [Module: Reports]
|
|
148
147
|
└── dashboard → SmartDashboard (hr-dashboard)
|
|
149
148
|
```
|
|
150
149
|
|
|
@@ -170,7 +169,6 @@ Write via ba-writer:
|
|
|
170
169
|
"applicationCode": "HumanResources",
|
|
171
170
|
"name": "Employés",
|
|
172
171
|
"featureType": "data-centric",
|
|
173
|
-
"priority": "must",
|
|
174
172
|
"entities": ["Employee", "Contract"],
|
|
175
173
|
"anticipatedSections": [
|
|
176
174
|
{ "code": "list", "label": "Liste", "sectionType": "primary", "permissionMode": "crud", "resources": [{ "code": "employees-grid", "type": "SmartTable" }] },
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dev-start
|
|
3
|
+
description: Launch SmartStack dev environment (backend + frontend + admin credentials)
|
|
4
|
+
argument-hint: "[--stop] [--reset] [--backend-only] [--frontend-only]"
|
|
5
|
+
model: haiku
|
|
6
|
+
allowed-tools: Read, Bash, Glob, Write
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Arguments
|
|
10
|
+
$ARGUMENTS
|
|
11
|
+
|
|
12
|
+
## Current state (auto-injected)
|
|
13
|
+
|
|
14
|
+
- Dev session: !`cat .claude/dev-session.json 2>/dev/null || echo "no session"`
|
|
15
|
+
- Backend port check: !`netstat.exe -ano 2>/dev/null | grep -E ":(5142|5290) .*LISTENING" | head -5 || echo "no backend listening"`
|
|
16
|
+
- Frontend port check: !`netstat.exe -ano 2>/dev/null | grep -E ":(6173|3000) .*LISTENING" | head -5 || echo "no frontend listening"`
|
|
17
|
+
- API directory: !`ls -d src/*.Api 2>/dev/null || echo "no API found"`
|
|
18
|
+
- Web directory: !`ls -d web/*-web 2>/dev/null || echo "no web found"`
|
|
19
|
+
- Local config: !`cat src/*.Api/appsettings.Local.json 2>/dev/null || echo "no local config"`
|
|
20
|
+
- Frontend env standalone: !`cat web/*-web/.env.standalone 2>/dev/null || echo "no .env.standalone"`
|
|
21
|
+
- Frontend env development: !`cat web/*-web/.env.development 2>/dev/null || echo "no .env.development"`
|
|
22
|
+
|
|
23
|
+
<objective>
|
|
24
|
+
Launch the SmartStack development environment in one command: detect configuration, validate config coherence, start backend + frontend, and provide admin credentials.
|
|
25
|
+
Idempotent: safe to run multiple times. Detects already-running processes.
|
|
26
|
+
</objective>
|
|
27
|
+
|
|
28
|
+
<quick_start>
|
|
29
|
+
```bash
|
|
30
|
+
/dev-start # Launch everything (idempotent)
|
|
31
|
+
/dev-start --stop # Stop backend + frontend
|
|
32
|
+
/dev-start --reset # Force admin password reset
|
|
33
|
+
/dev-start --backend-only # Launch only the backend
|
|
34
|
+
/dev-start --frontend-only # Launch only the frontend
|
|
35
|
+
```
|
|
36
|
+
</quick_start>
|
|
37
|
+
|
|
38
|
+
<subcommands>
|
|
39
|
+
|
|
40
|
+
| Command | Description |
|
|
41
|
+
|---------|-------------|
|
|
42
|
+
| `/dev-start` | Launch all (idempotent) + validate configs |
|
|
43
|
+
| `/dev-start --stop` | Stop backend + frontend via taskkill.exe |
|
|
44
|
+
| `/dev-start --reset` | Force reset admin password |
|
|
45
|
+
| `/dev-start --backend-only` | Launch only backend |
|
|
46
|
+
| `/dev-start --frontend-only` | Launch only frontend |
|
|
47
|
+
|
|
48
|
+
</subcommands>
|
|
49
|
+
|
|
50
|
+
<workflow>
|
|
51
|
+
|
|
52
|
+
## Handle --stop
|
|
53
|
+
|
|
54
|
+
If `--stop` argument detected, execute the stop workflow and exit:
|
|
55
|
+
|
|
56
|
+
1. Find backend PID:
|
|
57
|
+
```bash
|
|
58
|
+
netstat.exe -ano | grep ":${API_PORT} .*LISTENING" | awk '{print $5}' | head -1
|
|
59
|
+
```
|
|
60
|
+
2. If found: `taskkill.exe /PID $PID /F`
|
|
61
|
+
3. Find frontend PID:
|
|
62
|
+
```bash
|
|
63
|
+
netstat.exe -ano | grep ":${WEB_PORT} .*LISTENING" | awk '{print $5}' | head -1
|
|
64
|
+
```
|
|
65
|
+
4. If found: `taskkill.exe /PID $PID /F`
|
|
66
|
+
5. Display stopped status and exit.
|
|
67
|
+
|
|
68
|
+
If no processes found, display "Nothing running" and exit.
|
|
69
|
+
|
|
70
|
+
## Step 1: Detect project
|
|
71
|
+
|
|
72
|
+
Find the API and Web directories:
|
|
73
|
+
- API: `src/*.Api` (first match)
|
|
74
|
+
- Web: `web/*-web` (first match)
|
|
75
|
+
|
|
76
|
+
If neither found, display error and exit.
|
|
77
|
+
|
|
78
|
+
## Step 2: Detect environment and ports
|
|
79
|
+
|
|
80
|
+
Check if `appsettings.Local.json` exists in the API directory:
|
|
81
|
+
|
|
82
|
+
| Condition | Mode | Backend | Frontend | Backend command | Frontend command |
|
|
83
|
+
|-----------|------|---------|----------|-----------------|------------------|
|
|
84
|
+
| `appsettings.Local.json` exists | **Local** | Port from `Kestrel:Endpoints:Http:Url` | Port from `.env.standalone` or `package.json` local script | `dotnet run --launch-profile Local` | `npm run local` |
|
|
85
|
+
| No local config | **Development** | Port 5142 (launchSettings http profile) | Port 6173 (vite default) | `dotnet run --environment Development` | `npm run dev` |
|
|
86
|
+
|
|
87
|
+
**Reading ports:**
|
|
88
|
+
- **Local backend port**: Parse `appsettings.Local.json` -> `Kestrel.Endpoints.Http.Url` -> extract port from URL (e.g., `http://localhost:5290` -> `5290`)
|
|
89
|
+
- **Local frontend port**: Parse `.env.standalone` -> look for port in `VITE_API_URL`, or parse `package.json` -> `scripts.local` for `--port` flag
|
|
90
|
+
- **Dev backend port**: Default `5142`
|
|
91
|
+
- **Dev frontend port**: Default `6173`
|
|
92
|
+
|
|
93
|
+
## Step 3: Validate config coherence
|
|
94
|
+
|
|
95
|
+
**CRITICAL: Verify that backend and frontend configs are consistent.**
|
|
96
|
+
|
|
97
|
+
### Rules to check:
|
|
98
|
+
|
|
99
|
+
| Rule | Backend source | Frontend source | Validation |
|
|
100
|
+
|------|---------------|-----------------|------------|
|
|
101
|
+
| Frontend calls correct backend | Port API (Kestrel or launchSettings) | `VITE_API_URL` in `.env.standalone` (Local) or `.env.development` (Dev) | Port in VITE_API_URL must match API port |
|
|
102
|
+
| Backend allows frontend (CORS) | `SmartStack:CorsOrigins[]` in appsettings | Frontend port | CorsOrigins must contain `http://localhost:{WEB_PORT}` |
|
|
103
|
+
| OAuth redirects to correct frontend | `Authentication:FrontendUrl` in appsettings | Frontend port | FrontendUrl must point to `http://localhost:{WEB_PORT}` |
|
|
104
|
+
| Local files exist in pairs | `appsettings.Local.json` | `.env.standalone` | If one exists, the other must too |
|
|
105
|
+
|
|
106
|
+
### In Local mode:
|
|
107
|
+
|
|
108
|
+
1. Read `appsettings.Local.json` -> extract `Kestrel:Endpoints:Http:Url` (API port)
|
|
109
|
+
2. Read `appsettings.Local.json` -> extract `Authentication:FrontendUrl` (expected frontend URL)
|
|
110
|
+
3. Read `.env.standalone` -> extract `VITE_API_URL` (expected API URL by frontend)
|
|
111
|
+
4. Verify `VITE_API_URL` port matches Kestrel port
|
|
112
|
+
5. Verify `Authentication:FrontendUrl` port matches frontend port
|
|
113
|
+
6. If `appsettings.Local.json` exists but NOT `.env.standalone` -> **create `.env.standalone`** with correct values (ask user first)
|
|
114
|
+
7. If `.env.standalone` exists but NOT `appsettings.Local.json` -> **warn** (config incomplete)
|
|
115
|
+
|
|
116
|
+
### In Development mode:
|
|
117
|
+
|
|
118
|
+
1. Verify `.env.development` contains `VITE_API_URL=http://localhost:5142` (NOT 5290 which is Local profile)
|
|
119
|
+
2. Verify `appsettings.json` -> `SmartStack:CorsOrigins` contains `http://localhost:6173`
|
|
120
|
+
3. Verify `Authentication:FrontendUrl` = `http://localhost:6173`
|
|
121
|
+
|
|
122
|
+
### If incoherence detected:
|
|
123
|
+
|
|
124
|
+
1. Display a clear WARNING with detected values vs expected values
|
|
125
|
+
2. Propose to auto-correct (write the correct files)
|
|
126
|
+
3. If user refuses, continue with a warning
|
|
127
|
+
|
|
128
|
+
### Output format for coherence:
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
Config Coherence Check:
|
|
132
|
+
[OK] VITE_API_URL matches backend port (5290)
|
|
133
|
+
[OK] CORS allows frontend (http://localhost:3000)
|
|
134
|
+
[OK] OAuth redirect matches frontend
|
|
135
|
+
[OK] Local config files paired
|
|
136
|
+
|
|
137
|
+
-- OR --
|
|
138
|
+
|
|
139
|
+
[WARN] VITE_API_URL=http://localhost:5290 but backend runs on port 5142
|
|
140
|
+
Expected: VITE_API_URL=http://localhost:5142
|
|
141
|
+
[WARN] .env.standalone missing (appsettings.Local.json exists)
|
|
142
|
+
-> Creating .env.standalone with correct values...
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Step 4: Check if already running
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
netstat.exe -ano | grep ":${API_PORT} .*LISTENING"
|
|
149
|
+
netstat.exe -ano | grep ":${WEB_PORT} .*LISTENING"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
If `--backend-only`: skip frontend checks.
|
|
153
|
+
If `--frontend-only`: skip backend checks.
|
|
154
|
+
|
|
155
|
+
## Step 5: Launch backend
|
|
156
|
+
|
|
157
|
+
If NOT already running and NOT `--frontend-only`:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
# cd into the API directory first
|
|
161
|
+
cd {API_DIR} && dotnet run {LAUNCH_ARGS}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Use `Bash(run_in_background=true)` so it runs in background.
|
|
165
|
+
|
|
166
|
+
## Step 6: Launch frontend
|
|
167
|
+
|
|
168
|
+
If NOT already running and NOT `--backend-only`:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
# cd into the Web directory first
|
|
172
|
+
cd {WEB_DIR} && npm run {SCRIPT}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Use `Bash(run_in_background=true)` so it runs in background.
|
|
176
|
+
|
|
177
|
+
## Step 7: Handle admin password
|
|
178
|
+
|
|
179
|
+
1. If `.claude/dev-session.json` exists and contains a password and `--reset` NOT specified -> reuse stored credentials
|
|
180
|
+
2. If `--reset` OR no stored password:
|
|
181
|
+
a. If backend was just launched, wait for it to be UP first:
|
|
182
|
+
```bash
|
|
183
|
+
# Poll up to 30 seconds (10 attempts x 3s)
|
|
184
|
+
for i in $(seq 1 10); do
|
|
185
|
+
netstat.exe -ano | grep ":${API_PORT} .*LISTENING" && break
|
|
186
|
+
sleep 3
|
|
187
|
+
done
|
|
188
|
+
```
|
|
189
|
+
b. Run: `smartstack admin reset --force --json`
|
|
190
|
+
c. Parse JSON output to extract email and password
|
|
191
|
+
d. Write `.claude/dev-session.json`:
|
|
192
|
+
```json
|
|
193
|
+
{
|
|
194
|
+
"admin_password": "...",
|
|
195
|
+
"admin_email": "local.admin@smartstack.local",
|
|
196
|
+
"last_reset": "2026-03-10T14:30:00Z",
|
|
197
|
+
"api_port": 5142,
|
|
198
|
+
"web_port": 6173,
|
|
199
|
+
"environment": "Development"
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
e. Verify `.claude/dev-session.json` is in `.gitignore`. If not, warn the user (contains credentials).
|
|
203
|
+
|
|
204
|
+
## Step 8: Display connection info
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
================================================================
|
|
208
|
+
SMARTSTACK DEV ENVIRONMENT
|
|
209
|
+
================================================================
|
|
210
|
+
|
|
211
|
+
Backend: http://localhost:{API_PORT} [RUNNING|STARTING]
|
|
212
|
+
Frontend: http://localhost:{WEB_PORT} [RUNNING|STARTING]
|
|
213
|
+
Swagger: http://localhost:{API_PORT}/scalar
|
|
214
|
+
|
|
215
|
+
Email: local.admin@smartstack.local
|
|
216
|
+
Password: xxxxxxxxxxxxxx
|
|
217
|
+
|
|
218
|
+
Environment: {Development|Local}
|
|
219
|
+
Config: OK | WARNING (details)
|
|
220
|
+
================================================================
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
</workflow>
|
|
224
|
+
|
|
225
|
+
<important_notes>
|
|
226
|
+
|
|
227
|
+
1. **Backend must be UP before admin reset** - Poll the port (2-3s intervals, max 30s) before attempting password reset
|
|
228
|
+
2. **Cross-worktree support** - Detect the correct API directory regardless of which worktree we're in
|
|
229
|
+
3. **No MCP required** - This skill only uses bash commands and the `smartstack` CLI
|
|
230
|
+
4. **Config coherence is critical** - Mismatched ports between backend/frontend is the #1 cause of "it doesn't work" issues
|
|
231
|
+
5. **Idempotent** - Running twice should detect already-running processes and reuse stored credentials
|
|
232
|
+
6. **dev-session.json contains secrets** - Must be in `.gitignore`
|
|
233
|
+
|
|
234
|
+
</important_notes>
|
|
235
|
+
|
|
236
|
+
<success_criteria>
|
|
237
|
+
- Backend and frontend detected and launched (or already running)
|
|
238
|
+
- Config coherence validated (or warnings displayed)
|
|
239
|
+
- Admin credentials available (reused or freshly reset)
|
|
240
|
+
- Connection info displayed in clear format
|
|
241
|
+
- No errors on repeated invocations (idempotent)
|
|
242
|
+
</success_criteria>
|
|
@@ -156,7 +156,7 @@ export function EntityListPage() {
|
|
|
156
156
|
setLoading(true);
|
|
157
157
|
setError(null);
|
|
158
158
|
const result = await entityApi.getAll();
|
|
159
|
-
setData(result
|
|
159
|
+
setData(result?.items ?? []);
|
|
160
160
|
} catch (err: any) {
|
|
161
161
|
setError(err.message || t('{module}:errors.loadFailed', 'Failed to load data'));
|
|
162
162
|
} finally {
|
|
@@ -49,7 +49,7 @@ export function UsersPage() {
|
|
|
49
49
|
setLoading(true);
|
|
50
50
|
setError(null);
|
|
51
51
|
const result = await usersApi.getAll();
|
|
52
|
-
setUsers(result
|
|
52
|
+
setUsers(result?.items ?? []);
|
|
53
53
|
} catch (err: any) {
|
|
54
54
|
setError(err.message || t('users:errors.loadFailed', 'Failed to load users'));
|
|
55
55
|
} finally {
|