Module: Gem::Skill::Cache

Defined in:
lib/gem/skill/cache.rb

Overview

Manages the global ~/.gem/skills cache. Structure: ~/.gem/skills/<gem_name>/<version>/SKILL.md

Constant Summary collapse

ROOT =
File.expand_path(ENV.fetch("GEMSKILL_DIR", "~/.gem/skills")).freeze

Class Method Summary collapse

Class Method Details

.all_gemsObject



76
77
78
79
80
# File 'lib/gem/skill/cache.rb', line 76

def self.all_gems
  return [] unless Dir.exist?(ROOT)

  Dir.children(ROOT).sort
end

.cached?(gem_name, version) ⇒ Boolean

Returns:

  • (Boolean)


23
24
25
# File 'lib/gem/skill/cache.rb', line 23

def self.cached?(gem_name, version)
  File.exist?(skill_path(gem_name, version))
end

.merge_metadata(gem_name, version, extra) ⇒ Object

Merge additional keys into the existing metadata.json, preserving generated_at, model, sources, etc. Keys are normalized to strings so a symbol key never collides with its string twin.



64
65
66
67
# File 'lib/gem/skill/cache.rb', line 64

def self.(gem_name, version, extra)
  data = (gem_name, version).merge(extra.transform_keys(&:to_s))
  File.write((gem_name, version), JSON.generate(data))
end

.metadata_path(gem_name, version) ⇒ Object



19
20
21
# File 'lib/gem/skill/cache.rb', line 19

def self.(gem_name, version)
  File.join(ROOT, gem_name, version, "metadata.json")
end

.purge(gem_name, version) ⇒ Object



82
83
84
85
86
87
# File 'lib/gem/skill/cache.rb', line 82

def self.purge(gem_name, version)
  dir = File.join(ROOT, gem_name, version)
  FileUtils.rm_rf(dir)
  parent = File.join(ROOT, gem_name)
  Dir.rmdir(parent) if Dir.exist?(parent) && Dir.empty?(parent)
end

.read(gem_name, version) ⇒ Object

Raises:



38
39
40
41
42
43
# File 'lib/gem/skill/cache.rb', line 38

def self.read(gem_name, version)
  path = skill_path(gem_name, version)
  raise Error, "No cached skill for #{gem_name} #{version}" unless File.exist?(path)

  File.read(path)
end

.read_metadata(gem_name, version) ⇒ Object

Read metadata.json back as a Hash with string keys. Returns {} if absent or unparseable.



47
48
49
50
51
52
53
54
# File 'lib/gem/skill/cache.rb', line 47

def self.(gem_name, version)
  path = (gem_name, version)
  return {} unless File.exist?(path)

  JSON.parse(File.read(path))
rescue JSON::ParserError
  {}
end

.rootObject



13
# File 'lib/gem/skill/cache.rb', line 13

def self.root = ROOT

.skill_path(gem_name, version) ⇒ Object



15
16
17
# File 'lib/gem/skill/cache.rb', line 15

def self.skill_path(gem_name, version)
  File.join(ROOT, gem_name, version, "SKILL.md")
end

.store(gem_name, version, skill_content, metadata = {}) ⇒ Object



27
28
29
30
31
32
33
34
35
36
# File 'lib/gem/skill/cache.rb', line 27

def self.store(gem_name, version, skill_content,  = {})
  dir = File.join(ROOT, gem_name, version)
  FileUtils.mkdir_p(dir)
  File.write(skill_path(gem_name, version), skill_content)
  File.write((gem_name, version), JSON.generate(.merge(
    gem_name: gem_name,
    version: version,
    generated_at: Time.now.iso8601
  )))
end

.versions(gem_name) ⇒ Object



69
70
71
72
73
74
# File 'lib/gem/skill/cache.rb', line 69

def self.versions(gem_name)
  dir = File.join(ROOT, gem_name)
  return [] unless Dir.exist?(dir)

  Dir.children(dir).sort
end

.write_skill(gem_name, version, skill_content) ⇒ Object

Overwrite just the SKILL.md content, leaving metadata untouched.



57
58
59
# File 'lib/gem/skill/cache.rb', line 57

def self.write_skill(gem_name, version, skill_content)
  File.write(skill_path(gem_name, version), skill_content)
end