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.
|
63
src/cli.cr
63
src/cli.cr
|
@ -18,20 +18,52 @@ module CodePreloader
|
||||||
def initialize(args)
|
def initialize(args)
|
||||||
@output_file_path = ""
|
@output_file_path = ""
|
||||||
@config = Config.new()
|
@config = Config.new()
|
||||||
|
@config.detect_config()
|
||||||
@config.parse_arguments(args)
|
@config.parse_arguments(args)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Executes the main functionality of the CLI application.
|
# Executes the main functionality of the CLI application.
|
||||||
def exec
|
def exec
|
||||||
# get local values for typing
|
case @config.subcommand
|
||||||
output_file_path = @output_file_path
|
when Config::Subcommand::Init then exec_init(@config.init_options)
|
||||||
repository_path_list = @config.repository_path_list
|
when Config::Subcommand::Pack then exec_pack(@config.pack_options)
|
||||||
header_prompt_file_path = @config.header_prompt_file_path
|
when Config::Subcommand::Version then exec_version
|
||||||
footer_prompt_file_path = @config.footer_prompt_file_path
|
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 = FileList.new()
|
||||||
filelist.add(repository_path_list)
|
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)) }
|
filelist.reject { |path| !!(path =~ Regex.new(ignore_pattern)) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -45,27 +77,28 @@ module CodePreloader
|
||||||
footer_prompt = File.read(footer_prompt_file_path)
|
footer_prompt = File.read(footer_prompt_file_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
unless output_file_path.nil? || output_file_path.try(&.empty?) || (output_file_path != "-")
|
output_file_path.try do |path|
|
||||||
output_file = File.open(output_file_path, "w")
|
break if path.empty?
|
||||||
invalid_output_file = false
|
break if path == "-"
|
||||||
|
regular_output_file = true
|
||||||
|
output_file = File.open(path, "w")
|
||||||
end
|
end
|
||||||
|
|
||||||
invalid_output_file = true
|
|
||||||
output_file = STDOUT
|
output_file = STDOUT
|
||||||
header_prompt = ""
|
header_prompt = ""
|
||||||
footer_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|
|
filelist.each do |file_path|
|
||||||
process_file(file_path, output_file)
|
process_file(file_path, output_file)
|
||||||
end
|
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
|
output_file.close if regular_output_file
|
||||||
STDERR.puts "Processing completed. Output written to: #{invalid_output_file ? "stdout" : output_file_path}"
|
STDERR.puts "Processing completed. Output written to: #{regular_output_file ? output_file_path : "stdout" }"
|
||||||
|
|
||||||
rescue e : Exception
|
rescue e : Exception
|
||||||
STDERR.puts "An error occurred during execution: #{e.message}"
|
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
|
module CodePreloader
|
||||||
class Config
|
class Config
|
||||||
property repository_path_list : Array(String) = [] of String
|
|
||||||
property ignore_list : Array(String) = [] of String
|
enum Subcommand
|
||||||
property output_file_path : String?
|
None
|
||||||
property header_prompt_file_path : String?
|
Init
|
||||||
property footer_prompt_file_path : String?
|
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()
|
def initialize()
|
||||||
end
|
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))
|
def parse_arguments(args : Array(String))
|
||||||
OptionParser.parse(args) do |parser|
|
@parser = OptionParser.new do |parser|
|
||||||
parser.banner = [
|
parser.banner = [
|
||||||
"#{PROGRAM_NAME} v#{VERSION}",
|
"#{PROGRAM_NAME} v#{VERSION}",
|
||||||
"Usage: code-preloader [options] DIR ...\n",
|
"Usage: code-preloader <subcommand> [options] [DIR] [...]\n",
|
||||||
"Options:"
|
"Global options:"
|
||||||
].join("\n")
|
].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
|
parser.on("--version", "Show version") do
|
||||||
STDOUT.puts "#{PROGRAM_NAME} #{VERSION}"
|
STDOUT.puts "#{PROGRAM_NAME} #{VERSION}"
|
||||||
exit(0)
|
exit(0)
|
||||||
|
@ -73,20 +161,56 @@ module CodePreloader
|
||||||
exit
|
exit
|
||||||
end
|
end
|
||||||
|
|
||||||
parser.unknown_args do |remaining_args, _|
|
parser.separator "\nSubcommands:"
|
||||||
remaining_args.each do |arg|
|
|
||||||
@repository_path_list << arg
|
parser.on("init", "Create an example .code_preloader.yml file") do
|
||||||
end
|
@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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@parser.try &.parse(args)
|
||||||
validate
|
validate
|
||||||
end
|
end
|
||||||
|
|
||||||
private def validate
|
def detect_config
|
||||||
abort("Missing repository path.") if @repository_path_list.empty?
|
# FIXME: detect config name, if any
|
||||||
|
end
|
||||||
|
|
||||||
STDERR.puts("Output file path not specified (using STDOUT)") if @output_file_path.nil? || @output_file_path.try(&.empty?)
|
private def validate
|
||||||
|
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
|
end
|
||||||
|
|
||||||
# Reads and returns a list of paths to ignore from the given file.
|
# Reads and returns a list of paths to ignore from the given file.
|
||||||
|
@ -97,16 +221,19 @@ module CodePreloader
|
||||||
exit(1)
|
exit(1)
|
||||||
end
|
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)
|
config_str = File.read(config_file_path)
|
||||||
|
|
||||||
root = Models::RootConfig.from_yaml(config_str)
|
root = Models::RootConfig.from_yaml(config_str)
|
||||||
|
|
||||||
@repository_path = root.repository_path_list || @repository_path_list
|
@pack_options.try do |opts|
|
||||||
@ignore_list = root.ignore_list || @ignore_list
|
opts.config_file_path = config_file_path
|
||||||
@output_file_path = root.output_file_path || @output_file_path
|
opts.repository_path_list = root.repository_path_list || opts.repository_path_list
|
||||||
@header_prompt_file_path = root.header_prompt_file_path || @header_prompt_file_path
|
opts.ignore_list = root.ignore_list || opts.ignore_list
|
||||||
@footer_prompt_file_path = root.footer_prompt_file_path || @footer_prompt_file_path
|
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
|
rescue ex : Exception
|
||||||
STDERR.puts "Failed to load config file: #{ex.message}"
|
STDERR.puts "Failed to load config file: #{ex.message}"
|
||||||
|
|
Loading…
Reference in a new issue