Bash functions to provide repository context for LLM chats

Posted . Visible to the public.

I use the Gemini web chat interface quite extensively. One thing that is tedious is giving it all the context it needs to do a proper job. Context engineering is not an easy task, but on the other hand we now have context limits of ~1 million token, which allows us to just dump in everything we have in many cases. And when we do that in the web interface, we can avoid extra costs that would be charged when using the API!

The functions below pack your current work (diffs, full repos, or specific commits) into XML/Diff files, which are then load into your clipboard as File Uploads. My workflow with them is something like this:

  1. Run pack_repo_context in a terminal
  2. Open https://gemini.google.com Show archive.org snapshot in a new tab
  3. Press Ctrl+V
  4. Ask a question
  5. Repeat

Prerequisites

Usage

Function Use Cases
pack_repo_context Packs the entire repo (with some exclusions!) into 1-5 XML files. Can be used for all kind of tasks from "How does this project work?" to large refactors.
pack_git_diff_main Generates a diff between the current work and main. Can be used for Code reviews, test generation etc.
pack_git_diff_head Generates a diff of uncommitted changes. Can be used to get assistance on current work.
pack_git_show Generates a diff for the last commit on your current branch. Can sometimes be useful to iterate on already committed work.

Configuration

Bash Functions

# ~/.bashrc or wherever else you place your bash functions
IGNORED_BINARY_EXTS=(
  "png" "jpg" "jpeg" "gif" "ico" "svg"
  "woff" "woff2" "ttf" "eot"
  "pdf" "PDF" "d2" "bmml" "xsd" "xsl"
  "jar" "gz" "map" "enc" "m4v" "yml"
  "key" "sqlite3" "rdb"
)

_copy_file_uris() {
  local uri_list=""
  local count=0

  for file_path in "$@"; do
    if [[ -f "$file_path" ]]; then
       uri_list="${uri_list}file://$(readlink -f "$file_path")\n"
       ((count++))
    fi
  done

  if [ $count -eq 0 ]; then
    echo "No files created or found to copy."
    return 1
  fi

  printf "$uri_list" | head -c -1 | xclip -selection clipboard -t text/uri-list
  echo "📋 Copied $count file(s) to clipboard."
}

___repomix_ignores() {
  local excludes=""
  for ext in "${IGNORED_BINARY_EXTS[@]}"; do
    excludes="$excludes,**/*.$ext"
  done
  echo "${excludes:1}"
}

___git_excludes() {
  excludes=()
  for ext in "${IGNORED_BINARY_EXTS[@]}"; do
    excludes+=(":!*.$ext")
  done
}

pack_repo_context() {
  local dyn_ignores=$(___repomix_ignores)
  local out_dir="tmp"
  local base_name="repomix-out"

  mkdir -p "$out_dir"
  rm -f "$out_dir/$base_name"*.xml

  npx repomix@latest \
    --ignore "$dyn_ignores" \
    --output "$out_dir/$base_name.xml" \
    --split-output "2mb" \
    --quiet

  local files=()
  if compgen -G "$out_dir/$base_name*.xml" > /dev/null; then
    files=("$out_dir/$base_name"*.xml)
  else
    files=("$out_dir/$base_name.xml")
  fi

  _copy_file_uris "${files[@]}"
}

pack_git_diff_main() {
  local out_file="tmp/git-diff-main.diff"
  local excludes
  ___git_excludes

  mkdir -p tmp
  git diff main -- "${excludes[@]}" > "$out_file"
  _copy_file_uris "$out_file"
}

pack_git_diff_head() {
  local out_file="tmp/git-diff-head.diff"
  local excludes
  ___git_excludes

  mkdir -p tmp
  git diff HEAD -- "${excludes[@]}" > "$out_file"
  _copy_file_uris "$out_file"
}

pack_git_show() {
  local out_file="tmp/git-show.diff"
  local excludes
  ___git_excludes

  mkdir -p tmp

  if [ $# -eq 0 ]; then
     git show -- "${excludes[@]}" > "$out_file"
  else
     git show "$@" -- "${excludes[@]}" > "$out_file"
  fi

  _copy_file_uris "$out_file"
}

Repomix Config

LLMs parse XML quite well. Reduce whitespace and comments to save tokens. Ignore some files explicitly.

# ~/.config/repomix/repomix.config.json
{
  "output": {
    "dir": "tmp",
    "style": "xml",
    "removeEmptyLines": true,
    "removeComments": true,
    "parsableStyle": true,
    "compress": false
  },
  "ignore": {
    "customPatterns": [
      "db/migrate/*.rb",
      ".env*",
      "bin/*",
      "public/packs/*",
      "public/packs-test/*",
      "spec/fixtures/*",
      "gurney.yml",
      ".geordi.yml"
    ]
  }
}

Alternative bash alias

The bash functions above are only an iteration from my previous bash alias. It became quite unreadable over time and and copies the raw XML to the clipboard (not files). If you prefer less magic in your .bashrc, feel free to use it instead:

alias pack_repo_context='npx repomix@latest --remove-empty-lines --remove-comments --ignore "db/migrate/*.rb,.env*,bin/*,*/**/*.key,*/**/*.sqlite3,*/**/*.svg,*/**/*.woff2,*/**/*.eot,*/**/*.ttf,*/**/*.woff,*/**/*.rdb,*.rdb,*/**/*.gif,*/**/*.jpg,*/**/*.jpeg,*/**/*.d2,*/**/*.bmml,*/**/*.xsd,*/**/*.xsl,*/**/*.jar,*/**/*.gz*,*/**/*.map,public/packs/*,public/packs-test/*,spec/fixtures/*,**/*.png,**/*.pdf,**/*.jpg,**/*.ico,spec/**/*.yml,**/*.enc,**/*.PDF,gurney.yml,.geordi.yml," -o tmp/repomix.xml --quiet --copy'
Profile picture of Michael Leimstädtner
Michael Leimstädtner
Last edit
Michael Leimstädtner
License
Source code in this card is licensed under the MIT License.
Posted by Michael Leimstädtner to makandra dev (2026-01-15 11:12)