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
@@ -1,8 +1,9 @@
1
- import * as fs from "fs";
1
+ import * as fs from "fs";
2
2
  import * as path from "path";
3
3
  import { spawn, execSync } from "child_process";
4
4
  import prompts from "prompts";
5
- import { BackendLanguage } from "../../types";
5
+ import { BackendLanguage, FrontendFramework } from "../../types";
6
+ import { buildReactReadme, buildReactSwaConfig, createReactBffProject, createReactProject, buildReactGitHubSwaWorkflow, buildReactAzureSwaPipeline } from "../../core/project/react-project";
6
7
  import {
7
8
  type PackageManager,
8
9
  detectFromUserAgent,
@@ -24,11 +25,14 @@ import {
24
25
  buildCSharpCodegenToolManifestSource,
25
26
  buildPythonCodegenRequirementsSource,
26
27
  } from "../../core/scaffold/native-schema-generator";
27
- import { getProjectLocalUvPaths } from "../../utils/python-uv";
28
+ import { DEFAULT_AZURE_PYTHON_VERSION } from "../../utils/python-uv";
29
+ import { buildHomePage } from "../../core/project/home-page";
30
+ import { MCP_LATEST, McpPinDecision, lookupPublishedLatestVersion, resolveMcpPinVersion } from "../../core/project/mcp-config";
28
31
 
29
32
  interface InitOptions {
30
33
  name: string;
31
34
  template: string;
35
+ frontend?: FrontendFramework;
32
36
  nextVersion?: string;
33
37
  cicd?: CiCdProvider;
34
38
  backendLanguage?: BackendLanguage;
@@ -142,7 +146,7 @@ function getFunctionsRuntimeConfig(backendLanguage: BackendLanguage): { name: st
142
146
  return { name: "dotnet-isolated", version: "10.0" };
143
147
  }
144
148
  if (backendLanguage === "python") {
145
- return { name: "python", version: "3.11" };
149
+ return { name: "python", version: DEFAULT_AZURE_PYTHON_VERSION };
146
150
  }
147
151
  return { name: "node", version: "22" };
148
152
  }
@@ -257,6 +261,9 @@ const VALID_VNET: VNetOption[] = ['none', 'outbound'];
257
261
  const VALID_SWA_PLAN: StaticWebAppPlan[] = ['free', 'standard'];
258
262
 
259
263
  function validateInitFlags(options: InitOptions): void {
264
+ if (options.frontend && !["nextjs", "react"].includes(options.frontend)) {
265
+ throw new Error("Invalid --frontend: expected nextjs or react");
266
+ }
260
267
  if (options.packageManager && !['npm', 'pnpm'].includes(options.packageManager)) {
261
268
  throw new Error('Invalid --package-manager: expected npm or pnpm');
262
269
  }
@@ -322,17 +329,28 @@ export async function initCommand(options: InitOptions) {
322
329
  }
323
330
  }
324
331
 
332
+ // Preserve existing non-interactive invocations while offering a frontend choice in the wizard.
333
+ const frontend: FrontendFramework = options.frontend ?? (interactive
334
+ ? (await prompts({
335
+ type: "select", name: "frontend", message: "Frontend framework:",
336
+ choices: [{ title: "Next.js (App Router)", value: "nextjs" }, { title: "React (Vite SPA + SWA managed Functions BFF)", value: "react" }],
337
+ initial: 0,
338
+ })).frontend || "nextjs"
339
+ : "nextjs");
340
+ options = { ...options, frontend };
341
+
325
342
  // Use flag values if provided, otherwise prompt interactively
326
343
  const cicdProvider: CiCdProvider = options.cicd || await promptCiCd();
327
344
  const backendLanguage: BackendLanguage = options.backendLanguage || await promptBackendLanguage();
328
345
 
329
346
  const azureConfig = await promptAzureConfig(options);
330
347
 
331
- // Create Next.js project with create-next-app
332
- await createNextJsProject(options.name, pm);
333
-
334
- // Upgrade Next.js to specified version (or latest) to avoid cached old versions
335
- await upgradeNextJs(projectDir, options.nextVersion || 'latest', pm);
348
+ if (frontend === "react") {
349
+ createReactProject(projectDir, options.name, pm);
350
+ } else {
351
+ await createNextJsProject(options.name, pm);
352
+ await upgradeNextJs(projectDir, options.nextVersion || 'latest', pm);
353
+ }
336
354
 
337
355
  // Add SwallowKit specific files
338
356
  await addSwallowKitFiles(projectDir, options, cicdProvider, azureConfig, pm, backendLanguage);
@@ -342,9 +360,9 @@ export async function initCommand(options: InitOptions) {
342
360
 
343
361
  // Create CI/CD files based on choice
344
362
  if (cicdProvider === 'github') {
345
- await createGitHubActionsWorkflows(projectDir, azureConfig, pm, backendLanguage);
363
+ await createGitHubActionsWorkflows(projectDir, azureConfig, pm, backendLanguage, frontend);
346
364
  } else if (cicdProvider === 'azure') {
347
- await createAzurePipelines(projectDir, pm, backendLanguage);
365
+ await createAzurePipelines(projectDir, pm, backendLanguage, frontend);
348
366
  }
349
367
 
350
368
  await syncProjectManifest(projectDir);
@@ -651,9 +669,13 @@ export function parseIgnoredBuilds(output: string): string[] {
651
669
  }
652
670
 
653
671
  export function injectSwallowKitNextConfig(nextConfigContent: string, projectName: string): string {
654
- return nextConfigContent.replace(
672
+ const compatibleConfig = nextConfigContent.replace(/(\b(?:cacheComponents|partialPrefetching)\s*:\s*)true\b/g, '$1false');
673
+ const cacheComponentsSetting = /\bcacheComponents\s*:/.test(compatibleConfig)
674
+ ? ''
675
+ : ' cacheComponents: false,\n';
676
+ return compatibleConfig.replace(
655
677
  /(const\s+nextConfig[:\s]*(?::\s*NextConfig\s*)?=\s*\{)(\s*\/\*[^*]*\*\/)?/,
656
- `$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`
678
+ `$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`
657
679
  );
658
680
  }
659
681
 
@@ -691,7 +713,7 @@ export function buildCSharpFunctionsProjectSource(): string {
691
713
  <None Remove="generated\\**\\bin\\**;generated\\**\\obj\\**" />
692
714
  </ItemGroup>
693
715
  <ItemGroup>
694
- <PackageReference Include="Microsoft.Azure.Cosmos" Version="3.47.0" />
716
+ <PackageReference Include="Microsoft.Azure.Cosmos" Version="3.63.2" />
695
717
  <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
696
718
  <PackageReference Include="Azure.Identity" Version="1.17.0" />
697
719
  <PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.52.0" />
@@ -704,8 +726,11 @@ export function buildCSharpFunctionsProjectSource(): string {
704
726
  `;
705
727
  }
706
728
 
707
- export function buildSwallowKitConfigSource(backendLanguage: BackendLanguage): string {
729
+ export function buildSwallowKitConfigSource(backendLanguage: BackendLanguage, frontend: FrontendFramework = "nextjs"): string {
708
730
  return `module.exports = {
731
+ frontend: {
732
+ framework: '${frontend}',
733
+ },
709
734
  backend: {
710
735
  language: '${backendLanguage}',
711
736
  },
@@ -723,10 +748,10 @@ export function buildSwallowKitConfigSource(backendLanguage: BackendLanguage): s
723
748
  export function buildGeneratedProjectDependencies(
724
749
  projectName: string,
725
750
  pm: PackageManager = 'pnpm',
751
+ frontend: FrontendFramework = "nextjs",
726
752
  ): Record<string, string> {
727
753
  return {
728
- '@azure/cosmos': '^4.0.0',
729
- 'applicationinsights': '^3.3.0',
754
+ ...(frontend === "nextjs" ? { '@azure/cosmos': '^4.0.0', 'applicationinsights': '^3.3.0' } : {}),
730
755
  'zod': '^4.0.0',
731
756
  [`@${projectName}/shared`]: pm === 'pnpm' ? 'workspace:*' : 'file:shared',
732
757
  };
@@ -755,14 +780,30 @@ function getSwallowKitPackageMetadata(): { name: string; version: string } {
755
780
  };
756
781
  }
757
782
 
758
- export function buildGeneratedProjectDevDependencies(): Record<string, string> {
783
+ export function buildGeneratedProjectDevDependencies(frontend: FrontendFramework = "nextjs"): Record<string, string> {
759
784
  const { name, version } = getSwallowKitPackageMetadata();
760
785
  return {
761
786
  [name]: version,
787
+ ...(frontend === "nextjs" ? { "@tailwindcss/postcss": "^4.0.0" } : {}),
762
788
  };
763
789
  }
764
790
 
765
- export function buildSwallowKitMcpProjectConfigSource(pm: PackageManager = 'pnpm'): string {
791
+ export function ensureNextTailwindPostcssConfig(projectDir: string): void {
792
+ if (["postcss.config.mjs", "postcss.config.js", "postcss.config.cjs", "postcss.config.json"]
793
+ .some(file => fs.existsSync(path.join(projectDir, file)))) {
794
+ return;
795
+ }
796
+
797
+ // New create-next-app templates can configure Tailwind for Turbopack only.
798
+ fs.writeFileSync(path.join(projectDir, "postcss.config.mjs"), `export default {
799
+ plugins: {
800
+ "@tailwindcss/postcss": {},
801
+ },
802
+ };
803
+ `);
804
+ }
805
+
806
+ export function buildSwallowKitMcpProjectConfigSource(pm: PackageManager = 'pnpm', mcpVersion: string = MCP_LATEST): string {
766
807
  const { name } = getSwallowKitPackageMetadata();
767
808
  return JSON.stringify(
768
809
  {
@@ -774,7 +815,7 @@ export function buildSwallowKitMcpProjectConfigSource(pm: PackageManager = 'pnpm
774
815
  : ["--yes", "--package", `${name}@\${SWALLOWKIT_MCP_VERSION}`, "swallowkit-mcp"],
775
816
  cwd: ".",
776
817
  env: {
777
- SWALLOWKIT_MCP_VERSION: "latest",
818
+ SWALLOWKIT_MCP_VERSION: mcpVersion,
778
819
  },
779
820
  },
780
821
  },
@@ -784,6 +825,15 @@ export function buildSwallowKitMcpProjectConfigSource(pm: PackageManager = 'pnpm
784
825
  );
785
826
  }
786
827
 
828
+ /** latest by default; pinned to this CLI version only when it is newer than published latest. */
829
+ export function selectInitialMcpVersion(
830
+ lookupLatest: (packageName: string) => string | null = lookupPublishedLatestVersion
831
+ ): McpPinDecision {
832
+ const { name, version } = getSwallowKitPackageMetadata();
833
+ if (version === MCP_LATEST) return { version: MCP_LATEST, reason: "SwallowKit package metadata is unavailable." };
834
+ return resolveMcpPinVersion(version, lookupLatest(name));
835
+ }
836
+
787
837
  async function addSwallowKitFiles(
788
838
  projectDir: string,
789
839
  options: InitOptions,
@@ -795,6 +845,7 @@ async function addSwallowKitFiles(
795
845
  console.log('📦 Adding SwallowKit files...\n');
796
846
 
797
847
  const projectName = options.name;
848
+ const frontend = options.frontend ?? "nextjs";
798
849
 
799
850
  // 1. Update package.json to add runtime dependencies for generated projects
800
851
  const packageJsonPath = path.join(projectDir, 'package.json');
@@ -803,17 +854,21 @@ async function addSwallowKitFiles(
803
854
  // Root BFF/auth code imports Zod directly; declare it as well as the shared package.
804
855
  packageJson.dependencies = {
805
856
  ...packageJson.dependencies,
806
- ...buildGeneratedProjectDependencies(projectName, pm),
857
+ ...buildGeneratedProjectDependencies(projectName, pm, frontend),
807
858
  };
808
859
  packageJson.devDependencies = {
809
860
  ...packageJson.devDependencies,
810
- ...buildGeneratedProjectDevDependencies(),
861
+ ...buildGeneratedProjectDevDependencies(frontend),
811
862
  };
812
863
 
813
864
  packageJson.scripts = {
814
865
  ...packageJson.scripts,
815
- 'build': getBuildScript(pm),
816
- 'start': 'next start',
866
+ 'build': getBuildScript(pm, frontend),
867
+ 'start': frontend === "react" ? "vite preview" : 'next start',
868
+ ...(frontend === "react" ? {
869
+ typecheck: `tsc --noEmit && ${getCommands(pm).exec} tsc --noEmit -p bff`,
870
+ "bff:start": `${getCommands(pm).runFilter("bff")} start`,
871
+ } : {}),
817
872
  'functions:start': getFunctionsStartScript(pm, backendLanguage),
818
873
  };
819
874
 
@@ -827,7 +882,7 @@ async function addSwallowKitFiles(
827
882
  delete packageJson.packageManager;
828
883
 
829
884
  // Keep package.json workspaces for npm-based CI/CD even when local development uses pnpm.
830
- const workspacePackages = usesNodeFunctionsProject(backendLanguage) ? ['shared', 'functions'] : ['shared'];
885
+ const workspacePackages = [...(usesNodeFunctionsProject(backendLanguage) ? ['shared', 'functions'] : ['shared']), ...(frontend === "react" ? ["bff"] : [])];
831
886
  const wsConfig = getWorkspaceConfig(pm, workspacePackages);
832
887
  packageJson.workspaces = workspacePackages;
833
888
 
@@ -837,9 +892,11 @@ async function addSwallowKitFiles(
837
892
 
838
893
  fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
839
894
 
895
+ if (frontend === "nextjs") ensureNextTailwindPostcssConfig(projectDir);
896
+
840
897
  // Create pnpm-workspace.yaml for local pnpm development when needed.
841
898
  if (wsConfig.type === 'file') {
842
- fs.writeFileSync(path.join(projectDir, wsConfig.filename), wsConfig.content);
899
+ fs.writeFileSync(path.join(projectDir, wsConfig.filename), wsConfig.content + (frontend === "react" ? " esbuild: true\n" : ""));
843
900
  }
844
901
 
845
902
  // Don't install yet — wait until all workspace packages (shared, functions) are created
@@ -878,12 +935,13 @@ async function addSwallowKitFiles(
878
935
  }
879
936
 
880
937
  // 3. Create SwallowKit config
881
- const swallowkitConfig = buildSwallowKitConfigSource(backendLanguage);
938
+ const swallowkitConfig = buildSwallowKitConfigSource(backendLanguage, frontend);
882
939
  fs.writeFileSync(path.join(projectDir, 'swallowkit.config.js'), swallowkitConfig);
883
940
 
884
941
  // 4. Create shared workspace package for Zod models (Single Source of Truth)
885
942
  await createSharedPackage(projectDir, projectName);
886
943
 
944
+ if (frontend === "nextjs") {
887
945
  // Create lib directory for Next.js-specific utilities
888
946
  const libDir = path.join(projectDir, 'lib');
889
947
 
@@ -1003,6 +1061,7 @@ export const api = {
1003
1061
  };
1004
1062
  `;
1005
1063
  fs.writeFileSync(path.join(apiLibDir, 'backend.ts'), backendUtilContent);
1064
+ }
1006
1065
 
1007
1066
  // 5. Create components directory
1008
1067
  const componentsDir = path.join(projectDir, 'components');
@@ -1091,7 +1150,7 @@ export async function register() {
1091
1150
  }
1092
1151
  }
1093
1152
  `;
1094
- fs.writeFileSync(path.join(projectDir, 'instrumentation.ts'), instrumentationContent);
1153
+ if (frontend === "nextjs") fs.writeFileSync(path.join(projectDir, 'instrumentation.ts'), instrumentationContent);
1095
1154
 
1096
1155
  // 8. Create .env.local for local development
1097
1156
  const envLocalContent = [
@@ -1124,17 +1183,19 @@ export async function register() {
1124
1183
  };
1125
1184
  fs.writeFileSync(
1126
1185
  path.join(projectDir, 'staticwebapp.config.json'),
1127
- JSON.stringify(swaConfig, null, 2)
1186
+ JSON.stringify(frontend === "react" ? buildReactSwaConfig() : swaConfig, null, 2)
1128
1187
  );
1129
1188
 
1130
1189
  // 14. Create Azure Functions project
1131
1190
  await createAzureFunctionsProject(projectDir, pm, backendLanguage);
1132
1191
 
1133
1192
  // 15. Create BFF API route to call Azure Functions
1134
- await createBffApiRoute(projectDir);
1135
-
1136
- // 16. Create home page
1137
- await createHomePage(projectDir, pm);
1193
+ if (frontend === "react") {
1194
+ createReactBffProject(projectDir, projectName, pm);
1195
+ } else {
1196
+ await createBffApiRoute(projectDir);
1197
+ await createHomePage(projectDir, pm);
1198
+ }
1138
1199
 
1139
1200
  // 17. Install all workspace dependencies (root + shared + functions)
1140
1201
  console.log('📦 Installing workspace dependencies...\n');
@@ -1143,13 +1204,17 @@ export async function register() {
1143
1204
  console.log('✅ Project structure created\n');
1144
1205
 
1145
1206
  // 18. Create README.md
1146
- createReadme(projectDir, projectName, cicdChoice, azureConfig, pm, backendLanguage);
1207
+ if (frontend === "react") {
1208
+ fs.writeFileSync(path.join(projectDir, "README.md"), buildReactReadme(projectName, pm, backendLanguage));
1209
+ } else {
1210
+ createReadme(projectDir, projectName, cicdChoice, azureConfig, pm, backendLanguage);
1211
+ }
1147
1212
 
1148
1213
  // 19. Create AI agent instruction files (AGENTS.md, CLAUDE.md, .github/copilot-instructions.md, etc.)
1149
- createAiAgentFiles(projectDir, projectName, backendLanguage, pm);
1214
+ createAiAgentFiles(projectDir, projectName, backendLanguage, pm, frontend);
1150
1215
  }
1151
1216
 
1152
- async function createSharedPackage(projectDir: string, projectName: string) {
1217
+ export async function createSharedPackage(projectDir: string, projectName: string) {
1153
1218
  console.log('📦 Creating shared workspace package for Zod models...\n');
1154
1219
 
1155
1220
  const sharedDir = path.join(projectDir, 'shared');
@@ -1189,7 +1254,7 @@ async function createSharedPackage(projectDir: string, projectName: string) {
1189
1254
  // shared/index.ts (empty re-export file, scaffold will add entries)
1190
1255
  fs.writeFileSync(
1191
1256
  path.join(sharedDir, 'index.ts'),
1192
- `// Shared Zod models — auto-managed by SwallowKit scaffold command\n// Do not edit the export list below manually\n`
1257
+ `// 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`
1193
1258
  );
1194
1259
 
1195
1260
  // shared/.gitignore
@@ -1201,7 +1266,7 @@ async function createSharedPackage(projectDir: string, projectName: string) {
1201
1266
  console.log('✅ Shared package created\n');
1202
1267
  }
1203
1268
 
1204
- async function createAzureFunctionsProject(
1269
+ export async function createAzureFunctionsProject(
1205
1270
  projectDir: string,
1206
1271
  pm: PackageManager = 'pnpm',
1207
1272
  backendLanguage: BackendLanguage = 'typescript'
@@ -1456,7 +1521,6 @@ public sealed class GreetFunction
1456
1521
  }
1457
1522
 
1458
1523
  function createPythonFunctionsProject(projectDir: string, functionsDir: string): void {
1459
- fs.writeFileSync(path.join(projectDir, '.python-version'), '3.11\n');
1460
1524
  ensureProjectGitignoreEntry(projectDir, '.uv');
1461
1525
 
1462
1526
  fs.writeFileSync(path.join(functionsDir, 'requirements.txt'), `azure-functions>=1.20.0
@@ -1596,117 +1660,8 @@ export async function POST(request: NextRequest) {
1596
1660
 
1597
1661
  async function createHomePage(projectDir: string, pm: PackageManager = 'pnpm') {
1598
1662
  console.log('📦 Creating home page...\n');
1599
-
1600
- const pmCmd = getCommands(pm);
1601
-
1602
- const pageContent = `'use client'
1603
-
1604
- export const dynamic = 'force-dynamic';
1605
-
1606
- import { useState } from 'react';
1607
- import { scaffoldConfig } from '@/lib/scaffold-config';
1608
-
1609
- export default function Home() {
1610
- const [greetingStatus, setGreetingStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
1611
- const [message, setMessage] = useState('');
1612
-
1613
- const testConnection = async () => {
1614
- setGreetingStatus('loading');
1615
- try {
1616
- const response = await fetch('/api/greet?name=SwallowKit');
1617
- const data = await response.json();
1618
- if (!response.ok) {
1619
- throw new Error(data.error || \`Server error: \${response.status}\`);
1620
- }
1621
- setMessage(data.message);
1622
- setGreetingStatus('success');
1623
- } catch (error) {
1624
- setMessage(error instanceof Error ? error.message : 'Failed to connect to Azure Functions');
1625
- setGreetingStatus('error');
1626
- }
1627
- };
1628
1663
 
1629
- return (
1630
- <div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-50 dark:from-gray-900 dark:to-gray-800">
1631
- <div className="container mx-auto px-4 py-12">
1632
- <header className="text-center mb-16">
1633
- <h1 className="text-5xl font-bold text-gray-800 dark:text-white mb-4">
1634
- Welcome to SwallowKit
1635
- </h1>
1636
- <p className="text-xl text-gray-600 dark:text-gray-400">
1637
- Next.js on Azure Static Web Apps + Functions + Cosmos DB — Zod schema sharing
1638
- </p>
1639
- </header>
1640
-
1641
- {/* Connection Test */}
1642
- <section className="max-w-2xl mx-auto mb-12">
1643
- <div className="bg-white dark:bg-gray-800 rounded-xl p-8 border border-gray-200 dark:border-gray-700">
1644
- <h2 className="text-2xl font-semibold mb-4 text-gray-900 dark:text-gray-100">
1645
- Test BFF → Functions Connection
1646
- </h2>
1647
- <button
1648
- onClick={testConnection}
1649
- disabled={greetingStatus === 'loading'}
1650
- className="px-6 py-3 bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 text-white rounded-lg font-medium transition-colors"
1651
- >
1652
- {greetingStatus === 'loading' ? 'Testing...' : 'Test Connection'}
1653
- </button>
1654
- {greetingStatus === 'success' && (
1655
- <div className="mt-4 p-4 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg">
1656
- <p className="text-green-800 dark:text-green-200 font-medium">✅ Connection successful!</p>
1657
- <p className="text-green-700 dark:text-green-300 text-sm mt-1">{message}</p>
1658
- </div>
1659
- )}
1660
- {greetingStatus === 'error' && (
1661
- <div className="mt-4 p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg">
1662
- <p className="text-red-800 dark:text-red-200 font-medium">❌ Connection failed</p>
1663
- <p className="text-red-700 dark:text-red-300 text-sm mt-1">{message}</p>
1664
- </div>
1665
- )}
1666
- </div>
1667
- </section>
1668
-
1669
- {/* Scaffolded Models Menu */}
1670
- {scaffoldConfig.models.length > 0 ? (
1671
- <section className="max-w-6xl mx-auto">
1672
- <h2 className="text-3xl font-bold mb-8 text-gray-900 dark:text-gray-100">Your Models</h2>
1673
- <div className="grid gap-6 grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
1674
- {scaffoldConfig.models.map((model) => (
1675
- <a
1676
- key={model.name}
1677
- href={model.path}
1678
- 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"
1679
- >
1680
- <h3 className="text-2xl font-semibold mb-2 text-gray-900 dark:text-gray-100">{model.label}</h3>
1681
- <p className="text-gray-600 dark:text-gray-400">Manage {model.label.toLowerCase()}</p>
1682
- </a>
1683
- ))}
1684
- </div>
1685
- </section>
1686
- ) : (
1687
- <section className="max-w-2xl mx-auto text-center">
1688
- <div className="bg-white dark:bg-gray-800 rounded-xl p-12 border border-gray-200 dark:border-gray-700">
1689
- <h2 className="text-2xl font-semibold mb-4 text-gray-900 dark:text-gray-100">Get Started</h2>
1690
- <p className="text-gray-600 dark:text-gray-400 mb-6">
1691
- Create your first model with Zod and generate CRUD operations automatically.
1692
- </p>
1693
- <code className="block bg-gray-100 dark:bg-gray-900 p-4 rounded text-left text-sm">
1694
- ${pmCmd.exec} swallowkit scaffold shared/models/your-model.ts
1695
- </code>
1696
- </div>
1697
- </section>
1698
- )}
1699
-
1700
- <footer className="mt-16 text-center text-gray-600 dark:text-gray-400 text-sm">
1701
- <p>Built with SwallowKit</p>
1702
- </footer>
1703
- </div>
1704
- </div>
1705
- );
1706
- }
1707
- `;
1708
-
1709
- fs.writeFileSync(path.join(projectDir, 'app', 'page.tsx'), pageContent);
1664
+ fs.writeFileSync(path.join(projectDir, 'app', 'page.tsx'), buildHomePage("nextjs", pm));
1710
1665
 
1711
1666
  console.log('✅ Home page created\n');
1712
1667
 
@@ -1760,9 +1715,8 @@ function createReadme(
1760
1715
  const backendScaffoldNote = backendLanguage === 'typescript'
1761
1716
  ? '- Azure Functions CRUD endpoints'
1762
1717
  : `- Azure Functions ${backendLanguageLabel} CRUD handlers\n- OpenAPI export + native-generated ${backendLanguageLabel} schema assets`;
1763
- const pythonUvPaths = getProjectLocalUvPaths(projectDir);
1764
1718
  const pythonLocalDevNote = backendLanguage === 'python'
1765
- ? `\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`
1719
+ ? `\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 (${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`
1766
1720
  : '';
1767
1721
 
1768
1722
  const readme = `# ${projectName}
@@ -1871,10 +1825,12 @@ ${pmCmd.exec} swallowkit provision --resource-group <rg-name>
1871
1825
 
1872
1826
  This creates:
1873
1827
  - Static Web App (\`swa-${projectName}\`)
1874
- - Azure Functions (\`func-${projectName}\`)
1875
- - Cosmos DB (\`cosmos-${projectName}\`)
1828
+ - Azure Functions (\`func-${projectName}-<suffix>\`)
1829
+ - Cosmos DB (\`cosmos-${projectName}-<suffix>\`)
1876
1830
  - Storage Account
1877
1831
 
1832
+ 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.
1833
+
1878
1834
  You will be prompted to select Azure regions:
1879
1835
  1. **Primary location**: For Functions and Cosmos DB (default: Japan East)
1880
1836
  2. **Static Web App location**: Limited availability (default: East Asia)
@@ -1890,12 +1846,12 @@ ${cicdChoice === 'github' ? `#### GitHub Actions
1890
1846
 
1891
1847
  2. Get Function App publish profile:
1892
1848
  \`\`\`bash
1893
- az webapp deployment list-publishing-profiles --name func-${projectName} --resource-group <rg-name> --xml
1849
+ az webapp deployment list-publishing-profiles --name <functions-app-name> --resource-group <rg-name> --xml
1894
1850
  \`\`\`
1895
1851
 
1896
1852
  3. Add secrets to GitHub repository:
1897
1853
  - \`AZURE_STATIC_WEB_APPS_API_TOKEN\`: SWA deployment token (from step 1)
1898
- - \`AZURE_FUNCTIONAPP_NAME\`: \`func-${projectName}\`
1854
+ - \`AZURE_FUNCTIONAPP_NAME\`: Functions app name printed by \`swallowkit provision\` (\`func-${projectName}-<suffix>\`)
1899
1855
  - \`AZURE_FUNCTIONAPP_PUBLISH_PROFILE\`: Functions publish profile (from step 2)
1900
1856
 
1901
1857
  4. Push to \`main\` branch to trigger deployment (or use **Actions** → **Run workflow** for manual deployment)` : cicdChoice === 'azure' ? `#### Azure Pipelines
@@ -1917,8 +1873,10 @@ az staticwebapp deploy --name swa-${projectName} --resource-group <rg-name> --ap
1917
1873
  \`\`\`bash
1918
1874
  cd functions
1919
1875
  ${pmCmd.run} build
1920
- func azure functionapp publish func-${projectName}
1921
- \`\`\``}
1876
+ func azure functionapp publish <functions-app-name>
1877
+ \`\`\`
1878
+
1879
+ Use the Functions app name printed by \`swallowkit provision\`.`}
1922
1880
 
1923
1881
  ## 🔧 Available Commands
1924
1882
 
@@ -1978,7 +1936,8 @@ function createAiAgentFiles(
1978
1936
  projectDir: string,
1979
1937
  projectName: string,
1980
1938
  backendLanguage: BackendLanguage,
1981
- pm: PackageManager
1939
+ pm: PackageManager,
1940
+ frontend: FrontendFramework = "nextjs"
1982
1941
  ) {
1983
1942
  console.log('🤖 Creating AI agent instruction files...\n');
1984
1943
  const runCmd = getCommands(pm).exec;
@@ -1987,17 +1946,23 @@ function createAiAgentFiles(
1987
1946
  projectName,
1988
1947
  backendLanguage,
1989
1948
  packageManager: pm,
1949
+ frontend,
1990
1950
  });
1991
1951
  console.log(' ✅ AGENTS.md (canonical project contract)');
1992
1952
  console.log(' ✅ CLAUDE.md (Claude Code adapter)');
1993
1953
  console.log(' ✅ .github/copilot-instructions.md (GitHub Copilot adapter)');
1994
1954
  console.log(' ✅ .github/instructions/ (path-specific instructions)');
1995
1955
 
1956
+ const mcpVersion = selectInitialMcpVersion();
1996
1957
  fs.writeFileSync(
1997
1958
  path.join(projectDir, '.mcp.json'),
1998
- buildSwallowKitMcpProjectConfigSource(pm)
1959
+ buildSwallowKitMcpProjectConfigSource(pm, mcpVersion.version)
1960
+ );
1961
+ console.log(
1962
+ mcpVersion.version === MCP_LATEST
1963
+ ? ' ✅ .mcp.json (project-scoped MCP bootstrap, resolves latest)'
1964
+ : ` ✅ .mcp.json (project-scoped MCP bootstrap, pinned to ${mcpVersion.version}: ${mcpVersion.reason} Run "${runCmd} swallowkit configure-mcp" to re-select later.)`
1999
1965
  );
2000
- console.log(' ✅ .mcp.json (project-scoped MCP bootstrap)');
2001
1966
 
2002
1967
  writeWorkflowDocs(projectDir, runCmd);
2003
1968
  console.log(' ✅ .swallowkit/workflows/ (agent-agnostic autonomous-loop runbooks)');
@@ -2088,6 +2053,14 @@ param cosmosDbMode string = '${azureConfig.cosmosDbMode}'
2088
2053
  @description('Enable VNet integration')
2089
2054
  param enableVNet bool = ${enableVNet}
2090
2055
 
2056
+ // Cosmos DB account and Function App names are globally unique across Azure.
2057
+ // A suffix derived from the resource group keeps names stable for redeployments
2058
+ // to the same resource group while avoiding collisions with other environments
2059
+ // and with names Azure still reserves after a previous resource group is deleted.
2060
+ var resourceSuffix = take(uniqueString(resourceGroup().id, projectName), 6)
2061
+ var cosmosAccountName = 'cosmos-\${take(toLower(projectName), 30)}-\${resourceSuffix}'
2062
+ var functionsAppName = 'func-\${take(projectName, 40)}-\${resourceSuffix}'
2063
+
2091
2064
  // Shared Log Analytics Workspace (in Functions region for data residency)
2092
2065
  module logAnalytics 'modules/loganalytics.bicep' = {
2093
2066
  name: 'logAnalytics'
@@ -2141,7 +2114,7 @@ module vnet 'modules/vnet.bicep' = if (enableVNet) {
2141
2114
  module cosmosDbFreeTier 'modules/cosmosdb-freetier.bicep' = if (cosmosDbMode == 'freetier') {
2142
2115
  name: 'cosmosDb'
2143
2116
  params: {
2144
- accountName: 'cosmos-\${projectName}'
2117
+ accountName: cosmosAccountName
2145
2118
  databaseName: '\${projectName}Database'
2146
2119
  location: location
2147
2120
  publicNetworkAccess: enableVNet ? 'Disabled' : 'Enabled'
@@ -2151,7 +2124,7 @@ module cosmosDbFreeTier 'modules/cosmosdb-freetier.bicep' = if (cosmosDbMode ==
2151
2124
  module cosmosDbServerless 'modules/cosmosdb-serverless.bicep' = if (cosmosDbMode == 'serverless') {
2152
2125
  name: 'cosmosDb'
2153
2126
  params: {
2154
- accountName: 'cosmos-\${projectName}'
2127
+ accountName: cosmosAccountName
2155
2128
  databaseName: '\${projectName}Database'
2156
2129
  location: location
2157
2130
  publicNetworkAccess: enableVNet ? 'Disabled' : 'Enabled'
@@ -2175,7 +2148,7 @@ module cosmosPrivateEndpoint 'modules/private-endpoint-cosmos.bicep' = if (enabl
2175
2148
  module functionsFlex 'modules/functions-flex.bicep' = {
2176
2149
  name: 'functionsApp'
2177
2150
  params: {
2178
- name: 'func-\${projectName}'
2151
+ name: functionsAppName
2179
2152
  location: location
2180
2153
  storageAccountName: 'stg\${uniqueString(resourceGroup().id, projectName)}'
2181
2154
  appInsightsConnectionString: appInsightsFunctions.outputs.connectionString
@@ -2911,7 +2884,7 @@ jobs:
2911
2884
  ${commonSetup} - name: Setup Python
2912
2885
  uses: actions/setup-python@v5
2913
2886
  with:
2914
- python-version: '3.11'
2887
+ python-version: '${DEFAULT_AZURE_PYTHON_VERSION}'
2915
2888
 
2916
2889
  - name: Install Functions dependencies
2917
2890
  run: |
@@ -3100,7 +3073,7 @@ variables:
3100
3073
  steps:
3101
3074
  ${commonSetup} - task: UsePythonVersion@0
3102
3075
  inputs:
3103
- versionSpec: '3.11'
3076
+ versionSpec: '${DEFAULT_AZURE_PYTHON_VERSION}'
3104
3077
  displayName: 'Install Python'
3105
3078
 
3106
3079
  - script: |
@@ -3133,7 +3106,8 @@ export function getSwaNodeVersionCommand(): string {
3133
3106
  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));"`;
3134
3107
  }
3135
3108
 
3136
- export function buildAzureSwaPipeline(pm: PackageManager, projectPm: PackageManager = pm): string {
3109
+ export function buildAzureSwaPipeline(pm: PackageManager, projectPm: PackageManager = pm, frontend: FrontendFramework = "nextjs"): string {
3110
+ if (frontend === "react") return buildReactAzureSwaPipeline(projectPm);
3137
3111
  // SWA/Oryx also invokes npm internally; normalize only the disposable CI checkout.
3138
3112
  pm = 'npm';
3139
3113
  const pmCmd = getCommands(pm);
@@ -3321,7 +3295,8 @@ async function createGitHubActionsWorkflows(
3321
3295
  projectDir: string,
3322
3296
  azureConfig: AzureConfig,
3323
3297
  pm: PackageManager,
3324
- backendLanguage: BackendLanguage
3298
+ backendLanguage: BackendLanguage,
3299
+ frontend: FrontendFramework = "nextjs"
3325
3300
  ) {
3326
3301
  console.log('📦 Creating GitHub Actions workflows...\n');
3327
3302
  const workflowsDir = path.join(projectDir, '.github', 'workflows');
@@ -3329,7 +3304,7 @@ async function createGitHubActionsWorkflows(
3329
3304
 
3330
3305
  const actionsPm: PackageManager = pm;
3331
3306
 
3332
- const swaWorkflow = buildGitHubSwaWorkflow(actionsPm, pm);
3307
+ const swaWorkflow = buildGitHubSwaWorkflow(actionsPm, pm, frontend);
3333
3308
  fs.writeFileSync(path.join(workflowsDir, 'deploy-swa.yml'), swaWorkflow);
3334
3309
 
3335
3310
  // deploy-functions.yml
@@ -3339,7 +3314,8 @@ async function createGitHubActionsWorkflows(
3339
3314
  console.log('✅ GitHub Actions workflows created\n');
3340
3315
  }
3341
3316
 
3342
- export function buildGitHubSwaWorkflow(pm: PackageManager, projectPm: PackageManager = pm): string {
3317
+ export function buildGitHubSwaWorkflow(pm: PackageManager, projectPm: PackageManager = pm, frontend: FrontendFramework = "nextjs"): string {
3318
+ if (frontend === "react") return buildReactGitHubSwaWorkflow(projectPm);
3343
3319
  // SWA/Oryx also invokes npm internally; normalize only the disposable CI checkout.
3344
3320
  pm = 'npm';
3345
3321
  const npmWorkspaceNormalizationStep = getGitHubNpmWorkspaceNormalizationStep(pm, projectPm);
@@ -3417,7 +3393,7 @@ ${npmWorkspaceNormalizationStep ? `\n${npmWorkspaceNormalizationStep}` : ''}
3417
3393
  `;
3418
3394
  }
3419
3395
 
3420
- async function createAzurePipelines(projectDir: string, pm: PackageManager, backendLanguage: BackendLanguage) {
3396
+ async function createAzurePipelines(projectDir: string, pm: PackageManager, backendLanguage: BackendLanguage, frontend: FrontendFramework = "nextjs") {
3421
3397
  console.log('📦 Creating Azure Pipelines...\n');
3422
3398
 
3423
3399
  const pipelinesPm: PackageManager = pm;
@@ -3425,7 +3401,7 @@ async function createAzurePipelines(projectDir: string, pm: PackageManager, back
3425
3401
  fs.mkdirSync(pipelinesDir, { recursive: true });
3426
3402
 
3427
3403
  // swa.yml
3428
- const swaPipeline = buildAzureSwaPipeline(pipelinesPm, pm);
3404
+ const swaPipeline = buildAzureSwaPipeline(pipelinesPm, pm, frontend);
3429
3405
  fs.writeFileSync(path.join(pipelinesDir, 'swa.yml'), swaPipeline);
3430
3406
 
3431
3407
  // functions.yml
@@ -3434,4 +3410,3 @@ async function createAzurePipelines(projectDir: string, pm: PackageManager, back
3434
3410
 
3435
3411
  console.log('✅ Azure Pipelines created\n');
3436
3412
  }
3437
-