swallowkit 2.0.0-alpha.2 → 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/dev.d.ts +2 -2
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +39 -27
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/init.d.ts +6 -2
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +43 -122
- 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/agent-instructions.d.ts.map +1 -1
- package/dist/core/project/agent-instructions.js +4 -1
- package/dist/core/project/agent-instructions.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/home-page.d.ts +4 -0
- package/dist/core/project/home-page.d.ts.map +1 -0
- package/dist/core/project/home-page.js +113 -0
- package/dist/core/project/home-page.js.map +1 -0
- 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/react-project.d.ts +1 -1
- package/dist/core/project/react-project.d.ts.map +1 -1
- package/dist/core/project/react-project.js +45 -24
- package/dist/core/project/react-project.js.map +1 -1
- 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/auth-generator.js +8 -8
- 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 +279 -91
- 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/package-manager.js +1 -1
- package/dist/utils/package-manager.js.map +1 -1
- package/dist/utils/python-uv.d.ts +5 -7
- package/dist/utils/python-uv.d.ts.map +1 -1
- package/dist/utils/python-uv.js +50 -24
- package/dist/utils/python-uv.js.map +1 -1
- package/dist/utils/react-dev.d.ts +4 -0
- package/dist/utils/react-dev.d.ts.map +1 -0
- package/dist/utils/react-dev.js +35 -0
- package/dist/utils/react-dev.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__/auth.test.ts +9 -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__/dev.test.ts +13 -6
- package/src/__tests__/home-page.test.ts +50 -0
- package/src/__tests__/init.test.ts +57 -0
- package/src/__tests__/mcp-version.test.ts +262 -0
- package/src/__tests__/mcp.test.ts +49 -3
- package/src/__tests__/package-manager.test.ts +2 -2
- package/src/__tests__/plan-apply.test.ts +68 -0
- package/src/__tests__/python-uv.test.ts +50 -14
- package/src/__tests__/python-venv.test.ts +48 -0
- package/src/__tests__/react-dev.test.ts +72 -0
- package/src/__tests__/react.test.ts +19 -0
- package/src/__tests__/schema-analysis.test.ts +329 -0
- package/src/cli/commands/configure-mcp.ts +54 -0
- package/src/cli/commands/dev.ts +40 -31
- package/src/cli/commands/init.ts +48 -125
- 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-instructions.ts +4 -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/home-page.ts +114 -0
- package/src/core/project/mcp-config.ts +84 -0
- package/src/core/project/react-project.ts +49 -27
- package/src/core/project/workflows.ts +9 -1
- package/src/core/scaffold/auth-generator.ts +8 -8
- package/src/core/scaffold/model-parser.ts +413 -129
- package/src/core/scaffold/native-schema-generator.ts +305 -105
- 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/package-manager.ts +1 -1
- package/src/utils/python-uv.ts +53 -33
- package/src/utils/react-dev.ts +31 -0
- package/src/utils/semver.ts +59 -0
|
@@ -14,7 +14,9 @@ Always-on rules:
|
|
|
14
14
|
- Inspect the current project and SwallowKit capabilities before making
|
|
15
15
|
SwallowKit-dependent assumptions or asking questions the repository can answer.
|
|
16
16
|
- Prefer `swallowkit_*` MCP tools.
|
|
17
|
-
- Fall back to `{{runCmd}} swallowkit machine ...` when MCP is unavailable
|
|
17
|
+
- Fall back to `{{runCmd}} swallowkit machine ...` when MCP is unavailable, or
|
|
18
|
+
when an MCP response's `metadata.swallowkitVersion` is older than the project's
|
|
19
|
+
SwallowKit (`metadata.warnings` contains `mcp-older-than-project`).
|
|
18
20
|
- Inspect responsibility boundaries before editing framework-related files.
|
|
19
21
|
- Never hand-edit deterministic SwallowKit-managed artifacts.
|
|
20
22
|
- Add auth schemes only via plan/apply auth; never hand-write `auth.schemes`
|
|
@@ -13,7 +13,9 @@ Key rules:
|
|
|
13
13
|
- Inspect current SwallowKit capabilities instead of assuming them.
|
|
14
14
|
- Answer repository- and SwallowKit-inspectable questions before asking the human.
|
|
15
15
|
- Prefer `swallowkit_*` MCP tools when available.
|
|
16
|
-
- Otherwise use `{{runCmd}} swallowkit machine ...`.
|
|
16
|
+
- Otherwise use `{{runCmd}} swallowkit machine ...`. Also use it when an MCP
|
|
17
|
+
response's `metadata.swallowkitVersion` is older than the project's SwallowKit
|
|
18
|
+
or `metadata.warnings` contains `mcp-older-than-project`.
|
|
17
19
|
- Inspect responsibility boundaries before editing framework-related files.
|
|
18
20
|
- Never hand-edit deterministic SwallowKit-managed artifacts.
|
|
19
21
|
- Add auth schemes only via plan/apply auth; never hand-write `auth.schemes`
|
|
@@ -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,114 @@
|
|
|
1
|
+
import { FrontendFramework } from "../../types";
|
|
2
|
+
import { PackageManager, getCommands } from "../../utils/package-manager";
|
|
3
|
+
import { adaptReactPage } from "../scaffold/react-ui-generator";
|
|
4
|
+
|
|
5
|
+
export function buildHomePage(
|
|
6
|
+
frontend: FrontendFramework,
|
|
7
|
+
pm: PackageManager
|
|
8
|
+
): string {
|
|
9
|
+
const framework = frontend === "react" ? "React" : "Next.js";
|
|
10
|
+
const page = `import { useState } from 'react';
|
|
11
|
+
import Link from 'next/link';
|
|
12
|
+
import { scaffoldConfig } from '@/lib/scaffold-config';
|
|
13
|
+
|
|
14
|
+
export default function Home() {
|
|
15
|
+
const [greetingStatus, setGreetingStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
|
|
16
|
+
const [message, setMessage] = useState('');
|
|
17
|
+
|
|
18
|
+
const testConnection = async () => {
|
|
19
|
+
setGreetingStatus('loading');
|
|
20
|
+
try {
|
|
21
|
+
const response = await fetch('/api/greet?name=SwallowKit');
|
|
22
|
+
const data = await response.json();
|
|
23
|
+
if (!response.ok) {
|
|
24
|
+
throw new Error(data.error || \`Server error: \${response.status}\`);
|
|
25
|
+
}
|
|
26
|
+
setMessage(data.message);
|
|
27
|
+
setGreetingStatus('success');
|
|
28
|
+
} catch (error) {
|
|
29
|
+
setMessage(error instanceof Error ? error.message : 'Failed to connect to Azure Functions');
|
|
30
|
+
setGreetingStatus('error');
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-50 dark:from-gray-900 dark:to-gray-800">
|
|
36
|
+
<div className="container mx-auto px-4 py-12">
|
|
37
|
+
<header className="text-center mb-16">
|
|
38
|
+
<h1 className="text-5xl font-bold text-gray-800 dark:text-white mb-4">
|
|
39
|
+
Welcome to SwallowKit
|
|
40
|
+
</h1>
|
|
41
|
+
<p className="text-xl text-gray-600 dark:text-gray-400">
|
|
42
|
+
${framework} on Azure Static Web Apps + Functions + Cosmos DB — Zod schema sharing
|
|
43
|
+
</p>
|
|
44
|
+
</header>
|
|
45
|
+
|
|
46
|
+
<section className="max-w-2xl mx-auto mb-12">
|
|
47
|
+
<div className="bg-white dark:bg-gray-800 rounded-xl p-8 border border-gray-200 dark:border-gray-700">
|
|
48
|
+
<h2 className="text-2xl font-semibold mb-4 text-gray-900 dark:text-gray-100">
|
|
49
|
+
Test BFF → Functions Connection
|
|
50
|
+
</h2>
|
|
51
|
+
<button
|
|
52
|
+
onClick={testConnection}
|
|
53
|
+
disabled={greetingStatus === 'loading'}
|
|
54
|
+
className="px-6 py-3 bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 text-white rounded-lg font-medium transition-colors"
|
|
55
|
+
>
|
|
56
|
+
{greetingStatus === 'loading' ? 'Testing...' : 'Test Connection'}
|
|
57
|
+
</button>
|
|
58
|
+
{greetingStatus === 'success' && (
|
|
59
|
+
<div role="status" className="mt-4 p-4 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg">
|
|
60
|
+
<p className="text-green-800 dark:text-green-200 font-medium">✅ Connection successful!</p>
|
|
61
|
+
<p className="text-green-700 dark:text-green-300 text-sm mt-1">{message}</p>
|
|
62
|
+
</div>
|
|
63
|
+
)}
|
|
64
|
+
{greetingStatus === 'error' && (
|
|
65
|
+
<div role="alert" className="mt-4 p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg">
|
|
66
|
+
<p className="text-red-800 dark:text-red-200 font-medium">❌ Connection failed</p>
|
|
67
|
+
<p className="text-red-700 dark:text-red-300 text-sm mt-1">{message}</p>
|
|
68
|
+
</div>
|
|
69
|
+
)}
|
|
70
|
+
</div>
|
|
71
|
+
</section>
|
|
72
|
+
|
|
73
|
+
{scaffoldConfig.models.length > 0 ? (
|
|
74
|
+
<section className="max-w-6xl mx-auto">
|
|
75
|
+
<h2 className="text-3xl font-bold mb-8 text-gray-900 dark:text-gray-100">Your Models</h2>
|
|
76
|
+
<div className="grid gap-6 grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
|
|
77
|
+
{scaffoldConfig.models.map((model) => (
|
|
78
|
+
<Link
|
|
79
|
+
key={model.name}
|
|
80
|
+
href={model.path}
|
|
81
|
+
className="block p-8 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl hover:shadow-lg hover:border-blue-400 dark:hover:border-blue-600 transition-all"
|
|
82
|
+
>
|
|
83
|
+
<h3 className="text-2xl font-semibold mb-2 text-gray-900 dark:text-gray-100">{model.label}</h3>
|
|
84
|
+
<p className="text-gray-600 dark:text-gray-400">Manage {model.label.toLowerCase()}</p>
|
|
85
|
+
</Link>
|
|
86
|
+
))}
|
|
87
|
+
</div>
|
|
88
|
+
</section>
|
|
89
|
+
) : (
|
|
90
|
+
<section className="max-w-2xl mx-auto text-center">
|
|
91
|
+
<div className="bg-white dark:bg-gray-800 rounded-xl p-12 border border-gray-200 dark:border-gray-700">
|
|
92
|
+
<h2 className="text-2xl font-semibold mb-4 text-gray-900 dark:text-gray-100">Get Started</h2>
|
|
93
|
+
<p className="text-gray-600 dark:text-gray-400 mb-6">
|
|
94
|
+
Create your first model with Zod and generate CRUD operations automatically.
|
|
95
|
+
</p>
|
|
96
|
+
<code className="block bg-gray-100 dark:bg-gray-900 p-4 rounded text-left text-sm">
|
|
97
|
+
${getCommands(pm).exec} swallowkit scaffold shared/models/your-model.ts
|
|
98
|
+
</code>
|
|
99
|
+
</div>
|
|
100
|
+
</section>
|
|
101
|
+
)}
|
|
102
|
+
|
|
103
|
+
<footer className="mt-16 text-center text-gray-600 dark:text-gray-400 text-sm">
|
|
104
|
+
<p>Built with SwallowKit</p>
|
|
105
|
+
</footer>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
`;
|
|
111
|
+
return frontend === "react"
|
|
112
|
+
? adaptReactPage(page)
|
|
113
|
+
: `'use client'\n\nexport const dynamic = 'force-dynamic';\n\n${page}`;
|
|
114
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { BackendLanguage } from "../../types";
|
|
4
|
+
import { buildReactDevWatchIgnoredSource } from "../../utils/react-dev";
|
|
5
|
+
import { DEFAULT_AZURE_PYTHON_VERSION } from "../../utils/python-uv";
|
|
6
|
+
import { buildHomePage } from "./home-page";
|
|
4
7
|
import {
|
|
5
8
|
PackageManager,
|
|
6
9
|
getCommands,
|
|
@@ -45,7 +48,8 @@ export function buildReactSwaConfig() {
|
|
|
45
48
|
|
|
46
49
|
export function createReactProject(
|
|
47
50
|
projectDir: string,
|
|
48
|
-
projectName: string
|
|
51
|
+
projectName: string,
|
|
52
|
+
pm: PackageManager = "pnpm"
|
|
49
53
|
): void {
|
|
50
54
|
write(
|
|
51
55
|
projectDir,
|
|
@@ -117,7 +121,11 @@ export default defineConfig({
|
|
|
117
121
|
'@': fileURLToPath(new URL('.', import.meta.url)),
|
|
118
122
|
'@${projectName}/shared': fileURLToPath(new URL('./shared/index.ts', import.meta.url)),
|
|
119
123
|
} },
|
|
120
|
-
server: {
|
|
124
|
+
server: {
|
|
125
|
+
port: 3000,
|
|
126
|
+
strictPort: true,
|
|
127
|
+
watch: { ignored: ${buildReactDevWatchIgnoredSource("fileURLToPath(new URL('.', import.meta.url))")} },
|
|
128
|
+
},
|
|
121
129
|
build: { outDir: 'dist' },
|
|
122
130
|
});
|
|
123
131
|
`
|
|
@@ -126,9 +134,9 @@ export default defineConfig({
|
|
|
126
134
|
projectDir,
|
|
127
135
|
"index.html",
|
|
128
136
|
`<!doctype html>
|
|
129
|
-
<html lang="en">
|
|
137
|
+
<html lang="en" class="h-full antialiased">
|
|
130
138
|
<head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>SwallowKit</title></head>
|
|
131
|
-
<body><div id="root"></div><script type="module" src="/src/main.tsx"></script></body>
|
|
139
|
+
<body class="min-h-full flex flex-col"><div id="root" class="flex flex-1 flex-col"></div><script type="module" src="/src/main.tsx"></script></body>
|
|
132
140
|
</html>
|
|
133
141
|
`
|
|
134
142
|
);
|
|
@@ -169,31 +177,34 @@ export default function App() {
|
|
|
169
177
|
write(
|
|
170
178
|
projectDir,
|
|
171
179
|
"src\\Home.tsx",
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
180
|
+
buildHomePage("react", pm)
|
|
181
|
+
);
|
|
182
|
+
write(projectDir, "src\\style.css", `@import "tailwindcss";
|
|
183
|
+
|
|
184
|
+
:root {
|
|
185
|
+
--background: #ffffff;
|
|
186
|
+
--foreground: #171717;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
@theme inline {
|
|
190
|
+
--color-background: var(--background);
|
|
191
|
+
--color-foreground: var(--foreground);
|
|
192
|
+
--font-sans: Arial, Helvetica, sans-serif;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
@media (prefers-color-scheme: dark) {
|
|
196
|
+
:root {
|
|
197
|
+
--background: #0a0a0a;
|
|
198
|
+
--foreground: #ededed;
|
|
185
199
|
}
|
|
186
|
-
return <main className="container mx-auto p-8">
|
|
187
|
-
<h1 className="text-3xl font-bold mb-4">SwallowKit + React</h1>
|
|
188
|
-
<p>React SPA → SWA managed Functions BFF → standalone Functions → Cosmos DB</p>
|
|
189
|
-
<button className="my-4 bg-blue-600 text-white rounded px-4 py-2" onClick={testConnection}>Test backend connection</button>
|
|
190
|
-
<p role="status">{message}</p>
|
|
191
|
-
<nav>{scaffoldConfig.models.map(model => <p key={model.name}><Link to={model.path}>{model.label}</Link></p>)}</nav>
|
|
192
|
-
</main>;
|
|
193
200
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
201
|
+
|
|
202
|
+
body {
|
|
203
|
+
background: var(--background);
|
|
204
|
+
color: var(--foreground);
|
|
205
|
+
font-family: Arial, Helvetica, sans-serif;
|
|
206
|
+
}
|
|
207
|
+
`);
|
|
197
208
|
write(
|
|
198
209
|
projectDir,
|
|
199
210
|
"lib\\scaffold-config.ts",
|
|
@@ -375,6 +386,17 @@ The BFF stays separate even with \`--no-functions\`; \`--no-swa\` starts only th
|
|
|
375
386
|
direct Vite/BFF servers (no same-origin API routing or SWA authentication).
|
|
376
387
|
Core Tools v4 must already be available. Start Cosmos DB Emulator manually if
|
|
377
388
|
this application needs Cosmos; SwallowKit never starts it.
|
|
389
|
+
${backend === "python" ? `
|
|
390
|
+
Python environments use normal uv discovery and user settings, including installed
|
|
391
|
+
Python, \`.python-version\`, and \`UV_PYTHON\`. Local Python is not pinned to the Azure
|
|
392
|
+
deployment runtime (${DEFAULT_AZURE_PYTHON_VERSION}). Functions uses \`functions/.venv\`; scaffold uses
|
|
393
|
+
\`functions/.codegen-venv\`. Existing environments are never cleared automatically.
|
|
394
|
+
When switching interpreters, explicitly recreate them with uv. Do not disable
|
|
395
|
+
Windows application-control policy to run an unsigned interpreter.
|
|
396
|
+
` : backend === "csharp" ? `
|
|
397
|
+
The .NET isolated backend starts with \`dotnet run\` so extensions are loaded from
|
|
398
|
+
the build output. Vite does not watch \`functions/\` or \`bff/\` build artifacts.
|
|
399
|
+
` : ""}
|
|
378
400
|
|
|
379
401
|
Local BFF settings live in \`bff/local.settings.json\` (gitignored). Backend settings
|
|
380
402
|
live in \`functions/local.settings.json\`. Keep their secrets out of source control.
|
|
@@ -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
|
`,
|
|
@@ -1161,17 +1161,17 @@ export default function LoginPage() {
|
|
|
1161
1161
|
};
|
|
1162
1162
|
|
|
1163
1163
|
return (
|
|
1164
|
-
<div className="min-h-screen flex items-center justify-center bg-gray-50">
|
|
1165
|
-
<div className="w-full max-w-md p-8 bg-white rounded-lg shadow-md">
|
|
1166
|
-
<h1 className="text-2xl font-bold text-center mb-6">ログイン</h1>
|
|
1164
|
+
<div className="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900">
|
|
1165
|
+
<div className="w-full max-w-md p-8 bg-white dark:bg-gray-800 rounded-lg shadow-md">
|
|
1166
|
+
<h1 className="text-2xl font-bold text-center mb-6 text-gray-900 dark:text-gray-100">ログイン</h1>
|
|
1167
1167
|
{error && (
|
|
1168
|
-
<div className="mb-4 p-3 bg-red-50 border border-red-200 text-red-700 rounded text-sm">
|
|
1168
|
+
<div className="mb-4 p-3 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 text-red-700 dark:text-red-300 rounded text-sm">
|
|
1169
1169
|
{error}
|
|
1170
1170
|
</div>
|
|
1171
1171
|
)}
|
|
1172
1172
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
1173
1173
|
<div>
|
|
1174
|
-
<label htmlFor="loginId" className="block text-sm font-medium text-gray-700 mb-1">
|
|
1174
|
+
<label htmlFor="loginId" className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
1175
1175
|
ログインID
|
|
1176
1176
|
</label>
|
|
1177
1177
|
<input
|
|
@@ -1179,13 +1179,13 @@ export default function LoginPage() {
|
|
|
1179
1179
|
type="text"
|
|
1180
1180
|
value={loginId}
|
|
1181
1181
|
onChange={(e) => setLoginId(e.target.value)}
|
|
1182
|
-
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
1182
|
+
className="w-full px-3 py-2 bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
1183
1183
|
required
|
|
1184
1184
|
autoFocus
|
|
1185
1185
|
/>
|
|
1186
1186
|
</div>
|
|
1187
1187
|
<div>
|
|
1188
|
-
<label htmlFor="password" className="block text-sm font-medium text-gray-700 mb-1">
|
|
1188
|
+
<label htmlFor="password" className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
1189
1189
|
パスワード
|
|
1190
1190
|
</label>
|
|
1191
1191
|
<input
|
|
@@ -1193,7 +1193,7 @@ export default function LoginPage() {
|
|
|
1193
1193
|
type="password"
|
|
1194
1194
|
value={password}
|
|
1195
1195
|
onChange={(e) => setPassword(e.target.value)}
|
|
1196
|
-
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
1196
|
+
className="w-full px-3 py-2 bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
1197
1197
|
required
|
|
1198
1198
|
/>
|
|
1199
1199
|
</div>
|