swallowkit 2.0.0-alpha.1 → 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 (224) hide show
  1. package/README.ja.md +14 -0
  2. package/README.md +14 -0
  3. package/dist/cli/commands/add-auth.js +37 -6
  4. package/dist/cli/commands/add-auth.js.map +1 -1
  5. package/dist/cli/commands/configure-mcp.d.ts +12 -0
  6. package/dist/cli/commands/configure-mcp.d.ts.map +1 -0
  7. package/dist/cli/commands/configure-mcp.js +42 -0
  8. package/dist/cli/commands/configure-mcp.js.map +1 -0
  9. package/dist/cli/commands/dev.d.ts +2 -2
  10. package/dist/cli/commands/dev.d.ts.map +1 -1
  11. package/dist/cli/commands/dev.js +39 -27
  12. package/dist/cli/commands/dev.js.map +1 -1
  13. package/dist/cli/commands/init.d.ts +8 -2
  14. package/dist/cli/commands/init.d.ts.map +1 -1
  15. package/dist/cli/commands/init.js +67 -132
  16. package/dist/cli/commands/init.js.map +1 -1
  17. package/dist/cli/commands/migrations.d.ts.map +1 -1
  18. package/dist/cli/commands/migrations.js +25 -0
  19. package/dist/cli/commands/migrations.js.map +1 -1
  20. package/dist/cli/commands/provision.d.ts +5 -0
  21. package/dist/cli/commands/provision.d.ts.map +1 -1
  22. package/dist/cli/commands/provision.js +278 -55
  23. package/dist/cli/commands/provision.js.map +1 -1
  24. package/dist/cli/commands/scaffold.d.ts +4 -0
  25. package/dist/cli/commands/scaffold.d.ts.map +1 -1
  26. package/dist/cli/commands/scaffold.js +85 -3
  27. package/dist/cli/commands/scaffold.js.map +1 -1
  28. package/dist/cli/index.d.ts +2 -0
  29. package/dist/cli/index.d.ts.map +1 -1
  30. package/dist/cli/index.js +43 -5
  31. package/dist/cli/index.js.map +1 -1
  32. package/dist/core/operations/create-model.d.ts +2 -0
  33. package/dist/core/operations/create-model.d.ts.map +1 -1
  34. package/dist/core/operations/create-model.js +60 -8
  35. package/dist/core/operations/create-model.js.map +1 -1
  36. package/dist/core/operations/migration-generator.d.ts.map +1 -1
  37. package/dist/core/operations/migration-generator.js +12 -1
  38. package/dist/core/operations/migration-generator.js.map +1 -1
  39. package/dist/core/operations/model-migration.d.ts +22 -0
  40. package/dist/core/operations/model-migration.d.ts.map +1 -0
  41. package/dist/core/operations/model-migration.js +36 -0
  42. package/dist/core/operations/model-migration.js.map +1 -0
  43. package/dist/core/operations/provision-deployment.d.ts +52 -0
  44. package/dist/core/operations/provision-deployment.d.ts.map +1 -0
  45. package/dist/core/operations/provision-deployment.js +164 -0
  46. package/dist/core/operations/provision-deployment.js.map +1 -0
  47. package/dist/core/operations/provision-operations.d.ts +62 -6
  48. package/dist/core/operations/provision-operations.d.ts.map +1 -1
  49. package/dist/core/operations/provision-operations.js +653 -115
  50. package/dist/core/operations/provision-operations.js.map +1 -1
  51. package/dist/core/operations/scaffold-apply.d.ts +1 -0
  52. package/dist/core/operations/scaffold-apply.d.ts.map +1 -1
  53. package/dist/core/operations/scaffold-apply.js +1 -0
  54. package/dist/core/operations/scaffold-apply.js.map +1 -1
  55. package/dist/core/operations/scaffold-machine.d.ts +1 -0
  56. package/dist/core/operations/scaffold-machine.d.ts.map +1 -1
  57. package/dist/core/operations/scaffold-machine.js.map +1 -1
  58. package/dist/core/operations/scaffold-plan.d.ts +2 -0
  59. package/dist/core/operations/scaffold-plan.d.ts.map +1 -1
  60. package/dist/core/operations/scaffold-plan.js +3 -1
  61. package/dist/core/operations/scaffold-plan.js.map +1 -1
  62. package/dist/core/project/agent-instructions.d.ts.map +1 -1
  63. package/dist/core/project/agent-instructions.js +4 -1
  64. package/dist/core/project/agent-instructions.js.map +1 -1
  65. package/dist/core/project/boundaries.d.ts +5 -2
  66. package/dist/core/project/boundaries.d.ts.map +1 -1
  67. package/dist/core/project/boundaries.js +51 -20
  68. package/dist/core/project/boundaries.js.map +1 -1
  69. package/dist/core/project/capabilities.d.ts +10 -0
  70. package/dist/core/project/capabilities.d.ts.map +1 -1
  71. package/dist/core/project/capabilities.js +38 -6
  72. package/dist/core/project/capabilities.js.map +1 -1
  73. package/dist/core/project/home-page.d.ts +4 -0
  74. package/dist/core/project/home-page.d.ts.map +1 -0
  75. package/dist/core/project/home-page.js +113 -0
  76. package/dist/core/project/home-page.js.map +1 -0
  77. package/dist/core/project/mcp-config.d.ts +26 -0
  78. package/dist/core/project/mcp-config.d.ts.map +1 -0
  79. package/dist/core/project/mcp-config.js +117 -0
  80. package/dist/core/project/mcp-config.js.map +1 -0
  81. package/dist/core/project/react-project.d.ts +1 -1
  82. package/dist/core/project/react-project.d.ts.map +1 -1
  83. package/dist/core/project/react-project.js +46 -25
  84. package/dist/core/project/react-project.js.map +1 -1
  85. package/dist/core/project/state.d.ts +7 -0
  86. package/dist/core/project/state.d.ts.map +1 -1
  87. package/dist/core/project/state.js +86 -0
  88. package/dist/core/project/state.js.map +1 -1
  89. package/dist/core/project/workflows.d.ts.map +1 -1
  90. package/dist/core/project/workflows.js +53 -7
  91. package/dist/core/project/workflows.js.map +1 -1
  92. package/dist/core/scaffold/auth-generator.d.ts.map +1 -1
  93. package/dist/core/scaffold/auth-generator.js +26 -27
  94. package/dist/core/scaffold/auth-generator.js.map +1 -1
  95. package/dist/core/scaffold/model-parser.d.ts +23 -0
  96. package/dist/core/scaffold/model-parser.d.ts.map +1 -1
  97. package/dist/core/scaffold/model-parser.js +407 -137
  98. package/dist/core/scaffold/model-parser.js.map +1 -1
  99. package/dist/core/scaffold/native-schema-generator.d.ts +13 -0
  100. package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -1
  101. package/dist/core/scaffold/native-schema-generator.js +294 -91
  102. package/dist/core/scaffold/native-schema-generator.js.map +1 -1
  103. package/dist/core/scaffold/openapi-generator.d.ts +5 -0
  104. package/dist/core/scaffold/openapi-generator.d.ts.map +1 -1
  105. package/dist/core/scaffold/openapi-generator.js +53 -3
  106. package/dist/core/scaffold/openapi-generator.js.map +1 -1
  107. package/dist/core/scaffold/ui-generator.d.ts +0 -3
  108. package/dist/core/scaffold/ui-generator.d.ts.map +1 -1
  109. package/dist/core/scaffold/ui-generator.js +68 -7
  110. package/dist/core/scaffold/ui-generator.js.map +1 -1
  111. package/dist/core/verify/index.d.ts +4 -3
  112. package/dist/core/verify/index.d.ts.map +1 -1
  113. package/dist/core/verify/index.js +122 -5
  114. package/dist/core/verify/index.js.map +1 -1
  115. package/dist/machine/contracts.d.ts +1 -0
  116. package/dist/machine/contracts.d.ts.map +1 -1
  117. package/dist/machine/errors.d.ts +3 -2
  118. package/dist/machine/errors.d.ts.map +1 -1
  119. package/dist/machine/errors.js +2 -1
  120. package/dist/machine/errors.js.map +1 -1
  121. package/dist/machine/index.d.ts.map +1 -1
  122. package/dist/machine/index.js +59 -28
  123. package/dist/machine/index.js.map +1 -1
  124. package/dist/mcp/index.d.ts +6 -4
  125. package/dist/mcp/index.d.ts.map +1 -1
  126. package/dist/mcp/index.js +210 -25
  127. package/dist/mcp/index.js.map +1 -1
  128. package/dist/mcp/version-selection.d.ts +55 -0
  129. package/dist/mcp/version-selection.d.ts.map +1 -0
  130. package/dist/mcp/version-selection.js +152 -0
  131. package/dist/mcp/version-selection.js.map +1 -0
  132. package/dist/utils/azure-cli.d.ts +56 -0
  133. package/dist/utils/azure-cli.d.ts.map +1 -0
  134. package/dist/utils/azure-cli.js +326 -0
  135. package/dist/utils/azure-cli.js.map +1 -0
  136. package/dist/utils/azure-diagnostics.d.ts +16 -0
  137. package/dist/utils/azure-diagnostics.d.ts.map +1 -0
  138. package/dist/utils/azure-diagnostics.js +68 -0
  139. package/dist/utils/azure-diagnostics.js.map +1 -0
  140. package/dist/utils/package-manager.js +2 -2
  141. package/dist/utils/package-manager.js.map +1 -1
  142. package/dist/utils/python-uv.d.ts +5 -7
  143. package/dist/utils/python-uv.d.ts.map +1 -1
  144. package/dist/utils/python-uv.js +50 -24
  145. package/dist/utils/python-uv.js.map +1 -1
  146. package/dist/utils/react-dev.d.ts +4 -0
  147. package/dist/utils/react-dev.d.ts.map +1 -0
  148. package/dist/utils/react-dev.js +35 -0
  149. package/dist/utils/react-dev.js.map +1 -0
  150. package/dist/utils/semver.d.ts +14 -0
  151. package/dist/utils/semver.d.ts.map +1 -0
  152. package/dist/utils/semver.js +61 -0
  153. package/dist/utils/semver.js.map +1 -0
  154. package/package.json +6 -3
  155. package/src/__tests__/__snapshots__/boundaries.test.ts.snap +154 -0
  156. package/src/__tests__/agent-instructions.test.ts +5 -0
  157. package/src/__tests__/agent-loop-phase34.test.ts +143 -8
  158. package/src/__tests__/auth.test.ts +9 -0
  159. package/src/__tests__/azure-cli.test.ts +202 -0
  160. package/src/__tests__/boundaries.test.ts +42 -0
  161. package/src/__tests__/csharp-schema.test.ts +261 -0
  162. package/src/__tests__/dev.test.ts +13 -6
  163. package/src/__tests__/fixtures/az wrapper.cmd +2 -0
  164. package/src/__tests__/fixtures/azure-cli.cjs +153 -0
  165. package/src/__tests__/fixtures/provision-runner.cjs +28 -0
  166. package/src/__tests__/generated-project-matrix.test.ts +789 -0
  167. package/src/__tests__/home-page.test.ts +50 -0
  168. package/src/__tests__/init.test.ts +116 -0
  169. package/src/__tests__/mcp-version.test.ts +262 -0
  170. package/src/__tests__/mcp.test.ts +120 -3
  171. package/src/__tests__/multi-auth.test.ts +115 -4
  172. package/src/__tests__/package-manager.test.ts +17 -2
  173. package/src/__tests__/plan-apply.test.ts +179 -0
  174. package/src/__tests__/provision-runtime.test.ts +722 -0
  175. package/src/__tests__/python-uv.test.ts +50 -14
  176. package/src/__tests__/python-venv.test.ts +48 -0
  177. package/src/__tests__/react-dev.test.ts +72 -0
  178. package/src/__tests__/react.test.ts +19 -0
  179. package/src/__tests__/schema-analysis.test.ts +329 -0
  180. package/src/cli/commands/add-auth.ts +36 -6
  181. package/src/cli/commands/configure-mcp.ts +54 -0
  182. package/src/cli/commands/dev.ts +40 -31
  183. package/src/cli/commands/init.ts +73 -138
  184. package/src/cli/commands/migrations.ts +22 -0
  185. package/src/cli/commands/provision.ts +294 -56
  186. package/src/cli/commands/scaffold.ts +105 -3
  187. package/src/cli/index.ts +40 -7
  188. package/src/core/operations/create-model.ts +51 -13
  189. package/src/core/operations/migration-generator.ts +14 -1
  190. package/src/core/operations/model-migration.ts +52 -0
  191. package/src/core/operations/provision-deployment.ts +183 -0
  192. package/src/core/operations/provision-operations.ts +662 -154
  193. package/src/core/operations/scaffold-apply.ts +2 -0
  194. package/src/core/operations/scaffold-machine.ts +1 -0
  195. package/src/core/operations/scaffold-plan.ts +5 -1
  196. package/src/core/project/agent-instructions.ts +4 -1
  197. package/src/core/project/agent-templates/AGENTS.md +89 -6
  198. package/src/core/project/agent-templates/CLAUDE.md +14 -1
  199. package/src/core/project/agent-templates/copilot-instructions.md +15 -1
  200. package/src/core/project/agent-templates/shared-models.instructions.md +11 -3
  201. package/src/core/project/boundaries.ts +62 -20
  202. package/src/core/project/capabilities.ts +49 -6
  203. package/src/core/project/home-page.ts +114 -0
  204. package/src/core/project/mcp-config.ts +84 -0
  205. package/src/core/project/react-project.ts +50 -28
  206. package/src/core/project/state.ts +70 -0
  207. package/src/core/project/workflows.ts +53 -7
  208. package/src/core/scaffold/auth-generator.ts +26 -27
  209. package/src/core/scaffold/model-parser.ts +413 -129
  210. package/src/core/scaffold/native-schema-generator.ts +319 -103
  211. package/src/core/scaffold/openapi-generator.ts +63 -5
  212. package/src/core/scaffold/ui-generator.ts +70 -8
  213. package/src/core/verify/index.ts +118 -6
  214. package/src/machine/contracts.ts +1 -0
  215. package/src/machine/errors.ts +4 -2
  216. package/src/machine/index.ts +65 -34
  217. package/src/mcp/index.ts +221 -31
  218. package/src/mcp/version-selection.ts +163 -0
  219. package/src/utils/azure-cli.ts +315 -0
  220. package/src/utils/azure-diagnostics.ts +75 -0
  221. package/src/utils/package-manager.ts +2 -2
  222. package/src/utils/python-uv.ts +53 -33
  223. package/src/utils/react-dev.ts +31 -0
  224. package/src/utils/semver.ts +59 -0
