Class: KairosMcp::AnthropicSkillParser

Inherits:
Object
  • Object
show all
Defined in:
lib/kairos_mcp/anthropic_skill_parser.rb

Overview

AnthropicSkillParser: Parses Anthropic skills format (YAML frontmatter + Markdown)

Directory structure:

skill_name/
├── skill_name.md       # Required: YAML frontmatter + Markdown content
├── scripts/            # Optional: Executable scripts (Python, Bash, Node, etc.)
├── assets/             # Optional: Templates, images, CSS, etc.
└── references/         # Optional: Reference materials, datasets

Defined Under Namespace

Classes: SkillEntry

Class Method Summary collapse

Class Method Details

.create(base_dir, name, content, create_subdirs: false) ⇒ SkillEntry

Create a new skill directory with the Anthropic format

Parameters:

  • base_dir (String)

    Base directory (knowledge/ or context/session_id/)

  • name (String)

    Skill name

  • content (String)

    Full content including YAML frontmatter

  • create_subdirs (Boolean) (defaults to: false)

    Whether to create scripts/assets/references dirs

Returns:



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/kairos_mcp/anthropic_skill_parser.rb', line 99

def create(base_dir, name, content, create_subdirs: false)
  skill_dir = File.join(base_dir, name)
  FileUtils.mkdir_p(skill_dir)

  md_file = File.join(skill_dir, "#{name}.md")
  File.write(md_file, content)

  if create_subdirs
    FileUtils.mkdir_p(File.join(skill_dir, 'scripts'))
    FileUtils.mkdir_p(File.join(skill_dir, 'assets'))
    FileUtils.mkdir_p(File.join(skill_dir, 'references'))
  end

  parse(skill_dir)
end

.extract_frontmatter(content) ⇒ Array<Hash, String>

Extract frontmatter and body from content

Parameters:

  • content (String)

    Full file content

Returns:

  • (Array<Hash, String>)
    frontmatter, body


195
196
197
198
199
200
201
202
203
# File 'lib/kairos_mcp/anthropic_skill_parser.rb', line 195

def extract_frontmatter(content)
  if content =~ /\A---\r?\n(.+?)\r?\n---\r?\n(.*)/m
    frontmatter = YAML.safe_load($1, permitted_classes: [Symbol, Date, Time]) || {}
    body = $2
    [frontmatter, body]
  else
    [{}, content]
  end
end

.generate_content(frontmatter, body) ⇒ String

Generate YAML frontmatter + Markdown content

Parameters:

  • frontmatter (Hash)

    Frontmatter data

  • body (String)

    Markdown body content

Returns:

  • (String)

    Complete file content



186
187
188
189
# File 'lib/kairos_mcp/anthropic_skill_parser.rb', line 186

def generate_content(frontmatter, body)
  yaml_str = frontmatter.to_yaml.sub(/^---\n/, '')
  "---\n#{yaml_str}---\n\n#{body}"
end

.list_assets(skill) ⇒ Array<Hash>

List all assets in a skill

Parameters:

Returns:

  • (Array<Hash>)

    List of asset info



149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/kairos_mcp/anthropic_skill_parser.rb', line 149

def list_assets(skill)
  return [] unless skill.has_assets?

  Dir[File.join(skill.assets_path, '**/*')].select { |f| File.file?(f) }.map do |f|
    {
      name: File.basename(f),
      path: f,
      relative_path: f.sub(skill.assets_path + '/', ''),
      size: File.size(f),
      extension: File.extname(f)
    }
  end
end

.list_references(skill) ⇒ Array<Hash>

List all references in a skill

Parameters:

Returns:

  • (Array<Hash>)

    List of reference info



167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/kairos_mcp/anthropic_skill_parser.rb', line 167

def list_references(skill)
  return [] unless skill.has_references?

  Dir[File.join(skill.references_path, '**/*')].select { |f| File.file?(f) }.map do |f|
    {
      name: File.basename(f),
      path: f,
      relative_path: f.sub(skill.references_path + '/', ''),
      size: File.size(f),
      extension: File.extname(f)
    }
  end
end

.list_scripts(skill) ⇒ Array<Hash>

List all scripts in a skill

Parameters:

Returns:

  • (Array<Hash>)

    List of script info



132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/kairos_mcp/anthropic_skill_parser.rb', line 132

def list_scripts(skill)
  return [] unless skill.has_scripts?

  Dir[File.join(skill.scripts_path, '*')].map do |f|
    {
      name: File.basename(f),
      path: f,
      executable: File.executable?(f),
      size: File.size(f)
    }
  end
end

.parse(skill_dir) ⇒ SkillEntry?

Parse a skill directory and return a SkillEntry

Parameters:

  • skill_dir (String)

    Path to the skill directory

Returns:

  • (SkillEntry, nil)

    Parsed skill entry or nil if invalid



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/kairos_mcp/anthropic_skill_parser.rb', line 65

def parse(skill_dir)
  return nil unless File.directory?(skill_dir)

  md_file = find_md_file(skill_dir)
  return nil unless md_file

  content = File.read(md_file, encoding: 'UTF-8')
  frontmatter, body = extract_frontmatter(content)

  skill_name = File.basename(skill_dir)

  SkillEntry.new(
    name: frontmatter['name'] || skill_name,
    description: frontmatter['description'],
    version: frontmatter['version'],
    layer: frontmatter['layer'],
    tags: normalize_tags(frontmatter['tags']),
    content: body.strip,
    frontmatter: frontmatter,
    base_path: skill_dir,
    md_file_path: md_file,
    scripts_path: File.join(skill_dir, 'scripts'),
    assets_path: File.join(skill_dir, 'assets'),
    references_path: File.join(skill_dir, 'references')
  )
end

.update(skill_dir, new_content) ⇒ SkillEntry

Update an existing skill’s content

Parameters:

  • skill_dir (String)

    Path to the skill directory

  • new_content (String)

    New content including YAML frontmatter

Returns:



120
121
122
123
124
125
126
# File 'lib/kairos_mcp/anthropic_skill_parser.rb', line 120

def update(skill_dir, new_content)
  md_file = find_md_file(skill_dir)
  raise "No markdown file found in #{skill_dir}" unless md_file

  File.write(md_file, new_content)
  parse(skill_dir)
end