swallowkit 1.0.0-beta.56 → 1.0.0-beta.58
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/README.ja.md +1 -0
- package/README.md +1 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +12 -498
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +8 -6
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/core/operations/file-session.d.ts +3 -0
- package/dist/core/operations/file-session.d.ts.map +1 -1
- package/dist/core/operations/file-session.js +8 -0
- package/dist/core/operations/file-session.js.map +1 -1
- package/dist/core/operations/scaffold-plan.d.ts.map +1 -1
- package/dist/core/operations/scaffold-plan.js +1 -9
- package/dist/core/operations/scaffold-plan.js.map +1 -1
- package/dist/core/project/agent-instructions.d.ts +29 -0
- package/dist/core/project/agent-instructions.d.ts.map +1 -0
- package/dist/core/project/agent-instructions.js +159 -0
- package/dist/core/project/agent-instructions.js.map +1 -0
- package/dist/core/scaffold/native-schema-generator.d.ts +7 -1
- package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -1
- package/dist/core/scaffold/native-schema-generator.js +104 -47
- package/dist/core/scaffold/native-schema-generator.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/agent-instructions.test.ts +237 -0
- package/src/__tests__/init.test.ts +2 -0
- package/src/__tests__/plan-apply.test.ts +167 -0
- package/src/__tests__/scaffold.test.ts +2 -2
- package/src/cli/commands/init.ts +19 -525
- package/src/cli/commands/scaffold.ts +15 -8
- package/src/core/operations/file-session.ts +12 -0
- package/src/core/operations/scaffold-plan.ts +2 -11
- package/src/core/project/agent-instructions.ts +167 -0
- package/src/core/project/agent-templates/AGENTS.md +442 -0
- package/src/core/project/agent-templates/CLAUDE.md +28 -0
- package/src/core/project/agent-templates/azure-functions.instructions.md +22 -0
- package/src/core/project/agent-templates/bff-routes.instructions.md +18 -0
- package/src/core/project/agent-templates/copilot-instructions.md +24 -0
- package/src/core/project/agent-templates/shared-models.instructions.md +35 -0
- package/src/core/scaffold/native-schema-generator.ts +152 -70
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "app/api/**"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# BFF API Routes — Rules
|
|
6
|
+
|
|
7
|
+
The root `AGENTS.md` is the canonical contract. Inspect the current
|
|
8
|
+
responsibility boundary before editing files in this layer.
|
|
9
|
+
|
|
10
|
+
- `app/api/` is a BFF/proxy layer. Never place business logic, database access,
|
|
11
|
+
or direct Cosmos DB calls here.
|
|
12
|
+
- Use `callFunction()` from `@/lib/api/call-function` to forward requests to
|
|
13
|
+
Azure Functions.
|
|
14
|
+
- Input/output validation may use schemas imported from `@{{projectName}}/shared`.
|
|
15
|
+
- Never directly edit a route reported as deterministic. Change its source of
|
|
16
|
+
truth and use MCP-first plan/apply, with the machine interface as fallback.
|
|
17
|
+
- Edit shared artifacts only outside managed markers.
|
|
18
|
+
- Respect `requires-human` and run SwallowKit verification after changes.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Copilot Instructions
|
|
2
|
+
|
|
3
|
+
This is a **SwallowKit** project.
|
|
4
|
+
|
|
5
|
+
Read and follow the root `AGENTS.md` as the canonical project contract.
|
|
6
|
+
|
|
7
|
+
SwallowKit applies during discovery, requirements clarification, specification,
|
|
8
|
+
planning, ticket decomposition, implementation, verification, and review — not
|
|
9
|
+
only during code generation.
|
|
10
|
+
|
|
11
|
+
Key rules:
|
|
12
|
+
|
|
13
|
+
- Inspect current SwallowKit capabilities instead of assuming them.
|
|
14
|
+
- Answer repository- and SwallowKit-inspectable questions before asking the human.
|
|
15
|
+
- Prefer `swallowkit_*` MCP tools when available.
|
|
16
|
+
- Otherwise use `{{runCmd}} swallowkit machine ...`.
|
|
17
|
+
- Inspect responsibility boundaries before editing framework-related files.
|
|
18
|
+
- Never hand-edit deterministic SwallowKit-managed artifacts.
|
|
19
|
+
- Use plan/apply for deterministic generation.
|
|
20
|
+
- Respect `requires-human` states and other human approval gates.
|
|
21
|
+
- Run SwallowKit verification before completion.
|
|
22
|
+
|
|
23
|
+
Task-specific SwallowKit Agent Skills live under `.github/skills/`.
|
|
24
|
+
Agent-agnostic equivalents live under `.swallowkit/workflows/`.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "shared/models/**"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Shared Models — Zod Schema Rules
|
|
6
|
+
|
|
7
|
+
The root `AGENTS.md` is the canonical contract. Files here are ai-authored
|
|
8
|
+
source-of-truth models, subject to the current result of
|
|
9
|
+
`{{runCmd}} swallowkit machine inspect boundaries`.
|
|
10
|
+
|
|
11
|
+
## Rules
|
|
12
|
+
|
|
13
|
+
- Define Zod schemas using `zod/v4` (`import { z } from 'zod/v4'`).
|
|
14
|
+
- Use the Zod pattern where the schema constant and inferred TypeScript type
|
|
15
|
+
share the same name.
|
|
16
|
+
- Include `id`, `createdAt`, and `updatedAt` as optional string fields. The
|
|
17
|
+
backend manages their values.
|
|
18
|
+
- Export a `displayName` string for UI display.
|
|
19
|
+
- Re-export every model from `shared/index.ts`, outside managed markers where
|
|
20
|
+
applicable.
|
|
21
|
+
- For relationships, use nested schemas rather than duplicating model shapes.
|
|
22
|
+
|
|
23
|
+
After changing a model, do not manually patch generated artifacts.
|
|
24
|
+
|
|
25
|
+
Use the `swallowkit-modify-model` Agent Skill when available. Otherwise follow
|
|
26
|
+
`.swallowkit/workflows/modify-model.md`.
|
|
27
|
+
|
|
28
|
+
The normal agent workflow is:
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
inspect drift → plan scaffold → apply scaffold → verify project
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Prefer the equivalent `swallowkit_*` MCP tools; use the machine interface as the
|
|
35
|
+
fallback. Stop for any `requires-human` result.
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
|
+
import * as os from "os";
|
|
2
3
|
import * as path from "path";
|
|
3
4
|
import { spawn, spawnSync } from "child_process";
|
|
4
5
|
import { BackendLanguage } from "../../types";
|
|
6
|
+
import { FileOperationSession } from "../operations/file-session";
|
|
5
7
|
import { ModelInfo, toKebabCase } from "./model-parser";
|
|
6
8
|
import { generateOpenApiDocument } from "./openapi-generator";
|
|
7
9
|
import {
|
|
@@ -621,23 +623,47 @@ function mergePythonInitSource(existingSource: string | undefined, generatedSour
|
|
|
621
623
|
return `${normalizedExisting}${missingLines.join("\n")}\n`;
|
|
622
624
|
}
|
|
623
625
|
|
|
624
|
-
function writeMergedPythonInitFile(
|
|
625
|
-
|
|
626
|
-
|
|
626
|
+
function writeMergedPythonInitFile(
|
|
627
|
+
filePath: string,
|
|
628
|
+
generatedSource: string,
|
|
629
|
+
session: FileOperationSession,
|
|
630
|
+
sourceModel: string
|
|
631
|
+
): void {
|
|
632
|
+
const existingSource = session.fileExists(filePath) ? session.readFile(filePath) : undefined;
|
|
633
|
+
session.writeFile(filePath, mergePythonInitSource(existingSource, generatedSource), {
|
|
634
|
+
ownership: "extension-point",
|
|
635
|
+
generator: "native-schema",
|
|
636
|
+
sourceModel,
|
|
637
|
+
});
|
|
627
638
|
}
|
|
628
639
|
|
|
629
|
-
function ensureCSharpCodegenProjectFiles(
|
|
640
|
+
function ensureCSharpCodegenProjectFiles(
|
|
641
|
+
functionsRoot: string,
|
|
642
|
+
session: FileOperationSession,
|
|
643
|
+
sourceModel: string
|
|
644
|
+
): void {
|
|
630
645
|
const toolManifestPath = path.join(functionsRoot, ".config", "dotnet-tools.json");
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
646
|
+
if (!session.fileExists(toolManifestPath)) {
|
|
647
|
+
session.writeFile(toolManifestPath, buildCSharpCodegenToolManifestSource(), {
|
|
648
|
+
ownership: "generated-once",
|
|
649
|
+
generator: "native-schema",
|
|
650
|
+
sourceModel,
|
|
651
|
+
});
|
|
634
652
|
}
|
|
635
653
|
}
|
|
636
654
|
|
|
637
|
-
function ensurePythonCodegenProjectFiles(
|
|
655
|
+
function ensurePythonCodegenProjectFiles(
|
|
656
|
+
functionsRoot: string,
|
|
657
|
+
session: FileOperationSession,
|
|
658
|
+
sourceModel: string
|
|
659
|
+
): string {
|
|
638
660
|
const requirementsPath = path.join(functionsRoot, "requirements.codegen.txt");
|
|
639
|
-
if (!
|
|
640
|
-
|
|
661
|
+
if (!session.fileExists(requirementsPath)) {
|
|
662
|
+
session.writeFile(requirementsPath, buildPythonCodegenRequirementsSource(), {
|
|
663
|
+
ownership: "generated-once",
|
|
664
|
+
generator: "native-schema",
|
|
665
|
+
sourceModel,
|
|
666
|
+
});
|
|
641
667
|
}
|
|
642
668
|
return requirementsPath;
|
|
643
669
|
}
|
|
@@ -676,8 +702,7 @@ async function ensureProjectLocalUvCommand(projectRoot: string): Promise<{ comma
|
|
|
676
702
|
return { command: localUvExecutable, env: uvEnv };
|
|
677
703
|
}
|
|
678
704
|
|
|
679
|
-
async function ensurePythonCodegenEnvironment(functionsRoot: string): Promise<string> {
|
|
680
|
-
const requirementsPath = ensurePythonCodegenProjectFiles(functionsRoot);
|
|
705
|
+
async function ensurePythonCodegenEnvironment(functionsRoot: string, requirementsPath: string): Promise<string> {
|
|
681
706
|
const projectRoot = getPythonProjectRoot(functionsRoot);
|
|
682
707
|
const { command: uvCommand, env: uvEnv } = await ensureProjectLocalUvCommand(projectRoot);
|
|
683
708
|
const venvDir = path.join(functionsRoot, ".codegen-venv");
|
|
@@ -711,79 +736,125 @@ async function ensurePythonCodegenEnvironment(functionsRoot: string): Promise<st
|
|
|
711
736
|
return venvPython;
|
|
712
737
|
}
|
|
713
738
|
|
|
714
|
-
|
|
739
|
+
/**
|
|
740
|
+
* 外部 codegen toolchain の事前検証。プロジェクトには一切書き込まず、
|
|
741
|
+
* OS 一時ディレクトリ上で NSwag / datamodel-code-generator を実行して失敗を早期検出する。
|
|
742
|
+
*/
|
|
743
|
+
export async function validateNativeSchemaToolchain(
|
|
715
744
|
models: ModelInfo[],
|
|
716
|
-
|
|
717
|
-
|
|
745
|
+
rootModel: ModelInfo,
|
|
746
|
+
backendLanguage: Exclude<BackendLanguage, "typescript">,
|
|
718
747
|
functionsRoot: string
|
|
719
748
|
): Promise<void> {
|
|
720
|
-
|
|
749
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "swallowkit-native-codegen-"));
|
|
750
|
+
try {
|
|
751
|
+
const specPath = path.join(tempDir, `${toKebabCase(rootModel.name)}.openapi.json`);
|
|
752
|
+
fs.writeFileSync(specPath, generateOpenApiDocument(models, rootModel), "utf-8");
|
|
753
|
+
|
|
754
|
+
if (backendLanguage === "csharp") {
|
|
755
|
+
if (!canRun("dotnet", ["--version"], tempDir)) {
|
|
756
|
+
throw new Error(
|
|
757
|
+
"The .NET SDK is required to generate C# backend schema assets.\n" +
|
|
758
|
+
"Install the .NET 8 SDK and retry."
|
|
759
|
+
);
|
|
760
|
+
}
|
|
721
761
|
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
762
|
+
// プロジェクトの tool manifest(バージョン固定)があればそれを尊重し、なければ一時 manifest で検証する
|
|
763
|
+
const projectManifestPath = path.join(functionsRoot, ".config", "dotnet-tools.json");
|
|
764
|
+
let toolCwd = functionsRoot;
|
|
765
|
+
if (!fs.existsSync(projectManifestPath)) {
|
|
766
|
+
fs.mkdirSync(path.join(tempDir, ".config"), { recursive: true });
|
|
767
|
+
fs.writeFileSync(path.join(tempDir, ".config", "dotnet-tools.json"), buildCSharpCodegenToolManifestSource(), "utf-8");
|
|
768
|
+
toolCwd = tempDir;
|
|
769
|
+
}
|
|
728
770
|
|
|
729
|
-
|
|
730
|
-
|
|
771
|
+
await runCommand("dotnet", ["tool", "restore"], toolCwd, "Failed to restore the NSwag dotnet tool.");
|
|
772
|
+
await runCommand(
|
|
773
|
+
"dotnet",
|
|
774
|
+
getCSharpNativeGeneratorArgs(specPath, path.join(tempDir, "Contracts.cs")),
|
|
775
|
+
toolCwd,
|
|
776
|
+
"NSwag failed to generate C# backend schema assets."
|
|
777
|
+
);
|
|
778
|
+
} else {
|
|
779
|
+
const projectRequirementsPath = path.join(functionsRoot, "requirements.codegen.txt");
|
|
780
|
+
let requirementsPath = projectRequirementsPath;
|
|
781
|
+
if (!fs.existsSync(projectRequirementsPath)) {
|
|
782
|
+
requirementsPath = path.join(tempDir, "requirements.codegen.txt");
|
|
783
|
+
fs.writeFileSync(requirementsPath, buildPythonCodegenRequirementsSource(), "utf-8");
|
|
784
|
+
}
|
|
731
785
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
786
|
+
// .codegen-venv は environment cache であり managed artifact ではない(既存挙動どおり functionsRoot 配下)
|
|
787
|
+
fs.mkdirSync(functionsRoot, { recursive: true });
|
|
788
|
+
const pythonExecutable = await ensurePythonCodegenEnvironment(functionsRoot, requirementsPath);
|
|
789
|
+
await runCommand(
|
|
790
|
+
pythonExecutable,
|
|
791
|
+
getPythonNativeGeneratorArgs(specPath, path.join(tempDir, "models.py")),
|
|
792
|
+
functionsRoot,
|
|
793
|
+
"datamodel-code-generator failed to generate Python backend schema assets."
|
|
794
|
+
);
|
|
795
|
+
}
|
|
796
|
+
} finally {
|
|
797
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
798
|
+
}
|
|
799
|
+
}
|
|
744
800
|
|
|
745
|
-
|
|
801
|
+
async function generateCSharpSchemaArtifacts(
|
|
802
|
+
models: ModelInfo[],
|
|
803
|
+
outputDir: string,
|
|
804
|
+
functionsRoot: string,
|
|
805
|
+
session: FileOperationSession,
|
|
806
|
+
sourceModel: string
|
|
807
|
+
): Promise<void> {
|
|
808
|
+
ensureCSharpCodegenProjectFiles(functionsRoot, session, sourceModel);
|
|
746
809
|
|
|
747
810
|
const optionPath = getCSharpSchemaOptionPath(outputDir);
|
|
748
|
-
|
|
749
|
-
|
|
811
|
+
session.writeFile(optionPath, generateLegacyCompatibleOptionSource(), {
|
|
812
|
+
ownership: "managed",
|
|
813
|
+
generator: "native-schema",
|
|
814
|
+
sourceModel,
|
|
815
|
+
});
|
|
750
816
|
|
|
751
817
|
for (const model of models) {
|
|
752
818
|
const modelPath = getCSharpSchemaModelPath(outputDir, model.name);
|
|
753
|
-
|
|
754
|
-
|
|
819
|
+
session.writeFile(modelPath, generateLegacyCompatibleCSharpModelSource(model), {
|
|
820
|
+
ownership: "managed",
|
|
821
|
+
generator: "native-schema",
|
|
822
|
+
sourceModel,
|
|
823
|
+
});
|
|
755
824
|
}
|
|
756
825
|
}
|
|
757
826
|
|
|
758
827
|
async function generatePythonSchemaArtifacts(
|
|
759
828
|
models: ModelInfo[],
|
|
760
|
-
specPath: string,
|
|
761
829
|
outputDir: string,
|
|
762
|
-
functionsRoot: string
|
|
830
|
+
functionsRoot: string,
|
|
831
|
+
session: FileOperationSession,
|
|
832
|
+
sourceModel: string
|
|
763
833
|
): Promise<void> {
|
|
764
|
-
|
|
765
|
-
const tempModelsPath = path.join(outputDir, ".native-temp", "models.py");
|
|
766
|
-
fs.mkdirSync(path.dirname(tempModelsPath), { recursive: true });
|
|
767
|
-
|
|
768
|
-
await runCommand(
|
|
769
|
-
pythonExecutable,
|
|
770
|
-
getPythonNativeGeneratorArgs(specPath, tempModelsPath),
|
|
771
|
-
functionsRoot,
|
|
772
|
-
"datamodel-code-generator failed to generate Python backend schema assets."
|
|
773
|
-
);
|
|
774
|
-
|
|
775
|
-
fs.rmSync(path.dirname(tempModelsPath), { recursive: true, force: true });
|
|
834
|
+
ensurePythonCodegenProjectFiles(functionsRoot, session, sourceModel);
|
|
776
835
|
|
|
777
836
|
const packageRoot = path.join(outputDir, "backend_models");
|
|
778
837
|
const modelsRoot = path.join(packageRoot, "models");
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
838
|
+
writeMergedPythonInitFile(
|
|
839
|
+
path.join(packageRoot, "__init__.py"),
|
|
840
|
+
buildGeneratedPythonPackageInitSource(models),
|
|
841
|
+
session,
|
|
842
|
+
sourceModel
|
|
843
|
+
);
|
|
844
|
+
writeMergedPythonInitFile(
|
|
845
|
+
path.join(modelsRoot, "__init__.py"),
|
|
846
|
+
buildGeneratedPythonModelsInitSource(models),
|
|
847
|
+
session,
|
|
848
|
+
sourceModel
|
|
849
|
+
);
|
|
782
850
|
|
|
783
851
|
for (const model of models) {
|
|
784
852
|
const modelPath = getPythonSchemaModelPath(outputDir, model.name);
|
|
785
|
-
|
|
786
|
-
|
|
853
|
+
session.writeFile(modelPath, generateLegacyCompatiblePythonModelSource(model), {
|
|
854
|
+
ownership: "managed",
|
|
855
|
+
generator: "native-schema",
|
|
856
|
+
sourceModel,
|
|
857
|
+
});
|
|
787
858
|
}
|
|
788
859
|
}
|
|
789
860
|
|
|
@@ -791,18 +862,27 @@ export async function generateLanguageSchemaArtifacts(
|
|
|
791
862
|
models: ModelInfo[],
|
|
792
863
|
rootModel: ModelInfo,
|
|
793
864
|
functionsDir: string,
|
|
794
|
-
backendLanguage: Exclude<BackendLanguage, "typescript"
|
|
865
|
+
backendLanguage: Exclude<BackendLanguage, "typescript">,
|
|
866
|
+
session: FileOperationSession = new FileOperationSession("commit")
|
|
795
867
|
): Promise<void> {
|
|
796
|
-
|
|
868
|
+
const planning = session.mode === "collect";
|
|
869
|
+
console.log(
|
|
870
|
+
planning
|
|
871
|
+
? "\n\ud83e\uddec Planning OpenAPI export and native schema assets..."
|
|
872
|
+
: "\n\ud83e\uddec Generating OpenAPI export and native schema assets..."
|
|
873
|
+
);
|
|
797
874
|
|
|
798
875
|
const projectRoot = process.cwd();
|
|
799
876
|
const functionsRoot = path.join(projectRoot, functionsDir);
|
|
800
877
|
const openApiDir = path.join(functionsRoot, "openapi");
|
|
801
|
-
fs.mkdirSync(openApiDir, { recursive: true });
|
|
802
878
|
|
|
803
879
|
const specPath = path.join(openApiDir, `${toKebabCase(rootModel.name)}.openapi.json`);
|
|
804
|
-
|
|
805
|
-
|
|
880
|
+
session.writeFile(specPath, generateOpenApiDocument(models, rootModel), {
|
|
881
|
+
ownership: "managed",
|
|
882
|
+
generator: "native-schema",
|
|
883
|
+
sourceModel: rootModel.name,
|
|
884
|
+
});
|
|
885
|
+
console.log(planning ? `\ud83d\udccb Will create: ${specPath}` : `\u2705 Created: ${specPath}`);
|
|
806
886
|
|
|
807
887
|
const outputDir = path.join(
|
|
808
888
|
functionsRoot,
|
|
@@ -810,13 +890,15 @@ export async function generateLanguageSchemaArtifacts(
|
|
|
810
890
|
backendLanguage === "csharp" ? "csharp-models" : "python-models"
|
|
811
891
|
);
|
|
812
892
|
|
|
813
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
814
|
-
|
|
815
893
|
if (backendLanguage === "csharp") {
|
|
816
|
-
await generateCSharpSchemaArtifacts(models,
|
|
894
|
+
await generateCSharpSchemaArtifacts(models, outputDir, functionsRoot, session, rootModel.name);
|
|
817
895
|
} else {
|
|
818
|
-
await generatePythonSchemaArtifacts(models,
|
|
896
|
+
await generatePythonSchemaArtifacts(models, outputDir, functionsRoot, session, rootModel.name);
|
|
819
897
|
}
|
|
820
898
|
|
|
821
|
-
console.log(
|
|
899
|
+
console.log(
|
|
900
|
+
planning
|
|
901
|
+
? `\ud83d\udccb Will generate ${backendLanguage} schema assets: ${outputDir}`
|
|
902
|
+
: `\u2705 Generated ${backendLanguage} schema assets: ${outputDir}`
|
|
903
|
+
);
|
|
822
904
|
}
|