@atlashub/smartstack-cli 3.4.1 → 3.5.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 +147 -5
- package/dist/index.js.map +1 -1
- package/dist/mcp-entry.mjs +4 -3
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -1
- package/templates/skills/_shared.md +1 -1
- package/templates/skills/application/steps/step-04-backend.md +4 -4
- package/templates/skills/application/templates-backend.md +4 -4
- package/templates/skills/business-analyse/_architecture.md +3 -3
- package/templates/skills/business-analyse/steps/step-05b-deploy.md +43 -0
- package/templates/skills/business-analyse/templates/tpl-handoff.md +6 -6
- package/templates/skills/ralph-loop/steps/step-01-task.md +249 -6
package/dist/index.js
CHANGED
|
@@ -116146,6 +116146,56 @@ function checkPrerequisites() {
|
|
|
116146
116146
|
return { dotnet: false, dotnetVersion: null };
|
|
116147
116147
|
}
|
|
116148
116148
|
}
|
|
116149
|
+
function isDockerAvailable() {
|
|
116150
|
+
try {
|
|
116151
|
+
const result = (0, import_child_process5.spawnSync)("docker", ["info"], { encoding: "utf-8", shell: true, timeout: 1e4, stdio: "pipe" });
|
|
116152
|
+
return result.status === 0;
|
|
116153
|
+
} catch {
|
|
116154
|
+
return false;
|
|
116155
|
+
}
|
|
116156
|
+
}
|
|
116157
|
+
async function setupMailPitContainer() {
|
|
116158
|
+
const check = (0, import_child_process5.spawnSync)("docker", ["ps", "-a", "--filter", "name=^mailpit$", "--format", "{{.Status}}"], {
|
|
116159
|
+
encoding: "utf-8",
|
|
116160
|
+
shell: true,
|
|
116161
|
+
timeout: 1e4
|
|
116162
|
+
});
|
|
116163
|
+
if (check.stdout?.trim()) {
|
|
116164
|
+
const status = check.stdout.trim();
|
|
116165
|
+
if (status.startsWith("Up")) {
|
|
116166
|
+
logger.success(`MailPit is already running`);
|
|
116167
|
+
} else {
|
|
116168
|
+
logger.info("Starting existing MailPit container...");
|
|
116169
|
+
(0, import_child_process5.spawnSync)("docker", ["start", "mailpit"], { encoding: "utf-8", shell: true, timeout: 15e3 });
|
|
116170
|
+
logger.success("MailPit started");
|
|
116171
|
+
}
|
|
116172
|
+
} else {
|
|
116173
|
+
logger.info("Starting MailPit container...");
|
|
116174
|
+
const run = (0, import_child_process5.spawnSync)("docker", [
|
|
116175
|
+
"run",
|
|
116176
|
+
"-d",
|
|
116177
|
+
"--name",
|
|
116178
|
+
"mailpit",
|
|
116179
|
+
"--restart",
|
|
116180
|
+
"unless-stopped",
|
|
116181
|
+
"-p",
|
|
116182
|
+
"8025:8025",
|
|
116183
|
+
"-p",
|
|
116184
|
+
"1025:1025",
|
|
116185
|
+
"axllent/mailpit"
|
|
116186
|
+
], { encoding: "utf-8", shell: true, timeout: 6e4 });
|
|
116187
|
+
if (run.status === 0) {
|
|
116188
|
+
logger.success("MailPit started successfully");
|
|
116189
|
+
} else {
|
|
116190
|
+
logger.warning(`Failed to start MailPit: ${run.stderr?.trim() || "unknown error"}`);
|
|
116191
|
+
logger.info("You can start it manually later:");
|
|
116192
|
+
logger.info(source_default.cyan(" docker run -d --name mailpit --restart unless-stopped -p 8025:8025 -p 1025:1025 axllent/mailpit"));
|
|
116193
|
+
return;
|
|
116194
|
+
}
|
|
116195
|
+
}
|
|
116196
|
+
logger.info(`SMTP server: ${source_default.cyan("localhost:1025")}`);
|
|
116197
|
+
logger.info(`Web interface: ${source_default.cyan("http://localhost:8025")}`);
|
|
116198
|
+
}
|
|
116149
116199
|
function validateCSharpNamespace(name) {
|
|
116150
116200
|
if (!name || name.trim().length === 0) {
|
|
116151
116201
|
return { valid: false, error: "Project name cannot be empty" };
|
|
@@ -116700,6 +116750,10 @@ EndGlobal
|
|
|
116700
116750
|
SystemTenantName: config.multiTenant.systemTenantName,
|
|
116701
116751
|
AutoAssignUsersToSystemTenant: config.multiTenant.autoAssignUsersToSystemTenant
|
|
116702
116752
|
};
|
|
116753
|
+
appSettings.Email.Provider = config.email.provider;
|
|
116754
|
+
if (config.email.provider === "Disabled") {
|
|
116755
|
+
appSettings.Email.Enabled = false;
|
|
116756
|
+
}
|
|
116703
116757
|
const appSettingsRelPath = `src/${projectName}.Api/appsettings.json`;
|
|
116704
116758
|
const appSettingsResult = await safeWriteFile(
|
|
116705
116759
|
(0, import_path6.join)(apiDir2, "appsettings.json"),
|
|
@@ -116718,7 +116772,7 @@ EndGlobal
|
|
|
116718
116772
|
}
|
|
116719
116773
|
},
|
|
116720
116774
|
Email: {
|
|
116721
|
-
Provider:
|
|
116775
|
+
Provider: config.email.provider
|
|
116722
116776
|
}
|
|
116723
116777
|
};
|
|
116724
116778
|
const appSettingsDevRelPath = `src/${projectName}.Api/appsettings.Development.json`;
|
|
@@ -117672,6 +117726,10 @@ var initCommand = new Command("init").description("Initialize a new SmartStack p
|
|
|
117672
117726
|
systemTenantSlug: "default",
|
|
117673
117727
|
systemTenantName: "Default Workspace",
|
|
117674
117728
|
autoAssignUsersToSystemTenant: true
|
|
117729
|
+
},
|
|
117730
|
+
email: {
|
|
117731
|
+
provider: "Development",
|
|
117732
|
+
setupMailPit: false
|
|
117675
117733
|
}
|
|
117676
117734
|
};
|
|
117677
117735
|
} else {
|
|
@@ -117711,6 +117769,27 @@ var initCommand = new Command("init").description("Initialize a new SmartStack p
|
|
|
117711
117769
|
when: (answers2) => answers2.multiTenantEnabled
|
|
117712
117770
|
}
|
|
117713
117771
|
]);
|
|
117772
|
+
const emailAnswers = await lib_default.prompt([
|
|
117773
|
+
{
|
|
117774
|
+
type: "list",
|
|
117775
|
+
name: "emailProvider",
|
|
117776
|
+
message: "Email provider:",
|
|
117777
|
+
choices: [
|
|
117778
|
+
{ name: "Development (MailPit - local SMTP testing)", value: "Development" },
|
|
117779
|
+
{ name: "Mailgun", value: "Mailgun" },
|
|
117780
|
+
{ name: "Azure Communication Services", value: "AzureAcs" },
|
|
117781
|
+
{ name: "Disabled", value: "Disabled" }
|
|
117782
|
+
],
|
|
117783
|
+
default: "Development"
|
|
117784
|
+
},
|
|
117785
|
+
{
|
|
117786
|
+
type: "confirm",
|
|
117787
|
+
name: "setupMailPit",
|
|
117788
|
+
message: "Start MailPit Docker container for email testing?",
|
|
117789
|
+
default: true,
|
|
117790
|
+
when: (a) => a.emailProvider === "Development" && isDockerAvailable()
|
|
117791
|
+
}
|
|
117792
|
+
]);
|
|
117714
117793
|
config = {
|
|
117715
117794
|
name: finalProjectName,
|
|
117716
117795
|
nameLower: finalProjectName.toLowerCase(),
|
|
@@ -117724,6 +117803,10 @@ var initCommand = new Command("init").description("Initialize a new SmartStack p
|
|
|
117724
117803
|
systemTenantSlug: answers.systemTenantSlug || "default",
|
|
117725
117804
|
systemTenantName: answers.systemTenantName || "Default Workspace",
|
|
117726
117805
|
autoAssignUsersToSystemTenant: true
|
|
117806
|
+
},
|
|
117807
|
+
email: {
|
|
117808
|
+
provider: emailAnswers.emailProvider || "Development",
|
|
117809
|
+
setupMailPit: emailAnswers.setupMailPit ?? false
|
|
117727
117810
|
}
|
|
117728
117811
|
};
|
|
117729
117812
|
}
|
|
@@ -117741,6 +117824,8 @@ var initCommand = new Command("init").description("Initialize a new SmartStack p
|
|
|
117741
117824
|
logger.info(` B2C (User Tenants): ${config.multiTenant.enableB2C ? source_default.green("Enabled") : source_default.gray("Disabled")}`);
|
|
117742
117825
|
logger.info(` System Tenant: ${source_default.cyan(config.multiTenant.systemTenantSlug)} (${config.multiTenant.systemTenantName})`);
|
|
117743
117826
|
}
|
|
117827
|
+
const emailLabel = config.email.provider === "Development" ? `${source_default.green("Development")} (MailPit - SMTP :1025, Web :8025)` : config.email.provider === "Disabled" ? source_default.gray("Disabled") : source_default.cyan(config.email.provider);
|
|
117828
|
+
logger.info(`Email: ${emailLabel}`);
|
|
117744
117829
|
console.log();
|
|
117745
117830
|
try {
|
|
117746
117831
|
let cliVersion = "0.0.0";
|
|
@@ -117782,6 +117867,10 @@ var initCommand = new Command("init").description("Initialize a new SmartStack p
|
|
|
117782
117867
|
logger.info("Would create .ralph/ directory with configuration");
|
|
117783
117868
|
}
|
|
117784
117869
|
});
|
|
117870
|
+
if (config.email.setupMailPit && !dryRun) {
|
|
117871
|
+
console.log();
|
|
117872
|
+
await setupMailPitContainer();
|
|
117873
|
+
}
|
|
117785
117874
|
if (!dryRun) {
|
|
117786
117875
|
await saveInitState(finalProjectDir, state);
|
|
117787
117876
|
}
|
|
@@ -126219,9 +126308,11 @@ adminCommand.command("reset").description("Reset the localAdmin account password
|
|
|
126219
126308
|
logger.error(`No connection string found in ${(0, import_path10.basename)(selectedFile)}`);
|
|
126220
126309
|
process.exit(1);
|
|
126221
126310
|
}
|
|
126222
|
-
logger.info(`Using: ${source_default.cyan((0, import_path10.basename)(selectedFile))}`);
|
|
126223
126311
|
}
|
|
126224
126312
|
const adminEmail = options.email;
|
|
126313
|
+
const connInfo = parseConnectionString(connectionString);
|
|
126314
|
+
logger.info(`Server: ${source_default.cyan(connInfo.server)}`);
|
|
126315
|
+
logger.info(`Database: ${source_default.cyan(connInfo.database)}`);
|
|
126225
126316
|
if (!options.force) {
|
|
126226
126317
|
const { confirm } = await lib_default.prompt([
|
|
126227
126318
|
{
|
|
@@ -126237,7 +126328,6 @@ adminCommand.command("reset").description("Reset the localAdmin account password
|
|
|
126237
126328
|
}
|
|
126238
126329
|
}
|
|
126239
126330
|
const spinner = logger.spinner("Connecting to database...");
|
|
126240
|
-
const connInfo = parseConnectionString(connectionString);
|
|
126241
126331
|
const resetViaSqlCmd = async (sqlAuth) => {
|
|
126242
126332
|
const authLabel = sqlAuth ? "SQL Server Authentication" : "Windows Authentication";
|
|
126243
126333
|
spinner.text = `Using ${authLabel} (sqlcmd)...`;
|
|
@@ -126265,6 +126355,8 @@ adminCommand.command("reset").description("Reset the localAdmin account password
|
|
|
126265
126355
|
console.log(source_default.green.bold(" LOCAL ADMINISTRATOR PASSWORD RESET"));
|
|
126266
126356
|
console.log(source_default.green("\u2550".repeat(60)));
|
|
126267
126357
|
console.log();
|
|
126358
|
+
console.log(source_default.white(" Server: "), source_default.cyan(connInfo.server));
|
|
126359
|
+
console.log(source_default.white(" Database: "), source_default.cyan(connInfo.database));
|
|
126268
126360
|
console.log(source_default.white(" Email: "), source_default.cyan(email));
|
|
126269
126361
|
console.log(source_default.white(" Password: "), source_default.yellow.bold(password));
|
|
126270
126362
|
console.log();
|
|
@@ -126809,6 +126901,42 @@ function validateForPrdExtraction(feature) {
|
|
|
126809
126901
|
}
|
|
126810
126902
|
return errors;
|
|
126811
126903
|
}
|
|
126904
|
+
function validatePrdCompleteness(prd, feature) {
|
|
126905
|
+
const warnings = [];
|
|
126906
|
+
const ftc = prd.implementation.filesToCreate;
|
|
126907
|
+
const handoffFtc = feature.handoff?.filesToCreate;
|
|
126908
|
+
const categories = [
|
|
126909
|
+
{ key: "domain", label: "domain", check: () => !!feature.analysis?.entities?.length },
|
|
126910
|
+
{ key: "application", label: "application", check: () => !!feature.analysis?.entities?.length },
|
|
126911
|
+
{ key: "infrastructure", label: "infrastructure", check: () => !!feature.analysis?.entities?.length },
|
|
126912
|
+
{ key: "api", label: "api", check: () => !!feature.specification?.apiEndpoints?.length },
|
|
126913
|
+
{ key: "frontend", label: "frontend", check: () => !!(feature.specification?.uiWireframes?.length || feature.specification?.sections?.length) },
|
|
126914
|
+
{ key: "seedData", label: "seedData", check: () => !!(feature.specification?.seedDataCore?.length || feature.specification?.seedDataBusiness?.length) },
|
|
126915
|
+
{ key: "tests", label: "tests", check: () => !!feature.analysis?.entities?.length }
|
|
126916
|
+
];
|
|
126917
|
+
for (const cat of categories) {
|
|
126918
|
+
const prdCount = ftc[cat.key]?.length ?? 0;
|
|
126919
|
+
const handoffCount = handoffFtc?.[cat.key]?.length ?? 0;
|
|
126920
|
+
if (prdCount === 0 && handoffCount > 0) {
|
|
126921
|
+
warnings.push(`${cat.label}: 0 files in PRD but ${handoffCount} in feature.json handoff`);
|
|
126922
|
+
} else if (prdCount === 0 && cat.check()) {
|
|
126923
|
+
warnings.push(`${cat.label}: 0 files but feature.json has relevant data`);
|
|
126924
|
+
}
|
|
126925
|
+
}
|
|
126926
|
+
return warnings;
|
|
126927
|
+
}
|
|
126928
|
+
function getPrdFileCounts(prd) {
|
|
126929
|
+
const ftc = prd.implementation.filesToCreate;
|
|
126930
|
+
return {
|
|
126931
|
+
domain: ftc.domain?.length ?? 0,
|
|
126932
|
+
application: ftc.application?.length ?? 0,
|
|
126933
|
+
infrastructure: ftc.infrastructure?.length ?? 0,
|
|
126934
|
+
api: ftc.api?.length ?? 0,
|
|
126935
|
+
frontend: ftc.frontend?.length ?? 0,
|
|
126936
|
+
seedData: ftc.seedData?.length ?? 0,
|
|
126937
|
+
tests: ftc.tests?.length ?? 0
|
|
126938
|
+
};
|
|
126939
|
+
}
|
|
126812
126940
|
|
|
126813
126941
|
// src/commands/derive-prd.ts
|
|
126814
126942
|
function readSmartStackNamespace() {
|
|
@@ -126897,12 +127025,26 @@ Processing module: ${source_default.bold(moduleName)}`));
|
|
|
126897
127025
|
}
|
|
126898
127026
|
await import_fs_extra9.default.writeJson(outputPath, prd, { spaces: 2 });
|
|
126899
127027
|
totalGenerated++;
|
|
127028
|
+
const completenessWarnings = validatePrdCompleteness(prd, featureJson);
|
|
127029
|
+
if (completenessWarnings.length > 0) {
|
|
127030
|
+
console.log(source_default.yellow(` Completeness warnings:`));
|
|
127031
|
+
for (const w of completenessWarnings) {
|
|
127032
|
+
console.log(source_default.yellow(` - ${w}`));
|
|
127033
|
+
}
|
|
127034
|
+
if (options.strict) {
|
|
127035
|
+
console.log(source_default.red(` Skipping ${moduleName} \u2014 incomplete PRD (--strict mode)`));
|
|
127036
|
+
totalErrors++;
|
|
127037
|
+
continue;
|
|
127038
|
+
}
|
|
127039
|
+
}
|
|
126900
127040
|
const relOutput = (0, import_path11.relative)(process.cwd(), outputPath);
|
|
126901
127041
|
console.log(source_default.green(` Generated: ${relOutput}`));
|
|
126902
127042
|
console.log(source_default.gray(` UCs: ${prd.requirements.useCases.length} | FRs: ${prd.requirements.functionalRequirements.length} | BRs: ${prd.businessRules.length}`));
|
|
126903
127043
|
console.log(source_default.gray(` Endpoints: ${prd.architecture.apiEndpoints.length} | Sections: ${prd.architecture.sections.length}`));
|
|
126904
|
-
const
|
|
126905
|
-
|
|
127044
|
+
const counts = getPrdFileCounts(prd);
|
|
127045
|
+
const totalFiles = Object.values(counts).reduce((s, n) => s + n, 0);
|
|
127046
|
+
console.log(source_default.gray(` Files to create: ${totalFiles} (domain: ${counts.domain}, app: ${counts.application}, infra: ${counts.infrastructure}, api: ${counts.api}, frontend: ${counts.frontend}, seed: ${counts.seedData}, tests: ${counts.tests})`));
|
|
127047
|
+
console.log(source_default.gray(` BR mappings: ${prd.implementation.brToCodeMapping.length}`));
|
|
126906
127048
|
}
|
|
126907
127049
|
console.log();
|
|
126908
127050
|
if (totalErrors > 0) {
|