@atlashub/smartstack-cli 4.57.0 → 4.59.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 CHANGED
@@ -113279,13 +113279,18 @@ async function installRalphConfig(options = {}) {
113279
113279
  result.errors.push("Ralph config template not found");
113280
113280
  logger.warning("Ralph config template not found in package");
113281
113281
  }
113282
- const gitignoreSrc = (0, import_path2.join)(TEMPLATES_DIR, "ralph", ".gitignore");
113282
+ const gitignoreSrc = (0, import_path2.join)(TEMPLATES_DIR, "ralph", "gitignore.template");
113283
113283
  const gitignoreDest = (0, import_path2.join)(ralphDir, ".gitignore");
113284
113284
  if (await import_fs_extra2.default.pathExists(gitignoreSrc)) {
113285
113285
  if (!await import_fs_extra2.default.pathExists(gitignoreDest) || options.force) {
113286
113286
  const content = await import_fs_extra2.default.readFile(gitignoreSrc);
113287
113287
  await import_fs_extra2.default.writeFile(gitignoreDest, content);
113288
+ logger.success("Created .ralph/.gitignore");
113289
+ } else {
113290
+ logger.info(".ralph/.gitignore already exists (use --force to overwrite)");
113288
113291
  }
113292
+ } else {
113293
+ logger.warning(".ralph/gitignore.template not found in package \u2014 .gitignore not created");
113289
113294
  }
113290
113295
  const readmeSrc = (0, import_path2.join)(TEMPLATES_DIR, "ralph", "README.md");
113291
113296
  const readmeDest = (0, import_path2.join)(ralphDir, "README.md");
@@ -116756,7 +116761,8 @@ EndGlobal
116756
116761
  "Program.cs",
116757
116762
  "appsettings.json",
116758
116763
  "appsettings.Development.json",
116759
- (0, import_path7.join)("Properties", "launchSettings.json")
116764
+ (0, import_path7.join)("Properties", "launchSettings.json"),
116765
+ `${projectName}.Api.http`
116760
116766
  ];
