@atlashub/smartstack-cli 3.4.1 → 3.6.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.
Files changed (46) hide show
  1. package/dist/index.js +160 -5
  2. package/dist/index.js.map +1 -1
  3. package/dist/mcp-entry.mjs +4 -3
  4. package/dist/mcp-entry.mjs.map +1 -1
  5. package/package.json +1 -1
  6. package/templates/skills/_shared.md +1 -1
  7. package/templates/skills/application/steps/step-04-backend.md +4 -4
  8. package/templates/skills/application/templates-backend.md +4 -4
  9. package/templates/skills/business-analyse/SKILL.md +26 -15
  10. package/templates/skills/business-analyse/_architecture.md +4 -4
  11. package/templates/skills/business-analyse/_elicitation.md +1 -1
  12. package/templates/skills/business-analyse/_module-loop.md +4 -4
  13. package/templates/skills/business-analyse/html/ba-interactive.html +39 -10
  14. package/templates/skills/business-analyse/questionnaire/06-security.md +1 -1
  15. package/templates/skills/business-analyse/questionnaire.md +2 -2
  16. package/templates/skills/business-analyse/react/components.md +1 -1
  17. package/templates/skills/business-analyse/react/schema.md +1 -1
  18. package/templates/skills/business-analyse/references/html-data-mapping.md +4 -3
  19. package/templates/skills/business-analyse/schemas/feature-schema.json +1 -1
  20. package/templates/skills/business-analyse/schemas/sections/analysis-schema.json +1 -1
  21. package/templates/skills/business-analyse/schemas/sections/metadata-schema.json +1 -0
  22. package/templates/skills/business-analyse/schemas/sections/specification-schema.json +1 -1
  23. package/templates/skills/business-analyse/steps/step-00-init.md +29 -0
  24. package/templates/skills/business-analyse/steps/step-01-cadrage.md +166 -6
  25. package/templates/skills/business-analyse/steps/step-02-decomposition.md +4 -4
  26. package/templates/skills/business-analyse/steps/{step-03a-specify.md → step-03a-data.md} +10 -359
  27. package/templates/skills/business-analyse/steps/step-03b-ui.md +414 -0
  28. package/templates/skills/business-analyse/steps/step-03c-compile.md +343 -0
  29. package/templates/skills/business-analyse/steps/{step-03b-compile.md → step-03d-validate.md} +26 -308
  30. package/templates/skills/business-analyse/steps/step-04-consolidation.md +2 -2
  31. package/templates/skills/business-analyse/steps/step-05a-handoff.md +49 -292
  32. package/templates/skills/business-analyse/steps/step-05b-mapping.md +302 -0
  33. package/templates/skills/business-analyse/steps/step-05c-deploy.md +296 -0
  34. package/templates/skills/business-analyse/steps/step-05d-html.md +326 -0
  35. package/templates/skills/business-analyse/templates/tpl-frd.md +1 -1
  36. package/templates/skills/business-analyse/templates/tpl-handoff.md +6 -6
  37. package/templates/skills/business-analyse/templates/tpl-launch-displays.md +1 -1
  38. package/templates/skills/business-analyse/templates/tpl-progress.md +1 -1
  39. package/templates/skills/controller/steps/step-03-generate.md +2 -1
  40. package/templates/skills/ralph-loop/SKILL.md +17 -2
  41. package/templates/skills/ralph-loop/references/core-seed-data.md +538 -0
  42. package/templates/skills/ralph-loop/steps/step-00-init.md +2 -0
  43. package/templates/skills/ralph-loop/steps/step-01-task.md +273 -7
  44. package/templates/skills/ralph-loop/steps/step-02-execute.md +39 -15
  45. package/templates/skills/ralph-loop/steps/step-04-check.md +87 -4
  46. package/templates/skills/business-analyse/steps/step-05b-deploy.md +0 -432
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: "Development"
116775
+ Provider: config.email.provider
116722
116776
  }
116723
116777
  };
