diff --git a/CHANGELOG.md b/CHANGELOG.md
index bbee02314..c6a804327 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+### Changed
+
+- **Copilot Skills Architecture Unified** (`skills`): Consolidated 10 parallel ObjectUI Copilot skills (`objectui-auth-permissions`, `objectui-console-development`, `objectui-data-integration`, `objectui-i18n`, `objectui-mobile`, `objectui-plugin-development`, `objectui-project-setup`, `objectui-schema-expressions`, `objectui-sdui-page-builder`, `objectui-testing`) into a single tree-based `skills/objectui/` structure aligned with shadcn/ui best practices. The new architecture features:
+ - **Single entry point** (`skills/objectui/SKILL.md`): Unified skill manifest with core principles, architecture overview, and indexed references to guides and rules.
+ - **Global rules** (`skills/objectui/rules/`): Protocol compliance, styling constraints, component composition patterns, and no-touch zones as dedicated markdown files.
+ - **Domain guides** (`skills/objectui/guides/`): 10 specialized guides (page-builder, plugin-development, schema-expressions, data-integration, project-setup, testing, i18n, mobile, auth-permissions, console-development) for deep domain expertise.
+ - **Unified evals** (`skills/objectui/evals/`): All evaluation JSON files consolidated from parallel skill evals directories.
+ - **Updated skills-lock.json**: Now references only `objectui` (local) and `shadcn` (github) skills, removing fragmented cross-skill dependencies.
+ - This restructuring improves AI agent selection accuracy, reduces maintenance overhead, and aligns with shadcn/ui's single-skill tree model for better ecosystem compatibility.
+
### Improved
- **Console UI design optimization sweep** (`@object-ui/console`): Comprehensive accessibility and design consistency improvements across all major console interfaces:
diff --git a/ROADMAP.md b/ROADMAP.md
index e543b4975..9e50d03d5 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -1,11 +1,11 @@
# ObjectUI Development Roadmap
-> **Last Updated:** April 1, 2026
+> **Last Updated:** April 14, 2026
> **Current Version:** v0.5.x
> **Spec Version:** @objectstack/spec v3.3.0
> **Client Version:** @objectstack/client v3.3.0
> **Target UX Benchmark:** π― Airtable parity
-> **Current Priority:** AppShell Navigation Β· Designer Interaction Β· **View Config Live Preview Sync β
** Β· Dashboard Config Panel Β· Airtable UX Polish Β· **Flow Designer β
** Β· **App Creation & Editing Flow β
** Β· **System Settings & App Management β
** Β· **Right-Side Visual Editor Drawer β
** Β· **Object Manager & Field Designer β
** Β· **AI SDUI Chatbot (service-ai + vercel/ai) β
** Β· **Unified Home Dashboard β
**
+> **Current Priority:** AppShell Navigation Β· Designer Interaction Β· **View Config Live Preview Sync β
** Β· Dashboard Config Panel Β· Airtable UX Polish Β· **Flow Designer β
** Β· **App Creation & Editing Flow β
** Β· **System Settings & App Management β
** Β· **Right-Side Visual Editor Drawer β
** Β· **Object Manager & Field Designer β
** Β· **AI SDUI Chatbot (service-ai + vercel/ai) β
** Β· **Unified Home Dashboard β
** Β· **Unified Copilot Skills Architecture β
**
---
diff --git a/skills-lock.json b/skills-lock.json
index 4007b6cfd..d6f378796 100644
--- a/skills-lock.json
+++ b/skills-lock.json
@@ -1,20 +1,15 @@
{
"version": 1,
"skills": {
- "frontend-design": {
- "source": "anthropics/skills",
- "sourceType": "github",
- "computedHash": "063a0e6448123cd359ad0044cc46b0e490cc7964d45ef4bb9fd842bd2ffbca67"
+ "objectui": {
+ "source": "local",
+ "sourceType": "local",
+ "path": "./skills/objectui"
},
"shadcn": {
"source": "shadcn/ui",
"sourceType": "github",
"computedHash": "873c67922d80775a9fdf596db7964b579f0408c30ec6e3d11989f6055bbec89f"
- },
- "skill-creator": {
- "source": "anthropics/skills",
- "sourceType": "github",
- "computedHash": "b122f4d1e91aa1d83027e050b5b37c3156203ee85458d7b1360bc48167c9e02a"
}
}
}
diff --git a/skills/objectui/README.md b/skills/objectui/README.md
new file mode 100644
index 000000000..ebb5c138c
--- /dev/null
+++ b/skills/objectui/README.md
@@ -0,0 +1,104 @@
+# ObjectUI Copilot Skill
+
+This directory contains the unified ObjectUI Copilot skill, consolidating all ObjectUI development knowledge into a single tree-based structure aligned with [shadcn/ui](https://github.com/shadcn-ui/ui/tree/main/skills/shadcn) best practices.
+
+## Structure
+
+```
+skills/objectui/
+βββ SKILL.md # Main entry point - core principles & architecture
+βββ rules/ # Global non-negotiable constraints
+β βββ protocol.md # JSON Protocol compliance rules
+β βββ styling.md # Tailwind & Shadcn styling rules
+β βββ composition.md # Component composition patterns
+β βββ no-touch-zones.md # Protected upstream files
+βββ guides/ # Domain-specific expertise (10 guides)
+β βββ page-builder.md # Schema-driven page building
+β βββ plugin-development.md # Creating custom plugins
+β βββ schema-expressions.md # Expression syntax & debugging
+β βββ data-integration.md # DataSource & API integration
+β βββ project-setup.md # Project initialization & config
+β βββ testing.md # Testing patterns (Vitest, Storybook, Playwright)
+β βββ i18n.md # Internationalization & localization
+β βββ mobile.md # Mobile responsiveness & PWA
+β βββ auth-permissions.md # Authentication & RBAC
+β βββ console-development.md # Console app development
+βββ evals/ # Evaluation test cases (10 JSON files)
+βββ agents/ # Agent configurations (empty, reserved for future)
+```
+
+## Why This Structure?
+
+### Before (10 Parallel Skills)
+- `skills/objectui-auth-permissions/`
+- `skills/objectui-console-development/`
+- `skills/objectui-data-integration/`
+- `skills/objectui-i18n/`
+- `skills/objectui-mobile/`
+- `skills/objectui-plugin-development/`
+- `skills/objectui-project-setup/`
+- `skills/objectui-schema-expressions/`
+- `skills/objectui-sdui-page-builder/`
+- `skills/objectui-testing/`
+
+**Problems:**
+- Agent had to choose between 10+ skills
+- Cross-skill knowledge was fragmented
+- Maintenance overhead (10 separate manifests)
+- Inconsistent with shadcn/ui single-skill model
+
+### After (1 Tree-Based Skill)
+- `skills/objectui/` (single skill)
+ - `SKILL.md` - unified entry point
+ - `rules/` - global constraints
+ - `guides/` - domain expertise
+
+**Benefits:**
+- β
Single unified entry point
+- β
AI agent always knows where to look
+- β
Global rules apply consistently
+- β
Aligned with shadcn/ui architecture
+- β
Easier maintenance and updates
+
+## Usage
+
+When working with ObjectUI, the AI agent will:
+
+1. **Start with `SKILL.md`** - Core principles, architecture, and quick reference
+2. **Check `rules/`** - Ensure compliance with non-negotiable constraints
+3. **Consult `guides/`** - Get deep domain expertise for specific tasks
+
+### Example Flow
+
+**User:** "Help me build a dashboard page with ObjectUI"
+
+**Agent:**
+1. Reads `SKILL.md` β Understands core principles
+2. Checks `rules/protocol.md` β Learns JSON schema structure
+3. Checks `rules/styling.md` β Learns Tailwind requirements
+4. Reads `guides/page-builder.md` β Gets dashboard building patterns
+5. Reads `guides/schema-expressions.md` β Learns dynamic expressions
+
+## Maintenance
+
+When adding new content:
+
+- **Core principles or architecture changes** β Update `SKILL.md`
+- **New global rule** β Add to `rules/`
+- **New domain area** β Add guide to `guides/`
+- **New eval test** β Add JSON to `evals/`
+
+## Migration Notes
+
+This structure was created on April 14, 2026, consolidating:
+- 10 parallel skill directories
+- 10 SKILL.md files β 1 SKILL.md + 10 guides
+- 10 evals directories β 1 unified evals/
+- Cross-skill references unified into single namespace
+
+All content preserved and reorganized for better discoverability.
+
+## Reference
+
+- **shadcn/ui skill structure**: https://github.com/shadcn-ui/ui/tree/main/skills/shadcn
+- **Issue**: [#ζΆζδΌε-ε°-objectui-ε€-Copilot-skill-εεΉΆδΈΊε-skill-ζ ηΆδ½η³»](https://github.com/objectstack-ai/objectui/issues)
diff --git a/skills/objectui/SKILL.md b/skills/objectui/SKILL.md
new file mode 100644
index 000000000..e61b94a85
--- /dev/null
+++ b/skills/objectui/SKILL.md
@@ -0,0 +1,331 @@
+---
+name: objectui
+description: Universal Server-Driven UI (SDUI) Engine for building JSON-driven React interfaces with Shadcn design quality. Use this skill for all ObjectUI development tasks including schema-driven page building, plugin development, component integration, testing, auth/permissions, data integration, i18n, mobile responsiveness, project setup, and console development. Triggers on any mention of ObjectUI, SchemaRenderer, JSON schemas, SDUI, metadata-driven UIs, or Object Stack ecosystem work.
+user-invocable: false
+---
+
+# ObjectUI
+
+> **A Universal, Server-Driven UI (SDUI) Engine built on React + Tailwind + Shadcn.**
+
+ObjectUI renders JSON metadata from the `@objectstack/spec` protocol into pixel-perfect, accessible, and interactive enterprise interfaces (Dashboards, Kanbans, CRUDs, Forms, Grids).
+
+**Repository:** [github.com/objectstack-ai/objectui](https://github.com/objectstack-ai/objectui)
+
+## Strategic Positioning
+
+- **The "JSON-to-Shadcn" Bridge:** The only library combining Low-Code speed with Shadcn/Tailwind design quality
+- **The "Face" of ObjectStack:** Official renderer for the ecosystem, while remaining **Backend Agnostic**
+
+## Core Principles
+
+### 0. English-Only Codebase
+
+**Context:** This is an international open-source project.
+
+**Instruction:** ALL user-facing text in components, documentation, comments, and UI labels MUST be written in English.
+
+**Constraint:** Do NOT use Chinese or any other non-English language in:
+- Component text/labels (buttons, titles, descriptions, error messages)
+- Code comments
+- Documentation files (README.md, docs/*.md)
+- Console/log messages
+
+**Reasoning:** English ensures global accessibility and consistency across the codebase.
+
+### 1. Strict Adherence to @objectstack/spec
+
+**Context:** We are the implementation of a standard protocol.
+
+**Instruction:** All component schemas, JSON structures, and data types MUST strictly follow definitions in `@objectstack/spec`.
+
+**Constraint:** Do not invent new schema properties. If the spec says `columns`, do not use `fields`.
+
+**Validation:** Check `@objectstack/spec` definitions before writing any `interface` or `type`.
+
+### 2. Protocol Agnostic (The Universal Adapter)
+
+**Context:** Users might fetch data from a legacy SOAP API or a local JSON file.
+
+**Instruction:** Never hardcode `objectql.find()`. Use the DataSource Interface.
+
+**Pattern:** Inject dataSource via the root ``.
+
+### 3. Documentation Driven Development
+
+**Context:** Code without docs is dead code.
+
+**Instruction:** For EVERY feature implemented or refactored, you MUST update the corresponding documentation:
+1. Package `README.md`
+2. `content/docs/guide/*.md`
+
+**Definition of Done:** The task is not complete until the documentation reflects the new code/architecture.
+
+### 4. "Shadcn Native" Aesthetics
+
+**Identity:** We are essentially "Serializable Shadcn".
+
+**Instruction:** When implementing a component (e.g., Card), strictly follow Shadcn's DOM structure (CardHeader, CardTitle, CardContent).
+
+**Constraint:** ALWAYS expose `className` in the schema props. Allow users to inject `bg-red-500` via JSON to override default styles.
+
+### 5. The Action System (Interactivity)
+
+**Concept:** A static UI is useless. The JSON must define behavior.
+
+**Pattern:** Actions are defined as data, not functions.
+
+**Example JSON:**
+```json
+{
+ "events": {
+ "onClick": [
+ { "action": "validate", "target": "form_1" },
+ { "action": "submit", "target": "form_1" },
+ { "action": "navigate", "params": { "url": "/success" } }
+ ]
+ }
+}
+```
+
+**Implementation:** The `@object-ui/core` package acts as an Event Bus to dispatch these actions.
+
+### 6. Layout as Components
+
+**Concept:** Layouts are just components that render children.
+
+**Instruction:** Treat Grid, Stack, Container as first-class citizens.
+
+**Responsiveness:** Layout schemas must support responsive props (e.g., `cols: { sm: 1, md: 2, lg: 4 }`).
+
+### 7. Type Safety over Magic
+
+- **No `any`:** Use strict Generics.
+- **Registry:** Use a central ComponentRegistry to map strings (`"type": "button"`) to React components.
+- **No `eval()` or dynamic imports** for security.
+
+### 8. The "No-Touch" Zones (Shadcn Purity)
+
+**Protected Path:** `packages/components/src/ui/**/*.tsx`
+
+**Rule:** You are FORBIDDEN from modifying the logic or styles of files in this directory.
+
+**Reasoning:** These are upstream 3rd-party files that are overwritten by sync scripts.
+
+**Workaround:** If a user asks to change the behavior of Button or Dialog:
+1. Do NOT edit `src/ui/button.tsx`.
+2. Create or Edit a wrapper in `packages/components/src/custom/`.
+3. Import the primitive from `@/ui/...` and wrap it.
+
+## Tech Stack (Strict Constraints)
+
+- **Core:** React 18+ (Hooks), TypeScript 5.0+ (Strict)
+- **Styling:** Tailwind CSS (Utility First)
+ - β
REQUIRED: Use `class-variance-authority` (cva) for component variants
+ - β
REQUIRED: Use `tailwind-merge` + `clsx` (`cn()`) for class overrides
+ - β FORBIDDEN: Inline styles (`style={{}}`), CSS Modules, Styled-components
+- **UI Primitives:** Shadcn UI (Radix UI) + Lucide Icons
+- **State Management:** Zustand (for global store), React Context (for scoped data)
+- **Testing:** Vitest + React Testing Library
+- **Docs:** Storybook (Component isolation)
+
+## Architecture & Monorepo Topology
+
+You manage a strict PNPM Workspace.
+
+| Package | Role | Responsibility | π΄ Strict Constraints |
+|---|---|---|---|
+| `@object-ui/types` | The Protocol | Pure JSON Interfaces (ComponentSchema, ActionSchema). | ZERO dependencies. No React code. |
+| `@object-ui/core` | The Engine | Schema Registry, Validation, Expression Evaluation (`visible: "${data.age > 18}"`). | No UI library dependencies. Logic Only. |
+| `@object-ui/components` | The Atoms | Shadcn Primitives (Button, Badge, Card) & Icons. | Pure UI. No business logic. |
+| `@object-ui/fields` | The Inputs | Standard Field Renderers (Text, Number, Select). | Must implement FieldWidgetProps. |
+| `@object-ui/layout` | The Shell | Page Structure (Header, Sidebar, AppShell). | Routing-aware composition. |
+| `@object-ui/plugin-*` | The Widgets | Complex Views (Grid, Kanban, Map, Charts). | Heavy dependencies allowed here only. |
+| `@object-ui/react` | The Runtime | ``, useRenderer, useDataScope. | Bridges Core and Components. |
+| `@object-ui/data-*` | The Adapters | Connectors for REST, ObjectQL, GraphQL. | Isolate ALL fetch logic. |
+
+### Architectural Strategy (Strict)
+
+**β Do NOT create a package for every component.**
+
+**β
Group by Dependency Weight:**
+
+1. **Atoms (@object-ui/components):** Shadcn Primitives. Zero heavy 3rd-party deps.
+2. **Fields (@object-ui/fields):** Standard Inputs.
+3. **Layouts (@object-ui/layout):** Page Skeletons.
+4. **Plugins (@object-ui/plugin-*):** Heavy Widgets (>50KB) or specialized libraries (Maps, Editors, Charts).
+
+## The JSON Protocol Specification (The "DNA")
+
+You must enforce a strict JSON structure. Every node in the UI tree follows this shape:
+
+```typescript
+// @object-ui/types
+interface UIComponent {
+ /** The unique identifier for the renderer registry (e.g., 'input', 'grid', 'card') */
+ type: string;
+
+ /** Unique ID for DOM accessibility and event targeting */
+ id?: string;
+
+ /** Visual properties (mapped directly to Shadcn props) */
+ props?: Record;
+
+ /** Data binding path (e.g., 'user.address.city') */
+ bind?: string;
+
+ /** Styling overrides (Tailwind classes) */
+ className?: string;
+
+ /** Dynamic Behavior */
+ hidden?: string; // Expression: "${data.role != 'admin'}"
+ disabled?: string; // Expression
+
+ /** Event Handlers */
+ events?: Record; // onClick -> [Action1, Action2]
+
+ /** Layout Slots */
+ children?: UIComponent[];
+}
+```
+
+## Quick Reference
+
+### When to Use Specific Guides
+
+The guides in `guides/` provide deep domain expertise:
+
+- **Page Building & Schema Design** β [guides/page-builder.md](./guides/page-builder.md)
+- **Creating Custom Plugins** β [guides/plugin-development.md](./guides/plugin-development.md)
+- **Expression Syntax & Debugging** β [guides/schema-expressions.md](./guides/schema-expressions.md)
+- **Data Fetching & DataSource** β [guides/data-integration.md](./guides/data-integration.md)
+- **New Project Setup** β [guides/project-setup.md](./guides/project-setup.md)
+- **Testing Components & Schemas** β [guides/testing.md](./guides/testing.md)
+- **Multi-Language Support** β [guides/i18n.md](./guides/i18n.md)
+- **Mobile & Responsive Design** β [guides/mobile.md](./guides/mobile.md)
+- **Auth, Roles & Permissions** β [guides/auth-permissions.md](./guides/auth-permissions.md)
+- **Console Development** β [guides/console-development.md](./guides/console-development.md)
+
+### Critical Global Rules
+
+The rules in `rules/` define non-negotiable constraints:
+
+- **JSON Protocol Compliance** β [rules/protocol.md](./rules/protocol.md)
+- **Styling & Tailwind Usage** β [rules/styling.md](./rules/styling.md)
+- **Component Composition Patterns** β [rules/composition.md](./rules/composition.md)
+- **No-Touch Zones (Shadcn Upstream)** β [rules/no-touch-zones.md](./rules/no-touch-zones.md)
+
+## Implementation Patterns
+
+### Pattern A: The Component Registry (Extensibility)
+
+How do we let users add their own "Map" component?
+
+```typescript
+// packages/core/src/registry.ts
+export type ComponentImpl = React.FC<{ schema: any; ... }>;
+
+const registry = new Map();
+
+export function registerComponent(type: string, impl: ComponentImpl) {
+ registry.set(type, impl);
+}
+
+export function resolveComponent(type: string) {
+ return registry.get(type) || FallbackComponent;
+}
+```
+
+### Pattern B: The Renderer Loop (Recursion)
+
+How to render a tree?
+
+```typescript
+// packages/react/src/SchemaRenderer.tsx
+export const SchemaRenderer = ({ schema }: { schema: UIComponent }) => {
+ const Component = resolveComponent(schema.type);
+ const { isHidden } = useExpression(schema.hidden);
+
+ if (isHidden) return null;
+
+ return (
+
+ {schema.children?.map(child => (
+
+ ))}
+
+ );
+};
+```
+
+## AI Workflow Instructions
+
+### On "Create New Component" (e.g., 'DataTable')
+
+1. **Type Definition:** Update `@object-ui/types`. Define `DataTableSchema` (columns, sorting, pagination).
+2. **Shadcn Mapping:** Look at shadcn/ui/table. Create `DataTableRenderer` in `@object-ui/components`.
+3. **Data Scope:** Use `useDataScope()` to get the array data. Do not fetch data inside the component.
+4. **Registration:** Register `"type": "table"` in the core registry.
+
+### On "Action Logic" (e.g., 'Open Modal')
+
+1. **Define Schema:** Add `OpenModalAction` interface to types.
+2. **Implement Handler:** Add the logic to the ActionEngine in `@object-ui/core`.
+3. **Visuals:** Ensure the component triggering it calls `useActionRunner()`.
+
+### On "Documentation"
+
+1. **JSON First:** Always show the JSON configuration first.
+2. **Visuals:** Describe how Tailwind classes (`className`) affect the component.
+3. **Storybook:** Suggest creating a `.stories.tsx` file for every new component.
+
+## Debugging & Browser Simulation Strategy
+
+When debugging the simulated browser environment (e.g., `apps/console` in mock mode), strict adherence to the official toolchain is required.
+
+### Rule #1: Official MSW Integration
+
+- **Startup:** Use `@objectstack/plugin-msw` to initialize the mock API server. Do NOT write custom fetch interceptors or manual mock servers unless absolutely necessary.
+- **Configuration:** Ensure the `MSWPlugin` is configured with the correct `baseUrl` (e.g., `/api/v1`) to match the client's expectations.
+
+### Rule #2: Client Data Fetching
+
+- **Data Access:** Always use `@objectstack/client` for data fetching. Do not use raw `fetch` or `axios` directly in components.
+- **Alignment:** Verify that the client's `baseUrl` matches the mock server's configuration.
+
+### Rule #3: Upstream Fixes First
+
+- **Principle:** If you encounter a bug or limitation in the official packages (`@objectstack/*`):
+ - **Action 1:** Do NOT rely solely on local workarounds (monkey-patching) in the app.
+ - **Action 2:** Prompt the user to modify the source code in the official packages (if available in the workspace) or report the issue.
+ - **Reasoning:** We prioritize fixing the core engine over patching individual apps.
+
+## Common Mistakes to Avoid
+
+- Writing large bespoke React JSX trees before schema definition
+- Hardcoding API calls directly inside visual renderers
+- Introducing package coupling (e.g., UI package depending on business logic package)
+- Registering components without namespace in plugin-heavy projects
+- Skipping docs updates for newly introduced schema patterns
+- Putting expression values in top-level `value`/`label` fields instead of `props.*`
+- Missing Shadcn CSS variables β components render but look completely unstyled
+- Forgetting `@source` directives in Tailwind config β utility classes not generated for ObjectUI packages
+
+## Fast Triage Playbook for Ambiguous Requests
+
+If the request is underspecified:
+
+1. Infer likely page category (list/detail/form/dashboard)
+2. Produce a minimal viable schema first
+3. Mark assumptions clearly
+4. Provide one conservative and one advanced variant
+
+This keeps momentum while inviting focused user feedback.
+
+---
+
+**You are the Architect. Build the Engine.**
diff --git a/skills/objectui-auth-permissions/evals/evals.json b/skills/objectui/evals/auth-permissions.json
similarity index 100%
rename from skills/objectui-auth-permissions/evals/evals.json
rename to skills/objectui/evals/auth-permissions.json
diff --git a/skills/objectui-console-development/evals/evals.json b/skills/objectui/evals/console-development.json
similarity index 100%
rename from skills/objectui-console-development/evals/evals.json
rename to skills/objectui/evals/console-development.json
diff --git a/skills/objectui-data-integration/evals/evals.json b/skills/objectui/evals/data-integration.json
similarity index 100%
rename from skills/objectui-data-integration/evals/evals.json
rename to skills/objectui/evals/data-integration.json
diff --git a/skills/objectui-i18n/evals/evals.json b/skills/objectui/evals/i18n.json
similarity index 100%
rename from skills/objectui-i18n/evals/evals.json
rename to skills/objectui/evals/i18n.json
diff --git a/skills/objectui-mobile/evals/evals.json b/skills/objectui/evals/mobile.json
similarity index 100%
rename from skills/objectui-mobile/evals/evals.json
rename to skills/objectui/evals/mobile.json
diff --git a/skills/objectui-plugin-development/evals/evals.json b/skills/objectui/evals/plugin-development.json
similarity index 100%
rename from skills/objectui-plugin-development/evals/evals.json
rename to skills/objectui/evals/plugin-development.json
diff --git a/skills/objectui-project-setup/evals/evals.json b/skills/objectui/evals/project-setup.json
similarity index 100%
rename from skills/objectui-project-setup/evals/evals.json
rename to skills/objectui/evals/project-setup.json
diff --git a/skills/objectui-schema-expressions/evals/evals.json b/skills/objectui/evals/schema-expressions.json
similarity index 100%
rename from skills/objectui-schema-expressions/evals/evals.json
rename to skills/objectui/evals/schema-expressions.json
diff --git a/skills/objectui-sdui-page-builder/evals/evals.json b/skills/objectui/evals/sdui-page-builder.json
similarity index 100%
rename from skills/objectui-sdui-page-builder/evals/evals.json
rename to skills/objectui/evals/sdui-page-builder.json
diff --git a/skills/objectui-testing/evals/evals.json b/skills/objectui/evals/testing.json
similarity index 100%
rename from skills/objectui-testing/evals/evals.json
rename to skills/objectui/evals/testing.json
diff --git a/skills/objectui-auth-permissions/SKILL.md b/skills/objectui/guides/auth-permissions.md
similarity index 100%
rename from skills/objectui-auth-permissions/SKILL.md
rename to skills/objectui/guides/auth-permissions.md
diff --git a/skills/objectui-console-development/SKILL.md b/skills/objectui/guides/console-development.md
similarity index 100%
rename from skills/objectui-console-development/SKILL.md
rename to skills/objectui/guides/console-development.md
diff --git a/skills/objectui-data-integration/SKILL.md b/skills/objectui/guides/data-integration.md
similarity index 100%
rename from skills/objectui-data-integration/SKILL.md
rename to skills/objectui/guides/data-integration.md
diff --git a/skills/objectui-i18n/SKILL.md b/skills/objectui/guides/i18n.md
similarity index 100%
rename from skills/objectui-i18n/SKILL.md
rename to skills/objectui/guides/i18n.md
diff --git a/skills/objectui-mobile/SKILL.md b/skills/objectui/guides/mobile.md
similarity index 100%
rename from skills/objectui-mobile/SKILL.md
rename to skills/objectui/guides/mobile.md
diff --git a/skills/objectui-sdui-page-builder/SKILL.md b/skills/objectui/guides/page-builder.md
similarity index 100%
rename from skills/objectui-sdui-page-builder/SKILL.md
rename to skills/objectui/guides/page-builder.md
diff --git a/skills/objectui-plugin-development/SKILL.md b/skills/objectui/guides/plugin-development.md
similarity index 100%
rename from skills/objectui-plugin-development/SKILL.md
rename to skills/objectui/guides/plugin-development.md
diff --git a/skills/objectui-project-setup/SKILL.md b/skills/objectui/guides/project-setup.md
similarity index 100%
rename from skills/objectui-project-setup/SKILL.md
rename to skills/objectui/guides/project-setup.md
diff --git a/skills/objectui-schema-expressions/SKILL.md b/skills/objectui/guides/schema-expressions.md
similarity index 100%
rename from skills/objectui-schema-expressions/SKILL.md
rename to skills/objectui/guides/schema-expressions.md
diff --git a/skills/objectui-testing/SKILL.md b/skills/objectui/guides/testing.md
similarity index 100%
rename from skills/objectui-testing/SKILL.md
rename to skills/objectui/guides/testing.md
diff --git a/skills/objectui/rules/composition.md b/skills/objectui/rules/composition.md
new file mode 100644
index 000000000..70abe1475
--- /dev/null
+++ b/skills/objectui/rules/composition.md
@@ -0,0 +1,297 @@
+# Component Composition Rules
+
+> **Critical:** Follow Shadcn composition patterns strictly for consistency and accessibility.
+
+## Rule: Use Full Card Composition
+
+**β
CORRECT:**
+```typescript
+
+
+ Customer Summary
+ Active users this month
+
+
+ {/* main content */}
+
+
+
+
+
+```
+
+**β FORBIDDEN:**
+```typescript
+// β Don't dump everything in CardContent
+
+
+ Customer Summary
+ Active users this month
+ {/* content */}
+
+
+
+```
+
+## Rule: Overlays Need Accessible Titles
+
+Dialog, Sheet, and Drawer **always need a Title** for accessibility.
+
+**β
CORRECT:**
+```typescript
+
+
+// If visually hidden:
+Settings
+```
+
+**β FORBIDDEN:**
+```typescript
+// β Missing title - accessibility violation
+
+```
+
+## Rule: Items Inside Their Group
+
+**β
CORRECT:**
+```typescript
+
+
+
+
+
+
+ Profile
+ Settings
+
+
+
+```
+
+**β FORBIDDEN:**
+```typescript
+// β Items directly in Content without Group
+
+ Apple
+
+```
+
+## Rule: TabsTrigger Must Be Inside TabsList
+
+**β
CORRECT:**
+```typescript
+
+
+ Account
+ Password
+
+ ...
+ ...
+
+```
+
+**β FORBIDDEN:**
+```typescript
+// β Triggers directly in Tabs
+
+ Account
+ ...
+
+```
+
+## Rule: Avatar Always Needs Fallback
+
+**β
CORRECT:**
+```typescript
+
+
+ {user.initials}
+
+```
+
+**β FORBIDDEN:**
+```typescript
+// β Missing fallback - shows nothing if image fails
+
+
+
+```
+
+## Rule: Use Existing Components, Not Custom Markup
+
+**Callouts use Alert:**
+```typescript
+// β
Correct
+
+ Error
+ Something went wrong.
+
+
+// β Wrong
+
+
Error
+
Something went wrong.
+
+```
+
+**Empty states use Empty:**
+```typescript
+// β
Correct
+}
+ title="No results found"
+ description="Try adjusting your search"
+/>
+
+// β Wrong
+
+
+
No results found
+
+```
+
+**Use Separator instead of hr:**
+```typescript
+// β
Correct
+
+
+// β Wrong
+
+
+```
+
+**Use Skeleton for loading:**
+```typescript
+// β
Correct
+
+
+// β Wrong
+
+```
+
+**Use Badge instead of styled spans:**
+```typescript
+// β
Correct
+New
+
+// β Wrong
+New
+```
+
+## Rule: Toast via Sonner
+
+**β
CORRECT:**
+```typescript
+import { toast } from 'sonner';
+
+toast.success('Item saved');
+toast.error('Failed to save');
+toast.info('Processing...');
+```
+
+**β FORBIDDEN:**
+```typescript
+// β Don't build custom toast systems
+showNotification({ type: 'success', message: 'Saved' });
+```
+
+## Rule: Button Loading State
+
+**β
CORRECT:**
+```typescript
+
+```
+
+**β FORBIDDEN:**
+```typescript
+// β Button has no isPending/isLoading prop
+
+```
+
+## Rule: Icons in Buttons Use data-icon
+
+**β
CORRECT:**
+```typescript
+
+
+
+```
+
+**β FORBIDDEN:**
+```typescript
+// β No data-icon attribute
+
+
+// β Manual sizing classes on icons
+
+```
+
+**Why:** Components handle icon sizing via CSS. No manual `size-4` or `w-4 h-4` needed.
+
+## Rule: Pass Icons as Objects, Not String Keys
+
+**β
CORRECT:**
+```typescript
+import { CheckIcon } from 'lucide-react';
+
+
+```
+
+**β FORBIDDEN:**
+```typescript
+// β Don't use string lookups
+
+```
+
+## Rule: Registry-Based Component Resolution
+
+For schema-driven rendering, use ComponentRegistry, not switch/case:
+
+**β
CORRECT:**
+```typescript
+const Component = ComponentRegistry.get(schema.type);
+return ;
+```
+
+**β FORBIDDEN:**
+```typescript
+// β Don't use switch statements
+switch (schema.type) {
+ case 'button': return ;
+ case 'card': return ;
+ // ...
+}
+```
diff --git a/skills/objectui/rules/no-touch-zones.md b/skills/objectui/rules/no-touch-zones.md
new file mode 100644
index 000000000..83299d4d4
--- /dev/null
+++ b/skills/objectui/rules/no-touch-zones.md
@@ -0,0 +1,136 @@
+# No-Touch Zones (Shadcn Upstream Purity)
+
+> **Critical:** These files are synced from upstream. DO NOT modify them directly.
+
+## Protected Path
+
+```
+packages/components/src/ui/**/*.tsx
+```
+
+## Rule: Forbidden to Modify Shadcn Primitives
+
+**You are FORBIDDEN from modifying the logic or styles of files in this directory.**
+
+### Why This Rule Exists
+
+1. These are upstream 3rd-party files from [shadcn/ui](https://github.com/shadcn-ui/ui)
+2. They are **overwritten by sync scripts** (`scripts/shadcn-sync.js`)
+3. Any manual changes will be lost on the next sync
+
+### Examples of Protected Files
+
+- `packages/components/src/ui/button.tsx`
+- `packages/components/src/ui/card.tsx`
+- `packages/components/src/ui/dialog.tsx`
+- `packages/components/src/ui/input.tsx`
+- `packages/components/src/ui/select.tsx`
+- ...and all other files in `ui/**/*.tsx`
+
+## Workaround: Create Wrappers
+
+If a user asks to change the behavior of Button, Dialog, or any Shadcn primitive:
+
+### Step 1: DO NOT edit the primitive
+
+**β FORBIDDEN:**
+```typescript
+// packages/components/src/ui/button.tsx
+export function Button({ children, ...props }) {
+ // β DO NOT modify this file
+ return
+}
+```
+
+### Step 2: Create a wrapper in `custom/`
+
+**β
CORRECT:**
+```typescript
+// packages/components/src/custom/enhanced-button.tsx
+import { Button } from '../ui/button';
+
+export function EnhancedButton({ loading, children, ...props }) {
+ return (
+
+ );
+}
+```
+
+### Step 3: Export from package index
+
+```typescript
+// packages/components/src/index.ts
+export { Button } from './ui/button'; // Original primitive
+export { EnhancedButton } from './custom/enhanced-button'; // Wrapper
+```
+
+### Step 4: Register the wrapper if needed
+
+```typescript
+// For schema-driven rendering
+import { ComponentRegistry } from '@object-ui/core';
+import { EnhancedButton } from '@object-ui/components';
+
+ComponentRegistry.register('enhanced-button', EnhancedButton);
+```
+
+## Directory Structure
+
+```
+packages/components/src/
+βββ ui/ # π NO-TOUCH ZONE - Shadcn upstream
+β βββ button.tsx
+β βββ card.tsx
+β βββ dialog.tsx
+β βββ ...
+βββ custom/ # β
SAFE ZONE - Custom wrappers
+β βββ enhanced-button.tsx
+β βββ data-card.tsx
+β βββ ...
+βββ icons/ # β
SAFE ZONE - Icon components
+βββ index.ts # β
SAFE ZONE - Package exports
+```
+
+## Exception: CSS-Only Modifications
+
+If you need to add CSS rules for state-dependent behavior (e.g., Tailwind 4 sidebar compatibility), you may add them to:
+
+```
+packages/components/src/index.css
+```
+
+**Example (allowed):**
+```css
+/* Tailwind 4 sidebar state rules */
+.sidebar-menu-button-icon-mode {
+ /* Custom CSS for icon mode */
+}
+```
+
+**But NEVER modify the TypeScript/JSX in `ui/` files.**
+
+## Sync Script Reference
+
+The sync script that overwrites these files is located at:
+
+```
+scripts/shadcn-sync.js
+```
+
+This script fetches the latest components from shadcn/ui and overwrites local `ui/` files.
+
+## Summary
+
+| Action | Allowed? | Location |
+|--------|----------|----------|
+| Modify Button logic | β NO | `ui/button.tsx` |
+| Create ButtonWithIcon | β
YES | `custom/button-with-icon.tsx` |
+| Modify Card styles | β NO | `ui/card.tsx` |
+| Create DataCard wrapper | β
YES | `custom/data-card.tsx` |
+| Add CSS rules | β
YES | `index.css` |
+| Fix TypeScript errors in ui/ | β NO | Report upstream or use workaround |
+
+**When in doubt:** Create a wrapper in `custom/` instead of editing `ui/` files.
diff --git a/skills/objectui/rules/protocol.md b/skills/objectui/rules/protocol.md
new file mode 100644
index 000000000..8ee989fd7
--- /dev/null
+++ b/skills/objectui/rules/protocol.md
@@ -0,0 +1,151 @@
+# JSON Protocol Rules
+
+> **Critical:** All ObjectUI schemas MUST strictly follow `@objectstack/spec` definitions.
+
+## Rule: Expression Evaluation Boundaries
+
+### Fields That ARE Evaluated
+
+SchemaRenderer evaluates these fields automatically:
+
+| Field | Evaluation Type | Return Type | Example |
+|---|---|---|---|
+| `props.*` | Template (`${}`) | Preserves original type | `"props": { "count": "${items.length}" }` β number |
+| `content` | Template (`${}`) | string | `"content": "Total: ${data.total}"` |
+| `hidden` | Condition | boolean | `"hidden": "${data.role !== 'admin'}"` |
+| `hiddenOn` | Condition | boolean | `"hiddenOn": "data.status === 'draft'"` |
+| `visible` | Condition | boolean | `"visible": "${data.isActive}"` |
+| `visibleOn` | Condition | boolean | `"visibleOn": "data.permissions.canView"` |
+| `disabled` | Condition | boolean | `"disabled": "${form.isSubmitting}"` |
+| `disabledOn` | Condition | boolean | `"disabledOn": "!data.hasPermission"` |
+
+**Precedence rule:** `visible` takes priority over `hidden`.
+
+### Fields That are NOT Evaluated
+
+These top-level schema fields are passed as raw strings:
+
+- `value` β use `props.value` instead
+- `label` β use `props.label` instead
+- `description` β use `props.description` instead
+- `title` β use `props.title` instead
+- `className` β always a static Tailwind class string
+- `id` β always a static string
+- `type` β component type identifier
+- `bind` β data scope path (resolved by `useDataScope`, not by expressions)
+
+## Rule: Component Schema Structure
+
+Every UI component node MUST follow this shape:
+
+```typescript
+interface UIComponent {
+ type: string; // Required: component type identifier
+ id?: string; // Optional: unique identifier
+ props?: Record; // Optional: component properties
+ bind?: string; // Optional: data binding path
+ className?: string; // Optional: Tailwind CSS classes
+ hidden?: string; // Optional: visibility expression
+ disabled?: string; // Optional: disabled expression
+ events?: Record; // Optional: event handlers
+ children?: UIComponent[]; // Optional: nested components
+}
+```
+
+## Rule: No Schema Property Invention
+
+**β FORBIDDEN:** Adding custom properties not defined in `@objectstack/spec`.
+
+**Example violation:**
+```json
+{
+ "type": "grid",
+ "fields": [...], // β spec uses "columns"
+ "customProp": "value" // β not in spec
+}
+```
+
+**β
CORRECT:**
+```json
+{
+ "type": "grid",
+ "props": {
+ "columns": [...] // β
follows spec
+ }
+}
+```
+
+## Rule: Type Preservation in Expressions
+
+When the entire string is a single `${expression}`, the result preserves its type:
+
+```json
+"${data.count}" // β returns number 42, not string "42"
+"${data.isActive}" // β returns boolean true, not string "true"
+"Count: ${data.count}" // β returns string "Count: 42" (mixed template)
+```
+
+## Rule: Data Binding Path Resolution
+
+The `bind` field is NOT expression-evaluated. It's a path string resolved by `useDataScope()`:
+
+```json
+{
+ "type": "data-table",
+ "bind": "customers", // Resolved as dataSource.customers
+ "props": {
+ "columns": [...]
+ }
+}
+```
+
+**Nested paths work:** `"bind": "app.settings.users"` resolves `dataSource.app.settings.users`.
+
+## Rule: Action Event Structure
+
+Events must be defined as arrays of action definitions:
+
+```json
+{
+ "events": {
+ "onClick": [
+ { "action": "validate", "target": "form_1" },
+ { "action": "submit", "target": "form_1" },
+ { "action": "navigate", "params": { "url": "/success" } }
+ ]
+ }
+}
+```
+
+**β DO NOT** use function references or inline callbacks in JSON schemas.
+
+## Rule: Layout Responsiveness
+
+Layout components must support responsive properties:
+
+```json
+{
+ "type": "grid",
+ "props": {
+ "cols": { "sm": 1, "md": 2, "lg": 4 } // β
Responsive config
+ }
+}
+```
+
+## Rule: Expression Security
+
+The expression parser blocks dangerous patterns:
+
+**β BLOCKED:**
+- `eval()`
+- `Function()`
+- `setTimeout()`, `setInterval()`
+- `import()`, `require()`
+- `process.*`, `global.*`
+- `window.*`, `document.*`
+- `__proto__`, `constructor`, `prototype`
+
+**β
SAFE GLOBALS:**
+- `Math` β `${Math.round(price)}`
+- `JSON` β `${JSON.stringify(obj)}`
+- `parseInt`, `parseFloat`, `isNaN`, `isFinite`
diff --git a/skills/objectui/rules/styling.md b/skills/objectui/rules/styling.md
new file mode 100644
index 000000000..3f055388f
--- /dev/null
+++ b/skills/objectui/rules/styling.md
@@ -0,0 +1,240 @@
+# Styling Rules
+
+> **Critical:** ObjectUI uses Tailwind CSS exclusively. No inline styles, CSS modules, or styled-components.
+
+## Rule: Use Tailwind Utility Classes Only
+
+**β
CORRECT:**
+```json
+{
+ "type": "card",
+ "className": "col-span-12 lg:col-span-4 p-6"
+}
+```
+
+**β FORBIDDEN:**
+```typescript
+// Never do this in ObjectUI
+
+```
+
+## Rule: Use `cn()` for Class Merging
+
+When combining classes programmatically, always use the `cn()` utility:
+
+```typescript
+import { cn } from '@object-ui/components';
+
+function MyComponent({ className, ...props }) {
+ return (
+
+ {/* content */}
+
+ );
+}
+```
+
+**Why:** `cn()` uses `tailwind-merge` + `clsx` to properly handle class conflicts (e.g., `p-4` vs `p-6`).
+
+## Rule: Use Semantic Color Tokens
+
+**β
CORRECT:**
+```typescript
+className="bg-primary text-primary-foreground"
+className="bg-secondary text-secondary-foreground"
+className="bg-muted text-muted-foreground"
+className="bg-destructive text-destructive-foreground"
+className="border-border"
+```
+
+**β FORBIDDEN:**
+```typescript
+className="bg-blue-500 text-white" // β Hard-coded color
+className="bg-[#3b82f6]" // β Arbitrary value
+```
+
+**Why:** Semantic tokens support theming and dark mode automatically.
+
+## Rule: Use Component Variants (CVA)
+
+For component variations, use `class-variance-authority`:
+
+```typescript
+import { cva } from 'class-variance-authority';
+
+const buttonVariants = cva(
+ 'inline-flex items-center justify-center rounded-md', // base
+ {
+ variants: {
+ variant: {
+ default: 'bg-primary text-primary-foreground',
+ destructive: 'bg-destructive text-destructive-foreground',
+ outline: 'border border-input bg-background',
+ },
+ size: {
+ default: 'h-10 px-4 py-2',
+ sm: 'h-9 px-3',
+ lg: 'h-11 px-8',
+ },
+ },
+ defaultVariants: {
+ variant: 'default',
+ size: 'default',
+ },
+ }
+);
+```
+
+## Rule: Expose `className` in Schema Props
+
+**Every component must accept `className` in its schema props** to allow JSON-level style overrides:
+
+```json
+{
+ "type": "card",
+ "className": "bg-red-500", // β
User can override styles
+ "props": {
+ "title": "Alert"
+ }
+}
+```
+
+## Rule: Responsive Classes
+
+Use Tailwind's responsive prefixes in schemas:
+
+```json
+{
+ "type": "grid",
+ "className": "grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"
+}
+```
+
+## Rule: Dark Mode with Semantic Tokens
+
+**β DO NOT** manually add `dark:` variants:
+
+```typescript
+// β Wrong
+className="bg-white dark:bg-gray-900"
+```
+
+**β
CORRECT:**
+```typescript
+// β
Semantic tokens handle dark mode automatically
+className="bg-background text-foreground"
+```
+
+## Rule: No Manual Z-Index
+
+**β DO NOT** manually set `z-index` on overlay components:
+
+```typescript
+// β Wrong
+
+```
+
+**β
CORRECT:**
+```typescript
+// β
Dialog, Sheet, Popover, etc. handle their own stacking context
+
+```
+
+## Rule: Spacing with `gap-*` not `space-*`
+
+**β
CORRECT:**
+```typescript
+className="flex flex-col gap-4"
+className="grid grid-cols-3 gap-6"
+```
+
+**β FORBIDDEN:**
+```typescript
+className="space-y-4" // β Deprecated
+className="space-x-6" // β Use gap instead
+```
+
+## Rule: Use `size-*` for Equal Dimensions
+
+**β
CORRECT:**
+```typescript
+className="size-10" // width and height both 40px
+```
+
+**β FORBIDDEN:**
+```typescript
+className="w-10 h-10" // β Redundant
+```
+
+## Rule: Use `truncate` Shorthand
+
+**β
CORRECT:**
+```typescript
+className="truncate"
+```
+
+**β FORBIDDEN:**
+```typescript
+className="overflow-hidden text-ellipsis whitespace-nowrap"
+```
+
+## Rule: Tailwind 4 CSS Variables Setup
+
+For ObjectUI components to render correctly, third-party projects must include this CSS setup:
+
+```css
+@import "tailwindcss";
+
+/* Scan ObjectUI packages so Tailwind generates their utility classes */
+@source "../../node_modules/@object-ui/components/src/**/*.tsx";
+@source "../../node_modules/@object-ui/fields/src/**/*.tsx";
+@source "../../node_modules/@object-ui/layout/src/**/*.tsx";
+@source "../../node_modules/@object-ui/react/src/**/*.tsx";
+
+/* Map Shadcn CSS variables to Tailwind 4 color tokens */
+@theme {
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --color-card: var(--card);
+ --color-card-foreground: var(--card-foreground);
+ --color-primary: var(--primary);
+ --color-primary-foreground: var(--primary-foreground);
+ --color-secondary: var(--secondary);
+ --color-secondary-foreground: var(--secondary-foreground);
+ --color-muted: var(--muted);
+ --color-muted-foreground: var(--muted-foreground);
+ --color-accent: var(--accent);
+ --color-accent-foreground: var(--accent-foreground);
+ --color-destructive: var(--destructive);
+ --color-border: var(--border);
+ --color-input: var(--input);
+ --color-ring: var(--ring);
+ --radius-sm: calc(var(--radius) - 4px);
+ --radius-md: calc(var(--radius) - 2px);
+ --radius-lg: var(--radius);
+ --radius-xl: calc(var(--radius) + 4px);
+}
+
+/* Light mode CSS variables (Shadcn defaults) */
+:root {
+ --background: oklch(1 0 0);
+ --foreground: oklch(0.145 0 0);
+ --card: oklch(1 0 0);
+ --card-foreground: oklch(0.145 0 0);
+ --primary: oklch(0.205 0 0);
+ --primary-foreground: oklch(0.985 0 0);
+ --secondary: oklch(0.97 0 0);
+ --secondary-foreground: oklch(0.205 0 0);
+ --muted: oklch(0.97 0 0);
+ --muted-foreground: oklch(0.556 0 0);
+ --accent: oklch(0.97 0 0);
+ --accent-foreground: oklch(0.205 0 0);
+ --destructive: oklch(0.577 0.245 27.325);
+ --border: oklch(0.922 0 0);
+ --input: oklch(0.922 0 0);
+ --ring: oklch(0.708 0 0);
+ --radius: 0.625rem;
+}
+```
+
+**Without this setup, ObjectUI components will render but look completely unstyled.**