Compare commits

...
Sign in to create a new pull request.

16 commits

Author SHA1 Message Date
e057581c51 fix(config): add short option for version command
Enhancing user experience by providing a shorthand option for version
command, reducing potential user errors and improving command-line
efficiency.

- Added `-v` as a shorthand for the `--version` command in the parser.
- Ensures quicker access to version information, aligning with common
  CLI practices.

Signed-off-by: Glenn <glenux@glenux.net>
2025-06-16 16:47:08 +02:00
2b39fbddf2 feat(Makefile): enhance installation and cleanup process
Improves the installation process by ensuring the target directory
exists and adds a cleanup step to maintain a clean build environment.

- Add directory creation step in the install target to ensure
  $(PREFIX)/bin exists before installation.
- Introduce a clean target to remove the code-preloader binary,
  preventing stale binaries from affecting future builds.
  </message-format>

Signed-off-by: Glenn <glenux@glenux.net>
2025-06-16 16:46:25 +02:00
8377d59d7c docs(README): clarify user installation path
Clarified the user installation path in the README to prevent confusion
regarding where binaries are installed. This change helps users easily
locate the installed binaries, reducing potential setup errors.

- Updated the README to specify the user installation path as
  $HOME/.local/bin.

Signed-off-by: Glenn <glenux@glenux.net>
2025-06-16 16:45:37 +02:00
aea7979e40 refactor(cli): enhance file inclusion/exclusion logic
Improves the flexibility and clarity of file processing by introducing
separate include and exclude lists.

- Replace `ignore_list` with `exclude_list` and add `include_list` in
  `.code_preloader.yml`.
- Update `cli.cr` to handle both `include_list` and `exclude_list` for
  file selection.
- Add support for tracing exceptions with a new `trace` configuration
  option.
- Modify `PackOptions` class to include `exclude_list` and
  `include_list` properties.
- Adjust option parsing in `config.cr` to reflect new configuration
  options.

Signed-off-by: Glenn <glenux@glenux.net>
2025-06-16 16:14:11 +02:00
e3e091974d doc: use the new config format 2024-01-24 14:18:00 +01:00
74a52a43e0 fix: ensure files are sorted by name 2024-01-23 16:23:32 +01:00
2d97187e4f doc: add example prompts 2024-01-23 16:23:11 +01:00
b60f030824 fix: make unit tests work with CODE_PRELOADER_DETECT=no 2024-01-05 11:48:12 +01:00
bde15cfd25 feat: env CODE_PRELOADER_DETECT=no disables config auto-detect 2024-01-05 11:47:52 +01:00
d39d597128 feat: use embedded filesystem for default files 2024-01-04 23:14:14 +01:00
97a05896a3 feat: add defaults (template & config) as files 2024-01-04 23:03:21 +01:00
a53ae57f51 Merge branch 'develop' of code.apps.glenux.net:glenux/code-preloader into develop 2024-01-04 22:55:33 +01:00
46ef698224 fix: add missing patch for jinja 2024-01-04 22:54:32 +01:00
3c28d5eace refactor: move docs/ to misc/logo/ 2024-01-04 22:53:38 +01:00
3f75ccca34 refactor: move prompts/ to misc/ 2024-01-04 22:53:21 +01:00
7ff161f4ee Merge pull request 'refactor: store processed content for crinja (later)' () from feature/issue/9-add-support-for-jinja-template into develop
Reviewed-on: 
2024-01-04 21:51:46 +00:00
25 changed files with 216 additions and 116 deletions

View file

@ -1,6 +1,9 @@
---
ignore_list:
include_list:
- .*cr$
exclude_list:
- ^\.git/.*
- ^\.vagrant/.*
- ^misc/.*
@ -17,5 +20,5 @@ output_path: null
prompt:
header_path: null
footer_path: null
template_path: misc/templates/default.j2
template_path: null
#

View file

@ -14,9 +14,13 @@ test:
crystal spec --error-trace
install:
mkdir -p $(PREFIX)/bin
install \
-m 755 \
bin/code-preloader \
$(PREFIX)/bin
clean:
rm -f bin/code-preloader
.PHONY: spec test build all prepare install

View file

@ -75,7 +75,7 @@ cd code-preloader
make prepare
make build
sudo make install # either to install system-wide
make install PREFIX=$HOME/.local # or to install as a user
make install PREFIX=$HOME/.local # or to install as a user in $HOME/.local/bin
```
## Usage
@ -158,9 +158,12 @@ ignore_list:
- bin
- prompts
prompt:
header_path: prompts/context.txt
footer_path: prompts/request-readme.txt
template_path: null
output_file_path: null
header_prompt_file_path: prompts/context.txt
footer_prompt_file_path: prompts/request-readme.txt
```
Then you can type a shorter command like:

