# How to Create and Manage Skills for Claudian: File Structure, Storage, and API Guide

> Learn to create and manage Claudian skills. This guide covers file structure, storage, and API usage for custom markdown-based slash commands in your YishenTu/claudian repository.

- Repository: [YishenTu/claudian](https://github.com/YishenTu/claudian)
- Tags: how-to-guide
- Published: 2026-03-17

---

**Claudian skills are custom slash commands stored as markdown files in `/.claude/skills/<name>/SKILL.md` that support YAML front-matter configuration for model invocation control, user permissions, and tool access.**

Claudian implements a robust skill management system that treats specialized prompts as first-class executable units within Obsidian. Unlike standard slash commands, skills support advanced configuration options including sub-agent forking, restricted tool access, and invocation control, all persisted through a decoupled storage layer. This article explores the complete lifecycle of Claudian skills—from authoring the [`SKILL.md`](https://github.com/YishenTu/claudian/blob/main/SKILL.md) files to programmatic manipulation via the TypeScript API.

## Understanding the Skill Architecture

Claudian organizes skill management into five distinct layers, each handled by specific modules in the codebase:

| Layer | Responsibility | Source File |
|-------|---------------|-------------|
| **File format** | YAML front-matter + prompt body | [`src/utils/frontmatter.ts`](https://github.com/YishenTu/claudian/blob/main/src/utils/frontmatter.ts) |
| **Parsing** | Converts markdown to `SlashCommand` objects | [`src/utils/slashCommand.ts`](https://github.com/YishenTu/claudian/blob/main/src/utils/slashCommand.ts) |
| **Storage** | Vault I/O operations via `loadAll`, `save`, `delete` | [`src/core/storage/SkillStorage.ts`](https://github.com/YishenTu/claudian/blob/main/src/core/storage/SkillStorage.ts) |
| **UI** | Modal dialogs and settings interfaces | [`src/features/settings/ui/SlashCommandSettings.ts`](https://github.com/YishenTu/claudian/blob/main/src/features/settings/ui/SlashCommandSettings.ts) |
| **Type definitions** | Interface definitions with skill-specific fields | [`src/core/types/settings.ts`](https://github.com/YishenTu/claudian/blob/main/src/core/types/settings.ts) |

Skills differ from regular slash commands in that they default to **model-only invocation** unless explicitly configured otherwise. The separation of concerns ensures that malformed individual skills never crash the entire system during loading.

## Skill File Structure and Format

Every skill resides in its own directory under the vault root, following a strict naming convention:

```

/.claude/skills/
├─ my-skill/
│  └─ SKILL.md

```

The [`SKILL.md`](https://github.com/YishenTu/claudian/blob/main/SKILL.md) file must begin with a YAML front-matter block delimited by triple dashes. This metadata controls execution behavior and supports both kebab-case and camelCase key variants:

| Front-matter key | Type | Purpose |
|------------------|------|---------|
| `description` | string | Human-readable summary shown in UI |
| `disable-model-invocation` / `disableModelInvocation` | boolean | Prevents Claude from auto-invoking the skill |
| `user-invocable` / `userInvocable` | boolean | Allows manual user triggering via `/skill-name` |
| `context` | string | Set to `fork` to run in isolated sub-agent |
| `agent` | string | Sub-agent name when using `context: fork` |
| `allowed-tools` | string[] | Whitelist of tools (Bash, Read, Write, etc.) |
| `hooks` | object | Arbitrary JSON passed to Claude SDK |

The content following the closing `---` serves as the prompt template. Use `$ARGUMENTS` as a placeholder for dynamic input.

**Example skill definition** ([`.claude/skills/commit/SKILL.md`](https://github.com/YishenTu/claudian/blob/main/.claude/skills/commit/SKILL.md)):

```markdown
---
description: Commit staged changes
disable-model-invocation: true
user-invocable: false
allowed-tools:
  - Bash
model: sonnet
---
Commit the staged files with the message:
$ARGUMENTS

```

## Parsing and Loading Skills

When Claudian initializes, `SkillStorage.loadAll()` traverses the `/.claude/skills/` directory and processes each [`SKILL.md`](https://github.com/YishenTu/claudian/blob/main/SKILL.md) through `parseSlashCommandContent` in [`src/utils/slashCommand.ts`](https://github.com/YishenTu/claudian/blob/main/src/utils/slashCommand.ts).

The parser handles normalization between kebab-case file formats and camelCase internal properties:

```typescript
// src/utils/slashCommand.ts
disableModelInvocation:
  extractBoolean(fm, 'disable-model-invocation') ??
  extractBoolean(fm, 'disableModelInvocation'),
userInvocable:
  extractBoolean(fm, 'user-invocable') ??
  extractBoolean(fm, 'userInvocable'),
context: extractString(fm, 'context') === 'fork' ? 'fork' : undefined,
agent: extractString(fm, 'agent'),
hooks: isRecord(fm.hooks) ? fm.hooks : undefined,

```

The `parseFrontmatter` utility in [`src/utils/frontmatter.ts`](https://github.com/YishenTu/claudian/blob/main/src/utils/frontmatter.ts) provides fallback tolerant parsing, ensuring that YAML syntax errors in one skill do not prevent others from loading. Individual file errors are caught and silently skipped during the `loadAll()` operation.

## Storage Layer Operations

The `SkillStorage` class in [`src/core/storage/SkillStorage.ts`](https://github.com/YishenTu/claudian/blob/main/src/core/storage/SkillStorage.ts) abstracts vault file system operations through three primary methods:

- **`loadAll()`** - Returns an array of `SlashCommand` objects by reading every [`SKILL.md`](https://github.com/YishenTu/claudian/blob/main/SKILL.md) in `/.claude/skills/`. Errors in specific files are isolated and logged without breaking the bulk operation.
- **`save(skill)`** - Serializes a `SlashCommand` to markdown using kebab-case keys, creates the necessary folder structure via `ensureFolder`, and writes to [`SKILL.md`](https://github.com/YishenTu/claudian/blob/main/SKILL.md).
- **`delete(skillId)`** - Removes the [`SKILL.md`](https://github.com/YishenTu/claudian/blob/main/SKILL.md) file and its containing directory using `deleteFolder`.

All paths are relative to the vault root, and the storage layer guarantees atomic folder creation before file writes.

## Creating and Editing Skills via UI

The settings interface ([`src/features/settings/ui/SlashCommandSettings.ts`](https://github.com/YishenTu/claudian/blob/main/src/features/settings/ui/SlashCommandSettings.ts)) manages skills through the `SlashCommandModal` component. When creating a new skill via the **+** button:

1. Select **"Type: Skill"** from the dropdown to reveal skill-specific toggles
2. Configure **"Disable model invocation"** and **"Disable user invocation"** checkboxes
3. Set **"Context"** to `fork` and specify an **"Agent"** if running isolated sub-agents
4. Input the prompt body directly or write raw front-matter in the markdown textarea

The modal uses `parseSlashCommandContent` to validate inline front-matter entries, allowing power users to author skills entirely within the text editor. When saving, the UI constructs a `SlashCommand` object with `source: 'user'` and persists it via `storage.skills.save()`.

## Converting Commands to Skills

Existing slash commands can be promoted to skills through the **"Convert to skill"** button in `SlashCommandSettings`. The transformation logic (`transformToSkill`, lines 27-55) performs:

1. **Name sanitization** - Converts the command name to lowercase alphanumerics with hyphens
2. **Property migration** - Copies description and content, sets `id: 'skill-<name>'` and `source: 'user'`
3. **Atomic replacement** - Saves the new skill via `storage.skills.save()` and deletes the original command file

This one-click migration preserves the original prompt logic while unlocking skill-specific capabilities like invocation controls and tool restrictions.

## Programmatic Skill Management

Developers can manage skills directly through the storage API for automation or plugin integration:

```typescript
import { SkillStorage } from '@/core/storage/SkillStorage';
import type { VaultFileAdapter } from '@/core/storage/VaultFileAdapter';
import type { SlashCommand } from '@/core/types';

// Initialize with your VaultFileAdapter instance
const skillStorage = new SkillStorage(adapter);

// Retrieve all skills
const allSkills: SlashCommand[] = await skillStorage.loadAll();

// Create a skill programmatically
const newSkill: SlashCommand = {
  id: 'skill-quick-note',
  name: 'quick-note',
  description: 'Create a short note from the model',
  content: 'Write a concise note about: $ARGUMENTS',
  disableModelInvocation: true,
  userInvocable: true,
  source: 'user',
};

await skillStorage.save(newSkill);

// Remove a skill
await skillStorage.delete('skill-quick-note');

```

This API enables dynamic skill generation, bulk imports, or conditional skill registration based on vault state.

## End-to-End Implementation Example

**Step 1: Manual file creation**

Create [`.claude/skills/todo/SKILL.md`](https://github.com/YishenTu/claudian/blob/main/.claude/skills/todo/SKILL.md):

```markdown
---
description: Add a todo item to the user's TODO file
disable-model-invocation: true
user-invocable: true
allowed-tools:
  - Read
  - Write
---
Add a new todo entry:

$ARGUMENTS

```

**Step 2: Runtime loading**

```typescript
const skills = await plugin.storage.skills.loadAll();
const todoSkill = skills.find(s => s.name === 'todo');
console.log('Skill loaded:', todoSkill?.description);

```

**Step 3: Model invocation**

When Claude generates a tool-use request:

```json
{
  "type": "tool_use",
  "name": "Skill",
  "input": { "skill": "todo", "arguments": "Buy groceries" }
}

```

Claudian substitutes `$ARGUMENTS` with "Buy groceries", restricts execution to `Read` and `Write` tools, and prevents the model from recursively invoking the skill due to `disable-model-invocation: true`.

## Summary

- Skills are defined in `/.claude/skills/<name>/SKILL.md` with YAML front-matter controlling execution permissions and tool access
- The `SkillStorage` class in [`src/core/storage/SkillStorage.ts`](https://github.com/YishenTu/claudian/blob/main/src/core/storage/SkillStorage.ts) provides `loadAll()`, `save()`, and `delete()` methods with error isolation
- `parseSlashCommandContent` in [`src/utils/slashCommand.ts`](https://github.com/YishenTu/claudian/blob/main/src/utils/slashCommand.ts) normalizes kebab-case file syntax to camelCase internal properties
- The settings UI (`SlashCommandSettings`) supports both visual editing and raw markdown input for skill creation
- Regular slash commands convert to skills via `transformToSkill`, which handles ID generation and atomic file replacement
- Programmatic API access enables dynamic skill management for plugin developers and automation scripts

## Frequently Asked Questions

### What is the difference between a slash command and a skill in Claudian?

A standard slash command is a user-triggered prompt, while a skill is a specialized command that can be restricted to model-only invocation, limited to specific tools, and executed in forked sub-agent contexts. Skills support additional front-matter keys like `disable-model-invocation` and `context` that regular commands lack, and they are stored in dedicated [`SKILL.md`](https://github.com/YishenTu/claudian/blob/main/SKILL.md) files rather than the general commands directory.

### How do I prevent the Claude model from automatically calling a skill?

Set `disable-model-invocation: true` (or `disableModelInvocation: true`) in the skill's front-matter. According to [`src/utils/slashCommand.ts`](https://github.com/YishenTu/claudian/blob/main/src/utils/slashCommand.ts), this boolean flag maps to the `disableModelInvocation` property on the `SlashCommand` object, which the execution layer checks before allowing tool-use requests.

### Can skills be organized in subdirectories within `/.claude/skills/`?

The current implementation in [`src/core/storage/SkillStorage.ts`](https://github.com/YishenTu/claudian/blob/main/src/core/storage/SkillStorage.ts) expects each skill to reside in its own immediate subdirectory under `/.claude/skills/<skill-name>/SKILL.md`. While `loadAll()` walks the directory tree, the standard UI operations assume a flat structure where the folder name matches the skill identifier. For complex organizations, use the programmatic API with custom path handling.

### What happens if a SKILL.md file contains invalid YAML?

The `parseFrontmatter` utility in [`src/utils/frontmatter.ts`](https://github.com/YishenTu/claudian/blob/main/src/utils/frontmatter.ts) implements fallback tolerant parsing. If front-matter extraction fails, `SkillStorage.loadAll()` catches the error for that specific file and continues loading remaining skills. The malformed file is silently skipped rather than crashing the entire skill registry, ensuring system stability.