116724
116778
  const appSettingsDevRelPath = `src/${projectName}.Api/appsettings.Development.json`;
@@ -116985,6 +117039,19 @@ Generated with [SmartStack CLI](https://atlashub.io/products/smartstack-cli)
116985
117039
  const result7 = await safeWriteFile((0, import_path6.join)(projectDir, relPath7), readme, findKnownHash(state, "config", relPath7));
116986
117040
  logSafeWriteResult(relPath7, result7);
116987
117041
  recordFile(state, "config", relPath7, result7.hash);
117042
+ const claudeSettingsTemplatePath = (0, import_path6.join)(TEMPLATES_DIR2, "claude-settings.json.template");
117043
+ if (await import_fs_extra5.default.pathExists(claudeSettingsTemplatePath)) {
117044
+ await import_fs_extra5.default.ensureDir((0, import_path6.join)(projectDir, ".claude"));
117045
+ const claudeSettingsContent = await import_fs_extra5.default.readFile(claudeSettingsTemplatePath, "utf-8");
117046
+ const relPath8 = ".claude/settings.json";
117047
+ const result8 = await safeWriteFile(
117048
+ (0, import_path6.join)(projectDir, relPath8),
117049
+ claudeSettingsContent,
117050
+ findKnownHash(state, "config", relPath8)
117051
+ );
117052
+ logSafeWriteResult(relPath8, result8);
117053
+ recordFile(state, "config", relPath8, result8.hash);
117054
+ }
116988
117055
  }
116989
117056
  async function createFrontendStructure(config, state, dryRun) {
116990
117057
  const { name } = config;
@@ -117672,6 +117739,10 @@ var initCommand = new Command("init").description("Initialize a new SmartStack p
117672
117739
  systemTenantSlug: "default",
117673
117740
  systemTenantName: "Default Workspace",
117674
117741
  autoAssignUsersToSystemTenant: true
117742
+ },
117743
+ email: {
117744
+ provider: "Development",
117745
+ setupMailPit: false
117675
117746
  }
117676
117747
  };
117677
117748
  } else {
@@ -117711,6 +117782,27 @@ var initCommand = new Command("init").description("Initialize a new SmartStack p
117711
117782
  when: (answers2) => answers2.multiTenantEnabled
117712
117783
  }
117713
117784
  ]);
117785
+ const emailAnswers = await lib_default.prompt([
117786
+ {
117787
+ type: "list",
117788
+ name: "emailProvider",
117789
+ message: "Email provider:",
117790
+ choices: [
117791
+ { name: "Development (MailPit - local SMTP testing)", value: "Development" },
117792
+ { name: "Mailgun", value: "Mailgun" },
117793
+ { name: "Azure Communication Services", value: "AzureAcs" },
117794
+ { name: "Disabled", value: "Disabled" }
117795
+ ],
117796
+ default: "Development"
117797
+ },
117798
+ {
117799
+ type: "confirm",
117800
+ name: "setupMailPit",
117801
+ message: "Start MailPit Docker container for email testing?",
117802
+ default: true,
117803
+ when: (a) => a.emailProvider === "Development" && isDockerAvailable()
117804
+ }
117805
+ ]);
117714
117806
  config = {
117715
117807
  name: finalProjectName,
117716
117808
  nameLower: finalProjectName.toLowerCase(),
@@ -117724,6 +117816,10 @@ var initCommand = new Command("init").description("Initialize a new SmartStack p
117724
117816
  systemTenantSlug: answers.systemTenantSlug || "default",
117725
117817
  systemTenantName: answers.systemTenantName || "Default Workspace",
117726
117818
  autoAssignUsersToSystemTenant: true
117819
+ },
117820
+ email: {
117821
+ provider: emailAnswers.emailProvider || "Development",
117822
+ setupMailPit: emailAnswers.setupMailPit ?? false
117727
117823
  }
117728
117824
  };
117729
117825
  }
