create-egregore 0.19.3 → 0.19.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/egregore-launcher.js +410 -26
- package/bin/cli.js +34 -20
- package/lib/local.js +84 -11
- package/lib/pi-runtime.js +186 -0
- package/lib/setup.js +56 -11
- package/lib/ui.js +1 -1
- package/package.json +8 -6
- package/runtime/codex/.codex/hooks/search-hint.js +65 -0
- package/runtime/codex/.codex/hooks.json +13 -0
- package/runtime/codex/.codex/skills/deep-reflect/SKILL.md +14 -10
- package/runtime/codex/.codex/skills/minion/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/scroll/SKILL.md +168 -0
- package/runtime/codex/.codex/skills/scroll/agents/openai.yaml +4 -0
- package/runtime/codex/.codex/skills/scroll/assets/shell.html +542 -0
- package/runtime/codex/.codex/skills/search/SKILL.md +56 -0
- package/runtime/codex/.codex/skills/session-view/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/thread/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/view/SKILL.md +147 -32
- package/runtime/codex/.codex/skills/view/agents/openai.yaml +4 -0
- package/runtime/codex/.codex/skills/view/assets/compose-scaffold.html +221 -0
- package/runtime/codex/.codex/spec-manifest.json +4 -4
- package/runtime/codex/AGENTS.md +31 -5
- package/runtime/codex/bin/activity-data.sh +46 -4
- package/runtime/codex/bin/agent.sh +10 -2
- package/runtime/codex/bin/codex-install-skills.sh +1 -1
- package/runtime/codex/bin/codex-render-spec.mjs +24 -2
- package/runtime/codex/bin/codex-session-start.sh +6 -60
- package/runtime/codex/bin/codex-skill-render.mjs +20 -12
- package/runtime/codex/bin/codex-sync-skills.sh +4 -0
- package/runtime/codex/bin/graph-op.sh +56 -5
- package/runtime/codex/bin/graph.sh +36 -0
- package/runtime/codex/bin/handoff-run.sh +157 -7
- package/runtime/codex/bin/handoff-save-egregore.sh +6 -1
- package/runtime/codex/bin/index-handoff.sh +9 -3
- package/runtime/codex/bin/lib/context.sh +97 -18
- package/runtime/codex/bin/lib/dashboard-artifact.sh +1 -1
- package/runtime/codex/bin/lib/git-sync.sh +35 -9
- package/runtime/codex/bin/lib/greeting.sh +30 -104
- package/runtime/codex/bin/lib/metrics.sh +374 -0
- package/runtime/codex/bin/publish-artifact.sh +43 -1
- package/runtime/codex/bin/settings.sh +274 -0
- package/runtime/codex/manifest.json +18 -1
- package/runtime/pi/.codex/config.toml +15 -0
- package/runtime/pi/.codex/hooks/branch-guard.js +242 -0
- package/runtime/pi/.codex/hooks/search-hint.js +65 -0
- package/runtime/pi/.codex/hooks.json +30 -0
- package/runtime/pi/.codex/skills/activity/SKILL.md +70 -0
- package/runtime/pi/.codex/skills/add/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/announce/SKILL.md +54 -0
- package/runtime/pi/.codex/skills/answer/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/archive/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/ask/SKILL.md +59 -0
- package/runtime/pi/.codex/skills/branch/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/checkup/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/commit/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/contribute/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/dark-mode/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/dashboard/SKILL.md +59 -0
- package/runtime/pi/.codex/skills/deep-reflect/SKILL.md +74 -0
- package/runtime/pi/.codex/skills/delete-user/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/egregore-voice/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/emissary/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/env/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/handoff/SKILL.md +67 -0
- package/runtime/pi/.codex/skills/harvest/SKILL.md +107 -0
- package/runtime/pi/.codex/skills/ingest/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/ingest-google/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/ingest-user-interview/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/invite/SKILL.md +40 -0
- package/runtime/pi/.codex/skills/issue/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/me/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/meeting/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/minion/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/note/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/onboarding/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/pr/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/project/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/pull/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/push/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/quest/SKILL.md +77 -0
- package/runtime/pi/.codex/skills/quest-suggest/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/reflect/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/review-pr/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/save/SKILL.md +62 -0
- package/runtime/pi/.codex/skills/scroll/SKILL.md +168 -0
- package/runtime/pi/.codex/skills/scroll/agents/openai.yaml +4 -0
- package/runtime/pi/.codex/skills/scroll/assets/shell.html +542 -0
- package/runtime/pi/.codex/skills/search/SKILL.md +56 -0
- package/runtime/pi/.codex/skills/session-view/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/setup/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/summon/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/sync-repos/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/telegram-connect/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/telemetry/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/test/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/the-spiral/SKILL.md +84 -0
- package/runtime/pi/.codex/skills/thread/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/todo/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/tui-design/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/tutorial/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/update/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/view/SKILL.md +156 -0
- package/runtime/pi/.codex/skills/view/agents/openai.yaml +4 -0
- package/runtime/pi/.codex/skills/view/assets/compose-scaffold.html +221 -0
- package/runtime/pi/.codex/skills/visual-explain/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/wrap/SKILL.md +55 -0
- package/runtime/pi/.codex/spec-manifest.json +114 -0
- package/runtime/pi/.pi/APPEND_SYSTEM.md +255 -0
- package/runtime/pi/.pi/extensions/egregore.ts +324 -0
- package/runtime/pi/.pi/settings.json +6 -0
- package/runtime/pi/.pi/spec-manifest.json +12 -0
- package/runtime/pi/AGENTS.md +350 -0
- package/runtime/pi/bin/activity-data.sh +242 -0
- package/runtime/pi/bin/agent.sh +677 -0
- package/runtime/pi/bin/codex-install-skills.sh +78 -0
- package/runtime/pi/bin/codex-render-spec.mjs +394 -0
- package/runtime/pi/bin/codex-session-start.sh +188 -0
- package/runtime/pi/bin/codex-skill-render.mjs +323 -0
- package/runtime/pi/bin/codex-sync-skills.sh +227 -0
- package/runtime/pi/bin/dashboard-data.sh +178 -0
- package/runtime/pi/bin/graph-batch.sh +60 -0
- package/runtime/pi/bin/graph-op.sh +431 -0
- package/runtime/pi/bin/graph-wal.sh +228 -0
- package/runtime/pi/bin/graph.sh +177 -0
- package/runtime/pi/bin/handoff-pr-backfill.sh +86 -0
- package/runtime/pi/bin/handoff-run.sh +581 -0
- package/runtime/pi/bin/handoff-save-egregore.sh +136 -0
- package/runtime/pi/bin/index-handoff.sh +312 -0
- package/runtime/pi/bin/lib/artifact-id.sh +46 -0
- package/runtime/pi/bin/lib/config.sh +71 -0
- package/runtime/pi/bin/lib/context.sh +542 -0
- package/runtime/pi/bin/lib/dashboard-artifact.sh +142 -0
- package/runtime/pi/bin/lib/ensure-develop.sh +183 -0
- package/runtime/pi/bin/lib/git-sync.sh +288 -0
- package/runtime/pi/bin/lib/greeting.sh +340 -0
- package/runtime/pi/bin/lib/hash.sh +13 -0
- package/runtime/pi/bin/lib/identity.sh +174 -0
- package/runtime/pi/bin/lib/metrics.sh +374 -0
- package/runtime/pi/bin/lib/permissions.sh +106 -0
- package/runtime/pi/bin/lib/time.sh +56 -0
- package/runtime/pi/bin/lib/worktree-links.sh +54 -0
- package/runtime/pi/bin/notify.sh +207 -0
- package/runtime/pi/bin/pi-branch-policy.mjs +15 -0
- package/runtime/pi/bin/pi-product-workflows.mjs +324 -0
- package/runtime/pi/bin/pi-render-spec.mjs +114 -0
- package/runtime/pi/bin/pi-session-start.sh +24 -0
- package/runtime/pi/bin/publish-artifact.sh +201 -0
- package/runtime/pi/bin/publish-references.sh +144 -0
- package/runtime/pi/bin/repo-state.sh +99 -0
- package/runtime/pi/bin/telemetry.sh +391 -0
- package/runtime/pi/bin/worktree-create.sh +118 -0
- package/runtime/pi/bin/worktree-remove.sh +50 -0
- package/runtime/pi/bin/worktree.sh +198 -0
- package/runtime/pi/manifest.json +176 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# handoff-save-egregore.sh — background save+auto-merge for the core repo after a handoff.
|
|
3
|
+
#
|
|
4
|
+
# /handoff renders its card first, then fires this detached. The user never waits on git.
|
|
5
|
+
#
|
|
6
|
+
# Behavior:
|
|
7
|
+
# - If nothing to save (clean working tree + up-to-date with origin) → exit silently.
|
|
8
|
+
# - If on develop/main/master → create dev/{author}/handoff-{YYYY-MM-DD} branch from origin/develop.
|
|
9
|
+
# - Commit all changes, rebase onto origin/develop, push.
|
|
10
|
+
# - If push succeeds: create PR to develop (or reuse existing), then:
|
|
11
|
+
# - Markdown-only diff → `gh pr merge --auto --merge` (auto-merges when checks pass).
|
|
12
|
+
# - Code/config present → leave PR open for review (no auto-merge).
|
|
13
|
+
#
|
|
14
|
+
# Usage:
|
|
15
|
+
# handoff-save-egregore.sh <author> <topic> [--repo-dir <path>]
|
|
16
|
+
#
|
|
17
|
+
# --repo-dir overrides the default working directory (normally the script's
|
|
18
|
+
# own repo root, resolved from $0). Tests and sandbox runs MUST pass it;
|
|
19
|
+
# without it, the script operates on the real repo regardless of caller cwd.
|
|
20
|
+
#
|
|
21
|
+
# Detached usage (the skill calls it this way):
|
|
22
|
+
# ( bash bin/handoff-save-egregore.sh "$AUTHOR" "$TOPIC" >/dev/null 2>&1 & ) >/dev/null 2>&1
|
|
23
|
+
#
|
|
24
|
+
# Exit 0 on any non-fatal outcome. The caller never reads stdout/stderr — all status
|
|
25
|
+
# lives in the PR itself and in `git log`.
|
|
26
|
+
|
|
27
|
+
set -uo pipefail
|
|
28
|
+
|
|
29
|
+
AUTHOR="${1:-}"
|
|
30
|
+
TOPIC="${2:-session work}"
|
|
31
|
+
shift 2 2>/dev/null || true
|
|
32
|
+
|
|
33
|
+
REPO_DIR=""
|
|
34
|
+
while [ $# -gt 0 ]; do
|
|
35
|
+
case "$1" in
|
|
36
|
+
--repo-dir) REPO_DIR="${2:-}"; shift 2 ;;
|
|
37
|
+
*) shift ;;
|
|
38
|
+
esac
|
|
39
|
+
done
|
|
40
|
+
|
|
41
|
+
[ -n "$AUTHOR" ] || { echo "handoff-save-egregore: missing author" >&2; exit 1; }
|
|
42
|
+
|
|
43
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
44
|
+
# If --repo-dir wasn't given, default to the script's own repo root.
|
|
45
|
+
[ -z "$REPO_DIR" ] && REPO_DIR="$SCRIPT_DIR"
|
|
46
|
+
cd "$REPO_DIR" || exit 1
|
|
47
|
+
|
|
48
|
+
# Shared safe-staging guard — never auto-commit stray sibling-repo gitlinks.
|
|
49
|
+
# shellcheck source=bin/lib/git-safe.sh
|
|
50
|
+
. "$SCRIPT_DIR/bin/lib/git-safe.sh" 2>/dev/null || true
|
|
51
|
+
type git_add_guarded >/dev/null 2>&1 || git_add_guarded() { git add -A 2>/dev/null || true; }
|
|
52
|
+
|
|
53
|
+
# --- Early exit if nothing to do -----------------------------------------
|
|
54
|
+
DIRTY=0
|
|
55
|
+
[ -n "$(git status --porcelain 2>/dev/null)" ] && DIRTY=1
|
|
56
|
+
|
|
57
|
+
CURRENT_BRANCH=$(git branch --show-current 2>/dev/null || echo "")
|
|
58
|
+
AHEAD=0
|
|
59
|
+
if [ -n "$CURRENT_BRANCH" ]; then
|
|
60
|
+
# Count commits ahead of origin/develop on the current branch (works from any
|
|
61
|
+
# dev/* branch or from develop itself).
|
|
62
|
+
AHEAD=$(git rev-list --count "origin/develop..HEAD" 2>/dev/null || echo 0)
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
if [ "$DIRTY" = "0" ] && [ "$AHEAD" = "0" ]; then
|
|
66
|
+
exit 0
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# --- Protect the protected branches: create a working branch if needed --
|
|
70
|
+
case "$CURRENT_BRANCH" in
|
|
71
|
+
develop|main|master|"")
|
|
72
|
+
SLUG_DATE=$(date +%Y-%m-%d)
|
|
73
|
+
NEW_BRANCH="dev/${AUTHOR}/handoff-${SLUG_DATE}"
|
|
74
|
+
git fetch origin develop --quiet 2>/dev/null || true
|
|
75
|
+
git checkout -b "$NEW_BRANCH" origin/develop --quiet 2>/dev/null || \
|
|
76
|
+
git checkout "$NEW_BRANCH" --quiet 2>/dev/null || exit 0
|
|
77
|
+
CURRENT_BRANCH="$NEW_BRANCH"
|
|
78
|
+
;;
|
|
79
|
+
esac
|
|
80
|
+
|
|
81
|
+
# --- Commit whatever is uncommitted --------------------------------------
|
|
82
|
+
if [ -n "$(git status --porcelain 2>/dev/null)" ]; then
|
|
83
|
+
git_add_guarded
|
|
84
|
+
git commit -m "Handoff: ${TOPIC}" --quiet 2>/dev/null || true
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
# --- Rebase onto develop, fall back to merge if rebase conflicts ---------
|
|
88
|
+
git fetch origin develop --quiet 2>/dev/null || true
|
|
89
|
+
if ! git rebase origin/develop --quiet 2>/dev/null; then
|
|
90
|
+
git rebase --abort 2>/dev/null || true
|
|
91
|
+
if ! git merge origin/develop --quiet -m "Sync with develop" 2>/dev/null; then
|
|
92
|
+
# Conflicts we can't auto-resolve. Leave the branch as-is; the user will see it
|
|
93
|
+
# next session or when they run /save explicitly.
|
|
94
|
+
git merge --abort 2>/dev/null || true
|
|
95
|
+
exit 0
|
|
96
|
+
fi
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
# --- Push ---------------------------------------------------------------
|
|
100
|
+
if ! git push -u origin "$CURRENT_BRANCH" --quiet 2>/dev/null; then
|
|
101
|
+
# Push failed (network, auth). Commits are safe locally.
|
|
102
|
+
exit 0
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
# --- PR: reuse open PR on this branch, or create one --------------------
|
|
106
|
+
command -v gh >/dev/null 2>&1 || exit 0
|
|
107
|
+
|
|
108
|
+
PR_NUMBER=""
|
|
109
|
+
EXISTING=$(gh pr list --head "$CURRENT_BRANCH" --state open --json number,baseRefName --jq '.[0]' 2>/dev/null)
|
|
110
|
+
EXISTING_NUMBER=$(echo "$EXISTING" | jq -r '.number // empty' 2>/dev/null)
|
|
111
|
+
EXISTING_BASE=$(echo "$EXISTING" | jq -r '.baseRefName // empty' 2>/dev/null)
|
|
112
|
+
|
|
113
|
+
if [ -n "$EXISTING_NUMBER" ]; then
|
|
114
|
+
PR_NUMBER="$EXISTING_NUMBER"
|
|
115
|
+
if [ "$EXISTING_BASE" != "develop" ]; then
|
|
116
|
+
gh pr edit "$PR_NUMBER" --base develop >/dev/null 2>&1 || true
|
|
117
|
+
fi
|
|
118
|
+
else
|
|
119
|
+
PR_URL=$(gh pr create \
|
|
120
|
+
--base develop \
|
|
121
|
+
--head "$CURRENT_BRANCH" \
|
|
122
|
+
--title "Handoff: ${TOPIC}" \
|
|
123
|
+
--body "Auto-created by /handoff. Session work for: ${TOPIC}" \
|
|
124
|
+
2>/dev/null || echo "")
|
|
125
|
+
PR_NUMBER=$(echo "$PR_URL" | grep -oE '[0-9]+$' || echo "")
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
[ -n "$PR_NUMBER" ] || exit 0
|
|
129
|
+
|
|
130
|
+
# --- Auto-merge iff markdown-only ---------------------------------------
|
|
131
|
+
NON_MD=$(git diff origin/develop --name-only 2>/dev/null | grep -v '\.md$' | head -1 || echo "")
|
|
132
|
+
if [ -z "$NON_MD" ]; then
|
|
133
|
+
gh pr merge "$PR_NUMBER" --auto --merge >/dev/null 2>&1 || true
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
exit 0
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Index a single handoff file into the Neo4j graph.
|
|
5
|
+
# Usage: bash bin/index-handoff.sh <file-path>
|
|
6
|
+
# Returns: {"sessionId":"...","resolved":N} or {"error":"..."}
|
|
7
|
+
#
|
|
8
|
+
# The file-path should be relative to memory/ (e.g. handoffs/2026-02/14-oz-topic.md)
|
|
9
|
+
# or an absolute/relative path that contains "handoffs/" somewhere in it.
|
|
10
|
+
|
|
11
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
12
|
+
|
|
13
|
+
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
|
|
14
|
+
echo "Usage: index-handoff.sh <handoff-file-path>"
|
|
15
|
+
echo ""
|
|
16
|
+
echo "Index a single handoff file into the Neo4j graph."
|
|
17
|
+
echo "Parses metadata (author, date, topic, recipients) from"
|
|
18
|
+
echo "markdown headers or YAML front matter, creates Session"
|
|
19
|
+
echo "and relationship nodes, and auto-resolves old handoffs."
|
|
20
|
+
echo ""
|
|
21
|
+
echo "Returns: {\"sessionId\":\"...\",\"resolved\":N}"
|
|
22
|
+
exit 0
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# --- Local mode gate: bail immediately ---
|
|
26
|
+
_MODE=$(jq -r '.mode // "connected"' "$SCRIPT_DIR/egregore.json" 2>/dev/null)
|
|
27
|
+
if [ "$_MODE" = "local" ]; then
|
|
28
|
+
echo '{"sessionId":"","resolved":0,"mode":"local"}'
|
|
29
|
+
exit 0
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# --- Validate input ---
|
|
33
|
+
FILE_ARG="${1:?Usage: index-handoff.sh <handoff-file-path>}"
|
|
34
|
+
|
|
35
|
+
# Resolve to absolute path if needed
|
|
36
|
+
if [[ "$FILE_ARG" = /* ]]; then
|
|
37
|
+
FILE_PATH="$FILE_ARG"
|
|
38
|
+
elif [[ "$FILE_ARG" = memory/* ]]; then
|
|
39
|
+
FILE_PATH="$SCRIPT_DIR/$FILE_ARG"
|
|
40
|
+
else
|
|
41
|
+
FILE_PATH="$SCRIPT_DIR/memory/$FILE_ARG"
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
if [ ! -f "$FILE_PATH" ]; then
|
|
45
|
+
echo '{"error":"File not found: '"$FILE_ARG"'"}'
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# --- Derive filePath (relative to memory/) and sessionId ---
|
|
50
|
+
REL_PATH="${FILE_PATH##*/memory/}"
|
|
51
|
+
if [[ "$REL_PATH" = "$FILE_PATH" ]]; then
|
|
52
|
+
REL_PATH="handoffs/${FILE_PATH##*handoffs/}"
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
FILENAME="$(basename "$FILE_PATH" .md)"
|
|
56
|
+
DIR_MONTH="$(basename "$(dirname "$FILE_PATH")")"
|
|
57
|
+
|
|
58
|
+
# sessionId = YYYY-MM-DD-author-topic
|
|
59
|
+
# If file is in a YYYY-MM subdirectory, derive from dir + filename
|
|
60
|
+
# If file is in root handoffs/ (no month subdir), extract date from filename
|
|
61
|
+
if [[ "$DIR_MONTH" =~ ^[0-9]{4}-[0-9]{2}$ ]]; then
|
|
62
|
+
DAY="${FILENAME%%-*}"
|
|
63
|
+
REST="${FILENAME#*-}"
|
|
64
|
+
SESSION_ID="${DIR_MONTH}-${DAY}-${REST}"
|
|
65
|
+
else
|
|
66
|
+
# Filename starts with YYYY-MM-DD — use it directly
|
|
67
|
+
SESSION_ID="$FILENAME"
|
|
68
|
+
DAY="$(echo "$FILENAME" | grep -oE '^[0-9]{4}-[0-9]{2}-[0-9]{2}' || true)"
|
|
69
|
+
REST="${FILENAME#*-*-*-}"
|
|
70
|
+
DIR_MONTH="$(echo "$DAY" | grep -oE '^[0-9]{4}-[0-9]{2}' || true)"
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
# --- Parse handoff metadata from markdown ---
|
|
74
|
+
CONTENT="$(cat "$FILE_PATH")"
|
|
75
|
+
|
|
76
|
+
# Helper: extract from markdown headers, handles both **Key**: Value and **Key:** Value
|
|
77
|
+
md_extract() {
|
|
78
|
+
echo "$CONTENT" | grep -m1 "$1" 2>/dev/null | sed "$2" || true
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# Helper: extract markdown field by name (handles **Key**: and **Key:** patterns)
|
|
82
|
+
md_field() {
|
|
83
|
+
local key="$1"
|
|
84
|
+
local val
|
|
85
|
+
# Try **Key**: Value (colon outside bold)
|
|
86
|
+
val="$(echo "$CONTENT" | grep -m1 "^\*\*${key}\*\*:" 2>/dev/null | sed "s/\*\*${key}\*\*:[[:space:]]*//" | tr -d '\n' || true)"
|
|
87
|
+
# Try **Key:** Value (colon inside bold)
|
|
88
|
+
if [ -z "$val" ]; then
|
|
89
|
+
val="$(echo "$CONTENT" | grep -m1 "^\*\*${key}:\*\*" 2>/dev/null | sed "s/\*\*${key}:\*\*[[:space:]]*//" | tr -d '\n' || true)"
|
|
90
|
+
fi
|
|
91
|
+
echo "$val"
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# Helper: extract from YAML front matter (key: value between --- delimiters)
|
|
95
|
+
yaml_extract() {
|
|
96
|
+
local key="$1"
|
|
97
|
+
sed -n '/^---$/,/^---$/p' "$FILE_PATH" 2>/dev/null | grep -m1 "^${key}:" 2>/dev/null | sed "s/^${key}:[[:space:]]*//" | sed 's/^"//' | sed 's/"$//' | xargs 2>/dev/null || true
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
# H1 topic (always from markdown body, not front matter)
|
|
101
|
+
TOPIC="$(md_extract '^# ' 's/^# //; s/^Handoff: //')"
|
|
102
|
+
# Also check YAML front matter topic
|
|
103
|
+
[ -z "$TOPIC" ] && TOPIC="$(yaml_extract 'topic')"
|
|
104
|
+
[ -z "$TOPIC" ] && TOPIC="$REST"
|
|
105
|
+
|
|
106
|
+
# Date: try markdown header, then YAML
|
|
107
|
+
DATE="$(md_field 'Date')"
|
|
108
|
+
[ -z "$DATE" ] && DATE="$(yaml_extract 'date')"
|
|
109
|
+
[ -z "$DATE" ] && DATE="${DIR_MONTH}-${DAY}"
|
|
110
|
+
|
|
111
|
+
# Author: try markdown header (Author or From), then YAML (author or from)
|
|
112
|
+
AUTHOR="$(md_field 'Author')"
|
|
113
|
+
[ -z "$AUTHOR" ] && AUTHOR="$(md_field 'From')"
|
|
114
|
+
[ -z "$AUTHOR" ] && AUTHOR="$(yaml_extract 'author')"
|
|
115
|
+
[ -z "$AUTHOR" ] && AUTHOR="$(yaml_extract 'from')"
|
|
116
|
+
# Strip " → recipient" if present (e.g. "cem → oz" → "cem")
|
|
117
|
+
AUTHOR_HANDLE="$(echo "$AUTHOR" | sed 's/[[:space:]]*→.*//' | awk '{print tolower($1)}')"
|
|
118
|
+
[ -z "$AUTHOR_HANDLE" ] && AUTHOR_HANDLE="unknown"
|
|
119
|
+
|
|
120
|
+
# Recipients: try markdown header (To or For), then YAML (to or for)
|
|
121
|
+
RECIPIENTS_RAW="$(md_field 'To')"
|
|
122
|
+
[ -z "$RECIPIENTS_RAW" ] && RECIPIENTS_RAW="$(md_field 'For')"
|
|
123
|
+
[ -z "$RECIPIENTS_RAW" ] && RECIPIENTS_RAW="$(yaml_extract 'to')"
|
|
124
|
+
[ -z "$RECIPIENTS_RAW" ] && RECIPIENTS_RAW="$(yaml_extract 'for')"
|
|
125
|
+
# Also extract from "From: author → recipient" pattern
|
|
126
|
+
if [ -z "$RECIPIENTS_RAW" ] && echo "$AUTHOR" | grep -q '→' 2>/dev/null; then
|
|
127
|
+
RECIPIENTS_RAW="$(echo "$AUTHOR" | sed 's/.*→[[:space:]]*//')"
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
# Project: try markdown header, then YAML
|
|
131
|
+
PROJECT="$(md_field 'Project')"
|
|
132
|
+
[ -z "$PROJECT" ] && PROJECT="$(yaml_extract 'project')"
|
|
133
|
+
|
|
134
|
+
# Handoff kind: used by readers to distinguish automatic captures from manual handoffs.
|
|
135
|
+
KIND="$(md_field 'Kind')"
|
|
136
|
+
[ -z "$KIND" ] && KIND="$(yaml_extract 'kind')"
|
|
137
|
+
|
|
138
|
+
# Summary: 5-level precedence chain
|
|
139
|
+
# 1. Frontmatter summary field
|
|
140
|
+
SUMMARY="$(yaml_extract 'summary')"
|
|
141
|
+
# 2. First non-empty paragraph after ## Briefing
|
|
142
|
+
[ -z "$SUMMARY" ] && SUMMARY="$(echo "$CONTENT" | awk '/^## Briefing/{found=1; next} found && /^#/{exit} found && /^[^#]/ && !/^[[:space:]]*$/{print; exit}' || true)"
|
|
143
|
+
# 3. First non-empty paragraph after ## Session Summary (legacy)
|
|
144
|
+
[ -z "$SUMMARY" ] && SUMMARY="$(echo "$CONTENT" | awk '/^## Session Summary/{found=1; next} found && /^#/{exit} found && /^[^#]/ && !/^[[:space:]]*$/{print; exit}' || true)"
|
|
145
|
+
# 4. First non-heading, non-metadata, non-empty line in body
|
|
146
|
+
[ -z "$SUMMARY" ] && SUMMARY="$(echo "$CONTENT" | awk '!/^[[:space:]]*$/ && !/^#/ && !/^\*\*/ && !/^---/ && !/^\|/ && !/^- /{print; exit}' || true)"
|
|
147
|
+
# 5. Topic fallback
|
|
148
|
+
[ -z "$SUMMARY" ] && SUMMARY="$TOPIC"
|
|
149
|
+
|
|
150
|
+
# Repo State: parse markdown table after "## Repo State"
|
|
151
|
+
# Extracts repo, branch, PR number, and base branch from each table row
|
|
152
|
+
REPO_STATE_JSON="[]"
|
|
153
|
+
REPO_TABLE=$(echo "$CONTENT" | awk '/^## Repo State/{found=1; next} found && /^#/{exit} found && /^\|[^-]/ && !/^\| Repo/{print}' || true)
|
|
154
|
+
if [ -n "$REPO_TABLE" ]; then
|
|
155
|
+
REPO_STATE_JSON=$(echo "$REPO_TABLE" | while IFS='|' read -r _ R_REPO R_BRANCH R_PR R_BASE _; do
|
|
156
|
+
R_REPO=$(echo "$R_REPO" | xargs 2>/dev/null || true)
|
|
157
|
+
R_BRANCH=$(echo "$R_BRANCH" | xargs 2>/dev/null || true)
|
|
158
|
+
R_PR=$(echo "$R_PR" | xargs 2>/dev/null | sed 's/^#//' | sed 's/—//' || true)
|
|
159
|
+
R_BASE=$(echo "$R_BASE" | xargs 2>/dev/null || true)
|
|
160
|
+
[ -z "$R_REPO" ] && continue
|
|
161
|
+
if [ -n "$R_PR" ] && [ "$R_PR" -eq "$R_PR" ] 2>/dev/null; then
|
|
162
|
+
jq -n --arg repo "$R_REPO" --arg branch "$R_BRANCH" --argjson pr "$R_PR" --arg base "$R_BASE" \
|
|
163
|
+
'{repo:$repo, branch:$branch, pr:$pr, base:$base}'
|
|
164
|
+
else
|
|
165
|
+
jq -n --arg repo "$R_REPO" --arg branch "$R_BRANCH" --arg base "$R_BASE" \
|
|
166
|
+
'{repo:$repo, branch:$branch, pr:null, base:$base}'
|
|
167
|
+
fi
|
|
168
|
+
done | jq -s '.' 2>/dev/null || echo "[]")
|
|
169
|
+
fi
|
|
170
|
+
|
|
171
|
+
# --- Build Cypher queries ---
|
|
172
|
+
|
|
173
|
+
# Q1: MERGE Session + relationships
|
|
174
|
+
HANDED_TO_CYPHER=""
|
|
175
|
+
_RECIP_ARGS=()
|
|
176
|
+
if [ -n "$RECIPIENTS_RAW" ]; then
|
|
177
|
+
IFS=',' read -ra RECIPS <<< "$RECIPIENTS_RAW"
|
|
178
|
+
for i in "${!RECIPS[@]}"; do
|
|
179
|
+
R="$(echo "${RECIPS[$i]}" | xargs | tr '[:upper:]' '[:lower:]')"
|
|
180
|
+
[ -z "$R" ] && continue
|
|
181
|
+
HANDED_TO_CYPHER="${HANDED_TO_CYPHER}
|
|
182
|
+
WITH s
|
|
183
|
+
OPTIONAL MATCH (r${i}:Person) WHERE toLower(r${i}.name) = \$recipient${i} OR r${i}.github = \$recipient${i} OR toLower(r${i}.fullName) = \$recipient${i}
|
|
184
|
+
FOREACH (_ IN CASE WHEN r${i} IS NOT NULL THEN [1] ELSE [] END |
|
|
185
|
+
MERGE (s)-[:HANDED_TO]->(r${i})
|
|
186
|
+
)"
|
|
187
|
+
_RECIP_ARGS+=("--arg" "recipient${i}" "$R")
|
|
188
|
+
done
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
PROJECT_CYPHER=""
|
|
192
|
+
_PROJECT_ARGS=()
|
|
193
|
+
if [ -n "$PROJECT" ]; then
|
|
194
|
+
PROJECT_CYPHER="
|
|
195
|
+
WITH s
|
|
196
|
+
OPTIONAL MATCH (proj:Project) WHERE toLower(proj.name) = toLower(\$project)
|
|
197
|
+
FOREACH (_ IN CASE WHEN proj IS NOT NULL THEN [1] ELSE [] END |
|
|
198
|
+
MERGE (s)-[:ABOUT]->(proj)
|
|
199
|
+
)"
|
|
200
|
+
_PROJECT_ARGS=("--arg" "project" "$PROJECT")
|
|
201
|
+
fi
|
|
202
|
+
|
|
203
|
+
Q1_CYPHER="MATCH (p:Person) WHERE toLower(p.name) = \$author OR p.github = \$author OR toLower(p.fullName) = \$author OR \$author IN [x IN coalesce(p.previousNames, []) | toLower(x)]
|
|
204
|
+
MERGE (s:Session {id: \$sessionId})
|
|
205
|
+
ON CREATE SET s.date = date(\$date), s.topic = \$topic, s.summary = \$summary, s.filePath = \$filePath, s.author = \$author, s.handoffKind = CASE WHEN \$kind = '' THEN null ELSE \$kind END, s.handoffStatus = 'pending', s.repoState = \$repoState, s.startedAt = datetime(\$date + 'T00:00:00Z')
|
|
206
|
+
ON MATCH SET s.topic = \$topic, s.summary = \$summary, s.filePath = \$filePath, s.author = \$author, s.handoffKind = CASE WHEN \$kind = '' THEN null ELSE \$kind END, s.repoState = \$repoState
|
|
207
|
+
WITH s, p
|
|
208
|
+
MERGE (s)-[:BY]->(p)${PROJECT_CYPHER}${HANDED_TO_CYPHER}
|
|
209
|
+
RETURN s.id AS sessionId"
|
|
210
|
+
|
|
211
|
+
Q1_PARAMS=$(jq -n \
|
|
212
|
+
--arg sessionId "$SESSION_ID" \
|
|
213
|
+
--arg author "$AUTHOR_HANDLE" \
|
|
214
|
+
--arg date "$DATE" \
|
|
215
|
+
--arg topic "$TOPIC" \
|
|
216
|
+
--arg summary "$SUMMARY" \
|
|
217
|
+
--arg filePath "$REL_PATH" \
|
|
218
|
+
--arg kind "$KIND" \
|
|
219
|
+
--arg repoState "$REPO_STATE_JSON" \
|
|
220
|
+
"${_PROJECT_ARGS[@]:+${_PROJECT_ARGS[@]}}" \
|
|
221
|
+
"${_RECIP_ARGS[@]:+${_RECIP_ARGS[@]}}" \
|
|
222
|
+
'$ARGS.named')
|
|
223
|
+
|
|
224
|
+
# Q2: Auto-resolve old read handoffs from this author
|
|
225
|
+
Q2_CYPHER="MATCH (s:Session)-[:HANDED_TO]->(p:Person) WHERE (toLower(p.name) = \$author OR p.github = \$author OR toLower(p.fullName) = \$author) AND s.handoffStatus = 'read' AND s.id <> \$sessionId
|
|
226
|
+
WITH s, p, coalesce(s.handoffReadDate, s.date) AS sinceDate
|
|
227
|
+
MATCH (later:Session)-[:BY]->(p)
|
|
228
|
+
WHERE later.date > sinceDate
|
|
229
|
+
WITH s, count(later) AS laterSessions WHERE laterSessions > 0
|
|
230
|
+
SET s.handoffStatus = 'done'
|
|
231
|
+
RETURN count(s) AS resolved"
|
|
232
|
+
|
|
233
|
+
Q2_PARAMS=$(jq -n --arg author "$AUTHOR_HANDLE" --arg sessionId "$SESSION_ID" \
|
|
234
|
+
'{author: $author, sessionId: $sessionId}')
|
|
235
|
+
|
|
236
|
+
# Q3: Capture the handoff's knowledge-graph neighborhood for artifact rendering.
|
|
237
|
+
# Only the "graph-only" relations — things not already visible in the markdown
|
|
238
|
+
# header. Author/recipients/project live in the metadata row above the graph,
|
|
239
|
+
# so including them would be noise.
|
|
240
|
+
Q3_CYPHER="MATCH (s:Session {id: \$sessionId})
|
|
241
|
+
OPTIONAL MATCH (s)-[:IMPLEMENTS]->(prior:Session)
|
|
242
|
+
OPTIONAL MATCH (prior)-[:BY]->(priorAuthor:Person)
|
|
243
|
+
OPTIONAL MATCH (later:Session)-[:IMPLEMENTS]->(s)
|
|
244
|
+
OPTIONAL MATCH (later)-[:BY]->(laterAuthor:Person)
|
|
245
|
+
OPTIONAL MATCH (s)-[:CONTINUES]->(cont:Session)
|
|
246
|
+
OPTIONAL MATCH (cont)-[:BY]->(contAuthor:Person)
|
|
247
|
+
OPTIONAL MATCH (s)-[:ADVANCED|INVOLVES]->(quest:Quest)
|
|
248
|
+
OPTIONAL MATCH (s)-[:HAS_ACTIVITY]->(art:Artifact)
|
|
249
|
+
OPTIONAL MATCH (s)-[:PRODUCED]->(pr:PR)
|
|
250
|
+
RETURN
|
|
251
|
+
collect(DISTINCT {id: prior.id, topic: prior.topic, author: priorAuthor.name}) AS implementsHandoff,
|
|
252
|
+
collect(DISTINCT {id: later.id, topic: later.topic, author: laterAuthor.name}) AS implementedBy,
|
|
253
|
+
collect(DISTINCT {id: cont.id, topic: cont.topic, author: contAuthor.name}) AS continues,
|
|
254
|
+
collect(DISTINCT {id: quest.id, title: quest.title}) AS quests,
|
|
255
|
+
collect(DISTINCT {id: art.id, title: art.title, type: art.type, path: art.path}) AS artifacts,
|
|
256
|
+
collect(DISTINCT {id: pr.id, number: pr.number, title: pr.title, repo: pr.repo}) AS prs"
|
|
257
|
+
|
|
258
|
+
Q3_PARAMS=$(jq -n --arg sessionId "$SESSION_ID" '{sessionId: $sessionId}')
|
|
259
|
+
|
|
260
|
+
# --- Build batch request ---
|
|
261
|
+
BATCH_JSON=$(jq -n \
|
|
262
|
+
--arg q1 "$Q1_CYPHER" \
|
|
263
|
+
--argjson p1 "$Q1_PARAMS" \
|
|
264
|
+
--arg q2 "$Q2_CYPHER" \
|
|
265
|
+
--argjson p2 "$Q2_PARAMS" \
|
|
266
|
+
--arg q3 "$Q3_CYPHER" \
|
|
267
|
+
--argjson p3 "$Q3_PARAMS" \
|
|
268
|
+
'[{statement: $q1, parameters: $p1}, {statement: $q2, parameters: $p2}, {statement: $q3, parameters: $p3}]')
|
|
269
|
+
|
|
270
|
+
# --- Execute ---
|
|
271
|
+
RESPONSE=$(bash "$SCRIPT_DIR/bin/graph-batch.sh" "$BATCH_JSON" 2>/dev/null) || {
|
|
272
|
+
echo '{"error":"graph-batch.sh failed"}'
|
|
273
|
+
exit 1
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
# --- Mark auto-captured personal Session as handed_off ---
|
|
277
|
+
PROJ_HASH=$(echo -n "$SCRIPT_DIR" | md5 2>/dev/null || echo -n "$SCRIPT_DIR" | md5sum 2>/dev/null | cut -d' ' -f1)
|
|
278
|
+
CURRENT_SID=$(cat "$HOME/.egregore/session-${PROJ_HASH}.id" 2>/dev/null || echo "")
|
|
279
|
+
if [ -n "$CURRENT_SID" ]; then
|
|
280
|
+
bash "$SCRIPT_DIR/bin/graph.sh" query \
|
|
281
|
+
"MATCH (s:Session {id: \$sid}) SET s.status = 'handed_off' RETURN s.id" \
|
|
282
|
+
"$(jq -n --arg sid "$CURRENT_SID" '{sid: $sid}')" >/dev/null 2>&1 || true
|
|
283
|
+
fi
|
|
284
|
+
|
|
285
|
+
# --- Parse response ---
|
|
286
|
+
RESOLVED=$(echo "$RESPONSE" | jq -r '.results[1].values[0][0] // 0' 2>/dev/null || echo "0")
|
|
287
|
+
|
|
288
|
+
# Parse Q3 neighborhood into a subgraph object. Collect() entries that didn't
|
|
289
|
+
# match anything come back as {name: null, ...} — filter those out.
|
|
290
|
+
EMPTY_SG='{"implementsHandoff":[],"implementedBy":[],"continues":[],"quests":[],"artifacts":[],"prs":[]}'
|
|
291
|
+
SUBGRAPH_JSON=$(echo "$RESPONSE" | jq -c '
|
|
292
|
+
(.results[2] // null) as $r
|
|
293
|
+
| if $r == null or (($r.values // []) | length) == 0 then
|
|
294
|
+
{implementsHandoff:[], implementedBy:[], continues:[], quests:[], artifacts:[], prs:[]}
|
|
295
|
+
else
|
|
296
|
+
($r.fields // []) as $f
|
|
297
|
+
| ($r.values[0] // []) as $v
|
|
298
|
+
| reduce range(0; ($f | length)) as $i ({}; .[$f[$i]] = $v[$i])
|
|
299
|
+
| {
|
|
300
|
+
implementsHandoff: ((.implementsHandoff // []) | map(select(.id != null))),
|
|
301
|
+
implementedBy: ((.implementedBy // []) | map(select(.id != null))),
|
|
302
|
+
continues: ((.continues // []) | map(select(.id != null))),
|
|
303
|
+
quests: ((.quests // []) | map(select(.id != null))),
|
|
304
|
+
artifacts: ((.artifacts // []) | map(select(.id != null or .path != null))),
|
|
305
|
+
prs: ((.prs // []) | map(select(.number != null or .id != null)))
|
|
306
|
+
}
|
|
307
|
+
end
|
|
308
|
+
' 2>/dev/null || echo "$EMPTY_SG")
|
|
309
|
+
[ -z "$SUBGRAPH_JSON" ] && SUBGRAPH_JSON="$EMPTY_SG"
|
|
310
|
+
|
|
311
|
+
jq -n --arg sessionId "$SESSION_ID" --argjson resolved "$RESOLVED" --argjson subgraph "$SUBGRAPH_JSON" \
|
|
312
|
+
'{sessionId: $sessionId, resolved: $resolved, subgraph: $subgraph}'
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# artifact-id.sh — Deterministic artifact IDs for memory files.
|
|
3
|
+
#
|
|
4
|
+
# Emits `{prefix}-{12 hex}` where prefix is derived from the file extension
|
|
5
|
+
# (m = .md, h = .html) and the hex is the first 12 chars of sha256(canonical_path).
|
|
6
|
+
#
|
|
7
|
+
# Kept in parity with packages/egregore-artifacts/lib/artifact-id.js — both MUST
|
|
8
|
+
# produce identical output for the same input. Any change here requires an
|
|
9
|
+
# equivalent change there (and a passing parity test in test/artifact-id.test.js).
|
|
10
|
+
|
|
11
|
+
_artifact_id_sha256() {
|
|
12
|
+
local input="$1"
|
|
13
|
+
printf '%s' "$input" | shasum -a 256 2>/dev/null | cut -d' ' -f1 \
|
|
14
|
+
|| printf '%s' "$input" | sha256sum 2>/dev/null | cut -d' ' -f1
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
# artifact_id_from_path <path>
|
|
18
|
+
# Returns the deterministic artifact id for a repo-relative memory path, or
|
|
19
|
+
# empty (exit 1) if the extension is unsupported.
|
|
20
|
+
artifact_id_from_path() {
|
|
21
|
+
local raw="$1"
|
|
22
|
+
[ -n "$raw" ] || return 1
|
|
23
|
+
|
|
24
|
+
local canonical
|
|
25
|
+
canonical="${raw#./}"
|
|
26
|
+
canonical="$(printf '%s' "$canonical" | sed 's|/\{2,\}|/|g')"
|
|
27
|
+
|
|
28
|
+
# Extension match is case-insensitive to keep parity with the JS renderer
|
|
29
|
+
# (`extension()` in artifact-id.js lowercases before lookup). The canonical
|
|
30
|
+
# path itself is hashed verbatim — only the extension lookup is case-folded.
|
|
31
|
+
local ext_lower
|
|
32
|
+
ext_lower="$(printf '%s' "${canonical##*.}" | tr '[:upper:]' '[:lower:]')"
|
|
33
|
+
|
|
34
|
+
local prefix=""
|
|
35
|
+
case "$ext_lower" in
|
|
36
|
+
md) prefix="m" ;;
|
|
37
|
+
html) prefix="h" ;;
|
|
38
|
+
*) return 1 ;;
|
|
39
|
+
esac
|
|
40
|
+
|
|
41
|
+
local hash
|
|
42
|
+
hash="$(_artifact_id_sha256 "$canonical")"
|
|
43
|
+
[ -n "$hash" ] || return 1
|
|
44
|
+
|
|
45
|
+
printf '%s-%s' "$prefix" "${hash:0:12}"
|
|
46
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Shared config helpers — sourced by session-start.sh and other scripts.
|
|
3
|
+
# Expects SCRIPT_DIR to be set by the caller.
|
|
4
|
+
|
|
5
|
+
# Load specific variables from .env (safe extraction, no code execution)
|
|
6
|
+
_load_env_var() {
|
|
7
|
+
local var_name="$1"
|
|
8
|
+
local env_file="${ENV_FILE:-$SCRIPT_DIR/.env}"
|
|
9
|
+
if [ -f "$env_file" ]; then
|
|
10
|
+
grep "^${var_name}=" "$env_file" 2>/dev/null | cut -d'=' -f2- || true
|
|
11
|
+
fi
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
# Read a value from egregore.json
|
|
15
|
+
_config_val() {
|
|
16
|
+
local key="$1"
|
|
17
|
+
local default="${2:-}"
|
|
18
|
+
local config="${CONFIG:-$SCRIPT_DIR/egregore.json}"
|
|
19
|
+
if [ -f "$config" ]; then
|
|
20
|
+
local val
|
|
21
|
+
val=$(jq -r ".${key} // empty" "$config" 2>/dev/null || true)
|
|
22
|
+
echo "${val:-$default}"
|
|
23
|
+
else
|
|
24
|
+
echo "$default"
|
|
25
|
+
fi
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# Get base branch for a repo. Reads base_branch from egregore.json repos[] config.
|
|
29
|
+
# Defaults to "develop" for backwards compatibility (existing egregores use develop).
|
|
30
|
+
# Only returns something other than "develop" when base_branch is explicitly set
|
|
31
|
+
# (e.g., for teams who added existing repos with a different default branch).
|
|
32
|
+
# Usage: _get_base_branch "repo-name" (omit for core repo)
|
|
33
|
+
_get_base_branch() {
|
|
34
|
+
local repo_name="${1:-}"
|
|
35
|
+
local config="${CONFIG:-$SCRIPT_DIR/egregore.json}"
|
|
36
|
+
|
|
37
|
+
# Core egregore repo — always develop
|
|
38
|
+
if [ -z "$repo_name" ]; then
|
|
39
|
+
echo "develop"
|
|
40
|
+
return
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# Managed repo — check egregore.json for explicit base_branch
|
|
44
|
+
if [ -f "$config" ]; then
|
|
45
|
+
local base
|
|
46
|
+
base=$(jq -r --arg name "$repo_name" \
|
|
47
|
+
'(.repos[]? // empty) | select((if type == "object" then .name else . end) == $name) | if type == "object" then .base_branch // empty else empty end' \
|
|
48
|
+
"$config" 2>/dev/null || true)
|
|
49
|
+
if [ -n "$base" ]; then
|
|
50
|
+
echo "$base"
|
|
51
|
+
return
|
|
52
|
+
fi
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
# No explicit config — default to develop (backwards compatible)
|
|
56
|
+
echo "develop"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# Detect local vs connected mode
|
|
60
|
+
_detect_mode() {
|
|
61
|
+
local config="${CONFIG:-$SCRIPT_DIR/egregore.json}"
|
|
62
|
+
local mode
|
|
63
|
+
mode=$(jq -r '.mode // empty' "$config" 2>/dev/null || true)
|
|
64
|
+
local api_url
|
|
65
|
+
api_url=$(jq -r '.api_url // empty' "$config" 2>/dev/null || true)
|
|
66
|
+
if [ "$mode" = "local" ] || [ -z "$api_url" ]; then
|
|
67
|
+
echo "local"
|
|
68
|
+
else
|
|
69
|
+
echo "connected"
|
|
70
|
+
fi
|
|
71
|
+
}
|