116761
116767
  for (const placeholder of apiPlaceholders) {
116762
116768
  const placeholderPath = (0, import_path7.join)(apiDir, placeholder);
@@ -117198,7 +117204,7 @@ ${projectName}/
117198
117204
  \u2502 \u2514\u2500\u2500 ${projectName}.Api/ # Web API controllers and configuration
117199
117205
  \u251C\u2500\u2500 web/
117200
117206
  \u2502 \u2514\u2500\u2500 ${projectName.toLowerCase()}-web/ # React frontend (Vite + TypeScript + Tailwind)
117201
- \u251C\u2500\u2500 docker-image/ # Dockerfiles (backend + frontend)
117207
+ \u251C\u2500\u2500 docker-images/ # Dockerfiles (backend + frontend)
117202
117208
  \u2514\u2500\u2500 tests/ # Unit and integration tests
117203
117209
  \`\`\`
117204
117210
 
@@ -117315,6 +117321,7 @@ async function createFrontendStructure(config, state, dryRun) {
117315
117321
  "eslint-plugin-react-refresh": "^0.4.24",
117316
117322
  globals: "^16.5.0",
117317
117323
  jsdom: "^28.0.0",
117324
+ msw: "^2.7.5",
117318
117325
  typescript: "~5.9.3",
117319
117326
  "typescript-eslint": "^8.46.4",
117320
117327
  vite: "^7.2.4",
@@ -117655,6 +117662,36 @@ Thumbs.db
117655
117662
  logSafeWriteResult(webClaudeMd_relPath, webClaudeMd_result);
117656
117663
  recordFile(state, "frontend", webClaudeMd_relPath, webClaudeMd_result.hash);
117657
117664
  }
117665
+ const testFrontendDir = (0, import_path7.join)(TEMPLATES_DIR2, "test-frontend");
117666
+ if (await import_fs_extra6.default.pathExists(testFrontendDir)) {
117667
+ await import_fs_extra6.default.ensureDir((0, import_path7.join)(webDir, "src", "test", "msw"));
117668
+ const vitestContent = await import_fs_extra6.default.readFile((0, import_path7.join)(testFrontendDir, "vitest.config.ts"), "utf-8");
117669
+ const vitest_relPath = `${webRelPrefix}/vitest.config.ts`;
117670
+ const vitest_result = await safeWriteFile(
117671
+ (0, import_path7.join)(webDir, "vitest.config.ts"),
117672
+ vitestContent,
117673
+ findKnownHash(state, "frontend", vitest_relPath)
117674
+ );
117675
+ logSafeWriteResult(vitest_relPath, vitest_result);
117676
+ recordFile(state, "frontend", vitest_relPath, vitest_result.hash);
117677
+ const testFiles = [
117678
+ { src: "setup.ts", dest: "src/test/setup.ts" },
117679
+ { src: "test-utils.tsx", dest: "src/test/test-utils.tsx" },
117680
+ { src: "msw/handlers.ts", dest: "src/test/msw/handlers.ts" },
117681
+ { src: "msw/server.ts", dest: "src/test/msw/server.ts" }
117682
+ ];
117683
+ for (const { src, dest } of testFiles) {
117684
+ const content = await import_fs_extra6.default.readFile((0, import_path7.join)(testFrontendDir, src), "utf-8");
117685
+ const relPath = `${webRelPrefix}/${dest}`;
117686
+ const result = await safeWriteFile(
117687
+ (0, import_path7.join)(webDir, dest),
117688
+ content,
117689
+ findKnownHash(state, "frontend", relPath)
117690
+ );
117691
+ logSafeWriteResult(relPath, result);
117692
+ recordFile(state, "frontend", relPath, result.hash);
117693
+ }
117694
+ }
117658
117695
  logger.success("React frontend created at: " + webDir);
117659
117696
  }
117660
117697
  async function createDockerFiles(config, state, dryRun) {
@@ -117662,10 +117699,10 @@ async function createDockerFiles(config, state, dryRun) {
117662
117699
  const projectDir = config.projectDir ?? ((0, import_path7.isAbsolute)(name) ? name : (0, import_path7.join)(process.cwd(), name));
117663
117700
  const projectName = (0, import_path7.basename)(name);
117664
117701
  if (!dryRun) {
117665
- await import_fs_extra6.default.ensureDir((0, import_path7.join)(projectDir, "docker-image"));
117702
+ await import_fs_extra6.default.ensureDir((0, import_path7.join)(projectDir, "docker-images"));
117666
117703
  }
117667
117704
  const backendDockerfile = await loadTemplate("Dockerfile.backend.template", projectName);
117668
- const backendRelPath = "docker-image/Dockerfile.backend";
117705
+ const backendRelPath = "docker-images/Dockerfile.backend";
117669
117706
  if (dryRun) {
117670
117707
  logger.info(`Would create ${backendRelPath}`);
117671
117708
  } else {
@@ -117678,7 +117715,7 @@ async function createDockerFiles(config, state, dryRun) {
117678
117715
  recordFile(state, "docker", backendRelPath, backendResult.hash);
117679
117716
  }
117680
117717
  const frontendDockerfile = await loadTemplate("Dockerfile.frontend.template", projectName);
117681
- const frontendRelPath = "docker-image/Dockerfile.frontend";
117718
+ const frontendRelPath = "docker-images/Dockerfile.frontend";
117682
117719
  if (dryRun) {
117683
117720
  logger.info(`Would create ${frontendRelPath}`);
117684
117721
  } else {
@@ -118072,7 +118109,7 @@ var initCommand = new Command("init").description("Initialize a new SmartStack p
118072
118109
  try {
118073
118110
  let cliVersion = "0.0.0";
118074
118111
  try {
118075
- const pkgPath = (0, import_path7.join)((0, import_path7.dirname)((0, import_path7.dirname)(__dirname)), "package.json");
118112
+ const pkgPath = (0, import_path7.join)((0, import_path7.dirname)(__dirname), "package.json");
118076
118113
  const pkg2 = JSON.parse(await import_fs_extra6.default.readFile(pkgPath, "utf-8"));
118077
118114
  cliVersion = pkg2.version || "0.0.0";
118078
118115
  } catch {
@@ -118103,6 +118140,17 @@ var initCommand = new Command("init").description("Initialize a new SmartStack p
118103
118140
  const ralphResult = await installRalphConfig({ projectPath: finalProjectDir });
118104
118141
  if (ralphResult.success) {
118105
118142
  logger.success("Ralph configuration created at .ralph/");
118143
+ const ralphFiles = [
118144
+ { path: ".ralph/ralph.config.yaml", abs: ralphResult.configPath },
118145
+ { path: ".ralph/.gitignore", abs: (0, import_path7.join)(finalProjectDir, ".ralph", ".gitignore") },
118146
+ { path: ".ralph/README.md", abs: (0, import_path7.join)(finalProjectDir, ".ralph", "README.md") }
118147
+ ];
118148
+ for (const rf of ralphFiles) {
118149
+ if (await import_fs_extra6.default.pathExists(rf.abs)) {
118150
+ const content = await import_fs_extra6.default.readFile(rf.abs);
118151
+ recordFile(state, "ralph", rf.path, computeHash(content));
118152
+ }
118153
+ }
118106
118154
  } else {
118107
118155
  logger.warning("Could not create Ralph configuration (non-critical)");
118108
118156
  }
@@ -118137,8 +118185,8 @@ var initCommand = new Command("init").description("Initialize a new SmartStack p
118137
118185
  ` 3. Start dev server: ${source_default.cyan("npm run dev")}`,
118138
118186
  "",
118139
118187
  source_default.yellow("Docker:"),
118140
- ` Build backend: ${source_default.cyan(`docker build -t smartstack-${projectNameLower}-backend:latest -f docker-image/Dockerfile.backend .`)}`,
118141
- ` Build frontend: ${source_default.cyan(`docker build -t smartstack-${projectNameLower}-frontend:latest -f docker-image/Dockerfile.frontend .`)}`,
118188
+ ` Build backend: ${source_default.cyan(`docker build -t smartstack-${projectNameLower}-backend:latest -f docker-images/Dockerfile.backend .`)}`,
118189
+ ` Build frontend: ${source_default.cyan(`docker build -t smartstack-${projectNameLower}-frontend:latest -f docker-images/Dockerfile.frontend .`)}`,
118142
118190
  "",
118143
118191
  source_default.yellow("Ralph (AI Automation):"),
118144
118192
  ` 1. Check MCP servers: ${source_default.cyan("smartstack check-mcp")}`,
@@ -125244,20 +125292,87 @@ async function syncClaudeSettings(projectDir, dryRun) {
125244
125292
  }
125245
125293
  return added;
125246
125294
  }
125295
+ async function syncTestFrontendTemplates(frontendDir, dryRun) {
125296
+ const testFrontendDir = (0, import_path8.join)(TEMPLATES_DIR3, "test-frontend");
125297
+ if (!await import_fs_extra7.default.pathExists(testFrontendDir)) {
125298
+ logger.debug("test-frontend templates not found, skipping");
125299
+ return 0;
125300
+ }
125301
+ let synced = 0;
125302
+ const testFiles = [
125303
+ { src: "vitest.config.ts", dest: "vitest.config.ts" },
125304
+ { src: "setup.ts", dest: (0, import_path8.join)("src", "test", "setup.ts") },
125305
+ { src: "test-utils.tsx", dest: (0, import_path8.join)("src", "test", "test-utils.tsx") },
125306
+ { src: "msw/handlers.ts", dest: (0, import_path8.join)("src", "test", "msw", "handlers.ts") },
125307
+ { src: "msw/server.ts", dest: (0, import_path8.join)("src", "test", "msw", "server.ts") }
125308
+ ];
125309
+ for (const file of testFiles) {
125310
+ const destPath = (0, import_path8.join)(frontendDir, file.dest);
125311
+ if (await import_fs_extra7.default.pathExists(destPath)) continue;
125312
+ const srcPath = (0, import_path8.join)(testFrontendDir, file.src);
125313
+ if (!await import_fs_extra7.default.pathExists(srcPath)) continue;
125314
+ if (dryRun) {
125315
+ logger.warning(`[DRY RUN] Would create ${file.dest}`);
125316
+ } else {
125317
+ await import_fs_extra7.default.ensureDir((0, import_path8.dirname)(destPath));
125318
+ const content = await import_fs_extra7.default.readFile(srcPath, "utf-8");
125319
+ await import_fs_extra7.default.writeFile(destPath, content, "utf-8");
125320
+ logger.info(` ${source_default.green("+")} ${file.dest}`);
125321
+ }
125322
+ synced++;
125323
+ }
125324
+ return synced;
125325
+ }
125326
+ async function cleanResidualFiles(projectDir, baseNamespace, dryRun) {
125327
+ let cleaned = 0;
125328
+ const residuals = [
125329
+ (0, import_path8.join)(projectDir, "src", `${baseNamespace}.Api`, `${baseNamespace}.Api.http`)
125330
+ ];
125331
+ for (const file of residuals) {
125332
+ if (await import_fs_extra7.default.pathExists(file)) {
125333
+ if (dryRun) {
125334
+ logger.warning(`[DRY RUN] Would remove residual: ${file}`);
125335
+ } else {
125336
+ await import_fs_extra7.default.remove(file);
125337
+ logger.info(` ${source_default.red("\u2212")} Removed residual: ${baseNamespace}.Api.http`);
125338
+ }
125339
+ cleaned++;
125340
+ }
125341
+ }
125342
+ return cleaned;
125343
+ }
125344
+ async function fixInitStateVersion(projectDir, cliVersion, dryRun) {
125345
+ const statePath = (0, import_path8.join)(projectDir, ".smartstack", "init-state.json");
125346
+ if (!await import_fs_extra7.default.pathExists(statePath)) return false;
125347
+ try {
125348
+ const state = await import_fs_extra7.default.readJson(statePath);
125349
+ if (state.cliVersion === "0.0.0") {
125350
+ if (dryRun) {
125351
+ logger.warning("[DRY RUN] Would fix cliVersion 0.0.0 in init-state.json");
125352
+ } else {
125353
+ state.cliVersion = cliVersion;
125354
+ await import_fs_extra7.default.writeJson(statePath, state, { spaces: 2 });
125355
+ logger.info(` ${source_default.green("+")} Fixed cliVersion: 0.0.0 \u2192 ${cliVersion}`);
125356
+ }
125357
+ return true;
125358
+ }
125359
+ } catch {
125360
+ }
125361
+ return false;
125362
+ }
125247
125363
  async function syncDockerFiles(projectDir, baseNamespace, dryRun) {
125248
125364
  const projectName = baseNamespace;
125249
- const projectNameLower = projectName.toLowerCase();
125250
125365
  let updated = 0;
125251
125366
  const files = [
125252
125367
  {
125253
125368
  template: "Dockerfile.backend.template",
125254
- dest: (0, import_path8.join)(projectDir, "src", `${projectName}.Api`, "Dockerfile"),
125255
- label: `src/${projectName}.Api/Dockerfile`
125369
+ dest: (0, import_path8.join)(projectDir, "docker-images", "Dockerfile.backend"),
125370
+ label: "docker-images/Dockerfile.backend"
125256
125371
  },
125257
125372
  {
125258
125373
  template: "Dockerfile.frontend.template",
125259
- dest: (0, import_path8.join)(projectDir, "web", `${projectNameLower}-web`, "Dockerfile"),
125260
- label: `web/${projectNameLower}-web/Dockerfile`
125374
+ dest: (0, import_path8.join)(projectDir, "docker-images", "Dockerfile.frontend"),
125375
+ label: "docker-images/Dockerfile.frontend"
125261
125376
  },
125262
125377
  {
125263
125378
  template: "dockerignore.template",
@@ -125311,6 +125426,9 @@ var upgradeCommand = new Command("upgrade").description("Upgrade SmartStack pack
125311
125426
  ralphInitialized: false,
125312
125427
  ralphUpToDate: false,
125313
125428
  dockerSynced: 0,
125429
+ testFrontendSynced: 0,
125430
+ residualsCleaned: 0,
125431
+ initStateFixed: false,
125314
125432
  programCsIssues: []
125315
125433
  };
125316
125434
  if (dryRun) {
@@ -125494,6 +125612,14 @@ var upgradeCommand = new Command("upgrade").description("Upgrade SmartStack pack
125494
125612
  } else {
125495
125613
  logger.info(`Other npm packages: ${source_default.gray("skipped")} (use ${source_default.cyan("--all-packages")} to upgrade all)`);
125496
125614
  }
125615
+ logger.info("Syncing test-frontend templates...");
125616
+ const testSynced = await syncTestFrontendTemplates(frontendDir, dryRun);
125617
+ result.testFrontendSynced = testSynced;
125618
+ if (testSynced > 0) {
125619
+ logger.success(`${testSynced} test template(s) deployed`);
125620
+ } else {
125621
+ logger.info(`Test templates ${source_default.green("\u2713")} up to date`);
125622
+ }
125497
125623
  console.log();
125498
125624
  }
125499
125625
  logger.info("Checking for code migrations...");
@@ -125564,6 +125690,17 @@ var upgradeCommand = new Command("upgrade").description("Upgrade SmartStack pack
125564
125690
  logger.info(`Dockerfiles ${source_default.green("\u2713")} up to date`);
125565
125691
  }
125566
125692
  console.log();
125693
+ const residualsCleaned = await cleanResidualFiles(projectDir, config.baseNamespace, dryRun);
125694
+ result.residualsCleaned = residualsCleaned;
125695
+ if (residualsCleaned > 0) {
125696
+ logger.success(`${residualsCleaned} residual file(s) cleaned`);
125697
+ console.log();
125698
+ }
125699
+ result.initStateFixed = await fixInitStateVersion(projectDir, nugetVersion, dryRun);
125700
+ if (result.initStateFixed) {
125701
+ logger.success("Fixed cliVersion in init-state.json");
125702
+ console.log();
125703
+ }
125567
125704
  logger.info("Checking Ralph configuration...");
125568
125705
  const ralphStatus = await checkRalphInstallation(projectDir);
125569
125706
  if (ralphStatus.configExists) {
@@ -125607,7 +125744,7 @@ var upgradeCommand = new Command("upgrade").description("Upgrade SmartStack pack
125607
125744
  logger.success(`Updated config version to ${source_default.cyan(nugetVersion)}`);
125608
125745
  console.log();
125609
125746
  }
125610
- const totalChanged = result.nugetUpgraded + result.otherPkgUpdated + (result.npmUpgraded ? 1 : 0) + (result.npmOtherUpdated ? 1 : 0) + migrationSummary.totalApplied + result.configSynced + result.claudeSettingsSynced + result.dockerSynced + (result.ralphInitialized ? 1 : 0);
125747
+ const totalChanged = result.nugetUpgraded + result.otherPkgUpdated + (result.npmUpgraded ? 1 : 0) + (result.npmOtherUpdated ? 1 : 0) + migrationSummary.totalApplied + result.configSynced + result.claudeSettingsSynced + result.dockerSynced + result.testFrontendSynced + result.residualsCleaned + (result.initStateFixed ? 1 : 0) + (result.ralphInitialized ? 1 : 0);
125611
125748
  const allUpToDate = totalChanged === 0 && result.nugetFailed === 0 && result.otherPkgFailed === 0;
125612
125749
  const hasProgramIssues = result.programCsIssues.length > 0;
125613
125750
  if (allUpToDate && !hasProgramIssues) {
@@ -125671,6 +125808,15 @@ var upgradeCommand = new Command("upgrade").description("Upgrade SmartStack pack
125671
125808
  if (result.dockerSynced > 0) {
125672
125809
  lines.push(` ${source_default.green("\u2713")} Docker: ${result.dockerSynced} Dockerfile(s) updated`);
125673
125810
  }
125811
+ if (result.testFrontendSynced > 0) {
125812
+ lines.push(` ${source_default.green("\u2713")} Test templates: ${result.testFrontendSynced} file(s) deployed`);
125813
+ }
125814
+ if (result.residualsCleaned > 0) {
125815
+ lines.push(` ${source_default.green("\u2713")} Cleanup: ${result.residualsCleaned} residual file(s) removed`);
125816
+ }
125817
+ if (result.initStateFixed) {
125818
+ lines.push(` ${source_default.green("\u2713")} Init-state: cliVersion fixed`);
125819
+ }
125674
125820
  if (result.ralphInitialized) {
125675
125821
  lines.push(` ${source_default.green("\u2713")} Ralph: configuration initialized`);
125676
125822
  }
@@ -129094,7 +129240,7 @@ Processing module: ${source_default.bold(moduleName)}`));
129094
129240
  }
129095
129241
  let cliVersion = "unknown";
129096
129242
  try {
129097
- const pkg2 = import_fs_extra15.default.readJsonSync((0, import_path16.join)(__dirname, "..", "..", "package.json"));
129243
+ const pkg2 = import_fs_extra15.default.readJsonSync((0, import_path16.join)(__dirname, "..", "package.json"));
129098
129244
  cliVersion = pkg2.version ?? "unknown";
129099
129245
  } catch {
129100
129246
  }