@@ -117741,6 +117837,8 @@ var initCommand = new Command("init").description("Initialize a new SmartStack p
117741
117837
  logger.info(` B2C (User Tenants): ${config.multiTenant.enableB2C ? source_default.green("Enabled") : source_default.gray("Disabled")}`);
117742
117838
  logger.info(` System Tenant: ${source_default.cyan(config.multiTenant.systemTenantSlug)} (${config.multiTenant.systemTenantName})`);
117743
117839
  }
117840
+ 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);
117841
+ logger.info(`Email: ${emailLabel}`);
117744
117842
  console.log();
117745
117843
  try {
117746
117844
  let cliVersion = "0.0.0";
@@ -117782,6 +117880,10 @@ var initCommand = new Command("init").description("Initialize a new SmartStack p
117782
117880
  logger.info("Would create .ralph/ directory with configuration");
117783
117881
  }
117784
117882
  });
117883
+ if (config.email.setupMailPit && !dryRun) {
117884
+ console.log();
117885
+ await setupMailPitContainer();
117886
+ }
117785
117887
  if (!dryRun) {
117786
117888
  await saveInitState(finalProjectDir, state);
117787
117889
  }
@@ -126219,9 +126321,11 @@ adminCommand.command("reset").description("Reset the localAdmin account password
126219
126321
  logger.error(`No connection string found in ${(0, import_path10.basename)(selectedFile)}`);
126220
126322
  process.exit(1);
126221
126323
  }
126222
- logger.info(`Using: ${source_default.cyan((0, import_path10.basename)(selectedFile))}`);
126223
126324
  }
126224
126325
  const adminEmail = options.email;
