swallowkit 1.0.0-beta.6 → 1.0.0-beta.60

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 (296) hide show
  1. package/README.ja.md +86 -177
  2. package/README.md +86 -178
  3. package/dist/__tests__/fixtures.d.ts +12 -0
  4. package/dist/__tests__/fixtures.d.ts.map +1 -1
  5. package/dist/__tests__/fixtures.js +101 -0
  6. package/dist/__tests__/fixtures.js.map +1 -1
  7. package/dist/cli/commands/add-auth.d.ts +15 -0
  8. package/dist/cli/commands/add-auth.d.ts.map +1 -0
  9. package/dist/cli/commands/add-auth.js +790 -0
  10. package/dist/cli/commands/add-auth.js.map +1 -0
  11. package/dist/cli/commands/add-connector.d.ts +20 -0
  12. package/dist/cli/commands/add-connector.d.ts.map +1 -0
  13. package/dist/cli/commands/add-connector.js +163 -0
  14. package/dist/cli/commands/add-connector.js.map +1 -0
  15. package/dist/cli/commands/cosmos-worktree-isolation.d.ts +25 -0
  16. package/dist/cli/commands/cosmos-worktree-isolation.d.ts.map +1 -0
  17. package/dist/cli/commands/cosmos-worktree-isolation.js +124 -0
  18. package/dist/cli/commands/cosmos-worktree-isolation.js.map +1 -0
  19. package/dist/cli/commands/create-model.d.ts +1 -4
  20. package/dist/cli/commands/create-model.d.ts.map +1 -1
  21. package/dist/cli/commands/create-model.js +19 -81
  22. package/dist/cli/commands/create-model.js.map +1 -1
  23. package/dist/cli/commands/dev-seeds.d.ts +62 -0
  24. package/dist/cli/commands/dev-seeds.d.ts.map +1 -0
  25. package/dist/cli/commands/dev-seeds.js +474 -0
  26. package/dist/cli/commands/dev-seeds.js.map +1 -0
  27. package/dist/cli/commands/dev.d.ts +38 -0
  28. package/dist/cli/commands/dev.d.ts.map +1 -1
  29. package/dist/cli/commands/dev.js +733 -155
  30. package/dist/cli/commands/dev.js.map +1 -1
  31. package/dist/cli/commands/index.d.ts +1 -0
  32. package/dist/cli/commands/index.d.ts.map +1 -1
  33. package/dist/cli/commands/index.js +3 -1
  34. package/dist/cli/commands/index.js.map +1 -1
  35. package/dist/cli/commands/init.d.ts +78 -0
  36. package/dist/cli/commands/init.d.ts.map +1 -1
  37. package/dist/cli/commands/init.js +1453 -898
  38. package/dist/cli/commands/init.js.map +1 -1
  39. package/dist/cli/commands/scaffold.d.ts +24 -0
  40. package/dist/cli/commands/scaffold.d.ts.map +1 -1
  41. package/dist/cli/commands/scaffold.js +473 -90
  42. package/dist/cli/commands/scaffold.js.map +1 -1
  43. package/dist/cli/index.d.ts +5 -1
  44. package/dist/cli/index.d.ts.map +1 -1
  45. package/dist/cli/index.js +333 -55
  46. package/dist/cli/index.js.map +1 -1
  47. package/dist/core/config.d.ts +14 -3
  48. package/dist/core/config.d.ts.map +1 -1
  49. package/dist/core/config.js +179 -8
  50. package/dist/core/config.js.map +1 -1
  51. package/dist/core/mock/connector-mock-server.d.ts +101 -0
  52. package/dist/core/mock/connector-mock-server.d.ts.map +1 -0
  53. package/dist/core/mock/connector-mock-server.js +480 -0
  54. package/dist/core/mock/connector-mock-server.js.map +1 -0
  55. package/dist/core/mock/zod-mock-generator.d.ts +14 -0
  56. package/dist/core/mock/zod-mock-generator.d.ts.map +1 -0
  57. package/dist/core/mock/zod-mock-generator.js +163 -0
  58. package/dist/core/mock/zod-mock-generator.js.map +1 -0
  59. package/dist/core/operations/auth-operations.d.ts +61 -0
  60. package/dist/core/operations/auth-operations.d.ts.map +1 -0
  61. package/dist/core/operations/auth-operations.js +272 -0
  62. package/dist/core/operations/auth-operations.js.map +1 -0
  63. package/dist/core/operations/create-model.d.ts +15 -0
  64. package/dist/core/operations/create-model.d.ts.map +1 -0
  65. package/dist/core/operations/create-model.js +185 -0
  66. package/dist/core/operations/create-model.js.map +1 -0
  67. package/dist/core/operations/file-session.d.ts +56 -0
  68. package/dist/core/operations/file-session.d.ts.map +1 -0
  69. package/dist/core/operations/file-session.js +203 -0
  70. package/dist/core/operations/file-session.js.map +1 -0
  71. package/dist/core/operations/provision-operations.d.ts +52 -0
  72. package/dist/core/operations/provision-operations.d.ts.map +1 -0
  73. package/dist/core/operations/provision-operations.js +211 -0
  74. package/dist/core/operations/provision-operations.js.map +1 -0
  75. package/dist/core/operations/runtime.d.ts +37 -0
  76. package/dist/core/operations/runtime.d.ts.map +1 -0
  77. package/dist/core/operations/runtime.js +248 -0
  78. package/dist/core/operations/runtime.js.map +1 -0
  79. package/dist/core/operations/scaffold-apply.d.ts +21 -0
  80. package/dist/core/operations/scaffold-apply.d.ts.map +1 -0
  81. package/dist/core/operations/scaffold-apply.js +57 -0
  82. package/dist/core/operations/scaffold-apply.js.map +1 -0
  83. package/dist/core/operations/scaffold-machine.d.ts +16 -0
  84. package/dist/core/operations/scaffold-machine.d.ts.map +1 -0
  85. package/dist/core/operations/scaffold-machine.js +51 -0
  86. package/dist/core/operations/scaffold-machine.js.map +1 -0
  87. package/dist/core/operations/scaffold-plan.d.ts +43 -0
  88. package/dist/core/operations/scaffold-plan.d.ts.map +1 -0
  89. package/dist/core/operations/scaffold-plan.js +187 -0
  90. package/dist/core/operations/scaffold-plan.js.map +1 -0
  91. package/dist/core/project/agent-instructions.d.ts +29 -0
  92. package/dist/core/project/agent-instructions.d.ts.map +1 -0
  93. package/dist/core/project/agent-instructions.js +159 -0
  94. package/dist/core/project/agent-instructions.js.map +1 -0
  95. package/dist/core/project/artifacts.d.ts +58 -0
  96. package/dist/core/project/artifacts.d.ts.map +1 -0
  97. package/dist/core/project/artifacts.js +179 -0
  98. package/dist/core/project/artifacts.js.map +1 -0
  99. package/dist/core/project/boundaries.d.ts +50 -0
  100. package/dist/core/project/boundaries.d.ts.map +1 -0
  101. package/dist/core/project/boundaries.js +117 -0
  102. package/dist/core/project/boundaries.js.map +1 -0
  103. package/dist/core/project/capabilities.d.ts +58 -0
  104. package/dist/core/project/capabilities.d.ts.map +1 -0
  105. package/dist/core/project/capabilities.js +135 -0
  106. package/dist/core/project/capabilities.js.map +1 -0
  107. package/dist/core/project/drift.d.ts +29 -0
  108. package/dist/core/project/drift.d.ts.map +1 -0
  109. package/dist/core/project/drift.js +188 -0
  110. package/dist/core/project/drift.js.map +1 -0
  111. package/dist/core/project/infra.d.ts +49 -0
  112. package/dist/core/project/infra.d.ts.map +1 -0
  113. package/dist/core/project/infra.js +167 -0
  114. package/dist/core/project/infra.js.map +1 -0
  115. package/dist/core/project/manifest.d.ts +92 -0
  116. package/dist/core/project/manifest.d.ts.map +1 -0
  117. package/dist/core/project/manifest.js +321 -0
  118. package/dist/core/project/manifest.js.map +1 -0
  119. package/dist/core/project/state.d.ts +15 -0
  120. package/dist/core/project/state.d.ts.map +1 -0
  121. package/dist/core/project/state.js +106 -0
  122. package/dist/core/project/state.js.map +1 -0
  123. package/dist/core/project/validation.d.ts +20 -0
  124. package/dist/core/project/validation.d.ts.map +1 -0
  125. package/dist/core/project/validation.js +274 -0
  126. package/dist/core/project/validation.js.map +1 -0
  127. package/dist/core/project/workflows.d.ts +43 -0
  128. package/dist/core/project/workflows.d.ts.map +1 -0
  129. package/dist/core/project/workflows.js +299 -0
  130. package/dist/core/project/workflows.js.map +1 -0
  131. package/dist/core/scaffold/auth-generator.d.ts +63 -0
  132. package/dist/core/scaffold/auth-generator.d.ts.map +1 -0
  133. package/dist/core/scaffold/auth-generator.js +1774 -0
  134. package/dist/core/scaffold/auth-generator.js.map +1 -0
  135. package/dist/core/scaffold/connector-functions-generator.d.ts +41 -0
  136. package/dist/core/scaffold/connector-functions-generator.d.ts.map +1 -0
  137. package/dist/core/scaffold/connector-functions-generator.js +1029 -0
  138. package/dist/core/scaffold/connector-functions-generator.js.map +1 -0
  139. package/dist/core/scaffold/functions-generator.d.ts +7 -1
  140. package/dist/core/scaffold/functions-generator.d.ts.map +1 -1
  141. package/dist/core/scaffold/functions-generator.js +771 -69
  142. package/dist/core/scaffold/functions-generator.js.map +1 -1
  143. package/dist/core/scaffold/model-parser.d.ts +21 -1
  144. package/dist/core/scaffold/model-parser.d.ts.map +1 -1
  145. package/dist/core/scaffold/model-parser.js +243 -43
  146. package/dist/core/scaffold/model-parser.js.map +1 -1
  147. package/dist/core/scaffold/native-schema-generator.d.ts +19 -0
  148. package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -0
  149. package/dist/core/scaffold/native-schema-generator.js +753 -0
  150. package/dist/core/scaffold/native-schema-generator.js.map +1 -0
  151. package/dist/core/scaffold/nextjs-generator.d.ts +8 -0
  152. package/dist/core/scaffold/nextjs-generator.d.ts.map +1 -1
  153. package/dist/core/scaffold/nextjs-generator.js +153 -9
  154. package/dist/core/scaffold/nextjs-generator.js.map +1 -1
  155. package/dist/core/scaffold/openapi-generator.d.ts +3 -0
  156. package/dist/core/scaffold/openapi-generator.d.ts.map +1 -0
  157. package/dist/core/scaffold/openapi-generator.js +190 -0
  158. package/dist/core/scaffold/openapi-generator.js.map +1 -0
  159. package/dist/core/scaffold/ui-generator.d.ts +17 -4
  160. package/dist/core/scaffold/ui-generator.d.ts.map +1 -1
  161. package/dist/core/scaffold/ui-generator.js +174 -34
  162. package/dist/core/scaffold/ui-generator.js.map +1 -1
  163. package/dist/core/verify/index.d.ts +69 -0
  164. package/dist/core/verify/index.d.ts.map +1 -0
  165. package/dist/core/verify/index.js +412 -0
  166. package/dist/core/verify/index.js.map +1 -0
  167. package/dist/database/base-model.d.ts +3 -3
  168. package/dist/database/base-model.js +3 -3
  169. package/dist/index.d.ts +2 -2
  170. package/dist/index.d.ts.map +1 -1
  171. package/dist/index.js +2 -1
  172. package/dist/index.js.map +1 -1
  173. package/dist/machine/contracts.d.ts +24 -0
  174. package/dist/machine/contracts.d.ts.map +1 -0
  175. package/dist/machine/contracts.js +3 -0
  176. package/dist/machine/contracts.js.map +1 -0
  177. package/dist/machine/errors.d.ts +14 -0
  178. package/dist/machine/errors.d.ts.map +1 -0
  179. package/dist/machine/errors.js +42 -0
  180. package/dist/machine/errors.js.map +1 -0
  181. package/dist/machine/index.d.ts +3 -0
  182. package/dist/machine/index.d.ts.map +1 -0
  183. package/dist/machine/index.js +566 -0
  184. package/dist/machine/index.js.map +1 -0
  185. package/dist/mcp/index.d.ts +25 -0
  186. package/dist/mcp/index.d.ts.map +1 -0
  187. package/dist/mcp/index.js +425 -0
  188. package/dist/mcp/index.js.map +1 -0
  189. package/dist/types/index.d.ts +109 -0
  190. package/dist/types/index.d.ts.map +1 -1
  191. package/dist/utils/package-manager.d.ts +5 -2
  192. package/dist/utils/package-manager.d.ts.map +1 -1
  193. package/dist/utils/package-manager.js +30 -9
  194. package/dist/utils/package-manager.js.map +1 -1
  195. package/dist/utils/python-uv.d.ts +21 -0
  196. package/dist/utils/python-uv.d.ts.map +1 -0
  197. package/dist/utils/python-uv.js +111 -0
  198. package/dist/utils/python-uv.js.map +1 -0
  199. package/dist/version.d.ts +2 -0
  200. package/dist/version.d.ts.map +1 -0
  201. package/dist/version.js +49 -0
  202. package/dist/version.js.map +1 -0
  203. package/package.json +9 -5
  204. package/src/__tests__/__snapshots__/functions-generator.test.ts.snap +1139 -0
  205. package/src/__tests__/__snapshots__/nextjs-generator.test.ts.snap +206 -0
  206. package/src/__tests__/__snapshots__/ui-generator.test.ts.snap +532 -0
  207. package/src/__tests__/agent-instructions.test.ts +237 -0
  208. package/src/__tests__/agent-loop-phase34.test.ts +513 -0
  209. package/src/__tests__/auth.test.ts +770 -0
  210. package/src/__tests__/bff-swa-auth.test.ts +275 -0
  211. package/src/__tests__/config.test.ts +295 -0
  212. package/src/__tests__/connector-functions-generator.test.ts +288 -0
  213. package/src/__tests__/connector-mock-server.test.ts +439 -0
  214. package/src/__tests__/connector-model-bff.test.ts +162 -0
  215. package/src/__tests__/cosmos-worktree-isolation.test.ts +149 -0
  216. package/src/__tests__/dev-seeds.test.ts +236 -0
  217. package/src/__tests__/dev.test.ts +296 -0
  218. package/src/__tests__/file-session.test.ts +98 -0
  219. package/src/__tests__/fixtures.ts +184 -0
  220. package/src/__tests__/functions-generator.test.ts +237 -0
  221. package/src/__tests__/init.test.ts +411 -0
  222. package/src/__tests__/jest-globals.d.ts +8 -0
  223. package/src/__tests__/machine.test.ts +371 -0
  224. package/src/__tests__/mcp.test.ts +253 -0
  225. package/src/__tests__/model-parser.test.ts +81 -0
  226. package/src/__tests__/multi-auth.test.ts +160 -0
  227. package/src/__tests__/nextjs-generator.test.ts +105 -0
  228. package/src/__tests__/openapi-generator.test.ts +43 -0
  229. package/src/__tests__/package-manager.test.ts +227 -0
  230. package/src/__tests__/plan-apply.test.ts +499 -0
  231. package/src/__tests__/python-uv.test.ts +48 -0
  232. package/src/__tests__/scaffold.test.ts +302 -0
  233. package/src/__tests__/string-utils.test.ts +75 -0
  234. package/src/__tests__/tsconfig.json +15 -0
  235. package/src/__tests__/ui-generator.test.ts +248 -0
  236. package/src/__tests__/zod-mock-generator.test.ts +132 -0
  237. package/src/cli/commands/add-auth.ts +853 -0
  238. package/src/cli/commands/add-connector.ts +158 -0
  239. package/src/cli/commands/cosmos-worktree-isolation.ts +130 -0
  240. package/src/cli/commands/create-model.ts +62 -0
  241. package/src/cli/commands/dev-seeds.ts +629 -0
  242. package/src/cli/commands/dev.ts +1252 -0
  243. package/src/cli/commands/index.ts +9 -0
  244. package/src/cli/commands/init.ts +3541 -0
  245. package/src/cli/commands/provision.ts +193 -0
  246. package/src/cli/commands/scaffold.ts +1081 -0
  247. package/src/cli/index.ts +350 -0
  248. package/src/core/config.ts +380 -0
  249. package/src/core/mock/connector-mock-server.ts +555 -0
  250. package/src/core/mock/zod-mock-generator.ts +205 -0
  251. package/src/core/operations/auth-operations.ts +360 -0
  252. package/src/core/operations/create-model.ts +191 -0
  253. package/src/core/operations/file-session.ts +222 -0
  254. package/src/core/operations/provision-operations.ts +297 -0
  255. package/src/core/operations/runtime.ts +258 -0
  256. package/src/core/operations/scaffold-apply.ts +106 -0
  257. package/src/core/operations/scaffold-machine.ts +77 -0
  258. package/src/core/operations/scaffold-plan.ts +215 -0
  259. package/src/core/project/agent-instructions.ts +167 -0
  260. package/src/core/project/agent-templates/AGENTS.md +502 -0
  261. package/src/core/project/agent-templates/CLAUDE.md +31 -0
  262. package/src/core/project/agent-templates/azure-functions.instructions.md +22 -0
  263. package/src/core/project/agent-templates/bff-routes.instructions.md +18 -0
  264. package/src/core/project/agent-templates/copilot-instructions.md +27 -0
  265. package/src/core/project/agent-templates/shared-models.instructions.md +72 -0
  266. package/src/core/project/artifacts.ts +205 -0
  267. package/src/core/project/boundaries.ts +170 -0
  268. package/src/core/project/capabilities.ts +193 -0
  269. package/src/core/project/drift.ts +202 -0
  270. package/src/core/project/infra.ts +196 -0
  271. package/src/core/project/manifest.ts +402 -0
  272. package/src/core/project/state.ts +77 -0
  273. package/src/core/project/validation.ts +295 -0
  274. package/src/core/project/workflows.ts +295 -0
  275. package/src/core/scaffold/auth-generator.ts +1824 -0
  276. package/src/core/scaffold/connector-functions-generator.ts +1133 -0
  277. package/src/core/scaffold/functions-generator.ts +972 -0
  278. package/src/core/scaffold/model-parser.ts +854 -0
  279. package/src/core/scaffold/native-schema-generator.ts +904 -0
  280. package/src/core/scaffold/nextjs-generator.ts +382 -0
  281. package/src/core/scaffold/openapi-generator.ts +212 -0
  282. package/src/core/scaffold/ui-generator.ts +1100 -0
  283. package/src/core/verify/index.ts +468 -0
  284. package/src/database/base-model.ts +184 -0
  285. package/src/database/client.ts +140 -0
  286. package/src/database/repository.ts +104 -0
  287. package/src/database/runtime-check.ts +25 -0
  288. package/src/index.ts +27 -0
  289. package/src/machine/contracts.ts +33 -0
  290. package/src/machine/errors.ts +45 -0
  291. package/src/machine/index.ts +690 -0
  292. package/src/mcp/index.ts +423 -0
  293. package/src/types/index.ts +182 -0
  294. package/src/utils/package-manager.ts +246 -0
  295. package/src/utils/python-uv.ts +96 -0
  296. package/src/version.ts +13 -0
@@ -0,0 +1,690 @@
1
+ import { Command, CommanderError } from "commander";
2
+ import { isSwallowKitProject } from "../core/config";
3
+ import { createModelOperation } from "../core/operations/create-model";
4
+ import { applyScaffoldOperation } from "../core/operations/scaffold-apply";
5
+ import { applyAuthOperation, planAuthOperation } from "../core/operations/auth-operations";
6
+ import { applyProvisionOperation, planProvisionOperation } from "../core/operations/provision-operations";
7
+ import { ProcessExitInterceptError } from "../core/operations/runtime";
8
+ import { inspectInfra } from "../core/project/infra";
9
+ import { runMachineScaffoldOperation } from "../core/operations/scaffold-machine";
10
+ import { planScaffoldOperation } from "../core/operations/scaffold-plan";
11
+ import { inspectArtifacts } from "../core/project/artifacts";
12
+ import { inspectBoundaries } from "../core/project/boundaries";
13
+ import { inspectCapabilities } from "../core/project/capabilities";
14
+ import { detectDrift } from "../core/project/drift";
15
+ import { loadProjectManifest } from "../core/project/manifest";
16
+ import { loadLastVerifyState } from "../core/project/state";
17
+ import { validateProject } from "../core/project/validation";
18
+ import { explainVerifyFailure, compactVerifyResult, runVerify, VerifyResult } from "../core/verify";
19
+ import {
20
+ MachineErrorResponse,
21
+ MachineNextAction,
22
+ MachineOperationStatus,
23
+ MachineResponse,
24
+ MachineSuccessResponse,
25
+ } from "./contracts";
26
+ import { MachineCommandError, resolveMachineErrorStatus, toMachineError } from "./errors";
27
+
28
+ /** 最終 JSON envelope 専用の stdout writer。runMachineCli 実行中はこれ以外の stdout 書き込みを stderr へ逃がす。 */
29
+ let machineEnvelopeWrite: ((chunk: string) => void) | null = null;
30
+
31
+ function writeMachineResponse<TData>(response: MachineResponse<TData>): void {
32
+ const payload = `${JSON.stringify(response, null, 2)}\n`;
33
+ if (machineEnvelopeWrite) {
34
+ machineEnvelopeWrite(payload);
35
+ } else {
36
+ process.stdout.write(payload);
37
+ }
38
+ }
39
+
40
+ interface MachineResponseMeta {
41
+ status?: MachineOperationStatus;
42
+ nextActions?: MachineNextAction[];
43
+ }
44
+
45
+ function writeMachineSuccess<TData>(command: string, data: TData, meta?: MachineResponseMeta): void {
46
+ const response: MachineSuccessResponse<TData> = {
47
+ ok: true,
48
+ command,
49
+ status: meta?.status ?? "complete",
50
+ ...(meta?.nextActions && meta.nextActions.length > 0 ? { nextActions: meta.nextActions } : {}),
51
+ data,
52
+ };
53
+ writeMachineResponse(response);
54
+ }
55
+
56
+ function writeMachineError(command: string, error: unknown): void {
57
+ const response: MachineErrorResponse = {
58
+ ok: false,
59
+ command,
60
+ status: resolveMachineErrorStatus(error),
61
+ error: toMachineError(error),
62
+ };
63
+ writeMachineResponse(response);
64
+ }
65
+
66
+ /** 書き込み系操作の前提チェック。プロジェクト外なら明確な blocked エラーを返す。 */
67
+ function ensureProjectForMachine(): void {
68
+ if (!isSwallowKitProject()) {
69
+ throw new MachineCommandError(
70
+ "not-a-swallowkit-project",
71
+ `No SwallowKit project found in ${process.cwd()}. Expected swallowkit.config.js, swallowkit.config.json, or .swallowkitrc.json in the current directory. Run this command from the project root, or create a project with "swallowkit init".`,
72
+ { cwd: process.cwd() },
73
+ "blocked"
74
+ );
75
+ }
76
+ }
77
+
78
+ /**
79
+ * レガシーな人間向けコードパスが process.exit を直接呼んでも、
80
+ * JSON envelope なしにプロセスが死なないようにする安全網。
81
+ * console.error の出力を集めてエラーメッセージとして返す。
82
+ */
83
+ async function runActionWithExitGuard<TData>(action: () => Promise<TData>): Promise<TData> {
84
+ const capturedErrors: string[] = [];
85
+ const originalError = console.error;
86
+ console.error = (...args: unknown[]) => {
87
+ const text = args
88
+ .map((arg) => (typeof arg === "string" ? arg : arg instanceof Error ? arg.stack || arg.message : JSON.stringify(arg)))
89
+ .join(" ");
90
+ capturedErrors.push(text);
91
+ process.stderr.write(`${text}\n`);
92
+ };
93
+ const originalExit = process.exit;
94
+ process.exit = ((code?: number | string | null) => {
95
+ const normalized = typeof code === "number" ? code : typeof process.exitCode === "number" ? process.exitCode : 1;
96
+ throw new ProcessExitInterceptError(normalized);
97
+ }) as typeof process.exit;
98
+
99
+ try {
100
+ return await action();
101
+ } catch (error) {
102
+ if (error instanceof ProcessExitInterceptError) {
103
+ const message = capturedErrors
104
+ .map((entry) => entry.replace(/[❌💡]\s*/gu, "").trim())
105
+ .filter((entry) => entry.length > 0)
106
+ .join("\n");
107
+ throw new MachineCommandError(
108
+ "operation-aborted",
109
+ message || `Operation aborted with exit code ${error.exitCode}.`,
110
+ undefined,
111
+ "failed"
112
+ );
113
+ }
114
+ throw error;
115
+ } finally {
116
+ console.error = originalError;
117
+ process.exit = originalExit;
118
+ }
119
+ }
120
+
121
+ async function handleMachineAction<TData>(
122
+ command: string,
123
+ action: () => Promise<TData>,
124
+ meta?: (data: TData) => MachineResponseMeta
125
+ ): Promise<void> {
126
+ try {
127
+ const data = await runActionWithExitGuard(action);
128
+ writeMachineSuccess(command, data, meta?.(data));
129
+ } catch (error) {
130
+ writeMachineError(command, error);
131
+ process.exitCode = 1;
132
+ }
133
+ }
134
+
135
+ function createMachineProgram(): Command {
136
+ const program = new Command();
137
+ program
138
+ .name("swallowkit machine")
139
+ .description("SwallowKit machine-readable CLI for AI and MCP integrations")
140
+ .showHelpAfterError(false);
141
+
142
+ const inspect = new Command("inspect");
143
+ inspect
144
+ .command("project")
145
+ .description("Inspect SwallowKit project metadata")
146
+ .action(async () => {
147
+ await handleMachineAction("inspect-project", async () => {
148
+ const loaded = await loadProjectManifest();
149
+ return {
150
+ manifestSource: loaded.source,
151
+ diagnostics: loaded.diagnostics,
152
+ manifest: loaded.manifest,
153
+ };
154
+ });
155
+ });
156
+
157
+ inspect
158
+ .command("entities")
159
+ .description("Inspect SwallowKit entities")
160
+ .action(async () => {
161
+ await handleMachineAction("inspect-entities", async () => {
162
+ const loaded = await loadProjectManifest();
163
+ return {
164
+ manifestSource: loaded.source,
165
+ diagnostics: loaded.diagnostics,
166
+ entities: loaded.manifest.entities,
167
+ };
168
+ });
169
+ });
170
+
171
+ inspect
172
+ .command("routes")
173
+ .description("Inspect SwallowKit routes")
174
+ .action(async () => {
175
+ await handleMachineAction("inspect-routes", async () => {
176
+ const loaded = await loadProjectManifest();
177
+ return {
178
+ manifestSource: loaded.source,
179
+ diagnostics: loaded.diagnostics,
180
+ routes: loaded.manifest.routes,
181
+ };
182
+ });
183
+ });
184
+
185
+ inspect
186
+ .command("artifacts")
187
+ .description("Inspect generated artifacts and their ownership from the artifact ledger")
188
+ .action(async () => {
189
+ await handleMachineAction("inspect-artifacts", async () => inspectArtifacts());
190
+ });
191
+
192
+ inspect
193
+ .command("boundaries")
194
+ .description("Return the responsibility boundary contract between AI free-form authoring and deterministic generation")
195
+ .action(async () => {
196
+ await handleMachineAction("inspect-boundaries", async () => inspectBoundaries());
197
+ });
198
+
199
+ inspect
200
+ .command("capabilities")
201
+ .description("Return SwallowKit's machine-readable capability contract: model declarations, auth workflow, generated-CRUD scope, seeding, and available commands")
202
+ .action(async () => {
203
+ await handleMachineAction("inspect-capabilities", async () => inspectCapabilities());
204
+ });
205
+
206
+ inspect
207
+ .command("infra")
208
+ .description("Inspect Bicep infrastructure assets (params, modules, outputs, container wiring) without calling Azure")
209
+ .action(async () => {
210
+ await handleMachineAction("inspect-infra", async () => inspectInfra());
211
+ });
212
+
213
+ inspect
214
+ .command("drift")
215
+ .description("Detect drift between generated artifacts and the current project state")
216
+ .action(async () => {
217
+ await handleMachineAction(
218
+ "inspect-drift",
219
+ async () => detectDrift(),
220
+ (result) => ({
221
+ status: "complete",
222
+ nextActions:
223
+ result.findings.length > 0
224
+ ? [
225
+ {
226
+ command: "swallowkit machine verify project",
227
+ description: "Run full verification to confirm the project is in a consistent state.",
228
+ },
229
+ ]
230
+ : [],
231
+ })
232
+ );
233
+ });
234
+
235
+ const validate = new Command("validate");
236
+ validate
237
+ .command("project")
238
+ .description("Validate SwallowKit project metadata and conventions")
239
+ .action(async () => {
240
+ await handleMachineAction("validate-project", async () => validateProject());
241
+ });
242
+
243
+ const generate = new Command("generate");
244
+ generate
245
+ .command("model")
246
+ .description("Generate model templates with deterministic JSON output")
247
+ .argument("<names...>", "Model names to generate")
248
+ .option("--models-dir <dir>", "Models directory", "shared/models")
249
+ .option("--connector <name>", "Associate the models with a configured connector")
250
+ .option("--overwrite <mode>", "Overwrite policy: always | never", "never")
251
+ .action(async (names: string[], options: { modelsDir?: string; connector?: string; overwrite?: string }) => {
252
+ await handleMachineAction("generate-model", async () => {
253
+ ensureProjectForMachine();
254
+ if (options.overwrite !== "always" && options.overwrite !== "never") {
255
+ throw new MachineCommandError(
256
+ "invalid-overwrite-mode",
257
+ `Unsupported overwrite mode: ${options.overwrite}. Use "always" or "never".`
258
+ );
259
+ }
260
+
261
+ return createModelOperation({
262
+ names,
263
+ modelsDir: options.modelsDir,
264
+ connector: options.connector,
265
+ overwriteMode: options.overwrite,
266
+ });
267
+ });
268
+ });
269
+
270
+ generate
271
+ .command("scaffold")
272
+ .description("Generate scaffold artifacts with deterministic JSON output")
273
+ .argument("<model>", "Model file or model name")
274
+ .option("--functions-dir <dir>", "Functions directory", "functions")
275
+ .option("--api-dir <dir>", "API routes directory", "app/api")
276
+ .option("--api-only", "Skip UI components; still update Functions, BFF routes, OpenAPI, and native schema assets", false)
277
+ .action(async (model: string, options: { functionsDir?: string; apiDir?: string; apiOnly?: boolean }) => {
278
+ await handleMachineAction("generate-scaffold", async () => {
279
+ ensureProjectForMachine();
280
+ return runMachineScaffoldOperation({
281
+ model,
282
+ functionsDir: options.functionsDir,
283
+ apiDir: options.apiDir,
284
+ apiOnly: options.apiOnly,
285
+ });
286
+ });
287
+ });
288
+
289
+ const plan = new Command("plan");
290
+ plan
291
+ .command("scaffold")
292
+ .description("Compute the scaffold change plan without writing any files (accepts multiple models)")
293
+ .argument("<models...>", "Model files or model names")
294
+ .option("--functions-dir <dir>", "Functions directory", "functions")
295
+ .option("--api-dir <dir>", "API routes directory", "app/api")
296
+ .option("--api-only", "Skip UI components in the plan", false)
297
+ .action(async (models: string[], options: { functionsDir?: string; apiDir?: string; apiOnly?: boolean }) => {
298
+ await handleMachineAction(
299
+ "plan-scaffold",
300
+ async () => {
301
+ ensureProjectForMachine();
302
+ const plans = [];
303
+ for (const model of models) {
304
+ plans.push(
305
+ await planScaffoldOperation({
306
+ model,
307
+ functionsDir: options.functionsDir,
308
+ apiDir: options.apiDir,
309
+ apiOnly: options.apiOnly,
310
+ })
311
+ );
312
+ }
313
+ return plans.length === 1 ? plans[0] : { planType: "scaffold-batch" as const, plans };
314
+ },
315
+ (data) => {
316
+ const plans = "plans" in data ? data.plans : [data];
317
+ return {
318
+ status: plans.some((planData) => planData.requiresApproval) ? "requires-human" : "complete",
319
+ nextActions: plans.map((planData) => ({
320
+ command: `swallowkit machine apply scaffold --plan ${planData.planId}${planData.requiresApproval ? " --approve" : ""}`,
321
+ description: planData.requiresApproval
322
+ ? `Apply the plan for ${planData.model} after reviewing the listed conflicts (requires --approve).`
323
+ : `Apply the plan for ${planData.model}.`,
324
+ })),
325
+ };
326
+ }
327
+ );
328
+ });
329
+
330
+ plan
331
+ .command("auth")
332
+ .description("Compute the add-auth change plan without writing any files")
333
+ .requiredOption("--provider <provider>", "Auth provider: custom-jwt | swa | external-token | none")
334
+ .option("--scheme <name>", "Add as a named authentication scheme")
335
+ .option("--allowed-providers <list>", "Comma-separated SWA identity providers to allow (e.g. github,aad); defaults to aad")
336
+ .action(async (options: { provider: string; scheme?: string; allowedProviders?: string }) => {
337
+ await handleMachineAction(
338
+ "plan-auth",
339
+ async () => {
340
+ ensureProjectForMachine();
341
+ return planAuthOperation({
342
+ provider: options.provider,
343
+ scheme: options.scheme,
344
+ allowedProviders: options.allowedProviders ? options.allowedProviders.split(",") : undefined,
345
+ });
346
+ },
347
+ (planData) => ({
348
+ status: planData.requiresApproval ? "requires-human" : "complete",
349
+ nextActions: [
350
+ {
351
+ command: `swallowkit machine apply auth --plan ${planData.planId}${planData.requiresApproval ? " --approve" : ""}`,
352
+ description: planData.requiresApproval
353
+ ? "Apply the plan after reviewing the listed conflicts (requires --approve)."
354
+ : "Apply the plan.",
355
+ },
356
+ ],
357
+ })
358
+ );
359
+ });
360
+
361
+ plan
362
+ .command("provision")
363
+ .description("Preflight Azure provisioning locally; use --what-if for an az what-if analysis (requires az login)")
364
+ .requiredOption("-g, --resource-group <name>", "Resource group name")
365
+ .requiredOption("--location <region>", "Primary location for Functions and Cosmos DB")
366
+ .requiredOption("--swa-location <region>", "Static Web App location")
367
+ .option("--subscription <id>", "Azure subscription GUID")
368
+ .option("--what-if", "Run az deployment group what-if (requires az login)", false)
369
+ .action(async (options: { resourceGroup: string; location: string; swaLocation: string; subscription?: string; whatIf?: boolean }) => {
370
+ await handleMachineAction(
371
+ "plan-provision",
372
+ async () => {
373
+ ensureProjectForMachine();
374
+ return planProvisionOperation({
375
+ resourceGroup: options.resourceGroup,
376
+ location: options.location,
377
+ swaLocation: options.swaLocation,
378
+ subscription: options.subscription,
379
+ whatIf: options.whatIf,
380
+ });
381
+ },
382
+ (planData) => ({
383
+ status: "requires-human",
384
+ nextActions: [
385
+ {
386
+ command: `swallowkit machine apply provision --plan ${planData.planId} --approve`,
387
+ description:
388
+ "Provisioning creates billable Azure resources. A human must review plan.commands and approve before applying.",
389
+ },
390
+ ],
391
+ })
392
+ );
393
+ });
394
+
395
+ const apply = new Command("apply");
396
+ apply
397
+ .command("scaffold")
398
+ .description("Apply scaffold changes, verifying plan freshness and overwrite approval (accepts multiple models)")
399
+ .argument("[models...]", "Model files or model names (optional when --plan is given)")
400
+ .option("--plan <planId>", "Apply a previously computed plan")
401
+ .option("--approve", "Approve overwriting files that were modified after generation", false)
402
+ .option("--functions-dir <dir>", "Functions directory")
403
+ .option("--api-dir <dir>", "API routes directory")
404
+ .option("--api-only", "Skip UI components; still update Functions, BFF routes, OpenAPI, and native schema assets")
405
+ .action(
406
+ async (
407
+ models: string[],
408
+ options: { plan?: string; approve?: boolean; functionsDir?: string; apiDir?: string; apiOnly?: boolean }
409
+ ) => {
410
+ await handleMachineAction(
411
+ "apply-scaffold",
412
+ async () => {
413
+ ensureProjectForMachine();
414
+ if (models.length > 1 && options.plan) {
415
+ throw new MachineCommandError(
416
+ "invalid-arguments",
417
+ "--plan applies to a single model. Pass multiple models without --plan (each apply re-plans internally), or apply each plan separately.",
418
+ undefined,
419
+ "failed"
420
+ );
421
+ }
422
+ if (models.length <= 1) {
423
+ return applyScaffoldOperation({
424
+ model: models[0],
425
+ planId: options.plan,
426
+ approve: options.approve,
427
+ functionsDir: options.functionsDir,
428
+ apiDir: options.apiDir,
429
+ apiOnly: options.apiOnly,
430
+ });
431
+ }
432
+ const results = [];
433
+ for (const model of models) {
434
+ results.push(
435
+ await applyScaffoldOperation({
436
+ model,
437
+ approve: options.approve,
438
+ functionsDir: options.functionsDir,
439
+ apiDir: options.apiDir,
440
+ apiOnly: options.apiOnly,
441
+ })
442
+ );
443
+ }
444
+ return { applyType: "scaffold-batch" as const, results };
445
+ },
446
+ () => ({
447
+ status: "complete",
448
+ nextActions: [
449
+ {
450
+ command: "swallowkit machine verify project",
451
+ description: "Verify the project after applying scaffold changes.",
452
+ },
453
+ ],
454
+ })
455
+ );
456
+ }
457
+ );
458
+
459
+ apply
460
+ .command("auth")
461
+ .description("Apply add-auth changes, verifying plan freshness and overwrite approval")
462
+ .option("--plan <planId>", "Apply a previously computed plan")
463
+ .option("--provider <provider>", "Auth provider (optional when --plan is given)")
464
+ .option("--scheme <name>", "Add as a named authentication scheme")
465
+ .option("--allowed-providers <list>", "Comma-separated SWA identity providers to allow (e.g. github,aad); defaults to aad")
466
+ .option("--approve", "Approve overwriting files that were modified after generation", false)
467
+ .action(async (options: { plan?: string; provider?: string; scheme?: string; allowedProviders?: string; approve?: boolean }) => {
468
+ await handleMachineAction(
469
+ "apply-auth",
470
+ async () => {
471
+ ensureProjectForMachine();
472
+ return applyAuthOperation({
473
+ provider: options.provider,
474
+ scheme: options.scheme,
475
+ allowedProviders: options.allowedProviders ? options.allowedProviders.split(",") : undefined,
476
+ planId: options.plan,
477
+ approve: options.approve,
478
+ });
479
+ },
480
+ (result) => {
481
+ const configTarget = result.scheme
482
+ ? `auth.authorization.policies (reference scheme "${result.scheme}")`
483
+ : "auth.authorization.policies";
484
+ const nextActions: MachineNextAction[] = [];
485
+ if (result.provider !== "none") {
486
+ nextActions.push({
487
+ command: "edit swallowkit.config.js",
488
+ description: `Define ${configTarget}. The config is an extension point: hand-editing policies is the expected workflow. Do NOT hand-write auth.schemes entries; only plan/apply auth may add schemes.`,
489
+ });
490
+ }
491
+ if (result.provider === "swa") {
492
+ nextActions.push({
493
+ command: "edit swallowkit.config.js",
494
+ description: `Confirm swa.allowedProviders${result.scheme ? ` for scheme "${result.scheme}"` : ""} lists the identity providers you use (e.g. ['github']). Generated login URLs and provider checks use this list; it can also be set at plan time with --allowed-providers.`,
495
+ });
496
+ }
497
+ if (result.provider === "external-token") {
498
+ nextActions.push({
499
+ command: "edit the generated external token verifier",
500
+ description:
501
+ "Implement the verifier stub (it fails closed until implemented). typecheck and verify pass with the stub in place, so a passing verify does NOT mean external-token auth works.",
502
+ });
503
+ }
504
+ nextActions.push({
505
+ command: "swallowkit machine verify project",
506
+ description: "Verify the project after applying auth changes.",
507
+ });
508
+ return { status: "complete", nextActions };
509
+ }
510
+ );
511
+ });
512
+
513
+ apply
514
+ .command("provision")
515
+ .description("Apply an approved provisioning plan (always requires --approve; creates billable Azure resources)")
516
+ .requiredOption("--plan <planId>", "Provision plan to apply")
517
+ .option("--approve", "Confirm human approval of the provisioning plan", false)
518
+ .action(async (options: { plan: string; approve?: boolean }) => {
519
+ await handleMachineAction(
520
+ "apply-provision",
521
+ async () => {
522
+ ensureProjectForMachine();
523
+ return applyProvisionOperation({ planId: options.plan, approve: options.approve });
524
+ },
525
+ () => ({
526
+ status: "complete",
527
+ nextActions: [
528
+ {
529
+ command: "swallowkit machine inspect infra",
530
+ description: "Re-inspect infra assets after provisioning.",
531
+ },
532
+ ],
533
+ })
534
+ );
535
+ });
536
+
537
+ const verify = new Command("verify");
538
+ verify
539
+ .command("project")
540
+ .description("Run verification checks (structure, drift, typecheck) with machine-readable evidence")
541
+ .option(
542
+ "--checks <ids>",
543
+ "Comma-separated check ids to run (structure,drift,typecheck,build,lint,test, plus custom ids from swallowkit.config verify.checks)"
544
+ )
545
+ .option("--compact", "Suppress info-severity findings/violations from evidence to keep agent-facing output small", false)
546
+ .action(async (options: { checks?: string; compact?: boolean }) => {
547
+ await handleMachineAction(
548
+ "verify-project",
549
+ async () => {
550
+ const result = await runVerify(options.checks ? options.checks.split(",") : undefined);
551
+ return options.compact ? compactVerifyResult(result) : result;
552
+ },
553
+ (result) => ({
554
+ status: result.summary.done ? "complete" : "in-progress",
555
+ nextActions: result.summary.done
556
+ ? []
557
+ : [
558
+ {
559
+ command: "swallowkit machine explain failure",
560
+ description: "Get evidence and suggested actions for the failed checks.",
561
+ },
562
+ ],
563
+ })
564
+ );
565
+ });
566
+
567
+ const explain = new Command("explain");
568
+ explain
569
+ .command("failure")
570
+ .description("Explain failures from the most recent verify run")
571
+ .option("--check <id>", "Explain a specific check id")
572
+ .action(async (options: { check?: string }) => {
573
+ await handleMachineAction("explain-failure", async () => {
574
+ const lastVerify = loadLastVerifyState<VerifyResult>();
575
+ if (!lastVerify) {
576
+ throw new MachineCommandError(
577
+ "no-verification-result",
578
+ 'No verify result found. Run "swallowkit machine verify project" first.',
579
+ undefined,
580
+ "blocked"
581
+ );
582
+ }
583
+
584
+ try {
585
+ return {
586
+ verifiedAt: lastVerify.verifiedAt,
587
+ failures: explainVerifyFailure(lastVerify, options.check),
588
+ };
589
+ } catch (error) {
590
+ throw new MachineCommandError(
591
+ "unknown-check",
592
+ error instanceof Error ? error.message : String(error),
593
+ { check: options.check },
594
+ "failed"
595
+ );
596
+ }
597
+ });
598
+ });
599
+
600
+ program.addCommand(inspect);
601
+ program.addCommand(validate);
602
+ program.addCommand(generate);
603
+ program.addCommand(plan);
604
+ program.addCommand(apply);
605
+ program.addCommand(verify);
606
+ program.addCommand(explain);
607
+ return program;
608
+ }
609
+
610
+ export function isMachineCommand(argv: string[]): boolean {
611
+ return argv[2] === "machine";
612
+ }
613
+
614
+ /**
615
+ * すべてのコマンド階層に exitOverride と出力キャプチャを適用する。
616
+ * commander の addCommand は親の出力設定を継承しないため、再帰的に設定する。
617
+ */
618
+ function applyMachineOutputSettings(command: Command, sink: { text: string }): void {
619
+ command.exitOverride();
620
+ command.showHelpAfterError(false);
621
+ command.configureOutput({
622
+ writeErr: (str) => {
623
+ sink.text += str;
624
+ },
625
+ writeOut: (str) => {
626
+ sink.text += str;
627
+ },
628
+ });
629
+ for (const sub of command.commands) {
630
+ applyMachineOutputSettings(sub, sink);
631
+ }
632
+ }
633
+
634
+ export async function runMachineCli(argv: string[] = process.argv): Promise<void> {
635
+ const program = createMachineProgram();
636
+ const commanderOutput = { text: "" };
637
+ applyMachineOutputSettings(program, commanderOutput);
638
+
639
+ // Keep stdout reserved for the single JSON envelope: any console output
640
+ // produced by underlying operations (e.g. config-load logs) goes to stderr.
641
+ // A hard guard on process.stdout.write catches direct writes that bypass console.
642
+ const originalLog = console.log;
643
+ const originalInfo = console.info;
644
+ const originalWarn = console.warn;
645
+ const previousStdoutWrite = process.stdout.write;
646
+ const boundStdoutWrite = previousStdoutWrite.bind(process.stdout);
647
+ machineEnvelopeWrite = (chunk) => {
648
+ boundStdoutWrite(chunk);
649
+ };
650
+ process.stdout.write = ((chunk: string | Uint8Array, ...rest: unknown[]) =>
651
+ (process.stderr.write as (...args: unknown[]) => boolean)(chunk, ...rest)) as typeof process.stdout.write;
652
+ const toStderr = (...args: unknown[]) => {
653
+ process.stderr.write(`${args.map((arg) => (typeof arg === "string" ? arg : JSON.stringify(arg))).join(" ")}\n`);
654
+ };
655
+ console.log = toStderr;
656
+ console.info = toStderr;
657
+ console.warn = toStderr;
658
+
659
+ try {
660
+ await program.parseAsync(argv.slice(3), { from: "user" });
661
+ } catch (error) {
662
+ if (error instanceof CommanderError) {
663
+ const capturedText = commanderOutput.text.trim();
664
+ if (error.code === "commander.helpDisplayed" || error.code === "commander.help") {
665
+ // --help / help は正常応答として usage を JSON envelope で返す
666
+ writeMachineSuccess("machine-help", { help: capturedText });
667
+ return;
668
+ }
669
+ writeMachineError(
670
+ "machine-parse",
671
+ new MachineCommandError(
672
+ "invalid-command",
673
+ error.message,
674
+ capturedText ? { usage: capturedText } : undefined
675
+ )
676
+ );
677
+ process.exitCode = Number.isFinite(error.exitCode) && error.exitCode !== 0 ? error.exitCode : 1;
678
+ return;
679
+ }
680
+
681
+ writeMachineError("machine-parse", error);
682
+ process.exitCode = 1;
683
+ } finally {
684
+ console.log = originalLog;
685
+ console.info = originalInfo;
686
+ console.warn = originalWarn;
687
+ process.stdout.write = previousStdoutWrite;
688
+ machineEnvelopeWrite = null;
689
+ }
690
+ }