compass

add orient-project: root files, inlined docs, meta repo detection

commit 51870a0624b192a14c8a8db0fc22212cd291131d · parent 2385489487 · browse this tree
Ximianth, 2026-06-24 13:07:25 UTC

Combines project-root orientation and meta-repo detection in one probe,
since the two share resolution logic and naturally want to be reported
together.

Behaviour:

- Resolves the project root from an optional path arg, else `git rev-parse
  --show-toplevel`, else cwd.
- Inlines conventional orientation files (README, CONTRIBUTING, HACKME,
  AGENTS, CLAUDE -- case-insensitive, common extensions -- plus
  `.editorconfig` exactly).  Each wrapped in a `<file path="...">` tag
  per the project-wide convention for whole-file inclusion.
- Lists remaining root entries (depth 1) as a bullet list, capped at 50
  for sanity.
- Detects an associated meta repo by either an in-repo `.meta` symlink or
  non-empty directory (the documented convention dominates), or a sibling
  `<basename>.meta/` directory.  Reports the resolved path, a shallow dir
  layout (dotdirs pruned -- otherwise `.git/objects/` etc leaks in), and
  up to 5 most-recent worklogs by ISO date prefix.
diff --git a/orient-project b/orient-project
new file mode 100755
index 0000000..93d7292
--- /dev/null
+++ b/orient-project
@@ -0,0 +1,163 @@
+#!/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 <file path="..."> 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 <file> 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 "<file path=\"${rel}\">"
+		cat "$f"
+		echo "</file>"
+		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. `<root>/.meta` -- if it's a symlink, or a non-empty directory.
+#      (An empty real directory falls through; it's probably stale state.)
+#   2. `<root>/../<basename>.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