Skip to content

Commit 05c38f0

Browse files
committed
feat: Add Zod schemas for internationalization, responsive design, sharing, and view configurations
- Introduced I18nObjectSchema and I18nLabelSchema for structured internationalization labels. - Created ResponsiveConfigSchema for responsive layout configurations, including breakpoints and performance settings. - Implemented SharingConfigSchema and EmbedConfigSchema for public link sharing and iframe embedding configurations. - Developed comprehensive ListViewSchema and FormViewSchema for defining list and form views, including various configurations like pagination, grouping, and user actions.
1 parent 69de6ce commit 05c38f0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+12223
-21
lines changed

packages/spec/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
"gen:schema": "tsx scripts/build-schemas.ts",
102102
"gen:openapi": "tsx scripts/build-openapi.ts",
103103
"gen:docs": "tsx scripts/build-docs.ts",
104+
"gen:skill-refs": "tsx scripts/build-skill-references.ts",
104105
"analyze": "tsx scripts/analyze-bundle-size.ts",
105106
"gen:sbom": "tsx scripts/generate-sbom.ts",
106107
"test": "vitest run",
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
/**
4+
* Build Skill References
5+
*
6+
* Copies Zod schema source files into each skill's `references/zod/` folder
7+
* so that AI agents can read the precise type definitions directly — without
8+
* needing access to the monorepo source tree.
9+
*
10+
* Usage: tsx scripts/build-skill-references.ts
11+
*
12+
* The script:
13+
* 1. Reads a declarative mapping of { skill → core zod files }
14+
* 2. Recursively resolves local `import … from` dependencies
15+
* 3. Copies all resolved files into `skills/{name}/references/zod/`
16+
* preserving the category-based directory structure
17+
* 4. Generates an `_index.md` per skill listing all bundled schemas
18+
*/
19+
20+
import fs from 'fs';
21+
import path from 'path';
22+
23+
// ── Paths ────────────────────────────────────────────────────────────────────
24+
25+
const REPO_ROOT = path.resolve(__dirname, '../../..');
26+
const SPEC_SRC = path.resolve(__dirname, '../src');
27+
const SKILLS_DIR = path.resolve(REPO_ROOT, 'skills');
28+
29+
// ── Skill → Zod file mapping ────────────────────────────────────────────────
30+
// Paths are relative to packages/spec/src/ (category/file.zod.ts)
31+
32+
const SKILL_MAP: Record<string, string[]> = {
33+
'objectstack-schema': [
34+
'data/field.zod.ts',
35+
'data/object.zod.ts',
36+
'data/validation.zod.ts',
37+
],
38+
'objectstack-ai': [
39+
'ai/agent.zod.ts',
40+
'ai/tool.zod.ts',
41+
'ai/skill.zod.ts',
42+
'ai/rag-pipeline.zod.ts',
43+
'ai/model-registry.zod.ts',
44+
],
45+
'objectstack-api': [
46+
'api/endpoint.zod.ts',
47+
'api/auth.zod.ts',
48+
'api/realtime.zod.ts',
49+
'api/rest-server.zod.ts',
50+
],
51+
'objectstack-automation': [
52+
'automation/flow.zod.ts',
53+
'automation/workflow.zod.ts',
54+
'automation/trigger-registry.zod.ts',
55+
'automation/approval.zod.ts',
56+
'automation/state-machine.zod.ts',
57+
],
58+
'objectstack-ui': [
59+
'ui/view.zod.ts',
60+
'ui/app.zod.ts',
61+
'ui/dashboard.zod.ts',
62+
'ui/chart.zod.ts',
63+
'ui/action.zod.ts',
64+
],
65+
};
66+
67+
// ── Import resolver ──────────────────────────────────────────────────────────
68+
69+
/**
70+
* Extract local imports from a .zod.ts file.
71+
* Returns paths relative to SPEC_SRC (e.g. "shared/identifiers.zod.ts").
72+
* Ignores external imports (zod, node modules).
73+
*/
74+
function extractLocalImports(filePath: string): string[] {
75+
const content = fs.readFileSync(filePath, 'utf-8');
76+
const imports: string[] = [];
77+
// Match: import { ... } from './foo.zod' or '../shared/bar.zod'
78+
const re = /^import\s+.*\s+from\s+['"](\.[^'"]+)['"]/gm;
79+
let match: RegExpExecArray | null;
80+
while ((match = re.exec(content)) !== null) {
81+
const importSpec = match[1]; // e.g. '../shared/identifiers.zod'
82+
const dir = path.dirname(filePath);
83+
let resolved = path.resolve(dir, importSpec);
84+
// Append .ts if needed
85+
if (!resolved.endsWith('.ts')) {
86+
resolved += '.ts';
87+
}
88+
if (fs.existsSync(resolved)) {
89+
// Convert back to relative from SPEC_SRC
90+
const rel = path.relative(SPEC_SRC, resolved);
91+
imports.push(rel);
92+
}
93+
}
94+
return imports;
95+
}
96+
97+
/**
98+
* Recursively resolve all dependencies for a set of entry files.
99+
* Returns deduplicated set of all files (entries + transitive deps),
100+
* all relative to SPEC_SRC.
101+
*/
102+
function resolveAll(entryFiles: string[]): string[] {
103+
const visited = new Set<string>();
104+
const queue = [...entryFiles];
105+
106+
while (queue.length > 0) {
107+
const rel = queue.shift()!;
108+
if (visited.has(rel)) continue;
109+
visited.add(rel);
110+
111+
const abs = path.resolve(SPEC_SRC, rel);
112+
if (!fs.existsSync(abs)) {
113+
console.warn(` ⚠ File not found: ${rel}`);
114+
continue;
115+
}
116+
const deps = extractLocalImports(abs);
117+
for (const dep of deps) {
118+
if (!visited.has(dep)) {
119+
queue.push(dep);
120+
}
121+
}
122+
}
123+
124+
return [...visited].sort();
125+
}
126+
127+
// ── JSDoc description extractor ──────────────────────────────────────────────
128+
129+
/**
130+
* Extract a short description from a file's first JSDoc comment.
131+
* Takes only the first sentence/line. Falls back to exported schema names.
132+
*/
133+
function extractDescription(filePath: string): string {
134+
const content = fs.readFileSync(filePath, 'utf-8');
135+
// Try to grab the first top-level JSDoc comment
136+
const jsdocRe = /\/\*\*\s*\n([\s\S]*?)\*\//;
137+
const jsdocMatch = content.match(jsdocRe);
138+
if (jsdocMatch) {
139+
const lines = jsdocMatch[1]
140+
.split('\n')
141+
.map((line) => line.replace(/^\s*\*\s?/, '').trim())
142+
.filter((line) => line && !line.startsWith('@') && !line.startsWith('```'));
143+
// Take only the first non-empty line as a short description
144+
const firstLine = lines[0];
145+
if (firstLine && firstLine.length > 5) {
146+
// Strip leading markdown heading markers
147+
const clean = firstLine.replace(/^#+\s*/, '');
148+
// Truncate to first sentence or 120 chars
149+
const sentence = clean.split(/\.\s/)[0];
150+
return sentence.length > 120 ? sentence.slice(0, 117) + '...' : sentence;
151+
}
152+
}
153+
// Fallback: list exported schema names
154+
const exports: string[] = [];
155+
const exportRe = /export\s+const\s+(\w+Schema|\w+)\s*(?:[:=])/g;
156+
let m: RegExpExecArray | null;
157+
while ((m = exportRe.exec(content)) !== null) {
158+
exports.push(m[1]);
159+
}
160+
if (exports.length > 0) return `Exports: ${exports.slice(0, 5).join(', ')}`;
161+
return '';
162+
}
163+
164+
// ── Index generator ──────────────────────────────────────────────────────────
165+
166+
function generateIndex(skillName: string, coreFiles: string[], allFiles: string[]): string {
167+
const coreSet = new Set(coreFiles);
168+
const lines: string[] = [
169+
`# ${skillName} — Zod Schema Reference`,
170+
'',
171+
'> **Auto-generated** by `build-skill-references.ts`.',
172+
'> These files are copied from `packages/spec/src/` for AI agent consumption.',
173+
'> Do not edit — re-run `pnpm --filter @objectstack/spec run gen:skill-refs` to update.',
174+
'',
175+
'## Core Schemas',
176+
'',
177+
];
178+
179+
for (const f of allFiles.filter((f) => coreSet.has(f))) {
180+
const desc = extractDescription(path.resolve(SPEC_SRC, f));
181+
lines.push(`- [\`${f}\`](./${f})${desc ? ` — ${desc}` : ''}`);
182+
}
183+
184+
const deps = allFiles.filter((f) => !coreSet.has(f));
185+
if (deps.length > 0) {
186+
lines.push('', '## Dependencies (auto-resolved)', '');
187+
for (const f of deps) {
188+
const desc = extractDescription(path.resolve(SPEC_SRC, f));
189+
lines.push(`- [\`${f}\`](./${f})${desc ? ` — ${desc}` : ''}`);
190+
}
191+
}
192+
193+
lines.push('');
194+
return lines.join('\n');
195+
}
196+
197+
// ── Main ─────────────────────────────────────────────────────────────────────
198+
199+
function main() {
200+
console.log('🔗 Building skill Zod references...\n');
201+
202+
let totalFiles = 0;
203+
204+
for (const [skillName, coreFiles] of Object.entries(SKILL_MAP)) {
205+
const skillDir = path.resolve(SKILLS_DIR, skillName);
206+
if (!fs.existsSync(skillDir)) {
207+
console.warn(`⚠ Skill directory not found: ${skillName}, skipping`);
208+
continue;
209+
}
210+
211+
console.log(`📦 ${skillName}`);
212+
213+
// Resolve full dependency tree
214+
const allFiles = resolveAll(coreFiles);
215+
console.log(` ${coreFiles.length} core + ${allFiles.length - coreFiles.length} deps = ${allFiles.length} files`);
216+
217+
// Target directory
218+
const zodDir = path.resolve(skillDir, 'references/zod');
219+
220+
// Clean previous output
221+
if (fs.existsSync(zodDir)) {
222+
fs.rmSync(zodDir, { recursive: true });
223+
}
224+
225+
// Copy files preserving directory structure
226+
for (const rel of allFiles) {
227+
const src = path.resolve(SPEC_SRC, rel);
228+
const dest = path.resolve(zodDir, rel);
229+
fs.mkdirSync(path.dirname(dest), { recursive: true });
230+
fs.copyFileSync(src, dest);
231+
}
232+
233+
// Generate _index.md
234+
const indexContent = generateIndex(skillName, coreFiles, allFiles);
235+
fs.writeFileSync(path.resolve(zodDir, '_index.md'), indexContent);
236+
237+
totalFiles += allFiles.length;
238+
}
239+
240+
console.log(`\n✅ Done — ${totalFiles} files copied across ${Object.keys(SKILL_MAP).length} skills`);
241+
}
242+
243+
main();

skills/objectstack-ai/SKILL.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,16 @@ structuredOutput: {
343343

344344
## References
345345

346-
- **[Agent & Skill Reference](./references/agent-skill-reference.md)** — Agent, Skill, Tool schemas, LLM providers, trigger conditions
347-
- **[RAG Pipeline Reference](./references/rag-pipeline-reference.md)** — Vector stores, chunking strategies, retrieval config, embedding models
346+
### Zod Source Schemas (auto-copied)
348347

349-
> **Monorepo source** (for contributors): `packages/spec/src/ai/agent.zod.ts`, `skill.zod.ts`, `tool.zod.ts`, `rag-pipeline.zod.ts`, `model-registry.zod.ts`
348+
- [agent.zod.ts](./references/zod/ai/agent.zod.ts) — AgentSchema, model config, guardrails
349+
- [tool.zod.ts](./references/zod/ai/tool.zod.ts) — ToolSchema, categories, parameters
350+
- [skill.zod.ts](./references/zod/ai/skill.zod.ts) — SkillSchema, trigger conditions
351+
- [rag-pipeline.zod.ts](./references/zod/ai/rag-pipeline.zod.ts) — RAG, chunking, retrieval, embeddings
352+
- [model-registry.zod.ts](./references/zod/ai/model-registry.zod.ts) — LLM providers, model versioning
353+
- [Schema index](./references/zod/_index.md) — All bundled schemas with dependency tree
354+
355+
### Quick Reference
356+
357+
- [Agent & Skill Reference](./references/agent-skill-reference.md) — Compact property tables
358+
- [RAG Pipeline Reference](./references/rag-pipeline-reference.md) — Vector stores, strategies
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# objectstack-ai — Zod Schema Reference
2+
3+
> **Auto-generated** by `build-skill-references.ts`.
4+
> These files are copied from `packages/spec/src/` for AI agent consumption.
5+
> Do not edit — re-run `pnpm --filter @objectstack/spec run gen:skill-refs` to update.
6+
7+
## Core Schemas
8+
9+
- [`ai/agent.zod.ts`](./ai/agent.zod.ts) — AI Model Configuration
10+
- [`ai/model-registry.zod.ts`](./ai/model-registry.zod.ts) — AI Model Registry Protocol
11+
- [`ai/rag-pipeline.zod.ts`](./ai/rag-pipeline.zod.ts) — RAG (Retrieval-Augmented Generation) Pipeline Protocol
12+
- [`ai/skill.zod.ts`](./ai/skill.zod.ts) — Skill Trigger Condition Schema
13+
- [`ai/tool.zod.ts`](./ai/tool.zod.ts) — Tool Category
14+
15+
## Dependencies (auto-resolved)
16+
17+
- [`ai/cost.zod.ts`](./ai/cost.zod.ts) — AI Cost Tracking Protocol
18+
- [`automation/state-machine.zod.ts`](./automation/state-machine.zod.ts) — XState-inspired State Machine Protocol
19+
- [`shared/identifiers.zod.ts`](./shared/identifiers.zod.ts) — System Identifier Schema

0 commit comments

Comments
 (0)