#!/usr/bin/env bash
# Regenerate .claude/INDEX.md — a per-service inventory of GraphQL types, SQL
# tables, Python blueprints, and notable Go packages. Read by the
# sourcehut-lookup skill as the first step of any documentation lookup.
#
# Runs in seconds. Safe to re-run. Called as the final step of sourcehut-refresh.
set -u
cd "$(dirname "$0")/../.." || exit 1
ROOT="$PWD"
OUT="$ROOT/.claude/INDEX.md"
# Comma-join stdin lines, sorted unique.
join_csv() { sort -u | paste -sd',' - | sed 's/,/, /g'; }
# Identify each direct child that is a git repo.
mapfile -t REPOS < <(
find . -mindepth 2 -maxdepth 2 -name .git -type d 2>/dev/null \
| sed -E 's:^\./([^/]+)/\.git$:\1:' \
| sort
)
tmp=$(mktemp)
trap 'rm -f "$tmp"' EXIT
{
echo "# SourceHut documentation mirror — index"
echo
echo "Auto-generated by \`.claude/scripts/build-index.sh\`. Regenerated at the end of every \`sourcehut-refresh\` run. Do not edit by hand — changes will be overwritten."
echo
echo "Read this **before** grepping. Each section gives you the canonical place to look for symbols in that repo."
echo
echo "_Generated: $(date -u +'%Y-%m-%d %H:%M:%S UTC')_"
echo
echo "## Repos"
echo
for repo in "${REPOS[@]}"; do
tag=$(git -C "$repo" describe --tags --exact-match 2>/dev/null \
|| git -C "$repo" rev-parse --abbrev-ref HEAD 2>/dev/null)
echo "- \`$repo\` — $tag"
done
echo
} > "$tmp"
extract_one() {
local repo="$1"
local tag
tag=$(git -C "$repo" describe --tags --exact-match 2>/dev/null \
|| echo "(branch: $(git -C "$repo" rev-parse --abbrev-ref HEAD 2>/dev/null))")
echo "## $repo"
echo
echo "_Tag:_ \`$tag\`"
echo
# GraphQL types — typically <repo>/api/graph/schema.graphqls
local graphqls
graphqls=$(find "$repo" -name '*.graphqls' -not -path '*/node_modules/*' 2>/dev/null)
if [ -n "$graphqls" ]; then
local gqlpath
gqlpath=$(printf '%s\n' "$graphqls" | head -n1 | sed "s|^$repo/||")
echo "**GraphQL types** (\`$gqlpath\`)"
echo
for kind in type input enum interface union scalar; do
local names
names=$(grep -rhE "^${kind} [A-Z][A-Za-z0-9_]*" $graphqls 2>/dev/null \
| awk -v k="$kind" '{print $2}' \
| sed 's/[({].*//' \
| join_csv)
[ -n "$names" ] && echo "- \`${kind}\`: $names"
done
echo
fi
# SQL tables — <repo>/schema.sql
if [ -f "$repo/schema.sql" ]; then
local tables
tables=$(grep -hE '^CREATE TABLE' "$repo/schema.sql" 2>/dev/null \
| sed -E 's/CREATE TABLE (IF NOT EXISTS )?"?([A-Za-z0-9_]+)"?.*/\2/' \
| join_csv)
if [ -n "$tables" ]; then
echo "**SQL tables** (\`schema.sql\`)"
echo
echo "$tables"
echo
fi
# Migrations count
if [ -d "$repo/migrations" ]; then
local mcount latest
mcount=$(find "$repo/migrations" -name '*.sql' 2>/dev/null | wc -l | tr -d ' ')
latest=$(find "$repo/migrations" -name '*.sql' 2>/dev/null | sort | tail -n1 | sed "s|$repo/||")
[ "$mcount" -gt 0 ] && echo "_Migrations: $mcount files, latest \`$latest\`_" && echo
fi
fi
# Python blueprints — <repo>/<svc>srht/blueprints/*.py (require a prefix so we
# don't match core.sr.ht's bare `srht/` — that's handled separately below)
local py_pkg
py_pkg=$(find "$repo" -maxdepth 2 -type d -name '?*srht' -not -path '*/contrib/*' 2>/dev/null | head -n1)
if [ -n "$py_pkg" ] && [ -d "$py_pkg/blueprints" ]; then
local bps
bps=$(find "$py_pkg/blueprints" -maxdepth 1 -name '*.py' -not -name '__init__.py' 2>/dev/null \
| xargs -n1 basename 2>/dev/null \
| sed 's/\.py$//' \
| join_csv)
[ -n "$bps" ] && echo "**Python blueprints** (\`$(basename "$py_pkg")/blueprints/\`): $bps" && echo
fi
# Python GraphQL client queries
if [ -n "$py_pkg" ] && [ -d "$py_pkg/graphql" ]; then
local pyql
pyql=$(find "$py_pkg/graphql" -maxdepth 1 -name '*.graphql' 2>/dev/null \
| xargs -n1 basename 2>/dev/null | sed 's/\.graphql$//' | join_csv)
[ -n "$pyql" ] && echo "**Python GraphQL queries** (\`$(basename "$py_pkg")/graphql/\`): $pyql" && echo
fi
# Notable Go subdirs — cmd/*, top-level dirs with *.go, plus root-level .go files
if [ -f "$repo/go.mod" ]; then
local cmds gopkgs rootgo
cmds=$(find "$repo/cmd" -mindepth 1 -maxdepth 1 -type d 2>/dev/null \
| xargs -n1 basename 2>/dev/null | join_csv)
[ -n "$cmds" ] && echo "**Go binaries** (\`cmd/\`): $cmds" && echo
# Repo-root .go files (e.g. api.sr.ht has main.go + auth.go at top)
rootgo=$(find "$repo" -maxdepth 1 -name '*.go' 2>/dev/null \
| xargs -n1 basename 2>/dev/null | sort | join_csv)
[ -n "$rootgo" ] && echo "**Go files (root)**: $rootgo" && echo
# Top-level Go packages
gopkgs=$(find "$repo" -mindepth 2 -maxdepth 2 -name '*.go' -not -path '*/generated*' 2>/dev/null \
| sed -E "s:^$repo/::; s:/[^/]+\.go$::" \
| sort -u \
| grep -vE '^(cmd|migrations|node_modules)' \
| head -20 \
| join_csv)
[ -n "$gopkgs" ] && echo "**Go packages**: \`$gopkgs\`" && echo
fi
# Plain Python package (no `<svc>srht` directory) — e.g. core.sr.ht ships `srht/`
if [ -z "$py_pkg" ] && [ -d "$repo/srht" ]; then
local srht_mods
srht_mods=$(find "$repo/srht" -maxdepth 1 -name '*.py' -not -name '__init__.py' 2>/dev/null \
| xargs -n1 basename 2>/dev/null | sed 's/\.py$//' | join_csv)
local srht_subs
srht_subs=$(find "$repo/srht" -mindepth 1 -maxdepth 1 -type d -not -name '__pycache__' 2>/dev/null \
| xargs -n1 basename 2>/dev/null | join_csv)
[ -n "$srht_mods" ] && echo "**Python modules** (\`srht/\`): $srht_mods" && echo
[ -n "$srht_subs" ] && echo "**Python subpackages** (\`srht/\`): $srht_subs" && echo
fi
echo "---"
echo
}
for repo in "${REPOS[@]}"; do
extract_one "$repo" >> "$tmp"
done
# Cross-repo symbol map: which repos define each common GraphQL type name?
{
echo "## Cross-repo GraphQL type map"
echo
echo "When the same type name appears in multiple repos, it is a *different* type in each — they are not federated as one. Use this map to pick the right service before reading."
echo
declare -A type_to_repos
for repo in "${REPOS[@]}"; do
while IFS= read -r name; do
[ -z "$name" ] && continue
type_to_repos[$name]+="$repo "
done < <(
find "$repo" -name '*.graphqls' -exec grep -hE '^(type|input|enum) [A-Z][A-Za-z0-9_]*' {} + 2>/dev/null \
| awk '{print $2}' | sed 's/[({].*//' | sort -u
)
done
# Print only types defined in >=2 repos (the interesting cases).
for name in $(printf '%s\n' "${!type_to_repos[@]}" | sort); do
repos="${type_to_repos[$name]}"
count=$(printf '%s\n' $repos | wc -l | tr -d ' ')
if [ "$count" -ge 2 ]; then
echo "- \`$name\` → $(printf '%s, ' $repos | sed 's/, $//')"
fi
done
} >> "$tmp"
mv "$tmp" "$OUT"
trap - EXIT
echo "Wrote $OUT ($(wc -l < "$OUT" | tr -d ' ') lines, $(wc -c < "$OUT" | tr -d ' ') bytes)"