swallowkit 2.0.0-alpha.3 → 2.0.0-alpha.4
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.
- package/dist/cli/commands/configure-mcp.d.ts +12 -0
- package/dist/cli/commands/configure-mcp.d.ts.map +1 -0
- package/dist/cli/commands/configure-mcp.js +42 -0
- package/dist/cli/commands/configure-mcp.js.map +1 -0
- package/dist/cli/commands/init.d.ts +4 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +16 -4
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/migrations.d.ts.map +1 -1
- package/dist/cli/commands/migrations.js +25 -0
- package/dist/cli/commands/migrations.js.map +1 -1
- package/dist/cli/commands/provision.d.ts +2 -0
- package/dist/cli/commands/provision.d.ts.map +1 -1
- package/dist/cli/commands/provision.js +45 -16
- package/dist/cli/commands/provision.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts +4 -0
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +40 -2
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +27 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/core/operations/model-migration.d.ts +22 -0
- package/dist/core/operations/model-migration.d.ts.map +1 -0
- package/dist/core/operations/model-migration.js +36 -0
- package/dist/core/operations/model-migration.js.map +1 -0
- package/dist/core/operations/provision-operations.d.ts +19 -1
- package/dist/core/operations/provision-operations.d.ts.map +1 -1
- package/dist/core/operations/provision-operations.js +62 -35
- package/dist/core/operations/provision-operations.js.map +1 -1
- package/dist/core/operations/scaffold-apply.d.ts +1 -0
- package/dist/core/operations/scaffold-apply.d.ts.map +1 -1
- package/dist/core/operations/scaffold-apply.js +1 -0
- package/dist/core/operations/scaffold-apply.js.map +1 -1
- package/dist/core/operations/scaffold-machine.d.ts +1 -0
- package/dist/core/operations/scaffold-machine.d.ts.map +1 -1
- package/dist/core/operations/scaffold-machine.js.map +1 -1
- package/dist/core/operations/scaffold-plan.d.ts +2 -0
- package/dist/core/operations/scaffold-plan.d.ts.map +1 -1
- package/dist/core/operations/scaffold-plan.js +3 -1
- package/dist/core/operations/scaffold-plan.js.map +1 -1
- package/dist/core/project/boundaries.d.ts +5 -2
- package/dist/core/project/boundaries.d.ts.map +1 -1
- package/dist/core/project/boundaries.js +49 -18
- package/dist/core/project/boundaries.js.map +1 -1
- package/dist/core/project/capabilities.d.ts +1 -0
- package/dist/core/project/capabilities.d.ts.map +1 -1
- package/dist/core/project/capabilities.js +20 -2
- package/dist/core/project/capabilities.js.map +1 -1
- package/dist/core/project/mcp-config.d.ts +26 -0
- package/dist/core/project/mcp-config.d.ts.map +1 -0
- package/dist/core/project/mcp-config.js +117 -0
- package/dist/core/project/mcp-config.js.map +1 -0
- package/dist/core/project/workflows.d.ts.map +1 -1
- package/dist/core/project/workflows.js +9 -1
- package/dist/core/project/workflows.js.map +1 -1
- package/dist/core/scaffold/model-parser.d.ts +23 -0
- package/dist/core/scaffold/model-parser.d.ts.map +1 -1
- package/dist/core/scaffold/model-parser.js +407 -137
- package/dist/core/scaffold/model-parser.js.map +1 -1
- package/dist/core/scaffold/native-schema-generator.d.ts +11 -0
- package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -1
- package/dist/core/scaffold/native-schema-generator.js +273 -75
- package/dist/core/scaffold/native-schema-generator.js.map +1 -1
- package/dist/core/scaffold/openapi-generator.d.ts +5 -0
- package/dist/core/scaffold/openapi-generator.d.ts.map +1 -1
- package/dist/core/scaffold/openapi-generator.js +53 -3
- package/dist/core/scaffold/openapi-generator.js.map +1 -1
- package/dist/core/scaffold/ui-generator.d.ts +0 -3
- package/dist/core/scaffold/ui-generator.d.ts.map +1 -1
- package/dist/core/scaffold/ui-generator.js +68 -7
- package/dist/core/scaffold/ui-generator.js.map +1 -1
- package/dist/core/verify/index.d.ts.map +1 -1
- package/dist/core/verify/index.js +24 -1
- package/dist/core/verify/index.js.map +1 -1
- package/dist/machine/index.d.ts.map +1 -1
- package/dist/machine/index.js +39 -15
- package/dist/machine/index.js.map +1 -1
- package/dist/mcp/index.d.ts +3 -2
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +137 -13
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/version-selection.d.ts +55 -0
- package/dist/mcp/version-selection.d.ts.map +1 -0
- package/dist/mcp/version-selection.js +152 -0
- package/dist/mcp/version-selection.js.map +1 -0
- package/dist/utils/azure-cli.d.ts +44 -0
- package/dist/utils/azure-cli.d.ts.map +1 -0
- package/dist/utils/azure-cli.js +164 -0
- package/dist/utils/azure-cli.js.map +1 -0
- package/dist/utils/semver.d.ts +14 -0
- package/dist/utils/semver.d.ts.map +1 -0
- package/dist/utils/semver.js +61 -0
- package/dist/utils/semver.js.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/__snapshots__/boundaries.test.ts.snap +154 -0
- package/src/__tests__/agent-loop-phase34.test.ts +29 -0
- package/src/__tests__/azure-cli.test.ts +199 -0
- package/src/__tests__/boundaries.test.ts +42 -0
- package/src/__tests__/csharp-schema.test.ts +261 -0
- package/src/__tests__/mcp-version.test.ts +262 -0
- package/src/__tests__/mcp.test.ts +49 -3
- package/src/__tests__/plan-apply.test.ts +59 -0
- package/src/__tests__/schema-analysis.test.ts +329 -0
- package/src/cli/commands/configure-mcp.ts +54 -0
- package/src/cli/commands/init.ts +19 -4
- package/src/cli/commands/migrations.ts +22 -0
- package/src/cli/commands/provision.ts +53 -17
- package/src/cli/commands/scaffold.ts +40 -2
- package/src/cli/index.ts +26 -3
- package/src/core/operations/model-migration.ts +52 -0
- package/src/core/operations/provision-operations.ts +86 -42
- package/src/core/operations/scaffold-apply.ts +2 -0
- package/src/core/operations/scaffold-machine.ts +1 -0
- package/src/core/operations/scaffold-plan.ts +5 -1
- package/src/core/project/agent-templates/AGENTS.md +39 -3
- package/src/core/project/agent-templates/CLAUDE.md +3 -1
- package/src/core/project/agent-templates/copilot-instructions.md +3 -1
- package/src/core/project/boundaries.ts +60 -18
- package/src/core/project/capabilities.ts +22 -2
- package/src/core/project/mcp-config.ts +84 -0
- package/src/core/project/workflows.ts +9 -1
- package/src/core/scaffold/model-parser.ts +413 -129
- package/src/core/scaffold/native-schema-generator.ts +297 -78
- package/src/core/scaffold/openapi-generator.ts +63 -5
- package/src/core/scaffold/ui-generator.ts +70 -8
- package/src/core/verify/index.ts +23 -1
- package/src/machine/index.ts +42 -18
- package/src/mcp/index.ts +156 -14
- package/src/mcp/version-selection.ts +163 -0
- package/src/utils/azure-cli.ts +154 -0
- package/src/utils/semver.ts +59 -0
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
* - shared: マーカー区切りの共有領域。マーカー内は SwallowKit、外は自由。
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import { getBackendLanguage } from "../config";
|
|
13
14
|
import { ArtifactOwnership } from "../operations/file-session";
|
|
15
|
+
import { BackendLanguage } from "../../types";
|
|
14
16
|
import { inspectArtifacts } from "./artifacts";
|
|
15
17
|
|
|
16
18
|
export type BoundaryZone = "deterministic" | "ai-authored" | "shared";
|
|
@@ -48,6 +50,8 @@ export interface BoundaryArtifact {
|
|
|
48
50
|
export interface BoundariesContract {
|
|
49
51
|
contractVersion: number;
|
|
50
52
|
summary: string;
|
|
53
|
+
/** backend.language the convention rules were resolved for */
|
|
54
|
+
backendLanguage: BackendLanguage;
|
|
51
55
|
ownershipPolicies: OwnershipPolicy[];
|
|
52
56
|
/** 台帳に載っていないパスの既定規約 */
|
|
53
57
|
conventionRules: BoundaryPathRule[];
|
|
@@ -92,7 +96,7 @@ export const OWNERSHIP_POLICIES: OwnershipPolicy[] = [
|
|
|
92
96
|
},
|
|
93
97
|
];
|
|
94
98
|
|
|
95
|
-
|
|
99
|
+
const COMMON_LEADING_RULES: BoundaryPathRule[] = [
|
|
96
100
|
{
|
|
97
101
|
pattern: ".swallowkit/",
|
|
98
102
|
zone: "deterministic",
|
|
@@ -107,16 +111,25 @@ export const CONVENTION_RULES: BoundaryPathRule[] = [
|
|
|
107
111
|
"Zod model schemas — the single source of truth. AI/developers author these freely; deterministic generation derives everything else from them.",
|
|
108
112
|
},
|
|
109
113
|
{
|
|
110
|
-
pattern: "infra/
|
|
111
|
-
zone: "
|
|
112
|
-
editPolicy: "
|
|
113
|
-
description:
|
|
114
|
+
pattern: "infra/migrations/manifest.json",
|
|
115
|
+
zone: "deterministic",
|
|
116
|
+
editPolicy: "never-hand-edit",
|
|
117
|
+
description:
|
|
118
|
+
"Infrastructure migration ledger with baseline checksums. Updated by scaffold, `swallowkit migrations add-model`, and `swallowkit create-migration`.",
|
|
114
119
|
},
|
|
115
120
|
{
|
|
116
|
-
pattern: "
|
|
117
|
-
zone: "
|
|
118
|
-
editPolicy: "edit
|
|
119
|
-
description:
|
|
121
|
+
pattern: "infra/migrations/",
|
|
122
|
+
zone: "ai-authored",
|
|
123
|
+
editPolicy: "free-edit",
|
|
124
|
+
description:
|
|
125
|
+
"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>`.",
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
pattern: "infra/",
|
|
129
|
+
zone: "deterministic",
|
|
130
|
+
editPolicy: "never-hand-edit",
|
|
131
|
+
description:
|
|
132
|
+
"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.",
|
|
120
133
|
},
|
|
121
134
|
{
|
|
122
135
|
pattern: "shared/index.ts",
|
|
@@ -124,15 +137,40 @@ export const CONVENTION_RULES: BoundaryPathRule[] = [
|
|
|
124
137
|
editPolicy: "edit-outside-markers",
|
|
125
138
|
description: "SwallowKit appends model exports; other exports may be added freely.",
|
|
126
139
|
},
|
|
127
|
-
{
|
|
128
|
-
pattern: "*",
|
|
129
|
-
zone: "ai-authored",
|
|
130
|
-
editPolicy: "free-edit",
|
|
131
|
-
description:
|
|
132
|
-
"Any path not recorded in the artifact ledger is AI/developer-authored. Business logic, custom pages, custom Functions, and tests belong here.",
|
|
133
|
-
},
|
|
134
140
|
];
|
|
135
141
|
|
|
142
|
+
/** Backend files that SwallowKit appends to; C# and TypeScript discover functions without a registration file. */
|
|
143
|
+
const BACKEND_EXTENSION_RULES: Record<BackendLanguage, BoundaryPathRule[]> = {
|
|
144
|
+
python: [
|
|
145
|
+
{
|
|
146
|
+
pattern: "functions/function_app.py",
|
|
147
|
+
zone: "shared",
|
|
148
|
+
editPolicy: "edit-outside-markers",
|
|
149
|
+
description: "SwallowKit appends blueprint registrations under its marker; other edits are free.",
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
pattern: "functions/requirements.txt",
|
|
153
|
+
zone: "shared",
|
|
154
|
+
editPolicy: "free-edit",
|
|
155
|
+
description: "SwallowKit appends connector driver requirements when connector models are scaffolded; keep those lines and edit other requirements freely.",
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
csharp: [],
|
|
159
|
+
typescript: [],
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const FALLBACK_RULE: BoundaryPathRule = {
|
|
163
|
+
pattern: "*",
|
|
164
|
+
zone: "ai-authored",
|
|
165
|
+
editPolicy: "free-edit",
|
|
166
|
+
description:
|
|
167
|
+
"Any path not recorded in the artifact ledger is AI/developer-authored. Business logic, custom pages, custom Functions, and tests belong here.",
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
export function buildConventionRules(backendLanguage: BackendLanguage): BoundaryPathRule[] {
|
|
171
|
+
return [...COMMON_LEADING_RULES, ...BACKEND_EXTENSION_RULES[backendLanguage], FALLBACK_RULE];
|
|
172
|
+
}
|
|
173
|
+
|
|
136
174
|
const POLICY_BY_OWNERSHIP = new Map(OWNERSHIP_POLICIES.map((policy) => [policy.ownership, policy] as const));
|
|
137
175
|
|
|
138
176
|
export const BOUNDARY_GUIDANCE: string[] = [
|
|
@@ -143,7 +181,10 @@ export const BOUNDARY_GUIDANCE: string[] = [
|
|
|
143
181
|
"Run `swallowkit machine verify project` after applying changes; use `explain failure` to get repair evidence.",
|
|
144
182
|
];
|
|
145
183
|
|
|
146
|
-
export function inspectBoundaries(
|
|
184
|
+
export function inspectBoundaries(
|
|
185
|
+
projectRoot: string = process.cwd(),
|
|
186
|
+
backendLanguage: BackendLanguage = getBackendLanguage()
|
|
187
|
+
): BoundariesContract {
|
|
147
188
|
const inspected = inspectArtifacts(projectRoot);
|
|
148
189
|
|
|
149
190
|
const artifacts: BoundaryArtifact[] = inspected.artifacts.map((artifact) => {
|
|
@@ -162,8 +203,9 @@ export function inspectBoundaries(projectRoot: string = process.cwd()): Boundari
|
|
|
162
203
|
contractVersion: 1,
|
|
163
204
|
summary:
|
|
164
205
|
"SwallowKit separates deterministic generation (models → CRUD/BFF/UI/infra via plan/apply) from AI free-form authoring (models, business logic, custom code). This contract tells agents which files belong to which side.",
|
|
206
|
+
backendLanguage,
|
|
165
207
|
ownershipPolicies: OWNERSHIP_POLICIES,
|
|
166
|
-
conventionRules:
|
|
208
|
+
conventionRules: buildConventionRules(backendLanguage),
|
|
167
209
|
artifacts,
|
|
168
210
|
guidance: BOUNDARY_GUIDANCE,
|
|
169
211
|
};
|
|
@@ -53,6 +53,7 @@ export interface CapabilitiesContract {
|
|
|
53
53
|
sharedModels: {
|
|
54
54
|
idFieldRule: string;
|
|
55
55
|
buildNote: string;
|
|
56
|
+
nativeSchemaMapping: string[];
|
|
56
57
|
};
|
|
57
58
|
seeding: {
|
|
58
59
|
createTemplates: string;
|
|
@@ -109,6 +110,14 @@ export function inspectCapabilities(): CapabilitiesContract {
|
|
|
109
110
|
format:
|
|
110
111
|
"export const authPolicy = { roles: ['admin'] };\n// or per-operation:\nexport const authPolicy = { read: ['user'], write: ['admin'] };\n// or a named policy:\nexport const authPolicy = { policy: 'adminOnly' };",
|
|
111
112
|
},
|
|
113
|
+
{
|
|
114
|
+
name: "references",
|
|
115
|
+
required: false,
|
|
116
|
+
description:
|
|
117
|
+
"Explicit foreign keys used by generated UI lookups. Without it, a string field named <model>Id is treated as a foreign key only when shared/models contains that model (userId -> User only if user.ts exists). Map a field to false to disable inference. References to missing models are ignored with a reference-model-not-found warning.",
|
|
118
|
+
format: "export const references = { authorId: 'User', legacyId: false };",
|
|
119
|
+
notes: ["Only single string id fields can reference a model.", "No lookup UI, fetch, or OpenAPI reference is generated for fields that do not reference an existing model."],
|
|
120
|
+
},
|
|
112
121
|
{
|
|
113
122
|
name: "connectorConfig",
|
|
114
123
|
required: false,
|
|
@@ -174,6 +183,7 @@ export function inspectCapabilities(): CapabilitiesContract {
|
|
|
174
183
|
],
|
|
175
184
|
notGuaranteed: [
|
|
176
185
|
"Owner scoping: generated CRUD does NOT restrict rows to the authenticated principal. Any authenticated caller passing the policy can read/write any document.",
|
|
186
|
+
"Without authentication, plan scaffold reports user-scoped-data-without-auth when the partition key or an owner-like field (userId, learnerId, ownerId, ...) suggests per-user data.",
|
|
177
187
|
],
|
|
178
188
|
guidance: [
|
|
179
189
|
"For multi-user data where users may only touch their own records, add ai-authored custom endpoints that derive the scoping key (e.g. learnerId) from the verified principal — never from the request body — and restrict generated write endpoints to admin roles or disable them.",
|
|
@@ -181,6 +191,14 @@ export function inspectCapabilities(): CapabilitiesContract {
|
|
|
181
191
|
},
|
|
182
192
|
sharedModels: {
|
|
183
193
|
idFieldRule: "id: z.string() (required). createdAt/updatedAt: z.string().optional(). The backend owns their values.",
|
|
194
|
+
nativeSchemaMapping: [
|
|
195
|
+
"C#/Python schema assets derive types from Zod, never from field names: z.string() -> string/str (including createdAt/updatedAt).",
|
|
196
|
+
"Date types only when declared: z.iso.datetime() -> DateTime/datetime, z.iso.date() -> DateOnly/date, z.date() -> DateTime/datetime.",
|
|
197
|
+
"z.number() -> double / float|int; z.number().int() or z.int() -> long / int.",
|
|
198
|
+
"Inline z.object({...}) fields become named classes <Model><Field> (e.g. ProbeItemNested); imported model schemas keep their own class.",
|
|
199
|
+
"min/max/length/nonempty/regex/int/gt/gte/lt/lte/positive/nonnegative and array min/max are enforced by C# IValidatableObject.Validate and Python Field bounds/validators. Regexes use search semantics like Zod.",
|
|
200
|
+
"String formats (email/uuid/url), refinements, and transforms are not re-implemented natively; the BFF Zod schemas remain the authoritative validation.",
|
|
201
|
+
],
|
|
184
202
|
buildNote:
|
|
185
203
|
"verify typecheck resolves the shared package from its built dist/. SwallowKit rebuilds shared automatically during verify; when typechecking manually, build the shared package first (errors like \"no exported member\" usually mean a stale dist).",
|
|
186
204
|
},
|
|
@@ -193,10 +211,12 @@ export function inspectCapabilities(): CapabilitiesContract {
|
|
|
193
211
|
"inspect project | entities | routes | artifacts | boundaries | infra | drift | capabilities",
|
|
194
212
|
"validate project",
|
|
195
213
|
"generate model <names...> | generate scaffold <model>",
|
|
196
|
-
"plan scaffold <models...> | plan auth --provider <p> [--scheme <name>] [--allowed-providers <csv>] | plan provision -g <rg> --location <loc> --swa-location <loc> [--adopt-existing --baseline 0 --what-if] [--reconcile --what-if]",
|
|
197
|
-
"apply scaffold [models...] --plan <id> [--approve] | apply auth --plan <id> [--approve] | apply provision --plan <id> --approve",
|
|
214
|
+
"plan scaffold <models...> [--api-only] [--skip-infra] | plan auth --provider <p> [--scheme <name>] [--allowed-providers <csv>] | plan provision -g <rg> --location <loc> --swa-location <loc> [--adopt-existing --baseline 0 --what-if] [--reconcile --what-if]",
|
|
215
|
+
"apply scaffold [models...] --plan <id> [--approve] [--skip-infra] | apply auth --plan <id> [--approve] | apply provision --plan <id> --approve",
|
|
198
216
|
"verify project [--checks <ids>] [--compact]",
|
|
199
217
|
"explain failure [--check <id>]",
|
|
218
|
+
"(regular CLI) migrations add-model <model> [--dry-run] — generate only the container migration after scaffold --skip-infra",
|
|
219
|
+
"(regular CLI) configure-mcp [--version auto|latest|<semver>] — re-select the MCP launcher version in .mcp.json",
|
|
200
220
|
],
|
|
201
221
|
verifyChecks: {
|
|
202
222
|
defaultChecks: ["structure", "drift", "typecheck"],
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project-scoped MCP launcher version management.
|
|
3
|
+
*
|
|
4
|
+
* `.mcp.json` intentionally resolves `swallowkit@latest` so that projects created
|
|
5
|
+
* with older releases still get the current stable tools. A prerelease project can
|
|
6
|
+
* be newer than `latest`; in that case the launcher is pinned to the project
|
|
7
|
+
* version until a stable release that understands project-aware delegation catches up.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { spawnSync } from "child_process";
|
|
11
|
+
import * as fs from "fs";
|
|
12
|
+
import * as os from "os";
|
|
13
|
+
import * as path from "path";
|
|
14
|
+
import { compareSemver, isPrerelease, parseSemver } from "../../utils/semver";
|
|
15
|
+
|
|
16
|
+
export const MCP_CONFIG_PATH = ".mcp.json";
|
|
17
|
+
export const MCP_VERSION_ENV = "SWALLOWKIT_MCP_VERSION";
|
|
18
|
+
export const MCP_LATEST = "latest";
|
|
19
|
+
|
|
20
|
+
export interface McpPinDecision {
|
|
21
|
+
version: string;
|
|
22
|
+
reason: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Decide the SWALLOWKIT_MCP_VERSION value for a project that uses `projectVersion`.
|
|
27
|
+
* Uses `latest` unless the project version is newer than the published `latest`.
|
|
28
|
+
* When the registry could not be reached, prereleases are pinned conservatively.
|
|
29
|
+
*/
|
|
30
|
+
export function resolveMcpPinVersion(projectVersion: string, publishedLatest: string | null): McpPinDecision {
|
|
31
|
+
if (!parseSemver(projectVersion)) {
|
|
32
|
+
return { version: MCP_LATEST, reason: `Project version ${projectVersion} is not a valid semantic version.` };
|
|
33
|
+
}
|
|
34
|
+
if (publishedLatest && parseSemver(publishedLatest)) {
|
|
35
|
+
const comparison = compareSemver(projectVersion, publishedLatest)!;
|
|
36
|
+
return comparison > 0
|
|
37
|
+
? { version: projectVersion, reason: `Project version ${projectVersion} is newer than published latest ${publishedLatest}.` }
|
|
38
|
+
: { version: MCP_LATEST, reason: `Published latest ${publishedLatest} is the same as or newer than project version ${projectVersion}.` };
|
|
39
|
+
}
|
|
40
|
+
return isPrerelease(projectVersion)
|
|
41
|
+
? { version: projectVersion, reason: `Published latest is unknown; prerelease ${projectVersion} is pinned so MCP matches the project.` }
|
|
42
|
+
: { version: MCP_LATEST, reason: "Published latest is unknown; stable projects keep latest." };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function lookupPublishedLatestVersion(packageName: string, timeoutMs = 15000): string | null {
|
|
46
|
+
// Run outside the project so its devEngines/packageManager policy does not reject npm.
|
|
47
|
+
const result = spawnSync("npm", ["view", `${packageName}@latest`, "version"], {
|
|
48
|
+
cwd: os.tmpdir(),
|
|
49
|
+
encoding: "utf8",
|
|
50
|
+
timeout: timeoutMs,
|
|
51
|
+
shell: process.platform === "win32",
|
|
52
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
53
|
+
});
|
|
54
|
+
if (result.error || result.status !== 0) return null;
|
|
55
|
+
const version = String(result.stdout).trim().split(/\s+/).pop() ?? "";
|
|
56
|
+
return parseSemver(version) ? version : null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function readMcpConfigVersion(projectDir: string): string | null {
|
|
60
|
+
const configPath = path.join(projectDir, MCP_CONFIG_PATH);
|
|
61
|
+
if (!fs.existsSync(configPath)) return null;
|
|
62
|
+
try {
|
|
63
|
+
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
64
|
+
const value = config?.mcpServers?.swallowkit?.env?.[MCP_VERSION_ENV];
|
|
65
|
+
return typeof value === "string" ? value : null;
|
|
66
|
+
} catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Update only the SwallowKit MCP version in .mcp.json, preserving all other content. */
|
|
72
|
+
export function writeMcpConfigVersion(projectDir: string, version: string): void {
|
|
73
|
+
const configPath = path.join(projectDir, MCP_CONFIG_PATH);
|
|
74
|
+
if (!fs.existsSync(configPath)) {
|
|
75
|
+
throw new Error(`${MCP_CONFIG_PATH} was not found. Run this command from a SwallowKit project root.`);
|
|
76
|
+
}
|
|
77
|
+
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
78
|
+
const server = config?.mcpServers?.swallowkit;
|
|
79
|
+
if (!server || typeof server !== "object") {
|
|
80
|
+
throw new Error(`${MCP_CONFIG_PATH} does not define mcpServers.swallowkit.`);
|
|
81
|
+
}
|
|
82
|
+
server.env = { ...(server.env ?? {}), [MCP_VERSION_ENV]: version };
|
|
83
|
+
fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`);
|
|
84
|
+
}
|
|
@@ -52,9 +52,16 @@ Goal: add a data model and derive CRUD/BFF/UI/infra deterministically.
|
|
|
52
52
|
1. Create the model file (deterministic template):
|
|
53
53
|
\`${runCmd} swallowkit machine generate model <name> --overwrite never\`
|
|
54
54
|
2. Edit \`shared/models/<name>.ts\` — add domain fields (free-form authoring).
|
|
55
|
+
- Native C#/Python types follow Zod, not names: \`z.iso.datetime()\` for dates, \`z.number().int()\` for integers.
|
|
56
|
+
- \`<model>Id\` fields reference a model only when it exists in \`shared/models\`; otherwise declare
|
|
57
|
+
\`export const references = { fieldId: 'Model' }\` (or \`false\` to opt out).
|
|
55
58
|
3. Compute the change plan (writes nothing):
|
|
56
59
|
\`${runCmd} swallowkit machine plan scaffold <name>\`
|
|
57
60
|
- \`status: "requires-human"\` → review \`data.conflicts\`; only proceed with \`--approve\` if overwriting is intended.
|
|
61
|
+
- Must not change Bicep/infrastructure? Add \`--skip-infra\` to plan and apply. Nothing under \`infra/\` changes and
|
|
62
|
+
the plan warns \`infra-skipped\`; later run \`${runCmd} swallowkit migrations add-model <name>\` to add only the container migration.
|
|
63
|
+
- Read \`data.warnings\`. \`user-scoped-data-without-auth\` means generated CRUD would expose every user's documents;
|
|
64
|
+
add auth (\`swallowkit-add-auth\`) and owner-scoped ai-authored endpoints before exposing the model.
|
|
58
65
|
4. Apply:
|
|
59
66
|
\`${runCmd} swallowkit machine apply scaffold --plan <planId>\`
|
|
60
67
|
5. Verify:
|
|
@@ -247,7 +254,8 @@ under \`.github/skills/\` so skills-aware agents load them automatically.
|
|
|
247
254
|
|
|
248
255
|
- **ai-authored** (write freely): \`shared/models/\`, business logic, custom pages/components, tests.
|
|
249
256
|
- **deterministic** (never hand-edit): artifacts generated by SwallowKit (see \`.swallowkit/artifacts.json\`) and \`.swallowkit/\` metadata. Change models/config and regenerate via plan/apply.
|
|
250
|
-
- **shared** (edit outside markers): \`
|
|
257
|
+
- **shared** (edit outside markers): \`shared/index.ts\`, plus backend registration files for the configured language (Python: \`functions/function_app.py\`).
|
|
258
|
+
- **infrastructure**: baseline \`infra/\` files are checksum-locked by \`infra/migrations/manifest.json\`; add resources as new migrations (\`swallowkit create-migration <slug>\`, \`swallowkit migrations add-model <model>\`).
|
|
251
259
|
|
|
252
260
|
Full machine-readable contract: \`${runCmd} swallowkit machine inspect boundaries\`
|
|
253
261
|
`,
|