swallowkit 1.1.2 → 2.0.0-alpha.10

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 (262) hide show
  1. package/README.ja.md +61 -7
  2. package/README.md +61 -7
  3. package/dist/cli/commands/add-auth.d.ts.map +1 -1
  4. package/dist/cli/commands/add-auth.js +103 -27
  5. package/dist/cli/commands/add-auth.js.map +1 -1
  6. package/dist/cli/commands/configure-mcp.d.ts +12 -0
  7. package/dist/cli/commands/configure-mcp.d.ts.map +1 -0
  8. package/dist/cli/commands/configure-mcp.js +42 -0
  9. package/dist/cli/commands/configure-mcp.js.map +1 -0
  10. package/dist/cli/commands/dev.d.ts +5 -2
  11. package/dist/cli/commands/dev.d.ts.map +1 -1
  12. package/dist/cli/commands/dev.js +143 -67
  13. package/dist/cli/commands/dev.js.map +1 -1
  14. package/dist/cli/commands/init.d.ts +14 -7
  15. package/dist/cli/commands/init.d.ts.map +1 -1
  16. package/dist/cli/commands/init.js +149 -170
  17. package/dist/cli/commands/init.js.map +1 -1
  18. package/dist/cli/commands/migrations.d.ts.map +1 -1
  19. package/dist/cli/commands/migrations.js +25 -0
  20. package/dist/cli/commands/migrations.js.map +1 -1
  21. package/dist/cli/commands/provision.d.ts +5 -0
  22. package/dist/cli/commands/provision.d.ts.map +1 -1
  23. package/dist/cli/commands/provision.js +278 -55
  24. package/dist/cli/commands/provision.js.map +1 -1
  25. package/dist/cli/commands/scaffold.d.ts +5 -1
  26. package/dist/cli/commands/scaffold.d.ts.map +1 -1
  27. package/dist/cli/commands/scaffold.js +123 -18
  28. package/dist/cli/commands/scaffold.js.map +1 -1
  29. package/dist/cli/index.d.ts +2 -0
  30. package/dist/cli/index.d.ts.map +1 -1
  31. package/dist/cli/index.js +49 -8
  32. package/dist/cli/index.js.map +1 -1
  33. package/dist/core/config.d.ts +2 -1
  34. package/dist/core/config.d.ts.map +1 -1
  35. package/dist/core/config.js +19 -0
  36. package/dist/core/config.js.map +1 -1
  37. package/dist/core/operations/create-model.d.ts +2 -0
  38. package/dist/core/operations/create-model.d.ts.map +1 -1
  39. package/dist/core/operations/create-model.js +60 -8
  40. package/dist/core/operations/create-model.js.map +1 -1
  41. package/dist/core/operations/migration-generator.d.ts.map +1 -1
  42. package/dist/core/operations/migration-generator.js +12 -1
  43. package/dist/core/operations/migration-generator.js.map +1 -1
  44. package/dist/core/operations/model-migration.d.ts +22 -0
  45. package/dist/core/operations/model-migration.d.ts.map +1 -0
  46. package/dist/core/operations/model-migration.js +36 -0
  47. package/dist/core/operations/model-migration.js.map +1 -0
  48. package/dist/core/operations/provision-deployment.d.ts +52 -0
  49. package/dist/core/operations/provision-deployment.d.ts.map +1 -0
  50. package/dist/core/operations/provision-deployment.js +164 -0
  51. package/dist/core/operations/provision-deployment.js.map +1 -0
  52. package/dist/core/operations/provision-operations.d.ts +62 -6
  53. package/dist/core/operations/provision-operations.d.ts.map +1 -1
  54. package/dist/core/operations/provision-operations.js +653 -115
  55. package/dist/core/operations/provision-operations.js.map +1 -1
  56. package/dist/core/operations/scaffold-apply.d.ts +1 -0
  57. package/dist/core/operations/scaffold-apply.d.ts.map +1 -1
  58. package/dist/core/operations/scaffold-apply.js +1 -0
  59. package/dist/core/operations/scaffold-apply.js.map +1 -1
  60. package/dist/core/operations/scaffold-machine.d.ts +1 -0
  61. package/dist/core/operations/scaffold-machine.d.ts.map +1 -1
  62. package/dist/core/operations/scaffold-machine.js.map +1 -1
  63. package/dist/core/operations/scaffold-plan.d.ts +2 -0
  64. package/dist/core/operations/scaffold-plan.d.ts.map +1 -1
  65. package/dist/core/operations/scaffold-plan.js +3 -1
  66. package/dist/core/operations/scaffold-plan.js.map +1 -1
  67. package/dist/core/project/agent-instructions.d.ts +2 -1
  68. package/dist/core/project/agent-instructions.d.ts.map +1 -1
  69. package/dist/core/project/agent-instructions.js +15 -0
  70. package/dist/core/project/agent-instructions.js.map +1 -1
  71. package/dist/core/project/boundaries.d.ts +5 -2
  72. package/dist/core/project/boundaries.d.ts.map +1 -1
  73. package/dist/core/project/boundaries.js +51 -20
  74. package/dist/core/project/boundaries.js.map +1 -1
  75. package/dist/core/project/capabilities.d.ts +17 -0
  76. package/dist/core/project/capabilities.d.ts.map +1 -1
  77. package/dist/core/project/capabilities.js +45 -6
  78. package/dist/core/project/capabilities.js.map +1 -1
  79. package/dist/core/project/home-page.d.ts +4 -0
  80. package/dist/core/project/home-page.d.ts.map +1 -0
  81. package/dist/core/project/home-page.js +113 -0
  82. package/dist/core/project/home-page.js.map +1 -0
  83. package/dist/core/project/manifest.d.ts +3 -1
  84. package/dist/core/project/manifest.d.ts.map +1 -1
  85. package/dist/core/project/manifest.js +47 -25
  86. package/dist/core/project/manifest.js.map +1 -1
  87. package/dist/core/project/mcp-config.d.ts +26 -0
  88. package/dist/core/project/mcp-config.d.ts.map +1 -0
  89. package/dist/core/project/mcp-config.js +117 -0
  90. package/dist/core/project/mcp-config.js.map +1 -0
  91. package/dist/core/project/react-project.d.ts +26 -0
  92. package/dist/core/project/react-project.d.ts.map +1 -0
  93. package/dist/core/project/react-project.js +443 -0
  94. package/dist/core/project/react-project.js.map +1 -0
  95. package/dist/core/project/state.d.ts +7 -0
  96. package/dist/core/project/state.d.ts.map +1 -1
  97. package/dist/core/project/state.js +86 -0
  98. package/dist/core/project/state.js.map +1 -1
  99. package/dist/core/project/validation.d.ts.map +1 -1
  100. package/dist/core/project/validation.js +32 -10
  101. package/dist/core/project/validation.js.map +1 -1
  102. package/dist/core/project/workflows.d.ts.map +1 -1
  103. package/dist/core/project/workflows.js +53 -7
  104. package/dist/core/project/workflows.js.map +1 -1
  105. package/dist/core/scaffold/auth-generator.d.ts.map +1 -1
  106. package/dist/core/scaffold/auth-generator.js +26 -27
  107. package/dist/core/scaffold/auth-generator.js.map +1 -1
  108. package/dist/core/scaffold/model-parser.d.ts +23 -0
  109. package/dist/core/scaffold/model-parser.d.ts.map +1 -1
  110. package/dist/core/scaffold/model-parser.js +407 -137
  111. package/dist/core/scaffold/model-parser.js.map +1 -1
  112. package/dist/core/scaffold/native-schema-generator.d.ts +13 -0
  113. package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -1
  114. package/dist/core/scaffold/native-schema-generator.js +294 -91
  115. package/dist/core/scaffold/native-schema-generator.js.map +1 -1
  116. package/dist/core/scaffold/openapi-generator.d.ts +5 -0
  117. package/dist/core/scaffold/openapi-generator.d.ts.map +1 -1
  118. package/dist/core/scaffold/openapi-generator.js +53 -3
  119. package/dist/core/scaffold/openapi-generator.js.map +1 -1
  120. package/dist/core/scaffold/react-bff-generator.d.ts +10 -0
  121. package/dist/core/scaffold/react-bff-generator.d.ts.map +1 -0
  122. package/dist/core/scaffold/react-bff-generator.js +252 -0
  123. package/dist/core/scaffold/react-bff-generator.js.map +1 -0
  124. package/dist/core/scaffold/react-ui-generator.d.ts +6 -0
  125. package/dist/core/scaffold/react-ui-generator.d.ts.map +1 -0
  126. package/dist/core/scaffold/react-ui-generator.js +41 -0
  127. package/dist/core/scaffold/react-ui-generator.js.map +1 -0
  128. package/dist/core/scaffold/ui-generator.d.ts +0 -3
  129. package/dist/core/scaffold/ui-generator.d.ts.map +1 -1
  130. package/dist/core/scaffold/ui-generator.js +68 -7
  131. package/dist/core/scaffold/ui-generator.js.map +1 -1
  132. package/dist/core/verify/index.d.ts +4 -3
  133. package/dist/core/verify/index.d.ts.map +1 -1
  134. package/dist/core/verify/index.js +122 -5
  135. package/dist/core/verify/index.js.map +1 -1
  136. package/dist/index.d.ts +3 -3
  137. package/dist/index.d.ts.map +1 -1
  138. package/dist/index.js +3 -2
  139. package/dist/index.js.map +1 -1
  140. package/dist/machine/contracts.d.ts +1 -0
  141. package/dist/machine/contracts.d.ts.map +1 -1
  142. package/dist/machine/errors.d.ts +3 -2
  143. package/dist/machine/errors.d.ts.map +1 -1
  144. package/dist/machine/errors.js +2 -1
  145. package/dist/machine/errors.js.map +1 -1
  146. package/dist/machine/index.d.ts.map +1 -1
  147. package/dist/machine/index.js +61 -30
  148. package/dist/machine/index.js.map +1 -1
  149. package/dist/mcp/index.d.ts +6 -4
  150. package/dist/mcp/index.d.ts.map +1 -1
  151. package/dist/mcp/index.js +210 -25
  152. package/dist/mcp/index.js.map +1 -1
  153. package/dist/mcp/version-selection.d.ts +55 -0
  154. package/dist/mcp/version-selection.d.ts.map +1 -0
  155. package/dist/mcp/version-selection.js +152 -0
  156. package/dist/mcp/version-selection.js.map +1 -0
  157. package/dist/types/index.d.ts +4 -0
  158. package/dist/types/index.d.ts.map +1 -1
  159. package/dist/utils/azure-cli.d.ts +56 -0
  160. package/dist/utils/azure-cli.d.ts.map +1 -0
  161. package/dist/utils/azure-cli.js +326 -0
  162. package/dist/utils/azure-cli.js.map +1 -0
  163. package/dist/utils/azure-diagnostics.d.ts +16 -0
  164. package/dist/utils/azure-diagnostics.d.ts.map +1 -0
  165. package/dist/utils/azure-diagnostics.js +68 -0
  166. package/dist/utils/azure-diagnostics.js.map +1 -0
  167. package/dist/utils/package-manager.d.ts +2 -2
  168. package/dist/utils/package-manager.d.ts.map +1 -1
  169. package/dist/utils/package-manager.js +6 -3
  170. package/dist/utils/package-manager.js.map +1 -1
  171. package/dist/utils/python-uv.d.ts +5 -7
  172. package/dist/utils/python-uv.d.ts.map +1 -1
  173. package/dist/utils/python-uv.js +50 -24
  174. package/dist/utils/python-uv.js.map +1 -1
  175. package/dist/utils/react-dev.d.ts +4 -0
  176. package/dist/utils/react-dev.d.ts.map +1 -0
  177. package/dist/utils/react-dev.js +35 -0
  178. package/dist/utils/react-dev.js.map +1 -0
  179. package/dist/utils/semver.d.ts +14 -0
  180. package/dist/utils/semver.d.ts.map +1 -0
  181. package/dist/utils/semver.js +61 -0
  182. package/dist/utils/semver.js.map +1 -0
  183. package/package.json +7 -4
  184. package/src/__tests__/__snapshots__/boundaries.test.ts.snap +154 -0
  185. package/src/__tests__/agent-instructions.test.ts +6 -1
  186. package/src/__tests__/agent-loop-phase34.test.ts +143 -8
  187. package/src/__tests__/auth.test.ts +9 -0
  188. package/src/__tests__/azure-cli.test.ts +202 -0
  189. package/src/__tests__/boundaries.test.ts +42 -0
  190. package/src/__tests__/csharp-schema.test.ts +261 -0
  191. package/src/__tests__/dev.test.ts +13 -6
  192. package/src/__tests__/fixtures/az wrapper.cmd +2 -0
  193. package/src/__tests__/fixtures/azure-cli.cjs +153 -0
  194. package/src/__tests__/fixtures/provision-runner.cjs +28 -0
  195. package/src/__tests__/generated-project-matrix.test.ts +789 -0
  196. package/src/__tests__/home-page.test.ts +50 -0
  197. package/src/__tests__/init.test.ts +145 -5
  198. package/src/__tests__/mcp-version.test.ts +262 -0
  199. package/src/__tests__/mcp.test.ts +120 -3
  200. package/src/__tests__/multi-auth.test.ts +115 -4
  201. package/src/__tests__/package-manager.test.ts +17 -2
  202. package/src/__tests__/plan-apply.test.ts +179 -0
  203. package/src/__tests__/provision-runtime.test.ts +722 -0
  204. package/src/__tests__/python-uv.test.ts +50 -14
  205. package/src/__tests__/python-venv.test.ts +48 -0
  206. package/src/__tests__/react-dev.test.ts +72 -0
  207. package/src/__tests__/react.test.ts +838 -0
  208. package/src/__tests__/release-metadata.test.ts +121 -0
  209. package/src/__tests__/schema-analysis.test.ts +329 -0
  210. package/src/cli/commands/add-auth.ts +103 -29
  211. package/src/cli/commands/configure-mcp.ts +54 -0
  212. package/src/cli/commands/dev.ts +145 -73
  213. package/src/cli/commands/init.ts +147 -172
  214. package/src/cli/commands/migrations.ts +22 -0
  215. package/src/cli/commands/provision.ts +294 -56
  216. package/src/cli/commands/scaffold.ts +145 -22
  217. package/src/cli/index.ts +46 -10
  218. package/src/core/config.ts +21 -1
  219. package/src/core/operations/create-model.ts +51 -13
  220. package/src/core/operations/migration-generator.ts +14 -1
  221. package/src/core/operations/model-migration.ts +52 -0
  222. package/src/core/operations/provision-deployment.ts +183 -0
  223. package/src/core/operations/provision-operations.ts +662 -154
  224. package/src/core/operations/scaffold-apply.ts +2 -0
  225. package/src/core/operations/scaffold-machine.ts +1 -0
  226. package/src/core/operations/scaffold-plan.ts +5 -1
  227. package/src/core/project/agent-instructions.ts +17 -1
  228. package/src/core/project/agent-templates/AGENTS.md +113 -15
  229. package/src/core/project/agent-templates/CLAUDE.md +14 -1
  230. package/src/core/project/agent-templates/bff-routes.instructions.md +5 -3
  231. package/src/core/project/agent-templates/copilot-instructions.md +15 -1
  232. package/src/core/project/agent-templates/shared-models.instructions.md +11 -3
  233. package/src/core/project/boundaries.ts +62 -20
  234. package/src/core/project/capabilities.ts +63 -6
  235. package/src/core/project/home-page.ts +114 -0
  236. package/src/core/project/manifest.ts +52 -27
  237. package/src/core/project/mcp-config.ts +84 -0
  238. package/src/core/project/react-project.ts +529 -0
  239. package/src/core/project/state.ts +70 -0
  240. package/src/core/project/validation.ts +32 -10
  241. package/src/core/project/workflows.ts +53 -7
  242. package/src/core/scaffold/auth-generator.ts +26 -27
  243. package/src/core/scaffold/model-parser.ts +413 -129
  244. package/src/core/scaffold/native-schema-generator.ts +319 -103
  245. package/src/core/scaffold/openapi-generator.ts +63 -5
  246. package/src/core/scaffold/react-bff-generator.ts +292 -0
  247. package/src/core/scaffold/react-ui-generator.ts +51 -0
  248. package/src/core/scaffold/ui-generator.ts +70 -8
  249. package/src/core/verify/index.ts +118 -6
  250. package/src/index.ts +3 -1
  251. package/src/machine/contracts.ts +1 -0
  252. package/src/machine/errors.ts +4 -2
  253. package/src/machine/index.ts +67 -36
  254. package/src/mcp/index.ts +221 -31
  255. package/src/mcp/version-selection.ts +163 -0
  256. package/src/types/index.ts +4 -0
  257. package/src/utils/azure-cli.ts +315 -0
  258. package/src/utils/azure-diagnostics.ts +75 -0
  259. package/src/utils/package-manager.ts +7 -4
  260. package/src/utils/python-uv.ts +53 -33
  261. package/src/utils/react-dev.ts +31 -0
  262. package/src/utils/semver.ts +59 -0
@@ -48,7 +48,11 @@ exports.buildCSharpFunctionsProjectSource = buildCSharpFunctionsProjectSource;
48
48
  exports.buildSwallowKitConfigSource = buildSwallowKitConfigSource;
49
49
  exports.buildGeneratedProjectDependencies = buildGeneratedProjectDependencies;
50
50
  exports.buildGeneratedProjectDevDependencies = buildGeneratedProjectDevDependencies;
51
+ exports.ensureNextTailwindPostcssConfig = ensureNextTailwindPostcssConfig;
51
52
  exports.buildSwallowKitMcpProjectConfigSource = buildSwallowKitMcpProjectConfigSource;
53
+ exports.selectInitialMcpVersion = selectInitialMcpVersion;
54
+ exports.createSharedPackage = createSharedPackage;
55
+ exports.createAzureFunctionsProject = createAzureFunctionsProject;
52
56
  exports.buildTypeScriptFunctionsPackageJson = buildTypeScriptFunctionsPackageJson;
53
57
  exports.buildFunctionsHostKeyBicepExpression = buildFunctionsHostKeyBicepExpression;
54
58
  exports.buildStaticWebAppConfigBicepSource = buildStaticWebAppConfigBicepSource;
@@ -62,6 +66,7 @@ const fs = __importStar(require("fs"));
62
66
  const path = __importStar(require("path"));
