bootstrap: projects management repo

This commit is contained in:
OpenClaw Bot
2026-02-28 12:04:34 -06:00
commit 9cb7a67331
6 changed files with 245 additions and 0 deletions

124
tools/projectsctl Executable file
View File

@@ -0,0 +1,124 @@
#!/usr/bin/env bash
set -euo pipefail
REPO="${PROJREPO:-$(cd "$(dirname "$0")/.." && pwd)}"
LOCK="$REPO/.projects.lock"
TS="$(date +%F_%H%M%S)"
cd "$REPO"
need() { command -v "$1" >/dev/null 2>&1 || { echo "ERROR: missing $1"; exit 1; }; }
need jq
need git
need python3
need flock
exec 9>"$LOCK"
flock -n 9 || { echo "ERROR: projectsctl is already running"; exit 2; }
backup() {
mkdir -p "$REPO/backups"
cp -a "$REPO/projects.json" "$REPO/backups/projects.json.$TS"
}
render() {
./tools/render_projects_index.py >/dev/null
}
git_sync() {
git rev-parse --is-inside-work-tree >/dev/null
git checkout main >/dev/null 2>&1 || true
git pull --rebase --autostash >/dev/null 2>&1 || true
}
git_commit_push() {
if git diff --quiet && git diff --cached --quiet; then
echo "No changes to commit."
exit 0
fi
git add projects.json PROJECTS_INDEX.md
git commit -m "projects: update ($TS)" >/dev/null
git push origin main >/dev/null
echo "OK: committed and pushed to main."
}
cmd="${1:-}"; shift || true
case "$cmd" in
add)
name="${1:-}"; status="${2:-active}"; shift 2 || true
repo="${1:-}"; local_path="${2:-}"; shift 2 || true
notes="${*:-}"
[[ -n "$name" ]] || { echo "usage: projectsctl add <name> [status] [repo] [local_path] [notes...]"; exit 1; }
git_sync
backup
tmp="$(mktemp)"
jq --arg name "$name" --arg status "$status" --arg repo "$repo" --arg lp "$local_path" --arg notes "$notes" '
.projects |= ( . + [ {name:$name,status:$status,type:"",repo:$repo,local_path:$lp,notes:$notes} ] )
' projects.json > "$tmp"
mv "$tmp" projects.json
render
git_commit_push
;;
set-status)
name="${1:-}"; status="${2:-}"
[[ -n "$name" && -n "$status" ]] || { echo "usage: projectsctl set-status <name> <active|paused|archived>"; exit 1; }
git_sync
backup
tmp="$(mktemp)"
jq --arg name "$name" --arg status "$status" '
.projects |= (map(if .name==$name then .status=$status else . end))
' projects.json > "$tmp"
mv "$tmp" projects.json
render
git_commit_push
;;
update-notes)
name="${1:-}"; shift || true
notes="${*:-}"
[[ -n "$name" && -n "$notes" ]] || { echo "usage: projectsctl update-notes <name> <notes...>"; exit 1; }
git_sync
backup
tmp="$(mktemp)"
jq --arg name "$name" --arg notes "$notes" '
.projects |= (map(if .name==$name then .notes=$notes else . end))
' projects.json > "$tmp"
mv "$tmp" projects.json
render
git_commit_push
;;
list)
python3 - <<'PY'
import json
p=json.load(open("projects.json"))
for pr in p.get("projects",[]):
print(f"{pr.get('status',''):8} {pr.get('name','')}")
PY
;;
render)
render
echo "OK: rendered PROJECTS_INDEX.md"
;;
*)
echo "usage:"
echo " projectsctl add <name> [status] [repo] [local_path] [notes...]"
echo " projectsctl set-status <name> <active|paused|archived>"
echo " projectsctl update-notes <name> <notes...>"
echo " projectsctl list"
echo " projectsctl render"
exit 1
;;
esac