Skip to content

Instantly share code, notes, and snippets.

@code-yeongyu
Last active August 27, 2025 13:19
Show Gist options
  • Save code-yeongyu/9004d32faca17b8e691e0d6836f1c94d to your computer and use it in GitHub Desktop.
Save code-yeongyu/9004d32faca17b8e691e0d6836f1c94d to your computer and use it in GitHub Desktop.
print given files marked with xml-like tags, for LLMs.
#!/bin/bash
# Enable globstar for ** pattern support
shopt -s globstar 2>/dev/null
show_help() {
cat << 'EOF'
fp is an interactive file printer for Large Language Models.
It wraps file contents in LLM-friendly XML format, enabling efficient context
sharing with AI assistants for code review, debugging, and development tasks.
Author: YeonGyu Kim <[email protected]> https://github.com/code-yeongyu
Usage: fp [options] [file|directory|pattern]...
INPUT
file Output a single file
directory/ Output all files in directory recursively
*.ext Output all files matching the pattern
**/*.ext Output all files matching pattern recursively
OPTIONS
-h, --help Show this message
OUTPUT FORMAT
Files are wrapped in XML tags for optimal LLM processing:
<files>
<file path="relative/or/absolute/path">
... file contents ...
</file>
</files>
EXAMPLES
fp file.txt Output single file
fp src/ Output all files in directory recursively
fp *.js Output all JavaScript files
fp src/**/*.py Output all Python files under src (recursive)
fp main.go utils/*.go Output multiple files and patterns
NOTES
- Paths are shown relative to current directory when possible
- Supports glob patterns with ** for recursive matching
- No output is produced if no files match the pattern
- Empty arguments show this help message
ENVIRONMENT VARIABLES
None required. Uses standard shell globbing.
EOF
}
main() {
local file_found=0
local output=""
# Check for help flag or no arguments
if [ $# -eq 0 ] || [ "$1" = "--help" ] || [ "$1" = "-h" ]; then
show_help
exit 0
fi
# Process all arguments - bash will expand wildcards before passing them
for path in "$@"; do
local result
result=$(process_path "$path")
if [ -n "$result" ]; then
file_found=1
output="${output}${result}"
fi
done
# Only print output if files were found
if [ $file_found -eq 1 ]; then
printf "<files>\n"
printf "%s" "$output"
printf "</files>\n"
fi
}
get_display_path() {
local path="$1"
local cwd=$(pwd)
# Check if path is a child of cwd
if [[ "$path" == "$cwd"/* ]]; then
# Use relative path
echo "${path#$cwd/}"
else
# Use absolute path
echo "$path"
fi
}
print_file() {
local file_path="$1"
local display_path
display_path=$(get_display_path "$file_path")
printf '<file path="%s">\n' "$display_path"
cat "$file_path" 2>/dev/null
printf "</file>\n"
}
process_path() {
local path="$1"
local resolved_path
resolved_path=$(realpath "$path" 2>/dev/null || printf "%s" "$path")
if [ -f "$resolved_path" ]; then
print_file "$resolved_path"
elif [ -d "$resolved_path" ]; then
find "$resolved_path" -type f 2>/dev/null | sort | while IFS= read -r file; do
print_file "$file"
done
fi
}
# Call main with all arguments
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment