- agents: issue-worker.md and pr-reviewer.md updated for standard branch naming (issue/<number>-<slug> instead of ai/<repo>#<number>) - paper-dynasty: updated SKILL.md, generate_summary, smoke_test, validate_database scripts; added ecosystem_status.sh and plan/ - plugins: updated marketplace submodules and blocklist - sessions: rotate session files, add session-analysis/ - settings: updated settings.json Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
274 lines
9.7 KiB
Bash
Executable File
274 lines
9.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ecosystem_status.sh — Paper Dynasty cross-project dashboard
|
|
# Usage: GITEA_TOKEN=<token> ./ecosystem_status.sh
|
|
# or: ./ecosystem_status.sh (auto-reads from gitea-mcp config if available)
|
|
|
|
set -euo pipefail
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Auth
|
|
# ---------------------------------------------------------------------------
|
|
if [[ -z "${GITEA_TOKEN:-}" ]]; then
|
|
# Try to pull token from the gitea-mcp config (standard claude-code location)
|
|
GITEA_MCP_CONFIG="${HOME}/.config/claude-code/mcp-servers/gitea-mcp.json"
|
|
if [[ -f "$GITEA_MCP_CONFIG" ]]; then
|
|
GITEA_TOKEN=$(python3 -c "
|
|
import json, sys, os
|
|
cfg_path = os.environ.get('GITEA_MCP_CONFIG', '')
|
|
try:
|
|
cfg = json.load(open(cfg_path))
|
|
env = cfg.get('env', {})
|
|
print(env.get('GITEA_TOKEN', env.get('GITEA_API_TOKEN', '')))
|
|
except Exception:
|
|
print('')
|
|
" 2>/dev/null)
|
|
fi
|
|
fi
|
|
|
|
if [[ -z "${GITEA_TOKEN:-}" ]]; then
|
|
echo "ERROR: GITEA_TOKEN not set and could not be read from gitea-mcp config." >&2
|
|
echo " Set it with: export GITEA_TOKEN=your-token" >&2
|
|
exit 1
|
|
fi
|
|
|
|
API_BASE="https://git.manticorum.com/api/v1"
|
|
AUTH_HEADER="Authorization: token ${GITEA_TOKEN}"
|
|
|
|
REPOS=(
|
|
"cal/paper-dynasty-database"
|
|
"cal/paper-dynasty-discord"
|
|
"cal/paper-dynasty-card-creation"
|
|
"cal/paper-dynasty-website"
|
|
"cal/paper-dynasty-gameplay-webapp"
|
|
"cal/paper-dynasty-apiproxy"
|
|
)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
gitea_get() {
|
|
# gitea_get <endpoint-path> — returns JSON or "null" on error
|
|
curl -sf -H "$AUTH_HEADER" -H "Content-Type: application/json" \
|
|
"${API_BASE}/${1}" 2>/dev/null || echo "null"
|
|
}
|
|
|
|
count_items() {
|
|
local json="$1"
|
|
if [[ "$json" == "null" || -z "$json" ]]; then
|
|
echo "?"
|
|
return
|
|
fi
|
|
python3 -c "
|
|
import json,sys
|
|
data=json.loads(sys.argv[1])
|
|
print(len(data) if isinstance(data,list) else '?')
|
|
" "$json" 2>/dev/null || echo "?"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Banner
|
|
# ---------------------------------------------------------------------------
|
|
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
|
|
echo ""
|
|
echo "╔══════════════════════════════════════════════════════════╗"
|
|
echo "║ PAPER DYNASTY — ECOSYSTEM STATUS DASHBOARD ║"
|
|
printf "║ %-56s ║\n" "$TIMESTAMP"
|
|
echo "╚══════════════════════════════════════════════════════════╝"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Per-repo summary table
|
|
# ---------------------------------------------------------------------------
|
|
echo ""
|
|
printf "%-36s %6s %5s %s\n" "REPOSITORY" "ISSUES" "PRs" "LATEST COMMIT"
|
|
printf "%-36s %6s %5s %s\n" "────────────────────────────────────" "──────" "─────" "────────────────────────────────"
|
|
|
|
TOTAL_ISSUES=0
|
|
TOTAL_PRS=0
|
|
|
|
declare -A ALL_ISSUES_JSON
|
|
declare -A ALL_PRS_JSON
|
|
|
|
for REPO in "${REPOS[@]}"; do
|
|
SHORT_NAME="${REPO#cal/}"
|
|
|
|
ISSUES_JSON=$(gitea_get "repos/${REPO}/issues?type=issues&state=open&limit=50")
|
|
ISSUE_COUNT=$(count_items "$ISSUES_JSON")
|
|
ALL_ISSUES_JSON["$REPO"]="$ISSUES_JSON"
|
|
|
|
PRS_JSON=$(gitea_get "repos/${REPO}/pulls?state=open&limit=50")
|
|
PR_COUNT=$(count_items "$PRS_JSON")
|
|
ALL_PRS_JSON["$REPO"]="$PRS_JSON"
|
|
|
|
COMMITS_JSON=$(gitea_get "repos/${REPO}/commits?limit=1")
|
|
LATEST_SHA="n/a"
|
|
LATEST_DATE=""
|
|
if [[ "$COMMITS_JSON" != "null" && -n "$COMMITS_JSON" ]]; then
|
|
LATEST_SHA=$(python3 -c "
|
|
import json,sys
|
|
data=json.loads(sys.argv[1])
|
|
if isinstance(data,list) and data:
|
|
print(data[0].get('sha','')[:7])
|
|
else:
|
|
print('n/a')
|
|
" "$COMMITS_JSON" 2>/dev/null || echo "n/a")
|
|
LATEST_DATE=$(python3 -c "
|
|
import json,sys
|
|
data=json.loads(sys.argv[1])
|
|
if isinstance(data,list) and data:
|
|
ts=data[0].get('commit',{}).get('committer',{}).get('date','')
|
|
print(ts[:10] if ts else '')
|
|
else:
|
|
print('')
|
|
" "$COMMITS_JSON" 2>/dev/null || echo "")
|
|
fi
|
|
|
|
if [[ "$ISSUE_COUNT" =~ ^[0-9]+$ ]]; then
|
|
TOTAL_ISSUES=$((TOTAL_ISSUES + ISSUE_COUNT))
|
|
fi
|
|
if [[ "$PR_COUNT" =~ ^[0-9]+$ ]]; then
|
|
TOTAL_PRS=$((TOTAL_PRS + PR_COUNT))
|
|
fi
|
|
|
|
COMMIT_LABEL="${LATEST_SHA}${LATEST_DATE:+ [${LATEST_DATE}]}"
|
|
printf "%-36s %6s %5s %s\n" "$SHORT_NAME" "$ISSUE_COUNT" "$PR_COUNT" "$COMMIT_LABEL"
|
|
done
|
|
|
|
echo ""
|
|
printf " TOTALS: %d open issues, %d open PRs across %d repos\n" \
|
|
"$TOTAL_ISSUES" "$TOTAL_PRS" "${#REPOS[@]}"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Recent commits — last 3 per repo
|
|
# ---------------------------------------------------------------------------
|
|
echo ""
|
|
echo "═══════════════════════════════════════════════════════════════"
|
|
echo " RECENT COMMITS (last 3 per repo)"
|
|
echo "═══════════════════════════════════════════════════════════════"
|
|
|
|
for REPO in "${REPOS[@]}"; do
|
|
SHORT_NAME="${REPO#cal/}"
|
|
echo ""
|
|
echo " ▸ ${SHORT_NAME}"
|
|
|
|
COMMITS_JSON=$(gitea_get "repos/${REPO}/commits?limit=3")
|
|
if [[ "$COMMITS_JSON" == "null" || -z "$COMMITS_JSON" ]]; then
|
|
echo " (could not fetch commits)"
|
|
continue
|
|
fi
|
|
|
|
python3 -c "
|
|
import json, sys
|
|
|
|
data = json.loads(sys.argv[1])
|
|
if not isinstance(data, list) or not data:
|
|
print(' (no commits)')
|
|
sys.exit(0)
|
|
|
|
for c in data:
|
|
sha = c.get('sha', '')[:7]
|
|
msg = c.get('commit', {}).get('message', '').split('\n')[0][:58]
|
|
ts = c.get('commit', {}).get('committer', {}).get('date', '')[:10]
|
|
author = c.get('commit', {}).get('committer', {}).get('name', 'unknown')[:16]
|
|
print(f' {sha} {ts} {author:<16} {msg}')
|
|
" "$COMMITS_JSON"
|
|
done
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Open issues detail
|
|
# ---------------------------------------------------------------------------
|
|
echo ""
|
|
echo "═══════════════════════════════════════════════════════════════"
|
|
echo " OPEN ISSUES"
|
|
echo "═══════════════════════════════════════════════════════════════"
|
|
|
|
FOUND_ISSUES=false
|
|
for REPO in "${REPOS[@]}"; do
|
|
SHORT_NAME="${REPO#cal/}"
|
|
ISSUES_JSON="${ALL_ISSUES_JSON[$REPO]}"
|
|
ISSUE_COUNT=$(count_items "$ISSUES_JSON")
|
|
|
|
if [[ "$ISSUE_COUNT" == "0" || "$ISSUE_COUNT" == "?" ]]; then
|
|
continue
|
|
fi
|
|
|
|
echo ""
|
|
echo " ▸ ${SHORT_NAME} (${ISSUE_COUNT} open)"
|
|
FOUND_ISSUES=true
|
|
|
|
python3 -c "
|
|
import json, sys
|
|
|
|
data = json.loads(sys.argv[1])
|
|
if not isinstance(data, list):
|
|
print(' (error reading issues)')
|
|
sys.exit(0)
|
|
|
|
for i in data[:10]:
|
|
num = i.get('number', '?')
|
|
title = i.get('title', '(no title)')[:65]
|
|
labels = ', '.join(l.get('name','') for l in i.get('labels',[]))
|
|
lstr = f' [{labels}]' if labels else ''
|
|
print(f' #{num:<4} {title}{lstr}')
|
|
|
|
if len(data) > 10:
|
|
print(f' ... and {len(data)-10} more')
|
|
" "$ISSUES_JSON"
|
|
done
|
|
|
|
if [[ "$FOUND_ISSUES" == "false" ]]; then
|
|
echo ""
|
|
echo " (no open issues across all repos)"
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Open PRs detail
|
|
# ---------------------------------------------------------------------------
|
|
echo ""
|
|
echo "═══════════════════════════════════════════════════════════════"
|
|
echo " OPEN PULL REQUESTS"
|
|
echo "═══════════════════════════════════════════════════════════════"
|
|
|
|
FOUND_PRS=false
|
|
for REPO in "${REPOS[@]}"; do
|
|
SHORT_NAME="${REPO#cal/}"
|
|
PRS_JSON="${ALL_PRS_JSON[$REPO]}"
|
|
PR_COUNT=$(count_items "$PRS_JSON")
|
|
|
|
if [[ "$PR_COUNT" == "0" || "$PR_COUNT" == "?" ]]; then
|
|
continue
|
|
fi
|
|
|
|
echo ""
|
|
echo " ▸ ${SHORT_NAME} (${PR_COUNT} open)"
|
|
FOUND_PRS=true
|
|
|
|
python3 -c "
|
|
import json, sys
|
|
|
|
data = json.loads(sys.argv[1])
|
|
if not isinstance(data, list):
|
|
print(' (error reading PRs)')
|
|
sys.exit(0)
|
|
|
|
for pr in data:
|
|
num = pr.get('number', '?')
|
|
title = pr.get('title', '(no title)')[:65]
|
|
head = pr.get('head', {}).get('label', '')
|
|
base = pr.get('base', {}).get('label', '')
|
|
print(f' #{num:<4} {title}')
|
|
if head and base:
|
|
print(f' {head} → {base}')
|
|
" "$PRS_JSON"
|
|
done
|
|
|
|
if [[ "$FOUND_PRS" == "false" ]]; then
|
|
echo ""
|
|
echo " (no open PRs across all repos)"
|
|
fi
|
|
|
|
echo ""
|
|
echo "═══════════════════════════════════════════════════════════════"
|
|
echo " Done. Gitea: https://git.manticorum.com/cal"
|
|
echo "═══════════════════════════════════════════════════════════════"
|
|
echo ""
|