Understanding the Structure of a Skill in OpenWork
An OpenWork Skill is a lightweight metadata object defined by the SkillItem interface that points to a Markdown source file containing executable logic, parsed by the skill-markdown.ts utility.
The different-ai/openwork repository implements a modular skill system where reusable functionality—ranging from simple commands to complex browser automation—is encapsulated as discrete units. Understanding the structure of a Skill in OpenWork requires examining both the TypeScript metadata definitions in the server types and the Markdown source convention that stores the actual implementation.
The Core Metadata: SkillItem Interface
According to the source code in dev/apps/server/src/types.ts, every Skill is represented by a SkillItem interface that serves as the server's indexing and routing contract. This lightweight object contains six key properties:
- name: Human-readable identifier used in the UI and for routing
- path: Filesystem location relative to the workspace (e.g.,
"src/skills/my-skill.md") - description: Short summary displayed in the Skills picker
- scope: Visibility level—either
"project"(workspace-private) or"global"(shared across all workspaces) - trigger: Optional slash-command string (e.g.,
"/my-skill") for chat invocation - error: Optional error message populated when the skill file fails to parse or compile
The scope property determines whether a Skill is registered per-project or globally, while the trigger enables direct invocation from the chat interface without navigating the Skills picker.
The Markdown Source Convention
Beyond the metadata object, the actual Skill content resides in Markdown files following a strict front-matter convention. The parser located in dev/ee/packages/utils/src/skill-markdown.ts extracts three components via the parseSkillMarkdown() function:
- Front-matter (delimited by
---): Containsnameand optionaldescription - Body: Executable markdown including code blocks, prompts, and instructional text
- hasFrontmatter: Boolean indicating successful front-matter extraction
Thus, a complete Skill consists of the metadata object (SkillItem) used for indexing, and the Markdown source file that provides the implementation and UI rendering instructions.
Server-Side Skill Management
The dev/apps/server/src/skills.ts module implements the server-side discovery and rendering pipeline. Two critical functions manage the Skill lifecycle:
Skill Discovery: The listSkills() function aggregates all available Skills for a given workspace, scanning the filesystem and returning an array of SkillItem objects populated with metadata extracted from each Markdown file.
Content Rendering: The renderSkillContentForResponse() function prepares the Markdown body for client consumption, wrapping content in renderable blocks optimized for the chat interface.
Practical Implementation Examples
Defining a Skill in Markdown
Create a file at skills/example-skill.md:
---
name: Example Skill
description: Demonstrates the skill structure
---
## What this skill does
It replies with a friendly greeting.
```typescript
export async function run() {
return "Hello from Example Skill!";
}
When parsed by `parseSkillMarkdown()`, this generates a `SkillItem` object with `scope: "project"` (default for filesystem-discovered skills) and the extracted metadata.
### Loading Skills Programmatically
```typescript
import { listSkills } from "./skills.js";
import { WorkspaceInfo } from "./types.js";
async function getWorkspaceSkills(workspace: WorkspaceInfo) {
// Returns SkillItem[] indexed from the filesystem
const skills = await listSkills(workspace.path, true);
return skills;
}
This function, defined in dev/apps/server/src/skills.ts, reads the workspace directory structure and builds the complete list of available Skills.
Rendering for the Chat UI
import { renderSkillContentForResponse } from "./skills.js";
function formatSkillResponse(item: SkillItem, markdownBody: string) {
// Wraps body for client-side rendering in the chat interface
return renderSkillContentForResponse(item, markdownBody);
}
This utility ensures that Skill content is properly formatted before transmission to the client, handling any necessary markdown transformations or metadata injection.
Key Files in the Skill Architecture
dev/apps/server/src/types.ts: Defines theSkillIteminterface and related type contractsdev/apps/server/src/skills.ts: ImplementslistSkills()for discovery andrenderSkillContentForResponse()for renderingdev/ee/packages/utils/src/skill-markdown.ts: ContainsparseSkillMarkdown()for front-matter extraction and markdown compositiondev/apps/server/src/skills.test.ts: Unit tests validating the parsing logic and API surface
These files collectively define how OpenWork models a Skill: a structured metadata object that points to a Markdown source file, parsed by specialized utilities, and managed by server-side aggregation functions.
Summary
- Skills are metadata objects: The
SkillIteminterface indev/apps/server/src/types.tsdefines the canonical structure with properties for identification, scope, and routing. - Content lives in Markdown: Skill implementations use front-matter for metadata and the body for executable logic, parsed by
parseSkillMarkdown()inskill-markdown.ts. - Scope controls visibility: The
scopeproperty distinguishes between project-specific ("project") and global ("global") Skills. - Triggers enable chat integration: Optional
triggerproperties allow slash-command invocation from the chat interface without UI navigation. - Server-side aggregation: The
skills.tsmodule handles filesystem discovery vialistSkills()and content preparation viarenderSkillContentForResponse().
Frequently Asked Questions
What properties are required in a SkillItem object?
The SkillItem interface requires four properties: name, path, description, and scope. The trigger and error properties are optional, with error typically populated dynamically by the server when parsing fails or compilation errors occur.
How does OpenWork distinguish between project and global Skills?
The scope property accepts either "project" or "global" as defined in dev/apps/server/src/types.ts. Project-scoped Skills are private to their workspace and loaded from the local project directory, while global Skills are shared across all workspaces and typically loaded from a global configuration path.
Where is Skill content actually stored?
Skill content resides in Markdown files referenced by the path property of the SkillItem object. These files contain YAML front-matter for metadata and a Markdown body for executable logic, parsed by the parseSkillMarkdown() function in dev/ee/packages/utils/src/skill-markdown.ts.
How are Skills invoked from the chat interface?
When a user types a trigger string (e.g., "/my-skill") defined in the optional trigger property, the server routes the request to the corresponding Skill, reads the Markdown file from the path location, parses the front-matter, and renders the content using renderSkillContentForResponse() for display in the client UI.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →