@atlashub/smartstack-cli 3.0.0 → 3.1.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/agents.html +1 -371
- package/.documentation/cli-commands.html +1 -1
- package/.documentation/commands.html +1 -1
- package/.documentation/efcore.html +1 -1
- package/.documentation/gitflow.html +1 -1
- package/.documentation/hooks.html +27 -66
- package/.documentation/index.html +166 -166
- package/.documentation/init.html +6 -7
- package/.documentation/installation.html +1 -1
- package/.documentation/ralph-loop.html +1 -9
- package/.documentation/test-web.html +15 -39
- package/dist/index.js +23 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/agents/gitflow/merge.md +56 -6
- package/templates/agents/gitflow/pr.md +70 -9
- package/templates/project/appsettings.json.template +8 -2
- package/templates/skills/business-analyse/html/ba-interactive.html +1 -27
- package/templates/skills/business-analyse/steps/step-03-specify.md +57 -0
- package/templates/skills/business-analyse/steps/step-05-handoff.md +211 -13
- package/templates/skills/gitflow/steps/step-pr.md +17 -5
- package/templates/skills/ralph-loop/SKILL.md +22 -15
- package/templates/skills/ralph-loop/steps/step-01-task.md +89 -4
- package/templates/skills/ralph-loop/steps/step-02-execute.md +408 -23
- package/templates/skills/ralph-loop/steps/step-03-commit.md +82 -0
- package/templates/skills/ralph-loop/steps/step-04-check.md +235 -6
- package/templates/skills/ralph-loop/steps/step-05-report.md +115 -0
package/package.json
CHANGED
|
@@ -12,9 +12,48 @@ Safe PR merge with review checklist. Supports GitHub and Azure DevOps.
|
|
|
12
12
|
|
|
13
13
|
## EXECUTION SEQUENCE
|
|
14
14
|
|
|
15
|
-
### 1.
|
|
15
|
+
### 1. Load GitFlow Config (MANDATORY)
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
+
# Find and verify config exists
|
|
19
|
+
CONFIG_FILE=""
|
|
20
|
+
if [ -f ".claude/gitflow/config.json" ]; then
|
|
21
|
+
CONFIG_FILE=".claude/gitflow/config.json"
|
|
22
|
+
elif [ -f ".gitflow/config.json" ]; then
|
|
23
|
+
CONFIG_FILE=".gitflow/config.json"
|
|
24
|
+
else
|
|
25
|
+
# Try develop worktree
|
|
26
|
+
DEVELOP_PATH=$(git worktree list --porcelain 2>/dev/null | grep -A1 "branch refs/heads/develop" | grep "^worktree " | sed 's/^worktree //')
|
|
27
|
+
if [ -n "$DEVELOP_PATH" ] && [ -f "$DEVELOP_PATH/.claude/gitflow/config.json" ]; then
|
|
28
|
+
CONFIG_FILE="$DEVELOP_PATH/.claude/gitflow/config.json"
|
|
29
|
+
elif [ -n "$DEVELOP_PATH" ] && [ -f "$DEVELOP_PATH/.gitflow/config.json" ]; then
|
|
30
|
+
CONFIG_FILE="$DEVELOP_PATH/.gitflow/config.json"
|
|
31
|
+
fi
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# CRITICAL: Block if no config
|
|
35
|
+
if [ -z "$CONFIG_FILE" ] || [ ! -f "$CONFIG_FILE" ]; then
|
|
36
|
+
echo "❌ GitFlow config not found"
|
|
37
|
+
echo "→ Run: /gitflow init"
|
|
38
|
+
exit 1
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# Extract config values
|
|
42
|
+
GF_PROVIDER=$(grep -oP '"provider":\s*"\K[^"]+' "$CONFIG_FILE" | head -1)
|
|
43
|
+
GF_MAIN_BRANCH=$(grep -oP '"main":\s*"\K[^"]+' "$CONFIG_FILE" | head -1)
|
|
44
|
+
GF_DEVELOP_BRANCH=$(grep -oP '"develop":\s*"\K[^"]+' "$CONFIG_FILE" | head -1)
|
|
45
|
+
|
|
46
|
+
# Validate required values
|
|
47
|
+
if [ -z "$GF_MAIN_BRANCH" ] || [ -z "$GF_DEVELOP_BRANCH" ]; then
|
|
48
|
+
echo "❌ Invalid GitFlow config (missing branches)"
|
|
49
|
+
exit 1
|
|
50
|
+
fi
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 2. Detect Provider and Load PR Context
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Detect provider
|
|
18
57
|
REMOTE_URL=$(git remote get-url origin 2>/dev/null)
|
|
19
58
|
if [[ "$REMOTE_URL" == *"dev.azure.com"* ]]; then
|
|
20
59
|
GIT_PROVIDER="azuredevops"
|
|
@@ -25,12 +64,23 @@ fi
|
|
|
25
64
|
CURRENT=$(git rev-parse --abbrev-ref HEAD)
|
|
26
65
|
BRANCH_TYPE=$(echo $CURRENT | cut -d'/' -f1)
|
|
27
66
|
|
|
67
|
+
# Determine target branch (using config values)
|
|
68
|
+
case "$BRANCH_TYPE" in
|
|
69
|
+
feature) TARGET="$GF_DEVELOP_BRANCH" ;;
|
|
70
|
+
release) TARGET="$GF_MAIN_BRANCH" ;;
|
|
71
|
+
hotfix) TARGET="$GF_MAIN_BRANCH" ;;
|
|
72
|
+
*) TARGET="$GF_DEVELOP_BRANCH" ;;
|
|
73
|
+
esac
|
|
74
|
+
|
|
75
|
+
echo "✓ Config loaded: $CONFIG_FILE"
|
|
76
|
+
echo "✓ Branch: $CURRENT → Target: $TARGET"
|
|
77
|
+
|
|
28
78
|
# Find PR for current branch
|
|
29
79
|
# GitHub: gh pr list --head "$CURRENT"
|
|
30
80
|
# Azure DevOps: az repos pr list --source-branch "$CURRENT"
|
|
31
81
|
```
|
|
32
82
|
|
|
33
|
-
###
|
|
83
|
+
### 3. Determine Merge Strategy
|
|
34
84
|
|
|
35
85
|
| Type | Strategy | Delete Branch |
|
|
36
86
|
|------|----------|---------------|
|
|
@@ -38,7 +88,7 @@ BRANCH_TYPE=$(echo $CURRENT | cut -d'/' -f1)
|
|
|
38
88
|
| Release | merge --no-ff | yes |
|
|
39
89
|
| Hotfix | merge --no-ff | yes |
|
|
40
90
|
|
|
41
|
-
###
|
|
91
|
+
### 4. Review Checklist
|
|
42
92
|
|
|
43
93
|
Display review checklist:
|
|
44
94
|
- Build status (pass/fail)
|
|
@@ -47,7 +97,7 @@ Display review checklist:
|
|
|
47
97
|
- Conflicts (none/detected)
|
|
48
98
|
- CI status (if available)
|
|
49
99
|
|
|
50
|
-
###
|
|
100
|
+
### 5. Execute Merge
|
|
51
101
|
|
|
52
102
|
**GitHub:**
|
|
53
103
|
```bash
|
|
@@ -67,7 +117,7 @@ az repos pr update --id $PR_ID --status completed --squash true --delete-source-
|
|
|
67
117
|
az repos pr update --id $PR_ID --status completed --squash false --delete-source-branch true
|
|
68
118
|
```
|
|
69
119
|
|
|
70
|
-
###
|
|
120
|
+
### 6. Verify Merge
|
|
71
121
|
|
|
72
122
|
```bash
|
|
73
123
|
# Force update tracking refs
|
|
@@ -78,7 +128,7 @@ git fetch origin --prune --quiet
|
|
|
78
128
|
MERGED=$(git branch -r --merged origin/$TARGET | grep "$CURRENT" | wc -l)
|
|
79
129
|
```
|
|
80
130
|
|
|
81
|
-
###
|
|
131
|
+
### 7. Summary
|
|
82
132
|
|
|
83
133
|
```
|
|
84
134
|
PR MERGED
|
|
@@ -14,21 +14,68 @@ Create PR with auto-generated description, Azure DevOps and GitHub support.
|
|
|
14
14
|
|
|
15
15
|
## EXECUTION SEQUENCE
|
|
16
16
|
|
|
17
|
-
### 1.
|
|
17
|
+
### 1. Load GitFlow Config (MANDATORY)
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
#
|
|
21
|
-
|
|
22
|
-
if [
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
# Find and verify config exists
|
|
21
|
+
CONFIG_FILE=""
|
|
22
|
+
if [ -f ".claude/gitflow/config.json" ]; then
|
|
23
|
+
CONFIG_FILE=".claude/gitflow/config.json"
|
|
24
|
+
elif [ -f ".gitflow/config.json" ]; then
|
|
25
|
+
CONFIG_FILE=".gitflow/config.json"
|
|
26
|
+
else
|
|
27
|
+
# Try develop worktree
|
|
28
|
+
DEVELOP_PATH=$(git worktree list --porcelain 2>/dev/null | grep -A1 "branch refs/heads/develop" | grep "^worktree " | sed 's/^worktree //')
|
|
29
|
+
if [ -n "$DEVELOP_PATH" ] && [ -f "$DEVELOP_PATH/.claude/gitflow/config.json" ]; then
|
|
30
|
+
CONFIG_FILE="$DEVELOP_PATH/.claude/gitflow/config.json"
|
|
31
|
+
elif [ -n "$DEVELOP_PATH" ] && [ -f "$DEVELOP_PATH/.gitflow/config.json" ]; then
|
|
32
|
+
CONFIG_FILE="$DEVELOP_PATH/.gitflow/config.json"
|
|
33
|
+
fi
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# CRITICAL: Block if no config
|
|
37
|
+
if [ -z "$CONFIG_FILE" ] || [ ! -f "$CONFIG_FILE" ]; then
|
|
38
|
+
echo "❌ GitFlow config not found"
|
|
39
|
+
echo "→ Run: /gitflow init"
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# Extract config values
|
|
44
|
+
GF_PROVIDER=$(grep -oP '"provider":\s*"\K[^"]+' "$CONFIG_FILE" | head -1)
|
|
45
|
+
GF_MAIN_BRANCH=$(grep -oP '"main":\s*"\K[^"]+' "$CONFIG_FILE" | head -1)
|
|
46
|
+
GF_DEVELOP_BRANCH=$(grep -oP '"develop":\s*"\K[^"]+' "$CONFIG_FILE" | head -1)
|
|
47
|
+
GF_REMOTE_URL=$(grep -oP '"remoteUrl":\s*"\K[^"]+' "$CONFIG_FILE" | head -1)
|
|
48
|
+
|
|
49
|
+
# Validate required values
|
|
50
|
+
if [ -z "$GF_MAIN_BRANCH" ] || [ -z "$GF_DEVELOP_BRANCH" ]; then
|
|
51
|
+
echo "❌ Invalid GitFlow config (missing branches)"
|
|
52
|
+
exit 1
|
|
53
|
+
fi
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 2. Detect Provider and Branch Context
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Detect provider from config or remote URL
|
|
60
|
+
if [ -z "$GF_PROVIDER" ]; then
|
|
61
|
+
REMOTE_URL=$(git remote get-url origin 2>/dev/null)
|
|
62
|
+
if [[ "$REMOTE_URL" == *"dev.azure.com"* ]] || [[ "$REMOTE_URL" == *"visualstudio.com"* ]]; then
|
|
63
|
+
GIT_PROVIDER="azuredevops"
|
|
64
|
+
else
|
|
65
|
+
GIT_PROVIDER="github"
|
|
66
|
+
fi
|
|
67
|
+
else
|
|
68
|
+
GIT_PROVIDER="$GF_PROVIDER"
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
# Extract Azure DevOps details if needed
|
|
72
|
+
if [ "$GIT_PROVIDER" = "azuredevops" ]; then
|
|
73
|
+
REMOTE_URL="${GF_REMOTE_URL:-$(git remote get-url origin 2>/dev/null)}"
|
|
25
74
|
[[ "$REMOTE_URL" =~ dev\.azure\.com/([^/]+)/([^/]+)/_git/([^/]+) ]] && {
|
|
26
75
|
AZURE_ORG="${BASH_REMATCH[1]}"
|
|
27
76
|
AZURE_PROJECT="${BASH_REMATCH[2]}"
|
|
28
77
|
AZURE_REPO="${BASH_REMATCH[3]}"
|
|
29
78
|
}
|
|
30
|
-
else
|
|
31
|
-
GIT_PROVIDER="github"
|
|
32
79
|
fi
|
|
33
80
|
|
|
34
81
|
# Get current branch (worktree-compatible)
|
|
@@ -37,7 +84,21 @@ BRANCH_TYPE=$(echo $CURRENT | cut -d'/' -f1)
|
|
|
37
84
|
BRANCH_NAME=$(echo $CURRENT | sed 's/.*\///')
|
|
38
85
|
```
|
|
39
86
|
|
|
40
|
-
###
|
|
87
|
+
### 3. Determine Target Branch
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Map branch type to target branch (using config values)
|
|
91
|
+
case "$BRANCH_TYPE" in
|
|
92
|
+
feature) TARGET="$GF_DEVELOP_BRANCH" ;;
|
|
93
|
+
release) TARGET="$GF_MAIN_BRANCH" ;;
|
|
94
|
+
hotfix) TARGET="$GF_MAIN_BRANCH" ;;
|
|
95
|
+
*) TARGET="$GF_DEVELOP_BRANCH" ;;
|
|
96
|
+
esac
|
|
97
|
+
|
|
98
|
+
echo "✓ Config loaded: $CONFIG_FILE"
|
|
99
|
+
echo "✓ Branch: $CURRENT (type: $BRANCH_TYPE)"
|
|
100
|
+
echo "✓ Target: $TARGET"
|
|
101
|
+
```
|
|
41
102
|
|
|
42
103
|
| Type | Target | Reason |
|
|
43
104
|
|------|--------|--------|
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
"EnableDevSeeding": false
|
|
9
9
|
},
|
|
10
10
|
"Jwt": {
|
|
11
|
-
"Secret": "{{GenerateRandomSecret}}",
|
|
12
11
|
"Issuer": "{{ProjectName}}",
|
|
13
12
|
"Audience": "{{ProjectName}}",
|
|
14
13
|
"AccessTokenExpirationMinutes": 60,
|
|
@@ -132,6 +131,10 @@
|
|
|
132
131
|
"SenderAddress": ""
|
|
133
132
|
}
|
|
134
133
|
},
|
|
134
|
+
"Zefix": {
|
|
135
|
+
"Username": "",
|
|
136
|
+
"Password": ""
|
|
137
|
+
},
|
|
135
138
|
"Entra": {
|
|
136
139
|
"TenantId": "",
|
|
137
140
|
"ClientId": "",
|
|
@@ -140,10 +143,13 @@
|
|
|
140
143
|
"SyncIntervalMinutes": 60,
|
|
141
144
|
"UseDeltaSync": true,
|
|
142
145
|
"DefaultConflictResolution": "ManualReview",
|
|
143
|
-
"AutoCreateReferences": true
|
|
146
|
+
"AutoCreateReferences": true,
|
|
147
|
+
"BackgroundSyncEnabled": false,
|
|
148
|
+
"BackgroundSyncIntervalMinutes": 60
|
|
144
149
|
},
|
|
145
150
|
"MultiTenant": {
|
|
146
151
|
"Enabled": true,
|
|
152
|
+
"EnableB2B": true,
|
|
147
153
|
"EnableB2C": true,
|
|
148
154
|
"SystemTenantSlug": "default",
|
|
149
155
|
"SystemTenantName": "Default Workspace",
|
|
@@ -1406,33 +1406,7 @@
|
|
|
1406
1406
|
DATA STORE
|
|
1407
1407
|
============================================ */
|
|
1408
1408
|
const APP_KEY = 'ba-{{APPLICATION_ID}}';
|
|
1409
|
-
let data = {
|
|
1410
|
-
metadata: {
|
|
1411
|
-
applicationName: '{{APPLICATION_NAME}}',
|
|
1412
|
-
applicationId: '{{APPLICATION_ID}}',
|
|
1413
|
-
version: '{{VERSION}}',
|
|
1414
|
-
createdAt: '{{CREATED_AT}}',
|
|
1415
|
-
lastModified: new Date().toISOString()
|
|
1416
|
-
},
|
|
1417
|
-
cadrage: {
|
|
1418
|
-
problem: {},
|
|
1419
|
-
current: {},
|
|
1420
|
-
vision: {},
|
|
1421
|
-
stakeholders: [],
|
|
1422
|
-
scope: { vital: [], important: [], optional: [], excluded: [] },
|
|
1423
|
-
risks: [],
|
|
1424
|
-
assumptions: '',
|
|
1425
|
-
success: {}
|
|
1426
|
-
},
|
|
1427
|
-
modules: [],
|
|
1428
|
-
dependencies: [],
|
|
1429
|
-
moduleSpecs: {},
|
|
1430
|
-
consolidation: {
|
|
1431
|
-
interactions: [],
|
|
1432
|
-
e2eFlows: []
|
|
1433
|
-
},
|
|
1434
|
-
handoff: {}
|
|
1435
|
-
};
|
|
1409
|
+
let data = {{FEATURE_DATA}};
|
|
1436
1410
|
|
|
1437
1411
|
/* ============================================
|
|
1438
1412
|
INITIALIZATION
|
|
@@ -1263,6 +1263,62 @@ ba-writer.updateStatus({module_feature_id}, "specified")
|
|
|
1263
1263
|
ba-writer.updateModuleStatus({feature_id}, {currentModule.code}, "specified")
|
|
1264
1264
|
```
|
|
1265
1265
|
|
|
1266
|
+
### 11-bis. Deploy Incremental Interactive HTML (MANDATORY)
|
|
1267
|
+
|
|
1268
|
+
> **After each module is specified, deploy/update the interactive HTML document with all available data.**
|
|
1269
|
+
> This allows the client to review completed modules while the next module is being specified.
|
|
1270
|
+
> The HTML is incrementally enriched: after module 1, only module 1 specs appear; after module 2, both appear; etc.
|
|
1271
|
+
|
|
1272
|
+
**Source:** `html/ba-interactive.html` (relative to skill root = `~/.claude/skills/business-analyse/html/`)
|
|
1273
|
+
|
|
1274
|
+
**Destination:** `docs/business/{app}/business-analyse/v{version}/ba-interactive.html`
|
|
1275
|
+
|
|
1276
|
+
**Steps:**
|
|
1277
|
+
|
|
1278
|
+
1. **Read sources:**
|
|
1279
|
+
- Read the HTML template from skill directory
|
|
1280
|
+
- Read the master feature.json (application level — now updated with current module status)
|
|
1281
|
+
- Read EACH completed module's feature.json (including the one just specified)
|
|
1282
|
+
|
|
1283
|
+
2. **Build FEATURE_DATA object:**
|
|
1284
|
+
|
|
1285
|
+
> **Use the EXACT SAME mapping defined in step-05-handoff.md section 9d.**
|
|
1286
|
+
> The mapping is identical — the only difference is that `moduleSpecs` only includes completed modules.
|
|
1287
|
+
> Modules not yet specified will NOT appear in `moduleSpecs` (their entry in `modules[]` will show `status: "pending"`).
|
|
1288
|
+
|
|
1289
|
+
Follow step-05 section 9d "Step 2: Build FEATURE_DATA object" for the complete mapping pseudocode:
|
|
1290
|
+
- `metadata`, `cadrage`, `modules[]`, `dependencies[]` → from master feature.json
|
|
1291
|
+
- `moduleSpecs[moduleCode]` → only for modules with status "specified" (completed so far)
|
|
1292
|
+
- `consolidation` → empty `{ interactions: [], e2eFlows: [] }` (not yet consolidated)
|
|
1293
|
+
- `handoff` → empty `{}` (not yet handed off)
|
|
1294
|
+
|
|
1295
|
+
3. **Replace placeholders in template:**
|
|
1296
|
+
- Serialize the FEATURE_DATA object as JSON (2-space indentation)
|
|
1297
|
+
- Replace `{{FEATURE_DATA}}` with the serialized JSON
|
|
1298
|
+
- Replace `{{APPLICATION_NAME}}` → `{application_name}`
|
|
1299
|
+
- Replace `{{APPLICATION_ID}}` → `{feature_id}`
|
|
1300
|
+
- Replace `{{VERSION}}` → `{version}`
|
|
1301
|
+
- Replace `{{CREATED_AT}}` → `{ISO timestamp}`
|
|
1302
|
+
|
|
1303
|
+
4. **Write and confirm:**
|
|
1304
|
+
|
|
1305
|
+
```
|
|
1306
|
+
✓ Interactive HTML updated (incremental):
|
|
1307
|
+
Path: docs/business/{app}/business-analyse/v{version}/ba-interactive.html
|
|
1308
|
+
Modules included: {completedModules.length}/{totalModules} specified
|
|
1309
|
+
- {completedModule1}: {uc_count} UCs, {br_count} BRs, {entity_count} entities
|
|
1310
|
+
- {completedModule2}: ...
|
|
1311
|
+
Remaining: {pendingModules.join(', ')} (will be added after specification)
|
|
1312
|
+
→ Client can open in browser to review completed modules now.
|
|
1313
|
+
```
|
|
1314
|
+
|
|
1315
|
+
> **WHY incremental?** The client doesn't have to wait until handoff to start reviewing.
|
|
1316
|
+
> While module 2 is being specified, the client can already give feedback on module 1.
|
|
1317
|
+
> Each incremental deployment OVERWRITES the previous HTML (latest state always).
|
|
1318
|
+
> The FINAL deployment at step-05 (handoff) will include consolidation and handoff data.
|
|
1319
|
+
|
|
1320
|
+
---
|
|
1321
|
+
|
|
1266
1322
|
### 12. Loop Decision
|
|
1267
1323
|
|
|
1268
1324
|
```
|
|
@@ -1338,6 +1394,7 @@ Read metadata.workflow.currentModule
|
|
|
1338
1394
|
- Client confirmed module specification
|
|
1339
1395
|
- Module feature.json written
|
|
1340
1396
|
- Master updated with module status
|
|
1397
|
+
- **Interactive HTML deployed/updated with completed module data (incremental)**
|
|
1341
1398
|
- Loop advanced correctly
|
|
1342
1399
|
|
|
1343
1400
|
## FAILURE MODES
|
|
@@ -917,8 +917,10 @@ Status journey: analyze → consolidate → **handed-off**
|
|
|
917
917
|
|
|
918
918
|
### 9d. Deploy Interactive HTML Document (MANDATORY)
|
|
919
919
|
|
|
920
|
-
> **The interactive HTML document is deployed to the project
|
|
921
|
-
>
|
|
920
|
+
> **The interactive HTML document is deployed to the project PRE-POPULATED with ALL analysis data.**
|
|
921
|
+
> The client opens it in a browser and sees the complete analysis (cadrage, modules, entities, UCs, BRs, wireframes, permissions, consolidation).
|
|
922
|
+
> The client can then review, edit, enrich, and export modifications as JSON.
|
|
923
|
+
> That JSON can be re-imported via `/business-analyse -x` to update the feature.json.
|
|
922
924
|
|
|
923
925
|
**Source:** `html/ba-interactive.html` (relative to skill root = `~/.claude/skills/business-analyse/html/`)
|
|
924
926
|
|
|
@@ -926,27 +928,223 @@ Status journey: analyze → consolidate → **handed-off**
|
|
|
926
928
|
|
|
927
929
|
**Deployment steps:**
|
|
928
930
|
|
|
931
|
+
#### Step 1: Read source data
|
|
932
|
+
|
|
929
933
|
1. Read the HTML template from skill directory
|
|
930
|
-
2.
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
934
|
+
2. Read the master feature.json (application level)
|
|
935
|
+
3. Read EACH module feature.json (module level)
|
|
936
|
+
|
|
937
|
+
#### Step 2: Build FEATURE_DATA object
|
|
938
|
+
|
|
939
|
+
> **CRITICAL:** The HTML `data` object MUST be pre-populated with ALL analysis data.
|
|
940
|
+
> An empty data object is a **BUG** — the client would see a blank page.
|
|
941
|
+
|
|
942
|
+
Build a JSON object following this **exact mapping** from feature.json to the HTML data model:
|
|
943
|
+
|
|
944
|
+
```javascript
|
|
945
|
+
{
|
|
946
|
+
metadata: {
|
|
947
|
+
applicationName: master.metadata.application, // e.g. "RH"
|
|
948
|
+
applicationId: master.id, // e.g. "FEAT-001"
|
|
949
|
+
version: master.version, // e.g. "1.0"
|
|
950
|
+
createdAt: master.metadata.createdAt,
|
|
951
|
+
lastModified: master.metadata.updatedAt
|
|
952
|
+
},
|
|
953
|
+
cadrage: {
|
|
954
|
+
problem: {
|
|
955
|
+
description: master.cadrage.problem, // string → problem.description
|
|
956
|
+
trigger: master.cadrage.trigger, // string → problem.trigger
|
|
957
|
+
impactedPeople: "", // not in feature.json, client fills
|
|
958
|
+
history: "",
|
|
959
|
+
consequences: ""
|
|
960
|
+
},
|
|
961
|
+
current: {
|
|
962
|
+
tools: master.cadrage.asIs, // string → current.tools
|
|
963
|
+
steps: [], // client can add process steps
|
|
964
|
+
painPoints: master.cadrage.stakeholders
|
|
965
|
+
.flatMap(s => s.painPoints || []).join("\n"), // aggregate all painPoints
|
|
966
|
+
errors: ""
|
|
967
|
+
},
|
|
968
|
+
vision: {
|
|
969
|
+
changes: master.cadrage.toBe, // string → vision.changes
|
|
970
|
+
results: master.cadrage.acceptanceCriteria
|
|
971
|
+
.map(ac => ac.criterion).join("\n"), // AC → results (one per line)
|
|
972
|
+
successSign: ""
|
|
973
|
+
},
|
|
974
|
+
stakeholders: master.cadrage.stakeholders.map(s => ({
|
|
975
|
+
role: s.role,
|
|
976
|
+
function: s.function || "",
|
|
977
|
+
tasks: s.tasks || [],
|
|
978
|
+
frequency: mapFrequency(s.frequency), // "Quotidien"→"daily", etc.
|
|
979
|
+
access: mapAccess(s.involvement), // "decision-maker"→"admin", "end-user"→"contributor"
|
|
980
|
+
frustrations: (s.painPoints || []).join("\n")
|
|
981
|
+
})),
|
|
982
|
+
scope: {
|
|
983
|
+
vital: (master.cadrage.globalScope.mustHave || [])
|
|
984
|
+
.map(item => ({ name: item, description: "" })), // string[] → {name,description}[]
|
|
985
|
+
important: (master.cadrage.globalScope.shouldHave || [])
|
|
986
|
+
.map(item => ({ name: item, description: "" })),
|
|
987
|
+
optional: (master.cadrage.globalScope.couldHave || [])
|
|
988
|
+
.map(item => ({ name: item, description: "" })),
|
|
989
|
+
excluded: (master.cadrage.globalScope.outOfScope || [])
|
|
990
|
+
.map(item => ({ name: item, description: "" }))
|
|
991
|
+
},
|
|
992
|
+
risks: (master.cadrage.risks || []).map(r => ({
|
|
993
|
+
description: r.description,
|
|
994
|
+
probability: r.probability, // "high" | "medium" | "low"
|
|
995
|
+
impact: r.impact,
|
|
996
|
+
mitigation: r.mitigation || ""
|
|
997
|
+
})),
|
|
998
|
+
assumptions: "",
|
|
999
|
+
success: {
|
|
1000
|
+
definition: (master.cadrage.acceptanceCriteria || [])
|
|
1001
|
+
.map(ac => ac.criterion).join("\n"),
|
|
1002
|
+
metrics: "",
|
|
1003
|
+
timeline: "",
|
|
1004
|
+
minimumConditions: ""
|
|
1005
|
+
}
|
|
1006
|
+
},
|
|
1007
|
+
modules: master.modules.map(m => ({
|
|
1008
|
+
code: m.code,
|
|
1009
|
+
name: m.code, // module code as name
|
|
1010
|
+
description: m.description || "",
|
|
1011
|
+
featureType: m.featureType || "data-centric",
|
|
1012
|
+
priority: m.priority || "must",
|
|
1013
|
+
entities: m.entities || [],
|
|
1014
|
+
status: m.status || "handed-off"
|
|
1015
|
+
})),
|
|
1016
|
+
dependencies: (master.dependencyGraph?.edges || []).map(e => ({
|
|
1017
|
+
from: e.from,
|
|
1018
|
+
to: e.to,
|
|
1019
|
+
description: e.description || ""
|
|
1020
|
+
})),
|
|
1021
|
+
moduleSpecs: {
|
|
1022
|
+
// FOR EACH module: read module feature.json, then map:
|
|
1023
|
+
// [moduleCode]: { useCases, businessRules, entities, permissions, notes, mockupNotes }
|
|
1024
|
+
},
|
|
1025
|
+
consolidation: {
|
|
1026
|
+
interactions: (master.consolidation?.crossModuleInteractions || []).map(i => ({
|
|
1027
|
+
from: i.fromModule,
|
|
1028
|
+
to: i.toModule,
|
|
1029
|
+
description: i.description || ""
|
|
1030
|
+
})),
|
|
1031
|
+
e2eFlows: (master.consolidation?.e2eFlows || []).map(f => ({
|
|
1032
|
+
name: f.name,
|
|
1033
|
+
steps: (f.steps || []).map(s => ({ module: s.module, action: s.action })),
|
|
1034
|
+
actors: (f.steps || []).map(s => s.permission).join(", ")
|
|
1035
|
+
}))
|
|
1036
|
+
},
|
|
1037
|
+
handoff: master.handoff || {}
|
|
1038
|
+
}
|
|
1039
|
+
```
|
|
1040
|
+
|
|
1041
|
+
**Module specs mapping** — for EACH module in `master.modules[]`:
|
|
1042
|
+
|
|
1043
|
+
1. Read the module feature.json at `master.modules[i].featureJsonPath`
|
|
1044
|
+
2. Map to `moduleSpecs[moduleCode]`:
|
|
1045
|
+
|
|
1046
|
+
```javascript
|
|
1047
|
+
moduleSpecs[moduleCode] = {
|
|
1048
|
+
useCases: (moduleFeature.specification?.useCases || []).map(uc => ({
|
|
1049
|
+
name: uc.name,
|
|
1050
|
+
actor: uc.primaryActor,
|
|
1051
|
+
steps: (uc.mainScenario || []).join("\n"), // array → newline-separated string
|
|
1052
|
+
alternative: (uc.alternativeScenarios || [])
|
|
1053
|
+
.map(a => a.name + ": " + (a.steps || []).join(", ")).join("\n")
|
|
1054
|
+
})),
|
|
1055
|
+
businessRules: (moduleFeature.analysis?.businessRules || []).map(br => ({
|
|
1056
|
+
name: br.name,
|
|
1057
|
+
category: br.category, // "validation"|"calculation"|"workflow"|"security"|"data"
|
|
1058
|
+
statement: br.statement,
|
|
1059
|
+
example: (br.examples || []).map(e => e.input + " → " + e.expected).join("; ")
|
|
1060
|
+
})),
|
|
1061
|
+
entities: (moduleFeature.analysis?.entities || []).map(ent => ({
|
|
1062
|
+
name: ent.name,
|
|
1063
|
+
description: ent.description || "",
|
|
1064
|
+
attributes: (ent.attributes || []).map(a => ({
|
|
1065
|
+
name: a.name,
|
|
1066
|
+
description: a.description || ""
|
|
1067
|
+
})),
|
|
1068
|
+
relationships: (ent.relationships || []).map(r =>
|
|
1069
|
+
r.target + " (" + r.type + ") - " + (r.description || "")
|
|
1070
|
+
)
|
|
1071
|
+
})),
|
|
1072
|
+
permissions: buildPermissionKeys(moduleFeature), // see below
|
|
1073
|
+
notes: "",
|
|
1074
|
+
mockupNotes: (moduleFeature.specification?.uiWireframes || [])
|
|
1075
|
+
.map(w => "[" + w.screen + "]\n" + w.mockup).join("\n\n")
|
|
1076
|
+
}
|
|
1077
|
+
```
|
|
1078
|
+
|
|
1079
|
+
**Permission keys** — the HTML uses `"Role|Action"` format (e.g. `"RH Admin|Consulter"`):
|
|
1080
|
+
|
|
1081
|
+
```javascript
|
|
1082
|
+
function buildPermissionKeys(moduleFeature) {
|
|
1083
|
+
const keys = [];
|
|
1084
|
+
const matrix = moduleFeature.specification?.permissionMatrix;
|
|
1085
|
+
if (!matrix) return keys;
|
|
1086
|
+
const actionMap = { read: "Consulter", create: "Creer", update: "Modifier",
|
|
1087
|
+
delete: "Supprimer", validate: "Valider", export: "Exporter",
|
|
1088
|
+
submit: "Valider", import: "Creer" };
|
|
1089
|
+
(matrix.roleAssignments || []).forEach(ra => {
|
|
1090
|
+
(ra.permissions || []).forEach(permPath => {
|
|
1091
|
+
const action = permPath.split(".").pop(); // last segment = action
|
|
1092
|
+
const uiAction = actionMap[action] || action;
|
|
1093
|
+
keys.push(ra.role + "|" + uiAction);
|
|
1094
|
+
});
|
|
1095
|
+
});
|
|
1096
|
+
return keys;
|
|
1097
|
+
}
|
|
1098
|
+
```
|
|
1099
|
+
|
|
1100
|
+
**Frequency mapping:**
|
|
1101
|
+
```
|
|
1102
|
+
"Quotidien" → "daily", "Hebdomadaire" → "weekly", "Mensuel" → "monthly"
|
|
1103
|
+
Default: "daily"
|
|
1104
|
+
```
|
|
1105
|
+
|
|
1106
|
+
**Access mapping (involvement → access):**
|
|
1107
|
+
```
|
|
1108
|
+
"decision-maker" → "admin", "end-user" with manager-like tasks → "manager"
|
|
1109
|
+
"end-user" → "contributor", "observer" → "viewer"
|
|
1110
|
+
Default: "contributor"
|
|
1111
|
+
```
|
|
1112
|
+
|
|
1113
|
+
#### Step 3: Replace placeholders in template
|
|
1114
|
+
|
|
1115
|
+
1. Serialize the FEATURE_DATA object as JSON (with 2-space indentation for readability)
|
|
1116
|
+
2. Replace `{{FEATURE_DATA}}` with the serialized JSON
|
|
1117
|
+
3. Replace `{{APPLICATION_NAME}}` → `{application_name}` (still used in `<title>` and header)
|
|
1118
|
+
4. Replace `{{APPLICATION_ID}}` → `{feature_id}` (still used in `APP_KEY`)
|
|
1119
|
+
5. Replace `{{VERSION}}` → `{version}`
|
|
1120
|
+
6. Replace `{{CREATED_AT}}` → `{ISO timestamp}`
|
|
1121
|
+
|
|
1122
|
+
> **NOTE:** `{{APPLICATION_NAME}}`, `{{APPLICATION_ID}}`, `{{VERSION}}`, `{{CREATED_AT}}` still appear
|
|
1123
|
+
> in the HTML body (`<title>`, header, `APP_KEY`). They MUST be replaced separately from FEATURE_DATA.
|
|
1124
|
+
|
|
1125
|
+
#### Step 4: Write and confirm
|
|
1126
|
+
|
|
1127
|
+
1. Write the populated HTML to the output directory
|
|
1128
|
+
2. Display deployment confirmation:
|
|
937
1129
|
|
|
938
1130
|
```
|
|
939
1131
|
✓ Interactive HTML deployed:
|
|
940
1132
|
Path: docs/business/{app}/business-analyse/v{version}/ba-interactive.html
|
|
1133
|
+
Pre-populated with: {stakeholder_count} stakeholders, {module_count} modules,
|
|
1134
|
+
{total_uc} use cases, {total_br} business rules, {total_entity} entities
|
|
941
1135
|
Open in browser to review and edit the business analysis.
|
|
942
1136
|
Export JSON and re-import with: /business-analyse -x <exported-json-path>
|
|
943
1137
|
```
|
|
944
1138
|
|
|
945
|
-
**Why
|
|
946
|
-
-
|
|
947
|
-
-
|
|
1139
|
+
**Why a FINAL deployment at handoff?**
|
|
1140
|
+
- Step 03 already deploys the HTML incrementally after each module (partial data)
|
|
1141
|
+
- This final deployment adds the COMPLETE data: all modules + consolidation + handoff info
|
|
1142
|
+
- The client sees the FULL analysis pre-populated — including cross-module interactions and E2E flows
|
|
1143
|
+
- The client can review, edit, and enrich directly in the browser
|
|
948
1144
|
- Any client modifications can be re-imported via `-x` extraction mode
|
|
949
1145
|
- The HTML is standalone (no server required) with localStorage persistence
|
|
1146
|
+
- On first open: pre-populated data displays. After client edits: localStorage overrides
|
|
1147
|
+
- **NOTE:** This overwrites the incremental HTML from step-03 with the complete version
|
|
950
1148
|
|
|
951
1149
|
---
|
|
952
1150
|
|
|
@@ -1192,7 +1390,7 @@ Before presenting handoff to user:
|
|
|
1192
1390
|
- [ ] feature.json updated: handoff section + status "handed-off"
|
|
1193
1391
|
- [ ] All paths use project namespace from .smartstack/config.json
|
|
1194
1392
|
- [ ] No invented requirements (everything traced to feature.json)
|
|
1195
|
-
- [ ] ba-interactive.html deployed with
|
|
1393
|
+
- [ ] ba-interactive.html deployed PRE-POPULATED with all analysis data (not empty)
|
|
1196
1394
|
- [ ] BA manifest (docs/business/index.json) updated with current analysis entries
|
|
1197
1395
|
- [ ] User ready for next agent selection
|
|
1198
1396
|
|
|
@@ -38,13 +38,25 @@ fi
|
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
40
|
CURRENT=$(git rev-parse --abbrev-ref HEAD)
|
|
41
|
+
BRANCH_TYPE=$(echo $CURRENT | cut -d'/' -f1)
|
|
42
|
+
|
|
43
|
+
# CRITICAL: Verify config was loaded
|
|
44
|
+
if [ -z "$GF_MAIN_BRANCH" ] || [ -z "$GF_DEVELOP_BRANCH" ]; then
|
|
45
|
+
echo "❌ GitFlow config not loaded properly"
|
|
46
|
+
echo "→ Variables GF_MAIN_BRANCH and GF_DEVELOP_BRANCH are empty"
|
|
47
|
+
exit 1
|
|
48
|
+
fi
|
|
41
49
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
# Determine target branch based on type (using config values)
|
|
51
|
+
case "$BRANCH_TYPE" in
|
|
52
|
+
feature) TARGET_BRANCH="$GF_DEVELOP_BRANCH" ;;
|
|
53
|
+
release) TARGET_BRANCH="$GF_MAIN_BRANCH" ;;
|
|
54
|
+
hotfix) TARGET_BRANCH="$GF_MAIN_BRANCH" ;;
|
|
55
|
+
*) TARGET_BRANCH="$GF_DEVELOP_BRANCH" ;;
|
|
47
56
|
esac
|
|
57
|
+
|
|
58
|
+
echo "✓ Branch: $CURRENT (type: $BRANCH_TYPE)"
|
|
59
|
+
echo "✓ Target: $TARGET_BRANCH"
|
|
48
60
|
```
|
|
49
61
|
|
|
50
62
|
### 3. Check Prerequisites
|