add config schema generation for IDE autocomplete

- scripts/gen-config-schema.ts: generates JSON schema + TypeScript types from ClawdbotSchema
- schemas/clawdbot.schema.json: for $schema reference in config files
- schemas/clawdbot.d.ts: full types without digging into code
- pnpm schema:gen / schema:check scripts (mirrors protocol:check pattern)
- CI: add schema:check to catch drift
This commit is contained in:
CJ Winslow 2026-01-26 23:41:16 -08:00
parent 9daa846457
commit f15409bc1e
6 changed files with 16209 additions and 2 deletions

View File

@ -83,6 +83,9 @@ jobs:
- runtime: node
task: protocol
command: pnpm protocol:check
- runtime: node
task: schema
command: pnpm schema:check
- runtime: node
task: format
command: pnpm format
@ -207,6 +210,9 @@ jobs:
- runtime: node
task: protocol
command: pnpm protocol:check
- runtime: node
task: schema
command: pnpm schema:check
steps:
- name: Checkout
uses: actions/checkout@v4

View File

@ -139,6 +139,8 @@
"protocol:gen": "node --import tsx scripts/protocol-gen.ts",
"protocol:gen:swift": "node --import tsx scripts/protocol-gen-swift.ts",
"protocol:check": "pnpm protocol:gen && pnpm protocol:gen:swift && git diff --exit-code -- dist/protocol.schema.json apps/macos/Sources/ClawdbotProtocol/GatewayModels.swift",
"schema:gen": "node --import tsx scripts/gen-config-schema.ts",
"schema:check": "pnpm schema:gen && git diff --exit-code -- schemas/",
"canvas:a2ui:bundle": "bash scripts/bundle-a2ui.sh",
"check:loc": "node --import tsx scripts/check-ts-max-loc.ts --max 500"
},
@ -223,6 +225,7 @@
"@typescript/native-preview": "7.0.0-dev.20260124.1",
"@vitest/coverage-v8": "^4.0.18",
"docx-preview": "^0.3.7",
"json-schema-to-typescript": "^15.0.4",
"lit": "^3.3.2",
"lucide": "^0.563.0",
"ollama": "^0.6.3",

63
pnpm-lock.yaml generated
View File