63
67
  const child_process_1 = require("child_process");
64
68
  const prompts_1 = __importDefault(require("prompts"));
69
+ const react_project_1 = require("../../core/project/react-project");
65
70
  const package_manager_1 = require("../../utils/package-manager");
66
71
  const manifest_1 = require("../../core/project/manifest");
67
72
  const workflows_1 = require("../../core/project/workflows");
@@ -69,6 +74,8 @@ const agent_instructions_1 = require("../../core/project/agent-instructions");
69
74
  const migration_generator_1 = require("../../core/operations/migration-generator");
70
75
  const native_schema_generator_1 = require("../../core/scaffold/native-schema-generator");
71
76
  const python_uv_1 = require("../../utils/python-uv");
77
+ const home_page_1 = require("../../core/project/home-page");
78
+ const mcp_config_1 = require("../../core/project/mcp-config");
72
79
  function getStaticWebAppSku(plan) {
73
80
  return plan === 'free' ? 'Free' : 'Standard';
74
81
  }
@@ -156,7 +163,7 @@ function getFunctionsRuntimeConfig(backendLanguage) {
156
163
  return { name: "dotnet-isolated", version: "10.0" };
157
164
  }
158
165
  if (backendLanguage === "python") {
159
- return { name: "python", version: "3.11" };
166
+ return { name: "python", version: python_uv_1.DEFAULT_AZURE_PYTHON_VERSION };
160
167
  }
161
168
  return { name: "node", version: "22" };
162
169
  }
@@ -260,6 +267,9 @@ const VALID_COSMOS_DB_MODE = ['freetier', 'serverless'];
260
267
  const VALID_VNET = ['none', 'outbound'];
261
268
  const VALID_SWA_PLAN = ['free', 'standard'];
262
269
  function validateInitFlags(options) {
270
+ if (options.frontend && !["nextjs", "react"].includes(options.frontend)) {
271
+ throw new Error("Invalid --frontend: expected nextjs or react");
272
+ }
263
273
  if (options.packageManager && !['npm', 'pnpm'].includes(options.packageManager)) {
264
274
  throw new Error('Invalid --package-manager: expected npm or pnpm');
265
275
  }
@@ -320,24 +330,36 @@ async function initCommand(options) {
320
330
  process.exit(1);
321
331
  }
322
332
  }
333
+ // Preserve existing non-interactive invocations while offering a frontend choice in the wizard.
334
+ const frontend = options.frontend ?? (interactive
335
+ ? (await (0, prompts_1.default)({
336
+ type: "select", name: "frontend", message: "Frontend framework:",
337
+ choices: [{ title: "Next.js (App Router)", value: "nextjs" }, { title: "React (Vite SPA + SWA managed Functions BFF)", value: "react" }],
338
+ initial: 0,
339
+ })).frontend || "nextjs"
340
+ : "nextjs");
341
+ options = { ...options, frontend };
323
342
  // Use flag values if provided, otherwise prompt interactively
324
343
  const cicdProvider = options.cicd || await promptCiCd();
325
344
  const backendLanguage = options.backendLanguage || await promptBackendLanguage();
326
345
  const azureConfig = await promptAzureConfig(options);
327
- // Create Next.js project with create-next-app
328
- await createNextJsProject(options.name, pm);
329
- // Upgrade Next.js to specified version (or latest) to avoid cached old versions
330
- await upgradeNextJs(projectDir, options.nextVersion || 'latest', pm);
346
+ if (frontend === "react") {
347
+ (0, react_project_1.createReactProject)(projectDir, options.name, pm);
348
+ }
349
+ else {
350
+ await createNextJsProject(options.name, pm);
351
+ await upgradeNextJs(projectDir, options.nextVersion || 'latest', pm);
352
+ }
331
353
  // Add SwallowKit specific files
332
354
  await addSwallowKitFiles(projectDir, options, cicdProvider, azureConfig, pm, backendLanguage);
333
355
  // Create infrastructure files (Bicep)
334
356
  await createInfrastructure(projectDir, options.name, azureConfig, backendLanguage);
335
357
  // Create CI/CD files based on choice
336
358
  if (cicdProvider === 'github') {
337
- await createGitHubActionsWorkflows(projectDir, azureConfig, pm, backendLanguage);
359
+ await createGitHubActionsWorkflows(projectDir, azureConfig, pm, backendLanguage, frontend);
338
360
  }
