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
@@ -0,0 +1,154 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`inspect boundaries convention rules matches the csharp backend 1`] = `
4
+ [
5
+ {
6
+ "description": "SwallowKit metadata (manifest, artifact ledger, plan state). Managed by tooling only.",
7
+ "editPolicy": "never-hand-edit",
8
+ "pattern": ".swallowkit/",
9
+ "zone": "deterministic",
10
+ },
11
+ {
12
+ "description": "Zod model schemas — the single source of truth. AI/developers author these freely; deterministic generation derives everything else from them.",
13
+ "editPolicy": "free-edit",
14
+ "pattern": "shared/models/",
15
+ "zone": "ai-authored",
16
+ },
17
+ {
18
+ "description": "Infrastructure migration ledger with baseline checksums. Updated by scaffold, \`swallowkit migrations add-model\`, and \`swallowkit create-migration\`.",
19
+ "editPolicy": "never-hand-edit",
20
+ "pattern": "infra/migrations/manifest.json",
21
+ "zone": "deterministic",
22
+ },
23
+ {
24
+ "description": "Versioned Bicep migrations. Edit a migration only before it is provisioned; never change an applied migration — add a new one with \`swallowkit create-migration <slug>\`.",
25
+ "editPolicy": "free-edit",
26
+ "pattern": "infra/migrations/",
27
+ "zone": "ai-authored",
28
+ },
29
+ {
30
+ "description": "Baseline infrastructure (main.bicep and modules) whose checksums are recorded in infra/migrations/manifest.json. Hand edits block scaffold with baseline-drift; add resources through a new migration instead.",
31
+ "editPolicy": "never-hand-edit",
32
+ "pattern": "infra/",
33
+ "zone": "deterministic",
34
+ },
35
+ {
36
+ "description": "create-model and scaffold append missing model exports while preserving existing content. No managed markers; manual exports are allowed. Keep exports needed by generated code.",
37
+ "editPolicy": "free-edit",
38
+ "pattern": "shared/index.ts",
39
+ "zone": "shared",
40
+ },
41
+ {
42
+ "description": "Any path not recorded in the artifact ledger is AI/developer-authored. Business logic, custom pages, custom Functions, and tests belong here.",
43
+ "editPolicy": "free-edit",
44
+ "pattern": "*",
45
+ "zone": "ai-authored",
46
+ },
47
+ ]
48
+ `;
49
+
50
+ exports[`inspect boundaries convention rules matches the python backend 1`] = `
51
+ [
52
+ {
53
+ "description": "SwallowKit metadata (manifest, artifact ledger, plan state). Managed by tooling only.",
54
+ "editPolicy": "never-hand-edit",
55
+ "pattern": ".swallowkit/",
56
+ "zone": "deterministic",
57
+ },
58
+ {
59
+ "description": "Zod model schemas — the single source of truth. AI/developers author these freely; deterministic generation derives everything else from them.",
60
+ "editPolicy": "free-edit",
61
+ "pattern": "shared/models/",
62
+ "zone": "ai-authored",
63
+ },
64
+ {
65
+ "description": "Infrastructure migration ledger with baseline checksums. Updated by scaffold, \`swallowkit migrations add-model\`, and \`swallowkit create-migration\`.",
66
+ "editPolicy": "never-hand-edit",
67
+ "pattern": "infra/migrations/manifest.json",
68
+ "zone": "deterministic",
69
+ },
70
+ {
71
+ "description": "Versioned Bicep migrations. Edit a migration only before it is provisioned; never change an applied migration — add a new one with \`swallowkit create-migration <slug>\`.",
72
+ "editPolicy": "free-edit",
73
+ "pattern": "infra/migrations/",
74
+ "zone": "ai-authored",
75
+ },
76
+ {
77
+ "description": "Baseline infrastructure (main.bicep and modules) whose checksums are recorded in infra/migrations/manifest.json. Hand edits block scaffold with baseline-drift; add resources through a new migration instead.",
78
+ "editPolicy": "never-hand-edit",
79
+ "pattern": "infra/",
80
+ "zone": "deterministic",
81
+ },
82
+ {
83
+ "description": "create-model and scaffold append missing model exports while preserving existing content. No managed markers; manual exports are allowed. Keep exports needed by generated code.",
84
+ "editPolicy": "free-edit",
85
+ "pattern": "shared/index.ts",
86
+ "zone": "shared",
87
+ },
88
+ {
89
+ "description": "SwallowKit appends blueprint registrations under its marker; other edits are free.",
90
+ "editPolicy": "edit-outside-markers",
91
+ "pattern": "functions/function_app.py",
92
+ "zone": "shared",
93
+ },
94
+ {
95
+ "description": "SwallowKit appends connector driver requirements when connector models are scaffolded; keep those lines and edit other requirements freely.",
96
+ "editPolicy": "free-edit",
97
+ "pattern": "functions/requirements.txt",
98
+ "zone": "shared",
99
+ },
100
+ {
101
+ "description": "Any path not recorded in the artifact ledger is AI/developer-authored. Business logic, custom pages, custom Functions, and tests belong here.",
102
+ "editPolicy": "free-edit",
103
+ "pattern": "*",
104
+ "zone": "ai-authored",
105
+ },
106
+ ]
107
+ `;
108
+
109
+ exports[`inspect boundaries convention rules matches the typescript backend 1`] = `
110
+ [
111
+ {
112
+ "description": "SwallowKit metadata (manifest, artifact ledger, plan state). Managed by tooling only.",
113
+ "editPolicy": "never-hand-edit",
114
+ "pattern": ".swallowkit/",
115
+ "zone": "deterministic",
116
+ },
117
+ {
118
+ "description": "Zod model schemas — the single source of truth. AI/developers author these freely; deterministic generation derives everything else from them.",
119
+ "editPolicy": "free-edit",
120
+ "pattern": "shared/models/",
121
+ "zone": "ai-authored",
122
+ },
123
+ {
124
+ "description": "Infrastructure migration ledger with baseline checksums. Updated by scaffold, \`swallowkit migrations add-model\`, and \`swallowkit create-migration\`.",
125
+ "editPolicy": "never-hand-edit",
126
+ "pattern": "infra/migrations/manifest.json",
127
+ "zone": "deterministic",
128
+ },
129
+ {
130
+ "description": "Versioned Bicep migrations. Edit a migration only before it is provisioned; never change an applied migration — add a new one with \`swallowkit create-migration <slug>\`.",
131
+ "editPolicy": "free-edit",
132
+ "pattern": "infra/migrations/",
133
+ "zone": "ai-authored",
134
+ },
135
+ {
136
+ "description": "Baseline infrastructure (main.bicep and modules) whose checksums are recorded in infra/migrations/manifest.json. Hand edits block scaffold with baseline-drift; add resources through a new migration instead.",
137
+ "editPolicy": "never-hand-edit",
138
+ "pattern": "infra/",
139
+ "zone": "deterministic",
140
+ },
141
+ {
142
+ "description": "create-model and scaffold append missing model exports while preserving existing content. No managed markers; manual exports are allowed. Keep exports needed by generated code.",
143
+ "editPolicy": "free-edit",
144
+ "pattern": "shared/index.ts",
145
+ "zone": "shared",
146
+ },
147
+ {
148
+ "description": "Any path not recorded in the artifact ledger is AI/developer-authored. Business logic, custom pages, custom Functions, and tests belong here.",
149
+ "editPolicy": "free-edit",
150
+ "pattern": "*",
151
+ "zone": "ai-authored",
152
+ },
153
+ ]
154
+ `;
@@ -57,6 +57,10 @@ describe("coding-agent instruction contract", () => {
57
57
  expect(agentsMd).toContain("Verification Is a Completion Gate");
58
58
  expect(agentsMd).toContain("semantic code review answers different questions");
59
59
  expect(agentsMd).toContain("When the status is `requires-human`, stop");
60
+ expect(agentsMd).toContain("machine inspect provision --plan <runId> --wait-seconds 300");
61
+ expect(agentsMd).toContain("outcome: \"succeeded\"");
62
+ expect(agentsMd).toContain("Never re-submit Bicep");
63
+ expect(agentsMd).toContain("must not switch to normal CLI to bypass it");
60
64
  expect(agentsMd).toContain("never bypass its\napproval gate");
61
65
  });
62
66
 
@@ -88,6 +92,7 @@ describe("coding-agent instruction contract", () => {
88
92
  expect(adapter).toContain("planning");
89
93
  expect(adapter).toContain("Never hand-edit deterministic SwallowKit-managed artifacts");
90
94
  expect(adapter).toContain("SwallowKit verification");
95
+ expect(adapter).toContain("approval");
91
96
  }
92
97
  });
93
98
 
@@ -179,7 +184,7 @@ describe("coding-agent documentation synchronization", () => {
179
184
  }
180
185
 
181
186
  expect(englishGuide).toContain("complete English templates");
182
- expect(englishGuide).toContain("not\nabridged excerpts");
187
+ expect(englishGuide).toMatch(/not\r?\nabridged excerpts/);
183
188
  expect(japaneseGuide).toContain("抜粋ではありません");
184
189
  });
185
190
 
@@ -17,6 +17,8 @@ import { inspectInfra } from "../core/project/infra";
17
17
  import { loadCustomVerifyChecks, compactVerifyResult, runVerify, VerifyResult } from "../core/verify";
18
18
  import { buildAgentSkills, buildWorkflowDocs, writeAgentSkills, writeWorkflowDocs } from "../core/project/workflows";
19
19
  import { applyProvisionOperation, planProvisionOperation } from "../core/operations/provision-operations";
20
+ import { resetAzCliInvocationCache, setAzCliInvocationForTesting } from "../utils/azure-cli";
21
+ import * as azureCli from "../utils/azure-cli";
20
22
  import { createCustomMigration, initializeLegacyMigrations } from "../core/operations/migration-generator";
21
23
  import { createMigrationManifest, encodeMigrationState, getMigrationTagKey, saveMigrationManifest } from "../core/project/migrations";
22
24
 
@@ -126,12 +128,17 @@ describe("agent loop phase 3/4", () => {
126
128
  tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "swallowkit-p34-"));
127
129
  process.chdir(tempDir);
128
130
  (childProcess.spawnSync as unknown as any).mockReturnValue({ status: 1, stdout: "", stderr: "" });
131
+ // Platform-independent az invocation; the Windows wrapper resolution is covered in azure-cli.test.ts.
132
+ setAzCliInvocationForTesting({ command: "az", prefixArgs: [], source: "az" });
133
+ jest.spyOn(azureCli, "runAzCliAsync").mockImplementation(async (args) => azureCli.runAzCli(args));
129
134
  });
130
135
 
131
136
  afterEach(() => {
137
+ resetAzCliInvocationCache();
132
138
  process.chdir(originalCwd);
133
139
  fs.rmSync(tempDir, { recursive: true, force: true });
134
140
  jest.clearAllMocks();
141
+ jest.restoreAllMocks();
135
142
  });
136
143
 