126326
+ const connInfo = parseConnectionString(connectionString);
126327
+ logger.info(`Server: ${source_default.cyan(connInfo.server)}`);
126328
+ logger.info(`Database: ${source_default.cyan(connInfo.database)}`);
126225
126329
  if (!options.force) {
126226
126330
  const { confirm } = await lib_default.prompt([
126227
126331
  {
@@ -126237,7 +126341,6 @@ adminCommand.command("reset").description("Reset the localAdmin account password
126237
126341
  }
126238
126342
  }
126239
126343
  const spinner = logger.spinner("Connecting to database...");
126240
- const connInfo = parseConnectionString(connectionString);
126241
126344
  const resetViaSqlCmd = async (sqlAuth) => {
126242
126345
  const authLabel = sqlAuth ? "SQL Server Authentication" : "Windows Authentication";
126243
126346
  spinner.text = `Using ${authLabel} (sqlcmd)...`;
@@ -126265,6 +126368,8 @@ adminCommand.command("reset").description("Reset the localAdmin account password
126265
126368
  console.log(source_default.green.bold(" LOCAL ADMINISTRATOR PASSWORD RESET"));
126266
126369
  console.log(source_default.green("\u2550".repeat(60)));
126267
126370
  console.log();
126371
+ console.log(source_default.white(" Server: "), source_default.cyan(connInfo.server));
126372
+ console.log(source_default.white(" Database: "), source_default.cyan(connInfo.database));
126268
126373
  console.log(source_default.white(" Email: "), source_default.cyan(email));
126269
126374
  console.log(source_default.white(" Password: "), source_default.yellow.bold(password));
126270
126375
  console.log();
@@ -126809,6 +126914,42 @@ function validateForPrdExtraction(feature) {
126809
126914
  }
126810
126915
  return errors;
126811
126916
  }
126917
+ function validatePrdCompleteness(prd, feature) {
126918
+ const warnings = [];
126919
+ const ftc = prd.implementation.filesToCreate;
126920
+ const handoffFtc = feature.handoff?.filesToCreate;
126921
+ const categories = [
126922
+ { key: "domain", label: "domain", check: () => !!feature.analysis?.entities?.length },
126923
+ { key: "application", label: "application", check: () => !!feature.analysis?.entities?.length },
126924
+ { key: "infrastructure", label: "infrastructure", check: () => !!feature.analysis?.entities?.length },
126925
+ { key: "api", label: "api", check: () => !!feature.specification?.apiEndpoints?.length },
126926
+ { key: "frontend", label: "frontend", check: () => !!(feature.specification?.uiWireframes?.length || feature.specification?.sections?.length) },
126927
+ { key: "seedData", label: "seedData", check: () => !!(feature.specification?.seedDataCore?.length || feature.specification?.seedDataBusiness?.length) },
126928
+ { key: "tests", label: "tests", check: () => !!feature.analysis?.entities?.length }
126929
+ ];
126930
+ for (const cat of categories) {
126931
+ const prdCount = ftc[cat.key]?.length ?? 0;
126932
+ const handoffCount = handoffFtc?.[cat.key]?.length ?? 0;
126933
+ if (prdCount === 0 && handoffCount > 0) {
126934
+ warnings.push(`${cat.label}: 0 files in PRD but ${handoffCount} in feature.json handoff`);
126935
+ } else if (prdCount === 0 && cat.check()) {
126936
+ warnings.push(`${cat.label}: 0 files but feature.json has relevant data`);
126937
+ }
126938
+ }
126939
+ return warnings;
126940
+ }
126941
+ function getPrdFileCounts(prd) {
126942
+ const ftc = prd.implementation.filesToCreate;
126943
+ return {
126944
+ domain: ftc.domain?.length ?? 0,
126945
+ application: ftc.application?.length ?? 0,
126946
+ infrastructure: ftc.infrastructure?.length ?? 0,
126947
+ api: ftc.api?.length ?? 0,
126948
+ frontend: ftc.frontend?.length ?? 0,
126949
+ seedData: ftc.seedData?.length ?? 0,
126950
+ tests: ftc.tests?.length ?? 0
126951
+ };
126952
+ }
126812
126953
 
126813
126954
  // src/commands/derive-prd.ts
126814
126955
  function readSmartStackNamespace() {
@@ -126897,12 +127038,26 @@ Processing module: ${source_default.bold(moduleName)}`));
126897
127038
  }
126898
127039
  await import_fs_extra9.default.writeJson(outputPath, prd, { spaces: 2 });
126899
127040
  totalGenerated++;
127041
+ const completenessWarnings = validatePrdCompleteness(prd, featureJson);
127042
+ if (completenessWarnings.length > 0) {
127043
+ console.log(source_default.yellow(` Completeness warnings:`));
127044
+ for (const w of completenessWarnings) {
127045
+ console.log(source_default.yellow(` - ${w}`));
127046
+ }
127047
+ if (options.strict) {
127048
+ console.log(source_default.red(` Skipping ${moduleName} \u2014 incomplete PRD (--strict mode)`));
127049
+ totalErrors++;
127050
+ continue;
127051
+ }
127052
+ }
126900
127053
  const relOutput = (0, import_path11.relative)(process.cwd(), outputPath);
126901
127054
  console.log(source_default.green(` Generated: ${relOutput}`));
126902
127055
  console.log(source_default.gray(` UCs: ${prd.requirements.useCases.length} | FRs: ${prd.requirements.functionalRequirements.length} | BRs: ${prd.businessRules.length}`));
126903
127056
  console.log(source_default.gray(` Endpoints: ${prd.architecture.apiEndpoints.length} | Sections: ${prd.architecture.sections.length}`));
126904
- const fileCount = Object.values(prd.implementation.filesToCreate).reduce((sum, files) => sum + (files?.length ?? 0), 0);
126905
- console.log(source_default.gray(` Files to create: ${fileCount} | BR mappings: ${prd.implementation.brToCodeMapping.length}`));
127057
+ const counts = getPrdFileCounts(prd);
127058
+ const totalFiles = Object.values(counts).reduce((s, n) => s + n, 0);
127059
+ 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})`));
127060
+ console.log(source_default.gray(` BR mappings: ${prd.implementation.brToCodeMapping.length}`));
126906
127061
  }
126907
127062
  console.log();
126908
127063
  if (totalErrors > 0) {