#!/bin/bash # Describes a project root: lists notable files, inlines orientation docs, # and detects an associated "meta" repo (private notes companion). # # Usage: orient-project [PATH] # PATH defaults to the git toplevel of cwd, or cwd itself if not in a repo. # # Emits a markdown fragment intended to sit under an h3 or smaller. # Inlined file contents are wrapped in XML tags so they # carry their own framing and don't fight the surrounding markdown. # # Never fully silent (a project root always exists), but individual # subsections (meta repo, etc.) are omitted when there's nothing to say. set -euo pipefail # Resolve the project root. # Explicit arg wins; otherwise prefer git toplevel; fall back to cwd. if [ $# -ge 1 ]; then root=$(cd "$1" && pwd) elif root=$(git rev-parse --show-toplevel 2>/dev/null); then : else root="$PWD" fi echo "- project root: \`${root}\`" # Orientation docs to inline if present. # Case-insensitive name match against a small set of conventional filenames, # with a few common extensions. `.editorconfig` is included by exact name. # We deliberately don't recurse: this is a root-orientation probe. shopt -s nullglob nocaseglob orient_files=() for base in README CONTRIBUTING HACKME AGENTS CLAUDE; do for candidate in "$root/$base" "$root/$base".md "$root/$base".rst "$root/$base".txt; do if [ -f "$candidate" ]; then orient_files+=("$candidate") fi done done shopt -u nocaseglob if [ -f "$root/.editorconfig" ]; then orient_files+=("$root/.editorconfig") fi # Inline the orientation docs, each wrapped in its own tag. # The path attribute is repo-relative for readability. if [ ${#orient_files[@]} -gt 0 ]; then echo echo "#### Orientation files" echo for f in "${orient_files[@]}"; do rel="${f#$root/}" echo "" cat "$f" echo "" echo done fi # List remaining root entries (depth 1, no dotfiles, excluding files we inlined). # This is a sanity probe, not an inventory tool, so cap at 50 entries. # (If a project root has more than 50 visible entries at depth 1, # something else is going on and a fuller listing isn't the right tool anyway.) mapfile -t root_entries < <( find "$root" -mindepth 1 -maxdepth 1 -not -name '.*' -printf '%f\n' | sort ) # Build a lookup of inlined basenames so we can skip them in the listing. declare -A inlined=() for f in "${orient_files[@]}"; do inlined["$(basename "$f")"]=1 done remaining=() for entry in "${root_entries[@]}"; do if [ -z "${inlined[$entry]:-}" ]; then remaining+=("$entry") fi done if [ ${#remaining[@]} -gt 0 ]; then echo echo "#### Other root entries" echo count=0 for entry in "${remaining[@]}"; do if [ $count -ge 50 ]; then echo "- ... ($((${#remaining[@]} - 50)) more not shown)" break fi if [ -d "$root/$entry" ]; then echo "- \`${entry}/\`" else echo "- \`${entry}\`" fi count=$((count + 1)) done fi # Meta repo detection. # Two conventions, checked in order: # 1. `/.meta` -- if it's a symlink, or a non-empty directory. # (An empty real directory falls through; it's probably stale state.) # 2. `/../.meta/` -- sibling directory next to the repo. # The first hit wins; we don't try to merge. meta="" # Resolve symlinks (`pwd -P`) so the reported path points to where files live. if [ -L "$root/.meta" ] && [ -d "$root/.meta" ]; then meta=$(cd "$root/.meta" && pwd -P) elif [ -d "$root/.meta" ] && [ -n "$(ls -A "$root/.meta" 2>/dev/null)" ]; then meta=$(cd "$root/.meta" && pwd -P) fi if [ -z "$meta" ]; then parent=$(dirname "$root") base=$(basename "$root") sibling="$parent/$base.meta" if [ -d "$sibling" ]; then meta=$(cd "$sibling" && pwd) fi fi if [ -n "$meta" ]; then echo echo "#### Meta repo" echo echo "- path: \`${meta}\`" # Directory layout: shallow listing of subdirs to convey structure. # Prune dotdirs entirely (don't descend into them) so noise like `.git/objects/` # doesn't leak in via the maxdepth-2 walk. mapfile -t meta_dirs < <( find "$meta" -mindepth 1 -maxdepth 2 \ \( -name '.*' -prune \) -o \ \( -type d -printf '%P\n' \) \ | sort ) if [ ${#meta_dirs[@]} -gt 0 ]; then echo "- directory layout:" for d in "${meta_dirs[@]}"; do echo " - \`${d}/\`" done fi # Recent worklogs: filenames with a YYYY-MM-DD prefix. # Look first under `worklogs/`, then fall back to anywhere in the meta repo. # Sort by the date prefix (lexicographic = chronological for ISO dates), # show the 5 most recent. if [ -d "$meta/worklogs" ]; then worklog_search_root="$meta/worklogs" else worklog_search_root="$meta" fi mapfile -t worklogs < <( find "$worklog_search_root" -type f -name '????-??-??*' -printf '%P\n' \ | sort -r \ | head -n 5 ) if [ ${#worklogs[@]} -gt 0 ]; then echo "- recent worklogs (under \`${worklog_search_root#$meta/}\`):" for w in "${worklogs[@]}"; do echo " - \`${w}\`" done fi fi