Initial import
This commit is contained in:
commit
7da2465cad
7 changed files with 241 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
bin/
|
5
Makefile
Normal file
5
Makefile
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
all: build
|
||||||
|
|
||||||
|
build:
|
||||||
|
shards build
|
34
README.md
Normal file
34
README.md
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
# README
|
||||||
|
|
||||||
|
code-preloader is a tool that helps preloading all files for a given root directory
|
||||||
|
and concatenates them as a single file.
|
||||||
|
|
||||||
|
## Structure of the output file
|
||||||
|
|
||||||
|
```jinja2
|
||||||
|
{{ HEADER_PROMPT_FILE }}
|
||||||
|
|
||||||
|
{% for file in file_list %}
|
||||||
|
@@ File "{{ file.path }}"
|
||||||
|
{{ file.content }}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{{ FOOTER_PROMPT_FILE }}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage: code-preloader [options] ROOT_DIR
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-c, --config=CONFIG_FILE Load parameters from CONFIG_FILE (ignore, output, etc.)
|
||||||
|
-i, --ignore=IGNORE_FILE Ignore file or directory path (not a pattern)
|
||||||
|
-o, --output=OUTPUT_FILE Write output to OUTPUT_FILE (default to stdout if option missing or OUTPUT_FILE is "-")
|
||||||
|
--header-prompt=HEADER_PROMPT_FILE Load header prompt from PROMPT_FILE
|
||||||
|
--footer-prompt=FOOTER_PROMPT_FILE Load footer prompt from PROMPT_FILE
|
||||||
|
--version Show version
|
||||||
|
-v, --verbose Enable verbose mode
|
||||||
|
-h, --help Show this help
|
||||||
|
```
|
||||||
|
|
3
prompts/footer.txt
Normal file
3
prompts/footer.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
@@ REQUEST
|
||||||
|
|
||||||
|
Please follow crystal lang good practices, idioms and conventions. Please analyze code for every .cr file and tell me what we should change or fix (if needed) to improve the code and match the Usage described in README.md
|
23
shard.yml
Normal file
23
shard.yml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
name: chatgpt-preloader
|
||||||
|
version: 0.1.0
|
||||||
|
|
||||||
|
targets:
|
||||||
|
chatgpt-preloader:
|
||||||
|
main: src/main.cr
|
||||||
|
|
||||||
|
authors:
|
||||||
|
- Glenn Y. Rolland <glenux@glenux.net>
|
||||||
|
|
||||||
|
# description: |
|
||||||
|
# Short description of chatgpt-preloader
|
||||||
|
|
||||||
|
# dependencies:
|
||||||
|
# pg:
|
||||||
|
# github: will/crystal-pg
|
||||||
|
# version: "~> 0.5"
|
||||||
|
|
||||||
|
# development_dependencies:
|
||||||
|
# webmock:
|
||||||
|
# github: manastech/webmock.cr
|
||||||
|
|
||||||
|
# license: MIT
|
164
src/cli.cr
Normal file
164
src/cli.cr
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
# vim: set ts=2 sw=2 et ft=crystal:
|
||||||
|
|
||||||
|
require "file"
|
||||||
|
require "option_parser"
|
||||||
|
|
||||||
|
# The CodePreloader module organizes classes and methods related to preloading code files.
|
||||||
|
module CodePreloader
|
||||||
|
# The Cli class handles command-line interface operations for the CodePreloader.
|
||||||
|
class Cli
|
||||||
|
getter repo_path : String
|
||||||
|
getter ignore_list : Array(String) = [] of String
|
||||||
|
getter output_file_path : String
|
||||||
|
getter preamble_file_path : String?
|
||||||
|
getter header_prompt_file_path : String? # Add type annotation
|
||||||
|
getter footer_prompt_file_path : String? # Assuming you'll also need this
|
||||||
|
|
||||||
|
# Initializes the Cli class with default values.
|
||||||
|
def initialize
|
||||||
|
@repo_path = ""
|
||||||
|
@output_file_path = ""
|
||||||
|
end
|
||||||
|
|
||||||
|
# Parses command-line arguments and initializes the necessary configurations.
|
||||||
|
def parse_arguments(args : Array(String))
|
||||||
|
OptionParser.parse(args) do |parser|
|
||||||
|
parser.banner = "Usage: code-preloader [options] ROOT_DIR"
|
||||||
|
|
||||||
|
parser.on("-c CONFIG_FILE", "--config=CONFIG_FILE", "Load parameters from CONFIG_FILE") do |config_file|
|
||||||
|
load_config(config_file)
|
||||||
|
end
|
||||||
|
|
||||||
|
parser.on("-i IGNORE_PATH", "--ignore=IGNORE_PATH", "Ignore file or directory") do |ignore_file|
|
||||||
|
@ignore_list << ignore_file
|
||||||
|
end
|
||||||
|
|
||||||
|
parser.on("-o OUTPUT_FILE", "--output=OUTPUT_FILE", "Write output to OUTPUT_FILE") do |output_file|
|
||||||
|
@output_file_path = output_file
|
||||||
|
end
|
||||||
|
|
||||||
|
parser.on("--header-prompt=HEADER_PROMPT_FILE", "Load header prompt from HEADER_PROMPT_FILE") do |header_prompt_file|
|
||||||
|
@header_prompt_file_path = header_prompt_file
|
||||||
|
end
|
||||||
|
|
||||||
|
parser.on("--footer-prompt=FOOTER_PROMPT_FILE", "Load footer prompt from FOOTER_PROMPT_FILE") do |footer_prompt_file|
|
||||||
|
@footer_prompt_file_path = footer_prompt_file
|
||||||
|
end
|
||||||
|
|
||||||
|
parser.on("-h", "--help", "Show this help") do
|
||||||
|
STDERR.puts parser
|
||||||
|
exit
|
||||||
|
end
|
||||||
|
|
||||||
|
parser.unknown_args do |remaining_args, _|
|
||||||
|
if remaining_args.size != 1
|
||||||
|
abort("Invalid number of arguments. Expected exactly one argument for ROOT_DIR.")
|
||||||
|
end
|
||||||
|
@repo_path = remaining_args[0]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
validate_arguments
|
||||||
|
end
|
||||||
|
|
||||||
|
# Executes the main functionality of the CLI application.
|
||||||
|
def exec
|
||||||
|
if preamble_file_path
|
||||||
|
STDERR.puts "Loading preamble from: #{preamble_file_path}"
|
||||||
|
end
|
||||||
|
|
||||||
|
if header_prompt_file_path
|
||||||
|
STDERR.puts "Loading header prompt from: #{header_prompt_file_path}"
|
||||||
|
end
|
||||||
|
|
||||||
|
if footer_prompt_file_path
|
||||||
|
STDERR.puts "Loading footer prompt from: #{footer_prompt_file_path}"
|
||||||
|
end
|
||||||
|
|
||||||
|
STDERR.puts "Processing repository: #{repo_path}"
|
||||||
|
process_repository
|
||||||
|
STDERR.puts "Processing completed. Output written to: #{@output_file_path.empty? ? "stdout" : @output_file_path}"
|
||||||
|
|
||||||
|
rescue e : Exception
|
||||||
|
STDERR.puts "An error occurred during execution: #{e.message}"
|
||||||
|
exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Processes the specified repository and writes the output to a file.
|
||||||
|
def process_repository
|
||||||
|
local_output_file_path = @output_file_path
|
||||||
|
must_close = false
|
||||||
|
output_file = STDOUT
|
||||||
|
|
||||||
|
if !local_output_file_path.empty? && (local_output_file_path != "-")
|
||||||
|
output_file = File.open(local_output_file_path, "w")
|
||||||
|
must_close = true
|
||||||
|
end
|
||||||
|
|
||||||
|
output_file.puts preamble_text if preamble_file_path
|
||||||
|
process_directory(repo_path, output_file)
|
||||||
|
output_file.close if must_close
|
||||||
|
|
||||||
|
rescue e : IO::Error
|
||||||
|
STDERR.puts "Error processing repository: #{e.message}"
|
||||||
|
exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
private def process_directory(path : String, output_file : IO::FileDescriptor)
|
||||||
|
Dir.each_child(path) do |child|
|
||||||
|
child_path = File.join(path, child)
|
||||||
|
|
||||||
|
ignores = (
|
||||||
|
ignore_list
|
||||||
|
.map{ |prefix| [prefix, File.expand_path(child_path) =~ /^#{File.expand_path(prefix)}/] }
|
||||||
|
.reject!{ |item| item[1].nil? }
|
||||||
|
)
|
||||||
|
next if !ignores.empty?
|
||||||
|
|
||||||
|
puts "File: #{child_path}"
|
||||||
|
child_path = File.join(path, child)
|
||||||
|
if File.directory?(child_path)
|
||||||
|
process_directory(child_path, output_file)
|
||||||
|
else
|
||||||
|
process_file(child_path, output_file)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private def process_file(file_path : String, output_file : IO::FileDescriptor)
|
||||||
|
relative_file_path = file_path.sub(/^#{Regex.escape(repo_path)}/, ".").lstrip
|
||||||
|
output_file.puts "@@ File \"#{relative_file_path}\""
|
||||||
|
output_file.puts ""
|
||||||
|
output_file.puts(File.read(file_path))
|
||||||
|
output_file.puts ""
|
||||||
|
end
|
||||||
|
|
||||||
|
private def preamble_text : String
|
||||||
|
local_preamble_file_path = @preamble_file_path
|
||||||
|
return "" if local_preamble_file_path.nil?
|
||||||
|
|
||||||
|
File.read(local_preamble_file_path)
|
||||||
|
rescue e : IO::Error
|
||||||
|
STDERR.puts "Error reading preamble file: #{e.message}"
|
||||||
|
exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
private def validate_arguments
|
||||||
|
abort("Missing repository path.") if repo_path.empty?
|
||||||
|
STDERR.puts("Output file path not specified (using STDOUT)") if output_file_path.empty?
|
||||||
|
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
|
||||||
|
|
||||||
|
# Loads configuration from a config file.
|
||||||
|
private def load_config(config_file_path : String)
|
||||||
|
# Implement configuration loading logic here
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
11
src/main.cr
Normal file
11
src/main.cr
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
# vim: set ts=2 sw=2 et ft=crystal:
|
||||||
|
|
||||||
|
require "./cli"
|
||||||
|
|
||||||
|
# Now that we have checked for nil, it's safe to use not_nil!
|
||||||
|
app = CodePreloader::Cli.new
|
||||||
|
app.parse_arguments(ARGV)
|
||||||
|
app.exec()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue