Agent::ContextSourceAgentContextInstaller

class Installer

Installer class for managing context files from Ruby gems.

This class provides methods to find, list, show, and install context files from gems that provide them in a context/ directory.

Definitions

def initialize(root: Dir.pwd, specifications: ::Gem::Specification)

Initialize a new Installer instance.

Signature

parameter root String

The root directory to work from (default: current directory).

parameter specifications Gem::Specification

The gem specifications to search (default: all installed gems).

Implementation

def initialize(root: Dir.pwd, specifications: ::Gem::Specification)
	@root = root
	@context_path = ".context"
	@specifications = specifications
end

def find_gems_with_context(skip_local: true)

Find all gems that have a context directory

Implementation

def find_gems_with_context(skip_local: true)
	gems_with_context = []
		
	@specifications.each do |spec|
		# Skip gems loaded from current working directory if requested:
		next if skip_local && spec.full_gem_path == @root
		
		context_path = File.join(spec.full_gem_path, "context")
		if Dir.exist?(context_path)
			gems_with_context << {
				name: spec.name,
				version: spec.version.to_s,
				summary: spec.summary,
				metadata: spec.metadata,
				path: context_path
			}
		end
	end
		
	gems_with_context
end

def find_gem_with_context(gem_name)

Find a specific gem with context.

Implementation

def find_gem_with_context(gem_name)
	spec = @specifications.find {|spec| spec.name == gem_name}
	return nil unless spec
		
	context_path = File.join(spec.full_gem_path, "context")
		
	if Dir.exist?(context_path)
		{
				name: spec.name,
				version: spec.version.to_s,
				summary: spec.summary,
				metadata: spec.metadata,
				path: context_path
			}
	else
		nil
	end
end

def list_context_files(gem_name)

List context files for a gem.

Implementation

def list_context_files(gem_name)
	gem = find_gem_with_context(gem_name)
	return nil unless gem
		
	Dir.glob(File.join(gem[:path], "**/*")).select {|f| File.file?(f)}
end

def show_context_file(gem_name, file_name)

Show content of a specific context file.

Implementation

def show_context_file(gem_name, file_name)
	gem = find_gem_with_context(gem_name)
	return nil unless gem
		
	# Try to find the file with or without extension:
	possible_paths = [
			File.join(gem[:path], file_name),
			File.join(gem[:path], "#{file_name}.md"),
			File.join(gem[:path], "#{file_name}.md")
		]
		
	file_path = possible_paths.find {|path| File.exist?(path)}
	return nil unless file_path
		
	File.read(file_path)
end

def install_gem_context(gem_name)

Install context from a specific gem.

Implementation

def install_gem_context(gem_name)
	gem = find_gem_with_context(gem_name)
	return false unless gem
	
	target_path = File.join(@context_path, gem_name)
	
	# Remove old package directory if it exists to ensure clean install
	FileUtils.rm_rf(target_path) if Dir.exist?(target_path)
	
	FileUtils.mkdir_p(target_path)
	
	# Copy all files from the gem's context directory:
	FileUtils.cp_r(File.join(gem[:path], "."), target_path)
	
	# Generate index.yaml if it doesn't exist, passing the full gem hash
	ensure_gem_index(gem, target_path)
	
	true
end

def install_all_context(skip_local: true)

Install context from all gems.

Implementation

def install_all_context(skip_local: true)
	gems = find_gems_with_context(skip_local: skip_local)
	installed = []
		
	gems.each do |gem|
		if install_gem_context(gem[:name])
			installed << gem[:name]
		end
	end
		
	installed
end

def generate_dynamic_index(gem, gem_directory)

Generate a dynamic index from gemspec when no index.yaml is present

Implementation

def generate_dynamic_index(gem, gem_directory)
	# Collect all markdown files
	markdown_files = Dir.glob(File.join(gem_directory, "**", "*.md")).sort
	
	# Sort files: canonical first, then alpha
	files_sorted = markdown_files.sort_by do |file_path|
		base_filename = File.basename(file_path, ".md").downcase
		canonical_index = CANONICAL_ORDER.index(base_filename)
		[canonical_index ? CANONICAL_ORDER.index(base_filename) : CANONICAL_ORDER.length, base_filename]
	end
	
	files = []
	files_sorted.each do |file_path|
		next if File.basename(file_path) == "index.yaml" # Skip the index file itself
		title, description = extract_content(file_path)
		relative_path = file_path.sub("#{gem_directory}/", "")
		files << {
			"path" => relative_path,
			"title" => title,
			"description" => description
		}
	end
	
	{
		"description" => gem[:summary] || "Context files for #{gem[:name]}",
		"version" => gem[:version],
		"metadata" => gem[:metadata],
		"files" => files
	}
end

def ensure_gem_index(gem, gem_directory)

Check if a gem has an index.yaml file, generate one if not

Implementation

def ensure_gem_index(gem, gem_directory)
	index_path = File.join(gem_directory, "index.yaml")
	
	unless File.exist?(index_path)
		# Generate dynamic index from gemspec
		index = generate_dynamic_index(gem, gem_directory)
		
		# Write the generated index
		File.write(index_path, index.to_yaml)
		Console.debug("Generated dynamic index for #{gem[:name]}: #{index_path}")
	end
	
	# Load and return the index
	YAML.load_file(index_path)
rescue => error
	Console.debug("Error generating index for #{gem[:name]}: #{error.message}")
	# Return a fallback index
	{
		"description" => gem[:summary] || "Context files for #{gem[:name]}",
		"version" => gem[:version],
		"metadata" => gem[:metadata],
		"files" => []
	}
end