Skip to content

Instantly share code, notes, and snippets.

@curtis-arch
Created February 26, 2025 11:46
Show Gist options
  • Save curtis-arch/3371949e844c15df2201259c288e3d7b to your computer and use it in GitHub Desktop.
Save curtis-arch/3371949e844c15df2201259c288e3d7b to your computer and use it in GitHub Desktop.
Cursor Agent Tree Generator - Zero Dependencies Project Structure Visualization
#!/bin/bash
# save to .scripts/update_structure.sh
# Ensure required directory exists
mkdir -p .cursor/rules/
# Ensure we're in a git repository
if ! git rev-parse --is-inside-work-tree &> /dev/null; then
echo "Error: Not in a git repository. Please run this script from a git repository."
exit 1
fi
# Function to print a tree-like structure of files and directories
print_git_tree() {
# Create temporary files for git output and processing
local tmp_dir="/tmp"
local git_files="${tmp_dir}/git_files_$$_${RANDOM}.txt"
# Clean up on exit
trap 'rm -f "$git_files"' EXIT
# Get all git files and write to output file
git ls-files --others --exclude-standard --cached | sort > "$git_files"
# Create the output file with header
echo "# Project Structure" > .cursor/rules/structure.mdc
echo "" >> .cursor/rules/structure.mdc
echo "\`\`\`" >> .cursor/rules/structure.mdc
echo "." >> .cursor/rules/structure.mdc
# Process each path from git
local prev_path=""
local prev_parts=()
while IFS= read -r path; do
# Skip if it's a directory (git usually only lists files)
if [[ -d "$path" && ! -L "$path" ]]; then continue; fi
# Split current path into components
IFS='/' read -ra curr_parts <<< "$path"
local curr_depth=${#curr_parts[@]}
# Build directory structure for this file
for ((i=0; i<curr_depth; i++)); do
# Skip processing the file itself, we'll handle it separately
if [[ $i -eq $((curr_depth-1)) ]]; then
continue
fi
# Construct the partial path up to this component
local partial_path=""
for ((j=0; j<=i; j++)); do
if [[ -n "$partial_path" ]]; then
partial_path="$partial_path/${curr_parts[j]}"
else
partial_path="${curr_parts[j]}"
fi
done
# Check if we've already processed this directory
if [[ "$prev_path" == "$partial_path"* ]]; then
# Already processed this directory in the previous iteration
continue
fi
# Calculate common prefix with previous path
local common=0
if [[ ${#prev_parts[@]} -gt 0 ]]; then
for ((k=0; k<i && k<${#prev_parts[@]}; k++)); do
if [[ "${curr_parts[k]}" == "${prev_parts[k]}" ]]; then
((common++))
else
break
fi
done
fi
# Determine indent based on common prefix
local indent=""
for ((k=0; k<i; k++)); do
if [[ $k -lt $common ]]; then
# Check if this level has more items
local has_more=0
local parent_path=""
for ((l=0; l<k; l++)); do
if [[ -n "$parent_path" ]]; then
parent_path="$parent_path/${curr_parts[l]}"
else
parent_path="${curr_parts[l]}"
fi
done
# If there are more siblings at this level, add vertical line
if grep -q "^$parent_path/${curr_parts[k]}/" "$git_files"; then
has_more=1
fi
if [[ $has_more -eq 1 ]]; then
indent="${indent}│ "
else
indent="${indent} "
fi
else
indent="${indent} "
fi
done
# Determine if this is the last directory at its level
local is_last=1
local dir_path=""
for ((k=0; k<i; k++)); do
if [[ -n "$dir_path" ]]; then
dir_path="$dir_path/${curr_parts[k]}"
else
dir_path="${curr_parts[k]}"
fi
done
# Check if there are more directories at this level
if grep -q "^$dir_path/${curr_parts[i]}/" "$git_files"; then
is_last=0
fi
# Choose connector
local connector="├── "
if [[ $is_last -eq 1 ]]; then
connector="└── "
fi
# Output the directory
echo "${indent}${connector}${curr_parts[i]}/" >> .cursor/rules/structure.mdc
done
# Now output the file itself
local file_indent=""
for ((i=0; i<curr_depth-1; i++)); do
file_indent="${file_indent} "
done
# Determine if this is the last file in its directory
local parent_dir=""
for ((i=0; i<curr_depth-1; i++)); do
if [[ -n "$parent_dir" ]]; then
parent_dir="$parent_dir/${curr_parts[i]}"
else
parent_dir="${curr_parts[i]}"
fi
done
# Check if there are more files in this directory
local is_last_file=1
while IFS= read -r next_file; do
if [[ "$next_file" > "$path" && "$next_file" == "$parent_dir/"* ]]; then
is_last_file=0
break
fi
done < "$git_files"
# Choose connector for file
local file_connector="├── "
if [[ $is_last_file -eq 1 ]]; then
file_connector="└── "
fi
# Output the file
echo "${file_indent}${file_connector}${curr_parts[curr_depth-1]}" >> .cursor/rules/structure.mdc
# Remember this path for the next iteration
prev_path="$path"
prev_parts=("${curr_parts[@]}")
done < "$git_files"
# Close the code block
echo "\`\`\`" >> .cursor/rules/structure.mdc
echo "Project structure has been updated in .cursor/rules/structure.mdc"
}
# Run the tree function
print_git_tree
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment