@atlashub/smartstack-cli 3.35.0 → 3.36.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 +3 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp-entry.mjs +111 -46
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -1
- package/templates/skills/apex/SKILL.md +3 -0
- package/templates/skills/apex/steps/step-00-init.md +4 -1
- package/templates/skills/apex/steps/step-03-execute.md +22 -0
- package/templates/skills/documentation/SKILL.md +175 -9
- package/templates/skills/efcore/steps/squash/step-03-create.md +6 -4
- package/templates/skills/gitflow/_shared.md +3 -1
- package/templates/skills/gitflow/steps/step-pr.md +34 -0
- package/templates/skills/ralph-loop/SKILL.md +26 -2
- package/templates/skills/ralph-loop/references/team-orchestration.md +331 -14
- package/templates/skills/ralph-loop/steps/step-00-init.md +4 -0
- package/templates/skills/ralph-loop/steps/step-02-execute.md +163 -2
package/dist/mcp-entry.mjs
CHANGED
|
@@ -26396,6 +26396,7 @@ var init_config = __esm({
|
|
|
26396
26396
|
],
|
|
26397
26397
|
customTablePrefixes: [],
|
|
26398
26398
|
scopeTypes: ["Core", "Extension", "Partner", "Community"],
|
|
26399
|
+
// Incremental: {context}_v{version}_{sequence}_{Description} | Squash: {context}_v{version}
|
|
26399
26400
|
migrationFormat: "{context}_v{version}_{sequence}_{Description}",
|
|
26400
26401
|
namespaces: {
|
|
26401
26402
|
// Empty = auto-detect from .csproj files
|
|
@@ -26847,32 +26848,39 @@ async function validateMigrationNaming(structure, _config, result) {
|
|
|
26847
26848
|
return;
|
|
26848
26849
|
}
|
|
26849
26850
|
const migrationFiles = await findFiles("*.cs", { cwd: structure.migrations });
|
|
26850
|
-
const
|
|
26851
|
+
const incrementalPattern = /^(\w+)_v(\d+\.\d+\.\d+)_(\d{3})_(.+)\.cs$/;
|
|
26852
|
+
const squashPattern = /^(\w+)_v(\d+\.\d+\.\d+)\.cs$/;
|
|
26851
26853
|
const designerPattern = /\.Designer\.cs$/;
|
|
26852
26854
|
for (const file of migrationFiles) {
|
|
26853
26855
|
const fileName = path8.basename(file);
|
|
26854
26856
|
if (designerPattern.test(fileName) || fileName.includes("ModelSnapshot")) {
|
|
26855
26857
|
continue;
|
|
26856
26858
|
}
|
|
26857
|
-
if (!
|
|
26859
|
+
if (!incrementalPattern.test(fileName) && !squashPattern.test(fileName)) {
|
|
26858
26860
|
result.errors.push({
|
|
26859
26861
|
type: "error",
|
|
26860
26862
|
category: "migrations",
|
|
26861
26863
|
message: `Migration "${fileName}" does not follow naming convention`,
|
|
26862
26864
|
file: path8.relative(structure.root, file),
|
|
26863
|
-
suggestion: `Expected format: {context}_v{version}_{sequence}_{Description}.cs (
|
|
26865
|
+
suggestion: `Expected format: {context}_v{version}_{sequence}_{Description}.cs (incremental) or {context}_v{version}.cs (squash)`
|
|
26864
26866
|
});
|
|
26865
26867
|
}
|
|
26866
26868
|
}
|
|
26867
|
-
const
|
|
26869
|
+
const isValidMigration = (f) => (incrementalPattern.test(f) || squashPattern.test(f)) && !f.includes("Designer");
|
|
26870
|
+
const getVersion = (f) => {
|
|
26871
|
+
const inc = incrementalPattern.exec(f);
|
|
26872
|
+
if (inc) return inc[2];
|
|
26873
|
+
const sq = squashPattern.exec(f);
|
|
26874
|
+
if (sq) return sq[2];
|
|
26875
|
+
return "0.0.0";
|
|
26876
|
+
};
|
|
26877
|
+
const orderedMigrations = migrationFiles.map((f) => path8.basename(f)).filter(isValidMigration).sort();
|
|
26868
26878
|
for (let i = 1; i < orderedMigrations.length; i++) {
|
|
26869
26879
|
const prev = orderedMigrations[i - 1];
|
|
26870
26880
|
const curr = orderedMigrations[i];
|
|
26871
|
-
const
|
|
26872
|
-
const
|
|
26873
|
-
if (
|
|
26874
|
-
const prevVersion = prevMatch[2];
|
|
26875
|
-
const currVersion = currMatch[2];
|
|
26881
|
+
const prevVersion = getVersion(prev);
|
|
26882
|
+
const currVersion = getVersion(curr);
|
|
26883
|
+
if (prevVersion !== "0.0.0" && currVersion !== "0.0.0") {
|
|
26876
26884
|
if (currVersion < prevVersion) {
|
|
26877
26885
|
result.warnings.push({
|
|
26878
26886
|
type: "warning",
|
|
@@ -28263,26 +28271,34 @@ async function handleCheckMigrations(args, config2) {
|
|
|
28263
28271
|
async function parseMigrations(migrationsPath, rootPath) {
|
|
28264
28272
|
const files = await findFiles("*.cs", { cwd: migrationsPath });
|
|
28265
28273
|
const migrations = [];
|
|
28266
|
-
const
|
|
28274
|
+
const incrementalPattern = /^(\w+)_v(\d+\.\d+\.\d+)_(\d{3})_(.+)\.cs$/;
|
|
28275
|
+
const squashPattern = /^(\w+)_v(\d+\.\d+\.\d+)\.cs$/;
|
|
28267
28276
|
for (const file of files) {
|
|
28268
28277
|
const fileName = path9.basename(file);
|
|
28269
28278
|
if (fileName.includes(".Designer.") || fileName.includes("ModelSnapshot")) {
|
|
28270
28279
|
continue;
|
|
28271
28280
|
}
|
|
28272
|
-
const
|
|
28273
|
-
|
|
28281
|
+
const incrementalMatch = incrementalPattern.exec(fileName);
|
|
28282
|
+
const squashMatch = squashPattern.exec(fileName);
|
|
28283
|
+
if (incrementalMatch) {
|
|
28284
|
+
migrations.push({
|
|
28285
|
+
name: fileName.replace(".cs", ""),
|
|
28286
|
+
context: incrementalMatch[1],
|
|
28287
|
+
version: incrementalMatch[2],
|
|
28288
|
+
sequence: incrementalMatch[3],
|
|
28289
|
+
description: incrementalMatch[4],
|
|
28290
|
+
file: path9.relative(rootPath, file),
|
|
28291
|
+
applied: true
|
|
28292
|
+
});
|
|
28293
|
+
} else if (squashMatch) {
|
|
28274
28294
|
migrations.push({
|
|
28275
28295
|
name: fileName.replace(".cs", ""),
|
|
28276
|
-
context:
|
|
28277
|
-
|
|
28278
|
-
|
|
28279
|
-
|
|
28280
|
-
sequence: match2[3],
|
|
28281
|
-
// Sequence number (001, 002, etc.)
|
|
28282
|
-
description: match2[4],
|
|
28296
|
+
context: squashMatch[1],
|
|
28297
|
+
version: squashMatch[2],
|
|
28298
|
+
sequence: "000",
|
|
28299
|
+
description: "Squash",
|
|
28283
28300
|
file: path9.relative(rootPath, file),
|
|
28284
28301
|
applied: true
|
|
28285
|
-
// We'd need DB connection to check this
|
|
28286
28302
|
});
|
|
28287
28303
|
} else {
|
|
28288
28304
|
migrations.push({
|
|
@@ -28309,7 +28325,7 @@ function checkNamingConventions(result, _config) {
|
|
|
28309
28325
|
type: "naming",
|
|
28310
28326
|
description: `Migration "${migration.name}" does not follow naming convention`,
|
|
28311
28327
|
files: [migration.file],
|
|
28312
|
-
resolution: `Rename to format: {context}_v{version}_{sequence}_{Description} (
|
|
28328
|
+
resolution: `Rename to format: {context}_v{version}_{sequence}_{Description} (incremental) or {context}_v{version} (squash)`
|
|
28313
28329
|
});
|
|
28314
28330
|
}
|
|
28315
28331
|
if (migration.version === "0.0.0") {
|
|
@@ -28317,7 +28333,7 @@ function checkNamingConventions(result, _config) {
|
|
|
28317
28333
|
type: "naming",
|
|
28318
28334
|
description: `Migration "${migration.name}" missing version number`,
|
|
28319
28335
|
files: [migration.file],
|
|
28320
|
-
resolution: `Use format: {context}_v{version}_{sequence}_{Description} where version is semver (1.0.0, 1.2.0, etc.)`
|
|
28336
|
+
resolution: `Use format: {context}_v{version}_{sequence}_{Description} (incremental) or {context}_v{version} (squash) where version is semver (1.0.0, 1.2.0, etc.)`
|
|
28321
28337
|
});
|
|
28322
28338
|
}
|
|
28323
28339
|
if (migration.version !== "0.0.0" && !parseSemver(migration.version)) {
|
|
@@ -28425,7 +28441,7 @@ function generateSuggestions(result) {
|
|
|
28425
28441
|
}
|
|
28426
28442
|
if (result.conflicts.some((c) => c.type === "naming")) {
|
|
28427
28443
|
result.suggestions.push(
|
|
28428
|
-
"Use convention: {context}_v{version}_{sequence}_{Description} for
|
|
28444
|
+
"Use convention: {context}_v{version}_{sequence}_{Description} for incremental migrations (e.g., core_v1.0.0_001_CreateAuthUsers) or {context}_v{version} for squash migrations (e.g., core_v1.0.0)"
|
|
28429
28445
|
);
|
|
28430
28446
|
}
|
|
28431
28447
|
if (result.conflicts.some((c) => c.type === "order")) {
|
|
@@ -52779,19 +52795,24 @@ async function handleSuggestMigration(args, config2) {
|
|
|
52779
52795
|
} else {
|
|
52780
52796
|
version2 = version2 || "1.0.0";
|
|
52781
52797
|
}
|
|
52782
|
-
|
|
52783
|
-
if (
|
|
52784
|
-
|
|
52798
|
+
let migrationName;
|
|
52799
|
+
if (input.squash) {
|
|
52800
|
+
migrationName = `${context}_v${version2}`;
|
|
52801
|
+
} else {
|
|
52802
|
+
const pascalDescription = toPascalCase(sanitizedDescription);
|
|
52803
|
+
if (!pascalDescription || !/^[A-Z][a-zA-Z0-9]*$/.test(pascalDescription)) {
|
|
52804
|
+
throw new Error(`Invalid migration description after PascalCase conversion: "${pascalDescription}"`);
|
|
52805
|
+
}
|
|
52806
|
+
const sequenceStr = sequence.toString().padStart(3, "0");
|
|
52807
|
+
migrationName = `${context}_v${version2}_${sequenceStr}_${pascalDescription}`;
|
|
52785
52808
|
}
|
|
52786
|
-
const sequenceStr = sequence.toString().padStart(3, "0");
|
|
52787
|
-
const migrationName = `${context}_v${version2}_${sequenceStr}_${pascalDescription}`;
|
|
52788
52809
|
const dbContextName = context === "core" ? "CoreDbContext" : "ExtensionsDbContext";
|
|
52789
52810
|
const outputPath = context === "extensions" ? "Persistence/Migrations/Extensions" : "Persistence/Migrations";
|
|
52790
52811
|
const command = `dotnet ef migrations add ${migrationName} --context ${dbContextName} --project ../SmartStack.Infrastructure -o ${outputPath}`;
|
|
52791
52812
|
const lines = [];
|
|
52792
52813
|
lines.push("# Migration Name Suggestion");
|
|
52793
52814
|
lines.push("");
|
|
52794
|
-
lines.push(
|
|
52815
|
+
lines.push(`## Suggested Name (${input.squash ? "Squash" : "Incremental"})`);
|
|
52795
52816
|
lines.push("```");
|
|
52796
52817
|
lines.push(migrationName);
|
|
52797
52818
|
lines.push("```");
|
|
@@ -52809,8 +52830,13 @@ async function handleSuggestMigration(args, config2) {
|
|
|
52809
52830
|
lines.push(`| DbContext | \`${dbContextName}\` | EF Core DbContext to use |`);
|
|
52810
52831
|
lines.push(`| Schema | \`${context}\` | Database schema for tables |`);
|
|
52811
52832
|
lines.push(`| Version | \`v${version2}\` | Semver version |`);
|
|
52812
|
-
|
|
52813
|
-
|
|
52833
|
+
if (!input.squash) {
|
|
52834
|
+
const sequenceStr = sequence.toString().padStart(3, "0");
|
|
52835
|
+
lines.push(`| Sequence | \`${sequenceStr}\` | Order in version |`);
|
|
52836
|
+
lines.push(`| Description | \`${toPascalCase(sanitizedDescription)}\` | Migration description |`);
|
|
52837
|
+
} else {
|
|
52838
|
+
lines.push(`| Mode | \`squash\` | Consolidated baseline (no sequence/description) |`);
|
|
52839
|
+
}
|
|
52814
52840
|
lines.push("");
|
|
52815
52841
|
lines.push("> **Note**: Migrations are stored in separate history tables:");
|
|
52816
52842
|
lines.push(`> - Core: \`core.__EFMigrationsHistory\``);
|
|
@@ -52832,15 +52858,17 @@ async function findExistingMigrations(structure, config2, context) {
|
|
|
52832
52858
|
const migrationsPath = path12.join(infraPath, "Persistence", "Migrations");
|
|
52833
52859
|
try {
|
|
52834
52860
|
const migrationFiles = await findFiles("*.cs", { cwd: migrationsPath });
|
|
52835
|
-
const
|
|
52861
|
+
const incrementalPattern = /^(\w+)_v(\d+\.\d+\.\d+)_(\d+)_(\w+)\.cs$/;
|
|
52862
|
+
const squashPattern = /^(\w+)_v(\d+\.\d+\.\d+)\.cs$/;
|
|
52836
52863
|
for (const file of migrationFiles) {
|
|
52837
52864
|
const fileName = path12.basename(file);
|
|
52838
52865
|
if (fileName.includes(".Designer.") || fileName.includes("ModelSnapshot")) {
|
|
52839
52866
|
continue;
|
|
52840
52867
|
}
|
|
52841
|
-
const
|
|
52842
|
-
|
|
52843
|
-
|
|
52868
|
+
const incrementalMatch = fileName.match(incrementalPattern);
|
|
52869
|
+
const squashMatch = fileName.match(squashPattern);
|
|
52870
|
+
if (incrementalMatch) {
|
|
52871
|
+
const [, ctx, ver, seq, desc] = incrementalMatch;
|
|
52844
52872
|
if (ctx === context || !context) {
|
|
52845
52873
|
migrations.push({
|
|
52846
52874
|
name: fileName.replace(".cs", ""),
|
|
@@ -52850,6 +52878,17 @@ async function findExistingMigrations(structure, config2, context) {
|
|
|
52850
52878
|
description: desc
|
|
52851
52879
|
});
|
|
52852
52880
|
}
|
|
52881
|
+
} else if (squashMatch) {
|
|
52882
|
+
const [, ctx, ver] = squashMatch;
|
|
52883
|
+
if (ctx === context || !context) {
|
|
52884
|
+
migrations.push({
|
|
52885
|
+
name: fileName.replace(".cs", ""),
|
|
52886
|
+
context: ctx,
|
|
52887
|
+
version: ver,
|
|
52888
|
+
sequence: 0,
|
|
52889
|
+
description: "Squash"
|
|
52890
|
+
});
|
|
52891
|
+
}
|
|
52853
52892
|
}
|
|
52854
52893
|
}
|
|
52855
52894
|
migrations.sort((a, b) => {
|
|
@@ -52882,7 +52921,7 @@ var init_suggest_migration = __esm({
|
|
|
52882
52921
|
init_logger();
|
|
52883
52922
|
suggestMigrationTool = {
|
|
52884
52923
|
name: "suggest_migration",
|
|
52885
|
-
description: "Suggest a migration name following SmartStack conventions ({context}_v{version}_{sequence}_{Description})",
|
|
52924
|
+
description: "Suggest a migration name following SmartStack conventions ({context}_v{version}_{sequence}_{Description} for incremental, {context}_v{version} for squash)",
|
|
52886
52925
|
inputSchema: {
|
|
52887
52926
|
type: "object",
|
|
52888
52927
|
properties: {
|
|
@@ -52898,6 +52937,10 @@ var init_suggest_migration = __esm({
|
|
|
52898
52937
|
version: {
|
|
52899
52938
|
type: "string",
|
|
52900
52939
|
description: 'Semver version (e.g., "1.0.0", "1.2.0"). If not provided, uses latest from existing migrations.'
|
|
52940
|
+
},
|
|
52941
|
+
squash: {
|
|
52942
|
+
type: "boolean",
|
|
52943
|
+
description: "If true, generates squash format: {context}_v{version} (no sequence/description). Used before merge to consolidate feature migrations."
|
|
52901
52944
|
}
|
|
52902
52945
|
},
|
|
52903
52946
|
required: ["description"]
|
|
@@ -52906,7 +52949,8 @@ var init_suggest_migration = __esm({
|
|
|
52906
52949
|
SuggestMigrationInputSchema2 = external_exports.object({
|
|
52907
52950
|
description: external_exports.string().min(3, "Migration description must be at least 3 characters").max(100, "Migration description must be at most 100 characters").describe("Description of what the migration does"),
|
|
52908
52951
|
context: external_exports.enum(["core", "extensions"]).optional().describe("DbContext name (default: auto-detected from project config)"),
|
|
52909
|
-
version: external_exports.string().regex(/^\d+\.\d+\.\d+$/, 'Version must be semver format (e.g., "1.0.0")').optional().describe('Semver version (e.g., "1.0.0")')
|
|
52952
|
+
version: external_exports.string().regex(/^\d+\.\d+\.\d+$/, 'Version must be semver format (e.g., "1.0.0")').optional().describe('Semver version (e.g., "1.0.0")'),
|
|
52953
|
+
squash: external_exports.boolean().optional().describe("If true, generates squash format: {context}_v{version} (no sequence/description)")
|
|
52910
52954
|
});
|
|
52911
52955
|
}
|
|
52912
52956
|
});
|
|
@@ -64901,8 +64945,9 @@ builder.HasOne(e => e.User)
|
|
|
64901
64945
|
|
|
64902
64946
|
### Naming Format
|
|
64903
64947
|
|
|
64904
|
-
Migrations
|
|
64948
|
+
Migrations follow two naming patterns depending on context:
|
|
64905
64949
|
|
|
64950
|
+
**Incremental (during development):**
|
|
64906
64951
|
\`\`\`
|
|
64907
64952
|
${migrationFormat}
|
|
64908
64953
|
\`\`\`
|
|
@@ -64914,30 +64959,50 @@ ${migrationFormat}
|
|
|
64914
64959
|
| \`{sequence}\` | Order in version | \`001\`, \`002\` |
|
|
64915
64960
|
| \`{Description}\` | Action (PascalCase) | \`CreateAuthUsers\` |
|
|
64916
64961
|
|
|
64962
|
+
**Squash (before merge, consolidated baseline):**
|
|
64963
|
+
\`\`\`
|
|
64964
|
+
{context}_v{version}
|
|
64965
|
+
\`\`\`
|
|
64966
|
+
|
|
64967
|
+
The squash format has no sequence or description \u2014 it represents the complete state at a version.
|
|
64968
|
+
|
|
64917
64969
|
**Examples:**
|
|
64918
|
-
- \`core_v1.0.0_001_InitialSchema.cs\`
|
|
64919
|
-
- \`core_v1.0.0_002_CreateAuthUsers.cs\`
|
|
64920
|
-
- \`core_v1.2.0_001_AddUserProfiles.cs\`
|
|
64921
|
-
- \`extensions_v1.0.0_001_AddClientFeatures.cs\`
|
|
64970
|
+
- \`core_v1.0.0_001_InitialSchema.cs\` (incremental)
|
|
64971
|
+
- \`core_v1.0.0_002_CreateAuthUsers.cs\` (incremental)
|
|
64972
|
+
- \`core_v1.2.0_001_AddUserProfiles.cs\` (incremental)
|
|
64973
|
+
- \`extensions_v1.0.0_001_AddClientFeatures.cs\` (incremental)
|
|
64974
|
+
- \`core_v1.0.0.cs\` (squash \u2014 consolidated baseline)
|
|
64922
64975
|
|
|
64923
64976
|
### Creating Migrations
|
|
64924
64977
|
|
|
64925
64978
|
\`\`\`bash
|
|
64926
|
-
# Create
|
|
64979
|
+
# Create an incremental migration
|
|
64927
64980
|
dotnet ef migrations add core_v1.0.0_001_InitialSchema
|
|
64928
64981
|
|
|
64929
64982
|
# With context specified
|
|
64930
64983
|
dotnet ef migrations add core_v1.2.0_001_AddUserProfiles --context ApplicationDbContext
|
|
64931
64984
|
\`\`\`
|
|
64932
64985
|
|
|
64986
|
+
### Squash Workflow
|
|
64987
|
+
|
|
64988
|
+
Before creating a PR, squash all feature migrations into one:
|
|
64989
|
+
|
|
64990
|
+
\`\`\`bash
|
|
64991
|
+
# Run /efcore squash \u2014 produces: core_v1.0.0 (no sequence/description)
|
|
64992
|
+
dotnet ef migrations add core_v1.0.0 --context CoreDbContext
|
|
64993
|
+
\`\`\`
|
|
64994
|
+
|
|
64995
|
+
After a squash at \`core_v1.0.0\`, the next incremental migration starts at \`core_v1.0.0_001_*\`.
|
|
64996
|
+
|
|
64933
64997
|
### Migration Rules
|
|
64934
64998
|
|
|
64935
|
-
1. **One migration per feature** -
|
|
64999
|
+
1. **One migration per feature** - Squash before merge to ensure a single migration per feature branch
|
|
64936
65000
|
2. **Version-based naming** - Use semver (v1.0.0, v1.2.0) to link migrations to releases
|
|
64937
|
-
3. **Sequence numbers** - Use NNN (001, 002, etc.) for migrations in the same version
|
|
65001
|
+
3. **Sequence numbers** - Use NNN (001, 002, etc.) for incremental migrations in the same version
|
|
64938
65002
|
4. **Context prefix** - Use \`core_\` for platform tables, \`extensions_\` for client extensions
|
|
64939
|
-
5. **Descriptive names** - Use clear PascalCase descriptions (CreateAuthUsers, AddUserProfiles, etc.)
|
|
65003
|
+
5. **Descriptive names** - Use clear PascalCase descriptions for incremental (CreateAuthUsers, AddUserProfiles, etc.)
|
|
64940
65004
|
6. **Schema must be specified** - All tables must specify their schema in ToTable()
|
|
65005
|
+
7. **Squash before PR** - Feature branches must have exactly 1 migration (squashed) before creating a PR
|
|
64941
65006
|
|
|
64942
65007
|
---
|
|
64943
65008
|
|