@@ -1,4 +1,4 @@
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";
@@ -25,7 +25,9 @@ import {
25
25
  buildCSharpCodegenToolManifestSource,
26
26
  buildPythonCodegenRequirementsSource,
27
27
  } from "../../core/scaffold/native-schema-generator";
28
- 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";
29
31
 
30
32
  interface InitOptions {
31
33
  name: string;
@@ -144,7 +146,7 @@ function getFunctionsRuntimeConfig(backendLanguage: BackendLanguage): { name: st
144
146
  return { name: "dotnet-isolated", version: "10.0" };
145
147
  }
146
148
  if (backendLanguage === "python") {
147
- return { name: "python", version: "3.11" };
149
+ return { name: "python", version: DEFAULT_AZURE_PYTHON_VERSION };
148
150
  }
149
151
  return { name: "node", version: "22" };
150
152
  }
@@ -344,7 +346,7 @@ export async function initCommand(options: InitOptions) {
344
346
  const azureConfig = await promptAzureConfig(options);
345
347
 
346
348
  if (frontend === "react") {
347
- createReactProject(projectDir, options.name);
349
+ createReactProject(projectDir, options.name, pm);
348
350
  } else {
349
351
  await createNextJsProject(options.name, pm);
350
352
  await upgradeNextJs(projectDir, options.nextVersion || 'latest', pm);
@@ -711,7 +713,7 @@ export function buildCSharpFunctionsProjectSource(): string {
711
713
  <None Remove="generated\\**\\bin\\**;generated\\**\\obj\\**" />
712
714
  </ItemGroup>
713
715
  <ItemGroup>
714
- <PackageReference Include="Microsoft.Azure.Cosmos" Version="3.47.0" />
716
+ <PackageReference Include="Microsoft.Azure.Cosmos" Version="3.63.2" />
715
717
  <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
716
718
  <PackageReference Include="Azure.Identity" Version="1.17.0" />
717
719
  <PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.52.0" />
@@ -778,14 +780,30 @@ function getSwallowKitPackageMetadata(): { name: string; version: string } {
778
780
  };
779
781
  }
780
782
 
781
- export function buildGeneratedProjectDevDependencies(): Record<string, string> {
783
+ export function buildGeneratedProjectDevDependencies(frontend: FrontendFramework = "nextjs"): Record<string, string> {
782
784
  const { name, version } = getSwallowKitPackageMetadata();
783
785
  return {
784
786
  [name]: version,
787
+ ...(frontend === "nextjs" ? { "@tailwindcss/postcss": "^4.0.0" } : {}),
785
788
  };
786
789
  }
787
790
 
788
- 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 {
789
807
  const { name } = getSwallowKitPackageMetadata();
790
808
  return JSON.stringify(
791
809
  {
@@ -797,7 +815,7 @@ export function buildSwallowKitMcpProjectConfigSource(pm: PackageManager = 'pnpm
797
815
  : ["--yes", "--package", `${name}@\${SWALLOWKIT_MCP_VERSION}`, "swallowkit-mcp"],
798
816
  cwd: ".",
799
817
  env: {
800
- SWALLOWKIT_MCP_VERSION: "latest",
818
+ SWALLOWKIT_MCP_VERSION: mcpVersion,
801
819
  },
802
820
  },
803
821
  },
@@ -807,6 +825,15 @@ export function buildSwallowKitMcpProjectConfigSource(pm: PackageManager = 'pnpm
807
825
  );
808
826
  }
809
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
+
810
837
  async function addSwallowKitFiles(
811
838
  projectDir: string,
812
839
  options: InitOptions,
@@ -831,7 +858,7 @@ async function addSwallowKitFiles(
831
858
  };
832
859
  packageJson.devDependencies = {
833
860
  ...packageJson.devDependencies,
834
- ...buildGeneratedProjectDevDependencies(),
861
+ ...buildGeneratedProjectDevDependencies(frontend),
835
862
  };
836
863
 
837
864
  packageJson.scripts = {
@@ -865,6 +892,8 @@ async function addSwallowKitFiles(
865
892
 
866
893
  fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
867
894
 
895
+ if (frontend === "nextjs") ensureNextTailwindPostcssConfig(projectDir);
896
+
868
897
  // Create pnpm-workspace.yaml for local pnpm development when needed.
869
898
  if (wsConfig.type === 'file') {
870
899
  fs.writeFileSync(path.join(projectDir, wsConfig.filename), wsConfig.content + (frontend === "react" ? " esbuild: true\n" : ""));
@@ -1185,7 +1214,7 @@ export async function register() {
1185
1214
  createAiAgentFiles(projectDir, projectName, backendLanguage, pm, frontend);
1186
1215
  }
1187
1216
 
1188
- async function createSharedPackage(projectDir: string, projectName: string) {
1217
+ export async function createSharedPackage(projectDir: string, projectName: string) {
1189
1218
  console.log('📦 Creating shared workspace package for Zod models...\n');
1190
1219
 
1191
1220
  const sharedDir = path.join(projectDir, 'shared');
@@ -1225,7 +1254,7 @@ async function createSharedPackage(projectDir: string, projectName: string) {
1225
1254
  // shared/index.ts (empty re-export file, scaffold will add entries)
1226
1255
  fs.writeFileSync(
1227
1256
  path.join(sharedDir, 'index.ts'),
1228
- `// 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`
1229
1258
  );
1230
1259
 
1231
1260
  // shared/.gitignore
@@ -1237,7 +1266,7 @@ async function createSharedPackage(projectDir: string, projectName: string) {
1237
1266
  console.log('✅ Shared package created\n');
1238
1267
  }
1239
1268
 
1240
- async function createAzureFunctionsProject(
1269
+ export async function createAzureFunctionsProject(
1241
1270
  projectDir: string,
1242
1271
  pm: PackageManager = 'pnpm',
1243
1272
  backendLanguage: BackendLanguage = 'typescript'
@@ -1492,7 +1521,6 @@ public sealed class GreetFunction
1492
1521
  }
1493
1522
 
1494
1523
  function createPythonFunctionsProject(projectDir: string, functionsDir: string): void {
1495
- fs.writeFileSync(path.join(projectDir, '.python-version'), '3.11\n');
1496
1524
  ensureProjectGitignoreEntry(projectDir, '.uv');
1497
1525
 
1498
1526
  fs.writeFileSync(path.join(functionsDir, 'requirements.txt'), `azure-functions>=1.20.0
@@ -1632,117 +1660,8 @@ export async function POST(request: NextRequest) {
1632
1660
 
1633
1661
  async function createHomePage(projectDir: string, pm: PackageManager = 'pnpm') {
1634
1662
  console.log('📦 Creating home page...\n');
1635
-
1636
- const pmCmd = getCommands(pm);
1637
-
1638
- const pageContent = `'use client'
1639
-
1640
- export const dynamic = 'force-dynamic';
1641
-
1642
- import { useState } from 'react';
1643
- import { scaffoldConfig } from '@/lib/scaffold-config';
1644
1663
 
1645
- export default function Home() {
1646
- const [greetingStatus, setGreetingStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
1647
- const [message, setMessage] = useState('');
1648
-
1649
- const testConnection = async () => {
1650
- setGreetingStatus('loading');
1651
- try {
1652
- const response = await fetch('/api/greet?name=SwallowKit');
1653
- const data = await response.json();
1654
- if (!response.ok) {
1655
- throw new Error(data.error || \`Server error: \${response.status}\`);
1656
- }
1657
- setMessage(data.message);
1658
- setGreetingStatus('success');
1659
- } catch (error) {
1660
- setMessage(error instanceof Error ? error.message : 'Failed to connect to Azure Functions');
1661
- setGreetingStatus('error');
1662
- }
1663
- };
1664
-
1665
- return (
1666
- <div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-50 dark:from-gray-900 dark:to-gray-800">
1667
- <div className="container mx-auto px-4 py-12">
1668
- <header className="text-center mb-16">
1669
- <h1 className="text-5xl font-bold text-gray-800 dark:text-white mb-4">
1670
- Welcome to SwallowKit
1671
- </h1>
1672
- <p className="text-xl text-gray-600 dark:text-gray-400">
1673
- Next.js on Azure Static Web Apps + Functions + Cosmos DB — Zod schema sharing
1674
- </p>
1675
- </header>
1676
-
1677
- {/* Connection Test */}
1678
- <section className="max-w-2xl mx-auto mb-12">
1679
- <div className="bg-white dark:bg-gray-800 rounded-xl p-8 border border-gray-200 dark:border-gray-700">
1680
- <h2 className="text-2xl font-semibold mb-4 text-gray-900 dark:text-gray-100">
1681
- Test BFF → Functions Connection
1682
- </h2>
1683
- <button
1684
- onClick={testConnection}
1685
- disabled={greetingStatus === 'loading'}
1686
- className="px-6 py-3 bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 text-white rounded-lg font-medium transition-colors"
1687
- >
1688
- {greetingStatus === 'loading' ? 'Testing...' : 'Test Connection'}
1689
- </button>
1690
- {greetingStatus === 'success' && (
1691
- <div className="mt-4 p-4 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg">
1692
- <p className="text-green-800 dark:text-green-200 font-medium">✅ Connection successful!</p>
1693
- <p className="text-green-700 dark:text-green-300 text-sm mt-1">{message}</p>
1694
- </div>
1695
- )}
1696
- {greetingStatus === 'error' && (
1697
- <div className="mt-4 p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg">
1698
- <p className="text-red-800 dark:text-red-200 font-medium">❌ Connection failed</p>
1699
- <p className="text-red-700 dark:text-red-300 text-sm mt-1">{message}</p>
1700
- </div>
1701
- )}
1702
- </div>
1703
- </section>
1704
-
1705
- {/* Scaffolded Models Menu */}
1706
- {scaffoldConfig.models.length > 0 ? (
1707
- <section className="max-w-6xl mx-auto">
1708
- <h2 className="text-3xl font-bold mb-8 text-gray-900 dark:text-gray-100">Your Models</h2>
1709
- <div className="grid gap-6 grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
1710
- {scaffoldConfig.models.map((model) => (
1711
- <a
1712
- key={model.name}
1713
- href={model.path}
1714
- 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"
1715
- >
1716
- <h3 className="text-2xl font-semibold mb-2 text-gray-900 dark:text-gray-100">{model.label}</h3>
1717
- <p className="text-gray-600 dark:text-gray-400">Manage {model.label.toLowerCase()}</p>
1718
- </a>
1719
- ))}
1720
- </div>
1721
- </section>
1722
- ) : (
1723
- <section className="max-w-2xl mx-auto text-center">
1724
- <div className="bg-white dark:bg-gray-800 rounded-xl p-12 border border-gray-200 dark:border-gray-700">
1725
- <h2 className="text-2xl font-semibold mb-4 text-gray-900 dark:text-gray-100">Get Started</h2>
1726
- <p className="text-gray-600 dark:text-gray-400 mb-6">
1727
- Create your first model with Zod and generate CRUD operations automatically.
1728
- </p>
1729
- <code className="block bg-gray-100 dark:bg-gray-900 p-4 rounded text-left text-sm">
1730
- ${pmCmd.exec} swallowkit scaffold shared/models/your-model.ts
1731
- </code>
1732
- </div>
1733
- </section>
1734
- )}
1735
-
1736
- <footer className="mt-16 text-center text-gray-600 dark:text-gray-400 text-sm">
1737
- <p>Built with SwallowKit</p>
1738
- </footer>
1739
- </div>
1740
- </div>
1741
- );
1742
- }
1743
- `;
1744
-
1745
- fs.writeFileSync(path.join(projectDir, 'app', 'page.tsx'), pageContent);
1664
+ fs.writeFileSync(path.join(projectDir, 'app', 'page.tsx'), buildHomePage("nextjs", pm));
1746
1665
 
1747
1666
  console.log('✅ Home page created\n');
1748
1667
 
@@ -1796,9 +1715,8 @@ function createReadme(
1796
1715
  const backendScaffoldNote = backendLanguage === 'typescript'
1797
1716
  ? '- Azure Functions CRUD endpoints'
1798
1717
  : `- Azure Functions ${backendLanguageLabel} CRUD handlers\n- OpenAPI export + native-generated ${backendLanguageLabel} schema assets`;
1799
- const pythonUvPaths = getProjectLocalUvPaths(projectDir);
1800
1718
  const pythonLocalDevNote = backendLanguage === 'python'
1801
- ? `\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`
1802
1720
  : '';
1803
1721
 
1804
1722
  const readme = `# ${projectName}
@@ -1907,10 +1825,12 @@ ${pmCmd.exec} swallowkit provision --resource-group <rg-name>
1907
1825
 
1908
1826
  This creates:
1909
1827
  - Static Web App (\`swa-${projectName}\`)
1910
- - Azure Functions (\`func-${projectName}\`)
1911
- - Cosmos DB (\`cosmos-${projectName}\`)
1828
+ - Azure Functions (\`func-${projectName}-<suffix>\`)
1829
+ - Cosmos DB (\`cosmos-${projectName}-<suffix>\`)
1912
1830
  - Storage Account
1913
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
+
1914
1834
  You will be prompted to select Azure regions:
1915
1835
  1. **Primary location**: For Functions and Cosmos DB (default: Japan East)
1916
1836
  2. **Static Web App location**: Limited availability (default: East Asia)
@@ -1926,12 +1846,12 @@ ${cicdChoice === 'github' ? `#### GitHub Actions
1926
1846
 
1927
1847
  2. Get Function App publish profile:
1928
1848
  \`\`\`bash
1929
- 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
1930
1850
  \`\`\`
1931
1851
 
1932
1852
  3. Add secrets to GitHub repository:
1933
1853
  - \`AZURE_STATIC_WEB_APPS_API_TOKEN\`: SWA deployment token (from step 1)
1934
- - \`AZURE_FUNCTIONAPP_NAME\`: \`func-${projectName}\`
1854
+ - \`AZURE_FUNCTIONAPP_NAME\`: Functions app name printed by \`swallowkit provision\` (\`func-${projectName}-<suffix>\`)
1935
1855
  - \`AZURE_FUNCTIONAPP_PUBLISH_PROFILE\`: Functions publish profile (from step 2)
1936
1856
 
1937
1857
  4. Push to \`main\` branch to trigger deployment (or use **Actions** → **Run workflow** for manual deployment)` : cicdChoice === 'azure' ? `#### Azure Pipelines
@@ -1953,8 +1873,10 @@ az staticwebapp deploy --name swa-${projectName} --resource-group <rg-name> --ap
1953
1873
  \`\`\`bash
1954
1874
  cd functions
1955
1875
  ${pmCmd.run} build
1956
- func azure functionapp publish func-${projectName}
1957
- \`\`\``}
1876
+ func azure functionapp publish <functions-app-name>
1877
+ \`\`\`
1878
+
1879
+ Use the Functions app name printed by \`swallowkit provision\`.`}
1958
1880
 
1959
1881
  ## 🔧 Available Commands
1960
1882
 
@@ -2031,11 +1953,16 @@ function createAiAgentFiles(
2031
1953
  console.log(' ✅ .github/copilot-instructions.md (GitHub Copilot adapter)');
2032
1954
  console.log(' ✅ .github/instructions/ (path-specific instructions)');
2033
1955
 
1956
+ const mcpVersion = selectInitialMcpVersion();
2034
1957
  fs.writeFileSync(
2035
1958
  path.join(projectDir, '.mcp.json'),
2036
- 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.)`
2037
1965
  );
2038
- console.log(' ✅ .mcp.json (project-scoped MCP bootstrap)');
2039
1966
 
2040
1967
  writeWorkflowDocs(projectDir, runCmd);
2041
1968
  console.log(' ✅ .swallowkit/workflows/ (agent-agnostic autonomous-loop runbooks)');
@@ -2126,6 +2053,14 @@ param cosmosDbMode string = '${azureConfig.cosmosDbMode}'
2126
2053
  @description('Enable VNet integration')
2127
2054
  param enableVNet bool = ${enableVNet}
2128
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
+
2129
2064
  // Shared Log Analytics Workspace (in Functions region for data residency)
2130
2065
  module logAnalytics 'modules/loganalytics.bicep' = {
2131
2066
  name: 'logAnalytics'
@@ -2179,7 +2114,7 @@ module vnet 'modules/vnet.bicep' = if (enableVNet) {
2179
2114
  module cosmosDbFreeTier 'modules/cosmosdb-freetier.bicep' = if (cosmosDbMode == 'freetier') {
2180
2115
  name: 'cosmosDb'
2181
2116
  params: {
2182
- accountName: 'cosmos-\${projectName}'
2117
+ accountName: cosmosAccountName
2183
2118
  databaseName: '\${projectName}Database'
2184
2119
  location: location
2185
2120
  publicNetworkAccess: enableVNet ? 'Disabled' : 'Enabled'
@@ -2189,7 +2124,7 @@ module cosmosDbFreeTier 'modules/cosmosdb-freetier.bicep' = if (cosmosDbMode ==
2189
2124
  module cosmosDbServerless 'modules/cosmosdb-serverless.bicep' = if (cosmosDbMode == 'serverless') {
2190
2125
  name: 'cosmosDb'
2191
2126
  params: {
2192
- accountName: 'cosmos-\${projectName}'
2127
+ accountName: cosmosAccountName
2193
2128
  databaseName: '\${projectName}Database'
2194
2129
  location: location
2195
2130
  publicNetworkAccess: enableVNet ? 'Disabled' : 'Enabled'
@@ -2213,7 +2148,7 @@ module cosmosPrivateEndpoint 'modules/private-endpoint-cosmos.bicep' = if (enabl
2213
2148
  module functionsFlex 'modules/functions-flex.bicep' = {
2214
2149
  name: 'functionsApp'
2215
2150
  params: {
2216
- name: 'func-\${projectName}'
2151
+ name: functionsAppName
2217
2152
  location: location
2218
2153
  storageAccountName: 'stg\${uniqueString(resourceGroup().id, projectName)}'
2219
2154
  appInsightsConnectionString: appInsightsFunctions.outputs.connectionString
@@ -2949,7 +2884,7 @@ jobs:
2949
2884
  ${commonSetup} - name: Setup Python
2950
2885
  uses: actions/setup-python@v5
2951
2886
  with:
2952
- python-version: '3.11'
2887
+ python-version: '${DEFAULT_AZURE_PYTHON_VERSION}'
2953
2888
 
2954
2889
  - name: Install Functions dependencies
2955
2890
  run: |
@@ -3138,7 +3073,7 @@ variables:
3138
3073
  steps:
3139
3074
  ${commonSetup} - task: UsePythonVersion@0
3140
3075
  inputs:
3141
- versionSpec: '3.11'
3076
+ versionSpec: '${DEFAULT_AZURE_PYTHON_VERSION}'
3142
3077
  displayName: 'Install Python'
3143
3078
 
3144
3079
  - script: |
@@ -1,6 +1,7 @@
1
1
  import { Command } from "commander";
2
2
  import { ensureSwallowKitProject } from "../../core/config";
3
3
  import { initializeLegacyMigrations } from "../../core/operations/migration-generator";
4
+ import { addModelContainerMigration } from "../../core/operations/model-migration";
4
5
 
5
6
  export const migrationsCommand = new Command("migrations")
6
7
  .description("Manage versioned Azure infrastructure migrations");
@@ -18,3 +19,24 @@ migrationsCommand
18
19
  console.log(`Migration baseline 0 created for project ${manifest.projectId}.`);
19
20
  console.log("Commit infra/migrations/manifest.json, then run provision with --adopt-existing --baseline 0.");
20
21
  });
22
+
23
+ migrationsCommand
24
+ .command("add-model")
25
+ .description("Generate only the Cosmos DB container migration for a model (e.g. after scaffold --skip-infra)")
26
+ .argument("<model>", "Model file or model name")
27
+ .option("--dry-run", "Show the planned infra changes without writing anything", false)
28
+ .action(async (model: string, options: { dryRun?: boolean }) => {
29
+ ensureSwallowKitProject("migrations add-model");
30
+ const result = await addModelContainerMigration(model, { dryRun: options.dryRun });
31
+ for (const operation of result.operations) {
32
+ if (operation.action !== "skip") console.log(` ${operation.action.padEnd(7)} ${operation.path}`);
33
+ }
34
+ if (!result.migration) {
35
+ console.log(`No migration needed for ${result.model}: ${result.reason}`);
36
+ } else if (result.operations.every((operation) => operation.action === "skip")) {
37
+ console.log(`Migration ${result.migration.version} for ${result.model} already exists: ${result.migration.template}`);
38
+ } else {
39
+ console.log(`${result.dryRun ? "Would create" : "Created"} migration ${result.migration.version} for ${result.model}: ${result.migration.template}`);
40
+ if (!result.dryRun) console.log("Commit the migration, then run provision to plan and apply it.");
41
+ }
42
+ });