cognitive-memory/scripts/memory-git-sync.sh
Cal Corum d8dd1f35a5 feat: complete multi-graph support across CLI, scripts, and systemd timers
Non-default graphs were second-class citizens — timers only maintained the
default graph, git sync ignored named graphs, there was no way to create
a graph without editing config manually, cross-graph edge errors were
confusing, and utility scripts were hardcoded to the default graph.

- Add `graph-create` CLI command + `create_graph()` in common.py, with
  custom path registration written to the default graph's _config.json
- Add `scripts/maintain-all-graphs.sh` to loop decay/core/embed/reflect
  over all discovered graphs; update systemd services to call it
- Refactor `memory-git-sync.sh` into sync_repo() function that iterates
  default + all named graphs with .git directories
- Improve cross-graph edge ValueError to explain the same-graph constraint
- Add --graph flag to edge-proposer.py and session_memory.py
- Update systemd/README.md with portable paths and new architecture

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 22:35:55 -06:00

104 lines
4.2 KiB
Bash
Executable File

#!/bin/bash
# Commit and push cognitive memory changes to Gitea.
#
# Syncs the default graph and any named graphs whose directory is a git repo.
# Called daily by cognitive-memory-daily.service after decay/core runs.
# Safe to run multiple times — only commits when there are actual changes.
#
# Default graph: COGNITIVE_MEMORY_DIR > XDG_DATA_HOME > ~/.local/share/cognitive-memory
# Named graphs: ~/.local/share/cognitive-memory-graphs/<name>/ (any with .git/)
#
# Location: ~/.claude/skills/cognitive-memory/scripts/memory-git-sync.sh
set -uo pipefail
# Note: -e intentionally omitted at the top level so that one graph's failure
# does not prevent the remaining graphs from being synced. Each function call
# is checked explicitly.
# ── resolve directories ────────────────────────────────────────────────────────
MEMORY_DIR="${COGNITIVE_MEMORY_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/cognitive-memory}"
# Named graphs live in a sibling directory: cognitive-memory-graphs/<name>/
GRAPHS_BASE_DIR="${MEMORY_DIR%cognitive-memory}cognitive-memory-graphs"
# ── sync function ──────────────────────────────────────────────────────────────
# sync_repo <dir> <label>
# Commits and pushes any pending changes in the given git repo directory.
# Returns 0 on success or "nothing to commit", non-zero on error.
sync_repo() {
local dir="$1"
local label="$2"
# Directory must exist and be a git repo
if [ ! -d "$dir/.git" ]; then
echo "memory-git-sync [$label]: not a git repo, skipping"
return 0
fi
(
# Run everything inside a subshell so cd is scoped and set -e is safe
set -euo pipefail
cd "$dir"
# Nothing to commit?
if git diff --quiet && git diff --cached --quiet \
&& [ -z "$(git ls-files --others --exclude-standard)" ]; then
echo "memory-git-sync [$label]: no changes to commit"
exit 0
fi
# Stage all changes (.gitignore handles exclusions)
git add -A
# Build a descriptive commit message
ADDED=$(git diff --cached --name-only --diff-filter=A | wc -l)
MODIFIED=$(git diff --cached --name-only --diff-filter=M | wc -l)
DELETED=$(git diff --cached --name-only --diff-filter=D | wc -l)
EDGES=$(git diff --cached --name-only --diff-filter=ACM | grep -c '^graph/edges/' || true)
MSG="daily sync: ${ADDED} added, ${MODIFIED} modified, ${DELETED} deleted"
if [ "$EDGES" -gt 0 ]; then
MSG="$MSG (${EDGES} edges)"
fi
git commit -m "$MSG" --no-gpg-sign 2>/dev/null || {
echo "memory-git-sync [$label]: commit failed (pre-commit hook?)"
exit 1
}
git push origin main 2>/dev/null || {
echo "memory-git-sync [$label]: push failed"
exit 1
}
echo "memory-git-sync [$label]: pushed to origin/main"
)
}
# ── sync default graph ─────────────────────────────────────────────────────────
ERRORS=0
sync_repo "$MEMORY_DIR" "default" || ERRORS=$((ERRORS + 1))
# ── sync named graphs ──────────────────────────────────────────────────────────
# Iterate subdirectories of GRAPHS_BASE_DIR; skip non-git directories silently.
if [ -d "$GRAPHS_BASE_DIR" ]; then
for graph_dir in "$GRAPHS_BASE_DIR"/*/; do
# glob may yield literal "*/'" if directory is empty
[ -d "$graph_dir" ] || continue
graph_name="$(basename "$graph_dir")"
sync_repo "$graph_dir" "$graph_name" || ERRORS=$((ERRORS + 1))
done
fi
# ── exit status ───────────────────────────────────────────────────────────────
if [ "$ERRORS" -gt 0 ]; then
echo "memory-git-sync: completed with $ERRORS error(s)"
exit 1
fi