workspace-manager/.workspace-ops/skills/iop-weekly-subproject-work-summary/scripts/collect.sh

196 lines
4.2 KiB
Bash
Executable file

#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORKSPACE_ROOT="${1:-$(cd "$SCRIPT_DIR/../../../.." && pwd)}"
SINCE="${2:-7 days ago}"
UNTIL="${3:-}"
if [[ "$WORKSPACE_ROOT" != "/" ]]; then
WORKSPACE_ROOT="${WORKSPACE_ROOT%/}"
fi
TARGET_REPOS=(
"agent-shell"
"agent-framework"
"iop"
"nexo"
"oto"
"proto-socket"
)
resolve_repo_rel() {
local requested="$1"
if [[ -d "$WORKSPACE_ROOT/$requested/.git" ]]; then
printf '%s\n' "$requested"
return 0
fi
if [[ "$requested" == "agent-framework" && -d "$WORKSPACE_ROOT/agentic-framework/.git" ]]; then
printf '%s\n' "agentic-framework"
return 0
fi
return 1
}
is_sync_path() {
local path="$1"
case "$path" in
agent-ops/*|.claude/*|AGENTS.md|GEMINI.md|CLAUDE.md|.cursorrules|.clinerules)
return 0
;;
esac
return 1
}
includes_all_changes() {
local requested="$1"
local rel="$2"
[[ "$requested" == "agent-framework" || "$rel" == "agent-framework" || "$rel" == "agentic-framework" ]]
}
commit_paths() {
local repo="$1"
local sha="$2"
git -C "$repo" diff-tree --root --no-commit-id --name-only -r "$sha"
}
is_summary_commit() {
local repo="$1"
local sha="$2"
local path
while IFS= read -r path; do
[[ -z "$path" ]] && continue
if ! is_sync_path "$path"; then
return 0
fi
done < <(commit_paths "$repo" "$sha")
return 1
}
path_bucket() {
awk '
NF {
n = split($0, a, "/")
if (n == 1) {
p = a[1]
} else if (a[1] == "agent-task" || a[1] == "agent-roadmap" || a[1] == "agent-ops") {
p = a[1] "/" a[2]
} else {
p = a[1] "/" a[2]
}
c[p]++
}
END {
for (p in c) print c[p], p
}
'
}
shortstat_for_commits() {
local repo="$1"
shift
local sha
for sha in "$@"; do
git -C "$repo" show --shortstat --pretty=tformat: "$sha"
done |
awk '
/files? changed/ {
files += $1
for (i = 1; i <= NF; i++) {
if ($i == "insertion(+)" || $i == "insertions(+)") ins += $(i - 1)
if ($i == "deletion(-)" || $i == "deletions(-)") del += $(i - 1)
}
}
END {
printf "files_changed_sum=%d insertions=%d deletions=%d\n", files, ins, del
}
'
}
log_args=(--branches --remotes --since="$SINCE")
if [[ -n "$UNTIL" ]]; then
log_args+=(--until="$UNTIL")
fi
for requested in "${TARGET_REPOS[@]}"; do
if ! rel="$(resolve_repo_rel "$requested")"; then
continue
fi
repo="$WORKSPACE_ROOT/$rel"
mapfile -t all_shas < <(git -C "$repo" log "${log_args[@]}" --format='%H' | sort -u)
((${#all_shas[@]} > 0)) || continue
summary_shas=()
if includes_all_changes "$requested" "$rel"; then
summary_shas=("${all_shas[@]}")
else
for sha in "${all_shas[@]}"; do
if is_summary_commit "$repo" "$sha"; then
summary_shas+=("$sha")
fi
done
fi
((${#summary_shas[@]} > 0)) || continue
echo "## $rel"
if [[ "$requested" != "$rel" ]]; then
echo "requested_alias: $requested"
fi
printf 'branch: '
git -C "$repo" branch --show-current
echo "total_commits_in_period: ${#all_shas[@]}"
echo "summary_commits: ${#summary_shas[@]}"
authors="$(git -C "$repo" show --no-patch --format='%aN <%aE>' "${summary_shas[@]}" | sort -u)"
author_count="$(printf '%s\n' "$authors" | awk 'NF {c++} END {print c + 0}')"
echo "author_count: $author_count"
echo "authors:"
if [[ -n "$authors" ]]; then
printf '%s\n' "$authors" | sed 's/^/- /'
fi
echo "subjects:"
git -C "$repo" show --no-patch --date=iso --format='%h | %ad | %s' "${summary_shas[@]}"
echo
if includes_all_changes "$requested" "$rel"; then
echo "top paths:"
for sha in "${summary_shas[@]}"; do
commit_paths "$repo" "$sha"
done |
path_bucket |
sort -nr |
head -n 16
else
echo "top non-sync paths:"
for sha in "${summary_shas[@]}"; do
while IFS= read -r path; do
[[ -z "$path" ]] && continue
if ! is_sync_path "$path"; then
printf '%s\n' "$path"
fi
done < <(commit_paths "$repo" "$sha")
done |
path_bucket |
sort -nr |
head -n 16
fi
echo "shortstat:"
shortstat_for_commits "$repo" "${summary_shas[@]}"
echo
done