@atlashub/smartstack-cli 4.38.0 → 4.40.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 +20 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/agents/efcore/migration.md +17 -18
- package/templates/agents/efcore/rebase-snapshot.md +106 -23
- package/templates/agents/efcore/squash.md +131 -33
- package/templates/agents/gitflow/cleanup.md +17 -6
- package/templates/skills/dev-start/SKILL.md +60 -27
- package/templates/skills/efcore/SKILL.md +55 -12
- package/templates/skills/efcore/references/database-operations.md +2 -3
- package/templates/skills/efcore/references/seed-methods.md +1 -1
- package/templates/skills/efcore/references/shared-init-functions.md +8 -8
- package/templates/skills/efcore/references/zero-downtime-patterns.md +2 -0
- package/templates/skills/gitflow/phases/cleanup.md +33 -3
|
@@ -24,6 +24,7 @@ Parse `$ARGUMENTS` to determine the command:
|
|
|
24
24
|
| `migration` | **Agent** `efcore-migration` | sonnet |
|
|
25
25
|
| `squash` | **Agent** `efcore-squash` | sonnet |
|
|
26
26
|
| `rebase-snapshot` | **Agent** `efcore-rebase-snapshot` | sonnet |
|
|
27
|
+
| (none) | **Inline** `<command-help>` | — |
|
|
27
28
|
|
|
28
29
|
**Inline commands:** Execute directly following the matching `<command-*>` section below.
|
|
29
30
|
**Agent commands:** Delegate via Agent tool:
|
|
@@ -31,11 +32,19 @@ Parse `$ARGUMENTS` to determine the command:
|
|
|
31
32
|
Agent(
|
|
32
33
|
subagent_type: "{agent_name}",
|
|
33
34
|
model: "sonnet",
|
|
34
|
-
prompt: "Execute /efcore {command}
|
|
35
|
+
prompt: "Execute /efcore {command}.
|
|
36
|
+
Working directory: {cwd}
|
|
37
|
+
Branch: {branch}
|
|
38
|
+
Context: {--context value if provided, else 'auto-detect'}
|
|
39
|
+
Force: {true if --force flag, else false}
|
|
40
|
+
Description: {user description if provided}
|
|
41
|
+
"
|
|
35
42
|
)
|
|
36
43
|
```
|
|
37
44
|
|
|
38
|
-
**Flags:**
|
|
45
|
+
**Flags:**
|
|
46
|
+
- `--context <name>` — Force DbContext (`CoreDbContext` or `ExtensionsDbContext`), skip auto-detection
|
|
47
|
+
- `--force` — Skip all confirmations (backup still created)
|
|
39
48
|
|
|
40
49
|
</routing>
|
|
41
50
|
|
|
@@ -91,6 +100,8 @@ Pattern: `{context}_v{version}_{sequence}_{Description}`
|
|
|
91
100
|
|
|
92
101
|
**READ-ONLY: Never modify database or migrations.**
|
|
93
102
|
|
|
103
|
+
> **Troubleshooting:** See `references/troubleshooting.md` for common errors.
|
|
104
|
+
|
|
94
105
|
1. Call MCP `mcp__smartstack__check_migrations` with `branch: {current_branch}`
|
|
95
106
|
2. If MCP unavailable, fallback: `dotnet ef migrations list --no-build --context {Context}` per context
|
|
96
107
|
3. Display compact summary
|
|
@@ -117,11 +128,14 @@ BRANCH: {branch}
|
|
|
117
128
|
|
|
118
129
|
**ONLY runs `dotnet ef database update`. Never creates/deletes migrations.**
|
|
119
130
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
131
|
+
> **Reference:** See `references/database-operations.md` for connection test and migration count utilities.
|
|
132
|
+
|
|
133
|
+
1. Detect projects: `INFRA_PROJECT=$(find src -name "*Infrastructure.csproj" | head -1)` and `STARTUP_PROJECT=$(find src -name "*.Api.csproj" | head -1)`
|
|
134
|
+
2. Verify `appsettings.Local.json` exists in the API project
|
|
135
|
+
3. Apply Core: `dotnet ef database update --context CoreDbContext --project "$INFRA_PROJECT" --startup-project "$STARTUP_PROJECT" --verbose`
|
|
136
|
+
4. Apply Extensions: `dotnet ef database update --context ExtensionsDbContext --project "$INFRA_PROJECT" --startup-project "$STARTUP_PROJECT" --verbose`
|
|
123
137
|
|
|
124
|
-
If connection fails: suggest verifying SQL Server, appsettings.Local.json, or `/efcore db-reset`.
|
|
138
|
+
If connection fails: suggest verifying SQL Server, appsettings.Local.json, or `/efcore db-reset`. See `references/troubleshooting.md`.
|
|
125
139
|
|
|
126
140
|
```
|
|
127
141
|
DB DEPLOY
|
|
@@ -138,15 +152,18 @@ DB DEPLOY
|
|
|
138
152
|
|
|
139
153
|
**ONLY operates on database. Never creates/deletes migration files.**
|
|
140
154
|
|
|
141
|
-
|
|
155
|
+
> **Reference:** See `references/reset-operations.md` for drop/recreate commands.
|
|
156
|
+
|
|
157
|
+
1. Detect projects: `INFRA_PROJECT=$(find src -name "*Infrastructure.csproj" | head -1)` and `STARTUP_PROJECT=$(find src -name "*.Api.csproj" | head -1)`
|
|
158
|
+
2. **Confirm** (MANDATORY unless `--force`):
|
|
142
159
|
```
|
|
143
160
|
AskUserQuestion({ question: "DELETE the database? All data will be lost.", options: ["Yes, delete", "No, cancel"] })
|
|
144
161
|
```
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
162
|
+
3. **Backup** (optional): `sqlcmd -S "$SERVER" -E -Q "BACKUP DATABASE [$DB] TO DISK='$FILE' WITH FORMAT, INIT, COMPRESSION"`
|
|
163
|
+
4. **Drop**: `dotnet ef database drop --force --project "$INFRA_PROJECT" --startup-project "$STARTUP_PROJECT"`
|
|
164
|
+
5. **Recreate Core**: `dotnet ef database update --context CoreDbContext --project "$INFRA_PROJECT" --startup-project "$STARTUP_PROJECT"`
|
|
165
|
+
6. **Recreate Extensions**: `dotnet ef database update --context ExtensionsDbContext --project "$INFRA_PROJECT" --startup-project "$STARTUP_PROJECT"`
|
|
166
|
+
7. **Ask** if user wants `/efcore db-seed`
|
|
150
167
|
|
|
151
168
|
Block if ASPNETCORE_ENVIRONMENT=Production.
|
|
152
169
|
|
|
@@ -167,6 +184,8 @@ Next: /efcore db-status, /efcore db-seed
|
|
|
167
184
|
|
|
168
185
|
**Never drop or recreate database.**
|
|
169
186
|
|
|
187
|
+
> **Reference:** See `references/seed-methods.md` for detection and execution details.
|
|
188
|
+
|
|
170
189
|
1. **Detect** seeding method:
|
|
171
190
|
- `HasData()` in EF configs → apply via `dotnet ef database update --context {Context}`
|
|
172
191
|
- `IDataSeeder` / `--seed` CLI arg → `dotnet run --project {startup} -- --seed`
|
|
@@ -250,3 +269,27 @@ If conflict:
|
|
|
250
269
|
```
|
|
251
270
|
|
|
252
271
|
</command-conflicts>
|
|
272
|
+
|
|
273
|
+
<command-help>
|
|
274
|
+
|
|
275
|
+
## (no command) — Show available commands
|
|
276
|
+
|
|
277
|
+
Display the list of available `/efcore` commands:
|
|
278
|
+
|
|
279
|
+
```
|
|
280
|
+
EF CORE COMMANDS
|
|
281
|
+
────────────────
|
|
282
|
+
db-status Migration state check
|
|
283
|
+
db-deploy Apply pending migrations
|
|
284
|
+
db-reset Drop + recreate database
|
|
285
|
+
db-seed Populate test data
|
|
286
|
+
scan Cross-branch migration scanner
|
|
287
|
+
conflicts Migration conflict analyzer
|
|
288
|
+
migration Create/recreate migration
|
|
289
|
+
squash Consolidate migrations
|
|
290
|
+
rebase-snapshot Resync ModelSnapshot with parent
|
|
291
|
+
|
|
292
|
+
Usage: /efcore <command> [--context <name>] [--force]
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
</command-help>
|
|
@@ -7,8 +7,7 @@
|
|
|
7
7
|
```bash
|
|
8
8
|
# Validate required variables from step-00-init
|
|
9
9
|
for VAR_NAME in DBCONTEXT DBCONTEXT_TYPE INFRA_PROJECT STARTUP_PROJECT SELECTED_ENV; do
|
|
10
|
-
|
|
11
|
-
if [ -z "$VAR_VALUE" ]; then
|
|
10
|
+
if [ -z "${!VAR_NAME}" ]; then
|
|
12
11
|
echo "ERROR: Required variable $VAR_NAME is not set"
|
|
13
12
|
echo "Run step-00-init first"
|
|
14
13
|
exit 1
|
|
@@ -38,7 +37,7 @@ fi
|
|
|
38
37
|
## Connection Test
|
|
39
38
|
|
|
40
39
|
```bash
|
|
41
|
-
CONNECTION_TEST=$(dotnet ef
|
|
40
|
+
CONNECTION_TEST=$(dotnet ef migrations list \
|
|
42
41
|
--context "$DBCONTEXT" \
|
|
43
42
|
--project "$INFRA_PROJECT" \
|
|
44
43
|
--startup-project "$STARTUP_PROJECT" 2>&1)
|
|
@@ -27,7 +27,7 @@ if grep -q "\-\-seed" ./src/*/Program.cs 2>/dev/null; then
|
|
|
27
27
|
fi
|
|
28
28
|
|
|
29
29
|
# WARNING: SQL scripts detected
|
|
30
|
-
if [ -f "./scripts/seed.sql" ] || find
|
|
30
|
+
if [ -f "./scripts/seed.sql" ] || find src -name "seed*.sql" -not -path "*/bin/*" -not -path "*/obj/*" 2>/dev/null | grep -q .; then
|
|
31
31
|
echo ""
|
|
32
32
|
echo "WARNING: SQL seed scripts detected - FORBIDDEN by conventions"
|
|
33
33
|
echo "Migrate to HasData() or IDataSeeder"
|
|
@@ -58,7 +58,7 @@ the .NET SDK may not be found even if `dotnet-ef` is. Use `$USERPROFILE` first o
|
|
|
58
58
|
```bash
|
|
59
59
|
detect_efcore_project() {
|
|
60
60
|
# Find project with EF Core reference
|
|
61
|
-
CSPROJ=$(find
|
|
61
|
+
CSPROJ=$(find src-name "*.csproj" -exec grep -l "Microsoft.EntityFrameworkCore" {} \; | head -1)
|
|
62
62
|
|
|
63
63
|
if [ -z "$CSPROJ" ]; then
|
|
64
64
|
echo "ERROR: No EF Core project found"
|
|
@@ -70,8 +70,8 @@ detect_efcore_project() {
|
|
|
70
70
|
MIGRATIONS_DIR="$PROJECT_DIR/Persistence/Migrations"
|
|
71
71
|
|
|
72
72
|
# Find startup and infrastructure projects
|
|
73
|
-
STARTUP_PROJECT=$(find
|
|
74
|
-
INFRA_PROJECT=$(find
|
|
73
|
+
STARTUP_PROJECT=$(find src-name "*.Api.csproj" -o -name "*Web.csproj" | head -1)
|
|
74
|
+
INFRA_PROJECT=$(find src-name "*Infrastructure.csproj" | head -1)
|
|
75
75
|
[ -z "$INFRA_PROJECT" ] && INFRA_PROJECT="$CSPROJ"
|
|
76
76
|
|
|
77
77
|
echo "Project: $PROJECT_NAME"
|
|
@@ -84,7 +84,7 @@ detect_efcore_project() {
|
|
|
84
84
|
```bash
|
|
85
85
|
detect_dbcontext() {
|
|
86
86
|
# Priority 1: SmartStack.Domain exists → SmartStack source project (Core only)
|
|
87
|
-
if find
|
|
87
|
+
if find src-type d -name "SmartStack.Domain" | grep -q .; then
|
|
88
88
|
DBCONTEXT="CoreDbContext"
|
|
89
89
|
DBCONTEXT_TYPE="core"
|
|
90
90
|
SCHEMA="core"
|
|
@@ -94,7 +94,7 @@ detect_dbcontext() {
|
|
|
94
94
|
fi
|
|
95
95
|
|
|
96
96
|
# Priority 2: Client project with SmartStack NuGet → BOTH contexts
|
|
97
|
-
if find
|
|
97
|
+
if find src-name "*.csproj" -exec grep -ql "PackageReference.*SmartStack" {} \; 2>/dev/null; then
|
|
98
98
|
DBCONTEXT="CoreDbContext"
|
|
99
99
|
DBCONTEXT_TYPE="both"
|
|
100
100
|
SCHEMA="core"
|
|
@@ -105,8 +105,8 @@ detect_dbcontext() {
|
|
|
105
105
|
fi
|
|
106
106
|
|
|
107
107
|
# Priority 3: Scan for DbContext in code
|
|
108
|
-
CORE_CTX=$(find
|
|
109
|
-
EXT_CTX=$(find
|
|
108
|
+
CORE_CTX=$(find src-name "*.cs" -exec grep -l "CoreDbContext" {} \; 2>/dev/null | head -1)
|
|
109
|
+
EXT_CTX=$(find src-name "*.cs" -exec grep -l "ExtensionsDbContext" {} \; 2>/dev/null | head -1)
|
|
110
110
|
|
|
111
111
|
if [ -n "$CORE_CTX" ] && [ -n "$EXT_CTX" ]; then
|
|
112
112
|
DBCONTEXT="CoreDbContext"
|
|
@@ -137,7 +137,7 @@ detect_dbcontext() {
|
|
|
137
137
|
|
|
138
138
|
```bash
|
|
139
139
|
detect_environment() {
|
|
140
|
-
API_DIR=$(find
|
|
140
|
+
API_DIR=$(find src-type d -name "*.Api" | head -1)
|
|
141
141
|
[ -z "$API_DIR" ] && API_DIR="src/SmartStack.Api"
|
|
142
142
|
|
|
143
143
|
# Priority: --env flag > appsettings.Local.json > error
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Zero-downtime migration patterns for EF Core. Use when a migration contains destructive operations (column rename, type change, column drop) that could cause downtime during deployment.
|
|
3
3
|
|
|
4
4
|
These patterns apply to production deployments where the application runs continuously. For development environments, standard migrations are sufficient.
|
|
5
|
+
|
|
6
|
+
> **Exception to "no raw SQL" rule:** `migrationBuilder.Sql(...)` is authorized ONLY for data backfill operations within zero-downtime migration patterns (e.g., `UPDATE ... SET NewCol = OldCol`). Never use it for DDL (CREATE/ALTER/DROP) — those must go through EF Core Fluent API or `SqlObjects/`.
|
|
5
7
|
</overview>
|
|
6
8
|
|
|
7
9
|
<when_to_use>
|
|
@@ -60,9 +60,21 @@ while read -r line; do
|
|
|
60
60
|
continue
|
|
61
61
|
fi
|
|
62
62
|
|
|
63
|
+
# Skip permanent branches
|
|
64
|
+
[[ "$WT_BRANCH" == "$GF_MAIN_BRANCH" ]] || [[ "$WT_BRANCH" == "$GF_DEVELOP_BRANCH" ]] && {
|
|
65
|
+
VALID+=("$WT_PATH:$WT_BRANCH")
|
|
66
|
+
continue
|
|
67
|
+
}
|
|
68
|
+
|
|
63
69
|
# Check if branch still exists on remote
|
|
64
70
|
REMOTE_EXISTS=$(git branch -r --list "origin/$WT_BRANCH" | wc -l)
|
|
65
|
-
|
|
71
|
+
|
|
72
|
+
# Check if branch is merged into develop or main
|
|
73
|
+
MERGED_INTO_DEVELOP=$(git branch --merged "$GF_DEVELOP_BRANCH" | grep -c "$WT_BRANCH" 2>/dev/null || echo 0)
|
|
74
|
+
MERGED_INTO_MAIN=$(git branch --merged "$GF_MAIN_BRANCH" | grep -c "$WT_BRANCH" 2>/dev/null || echo 0)
|
|
75
|
+
|
|
76
|
+
# Stale if: no remote OR already merged
|
|
77
|
+
if [ "$REMOTE_EXISTS" -eq 0 ] || [ "$MERGED_INTO_DEVELOP" -gt 0 ] || [ "$MERGED_INTO_MAIN" -gt 0 ]; then
|
|
66
78
|
STALE+=("$WT_PATH:$WT_BRANCH")
|
|
67
79
|
continue
|
|
68
80
|
fi
|
|
@@ -166,10 +178,14 @@ AskUserQuestion:
|
|
|
166
178
|
# Remove orphaned
|
|
167
179
|
for item in "${ORPHANED[@]}"; do
|
|
168
180
|
WT_PATH=$(echo "$item" | cut -d':' -f1)
|
|
181
|
+
WT_BRANCH=$(echo "$item" | cut -d':' -f2)
|
|
169
182
|
git worktree remove "$WT_PATH" --force 2>/dev/null || rm -rf "$WT_PATH"
|
|
183
|
+
# Delete remote branch if exists
|
|
184
|
+
git push origin --delete "$WT_BRANCH" 2>/dev/null
|
|
185
|
+
git branch -D "$WT_BRANCH" 2>/dev/null
|
|
170
186
|
done
|
|
171
187
|
|
|
172
|
-
# Prune
|
|
188
|
+
# Prune stale worktree references
|
|
173
189
|
git worktree prune
|
|
174
190
|
|
|
175
191
|
# Remove stale
|
|
@@ -178,15 +194,29 @@ for item in "${STALE[@]}"; do
|
|
|
178
194
|
WT_BRANCH=$(echo "$item" | cut -d':' -f2)
|
|
179
195
|
|
|
180
196
|
git worktree remove "$WT_PATH" --force 2>/dev/null || rm -rf "$WT_PATH"
|
|
197
|
+
# Delete remote branch if exists
|
|
198
|
+
git push origin --delete "$WT_BRANCH" 2>/dev/null
|
|
181
199
|
git branch -D "$WT_BRANCH" 2>/dev/null
|
|
182
200
|
done
|
|
201
|
+
|
|
202
|
+
# Final prune after all removals
|
|
203
|
+
git worktree prune
|
|
183
204
|
```
|
|
184
205
|
|
|
185
206
|
**Branches:**
|
|
186
207
|
```bash
|
|
187
|
-
# Delete merged branches
|
|
208
|
+
# Delete merged branches (local + remote)
|
|
188
209
|
for branch in "${MERGED_BRANCHES[@]}"; do
|
|
189
210
|
git branch -d "$branch"
|
|
211
|
+
git push origin --delete "$branch" 2>/dev/null
|
|
212
|
+
done
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**Empty parent directories:**
|
|
216
|
+
```bash
|
|
217
|
+
# Clean up empty worktree parent directories
|
|
218
|
+
for dir in "$GF_FEATURES_DIR" "$GF_RELEASES_DIR" "$GF_HOTFIXES_DIR"; do
|
|
219
|
+
[ -d "$dir" ] && [ -z "$(ls -A "$dir" 2>/dev/null)" ] && rm -rf "$dir"
|
|
190
220
|
done
|
|
191
221
|
```
|
|
192
222
|
|