339
361
  else if (cicdProvider === 'azure') {
340
- await createAzurePipelines(projectDir, pm, backendLanguage);
362
+ await createAzurePipelines(projectDir, pm, backendLanguage, frontend);
341
363
  }
342
364
  await (0, manifest_1.syncProjectManifest)(projectDir);
343
365
  // Initialize Git repository and create initial commit
@@ -588,7 +610,11 @@ function parseIgnoredBuilds(output) {
588
610
  return Array.from(new Set(list));
589
611
  }
590
612
  function injectSwallowKitNextConfig(nextConfigContent, projectName) {
591
- return nextConfigContent.replace(/(const\s+nextConfig[:\s]*(?::\s*NextConfig\s*)?=\s*\{)(\s*\/\*[^*]*\*\/)?/, `$1\n output: 'standalone',\n agentRules: false, // SwallowKit owns the canonical root AGENTS.md contract\n transpilePackages: ['@${projectName}/shared'],\n serverExternalPackages: ['applicationinsights', 'diagnostic-channel-publishers'],$2`);
613
+ const compatibleConfig = nextConfigContent.replace(/(\b(?:cacheComponents|partialPrefetching)\s*:\s*)true\b/g, '$1false');
614
+ const cacheComponentsSetting = /\bcacheComponents\s*:/.test(compatibleConfig)
615
+ ? ''
616
+ : ' cacheComponents: false,\n';
617
+ return compatibleConfig.replace(/(const\s+nextConfig[:\s]*(?::\s*NextConfig\s*)?=\s*\{)(\s*\/\*[^*]*\*\/)?/, `$1\n output: 'standalone',\n${cacheComponentsSetting} agentRules: false, // SwallowKit owns the canonical root AGENTS.md contract\n transpilePackages: ['@${projectName}/shared'],\n serverExternalPackages: ['applicationinsights', 'diagnostic-channel-publishers'],$2`);
592
618
  }
593
619
  function buildCSharpFunctionsProgramSource() {
594
620
  return `using Microsoft.Azure.Functions.Worker;
@@ -623,7 +649,7 @@ function buildCSharpFunctionsProjectSource() {
623
649
  <None Remove="generated\\**\\bin\\**;generated\\**\\obj\\**" />
624
650
  </ItemGroup>
625
651
  <ItemGroup>
626
- <PackageReference Include="Microsoft.Azure.Cosmos" Version="3.47.0" />
652
+ <PackageReference Include="Microsoft.Azure.Cosmos" Version="3.63.2" />
627
653
  <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
628
654
  <PackageReference Include="Azure.Identity" Version="1.17.0" />
629
655
  <PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.52.0" />
@@ -635,8 +661,11 @@ function buildCSharpFunctionsProjectSource() {
635
661
  </Project>
636
662
  `;
637
663
  }
638
- function buildSwallowKitConfigSource(backendLanguage) {
664
+ function buildSwallowKitConfigSource(backendLanguage, frontend = "nextjs") {
639
665
  return `module.exports = {
666
+ frontend: {
667
+ framework: '${frontend}',
668
+ },
640
669
  backend: {
641
670
  language: '${backendLanguage}',
642
671
  },
@@ -650,10 +679,9 @@ function buildSwallowKitConfigSource(backendLanguage) {
650
679
  }
651
680
  `;
652
681
  }
653
- function buildGeneratedProjectDependencies(projectName, pm = 'pnpm') {
682
+ function buildGeneratedProjectDependencies(projectName, pm = 'pnpm', frontend = "nextjs") {
654
683
  return {
655
- '@azure/cosmos': '^4.0.0',
656
- 'applicationinsights': '^3.3.0',
684
+ ...(frontend === "nextjs" ? { '@azure/cosmos': '^4.0.0', 'applicationinsights': '^3.3.0' } : {}),
657
685
  'zod': '^4.0.0',
658
686
  [`@${projectName}/shared`]: pm === 'pnpm' ? 'workspace:*' : 'file:shared',
659
687
  };
@@ -675,13 +703,27 @@ function getSwallowKitPackageMetadata() {
675
703
  version: "latest",
676
704
  };
677
705
  }
678
- function buildGeneratedProjectDevDependencies() {
706
+ function buildGeneratedProjectDevDependencies(frontend = "nextjs") {
679
707
  const { name, version } = getSwallowKitPackageMetadata();
680
708
  return {
681
709
  [name]: version,
710
+ ...(frontend === "nextjs" ? { "@tailwindcss/postcss": "^4.0.0" } : {}),
682
711
  };
683
712
  }
684
- function buildSwallowKitMcpProjectConfigSource(pm = 'pnpm') {
713
+ function ensureNextTailwindPostcssConfig(projectDir) {
714
+ if (["postcss.config.mjs", "postcss.config.js", "postcss.config.cjs", "postcss.config.json"]
715
+ .some(file => fs.existsSync(path.join(projectDir, file)))) {
716
+ return;
717
+ }
718
+ // New create-next-app templates can configure Tailwind for Turbopack only.
719
+ fs.writeFileSync(path.join(projectDir, "postcss.config.mjs"), `export default {
720
+ plugins: {
721
+ "@tailwindcss/postcss": {},
722
+ },
723
+ };
724
+ `);
725
+ }
726
+ function buildSwallowKitMcpProjectConfigSource(pm = 'pnpm', mcpVersion = mcp_config_1.MCP_LATEST) {
685
727
  const { name } = getSwallowKitPackageMetadata();
686
728
  return JSON.stringify({
687
729
  mcpServers: {
@@ -692,31 +734,43 @@ function buildSwallowKitMcpProjectConfigSource(pm = 'pnpm') {
692
734
  : ["--yes", "--package", `${name}@\${SWALLOWKIT_MCP_VERSION}`, "swallowkit-mcp"],
693
735
  cwd: ".",
694
736
  env: {
695
- SWALLOWKIT_MCP_VERSION: "latest",
737
+ SWALLOWKIT_MCP_VERSION: mcpVersion,
696
738
  },
697
739
  },
698
740
  },
699
741
  }, null, 2);
700
742
  }
743
+ /** latest by default; pinned to this CLI version only when it is newer than published latest. */
744
+ function selectInitialMcpVersion(lookupLatest = mcp_config_1.lookupPublishedLatestVersion) {
745
+ const { name, version } = getSwallowKitPackageMetadata();
746
+ if (version === mcp_config_1.MCP_LATEST)
747
+ return { version: mcp_config_1.MCP_LATEST, reason: "SwallowKit package metadata is unavailable." };
748
+ return (0, mcp_config_1.resolveMcpPinVersion)(version, lookupLatest(name));
749
+ }
701
750
  async function addSwallowKitFiles(projectDir, options, cicdChoice, azureConfig, pm, backendLanguage) {
702
751
  console.log('📦 Adding SwallowKit files...\n');
703
752
  const projectName = options.name;
753
+ const frontend = options.frontend ?? "nextjs";
704
754
  // 1. Update package.json to add runtime dependencies for generated projects
705
755
  const packageJsonPath = path.join(projectDir, 'package.json');
706
756
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
707
757
  // Root BFF/auth code imports Zod directly; declare it as well as the shared package.
708
758
  packageJson.dependencies = {
709
759
  ...packageJson.dependencies,
710
- ...buildGeneratedProjectDependencies(projectName, pm),
760
+ ...buildGeneratedProjectDependencies(projectName, pm, frontend),
711
761
  };
712
762
  packageJson.devDependencies = {
713
763
  ...packageJson.devDependencies,
714
- ...buildGeneratedProjectDevDependencies(),
764
+ ...buildGeneratedProjectDevDependencies(frontend),
715
765
  };
716
766
  packageJson.scripts = {
717
767
  ...packageJson.scripts,
718
- 'build': (0, package_manager_1.getBuildScript)(pm),
719
- 'start': 'next start',
768
+ 'build': (0, package_manager_1.getBuildScript)(pm, frontend),
769
+ 'start': frontend === "react" ? "vite preview" : 'next start',
770
+ ...(frontend === "react" ? {
771
+ typecheck: `tsc --noEmit && ${(0, package_manager_1.getCommands)(pm).exec} tsc --noEmit -p bff`,
772
+ "bff:start": `${(0, package_manager_1.getCommands)(pm).runFilter("bff")} start`,
773
+ } : {}),
720
774
  'functions:start': (0, package_manager_1.getFunctionsStartScript)(pm, backendLanguage),
721
775
  };
722
776
  packageJson.engines = {
@@ -728,16 +782,18 @@ async function addSwallowKitFiles(projectDir, options, cicdChoice, azureConfig,
728
782
  };
729
783
  delete packageJson.packageManager;
730
784
  // Keep package.json workspaces for npm-based CI/CD even when local development uses pnpm.
731
- const workspacePackages = usesNodeFunctionsProject(backendLanguage) ? ['shared', 'functions'] : ['shared'];
785
+ const workspacePackages = [...(usesNodeFunctionsProject(backendLanguage) ? ['shared', 'functions'] : ['shared']), ...(frontend === "react" ? ["bff"] : [])];
732
786
  const wsConfig = (0, package_manager_1.getWorkspaceConfig)(pm, workspacePackages);
733
787
  packageJson.workspaces = workspacePackages;
734
788
  if (wsConfig.type !== 'file') {
735
789
  packageJson.workspaces = wsConfig.value;
736
790
  }
737
791
  fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
792
+ if (frontend === "nextjs")
793
+ ensureNextTailwindPostcssConfig(projectDir);
738
794
  // Create pnpm-workspace.yaml for local pnpm development when needed.
739
795
  if (wsConfig.type === 'file') {
740
- fs.writeFileSync(path.join(projectDir, wsConfig.filename), wsConfig.content);
796
+ fs.writeFileSync(path.join(projectDir, wsConfig.filename), wsConfig.content + (frontend === "react" ? " esbuild: true\n" : ""));
741
797
  }
742
798
  // Don't install yet — wait until all workspace packages (shared, functions) are created
743
799
  // 2. Update next.config to add standalone output
@@ -770,17 +826,18 @@ async function addSwallowKitFiles(projectDir, options, cicdChoice, azureConfig,
770
826
  fs.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2));
771
827
  }
772
828
  // 3. Create SwallowKit config
773
- const swallowkitConfig = buildSwallowKitConfigSource(backendLanguage);
829
+ const swallowkitConfig = buildSwallowKitConfigSource(backendLanguage, frontend);
774
830
  fs.writeFileSync(path.join(projectDir, 'swallowkit.config.js'), swallowkitConfig);
775
831
  // 4. Create shared workspace package for Zod models (Single Source of Truth)
776
832
  await createSharedPackage(projectDir, projectName);
777
- // Create lib directory for Next.js-specific utilities
778
- const libDir = path.join(projectDir, 'lib');
779
- // Create lib/api directory for backend utilities
780
- const apiLibDir = path.join(libDir, 'api');
781
- fs.mkdirSync(apiLibDir, { recursive: true });
782
- // Create backend utility for calling Azure Functions
783
- const backendUtilContent = `// Get Functions base URL at runtime (not at build time)
833
+ if (frontend === "nextjs") {
834
+ // Create lib directory for Next.js-specific utilities
835
+ const libDir = path.join(projectDir, 'lib');
836
+ // Create lib/api directory for backend utilities
837
+ const apiLibDir = path.join(libDir, 'api');
838
+ fs.mkdirSync(apiLibDir, { recursive: true });
839
+ // Create backend utility for calling Azure Functions
840
+ const backendUtilContent = `// Get Functions base URL at runtime (not at build time)
784
841
  function getFunctionsBaseUrl(): string {
785
842
  return process.env.BACKEND_FUNCTIONS_BASE_URL || process.env.FUNCTIONS_BASE_URL || 'http://localhost:7071';
786
843
  }
@@ -890,7 +947,8 @@ export const api = {
890
947
  },
891
948
  };
892
949
  `;
893
- fs.writeFileSync(path.join(apiLibDir, 'backend.ts'), backendUtilContent);
950
+ fs.writeFileSync(path.join(apiLibDir, 'backend.ts'), backendUtilContent);
951
+ }
894
952
  // 5. Create components directory
895
953
  const componentsDir = path.join(projectDir, 'components');
896
954
  fs.mkdirSync(componentsDir, { recursive: true });
@@ -976,7 +1034,8 @@ export async function register() {
976
1034
  }
977
1035
  }
978
1036
  `;
979
- fs.writeFileSync(path.join(projectDir, 'instrumentation.ts'), instrumentationContent);
1037
+ if (frontend === "nextjs")
1038
+ fs.writeFileSync(path.join(projectDir, 'instrumentation.ts'), instrumentationContent);
980
1039
  // 8. Create .env.local for local development
981
1040
  const envLocalContent = [
982
1041
  '# Azure Functions Backend URL (Local)',
@@ -1005,21 +1064,30 @@ export async function register() {
1005
1064
  ".json": "application/json"
1006
1065
  }
1007
1066
  };
1008
- fs.writeFileSync(path.join(projectDir, 'staticwebapp.config.json'), JSON.stringify(swaConfig, null, 2));
1067
+ fs.writeFileSync(path.join(projectDir, 'staticwebapp.config.json'), JSON.stringify(frontend === "react" ? (0, react_project_1.buildReactSwaConfig)() : swaConfig, null, 2));
1009
1068
  // 14. Create Azure Functions project
1010
1069
  await createAzureFunctionsProject(projectDir, pm, backendLanguage);
1011
1070
  // 15. Create BFF API route to call Azure Functions
1012
- await createBffApiRoute(projectDir);
1013
- // 16. Create home page
1014
- await createHomePage(projectDir, pm);
1071
+ if (frontend === "react") {
1072
+ (0, react_project_1.createReactBffProject)(projectDir, projectName, pm);
1073
+ }
1074
+ else {
1075
+ await createBffApiRoute(projectDir);
1076
+ await createHomePage(projectDir, pm);
1077
+ }
1015
1078
  // 17. Install all workspace dependencies (root + shared + functions)
1016
1079
  console.log('📦 Installing workspace dependencies...\n');
1017
1080
  await installDependencies(projectDir, pm);
1018
1081
  console.log('✅ Project structure created\n');
1019
1082
  // 18. Create README.md
1020
- createReadme(projectDir, projectName, cicdChoice, azureConfig, pm, backendLanguage);
1083
+ if (frontend === "react") {
1084
+ fs.writeFileSync(path.join(projectDir, "README.md"), (0, react_project_1.buildReactReadme)(projectName, pm, backendLanguage));
1085
+ }
1086
+ else {
1087
+ createReadme(projectDir, projectName, cicdChoice, azureConfig, pm, backendLanguage);
1088
+ }
1021
1089
  // 19. Create AI agent instruction files (AGENTS.md, CLAUDE.md, .github/copilot-instructions.md, etc.)
1022
- createAiAgentFiles(projectDir, projectName, backendLanguage, pm);
1090
+ createAiAgentFiles(projectDir, projectName, backendLanguage, pm, frontend);
1023
1091
  }
1024
1092
  async function createSharedPackage(projectDir, projectName) {
1025
1093
  console.log('📦 Creating shared workspace package for Zod models...\n');
@@ -1049,7 +1117,7 @@ async function createSharedPackage(projectDir, projectName) {
1049
1117
  // shared/tsconfig.json
1050
1118
  fs.writeFileSync(path.join(sharedDir, 'tsconfig.json'), JSON.stringify(buildSharedTsConfig(), null, 2));
1051
1119
  // shared/index.ts (empty re-export file, scaffold will add entries)
1052
- fs.writeFileSync(path.join(sharedDir, 'index.ts'), `// Shared Zod models — auto-managed by SwallowKit scaffold command\n// Do not edit the export list below manually\n`);
1120
+ fs.writeFileSync(path.join(sharedDir, 'index.ts'), `// Shared Zod models. SwallowKit create-model/scaffold append missing model exports.\n// You may add exports manually; existing exports and comments are preserved. No managed markers.\n`);
1053
1121
  // shared/.gitignore
1054
1122
  fs.writeFileSync(path.join(sharedDir, '.gitignore'), `node_modules\ndist\n`);
1055
1123
  console.log('✅ Shared package created\n');
@@ -1287,7 +1355,6 @@ public sealed class GreetFunction
1287
1355
  `);
1288
1356
  }
1289
1357
  function createPythonFunctionsProject(projectDir, functionsDir) {
1290
- fs.writeFileSync(path.join(projectDir, '.python-version'), '3.11\n');
1291
1358
  ensureProjectGitignoreEntry(projectDir, '.uv');
1292
1359
  fs.writeFileSync(path.join(functionsDir, 'requirements.txt'), `azure-functions>=1.20.0
1293
1360
  azure-cosmos>=4.9.0
@@ -1408,114 +1475,7 @@ export async function POST(request: NextRequest) {
1408
1475
  }
1409
1476
  async function createHomePage(projectDir, pm = 'pnpm') {
1410
1477
  console.log('📦 Creating home page...\n');
1411
- const pmCmd = (0, package_manager_1.getCommands)(pm);
1412
- const pageContent = `'use client'
1413
-
1414
- export const dynamic = 'force-dynamic';
1415
-
1416
- import { useState } from 'react';
1417
- import { scaffoldConfig } from '@/lib/scaffold-config';
1418
-
1419
- export default function Home() {
1420
- const [greetingStatus, setGreetingStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
1421
- const [message, setMessage] = useState('');
1422
-
1423
- const testConnection = async () => {
1424
- setGreetingStatus('loading');
1425
- try {
1426
- const response = await fetch('/api/greet?name=SwallowKit');
1427
- const data = await response.json();
1428
- if (!response.ok) {
1429
- throw new Error(data.error || \`Server error: \${response.status}\`);
1430
- }
1431
- setMessage(data.message);
1432
- setGreetingStatus('success');
1433
- } catch (error) {
1434
- setMessage(error instanceof Error ? error.message : 'Failed to connect to Azure Functions');
1435
- setGreetingStatus('error');
1436
- }
1437
- };
1438
-
1439
- return (
1440
- <div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-50 dark:from-gray-900 dark:to-gray-800">
1441
- <div className="container mx-auto px-4 py-12">
1442
- <header className="text-center mb-16">
1443
- <h1 className="text-5xl font-bold text-gray-800 dark:text-white mb-4">
1444
- Welcome to SwallowKit
1445
- </h1>
1446
- <p className="text-xl text-gray-600 dark:text-gray-400">
1447
- Next.js on Azure Static Web Apps + Functions + Cosmos DB — Zod schema sharing
1448
- </p>
1449
- </header>
1450
-
1451
- {/* Connection Test */}
1452
- <section className="max-w-2xl mx-auto mb-12">
1453
- <div className="bg-white dark:bg-gray-800 rounded-xl p-8 border border-gray-200 dark:border-gray-700">
1454
- <h2 className="text-2xl font-semibold mb-4 text-gray-900 dark:text-gray-100">
1455
- Test BFF → Functions Connection
1456
- </h2>
1457
- <button
1458
- onClick={testConnection}
1459
- disabled={greetingStatus === 'loading'}
1460
- className="px-6 py-3 bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 text-white rounded-lg font-medium transition-colors"
1461
- >
1462
- {greetingStatus === 'loading' ? 'Testing...' : 'Test Connection'}
1463
- </button>
1464
- {greetingStatus === 'success' && (
1465
- <div className="mt-4 p-4 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg">
1466
- <p className="text-green-800 dark:text-green-200 font-medium">✅ Connection successful!</p>
1467
- <p className="text-green-700 dark:text-green-300 text-sm mt-1">{message}</p>
1468
- </div>
1469
- )}
1470
- {greetingStatus === 'error' && (
1471
- <div className="mt-4 p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg">
1472
- <p className="text-red-800 dark:text-red-200 font-medium">❌ Connection failed</p>
1473
- <p className="text-red-700 dark:text-red-300 text-sm mt-1">{message}</p>
1474
- </div>
1475
- )}
1476
- </div>
1477
- </section>
1478
-
1479
- {/* Scaffolded Models Menu */}
1480
- {scaffoldConfig.models.length > 0 ? (
1481
- <section className="max-w-6xl mx-auto">
1482
- <h2 className="text-3xl font-bold mb-8 text-gray-900 dark:text-gray-100">Your Models</h2>
1483
- <div className="grid gap-6 grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
1484
- {scaffoldConfig.models.map((model) => (
1485
- <a
1486
- key={model.name}
1487
- href={model.path}
1488
- className="block p-8 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl hover:shadow-lg hover:border-blue-400 dark:hover:border-blue-600 transition-all"
1489
- >
1490
- <h3 className="text-2xl font-semibold mb-2 text-gray-900 dark:text-gray-100">{model.label}</h3>
1491
- <p className="text-gray-600 dark:text-gray-400">Manage {model.label.toLowerCase()}</p>
1492
- </a>
1493
- ))}
1494
- </div>
1495
- </section>
1496
- ) : (
1497
- <section className="max-w-2xl mx-auto text-center">
1498
- <div className="bg-white dark:bg-gray-800 rounded-xl p-12 border border-gray-200 dark:border-gray-700">
1499
- <h2 className="text-2xl font-semibold mb-4 text-gray-900 dark:text-gray-100">Get Started</h2>
1500
- <p className="text-gray-600 dark:text-gray-400 mb-6">
1501
- Create your first model with Zod and generate CRUD operations automatically.
1502
- </p>
1503
- <code className="block bg-gray-100 dark:bg-gray-900 p-4 rounded text-left text-sm">
1504
- ${pmCmd.exec} swallowkit scaffold shared/models/your-model.ts
1505
- </code>
1506
- </div>
1507
- </section>
1508
- )}
1509
-
1510
- <footer className="mt-16 text-center text-gray-600 dark:text-gray-400 text-sm">
1511
- <p>Built with SwallowKit</p>
1512
- </footer>
1513
- </div>
1514
- </div>
1515
- );
1516
- }
1517
- `;
1518
- fs.writeFileSync(path.join(projectDir, 'app', 'page.tsx'), pageContent);
1478
+ fs.writeFileSync(path.join(projectDir, 'app', 'page.tsx'), (0, home_page_1.buildHomePage)("nextjs", pm));
1519
1479
  console.log('✅ Home page created\n');
1520
1480
  // Create initial scaffold-config.ts
1521
1481
  const scaffoldConfigDir = path.join(projectDir, 'lib');
@@ -1556,9 +1516,8 @@ function createReadme(projectDir, projectName, cicdChoice, azureConfig, pm, back
1556
1516
  const backendScaffoldNote = backendLanguage === 'typescript'
1557
1517
  ? '- Azure Functions CRUD endpoints'
1558
1518
  : `- Azure Functions ${backendLanguageLabel} CRUD handlers\n- OpenAPI export + native-generated ${backendLanguageLabel} schema assets`;
1559
- const pythonUvPaths = (0, python_uv_1.getProjectLocalUvPaths)(projectDir);
1560
1519
  const pythonLocalDevNote = backendLanguage === 'python'
1561
- ? `\n**Python local dev note**: SwallowKit uses \`uv\` for Python backends and keeps the managed Python runtime under \`${path.relative(projectDir, pythonUvPaths.pythonInstallDir)}\`. Local Azure Functions runs from \`functions/.venv\`, schema generation uses \`functions/.codegen-venv\`, and \`swallowkit dev\` bootstraps a project-local \`uv\` binary automatically when needed. Keep \`functions/requirements.txt\` and \`functions/requirements.codegen.txt\` as the dependency sources of truth.\n`
1520
+ ? `\n**Python local dev note**: SwallowKit uses normal \`uv\` interpreter discovery and settings, including installed Python, \`.python-version\`, and \`UV_PYTHON\`. It does not pin local Python to the Azure deployment version (${python_uv_1.DEFAULT_AZURE_PYTHON_VERSION}). Local Functions uses \`functions/.venv\` and schema generation uses \`functions/.codegen-venv\`. Existing environments are preserved; explicitly recreate them with uv when switching interpreters. A missing uv binary is bootstrapped under \`.uv/bin\` without changing PATH. Keep \`functions/requirements.txt\` and \`functions/requirements.codegen.txt\` as the dependency sources of truth.\n`
1562
1521
  : '';
1563
1522
  const readme = `# ${projectName}
1564
1523
 
@@ -1666,10 +1625,12 @@ ${pmCmd.exec} swallowkit provision --resource-group <rg-name>
1666
1625
 
1667
1626
  This creates:
1668
1627
  - Static Web App (\`swa-${projectName}\`)
1669
- - Azure Functions (\`func-${projectName}\`)
1670
- - Cosmos DB (\`cosmos-${projectName}\`)
1628
+ - Azure Functions (\`func-${projectName}-<suffix>\`)
1629
+ - Cosmos DB (\`cosmos-${projectName}-<suffix>\`)
1671
1630
  - Storage Account
1672
1631
 
1632
+ Cosmos DB and Function App names must be globally unique, so they include a short suffix derived from the resource group. When provisioning completes, the command prints the actual resource names and the CI/CD settings to use.
1633
+
1673
1634
  You will be prompted to select Azure regions:
1674
1635
  1. **Primary location**: For Functions and Cosmos DB (default: Japan East)
1675
1636
  2. **Static Web App location**: Limited availability (default: East Asia)
@@ -1685,12 +1646,12 @@ ${cicdChoice === 'github' ? `#### GitHub Actions
1685
1646
 
1686
1647
  2. Get Function App publish profile:
1687
1648
  \`\`\`bash
1688
- az webapp deployment list-publishing-profiles --name func-${projectName} --resource-group <rg-name> --xml
1649
+ az webapp deployment list-publishing-profiles --name <functions-app-name> --resource-group <rg-name> --xml
1689
1650
  \`\`\`
1690
1651
 
1691
1652
  3. Add secrets to GitHub repository:
1692
1653
  - \`AZURE_STATIC_WEB_APPS_API_TOKEN\`: SWA deployment token (from step 1)
1693
- - \`AZURE_FUNCTIONAPP_NAME\`: \`func-${projectName}\`
1654
+ - \`AZURE_FUNCTIONAPP_NAME\`: Functions app name printed by \`swallowkit provision\` (\`func-${projectName}-<suffix>\`)
1694
1655
  - \`AZURE_FUNCTIONAPP_PUBLISH_PROFILE\`: Functions publish profile (from step 2)
1695
1656
 
1696
1657
  4. Push to \`main\` branch to trigger deployment (or use **Actions** → **Run workflow** for manual deployment)` : cicdChoice === 'azure' ? `#### Azure Pipelines
@@ -1712,8 +1673,10 @@ az staticwebapp deploy --name swa-${projectName} --resource-group <rg-name> --ap
1712
1673
  \`\`\`bash
1713
1674
  cd functions
1714
1675
  ${pmCmd.run} build
1715
- func azure functionapp publish func-${projectName}
1716
- \`\`\``}
1676
+ func azure functionapp publish <functions-app-name>
1677
+ \`\`\`
1678
+
1679
+ Use the Functions app name printed by \`swallowkit provision\`.`}
1717
1680
 
1718
1681
  ## 🔧 Available Commands
1719
1682
 
@@ -1767,20 +1730,24 @@ This project was generated by SwallowKit. If you encounter any issues or have su
1767
1730
  fs.writeFileSync(path.join(projectDir, 'README.md'), readme);
1768
1731
  console.log('✅ README.md created\n');
1769
1732
  }
1770
- function createAiAgentFiles(projectDir, projectName, backendLanguage, pm) {
1733
+ function createAiAgentFiles(projectDir, projectName, backendLanguage, pm, frontend = "nextjs") {
1771
1734
  console.log('🤖 Creating AI agent instruction files...\n');
1772
1735
  const runCmd = (0, package_manager_1.getCommands)(pm).exec;
1773
1736
  (0, agent_instructions_1.writeAgentInstructionFiles)(projectDir, {
1774
1737
  projectName,
1775
1738
  backendLanguage,
1776
1739
  packageManager: pm,
1740
+ frontend,
1777
1741
  });
1778
1742
  console.log(' ✅ AGENTS.md (canonical project contract)');
1779
1743
  console.log(' ✅ CLAUDE.md (Claude Code adapter)');
1780
1744
  console.log(' ✅ .github/copilot-instructions.md (GitHub Copilot adapter)');
1781
1745
  console.log(' ✅ .github/instructions/ (path-specific instructions)');
1782
- fs.writeFileSync(path.join(projectDir, '.mcp.json'), buildSwallowKitMcpProjectConfigSource(pm));
1783
- console.log(' ✅ .mcp.json (project-scoped MCP bootstrap)');
1746
+ const mcpVersion = selectInitialMcpVersion();
1747
+ fs.writeFileSync(path.join(projectDir, '.mcp.json'), buildSwallowKitMcpProjectConfigSource(pm, mcpVersion.version));
1748
+ console.log(mcpVersion.version === mcp_config_1.MCP_LATEST
1749
+ ? ' ✅ .mcp.json (project-scoped MCP bootstrap, resolves latest)'
1750
+ : ` ✅ .mcp.json (project-scoped MCP bootstrap, pinned to ${mcpVersion.version}: ${mcpVersion.reason} Run "${runCmd} swallowkit configure-mcp" to re-select later.)`);
1784
1751
  (0, workflows_1.writeWorkflowDocs)(projectDir, runCmd);
1785
1752
  console.log(' ✅ .swallowkit/workflows/ (agent-agnostic autonomous-loop runbooks)');
1786
1753
  const skillPaths = (0, workflows_1.writeAgentSkills)(projectDir, runCmd);
@@ -1858,6 +1825,14 @@ param cosmosDbMode string = '${azureConfig.cosmosDbMode}'
1858
1825
  @description('Enable VNet integration')
1859
1826
  param enableVNet bool = ${enableVNet}
1860
1827
 
1828
+ // Cosmos DB account and Function App names are globally unique across Azure.
1829
+ // A suffix derived from the resource group keeps names stable for redeployments
1830
+ // to the same resource group while avoiding collisions with other environments
1831
+ // and with names Azure still reserves after a previous resource group is deleted.
1832
+ var resourceSuffix = take(uniqueString(resourceGroup().id, projectName), 6)
1833
+ var cosmosAccountName = 'cosmos-\${take(toLower(projectName), 30)}-\${resourceSuffix}'
1834
+ var functionsAppName = 'func-\${take(projectName, 40)}-\${resourceSuffix}'
1835
+
1861
1836
  // Shared Log Analytics Workspace (in Functions region for data residency)
1862
1837
  module logAnalytics 'modules/loganalytics.bicep' = {
1863
1838
  name: 'logAnalytics'
@@ -1911,7 +1886,7 @@ module vnet 'modules/vnet.bicep' = if (enableVNet) {
1911
1886
  module cosmosDbFreeTier 'modules/cosmosdb-freetier.bicep' = if (cosmosDbMode == 'freetier') {
1912
1887
  name: 'cosmosDb'
1913
1888
  params: {
1914
- accountName: 'cosmos-\${projectName}'
1889
+ accountName: cosmosAccountName
1915
1890
  databaseName: '\${projectName}Database'
1916
1891
  location: location
1917
1892
  publicNetworkAccess: enableVNet ? 'Disabled' : 'Enabled'
@@ -1921,7 +1896,7 @@ module cosmosDbFreeTier 'modules/cosmosdb-freetier.bicep' = if (cosmosDbMode ==
1921
1896
  module cosmosDbServerless 'modules/cosmosdb-serverless.bicep' = if (cosmosDbMode == 'serverless') {
1922
1897
  name: 'cosmosDb'
1923
1898
  params: {
1924
- accountName: 'cosmos-\${projectName}'
1899
+ accountName: cosmosAccountName
1925
1900
  databaseName: '\${projectName}Database'
1926
1901
  location: location
1927
1902
  publicNetworkAccess: enableVNet ? 'Disabled' : 'Enabled'
@@ -1945,7 +1920,7 @@ module cosmosPrivateEndpoint 'modules/private-endpoint-cosmos.bicep' = if (enabl
1945
1920
  module functionsFlex 'modules/functions-flex.bicep' = {
1946
1921
  name: 'functionsApp'
1947
1922
  params: {
1948
- name: 'func-\${projectName}'
1923
+ name: functionsAppName
1949
1924
  location: location
1950
1925
  storageAccountName: 'stg\${uniqueString(resourceGroup().id, projectName)}'
1951
1926
  appInsightsConnectionString: appInsightsFunctions.outputs.connectionString
@@ -2662,7 +2637,7 @@ jobs:
2662
2637
  ${commonSetup} - name: Setup Python
2663
2638
  uses: actions/setup-python@v5
2664
2639
  with:
2665
- python-version: '3.11'
2640
+ python-version: '${python_uv_1.DEFAULT_AZURE_PYTHON_VERSION}'
2666
2641
 
2667
2642
  - name: Install Functions dependencies
2668
2643
  run: |
@@ -2847,7 +2822,7 @@ variables:
2847
2822
  steps:
2848
2823
  ${commonSetup} - task: UsePythonVersion@0
2849
2824
  inputs:
2850
- versionSpec: '3.11'
2825
+ versionSpec: '${python_uv_1.DEFAULT_AZURE_PYTHON_VERSION}'
2851
2826
  displayName: 'Install Python'
2852
2827
 
2853
2828
  - script: |
@@ -2878,7 +2853,9 @@ ${commonSetup} - task: UsePythonVersion@0
2878
2853
  function getSwaNodeVersionCommand() {
2879
2854
  return `node -e "const fs=require('fs');const p=JSON.parse(fs.readFileSync('package.json','utf8'));p.engines={...p.engines,node:'>=22.14.0 <23'};fs.writeFileSync('package.json',JSON.stringify(p,null,2)+String.fromCharCode(10));"`;
2880
2855
  }
2881
- function buildAzureSwaPipeline(pm, projectPm = pm) {
2856
+ function buildAzureSwaPipeline(pm, projectPm = pm, frontend = "nextjs") {
2857
+ if (frontend === "react")
2858
+ return (0, react_project_1.buildReactAzureSwaPipeline)(projectPm);
2882
2859
  // SWA/Oryx also invokes npm internally; normalize only the disposable CI checkout.
2883
2860
  pm = 'npm';
2884
2861
  const pmCmd = (0, package_manager_1.getCommands)(pm);
@@ -3056,19 +3033,21 @@ function getGitHubNpmWorkspaceNormalizationStep(pm, projectPm) {
3056
3033
  npm install --package-lock-only --ignore-scripts
3057
3034
  `;
3058
3035
  }
3059
- async function createGitHubActionsWorkflows(projectDir, azureConfig, pm, backendLanguage) {
3036
+ async function createGitHubActionsWorkflows(projectDir, azureConfig, pm, backendLanguage, frontend = "nextjs") {
3060
3037
  console.log('📦 Creating GitHub Actions workflows...\n');
3061
3038
  const workflowsDir = path.join(projectDir, '.github', 'workflows');
3062
3039
  fs.mkdirSync(workflowsDir, { recursive: true });
3063
3040
  const actionsPm = pm;
3064
- const swaWorkflow = buildGitHubSwaWorkflow(actionsPm, pm);
3041
+ const swaWorkflow = buildGitHubSwaWorkflow(actionsPm, pm, frontend);
3065
3042
  fs.writeFileSync(path.join(workflowsDir, 'deploy-swa.yml'), swaWorkflow);
3066
3043
  // deploy-functions.yml
3067
3044
  const functionsWorkflow = getGitHubFunctionsWorkflow(actionsPm, backendLanguage, pm);
3068
3045
  fs.writeFileSync(path.join(workflowsDir, 'deploy-functions.yml'), functionsWorkflow);
3069
3046
  console.log('✅ GitHub Actions workflows created\n');
3070
3047
  }
3071
- function buildGitHubSwaWorkflow(pm, projectPm = pm) {
3048
+ function buildGitHubSwaWorkflow(pm, projectPm = pm, frontend = "nextjs") {
3049
+ if (frontend === "react")
3050
+ return (0, react_project_1.buildReactGitHubSwaWorkflow)(projectPm);
3072
3051
  // SWA/Oryx also invokes npm internally; normalize only the disposable CI checkout.
3073
3052
  pm = 'npm';
3074
3053
  const npmWorkspaceNormalizationStep = getGitHubNpmWorkspaceNormalizationStep(pm, projectPm);
@@ -3144,13 +3123,13 @@ ${npmWorkspaceNormalizationStep ? `\n${npmWorkspaceNormalizationStep}` : ''}
3144
3123
  NEXT_TURBOPACK_EXPERIMENTAL_USE_SYSTEM_TLS_CERTS: '1'
3145
3124
  `;
3146
3125
  }
3147
- async function createAzurePipelines(projectDir, pm, backendLanguage) {
3126
+ async function createAzurePipelines(projectDir, pm, backendLanguage, frontend = "nextjs") {
3148
3127
  console.log('📦 Creating Azure Pipelines...\n');
3149
3128
  const pipelinesPm = pm;
3150
3129
  const pipelinesDir = path.join(projectDir, 'pipelines');
3151
3130
  fs.mkdirSync(pipelinesDir, { recursive: true });
3152
3131
  // swa.yml
3153
- const swaPipeline = buildAzureSwaPipeline(pipelinesPm, pm);
3132
+ const swaPipeline = buildAzureSwaPipeline(pipelinesPm, pm, frontend);
3154
3133
  fs.writeFileSync(path.join(pipelinesDir, 'swa.yml'), swaPipeline);
3155
3134
  // functions.yml
3156
3135
  const functionsPipeline = getAzureFunctionsPipeline(pipelinesPm, backendLanguage, pm);