137
144
  it("returns the responsibility boundary contract via inspect boundaries", async () => {
@@ -269,6 +276,7 @@ describe("agent loop phase 3/4", () => {
269
276
  createProjectFixture(tempDir);
270
277
  writeFile(path.join(tempDir, "infra", "main.bicep"), SAMPLE_MAIN_BICEP);
271
278
  writeFile(path.join(tempDir, "infra", "main.parameters.json"), "{}\n");
279
+ setAzCliInvocationForTesting(null);
272
280
 
273
281
  const plan = await runMachine([
274
282
  "node", "swallowkit", "machine", "plan", "provision",
@@ -312,11 +320,11 @@ describe("agent loop phase 3/4", () => {
312
320
  (childProcess.spawnSync as unknown as any).mockImplementation((_file: string, args: string[]) => {
313
321
  if (args[0] === "--version") return { status: 0, stdout: "azure-cli", stderr: "" };
314
322
  if (args[0] === "group" && args[1] === "exists") return { status: 0, stdout: "true\n", stderr: "" };
315
- if (args[0] === "group" && args[1] === "show") return { status: 0, stdout: JSON.stringify({ id: "/subscriptions/test/resourceGroups/rg-sample", tags: {} }), stderr: "" };
323
+ if (args[0] === "group" && args[1] === "show") return { status: 0, stdout: JSON.stringify({ id: "/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/rg-sample", tags: {} }), stderr: "" };
316
324
  return { status: 1, stdout: "", stderr: "unexpected" };
317
325
  });
318
326
 
319
- const plan = await planProvisionOperation({ resourceGroup: "rg-sample", location: "japaneast", swaLocation: "eastasia" });
327
+ const plan = await planProvisionOperation({ resourceGroup: "rg-sample", location: "japaneast", swaLocation: "eastasia", subscription: "11111111-1111-1111-1111-111111111111" });
320
328
 
321
329
  expect(plan.mode).toBe("adoption-required");
322
330
  expect(plan.commands).toEqual([]);
@@ -331,7 +339,7 @@ describe("agent loop phase 3/4", () => {
331
339
  (childProcess.spawnSync as unknown as any).mockImplementation((_file: string, args: string[]) => {
332
340
  if (args[0] === "--version") return { status: 0, stdout: "azure-cli", stderr: "" };
333
341
  if (args[0] === "group" && args[1] === "exists") return { status: 0, stdout: "true\n", stderr: "" };
334
- if (args[0] === "group" && args[1] === "show") return { status: 0, stdout: JSON.stringify({ id: "/subscriptions/test/resourceGroups/rg-sample", tags: {} }), stderr: "" };
342
+ if (args[0] === "group" && args[1] === "show") return { status: 0, stdout: JSON.stringify({ id: "/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/rg-sample", tags: {} }), stderr: "" };
335
343
  if (args[0] === "deployment" && args[2] === "what-if") {
336
344
  return { status: 0, stdout: JSON.stringify({ changes: [{ changeType: "Modify", resourceId: "/functions/sample" }] }), stderr: "" };
337
345
  }
@@ -345,6 +353,7 @@ describe("agent loop phase 3/4", () => {
345
353
  adoptExisting: true,
346
354
  baseline: 0,
347
355
  whatIf: true,
356
+ subscription: "11111111-1111-1111-1111-111111111111",
348
357
  });
349
358
 
350
359
  expect(plan.mode).toBe("adopt");
@@ -362,7 +371,7 @@ describe("agent loop phase 3/4", () => {
362
371
  (childProcess.spawnSync as unknown as any).mockImplementation((_file: string, args: string[]) => {
363
372
  if (args[0] === "--version") return { status: 0, stdout: "azure-cli", stderr: "" };
364
373
  if (args[0] === "group" && args[1] === "exists") return { status: 0, stdout: "true\n", stderr: "" };
365
- if (args[0] === "group" && args[1] === "show") return { status: 0, stdout: JSON.stringify({ id: "/subscriptions/test/resourceGroups/rg-sample", tags: {} }), stderr: "" };
374
+ if (args[0] === "group" && args[1] === "show") return { status: 0, stdout: JSON.stringify({ id: "/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/rg-sample", tags: {} }), stderr: "" };
366
375
  if (args[0] === "deployment" && args[2] === "what-if") return { status: 0, stdout: JSON.stringify({ changes: [] }), stderr: "" };
367
376
  return { status: 1, stdout: "", stderr: "unexpected" };
368
377
  });
@@ -372,6 +381,7 @@ describe("agent loop phase 3/4", () => {
372
381
  location: "japaneast",
373
382
  swaLocation: "eastasia",
374
383
  reconcile: true,
384
+ subscription: "11111111-1111-1111-1111-111111111111",
375
385
  });
376
386
 
377
387
  expect(plan.mode).toBe("reconcile");
@@ -393,12 +403,12 @@ describe("agent loop phase 3/4", () => {
393
403
  if (args[0] === "group" && args[1] === "exists") return { status: 0, stdout: "true\n", stderr: "" };
394
404
  if (args[0] === "group" && args[1] === "show") {
395
405
  const tagKey = getMigrationTagKey(manifest.projectId);
396
- return { status: 0, stdout: JSON.stringify({ id: "/subscriptions/test/resourceGroups/rg-sample", tags: { [tagKey]: tagValue } }), stderr: "" };
406
+ return { status: 0, stdout: JSON.stringify({ id: "/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/rg-sample", tags: { [tagKey]: tagValue } }), stderr: "" };
397
407
  }
398
408
  return { status: 1, stdout: "", stderr: "unexpected" };
399
409
  });
400
410
 
401
- const plan = await planProvisionOperation({ resourceGroup: "rg-sample", location: "japaneast", swaLocation: "eastasia" });
411
+ const plan = await planProvisionOperation({ resourceGroup: "rg-sample", location: "japaneast", swaLocation: "eastasia", subscription: "11111111-1111-1111-1111-111111111111" });
402
412
 
403
413
  expect(plan.mode).toBe("migrate");
404
414
  expect(plan.pendingMigrations.map((migration) => migration.version)).toEqual([1]);
@@ -419,7 +429,13 @@ describe("agent loop phase 3/4", () => {
419
429
  (childProcess.spawnSync as unknown as any).mockImplementation((_file: string, args: string[]) => {
420
430
  if (args[0] === "--version") return { status: 0, stdout: "azure-cli", stderr: "" };
421
431
  if (args[0] === "group" && args[1] === "exists") return { status: 0, stdout: "true\n", stderr: "" };
422
- if (args[0] === "group" && args[1] === "show") return { status: 0, stdout: JSON.stringify({ id: "/subscriptions/test/resourceGroups/rg-sample", tags: { [tagKey]: tagValue } }), stderr: "" };
432
+ if (args[0] === "group" && args[1] === "show") return { status: 0, stdout: JSON.stringify({ id: "/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/rg-sample", tags: { [tagKey]: tagValue } }), stderr: "" };
433
+ if (args[0] === "deployment" && args[2] === "show") {
434
+ const name = args[args.indexOf("--name") + 1];
435
+ return deployedTemplates.length === 0
436
+ ? { status: 1, stdout: "", stderr: "DeploymentNotFound" }
437
+ : { status: 0, stdout: JSON.stringify({ id: `/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/rg-sample/providers/Microsoft.Resources/deployments/${name}`, state: "Succeeded", correlationId: "fixture-correlation" }), stderr: "" };
438
+ }
423
439
  if (args[0] === "deployment" && args[2] === "create") {
424
440
  deployedTemplates.push(args[args.indexOf("--template-file") + 1]);
425
441
  return { status: 0, stdout: JSON.stringify({ properties: { outputs: {} } }), stderr: "" };
@@ -431,7 +447,7 @@ describe("agent loop phase 3/4", () => {
431
447
  return { status: 1, stdout: "", stderr: `unexpected: ${args.join(" ")}` };
432
448
  });
433
449
 
434
- const plan = await planProvisionOperation({ resourceGroup: "rg-sample", location: "japaneast", swaLocation: "eastasia" });
450
+ const plan = await planProvisionOperation({ resourceGroup: "rg-sample", location: "japaneast", swaLocation: "eastasia", subscription: "11111111-1111-1111-1111-111111111111" });
435
451
  const result = await applyProvisionOperation({ planId: plan.planId, approve: true });
436
452
 
437
453
  expect(result.executedCommands).toHaveLength(2);
@@ -508,6 +524,30 @@ describe("agent loop phase 3/4", () => {
508
524
  );
509
525
  });
510
526
 
527
+ it.each<[string, string[]]>([
528
+ ["pnpm", ["exec", "next", "typegen"]],
529
+ ["npm", ["exec", "--", "next", "typegen"]],
530
+ ])("generates Next.js route types with %s before typechecking", async (packageManager, typegenArgs) => {
531
+ writeFile(
532
+ path.join(tempDir, "package.json"),
533
+ JSON.stringify({ name: "sample-app", devEngines: { packageManager: { name: packageManager, version: ">=10", onFail: "error" } } })
534
+ );
535
+ writeFile(path.join(tempDir, "tsconfig.json"), JSON.stringify({ compilerOptions: { noEmit: true } }));
536
+ writeFile(path.join(tempDir, "next.config.ts"), "export default {};\n");
537
+ writeFile(path.join(tempDir, "node_modules", "next", "package.json"), JSON.stringify({ name: "next", version: "16.0.0" }));
538
+ const spawnSyncMock = childProcess.spawnSync as unknown as ReturnType<typeof jest.fn>;
539
+ spawnSyncMock.mockReset();
540
+ spawnSyncMock
541
+ .mockReturnValueOnce({ status: 1, stdout: "", stderr: "typegen failed" })
542
+ .mockReturnValueOnce({ status: 2, stdout: "app/layout.tsx: Cannot find name 'LayoutProps'.", stderr: "" });
543
+
544
+ const result = await runVerify(["typecheck"], tempDir);
545
+
546
+ expect(spawnSyncMock.mock.calls[0].slice(0, 2)).toEqual([packageManager, typegenArgs]);
547
+ expect(result.checks[0].status).toBe("fail");
548
+ expect(result.checks[0].evidence.logTail?.[0]).toMatch(/^next typegen did not complete \(exit 1\)/);
549
+ });
550
+
511
551
  it("uses the existing typecheck script when present", async () => {
512
552
  writeFile(
513
553
  path.join(tempDir, "package.json"),
@@ -530,6 +570,101 @@ describe("agent loop phase 3/4", () => {
530
570
  );
531
571
  });
532
572
 
573
+ it("includes standalone C# compilation in default verification and explains source errors", async () => {
574
+ createProjectFixture(tempDir);
575
+ writeFile(path.join(tempDir, "swallowkit.config.json"), JSON.stringify({ backend: { language: "csharp" } }));
576
+ // The fixture's .js config otherwise takes precedence.
577
+ fs.unlinkSync(path.join(tempDir, "swallowkit.config.js"));
578
+ fs.mkdirSync(path.join(tempDir, "shared", "models"), { recursive: true });
579
+ fs.mkdirSync(path.join(tempDir, "app", "api"), { recursive: true });
580
+ writeFile(path.join(tempDir, "functions", "SwallowKit.Functions.csproj"), "<Project />\n");
581
+ const spawnMock = childProcess.spawnSync as unknown as ReturnType<typeof jest.fn>;
582
+ spawnMock.mockImplementation((command: string, args: string[]) => ({
583
+ status: command === "dotnet" && args[0] === "build" ? 1 : 0,
584
+ stdout: command === "dotnet" && args[0] === "build"
585
+ ? `${path.join(tempDir, "functions", "Crud", "ProbeItemCrudFunctions.cs")}(8,7): error CS0246: Functions namespace not found`
586
+ : "",
587
+ stderr: "",
588
+ }));
589
+ const result = await runVerify(undefined, tempDir);
590
+ expect(result.summary.done).toBe(false);
591
+ expect(result.checks).toContainEqual(expect.objectContaining({
592
+ id: "backend-build", status: "fail",
593
+ evidence: expect.objectContaining({ logTail: expect.arrayContaining([expect.stringContaining("(8,7): error CS0246")]) }),
594
+ }));
595
+ const explanation = await runMachine(["node", "swallowkit", "machine", "explain", "failure", "--check", "backend-build"]);
596
+ expect(JSON.stringify(explanation.response)).toContain("(8,7): error CS0246");
597
+ });
598
+
599
+ it.each(["csharp", "python"] as const)("skips %s backend compilation when its toolchain is missing", async (language) => {
600
+ createProjectFixture(tempDir);
601
+ writeFile(path.join(tempDir, "swallowkit.config.js"), `module.exports = { backend: { language: '${language}' } };`);
602
+ const missing = Object.assign(new Error("not found"), { code: "ENOENT" });
603
+ (childProcess.spawnSync as unknown as ReturnType<typeof jest.fn>).mockReturnValue({ status: null, error: missing });
604
+ const result = await runVerify(["backend-build"], tempDir);
605
+ expect(result.checks[0]).toMatchObject({ id: "backend-build", status: "skip" });
606
+ expect(result.checks[0].evidence.diagnostics?.[0]).toMatch(/not installed|not available/);
607
+ expect(result.summary).toMatchObject({ skipped: 1, failed: 0 });
608
+ });
609
+
610
+ it("does not hide an installed backend toolchain execution failure as a skip", async () => {
611
+ createProjectFixture(tempDir);
612
+ writeFile(path.join(tempDir, "swallowkit.config.js"), "module.exports = { backend: { language: 'csharp' } };");
613
+ const denied = Object.assign(new Error("spawn EPERM"), { code: "EPERM" });
614
+ (childProcess.spawnSync as unknown as ReturnType<typeof jest.fn>).mockReturnValue({ status: null, error: denied });
615
+ const result = await runVerify(["backend-build"], tempDir);
616
+ expect(result.checks[0]).toMatchObject({ status: "error", evidence: { errorCode: "verification-command-unavailable" } });
617
+ });
618
+
619
+ it("uses the project's compiler and functions tsconfig for TypeScript backend verification", async () => {
620
+ createProjectFixture(tempDir);
621
+ fs.symlinkSync(path.join(repoRoot, "node_modules", "typescript"), path.join(tempDir, "node_modules", "typescript"), "junction");
622
+ const spawnMock = childProcess.spawnSync as unknown as ReturnType<typeof jest.fn>;
623
+ spawnMock.mockReturnValue({ status: 2, stdout: "src/probe-item.ts(9,3): error TS2322: Invalid type", stderr: "" });
624
+ const result = await runVerify(["backend-build"], tempDir);
625
+ expect(result.summary.done).toBe(false);
626
+ expect(result.checks[0].evidence.diagnostics).toContain("src/probe-item.ts(9,3): error TS2322: Invalid type");
627
+ expect(spawnMock).toHaveBeenCalledWith(process.execPath, [
628
+ expect.stringContaining(path.join("typescript", "bin", "tsc")), "--project", "tsconfig.json", "--noEmit",
629
+ ], expect.objectContaining({ cwd: path.join(tempDir, "functions"), shell: false }));
630
+ });
631
+
632
+ it("prefers the existing Python dev virtualenv and includes source syntax errors", async () => {
633
+ createProjectFixture(tempDir);
634
+ writeFile(path.join(tempDir, "swallowkit.config.js"), "module.exports = { backend: { language: 'python' } };");
635
+ const python = path.join(tempDir, "functions", ".venv", ...(process.platform === "win32" ? ["Scripts", "python.exe"] : ["bin", "python"]));
636
+ writeFile(python, "mock interpreter");
637
+ const spawnMock = childProcess.spawnSync as unknown as ReturnType<typeof jest.fn>;
638
+ spawnMock.mockImplementation((_command: string, args: string[]) => args.includes("compileall")
639
+ ? { status: 1, stdout: '*** Error compiling "./blueprints/probe_item.py"...\n File "./blueprints/probe_item.py", line 12\nSyntaxError: invalid syntax', stderr: "" }
640
+ : { status: 0, stdout: "", stderr: "" });
641
+ const result = await runVerify(["backend-build"], tempDir);
642
+ expect(result.checks[0].status).toBe("fail");
643
+ expect(result.checks[0].evidence.diagnostics).toContain(' File "./blueprints/probe_item.py", line 12');
644
+ expect(spawnMock).toHaveBeenCalledWith(python, [
645
+ "-m", "compileall", "-q", "-f", "-x", expect.stringContaining(".codegen-venv"), ".",
646
+ ], expect.objectContaining({ cwd: path.join(tempDir, "functions"), shell: false }));
647
+ });
648
+
649
+ it.each([
650
+ ["python", "python"],
651
+ ["unsupported", "typescript"],
652
+ ])("resolves the backend like generation when SWALLOWKIT_BACKEND_LANGUAGE=%s", async (override, expected) => {
653
+ createProjectFixture(tempDir); // config file says typescript
654
+ fs.symlinkSync(path.join(repoRoot, "node_modules", "typescript"), path.join(tempDir, "node_modules", "typescript"), "junction");
655
+ const spawnMock = childProcess.spawnSync as unknown as ReturnType<typeof jest.fn>;
656
+ spawnMock.mockReturnValue({ status: 0, stdout: "", stderr: "" });
657
+ const previous = process.env.SWALLOWKIT_BACKEND_LANGUAGE;
658
+ process.env.SWALLOWKIT_BACKEND_LANGUAGE = override;
659
+ try {
660
+ const result = await runVerify(["backend-build"], tempDir);
661
+ expect(result.checks[0].evidence.command).toContain(expected === "python" ? "compileall" : "tsc");
662
+ } finally {
663
+ if (previous === undefined) delete process.env.SWALLOWKIT_BACKEND_LANGUAGE;
664
+ else process.env.SWALLOWKIT_BACKEND_LANGUAGE = previous;
665
+ }
666
+ });
667
+
533
668
  it("reports verification command startup failures as blocked", async () => {
534
669
  createProjectFixture(tempDir);
535
670
  writeFile(path.join(tempDir, "tsconfig.json"), JSON.stringify({ compilerOptions: { noEmit: true } }));
@@ -300,6 +300,15 @@ describe("generateLoginPage", () => {
300
300
  expect(code).toContain("handleSubmit");
301
301
  expect(code).toContain("/api/auth/login");
302
302
  });
303
+
304
+ it("keeps the shared form legible with either global color scheme", () => {
305
+ const code = generateLoginPage();
306
+ expect(code).toContain("bg-gray-50 dark:bg-gray-900");
307
+ expect(code).toContain("bg-white dark:bg-gray-800");
308
+ expect(code).toContain("text-gray-900 dark:text-gray-100");
309
+ expect(code).toContain("dark:border-gray-600");
310
+ expect(code).toContain("dark:text-red-300");
311
+ });
303
312
  });
304
313
 
305
314
  describe("generateAuthContext", () => {