swallowkit 1.0.0-beta.55 → 1.0.0-beta.57
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/add-auth.d.ts.map +1 -1
- package/dist/cli/commands/add-auth.js +154 -157
- package/dist/cli/commands/add-auth.js.map +1 -1
- package/dist/cli/commands/init.d.ts +5 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +178 -25
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts +7 -2
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +113 -123
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +127 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/core/config.d.ts +1 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +8 -4
- package/dist/core/config.js.map +1 -1
- package/dist/core/operations/auth-operations.d.ts +57 -0
- package/dist/core/operations/auth-operations.d.ts.map +1 -0
- package/dist/core/operations/auth-operations.js +269 -0
- package/dist/core/operations/auth-operations.js.map +1 -0
- package/dist/core/operations/create-model.d.ts.map +1 -1
- package/dist/core/operations/create-model.js +23 -13
- package/dist/core/operations/create-model.js.map +1 -1
- package/dist/core/operations/file-session.d.ts +56 -0
- package/dist/core/operations/file-session.d.ts.map +1 -0
- package/dist/core/operations/file-session.js +203 -0
- package/dist/core/operations/file-session.js.map +1 -0
- package/dist/core/operations/provision-operations.d.ts +52 -0
- package/dist/core/operations/provision-operations.d.ts.map +1 -0
- package/dist/core/operations/provision-operations.js +211 -0
- package/dist/core/operations/provision-operations.js.map +1 -0
- package/dist/core/operations/runtime.d.ts +5 -0
- package/dist/core/operations/runtime.d.ts.map +1 -1
- package/dist/core/operations/runtime.js +23 -0
- package/dist/core/operations/runtime.js.map +1 -1
- package/dist/core/operations/scaffold-apply.d.ts +21 -0
- package/dist/core/operations/scaffold-apply.d.ts.map +1 -0
- package/dist/core/operations/scaffold-apply.js +57 -0
- package/dist/core/operations/scaffold-apply.js.map +1 -0
- package/dist/core/operations/scaffold-machine.d.ts.map +1 -1
- package/dist/core/operations/scaffold-machine.js +1 -13
- package/dist/core/operations/scaffold-machine.js.map +1 -1
- package/dist/core/operations/scaffold-plan.d.ts +43 -0
- package/dist/core/operations/scaffold-plan.d.ts.map +1 -0
- package/dist/core/operations/scaffold-plan.js +187 -0
- package/dist/core/operations/scaffold-plan.js.map +1 -0
- package/dist/core/project/artifacts.d.ts +58 -0
- package/dist/core/project/artifacts.d.ts.map +1 -0
- package/dist/core/project/artifacts.js +179 -0
- package/dist/core/project/artifacts.js.map +1 -0
- package/dist/core/project/boundaries.d.ts +50 -0
- package/dist/core/project/boundaries.d.ts.map +1 -0
- package/dist/core/project/boundaries.js +117 -0
- package/dist/core/project/boundaries.js.map +1 -0
- package/dist/core/project/drift.d.ts +29 -0
- package/dist/core/project/drift.d.ts.map +1 -0
- package/dist/core/project/drift.js +188 -0
- package/dist/core/project/drift.js.map +1 -0
- package/dist/core/project/infra.d.ts +49 -0
- package/dist/core/project/infra.d.ts.map +1 -0
- package/dist/core/project/infra.js +167 -0
- package/dist/core/project/infra.js.map +1 -0
- package/dist/core/project/state.d.ts +15 -0
- package/dist/core/project/state.d.ts.map +1 -0
- package/dist/core/project/state.js +106 -0
- package/dist/core/project/state.js.map +1 -0
- package/dist/core/project/workflows.d.ts +43 -0
- package/dist/core/project/workflows.d.ts.map +1 -0
- package/dist/core/project/workflows.js +255 -0
- package/dist/core/project/workflows.js.map +1 -0
- package/dist/core/scaffold/auth-generator.d.ts.map +1 -1
- package/dist/core/scaffold/auth-generator.js +24 -27
- package/dist/core/scaffold/auth-generator.js.map +1 -1
- package/dist/core/scaffold/functions-generator.js +9 -9
- 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/dist/core/scaffold/nextjs-generator.js +6 -6
- package/dist/core/scaffold/ui-generator.js +19 -19
- package/dist/core/scaffold/ui-generator.js.map +1 -1
- package/dist/core/verify/index.d.ts +62 -0
- package/dist/core/verify/index.d.ts.map +1 -0
- package/dist/core/verify/index.js +343 -0
- package/dist/core/verify/index.js.map +1 -0
- package/dist/machine/contracts.d.ts +8 -0
- package/dist/machine/contracts.d.ts.map +1 -1
- package/dist/machine/errors.d.ts +4 -1
- package/dist/machine/errors.d.ts.map +1 -1
- package/dist/machine/errors.js +9 -1
- package/dist/machine/errors.js.map +1 -1
- package/dist/machine/index.d.ts.map +1 -1
- package/dist/machine/index.js +350 -16
- package/dist/machine/index.js.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +202 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/types/index.d.ts +14 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/__snapshots__/functions-generator.test.ts.snap +17 -17
- package/src/__tests__/__snapshots__/nextjs-generator.test.ts.snap +6 -6
- package/src/__tests__/__snapshots__/ui-generator.test.ts.snap +11 -11
- package/src/__tests__/agent-loop-phase34.test.ts +449 -0
- package/src/__tests__/file-session.test.ts +98 -0
- package/src/__tests__/init.test.ts +50 -0
- package/src/__tests__/mcp.test.ts +113 -5
- package/src/__tests__/plan-apply.test.ts +461 -0
- package/src/__tests__/scaffold.test.ts +2 -2
- package/src/cli/commands/add-auth.ts +164 -164
- package/src/cli/commands/init.ts +186 -24
- package/src/cli/commands/scaffold.ts +136 -130
- package/src/cli/index.ts +148 -2
- package/src/core/config.ts +5 -4
- package/src/core/operations/auth-operations.ts +353 -0
- package/src/core/operations/create-model.ts +25 -13
- package/src/core/operations/file-session.ts +222 -0
- package/src/core/operations/provision-operations.ts +297 -0
- package/src/core/operations/runtime.ts +23 -0
- package/src/core/operations/scaffold-apply.ts +106 -0
- package/src/core/operations/scaffold-machine.ts +2 -16
- package/src/core/operations/scaffold-plan.ts +215 -0
- package/src/core/project/artifacts.ts +205 -0
- package/src/core/project/boundaries.ts +170 -0
- package/src/core/project/drift.ts +202 -0
- package/src/core/project/infra.ts +196 -0
- package/src/core/project/state.ts +77 -0
- package/src/core/project/workflows.ts +249 -0
- package/src/core/scaffold/auth-generator.ts +24 -27
- package/src/core/scaffold/functions-generator.ts +9 -9
- package/src/core/scaffold/native-schema-generator.ts +152 -70
- package/src/core/scaffold/nextjs-generator.ts +6 -6
- package/src/core/scaffold/ui-generator.ts +19 -19
- package/src/core/verify/index.ts +392 -0
- package/src/machine/contracts.ts +16 -0
- package/src/machine/errors.ts +12 -1
- package/src/machine/index.ts +444 -15
- package/src/mcp/index.ts +198 -2
- package/src/types/index.ts +17 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow Docs & Agent Skills
|
|
3
|
+
*
|
|
4
|
+
* 同一のランブック コンテンツから 2 つの表面を生成する:
|
|
5
|
+
* - `.swallowkit/workflows/*.md` — Coding Agent 非依存の Markdown ランブック
|
|
6
|
+
* (AGENTS.md / CLAUDE.md / copilot-instructions.md から参照される)
|
|
7
|
+
* - `.github/skills/<name>/SKILL.md` — Agent Skills (agentskills.io open standard)。
|
|
8
|
+
* GitHub Copilot (VS Code / CLI / cloud agent) 等が name/description で自動発見し、
|
|
9
|
+
* タスクに関連するときだけ本文をロードする (progressive disclosure)。
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import * as fs from "fs";
|
|
13
|
+
import * as path from "path";
|
|
14
|
+
|
|
15
|
+
export interface WorkflowDoc {
|
|
16
|
+
/** `.swallowkit/workflows/` 配下のファイル名 */
|
|
17
|
+
fileName: string;
|
|
18
|
+
title: string;
|
|
19
|
+
content: string;
|
|
20
|
+
/**
|
|
21
|
+
* Agent Skills のスキル名 (ディレクトリ名と一致必須)。
|
|
22
|
+
* agentskills.io 仕様: 小文字英数字とハイフンのみ・64 文字以内。
|
|
23
|
+
* undefined の場合はスキルとして出力しない (README など)。
|
|
24
|
+
*/
|
|
25
|
+
skillName?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Agent Skills の description (最大 1024 文字)。
|
|
28
|
+
* 「何をするか」と「いつ使うか」の両方を書く。
|
|
29
|
+
*/
|
|
30
|
+
skillDescription?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function buildWorkflowDocs(runCmd: string): WorkflowDoc[] {
|
|
34
|
+
const addModel: WorkflowDoc = {
|
|
35
|
+
fileName: "add-model.md",
|
|
36
|
+
title: "Add a new model (autonomous loop)",
|
|
37
|
+
skillName: "swallowkit-add-model",
|
|
38
|
+
skillDescription:
|
|
39
|
+
"Add a new data model to a SwallowKit project and deterministically derive CRUD Functions, BFF routes, UI pages, and Cosmos DB infra from it using the plan/apply/verify loop. Use when asked to add a model, entity, resource, table, or a new CRUD feature.",
|
|
40
|
+
content: `# Workflow: Add a new model
|
|
41
|
+
|
|
42
|
+
Goal: add a data model and derive CRUD/BFF/UI/infra deterministically.
|
|
43
|
+
|
|
44
|
+
## Responsibility boundary
|
|
45
|
+
|
|
46
|
+
- **You author freely**: the model schema (\`shared/models/<name>.ts\`) and any business logic.
|
|
47
|
+
- **SwallowKit generates deterministically**: Functions CRUD, BFF routes, UI pages, OpenAPI, Bicep containers.
|
|
48
|
+
- Query the full contract: \`${runCmd} swallowkit machine inspect boundaries\`
|
|
49
|
+
|
|
50
|
+
## Steps
|
|
51
|
+
|
|
52
|
+
1. Create the model file (deterministic template):
|
|
53
|
+
\`${runCmd} swallowkit machine generate model <name> --overwrite never\`
|
|
54
|
+
2. Edit \`shared/models/<name>.ts\` — add domain fields (free-form authoring).
|
|
55
|
+
3. Compute the change plan (writes nothing):
|
|
56
|
+
\`${runCmd} swallowkit machine plan scaffold <name>\`
|
|
57
|
+
- \`status: "requires-human"\` → review \`data.conflicts\`; only proceed with \`--approve\` if overwriting is intended.
|
|
58
|
+
4. Apply:
|
|
59
|
+
\`${runCmd} swallowkit machine apply scaffold --plan <planId>\`
|
|
60
|
+
5. Verify:
|
|
61
|
+
\`${runCmd} swallowkit machine verify project\`
|
|
62
|
+
- \`summary.done: false\` → \`${runCmd} swallowkit machine explain failure\` and repair, then re-verify.
|
|
63
|
+
6. If \`dev-seeds/\` exists, update seed JSON for the new model.
|
|
64
|
+
|
|
65
|
+
## Terminal states
|
|
66
|
+
|
|
67
|
+
| status | meaning | next |
|
|
68
|
+
|--------|---------|------|
|
|
69
|
+
| complete | operation finished | continue |
|
|
70
|
+
| in-progress | more work remains (e.g. failing checks) | follow nextActions |
|
|
71
|
+
| blocked | prerequisite missing (e.g. stale plan) | re-plan |
|
|
72
|
+
| requires-human | approval needed | ask the human, or pass --approve when instructed |
|
|
73
|
+
| failed | operation failed | inspect error.details |
|
|
74
|
+
`,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const modifyModel: WorkflowDoc = {
|
|
78
|
+
fileName: "modify-model.md",
|
|
79
|
+
title: "Modify an existing model schema",
|
|
80
|
+
skillName: "swallowkit-modify-model",
|
|
81
|
+
skillDescription:
|
|
82
|
+
"Change an existing Zod model schema in a SwallowKit project and re-align every generated artifact (Functions, BFF, UI, infra) via drift inspection and plan/apply regeneration. Use when asked to add/remove/rename model fields, change validation rules, or when inspect drift reports schema-drift.",
|
|
83
|
+
content: `# Workflow: Modify an existing model
|
|
84
|
+
|
|
85
|
+
Goal: change a schema and re-align every generated artifact.
|
|
86
|
+
|
|
87
|
+
## Steps
|
|
88
|
+
|
|
89
|
+
1. Edit \`shared/models/<name>.ts\` (free-form authoring).
|
|
90
|
+
2. Check what drifted:
|
|
91
|
+
\`${runCmd} swallowkit machine inspect drift\`
|
|
92
|
+
- \`schema-drift\` findings list the models whose artifacts are stale.
|
|
93
|
+
3. Plan the regeneration:
|
|
94
|
+
\`${runCmd} swallowkit machine plan scaffold <name>\`
|
|
95
|
+
- Conflicts mean generated files were hand-edited after generation. Decide: keep the hand-edit (move the logic to an ai-authored file first) or overwrite with \`--approve\`.
|
|
96
|
+
4. Apply: \`${runCmd} swallowkit machine apply scaffold --plan <planId>\`
|
|
97
|
+
5. Verify: \`${runCmd} swallowkit machine verify project\`
|
|
98
|
+
|
|
99
|
+
## Guardrail
|
|
100
|
+
|
|
101
|
+
Never hand-edit managed artifacts to implement schema changes — always change the model
|
|
102
|
+
and regenerate. Hand-edits are detected as drift and block unattended overwrites.
|
|
103
|
+
`,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const verifyRepair: WorkflowDoc = {
|
|
107
|
+
fileName: "verify-and-repair.md",
|
|
108
|
+
title: "Verify and repair loop",
|
|
109
|
+
skillName: "swallowkit-verify-repair",
|
|
110
|
+
skillDescription:
|
|
111
|
+
"Converge a SwallowKit project to a verified state: run structure/drift/typecheck (and build/lint/test) checks, read evidence with explain failure, repair, and repeat. Use after any code or schema change, when verification fails, when the build/lint/tests are broken, or before finishing a task.",
|
|
112
|
+
content: `# Workflow: Verify and repair
|
|
113
|
+
|
|
114
|
+
Goal: converge the project to a verified state after any change.
|
|
115
|
+
|
|
116
|
+
## Loop
|
|
117
|
+
|
|
118
|
+
1. \`${runCmd} swallowkit machine verify project\`
|
|
119
|
+
- default checks: structure, drift, typecheck (+ project-specific checks from swallowkit.config \`verify.checks\`)
|
|
120
|
+
- opt-in heavier checks: \`--checks structure,drift,typecheck,build,lint,test\`
|
|
121
|
+
2. If \`summary.done\` is false:
|
|
122
|
+
- \`${runCmd} swallowkit machine explain failure\` → evidence (logTail, findings, violations) and suggestedActions per check.
|
|
123
|
+
- Repair:
|
|
124
|
+
- \`drift\` failures → follow each finding's \`repairAction\` (usually re-run plan/apply scaffold).
|
|
125
|
+
- \`structure\` failures → follow \`suggestedFix\` per violation.
|
|
126
|
+
- \`typecheck\`/\`build\`/\`lint\`/\`test\` failures → fix the code in ai-authored files.
|
|
127
|
+
3. Repeat until \`summary.done: true\`.
|
|
128
|
+
|
|
129
|
+
## Exit criteria
|
|
130
|
+
|
|
131
|
+
Stop and ask a human when:
|
|
132
|
+
- the same check fails 3 times in a row with the same evidence, or
|
|
133
|
+
- a repair would require overwriting hand-edited files (\`requires-human\`).
|
|
134
|
+
`,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const provision: WorkflowDoc = {
|
|
138
|
+
fileName: "provision.md",
|
|
139
|
+
title: "Provision Azure resources (plan/apply with approval gate)",
|
|
140
|
+
skillName: "swallowkit-provision",
|
|
141
|
+
skillDescription:
|
|
142
|
+
"Provision Azure resources (Static Web Apps, Functions, Cosmos DB) for a SwallowKit project using the two-phase plan/apply flow with a mandatory human approval gate. Use when asked to deploy infrastructure, provision Azure, create resource groups, or run Bicep deployments.",
|
|
143
|
+
content: `# Workflow: Provision Azure resources
|
|
144
|
+
|
|
145
|
+
Goal: deploy the Bicep infrastructure with an explicit human approval gate.
|
|
146
|
+
|
|
147
|
+
## Rules
|
|
148
|
+
|
|
149
|
+
- Provisioning **always** requires human approval — \`plan provision\` returns
|
|
150
|
+
\`status: "requires-human"\` and \`requiresApproval: true\` by design.
|
|
151
|
+
- Never pass \`--approve\` to \`apply provision\` unless a human explicitly
|
|
152
|
+
instructed you to provision in this session.
|
|
153
|
+
- Inspect infra locally first — no \`az\` calls are needed for inspection.
|
|
154
|
+
|
|
155
|
+
## Steps
|
|
156
|
+
|
|
157
|
+
1. Inspect the infrastructure definition (pure local Bicep analysis):
|
|
158
|
+
\`${runCmd} swallowkit machine inspect infra\`
|
|
159
|
+
- review parameters, modules, outputs, and container wiring warnings.
|
|
160
|
+
2. Compute the provisioning plan (no resources are created):
|
|
161
|
+
\`${runCmd} swallowkit machine plan provision -g <resource-group> --location <region>\`
|
|
162
|
+
- optional: \`--what-if\` to include an \`az deployment ... what-if\` preview.
|
|
163
|
+
- the plan lists the exact \`az\` commands that would run.
|
|
164
|
+
3. Present the plan to the human and wait for explicit approval.
|
|
165
|
+
4. Only after approval:
|
|
166
|
+
\`${runCmd} swallowkit machine apply provision --plan <planId> --approve\`
|
|
167
|
+
5. Verify the deployment outputs and report them.
|
|
168
|
+
|
|
169
|
+
## Terminal states
|
|
170
|
+
|
|
171
|
+
\`apply provision\` without \`--approve\` fails with \`approval-required\` —
|
|
172
|
+
this is the guardrail working as intended, not an error to work around.
|
|
173
|
+
`,
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const index: WorkflowDoc = {
|
|
177
|
+
fileName: "README.md",
|
|
178
|
+
title: "SwallowKit agent workflows",
|
|
179
|
+
content: `# SwallowKit Agent Workflows
|
|
180
|
+
|
|
181
|
+
Agent-agnostic runbooks for autonomous development loops. Machine-readable
|
|
182
|
+
interfaces (\`swallowkit machine ...\` / MCP \`swallowkit_*\` tools) return a
|
|
183
|
+
\`status\` terminal state and \`nextActions\`, so agents can chain steps without
|
|
184
|
+
parsing human-oriented output.
|
|
185
|
+
|
|
186
|
+
These runbooks are also installed as Agent Skills (agentskills.io format)
|
|
187
|
+
under \`.github/skills/\` so skills-aware agents load them automatically.
|
|
188
|
+
|
|
189
|
+
| Workflow | File | Skill |
|
|
190
|
+
|----------|------|-------|
|
|
191
|
+
| Add a new model | [add-model.md](./add-model.md) | \`swallowkit-add-model\` |
|
|
192
|
+
| Modify an existing model | [modify-model.md](./modify-model.md) | \`swallowkit-modify-model\` |
|
|
193
|
+
| Verify and repair | [verify-and-repair.md](./verify-and-repair.md) | \`swallowkit-verify-repair\` |
|
|
194
|
+
| Provision Azure resources | [provision.md](./provision.md) | \`swallowkit-provision\` |
|
|
195
|
+
|
|
196
|
+
## Responsibility boundary (summary)
|
|
197
|
+
|
|
198
|
+
- **ai-authored** (write freely): \`shared/models/\`, business logic, custom pages/components, tests.
|
|
199
|
+
- **deterministic** (never hand-edit): artifacts generated by SwallowKit (see \`.swallowkit/artifacts.json\`) and \`.swallowkit/\` metadata. Change models/config and regenerate via plan/apply.
|
|
200
|
+
- **shared** (edit outside markers): \`infra/main.bicep\`, \`functions/function_app.py\`, \`shared/index.ts\`.
|
|
201
|
+
|
|
202
|
+
Full machine-readable contract: \`${runCmd} swallowkit machine inspect boundaries\`
|
|
203
|
+
`,
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
return [index, addModel, modifyModel, verifyRepair, provision];
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export const WORKFLOWS_DIR = path.join(".swallowkit", "workflows");
|
|
210
|
+
export const SKILLS_DIR = path.join(".github", "skills");
|
|
211
|
+
|
|
212
|
+
/** `.swallowkit/workflows/*.md` を書き出し、書き込んだ相対パスを返す。 */
|
|
213
|
+
export function writeWorkflowDocs(projectDir: string, runCmd: string): string[] {
|
|
214
|
+
const dir = path.join(projectDir, WORKFLOWS_DIR);
|
|
215
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
216
|
+
const written: string[] = [];
|
|
217
|
+
for (const doc of buildWorkflowDocs(runCmd)) {
|
|
218
|
+
fs.writeFileSync(path.join(dir, doc.fileName), doc.content, "utf-8");
|
|
219
|
+
written.push(path.posix.join(".swallowkit", "workflows", doc.fileName));
|
|
220
|
+
}
|
|
221
|
+
return written;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** Agent Skills (agentskills.io) の SKILL.md コンテンツを組み立てる。 */
|
|
225
|
+
export function buildAgentSkills(runCmd: string): Array<{ skillName: string; content: string }> {
|
|
226
|
+
return buildWorkflowDocs(runCmd)
|
|
227
|
+
.filter((doc): doc is WorkflowDoc & { skillName: string; skillDescription: string } =>
|
|
228
|
+
Boolean(doc.skillName && doc.skillDescription)
|
|
229
|
+
)
|
|
230
|
+
.map((doc) => ({
|
|
231
|
+
skillName: doc.skillName,
|
|
232
|
+
content: `---\nname: ${doc.skillName}\ndescription: ${doc.skillDescription}\nmetadata:\n generator: swallowkit\n---\n\n${doc.content}`,
|
|
233
|
+
}));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* `.github/skills/<name>/SKILL.md` を書き出し、書き込んだ相対パスを返す。
|
|
238
|
+
* agentskills.io 仕様: ディレクトリ名は frontmatter の name と一致必須。
|
|
239
|
+
*/
|
|
240
|
+
export function writeAgentSkills(projectDir: string, runCmd: string): string[] {
|
|
241
|
+
const written: string[] = [];
|
|
242
|
+
for (const skill of buildAgentSkills(runCmd)) {
|
|
243
|
+
const dir = path.join(projectDir, SKILLS_DIR, skill.skillName);
|
|
244
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
245
|
+
fs.writeFileSync(path.join(dir, "SKILL.md"), skill.content, "utf-8");
|
|
246
|
+
written.push(path.posix.join(".github", "skills", skill.skillName, "SKILL.md"));
|
|
247
|
+
}
|
|
248
|
+
return written;
|
|
249
|
+
}
|
|
@@ -20,6 +20,7 @@ export function generateNamedAuthRouterTS(config: NormalizedAuthConfig): string
|
|
|
20
20
|
})).join("\n");
|
|
21
21
|
const verifiers = external.map(([name]) => { const id = safeIdentifier(name); return `${JSON.stringify(name)}: async (token: string) => { try { return await verify_${id}(token); } catch (e) { if (e instanceof Error_${id}) throw new AuthError(e.status, e.message); throw e; } }`; }).join(",\n ");
|
|
22
22
|
return `import type { HttpRequest, HttpResponseInit } from '@azure/functions';
|
|
23
|
+
/* eslint-disable @typescript-eslint/no-explicit-any -- generated multi-scheme router works with dynamically shaped principals */
|
|
23
24
|
${imports}
|
|
24
25
|
export type AuthPrincipal = { subject: string; scheme: string; issuer: string; roles: string[]; claims: Record<string, unknown>; /** @deprecated */ userId?: string; /** @deprecated */ userDetails?: string };
|
|
25
26
|
export class AuthError extends Error { constructor(public status: 401 | 403 | 503, message: string) { super(message); } }
|
|
@@ -283,6 +284,7 @@ export function generateAuthFunctionsTS(
|
|
|
283
284
|
'SELECT * FROM ${config.userTable} WHERE ${config.loginIdColumn} = ?',
|
|
284
285
|
[body.loginId]
|
|
285
286
|
);
|
|
287
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- mysql2 rows are dynamically shaped
|
|
286
288
|
const users = rows as any[];`
|
|
287
289
|
: provider === "postgres"
|
|
288
290
|
? `const result = await ${connVar}.query(
|
|
@@ -360,7 +362,7 @@ app.http('auth-login', {
|
|
|
360
362
|
} finally {
|
|
361
363
|
${cleanup}
|
|
362
364
|
}
|
|
363
|
-
} catch (error
|
|
365
|
+
} catch (error) {
|
|
364
366
|
context.error('Login error:', error);
|
|
365
367
|
return { status: 500, jsonBody: { error: 'Login failed' } };
|
|
366
368
|
}
|
|
@@ -998,7 +1000,7 @@ export async function POST(request: NextRequest) {
|
|
|
998
1000
|
maxAge: 86400,
|
|
999
1001
|
});
|
|
1000
1002
|
return response;
|
|
1001
|
-
} catch (error
|
|
1003
|
+
} catch (error) {
|
|
1002
1004
|
console.error('[BFF] Login error:', error);
|
|
1003
1005
|
return NextResponse.json({ error: 'Login failed' }, { status: 500 });
|
|
1004
1006
|
}
|
|
@@ -1043,7 +1045,7 @@ export async function GET() {
|
|
|
1043
1045
|
|
|
1044
1046
|
const data = await result.json();
|
|
1045
1047
|
return NextResponse.json(data);
|
|
1046
|
-
} catch (error
|
|
1048
|
+
} catch (error) {
|
|
1047
1049
|
console.error('[BFF] Auth/me error:', error);
|
|
1048
1050
|
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
|
|
1049
1051
|
}
|
|
@@ -1212,7 +1214,7 @@ export default function LoginPage() {
|
|
|
1212
1214
|
export function generateAuthContext(): string {
|
|
1213
1215
|
return `'use client';
|
|
1214
1216
|
|
|
1215
|
-
import { createContext, useContext, useEffect, useState,
|
|
1217
|
+
import { createContext, useContext, useEffect, useState, ReactNode } from 'react';
|
|
1216
1218
|
import { useRouter } from 'next/navigation';
|
|
1217
1219
|
|
|
1218
1220
|
interface AuthUser {
|
|
@@ -1239,26 +1241,21 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|
|
1239
1241
|
const [loading, setLoading] = useState(true);
|
|
1240
1242
|
const router = useRouter();
|
|
1241
1243
|
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1244
|
+
useEffect(() => {
|
|
1245
|
+
let cancelled = false;
|
|
1246
|
+
fetch('/api/auth/me')
|
|
1247
|
+
.then((res) => (res.ok ? res.json() : null))
|
|
1248
|
+
.catch(() => null)
|
|
1249
|
+
.then((data) => {
|
|
1250
|
+
if (cancelled) return;
|
|
1247
1251
|
setUser(data);
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
} finally {
|
|
1254
|
-
setLoading(false);
|
|
1255
|
-
}
|
|
1252
|
+
setLoading(false);
|
|
1253
|
+
});
|
|
1254
|
+
return () => {
|
|
1255
|
+
cancelled = true;
|
|
1256
|
+
};
|
|
1256
1257
|
}, []);
|
|
1257
1258
|
|
|
1258
|
-
useEffect(() => {
|
|
1259
|
-
fetchUser();
|
|
1260
|
-
}, [fetchUser]);
|
|
1261
|
-
|
|
1262
1259
|
const login = async (loginId: string, password: string) => {
|
|
1263
1260
|
try {
|
|
1264
1261
|
const res = await fetch('/api/auth/login', {
|
|
@@ -1311,7 +1308,7 @@ export function useAuth() {
|
|
|
1311
1308
|
// ============================================================
|
|
1312
1309
|
|
|
1313
1310
|
export function generateBFFCallFunctionWithAuth(): string {
|
|
1314
|
-
return `import {
|
|
1311
|
+
return `import { NextResponse } from 'next/server';
|
|
1315
1312
|
import { headers } from 'next/headers';
|
|
1316
1313
|
import { z } from 'zod/v4';
|
|
1317
1314
|
|
|
@@ -1335,13 +1332,13 @@ function getFunctionsKeyHeaders(functionsBaseUrl: string): Record<string, string
|
|
|
1335
1332
|
throw new Error('BACKEND_FUNCTIONS_KEY is required for non-local Functions calls');
|
|
1336
1333
|
}
|
|
1337
1334
|
|
|
1338
|
-
interface CallFunctionConfig<TInput =
|
|
1335
|
+
interface CallFunctionConfig<TInput = unknown, TOutput = unknown> {
|
|
1339
1336
|
/** HTTP メソッド */
|
|
1340
1337
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
1341
1338
|
/** Azure Functions のパス (例: '/api/todo', '/api/todo/123') */
|
|
1342
1339
|
path: string;
|
|
1343
1340
|
/** リクエストボディ (POST/PUT 用) */
|
|
1344
|
-
body?:
|
|
1341
|
+
body?: unknown;
|
|
1345
1342
|
/** 入力バリデーション用 Zod スキーマ (省略時はバリデーションなし) */
|
|
1346
1343
|
inputSchema?: z.ZodSchema<TInput>;
|
|
1347
1344
|
/** 出力バリデーション用 Zod スキーマ (省略時はそのまま返す) */
|
|
@@ -1350,7 +1347,7 @@ interface CallFunctionConfig<TInput = any, TOutput = any> {
|
|
|
1350
1347
|
successStatus?: number;
|
|
1351
1348
|
}
|
|
1352
1349
|
|
|
1353
|
-
export async function callFunction<TInput =
|
|
1350
|
+
export async function callFunction<TInput = unknown, TOutput = unknown>(
|
|
1354
1351
|
config: CallFunctionConfig<TInput, TOutput>
|
|
1355
1352
|
): Promise<NextResponse> {
|
|
1356
1353
|
const { method, path, body, inputSchema, responseSchema, successStatus = 200 } = config;
|
|
@@ -1428,10 +1425,10 @@ export async function callFunction<TInput = any, TOutput = any>(
|
|
|
1428
1425
|
}
|
|
1429
1426
|
|
|
1430
1427
|
return NextResponse.json(data, { status: successStatus });
|
|
1431
|
-
} catch (error
|
|
1428
|
+
} catch (error) {
|
|
1432
1429
|
console.error(\`[BFF] Error:\`, error);
|
|
1433
1430
|
return NextResponse.json(
|
|
1434
|
-
{ error: error.message
|
|
1431
|
+
{ error: error instanceof Error ? error.message : 'Internal server error' },
|
|
1435
1432
|
{ status: 500 }
|
|
1436
1433
|
);
|
|
1437
1434
|
}
|
|
@@ -83,7 +83,7 @@ app.http('${modelCamel}-get-all', {
|
|
|
83
83
|
],
|
|
84
84
|
handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
|
|
85
85
|
try {${readGuard}
|
|
86
|
-
const documents = context.extraInputs.get('cosmosInput') as
|
|
86
|
+
const documents = context.extraInputs.get('cosmosInput') as unknown[];
|
|
87
87
|
|
|
88
88
|
if (!documents || !Array.isArray(documents)) {
|
|
89
89
|
return { status: 200, jsonBody: [] };
|
|
@@ -119,7 +119,7 @@ app.http('${modelCamel}-create', {
|
|
|
119
119
|
],
|
|
120
120
|
handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
|
|
121
121
|
try {${writeGuard}
|
|
122
|
-
const body = await request.json() as
|
|
122
|
+
const body = await request.json() as Record<string, unknown>;
|
|
123
123
|
|
|
124
124
|
const { id, createdAt, updatedAt, ...userData } = body;
|
|
125
125
|
const now = new Date().toISOString();
|
|
@@ -258,12 +258,12 @@ app.http('${modelCamel}-update', {
|
|
|
258
258
|
return { status: 400, jsonBody: { error: 'ID is required' } };
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
-
const existingDocument = context.extraInputs.get('cosmosInput') as
|
|
261
|
+
const existingDocument = context.extraInputs.get('cosmosInput') as Record<string, unknown> | undefined;
|
|
262
262
|
if (!existingDocument) {
|
|
263
263
|
return { status: 404, jsonBody: { error: 'Item not found' } };
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
-
const body = await request.json() as
|
|
266
|
+
const body = await request.json() as Record<string, unknown>;
|
|
267
267
|
const { createdAt, updatedAt, ...userData } = body;
|
|
268
268
|
|
|
269
269
|
const dataWithManagedFields = {
|
|
@@ -315,7 +315,7 @@ app.http('${modelCamel}-update', {
|
|
|
315
315
|
}
|
|
316
316
|
|
|
317
317
|
const existingDocument = resources[0];
|
|
318
|
-
const body = await request.json() as
|
|
318
|
+
const body = await request.json() as Record<string, unknown>;
|
|
319
319
|
const { createdAt, updatedAt, ...userData } = body;
|
|
320
320
|
|
|
321
321
|
const dataWithManagedFields = {
|
|
@@ -364,8 +364,8 @@ app.http('${modelCamel}-delete', {
|
|
|
364
364
|
context.log(\`Deleted item \${id} from \${containerName}\`);
|
|
365
365
|
|
|
366
366
|
return { status: 204 };
|
|
367
|
-
} catch (error
|
|
368
|
-
if (error.code === 404) {
|
|
367
|
+
} catch (error) {
|
|
368
|
+
if ((error as { code?: number }).code === 404) {
|
|
369
369
|
return { status: 404, jsonBody: { error: 'Item not found' } };
|
|
370
370
|
}
|
|
371
371
|
${authCatchBlock} context.error(\`Error deleting item from \${containerName}:\`, error);
|
|
@@ -405,8 +405,8 @@ app.http('${modelCamel}-delete', {
|
|
|
405
405
|
context.log(\`Deleted item \${id} from \${containerName}\`);
|
|
406
406
|
|
|
407
407
|
return { status: 204 };
|
|
408
|
-
} catch (error
|
|
409
|
-
if (error.code === 404) {
|
|
408
|
+
} catch (error) {
|
|
409
|
+
if ((error as { code?: number }).code === 404) {
|
|
410
410
|
return { status: 404, jsonBody: { error: 'Item not found' } };
|
|
411
411
|
}
|
|
412
412
|
${authCatchBlock} context.error(\`Error deleting item from \${containerName}:\`, error);
|