@ -215,6 +215,9 @@ importers:
docx-preview:
specifier: ^0.3.7
version: 0.3.7
json-schema-to-typescript:
specifier: ^15.0.4
version: 15.0.4
lit:
specifier: ^3.3.2
version: 3.3.2
@ -522,6 +525,10 @@ packages:
zod:
optional: true
'@apidevtools/json-schema-ref-parser@11.9.3':
resolution: {integrity: sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ==}
engines: {node: '>= 16'}
'@aws-crypto/crc32@5.2.0':
resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==}
engines: {node: '>=16.0.0'}
@ -1256,6 +1263,9 @@ packages:
'@js-sdsl/ordered-map@4.4.2':
resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==}
'@jsdevtools/ono@7.1.3':
resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==}
'@keyv/bigmap@1.3.1':
resolution: {integrity: sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==}
engines: {node: '>= 18'}
@ -2704,12 +2714,18 @@ packages:
'@types/http-errors@2.0.5':
resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==}
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
'@types/jsonwebtoken@9.0.10':
resolution: {integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==}
'@types/linkify-it@5.0.0':
resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==}
'@types/lodash@4.17.23':
resolution: {integrity: sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA==}
'@types/long@4.0.2':
resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==}
@ -3975,6 +3991,10 @@ packages:
js-tokens@9.0.1:
resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
js-yaml@4.1.1:
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
hasBin: true
jsbn@0.1.1:
resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==}
@ -3989,6 +4009,11 @@ packages:
resolution: {integrity: sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==}
engines: {node: '>=16'}
json-schema-to-typescript@15.0.4:
resolution: {integrity: sha512-Su9oK8DR4xCmDsLlyvadkXzX6+GGXJpbhwoLtOGArAG61dvbW4YQmSEno2y66ahpIdmLMg6YUf/QHLgiwvkrHQ==}
engines: {node: '>=16.0.0'}
hasBin: true
json-schema-traverse@0.4.1:
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
@ -4716,6 +4741,11 @@ packages:
resolution: {integrity: sha512-d+JFcLM17njZaOLkv6SCev7uoLaBtfK86vMUXhW1Z4glPWh4jozno9APvW/XKFJ3CCxVoC7OL38BqRydtu5nGg==}
engines: {node: '>=12'}
prettier@3.8.1:
resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==}
engines: {node: '>=14'}
hasBin: true
pretty-bytes@6.1.1:
resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==}
engines: {node: ^14.13.1 || >=16.0.0}
@ -5582,6 +5612,12 @@ snapshots:
optionalDependencies:
zod: 4.3.6
'@apidevtools/json-schema-ref-parser@11.9.3':
dependencies:
'@jsdevtools/ono': 7.1.3
'@types/json-schema': 7.0.15
js-yaml: 4.1.1
'@aws-crypto/crc32@5.2.0':
dependencies:
'@aws-crypto/util': 5.2.0
@ -6824,6 +6860,8 @@ snapshots:
'@js-sdsl/ordered-map@4.4.2': {}
'@jsdevtools/ono@7.1.3': {}
'@keyv/bigmap@1.3.1(keyv@5.6.0)':
dependencies:
hashery: 1.4.0
@ -8486,6 +8524,8 @@ snapshots:
'@types/http-errors@2.0.5': {}
'@types/json-schema@7.0.15': {}
'@types/jsonwebtoken@9.0.10':
dependencies:
'@types/ms': 2.1.0
@ -8493,6 +8533,8 @@ snapshots:
'@types/linkify-it@5.0.0': {}
'@types/lodash@4.17.23': {}
'@types/long@4.0.2': {}
'@types/markdown-it@14.1.2':
@ -10056,6 +10098,10 @@ snapshots:
js-tokens@9.0.1: {}
js-yaml@4.1.1:
dependencies:
argparse: 2.0.1
jsbn@0.1.1: {}
json-bigint@1.0.0:
@ -10069,6 +10115,18 @@ snapshots:
'@babel/runtime': 7.28.6
ts-algebra: 2.0.0
json-schema-to-typescript@15.0.4:
dependencies:
'@apidevtools/json-schema-ref-parser': 11.9.3
'@types/json-schema': 7.0.15
'@types/lodash': 4.17.23
is-glob: 4.0.3
js-yaml: 4.1.1
lodash: 4.17.23
minimist: 1.2.8
prettier: 3.8.1
tinyglobby: 0.2.15
json-schema-traverse@0.4.1: {}
json-schema-traverse@1.0.0: {}
@ -10389,8 +10447,7 @@ snapshots:
dependencies:
brace-expansion: 2.0.2
minimist@1.2.8:
optional: true
minimist@1.2.8: {}
minipass@7.1.2: {}
@ -10820,6 +10877,8 @@ snapshots:
postgres@3.4.8: {}
prettier@3.8.1: {}
pretty-bytes@6.1.1:
optional: true

2820
schemas/clawdbot.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

13269
schemas/clawdbot.schema.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,50 @@
#!/usr/bin/env bun
/**
* Generates schemas/clawdbot.schema.json and schemas/clawdbot.d.ts
* from the zod schema source.
*
* Usage: bun scripts/gen-config-schema.ts
*/
import { writeFile, mkdir } from "node:fs/promises";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { compile } from "json-schema-to-typescript";
import { ClawdbotSchema } from "../src/config/zod-schema.js";
const __dirname = dirname(fileURLToPath(import.meta.url));
const rootDir = join(__dirname, "..");
const schemasDir = join(rootDir, "schemas");
async function main() {
await mkdir(schemasDir, { recursive: true });
// Generate JSON schema from zod
const jsonSchema = ClawdbotSchema.toJSONSchema({
target: "draft-07",
unrepresentable: "any",
});
const schemaPath = join(schemasDir, "clawdbot.schema.json");
await writeFile(schemaPath, JSON.stringify(jsonSchema, null, 2));
console.log(`Wrote ${schemaPath}`);
// Generate TypeScript types from JSON schema
const dts = await compile(jsonSchema as Record<string, unknown>, "ClawdbotConfig", {
bannerComment: `/* eslint-disable */
/**
* This file was automatically generated by json-schema-to-typescript.
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
* and run json-schema-to-typescript to regenerate this file.
*/`,
additionalProperties: false,
});
const dtsPath = join(schemasDir, "clawdbot.d.ts");
await writeFile(dtsPath, dts);
console.log(`Wrote ${dtsPath}`);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});