Created
June 21, 2026 11:00
-
-
Save ramsunvtech/c06c2b780a4f1e72f3ff23b4a05d9165 to your computer and use it in GitHub Desktop.
Frontend Claude MD Generator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # Regenerates the "Routes" section of CLAUDE.md from a Next.js project. | |
| # Works with App Router (app/) or Pages Router (pages/), auto-detected. | |
| # Handles any route group names (parens), dynamic segments ([id], [...slug]), | |
| # and flat structures with no groups at all. | |
| # Everything outside the BEGIN/END markers is left untouched. | |
| # Run from the repo root: ./update-claude-md.sh | |
| set -euo pipefail | |
| FILE="CLAUDE.md" | |
| BEGIN="<!-- BEGIN:routes -->" | |
| END="<!-- END:routes -->" | |
| if [ ! -f "$FILE" ]; then | |
| echo "Error: $FILE not found in $(pwd). Create it first." >&2 | |
| exit 1 | |
| fi | |
| # Detect router type. Supports a /src layout too. | |
| ROOT_DIR="" | |
| ROUTER_TYPE="" | |
| for candidate in "app" "src/app"; do | |
| if [ -d "$candidate" ]; then | |
| ROOT_DIR="$candidate" | |
| ROUTER_TYPE="App Router" | |
| break | |
| fi | |
| done | |
| if [ -z "$ROOT_DIR" ]; then | |
| for candidate in "pages" "src/pages"; do | |
| if [ -d "$candidate" ]; then | |
| ROOT_DIR="$candidate" | |
| ROUTER_TYPE="Pages Router" | |
| break | |
| fi | |
| done | |
| fi | |
| if [ -z "$ROOT_DIR" ]; then | |
| echo "Error: no app/ or pages/ directory found. Run this from the repo root." >&2 | |
| exit 1 | |
| fi | |
| # Folders to ignore anywhere in the tree (not routes) | |
| EXCLUDE_NAMES="api|components|lib|utils|hooks|styles|constants|types|context|provider|redux|store|services|config|__tests__|node_modules" | |
| is_route_group() { | |
| [[ "$1" == \(*\) ]] | |
| } | |
| is_excluded() { | |
| local name="$1" | |
| [[ "$name" =~ ^(${EXCLUDE_NAMES})$ ]] | |
| } | |
| # List route-like children of a dir (skips excluded names, includes route groups & dynamic segments) | |
| list_children() { | |
| local dir="$1" | |
| find "$dir" -mindepth 1 -maxdepth 1 -type d | sort | while IFS= read -r entry; do | |
| basename "$entry" | |
| done | |
| } | |
| LINES="" | |
| while IFS= read -r name; do | |
| full="$ROOT_DIR/$name" | |
| if is_route_group "$name"; then | |
| children=$(list_children "$full" | grep -vE "^(${EXCLUDE_NAMES})$" || true) | |
| children_csv=$(echo "$children" | sed '/^$/d' | tr '\n' ',' | sed 's/,/, /g; s/, $//') | |
| if [ -n "$children_csv" ]; then | |
| LINES="${LINES}- \`${name}/\` — ${children_csv}\n" | |
| else | |
| LINES="${LINES}- \`${name}/\` (empty)\n" | |
| fi | |
| elif is_excluded "$name"; then | |
| continue | |
| else | |
| LINES="${LINES}- \`${name}/\`\n" | |
| fi | |
| done < <(list_children "$ROOT_DIR") | |
| if [ -z "$LINES" ]; then | |
| LINES="(no routes found under ${ROOT_DIR}/)\n" | |
| fi | |
| BLOCK="${BEGIN} | |
| ## Routes (${ROUTER_TYPE}, \`${ROOT_DIR}/\`) | |
| $(printf '%b' "$LINES") | |
| ${END}" | |
| if grep -q "$BEGIN" "$FILE" 2>/dev/null; then | |
| awk -v block="$BLOCK" -v begin="$BEGIN" -v end="$END" ' | |
| $0 ~ begin {print block; skip=1; next} | |
| $0 ~ end {skip=0; next} | |
| !skip {print} | |
| ' "$FILE" > "${FILE}.tmp" && mv "${FILE}.tmp" "$FILE" | |
| echo "Updated route map in $FILE ($ROUTER_TYPE detected at $ROOT_DIR/)" | |
| else | |
| printf '\n%s\n' "$BLOCK" >> "$FILE" | |
| echo "Added route map block to $FILE ($ROUTER_TYPE detected at $ROOT_DIR/)" | |
| fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment