core-development
npx machina-cli add skill aiskillstore/marketplace/core-development --openclawCore Package Development
The core package (packages/core/) is dependency-free and handles all DSL processing.
Data Flow
DSL (YAML input) → validate() → normalize() → IR → diff() → Patch
Key Files
| File | Purpose | Exports |
|---|---|---|
types.ts | Type definitions | DSL*, IR*, Patch, WebSocket protocol |
validate.ts | YAML validation | validate(dsl): ValidationResult |
normalize.ts | DSL → IR conversion | normalize(dsl): IRDocument |
diff.ts | IR diff calculation | diff(prev, next): Patch |
Type Hierarchy
DSL Types (user input) IR Types (normalized)
───────────────────── ────────────────────
DSLDocument IRDocument
├─ version: number ├─ version: number
├─ docId: string ├─ docId: string
├─ title?: string ├─ title: string
├─ nodes: DSLNode[] ├─ nodes: Record<string, IRNode>
└─ edges?: DSLEdge[] └─ edges: Record<string, IREdge>
DSLNode IRNode
├─ id: string ├─ id: string
├─ provider: string ├─ provider: string
├─ kind: string ├─ kind: string
├─ label?: string ├─ label: string (default: id)
├─ parent?: string ├─ parent: string | null
└─ layout: DSLLayout └─ layout: { x, y, w, h }
DSLEdge IREdge
├─ id: string ├─ id: string
├─ from: string ├─ from: string
├─ to: string ├─ to: string
└─ label?: string └─ label: string (default: "")
Patch Operations
type PatchOp =
| { op: "upsertNode"; node: IRNode }
| { op: "removeNode"; id: string }
| { op: "upsertEdge"; edge: IREdge }
| { op: "removeEdge"; id: string };
interface Patch {
baseRev: number;
nextRev: number;
ops: PatchOp[];
}
WebSocket Protocol Types
// Plugin → CLI
interface HelloMessage {
type: "hello";
docId: string;
secret?: string;
}
interface RequestFullMessage {
type: "requestFull";
docId: string;
}
// CLI → Plugin
interface FullMessage {
type: "full";
rev: number;
ir: IRDocument;
}
interface PatchMessage {
type: "patch";
baseRev: number;
nextRev: number;
ops: PatchOp[];
}
interface ErrorMessage {
type: "error";
message: string;
}
Development Workflow
- Modify types → Update
types.ts - Update validation → Ensure
validate.tscatches invalid input - Update normalization → Handle new fields/defaults in
normalize.ts - Update diff → Handle new patch scenarios in
diff.ts - Add tests → Co-located
*.test.tsfiles - Run tests →
bun test packages/core/
Testing
# All core tests
bun test packages/core/
# Specific test file
bun test packages/core/src/diff.test.ts
bun test packages/core/src/validate.test.ts
bun test packages/core/src/normalize.test.ts
# Watch mode
bun test --watch packages/core/
Common Patterns
Adding a new node property
- Add to
DSLNodeandIRNodeintypes.ts - Add validation in
validate.ts - Add default value handling in
normalize.ts - Update diff logic if property affects equality
- Add test cases for validation, normalization, and diff
Adding a new edge property
- Add to
DSLEdgeandIREdgeintypes.ts - Add validation in
validate.ts - Add default value handling in
normalize.ts - Update diff logic for edge equality check
- Add test cases
Source
git clone https://github.com/aiskillstore/marketplace/blob/main/skills/7nohe/core-development/SKILL.mdView on GitHub Overview
Core package development covers the essential DSL processing pipeline: validating YAML input, normalizing it into an IR, and diffing IR states to produce patches. It’s the backbone for any changes to DSL processing logic or data flow. This skill ensures the core types, validation rules, and patch logic stay in sync as the system evolves.
How This Skill Works
The core package processes DSL input through a fixed pipeline: DSL YAML input is validated, then normalized into an IRDocument, which is diffed against the previous IR to produce a Patch. The core files—types.ts, validate.ts, normalize.ts, and diff.ts—expose typed interfaces and operations; WebSocket protocol types define messaging for live synchronization. Development workflow emphasizes updating these files in tandem and adding tests to verify the full flow.
When to Use It
- You’re adding or modifying core DSL types in types.ts (e.g., DSLNode, DSLEdge, IRNode, IREdge).
- You need to adjust validation rules in validate.ts for new or changed DSL input constraints.
- You’re changing DSL→IR conversion logic in normalize.ts (defaults, mappings, or field handling).
- You’re extending patch logic in diff.ts to account for new PatchOp scenarios or edge cases.
- You’re improving or expanding the core flow and tests in the core package (adding/co-locating tests and running the core test suite).
Quick Start
- Step 1: Identify the affected core files (types.ts, validate.ts, normalize.ts, diff.ts) based on the change.
- Step 2: Implement changes across the core files and update or add tests to cover validation, normalization, and diff.
- Step 3: Run the core test suite: bun test packages/core/ and iterate until all tests pass.
Best Practices
- Keep types.ts, validate.ts, normalize.ts, and diff.ts in sync whenever you add or change fields.
- Add or update tests co-located with core files to cover validation, normalization, and diff scenarios.
- Run the core test suite regularly: bun test packages/core/.
- Document new fields and default behavior in normalize.ts to avoid silent regressions.
- Plan for backward compatibility when changing patch semantics or patch operations.
Example Use Cases
- Add a new optional 'description' field to DSLNode and IRNode, ensure validation allows it, and populate a default label during normalization.
- Introduce a new DSLEdge property (e.g., 'type') and implement validation, normalization defaults, and adjusted diff logic.
- Require IRNode label to be non-optional by default and implement normalization to fallback to 'id' when missing.
- Change IR storage from an array to a map (Record<string, IREdge>) and update diff to handle edge-equality accordingly.
- Plan for a future patch operation (e.g., 'moveNode') by outlining changes in PatchOp types and adding placeholder tests to verify extensibility.