Compare commits
3 commits
d296c9d159
...
e418f58f44
Author | SHA1 | Date | |
---|---|---|---|
e418f58f44 | |||
ca4485b1ca | |||
d873083ad8 |
4 changed files with 253 additions and 77 deletions
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
*.svg filter=lfs diff=lfs merge=lfs -text
|
15
prompts/request-subcommand.txt
Normal file
15
prompts/request-subcommand.txt
Normal file
|
@ -0,0 +1,15 @@
|
|||
@@REQUEST
|
||||
|
||||
I would like to change CLI parameters structure add add two subcommands:
|
||||
* init : which will create an example .code_preloader.yml file (with comments)
|
||||
* pack : which will create the packed version of the current directory for LLM prompting
|
||||
|
||||
Most of current options (except --version and --help) must become options of the pack subcommand.
|
||||
|
||||
I already started some changes to achieve this goal, but it is not finished, and I need your help and expert advises.
|
||||
|
||||
Can you please tell me :
|
||||
* where the changes should occur (which File? which Class? and which method?)
|
||||
* what kind of changes must be made there?
|
||||
|
||||
Please do not write code yet, simply explain.
|
65
src/cli.cr
65
src/cli.cr
|
@ -18,20 +18,52 @@ module CodePreloader
|
|||
def initialize(args)
|
||||
@output_file_path = ""
|
||||
@config = Config.new()
|
||||
@config.detect_config()
|
||||
@config.parse_arguments(args)
|
||||
end
|
||||
|
||||
# Executes the main functionality of the CLI application.
|
||||
def exec
|
||||
# get local values for typing
|
||||
output_file_path = @output_file_path
|
||||
repository_path_list = @config.repository_path_list
|
||||
header_prompt_file_path = @config.header_prompt_file_path
|
||||
footer_prompt_file_path = @config.footer_prompt_file_path
|
||||
case @config.subcommand
|
||||
when Config::Subcommand::Init then exec_init(@config.init_options)
|
||||
when Config::Subcommand::Pack then exec_pack(@config.pack_options)
|
||||
when Config::Subcommand::Version then exec_version
|
||||
when Config::Subcommand::Help then exec_help
|
||||
when Config::Subcommand::None then exec_none
|
||||
else
|
||||
abort("Unknown subcommand #{@config.subcommand}!")
|
||||
end
|
||||
end
|
||||
|
||||
def exec_init(init_options)
|
||||
abort("Unexpected nil value for init_options!") if init_options.nil?
|
||||
abort("FIXME: Not implemented!")
|
||||
end
|
||||
|
||||
def exec_version
|
||||
abort("FIXME: Not implemented!")
|
||||
end
|
||||
|
||||
def exec_none
|
||||
abort("No command specified!")
|
||||
end
|
||||
|
||||
def exec_help
|
||||
abort("FIXME: Not implemented!")
|
||||
end
|
||||
|
||||
def exec_pack(pack_options)
|
||||
abort("Unexpected nil value for pack_options!") if pack_options.nil?
|
||||
|
||||
output_file_path = pack_options.output_file_path
|
||||
repository_path_list = pack_options.repository_path_list
|
||||
header_prompt_file_path = pack_options.header_prompt_file_path
|
||||
footer_prompt_file_path = pack_options.footer_prompt_file_path
|
||||
regular_output_file = false
|
||||
|
||||
filelist = FileList.new()
|
||||
filelist.add(repository_path_list)
|
||||
@config.ignore_list.each do |ignore_pattern|
|
||||
pack_options.ignore_list.each do |ignore_pattern|
|
||||
filelist.reject { |path| !!(path =~ Regex.new(ignore_pattern)) }
|
||||
end
|
||||
|
||||
|
@ -45,27 +77,28 @@ module CodePreloader
|
|||
footer_prompt = File.read(footer_prompt_file_path)
|
||||
end
|
||||
|
||||
unless output_file_path.nil? || output_file_path.try(&.empty?) || (output_file_path != "-")
|
||||
output_file = File.open(output_file_path, "w")
|
||||
invalid_output_file = false
|
||||
end
|
||||
output_file_path.try do |path|
|
||||
break if path.empty?
|
||||
break if path == "-"
|
||||
regular_output_file = true
|
||||
output_file = File.open(path, "w")
|
||||
end
|
||||
|
||||
invalid_output_file = true
|
||||
output_file = STDOUT
|
||||
header_prompt = ""
|
||||
footer_prompt = ""
|
||||
|
||||
output_file.puts header_prompt if @config.header_prompt_file_path
|
||||
output_file.puts header_prompt if header_prompt_file_path
|
||||
|
||||
STDERR.puts "Processing repository: #{@config.repository_path_list}"
|
||||
STDERR.puts "Processing repository: #{repository_path_list}"
|
||||
filelist.each do |file_path|
|
||||
process_file(file_path, output_file)
|
||||
end
|
||||
|
||||
output_file.puts footer_prompt if @config.footer_prompt_file_path
|
||||
output_file.puts footer_prompt if footer_prompt_file_path
|
||||
|
||||
output_file.close if !invalid_output_file
|
||||
STDERR.puts "Processing completed. Output written to: #{invalid_output_file ? "stdout" : output_file_path}"
|
||||
output_file.close if regular_output_file
|
||||
STDERR.puts "Processing completed. Output written to: #{regular_output_file ? output_file_path : "stdout" }"
|
||||
|
||||
rescue e : Exception
|
||||
STDERR.puts "An error occurred during execution: #{e.message}"
|
||||
|
|
249
src/config.cr
249
src/config.cr
|
@ -6,63 +6,151 @@ require "./version"
|
|||
|
||||
module CodePreloader
|
||||
class Config
|
||||
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?
|
||||
|
||||
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?
|
||||
|
||||
def initialize()
|
||||
end
|
||||
|
||||
def parse_init_options(parser)
|
||||
@init_options = InitOptions.new
|
||||
|
||||
parser.banner = [
|
||||
"#{PROGRAM_NAME} v#{VERSION}",
|
||||
"Usage: code-preloader init [options]\n",
|
||||
"Global options:"
|
||||
].join("\n")
|
||||
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
parser.on(
|
||||
"-c FILE",
|
||||
"--config=FILE",
|
||||
"Load parameters from FILE"
|
||||
) do |config_file|
|
||||
@init_options.try { |opt| opt.config_file_path = config_file }
|
||||
end
|
||||
|
||||
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 ""
|
||||
|
||||
parser.unknown_args do |remaining_args, _|
|
||||
remaining_args.each do |arg|
|
||||
@pack_options.try { |opt| opt.repository_path_list << arg }
|
||||
end
|
||||
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))
|
||||
OptionParser.parse(args) do |parser|
|
||||
@parser = OptionParser.new do |parser|
|
||||
parser.banner = [
|
||||
"#{PROGRAM_NAME} v#{VERSION}",
|
||||
"Usage: code-preloader [options] DIR ...\n",
|
||||
"Options:"
|
||||
"Usage: code-preloader <subcommand> [options] [DIR] [...]\n",
|
||||
"Global options:"
|
||||
].join("\n")
|
||||
|
||||
parser.on(
|
||||
"-c FILE",
|
||||
"--config=FILE",
|
||||
"Load parameters from FILE"
|
||||
) do |config_file|
|
||||
load_config(config_file)
|
||||
end
|
||||
|
||||
parser.on(
|
||||
"-i REGEXP",
|
||||
"--ignore=REGEXP",
|
||||
"Ignore file or directory"
|
||||
) do |ignore_file|
|
||||
@ignore_list << ignore_file
|
||||
end
|
||||
|
||||
parser.on(
|
||||
"-o FILE",
|
||||
"--output=FILE",
|
||||
"Write output to FILE"
|
||||
) do |output_file|
|
||||
@output_file_path = output_file
|
||||
end
|
||||
|
||||
parser.on(
|
||||
"-H FILE",
|
||||
"--header-prompt=FILE",
|
||||
"Load header prompt from FILE"
|
||||
) do |header_prompt_file|
|
||||
@header_prompt_file_path = header_prompt_file
|
||||
end
|
||||
|
||||
parser.on(
|
||||
"-F FILE",
|
||||
"--footer-prompt=FILE",
|
||||
"Load footer prompt from FILE"
|
||||
) do |footer_prompt_file|
|
||||
@footer_prompt_file_path = footer_prompt_file
|
||||
end
|
||||
|
||||
parser.on("--version", "Show version") do
|
||||
STDOUT.puts "#{PROGRAM_NAME} #{VERSION}"
|
||||
exit(0)
|
||||
|
@ -73,20 +161,56 @@ module CodePreloader
|
|||
exit
|
||||
end
|
||||
|
||||
parser.unknown_args do |remaining_args, _|
|
||||
remaining_args.each do |arg|
|
||||
@repository_path_list << arg
|
||||
end
|
||||
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}")
|
||||
end
|
||||
end
|
||||
|
||||
@parser.try &.parse(args)
|
||||
validate
|
||||
end
|
||||
|
||||
def detect_config
|
||||
# FIXME: detect config name, if any
|
||||
end
|
||||
|
||||
private def validate
|
||||
abort("Missing repository path.") if @repository_path_list.empty?
|
||||
|
||||
STDERR.puts("Output file path not specified (using STDOUT)") if @output_file_path.nil? || @output_file_path.try(&.empty?)
|
||||
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
|
||||
end
|
||||
|
||||
# Reads and returns a list of paths to ignore from the given file.
|
||||
|
@ -97,16 +221,19 @@ module CodePreloader
|
|||
exit(1)
|
||||
end
|
||||
|
||||
private def load_config(config_file_path : String)
|
||||
private def load_pack_config(config_file_path : String)
|
||||
config_str = File.read(config_file_path)
|
||||
|
||||
root = Models::RootConfig.from_yaml(config_str)
|
||||
|
||||
@repository_path = root.repository_path_list || @repository_path_list
|
||||
@ignore_list = root.ignore_list || @ignore_list
|
||||
@output_file_path = root.output_file_path || @output_file_path
|
||||
@header_prompt_file_path = root.header_prompt_file_path || @header_prompt_file_path
|
||||
@footer_prompt_file_path = root.footer_prompt_file_path || @footer_prompt_file_path
|
||||
@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
|
||||
|
||||
rescue ex : Exception
|
||||
STDERR.puts "Failed to load config file: #{ex.message}"
|
||||
|
|
Loading…
Reference in a new issue