View file

@ -0,0 +1,7 @@
Please focus on «@@ FILE ...» and analyze the content of «@@ FILE "{{FIXME: name of the file you want ot analyze}}"».
Provide your analysis as an appreciative feedback, stating:
- what you appreciate in current version
- what you would amplify / augment
- what you would adjust / change / remplace

View file

@ -0,0 +1,3 @@
Focus on the existing «@@ FILE "README.md"». and concentrate on your analysis and appreciative feedback too.
Please take in account each change you propose and write a fixed/improved version of the «@@ FILE "README.md"»

View file

@ -0,0 +1,10 @@
I would like to replace the hardcoded structure of the output "@@ ... " with a jinja template that would be handler with the crinja.cr shard.
My strategy is to preload the content during process_file and store it in a hash.
Then the hash will be used in the end to render a final template (from crinja).
Can you please tell me :
* where the changes should occur (which file? which class? which method?)
* what kind of changes must be made there?
Please do not write code yet, simply explain.

View file

@ -0,0 +1,7 @@
We want to improve the README.md file as for an open-source project following the best practices.
Please focus on the code provided in the « @@ File ... » sections and analyze the existing « @@ File "README.md" », then take in account the content of class Config which analyzes the command line.
In this analysis of the existing « @@ File "README.md" », please provide me with a list of things that you appreciate, a list of things to amplify, a list of things to adjust.

View file

@ -0,0 +1,7 @@
We want to improve the README.md file as for an open-source project following the best practices.
Please focus on the code provided in the « @@ File ... » sections and analyze the existing « @@ File "README.md" », then take in account the content of class Config which analyzes the command line.
Please concentrate on the detail of your analysis in your previous message and write a new improved/fixed version of « @@ File "README.md"

View file

@ -1,5 +1,3 @@
@@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

View file

@ -1,16 +0,0 @@
@@ REQUEST (ANALYZE)
We want to improve the README.md file as for an open-source project following the best practices.
Please focus on the code provided in the « @@ File ... » sections and analyze the existing « @@ File "README.md" », then take in account the content of class Config which analyzes the command line.
In this analysis of the existing « @@ File "README.md" », please provide me with a list of things that you appreciate, a list of things to amplify, a list of things to adjust.
@@ REQUEST (WRITE)
We want to improve the README.md file as for an open-source project following the best practices.
Please focus on the code provided in the « @@ File ... » sections and analyze the existing « @@ File "README.md" », then take in account the content of class Config which analyzes the command line.
Please concentrate on the detail of your analysis in your previous message and write a new improved/fixed version of « @@ File "README.md"

View file

@ -1,5 +1,9 @@
version: 2.0
shards:
baked_file_system:
git: https://github.com/schovi/baked_file_system.git
version: 0.10.0
crinja:
git: https://github.com/straight-shoota/crinja.git
version: 0.8.1

View file

@ -18,6 +18,9 @@ dependencies:
github: alexherbo2/walk.cr
version_from_shard:
github: hugopl/version_from_shard
baked_file_system:
github: schovi/baked_file_system
version: 0.10.0
# completion:
# github: f/completion

View file

@ -12,10 +12,11 @@ ignore_list:
- complex/ignore2
# Path to the output file (if null, output to STDOUT)
output_file_path: complex_output.txt
output_path: complex_output.txt
# Optional: Path to a file containing the header prompt
header_prompt_file_path: null
prompt:
# Optional: Path to a file containing the header prompt
header_path: null
# Optional: Path to a file containing the footer prompt
footer_prompt_file_path: null
# Optional: Path to a file containing the footer prompt
footer_path: null

View file

@ -11,10 +11,11 @@ ignore_list:
- simple/ignore
# Path to the output file (if null, output to STDOUT)
output_file_path: simple_output.txt
output_path: simple_output.txt
# Optional: Path to a file containing the header prompt
header_prompt_file_path: null
prompt:
# Optional: Path to a file containing the header prompt
header_path: null
# Optional: Path to a file containing the footer prompt
footer_prompt_file_path: null
# Optional: Path to a file containing the footer prompt
footer_path: null

View file

@ -50,7 +50,7 @@ describe CodePreloader::Config do
config.subcommand.should eq(Config::Subcommand::Pack)
config.pack_options.should be_truthy
config.pack_options.try do |opts|
opts.repository_path_list.should eq ["path/to/repo1", "path/to/repo2"]
opts.source_list.should eq ["path/to/repo1", "path/to/repo2"]
end
end
@ -72,7 +72,7 @@ describe CodePreloader::Config do
config.subcommand.should eq(Config::Subcommand::Pack)
config.pack_options.should be_truthy
config.pack_options.try do |opts|
opts.output_file_path.should eq "output.txt"
opts.output_path.should eq "output.txt"
end
end
@ -88,9 +88,9 @@ describe CodePreloader::Config do
# Assuming the simple_config.yml has specific settings
config.pack_options.should be_truthy
config.pack_options.try do |opts|
opts.repository_path_list.should eq ["path/to/repo"]
opts.source_list.should eq ["path/to/repo"]
opts.ignore_list.should eq ["simple/ignore"]
opts.output_file_path.should eq "simple_output.txt"
opts.output_path.should eq "simple_output.txt"
end
end
@ -103,9 +103,9 @@ describe CodePreloader::Config do
# Assuming the complex_config.yml has specific settings
config.pack_options.should be_truthy
config.pack_options.try do |opts|
opts.repository_path_list.should eq [repo_path]
opts.source_list.should eq [repo_path]
opts.ignore_list.should eq ["complex/ignore1", "complex/ignore2"]
opts.output_file_path.should eq "complex_output.txt"
opts.output_path.should eq "complex_output.txt"
end
end

View file

@ -1,4 +1,5 @@
require "spec"
# require "../src/"
ENV["CODE_PRELOADER_DETECT"] = "no"

View file

@ -6,6 +6,7 @@ require "crinja"
require "./config"
require "./filelist"
require "./file_storage"
# The CodePreloader module organizes classes and methods related to preloading code files.
module CodePreloader
@ -40,35 +41,14 @@ module CodePreloader
abort("Unexpected nil value for init_options!") if init_options.nil?
# Default path for the .code_preloader.yml file
default_config_path = "example.code_preloader.yml"
default_config_path = ".code_preloader.yml"
# Use the specified path if provided, otherwise use the default
config_path = init_options.config_path || default_config_path
abort("ERROR: configuration file already exist: #{config_path}") if File.exists? config_path
# Content of the .code_preloader.yml file
config_content = [
"---",
"# Example configuration for Code-Preloader",
"",
"# List of repository paths to preload",
"# source_list:",
"# - \"path/to/repo1\"",
"# - \"path/to/repo2\"",
"",
"# List of patterns to ignore during preloading",
"ignore_list:",
" - ^\\.git/.*",
"",
"# Path to the output file (if null, output to STDOUT)",
"output_path: null",
"",
"# Optional: Path to a file containing the header prompt",
"header_path: null",
"",
"# Optional: Path to a file containing the footer prompt",
"footer_path: null",
""
].join("\n")
# Content of the default .code_preloader.yml file
config_content = FileStorage.get("default_config.yml").gets_to_end
# Writing the configuration content to the file
File.write(config_path, config_content)
@ -112,12 +92,19 @@ module CodePreloader
filelist = FileList.new()
filelist.add(source_list)
pack_options.ignore_list.each do |ignore_pattern|
filelist.reject { |path| !!(path =~ Regex.new(ignore_pattern)) }
end
pack_options.exclude_list
.map { |exclude_pattern| Regex.new(exclude_pattern) }
.each { |exclude_regexp| filelist.reject { |path| !!(path =~ exclude_regexp) } }
pack_options.include_list
.map { |include_pattern| Regex.new(include_pattern) }
.each { |include_regexp| filelist.select { |path| !!(path =~ include_regexp) } }
abort("No prompt file defined!") if prompt_template_path.nil?
prompt_template_content = File.read(prompt_template_path)
STDERR.puts "Loading template file from: #{prompt_template_path ? prompt_template_path : "<internal>" }".colorize(:yellow)
if prompt_template_path
prompt_template_content = File.read(prompt_template_path)
else
prompt_template_content = FileStorage.get("default_template.j2").gets_to_end
end
if !prompt_header_path.nil?
@ -143,7 +130,7 @@ module CodePreloader
STDERR.puts "Processing source directories: #{source_list}".colorize(:yellow)
processed_files = [] of ProcessedFile
filelist.each do |file_path|
filelist.to_a.sort.each do |file_path|
STDERR.puts "Processing file: #{file_path}".colorize(:yellow)
file_result = process_file(file_path, output_file)
processed_files << file_result
@ -152,19 +139,22 @@ module CodePreloader
# FIXME: prompt_footer_path.try { output_file.puts prompt_footer_content }
output_file.puts Crinja.render(
prompt_template_content,
{
prompt_template_content,
{
"prompt_header": prompt_header_content,
"prompt_files": processed_files,
"prompt_footer": prompt_footer_content
}
)
)
output_file.close if regular_output_file
STDERR.puts "Processing completed.".colorize(:yellow)
rescue e : Exception
STDERR.puts "ERROR: #{e.message}"
if @config.trace?
STDERR.puts e.backtrace.map(&.gsub(/^/, " ")).join("\n")
end
exit(1)
end

View file

@ -27,14 +27,16 @@ module CodePreloader
class PackOptions
property config_path : String? = nil
property source_list : Array(String) = [] of String
property ignore_list : Array(String) = [] of String
property exclude_list : Array(String) = [] of String
property include_list : Array(String) = [] of String
property output_path : String?
property prompt_template_path : String?
property prompt_header_path : String?
property prompt_footer_path : String?
end
getter verbose : Bool = false
getter? verbose : Bool = false
getter? trace : Bool = false
getter parser : OptionParser?
getter subcommand : Subcommand = Subcommand::None
getter pack_options : PackOptions?
@ -60,9 +62,10 @@ module CodePreloader
end
end
parser.on(
"-c FILE",
"--config=FILE",
"-c FILE",
"--config=FILE",
"Load parameters from FILE"
) do |config_file|
@init_options.try { |opt| opt.config_path = config_file }
@ -83,11 +86,13 @@ module CodePreloader
# complete_with "code-preloader init", parser
end
def parse_pack_options(parser)
def parse_pack_options(parser)
@pack_options = PackOptions.new
config_file = detect_config_file
config_file.try { |path| load_pack_config(path) }
unless ENV["CODE_PRELOADER_DETECT"]? =~ /(no|false|0)/i
config_file = detect_config_file
config_file.try { |path| load_pack_config(path) }
end
parser.banner = [
"Usage: code-preloader pack [options] DIR ...\n",
@ -97,48 +102,56 @@ module CodePreloader
parser.separator "\nPack options:"
parser.on(
"-c FILE",
"--config=FILE",
"Load parameters from FILE\n(default: \".code_preload.yml\", if present)"
"-c FILE",
"--config=FILE",
"Load parameters from FILE\n(default: autodetect)"
) do |config_file|
@pack_options.try { |opt| load_pack_config(config_file) }
end
parser.on(
"-F FILE",
"--prompt-footer=FILE",
"-F FILE",
"--prompt-footer=FILE",
"Load prompt footer from FILE (default: none)"
) do |prompt_footer_path|
@pack_options.try { |opt| opt.prompt_footer_path = prompt_footer_path }
end
parser.on(
"-H FILE",
"--prompt-header=FILE",
"-H FILE",
"--prompt-header=FILE",
"Load prompt header from FILE (default: none)"
) do |prompt_header_path|
@pack_options.try { |opt| opt.prompt_header_path = prompt_header_path }
end
parser.on(
"-i REGEXP",
"--ignore=REGEXP",
"Ignore file or directory. Can be used\nmultiple times (default: none)"
) do |ignore_file|
@pack_options.try { |opt| opt.ignore_list << ignore_file }
"-i REGEXP",
"--include=REGEXP",
"Include file or directory. Can be used\nmultiple times (default: none)"
) do |include_regexp|
@pack_options.try { |opt| opt.include_list << include_regexp }
end
parser.on(
"-o FILE",
"--output=FILE",
"-e REGEXP",
"--exclude=REGEXP",
"Exclude file or directory. Can be used\nmultiple times (default: none)"
) do |exclude_regexp|
@pack_options.try { |opt| opt.exclude_list << exclude_regexp }
end
parser.on(
"-o FILE",
"--output=FILE",
"Write output to FILE (default: \"-\", STDOUT)"
) do |output_file|
@pack_options.try { |opt| opt.output_path = output_file }
end
parser.on(
"-t FILE",
"--template=FILE",
"-t FILE",
"--template=FILE",
"Load template from FILE (default: internal)"
) do |prompt_template_path|
@pack_options.try { |opt| opt.prompt_template_path = prompt_template_path }
@ -184,10 +197,13 @@ module CodePreloader
@verbose = true
end
parser.on("--version", "Show version") do
@subcommand = Subcommand::Version
parser.on("-t", "--trace", "Show detailed traces for exceptions") do
@trace = true
end
parser.on("-v", "--version", "Show version") do
@subcommand = Subcommand::Version
end
parser.separator "\nSubcommands:"
@ -275,11 +291,14 @@ module CodePreloader
if opts.source_list.nil? || opts.source_list.try &.empty?
root.source_list.try { |value| opts.source_list = value }
end
if opts.ignore_list.nil? || opts.ignore_list.try &.empty?
root.ignore_list.try { |value| opts.ignore_list = value }
if opts.exclude_list.nil? || opts.exclude_list.try &.empty?
root.exclude_list.try { |value| opts.exclude_list = value }
end
if opts.include_list.nil? || opts.include_list.try &.empty?
root.include_list.try { |value| opts.include_list = value }
end
if opts.output_path.nil?
opts.output_path = root.output_path
opts.output_path = root.output_path
end
if opts.prompt_header_path.nil?
root.prompt.try &.header_path.try { |value| opts.prompt_header_path = value }

7
src/file_storage.cr Normal file
View file

@ -0,0 +1,7 @@
require "baked_file_system"
class FileStorage
extend BakedFileSystem
bake_folder "../static"
end

View file

@ -13,13 +13,13 @@ module CodePreloader
end
@sources : Array(String)
@filters_in : Array(Filter)
@filters_out : Array(Filter)
@include_filters : Array(Filter)
@exclude_filters : Array(Filter)
def initialize(dirs = [] of String)
@sources = [] of String
@filters_in = [] of Filter
@filters_out = [] of Filter
@include_filters = [] of Filter
@exclude_filters = [] of Filter
dirs.each { |dir| self.add(dir) }
end
@ -34,11 +34,11 @@ module CodePreloader
end
def select(&filter : Filter)
@filters_in << filter
@include_filters << filter
end
def reject(&filter : Filter)
@filters_out << filter
@exclude_filters << filter
end
def each(&block)
@ -46,7 +46,7 @@ module CodePreloader
seen = Set(String).new
# walk each source
@sources.each do |dir|
@sources.sort.each do |dir|
walker = Walk::Down.new(dir)
walker = walker.filter do |path|
@ -56,16 +56,16 @@ module CodePreloader
must_reject = false
clean_path = path.to_s.gsub(/^\.\//,"")
@filters_in.each do |filter_in|
@include_filters.each do |filter_in|
must_select = must_select || filter_in.call(clean_path)
end
keep = keep && must_select if @filters_in.any?
keep = keep && must_select if @include_filters.any?
keep = keep || is_dir
@filters_out.each do |filter_out|
@exclude_filters.each do |filter_out|
must_reject = must_reject || filter_out.call(clean_path)
end
keep = keep && !must_reject if @filters_out.any?
keep = keep && !must_reject if @exclude_filters.any?
keep
end
@ -88,7 +88,7 @@ module CodePreloader
self.each do |path|
files << path.to_s
end
files
files.sort
end
end
end

View file

@ -16,7 +16,10 @@ module CodePreloader::Models
@[YAML::Field(key: "prompt")]
getter prompt : PromptConfig?
@[YAML::Field(key: "ignore_list")]
getter ignore_list : Array(String)?
@[YAML::Field(key: "exclude_list")]
getter exclude_list : Array(String)?
@[YAML::Field(key: "include_list")]
getter include_list : Array(String)?
end
end

29
static/default_config.yml Normal file
View file

@ -0,0 +1,29 @@
---
# Example configuration for Code-Preloader
# List of repository paths to preload
# source_list:
# - "path/to/repo1"
# - "path/to/repo2"
# List of patterns to exclude (= ignore) during preloading
exclude_list:
- ^\.git/.*
- ".*\\.(png|jpeg|jpg|webp|pdf|mp4|mp3)$"
# List of patterns to include (= limit) during preloading
include_list:
- ".*\\.(md|txt|markdown)$"
# Path to the output file (if null, output to STDOUT)
output_path: null
prompt:
# Optional: Path to a file containing the prompt header
header_path: null
# Optional: Path to a file containing the prompt footer
footer_path: null
# Optional: Path to a file container a jinja template to structure the prompt
template_path: null

View file

@ -0,0 +1,16 @@
{%- if prompt_header -%}
@@ CONTEXT
{{ prompt_header }}
{%- endif -%}
{%- for file in prompt_files -%}
@@ FILE "{{ file.path }}" WITH MIME-TYPE "{{ file.mime_type }}"
{{- file.content -}}
{%- endfor -%}
{%- if prompt_footer -%}
@@ REQUEST
{{ prompt_footer }}
{%- endif -%}