44# Generates contextual information from a codebase to send to an LLM
55
66# Default values
7- FILES=" "
7+ FILES=()
88EXCLUDE=" "
99MAX_SIZE=" 500KB"
1010INCLUDE_DEPS=false
@@ -15,11 +15,13 @@ FORMAT="md"
1515SUMMARY=false
1616SHOW_FILE_SIZES=false
1717TRUNCATE_LARGE=" "
18+ PROMPT_FILE=" "
19+ VERBOSE=false
1820
1921# Parse arguments
2022while [[ " $# " -gt 0 ]]; do
2123 case $1 in
22- --files=* ) FILES= " ${1#* =} " ; shift ;;
24+ --files=* ) FILES+=( " ${1#* =} " ) ; shift ;;
2325 --exclude=* ) EXCLUDE=" ${1#* =} " ; shift ;;
2426 --max-size=* ) MAX_SIZE=" ${1#* =} " ; shift ;;
2527 --include-deps) INCLUDE_DEPS=true; shift ;;
@@ -30,8 +32,10 @@ while [[ "$#" -gt 0 ]]; do
3032 --summary) SUMMARY=true; shift ;;
3133 --show-file-sizes) SHOW_FILE_SIZES=true; shift ;;
3234 --truncate-large=* ) TRUNCATE_LARGE=" ${1#* =} " ; shift ;;
35+ --prompt=* ) PROMPT_FILE=" ${1#* =} " ; shift ;;
36+ --verbose) VERBOSE=true; shift ;;
3337 --help|-h)
34- echo " Usage: ./context [options]"
38+ echo " Usage: ./context [options] [file1 file2 ...] "
3539 echo " "
3640 echo " Options:"
3741 echo " --files=<pattern> File pattern to include (e.g., \" src/*.js\" )"
@@ -45,19 +49,16 @@ while [[ "$#" -gt 0 ]]; do
4549 echo " --summary Include short summary of each file"
4650 echo " --show-file-sizes Include file sizes in output"
4751 echo " --truncate-large=<size> Truncate files larger than specified size (e.g., \" 50KB\" )"
52+ echo " --prompt=<file> Include prompt template from specified file"
53+ echo " --verbose Show verbose output"
4854 echo " --help, -h Show this help message"
4955 exit 0
5056 ;;
51- * ) echo " Unknown parameter: $1 " ; exit 1 ;;
57+ --* ) echo " Unknown parameter: $1 " ; exit 1 ;;
58+ * ) FILES+=(" $1 " ); shift ;;
5259 esac
5360done
5461
55- # Validate required parameters
56- if [ -z " $FILES " ]; then
57- echo " Error: --files parameter is required"
58- exit 1
59- fi
60-
6162# Convert human-readable size to bytes
6263convert_to_bytes () {
6364 local size=$1
8081
8182# Get all matching files
8283find_files () {
83- local find_cmd=" find . -type f -path \" $FILES \" "
84- if [ -n " $EXCLUDE " ]; then
85- find_cmd=" $find_cmd -not -path \" $EXCLUDE \" "
86- fi
87- eval $find_cmd | sort
84+ local all_files=" "
85+
86+ # Process each file or pattern
87+ for file_pattern in " ${FILES[@]} " ; do
88+ # Check if it's a direct file path
89+ if [ -f " $file_pattern " ]; then
90+ all_files=" $all_files " $' \n ' " $file_pattern "
91+ else
92+ # It's a pattern, use find
93+ local find_cmd=" find . -type f -path \" $file_pattern \" "
94+ if [ -n " $EXCLUDE " ]; then
95+ find_cmd=" $find_cmd -not -path \" $EXCLUDE \" "
96+ fi
97+ local found_files=$( eval $find_cmd )
98+ if [ -n " $found_files " ]; then
99+ all_files=" $all_files " $' \n ' " $found_files "
100+ fi
101+ fi
102+ done
103+
104+ # Remove empty lines and sort
105+ echo " $all_files " | grep -v " ^$" | sort | uniq
88106}
89107
108+ # Handle empty file list
109+ if [ ${# FILES[@]} -eq 0 ]; then
110+ echo " Error: No files specified. Use --files=<pattern> or provide filenames directly."
111+ exit 1
112+ fi
113+
90114ALL_FILES=$( find_files)
91115TOTAL_SIZE=0
92116
93- # Calculate current size
94- for file in $ALL_FILES ; do
95- if [ -f " $file " ]; then
96- file_size=$( wc -c < " $file " )
97- TOTAL_SIZE=$(( TOTAL_SIZE + file_size))
98- fi
99- done
117+ # Human-readable size function
118+ human_readable_size () {
119+ local size=$1
120+ local units=(" B" " KB" " MB" " GB" " TB" )
121+ local unit_index=0
122+ local size_float=$size
123+
124+ while [ $( echo " $size_float >= 1024" | bc -l) -eq 1 ] && [ $unit_index -lt 4 ]; do
125+ size_float=$( echo " scale=2; $size_float / 1024" | bc -l)
126+ unit_index=$(( unit_index + 1 ))
127+ done
128+
129+ # Remove trailing zeros and decimal point if it's a whole number
130+ size_float=$( echo $size_float | sed ' s/\.0*$//' )
131+
132+ echo " $size_float ${units[$unit_index]} "
133+ }
100134
101135# Start the output based on format
102136case $FORMAT in
@@ -117,63 +151,18 @@ case $FORMAT in
117151 ;;
118152esac
119153
120- # Function to find dependencies in a file (basic implementation)
121- find_dependencies () {
122- local file=$1
123- local ext=${file##* .}
124-
125- case $ext in
126- js|jsx|ts|tsx)
127- grep -E " ^import .+ from ['\" ]\..*['\" ]|^const .+ = require[[:space:]]*\(['\" ]\..*['\" ]\)" " $file " |
128- sed -E " s/^import .+ from ['\" ](\..*)['\" ];?/\1/g;s/^const .+ = require[[:space:]]*\(['\" ](\..*)['\" ].*\);?/\1/g" |
129- sed " s/'//g;s/\" //g"
130- ;;
131- py)
132- grep -E " ^import .+|^from .+ import" " $file " |
133- sed -E " s/^import (.+)/\1/g;s/^from (.+) import.*/\1/g" |
134- grep -v " ^import" | grep -v " ^from"
135- ;;
136- rb)
137- grep -E " ^require ['\" ]\." " $file " |
138- sed -E " s/^require ['\" ](.+)['\" ].*/\1/g"
139- ;;
140- php)
141- grep -E " ^require|^include|^require_once|^include_once" " $file " |
142- sed -E " s/^(require|include|require_once|include_once) ['\" ](.+)['\" ].*/\2/g"
143- ;;
144- esac
145- }
146-
147- # Function to resolve a dependency path relative to a file
148- resolve_dependency () {
149- local base_file=$1
150- local dep=$2
151- local base_dir=$( dirname " $base_file " )
152-
153- # Handle file extensions if not specified
154- if [[ ! " $dep " =~ \. [a-zA-Z0-9]+$ ]]; then
155- # Try to find the file with common extensions
156- for ext in js jsx ts tsx py rb php; do
157- if [ -f " $base_dir /$dep .$ext " ]; then
158- dep=" $dep .$ext "
159- break
160- fi
161- done
162- fi
163-
164- # Normalize the path
165- local full_path=" $base_dir /$dep "
166- realpath " $full_path " 2> /dev/null || echo " $full_path "
167- }
168-
169154# Process each file
170155file_count=0
171156for file in $ALL_FILES ; do
172157 if [ ! -f " $file " ]; then
158+ if [ " $VERBOSE " = true ]; then
159+ echo " Warning: File not found: $file " >&2
160+ fi
173161 continue
174162 fi
175163
176164 file_size=$( wc -c < " $file " )
165+ TOTAL_SIZE=$(( TOTAL_SIZE + file_size))
177166 truncated=false
178167 file_content=" "
179168
@@ -197,7 +186,7 @@ for file in $ALL_FILES; do
197186 echo " ### $file "
198187 if [ " $SHOW_FILE_SIZES " = true ]; then
199188 echo " "
200- echo " Size: $( numfmt --to=iec-i --suffix=B $file_size ) "
189+ echo " Size: $( human_readable_size $file_size ) "
201190 echo " "
202191 fi
203192
@@ -242,7 +231,7 @@ for file in $ALL_FILES; do
242231 text)
243232 echo " === $file ==="
244233 if [ " $SHOW_FILE_SIZES " = true ]; then
245- echo " Size: $( numfmt --to=iec-i --suffix=B $file_size ) "
234+ echo " Size: $( human_readable_size $file_size ) "
246235 fi
247236
248237 if [ " $SUMMARY " = true ] && [ -n " $file_summary " ]; then
@@ -262,15 +251,11 @@ for file in $ALL_FILES; do
262251
263252 file_count=$(( file_count + 1 ))
264253
265- # Process dependencies if requested
254+ # Note: Dependency resolution disabled for now
266255 if [ " $INCLUDE_DEPS " = true ] && [ $DEPTH -gt 0 ]; then
267- deps=$( find_dependencies " $file " )
268- for dep in $deps ; do
269- resolved_dep=$( resolve_dependency " $file " " $dep " )
270- if [ -f " $resolved_dep " ] && ! echo " $ALL_FILES " | grep -q " $resolved_dep " ; then
271- ALL_FILES=" $ALL_FILES " $' \n ' " $resolved_dep "
272- fi
273- done
256+ if [ " $VERBOSE " = true ]; then
257+ echo " Note: Dependency resolution is currently disabled" >&2
258+ fi
274259 fi
275260done
276261
@@ -295,8 +280,9 @@ if [ "$INCLUDE_GIT" = true ] && command -v git >/dev/null 2>&1 && git rev-parse
295280 echo " ,"
296281 echo " \" git\" : {"
297282 echo " \" commits\" : ["
283+ commit_count=0
298284 git log -n $GIT_DEPTH --pretty=format:" %h|%s|%an|%ar" | while IFS=" |" read -r hash message author date; do
299- if [ " $hash " != " $( git log -n $GIT_DEPTH --pretty=format: " %h " | head -n1 ) " ]; then
285+ if [ $commit_count -gt 0 ]; then
300286 echo " ,"
301287 fi
302288 echo " {"
@@ -305,6 +291,7 @@ if [ "$INCLUDE_GIT" = true ] && command -v git >/dev/null 2>&1 && git rev-parse
305291 echo " \" author\" : \" $( echo " $author " | sed ' s/"/\\"/g' ) \" ,"
306292 echo " \" date\" : \" $date \" "
307293 echo -n " }"
294+ commit_count=$(( commit_count + 1 ))
308295 done
309296 echo " "
310297 echo " ],"
331318if [ " $FORMAT " = " json" ]; then
332319 echo " "
333320 echo " ]"
321+
322+ # Include prompt if specified
323+ if [ -n " $PROMPT_FILE " ] && [ -f " $PROMPT_FILE " ]; then
324+ prompt_content=$( cat " $PROMPT_FILE " | sed ' s/"/\\"/g' | sed ' :a;N;$!ba;s/\n/\\n/g' )
325+ echo " ,"
326+ echo " \" prompt\" : \" $prompt_content \" "
327+ fi
328+
334329 echo " }"
330+ else
331+ # Add context generation footer
332+ case $FORMAT in
333+ md)
334+ echo " ## Context Information"
335+ echo " "
336+ echo " Generated on: $( date) "
337+ echo " Total files: $file_count "
338+ echo " Total size: $( human_readable_size $TOTAL_SIZE ) "
339+ echo " "
340+
341+ # Include prompt template if specified
342+ if [ -n " $PROMPT_FILE " ] && [ -f " $PROMPT_FILE " ]; then
343+ echo " ## Instructions for LLM"
344+ echo " "
345+ cat " $PROMPT_FILE "
346+ echo " "
347+ fi
348+ ;;
349+ text)
350+ echo " CONTEXT INFORMATION"
351+ echo " ------------------"
352+ echo " "
353+ echo " Generated on: $( date) "
354+ echo " Total files: $file_count "
355+ echo " Total size: $( human_readable_size $TOTAL_SIZE ) "
356+ echo " "
357+
358+ # Include prompt template if specified
359+ if [ -n " $PROMPT_FILE " ] && [ -f " $PROMPT_FILE " ]; then
360+ echo " INSTRUCTIONS FOR LLM"
361+ echo " -------------------"
362+ echo " "
363+ cat " $PROMPT_FILE "
364+ echo " "
365+ fi
366+ ;;
367+ esac
335368fi
336369
337- # Add context generation footer
338- case $FORMAT in
339- md)
340- echo " ## Context Information"
341- echo " "
342- echo " Generated on: $( date) "
343- echo " Total files: $file_count "
344- echo " Total size: $( numfmt --to=iec-i --suffix=B $TOTAL_SIZE ) "
345- echo " "
346- ;;
347- json)
348- # Already closed
349- ;;
350- text)
351- echo " CONTEXT INFORMATION"
352- echo " ------------------"
353- echo " "
354- echo " Generated on: $( date) "
355- echo " Total files: $file_count "
356- echo " Total size: $( numfmt --to=iec-i --suffix=B $TOTAL_SIZE ) "
357- echo " "
358- ;;
359- esac
360-
361370exit 0
0 commit comments