2023-12-29 15:53:54 +01:00
|
|
|
require "option_parser"
|
|
|
|
require "yaml"
|
|
|
|
|
2023-12-29 16:31:17 +01:00
|
|
|
require "./models/root_config"
|
2024-01-03 17:03:40 +01:00
|
|
|
require "./version"
|
2023-12-29 16:31:17 +01:00
|
|
|
|
2023-12-29 15:53:54 +01:00
|
|
|
module CodePreloader
|
|
|
|
class Config
|
2024-01-04 11:53:50 +01:00
|
|
|
|
|
|
|
enum Subcommand
|
|
|
|
None
|
|
|
|
Init
|
|
|
|
Pack
|
|
|
|
Help
|
|
|
|
Version
|
|
|
|
end
|
|
|
|
|
|
|
|
class InitOptions
|
|
|
|
property config_file_path : String? = nil
|
|
|
|
end
|
|
|
|
|
|
|
|
class PackOptions
|
|
|
|
property config_file_path : String? = nil
|
|
|
|
property repository_path_list : Array(String) = [] of String
|
|
|
|
property ignore_list : Array(String) = [] of String
|
|
|
|
property output_file_path : String?
|
|
|
|
property header_prompt_file_path : String?
|
|
|
|
property footer_prompt_file_path : String?
|
|
|
|
end
|
|
|
|
|
|
|
|
getter parser : OptionParser?
|
|
|
|
property subcommand : Subcommand = Subcommand::None
|
|
|
|
property pack_options : PackOptions?
|
|
|
|
property init_options : InitOptions?
|
2023-12-29 15:53:54 +01:00
|
|
|
|
|
|
|
def initialize()
|
|
|
|
end
|
|
|
|
|
2024-01-04 11:53:50 +01:00
|
|
|
def parse_init_options(parser)
|
|
|
|
@init_options = InitOptions.new
|
2023-12-29 15:53:54 +01:00
|
|
|
|
2024-01-04 11:53:50 +01:00
|
|
|
parser.banner = [
|
|
|
|
"#{PROGRAM_NAME} v#{VERSION}",
|
|
|
|
"Usage: code-preloader init [options]\n",
|
|
|
|
"Global options:"
|
|
|
|
].join("\n")
|
2023-12-29 15:53:54 +01:00
|
|
|
|
2024-01-04 11:53:50 +01:00
|
|
|
parser.separator "\nInit options:"
|
|
|
|
parser.unknown_args do |remaining_args, _|
|
|
|
|
# FIXME: detect and make error if there are more or less than one
|
|
|
|
remaining_args.each do |arg|
|
|
|
|
@init_options.try &.config_file_path = arg
|
2023-12-29 15:53:54 +01:00
|
|
|
end
|
2024-01-04 11:53:50 +01:00
|
|
|
end
|
2023-12-29 15:53:54 +01:00
|
|
|
|
2024-01-04 11:53:50 +01:00
|
|
|
parser.on(
|
|
|
|
"-c FILE",
|
|
|
|
"--config=FILE",
|
|
|
|
"Load parameters from FILE"
|
|
|
|
) do |config_file|
|
|
|
|
@init_options.try { |opt| opt.config_file_path = config_file }
|
|
|
|
end
|
2023-12-29 15:53:54 +01:00
|
|
|
|
2024-01-04 11:53:50 +01:00
|
|
|
parser.separator ""
|
|
|
|
|
|
|
|
parser.missing_option do |opt|
|
|
|
|
puts parser
|
|
|
|
abort("ERROR: Missing parameter for option #{opt}!")
|
|
|
|
end
|
|
|
|
|
|
|
|
parser.invalid_option do |opt|
|
|
|
|
puts parser
|
|
|
|
abort("ERROR: Invalid option #{opt}!")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def parse_pack_options(parser)
|
|
|
|
@pack_options = PackOptions.new
|
|
|
|
|
|
|
|
parser.banner = [
|
|
|
|
"#{PROGRAM_NAME} v#{VERSION}",
|
|
|
|
"Usage: code-preloader pack [options] DIR ...\n",
|
|
|
|
"Global options:"
|
|
|
|
].join("\n")
|
|
|
|
|
|
|
|
parser.separator "\nPack options:"
|
|
|
|
parser.on(
|
|
|
|
"-i REGEXP",
|
|
|
|
"--ignore=REGEXP",
|
|
|
|
"Ignore file or directory"
|
|
|
|
) do |ignore_file|
|
|
|
|
@pack_options.try { |opt| opt.ignore_list << ignore_file }
|
|
|
|
end
|
|
|
|
|
|
|
|
parser.on(
|
|
|
|
"-o FILE",
|
|
|
|
"--output=FILE",
|
|
|
|
"Write output to FILE"
|
|
|
|
) do |output_file|
|
|
|
|
@pack_options.try { |opt| opt.output_file_path = output_file }
|
|
|
|
end
|
|
|
|
|
|
|
|
parser.on(
|
|
|
|
"-H FILE",
|
|
|
|
"--header-prompt=FILE",
|
|
|
|
"Load header prompt from FILE"
|
|
|
|
) do |header_prompt_file|
|
|
|
|
@pack_options.try { |opt| opt.header_prompt_file_path = header_prompt_file }
|
|
|
|
end
|
|
|
|
|
|
|
|
parser.on(
|
|
|
|
"-F FILE",
|
|
|
|
"--footer-prompt=FILE",
|
|
|
|
"Load footer prompt from FILE"
|
|
|
|
) do |footer_prompt_file|
|
|
|
|
@pack_options.try { |opt| opt.footer_prompt_file_path = footer_prompt_file }
|
|
|
|
end
|
|
|
|
|
|
|
|
parser.on(
|
|
|
|
"-c FILE",
|
|
|
|
"--config=FILE",
|
|
|
|
"Load parameters from FILE"
|
|
|
|
) do |config_file|
|
|
|
|
@pack_options.try { |opt| load_pack_config(config_file) }
|
|
|
|
end
|
|
|
|
|
|
|
|
parser.separator ""
|
2023-12-29 15:53:54 +01:00
|
|
|
|
2024-01-04 11:53:50 +01:00
|
|
|
parser.unknown_args do |remaining_args, _|
|
|
|
|
remaining_args.each do |arg|
|
|
|
|
@pack_options.try { |opt| opt.repository_path_list << arg }
|
2023-12-29 15:53:54 +01:00
|
|
|
end
|
2024-01-04 11:53:50 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
parser.missing_option do |opt|
|
|
|
|
puts parser
|
|
|
|
abort("ERROR: Missing parameter for option #{opt}!")
|
|
|
|
end
|
|
|
|
|
|
|
|
parser.invalid_option do |ex|
|
|
|
|
puts parser
|
|
|
|
abort("ERROR: Invalid option #{ex}")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def parse_arguments(args : Array(String))
|
|
|
|
@parser = OptionParser.new do |parser|
|
|
|
|
parser.banner = [
|
|
|
|
"#{PROGRAM_NAME} v#{VERSION}",
|
|
|
|
"Usage: code-preloader <subcommand> [options] [DIR] [...]\n",
|
|
|
|
"Global options:"
|
|
|
|
].join("\n")
|
2023-12-29 15:53:54 +01:00
|
|
|
|
2024-01-03 17:03:40 +01:00
|
|
|
parser.on("--version", "Show version") do
|
|
|
|
STDOUT.puts "#{PROGRAM_NAME} #{VERSION}"
|
|
|
|
exit(0)
|
|
|
|
end
|
|
|
|
|
2023-12-29 15:53:54 +01:00
|
|
|
parser.on("-h", "--help", "Show this help") do
|
|
|
|
STDERR.puts parser
|
|
|
|
exit
|
|
|
|
end
|
|
|
|
|
2024-01-04 11:53:50 +01:00
|
|
|
parser.separator "\nSubcommands:"
|
|
|
|
|
|
|
|
parser.on("init", "Create an example .code_preloader.yml file") do
|
|
|
|
@subcommand = Subcommand::Init
|
|
|
|
parse_init_options(parser)
|
|
|
|
end
|
|
|
|
|
|
|
|
parser.on("pack", "Create the packed version of a directory for LLM prompting") do
|
|
|
|
@subcommand = Subcommand::Pack
|
|
|
|
parse_pack_options(parser)
|
|
|
|
end
|
|
|
|
|
|
|
|
parser.separator ""
|
|
|
|
|
|
|
|
parser.invalid_option do |ex|
|
|
|
|
puts parser
|
|
|
|
abort("ERROR: Invalid option #{ex}")
|
2023-12-29 15:53:54 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-01-04 11:53:50 +01:00
|
|
|
@parser.try &.parse(args)
|
2024-01-02 12:32:00 +01:00
|
|
|
validate
|
2023-12-29 15:53:54 +01:00
|
|
|
end
|
|
|
|
|
2024-01-04 11:53:50 +01:00
|
|
|
def detect_config
|
|
|
|
# FIXME: detect config name, if any
|
|
|
|
end
|
|
|
|
|
2024-01-02 12:32:00 +01:00
|
|
|
private def validate
|
2024-01-04 11:53:50 +01:00
|
|
|
case @subcommand
|
|
|
|
when Subcommand::Init then validate_init
|
|
|
|
when Subcommand::Pack then validate_pack
|
|
|
|
else
|
|
|
|
abort("Unknown subcommand #{@subcommand}")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private def validate_init
|
|
|
|
abort("No init options defined!") if @init_options.nil?
|
|
|
|
end
|
|
|
|
|
|
|
|
private def validate_pack
|
|
|
|
abort("No pack options defined!") if @pack_options.nil?
|
|
|
|
@pack_options.try do |opts|
|
|
|
|
abort("Missing repository path.") if opts.repository_path_list.empty?
|
|
|
|
|
|
|
|
if opts.output_file_path.nil? || opts.output_file_path.try(&.empty?)
|
|
|
|
STDERR.puts("Output file path not specified (using STDOUT)")
|
|
|
|
end
|
|
|
|
end
|
2023-12-29 15:53:54 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
# Reads and returns a list of paths to ignore from the given file.
|
|
|
|
def self.get_ignore_list(ignore_file_path : String) : Array(String)
|
|
|
|
File.exists?(ignore_file_path) ? File.read_lines(ignore_file_path).map(&.strip) : [] of String
|
|
|
|
rescue e : IO::Error
|
|
|
|
STDERR.puts "Error reading ignore file: #{e.message}"
|
|
|
|
exit(1)
|
|
|
|
end
|
|
|
|
|
2024-01-04 11:53:50 +01:00
|
|
|
private def load_pack_config(config_file_path : String)
|
2023-12-29 16:31:17 +01:00
|
|
|
config_str = File.read(config_file_path)
|
2023-12-29 15:53:54 +01:00
|
|
|
|
2023-12-29 16:31:17 +01:00
|
|
|
root = Models::RootConfig.from_yaml(config_str)
|
2023-12-29 15:53:54 +01:00
|
|
|
|
2024-01-04 11:53:50 +01:00
|
|
|
@pack_options.try do |opts|
|
|
|
|
opts.config_file_path = config_file_path
|
|
|
|
opts.repository_path_list = root.repository_path_list || opts.repository_path_list
|
|
|
|
opts.ignore_list = root.ignore_list || opts.ignore_list
|
|
|
|
opts.output_file_path = root.output_file_path || opts.output_file_path
|
|
|
|
opts.header_prompt_file_path = root.header_prompt_file_path || opts.header_prompt_file_path
|
|
|
|
opts.footer_prompt_file_path = root.footer_prompt_file_path || opts.footer_prompt_file_path
|
|
|
|
end
|
2023-12-29 15:53:54 +01:00
|
|
|
|
2024-01-02 12:32:00 +01:00
|
|
|
rescue ex : Exception
|
2023-12-29 15:53:54 +01:00
|
|
|
STDERR.puts "Failed to load config file: #{ex.message}"
|
|
|
|
exit(1)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|