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.
Files changed (154) hide show
  1. package/assets/egregore-launcher.js +410 -26
  2. package/bin/cli.js +34 -20
  3. package/lib/local.js +84 -11
  4. package/lib/pi-runtime.js +186 -0
  5. package/lib/setup.js +56 -11
  6. package/lib/ui.js +1 -1
  7. package/package.json +8 -6
  8. package/runtime/codex/.codex/hooks/search-hint.js +65 -0
  9. package/runtime/codex/.codex/hooks.json +13 -0
  10. package/runtime/codex/.codex/skills/deep-reflect/SKILL.md +14 -10
  11. package/runtime/codex/.codex/skills/minion/SKILL.md +25 -0
  12. package/runtime/codex/.codex/skills/scroll/SKILL.md +168 -0
  13. package/runtime/codex/.codex/skills/scroll/agents/openai.yaml +4 -0
  14. package/runtime/codex/.codex/skills/scroll/assets/shell.html +542 -0
  15. package/runtime/codex/.codex/skills/search/SKILL.md +56 -0
  16. package/runtime/codex/.codex/skills/session-view/SKILL.md +25 -0
  17. package/runtime/codex/.codex/skills/thread/SKILL.md +25 -0
  18. package/runtime/codex/.codex/skills/view/SKILL.md +147 -32
  19. package/runtime/codex/.codex/skills/view/agents/openai.yaml +4 -0
  20. package/runtime/codex/.codex/skills/view/assets/compose-scaffold.html +221 -0
  21. package/runtime/codex/.codex/spec-manifest.json +4 -4
  22. package/runtime/codex/AGENTS.md +31 -5
  23. package/runtime/codex/bin/activity-data.sh +46 -4
  24. package/runtime/codex/bin/agent.sh +10 -2
  25. package/runtime/codex/bin/codex-install-skills.sh +1 -1
  26. package/runtime/codex/bin/codex-render-spec.mjs +24 -2
  27. package/runtime/codex/bin/codex-session-start.sh +6 -60
  28. package/runtime/codex/bin/codex-skill-render.mjs +20 -12
  29. package/runtime/codex/bin/codex-sync-skills.sh +4 -0
  30. package/runtime/codex/bin/graph-op.sh +56 -5
  31. package/runtime/codex/bin/graph.sh +36 -0
  32. package/runtime/codex/bin/handoff-run.sh +157 -7
  33. package/runtime/codex/bin/handoff-save-egregore.sh +6 -1
  34. package/runtime/codex/bin/index-handoff.sh +9 -3
  35. package/runtime/codex/bin/lib/context.sh +97 -18
  36. package/runtime/codex/bin/lib/dashboard-artifact.sh +1 -1
  37. package/runtime/codex/bin/lib/git-sync.sh +35 -9
  38. package/runtime/codex/bin/lib/greeting.sh +30 -104
  39. package/runtime/codex/bin/lib/metrics.sh +374 -0
  40. package/runtime/codex/bin/publish-artifact.sh +43 -1
  41. package/runtime/codex/bin/settings.sh +274 -0
  42. package/runtime/codex/manifest.json +18 -1
  43. package/runtime/pi/.codex/config.toml +15 -0
  44. package/runtime/pi/.codex/hooks/branch-guard.js +242 -0
  45. package/runtime/pi/.codex/hooks/search-hint.js +65 -0
  46. package/runtime/pi/.codex/hooks.json +30 -0
  47. package/runtime/pi/.codex/skills/activity/SKILL.md +70 -0
  48. package/runtime/pi/.codex/skills/add/SKILL.md +25 -0
  49. package/runtime/pi/.codex/skills/announce/SKILL.md +54 -0
  50. package/runtime/pi/.codex/skills/answer/SKILL.md +25 -0
  51. package/runtime/pi/.codex/skills/archive/SKILL.md +41 -0
  52. package/runtime/pi/.codex/skills/ask/SKILL.md +59 -0
  53. package/runtime/pi/.codex/skills/branch/SKILL.md +25 -0
  54. package/runtime/pi/.codex/skills/checkup/SKILL.md +41 -0
  55. package/runtime/pi/.codex/skills/commit/SKILL.md +25 -0
  56. package/runtime/pi/.codex/skills/contribute/SKILL.md +25 -0
  57. package/runtime/pi/.codex/skills/dark-mode/SKILL.md +25 -0
  58. package/runtime/pi/.codex/skills/dashboard/SKILL.md +59 -0
  59. package/runtime/pi/.codex/skills/deep-reflect/SKILL.md +74 -0
  60. package/runtime/pi/.codex/skills/delete-user/SKILL.md +25 -0
  61. package/runtime/pi/.codex/skills/egregore-voice/SKILL.md +25 -0
  62. package/runtime/pi/.codex/skills/emissary/SKILL.md +41 -0
  63. package/runtime/pi/.codex/skills/env/SKILL.md +25 -0
  64. package/runtime/pi/.codex/skills/handoff/SKILL.md +67 -0
  65. package/runtime/pi/.codex/skills/harvest/SKILL.md +107 -0
  66. package/runtime/pi/.codex/skills/ingest/SKILL.md +25 -0
  67. package/runtime/pi/.codex/skills/ingest-google/SKILL.md +25 -0
  68. package/runtime/pi/.codex/skills/ingest-user-interview/SKILL.md +41 -0
  69. package/runtime/pi/.codex/skills/invite/SKILL.md +40 -0
  70. package/runtime/pi/.codex/skills/issue/SKILL.md +41 -0
  71. package/runtime/pi/.codex/skills/me/SKILL.md +25 -0
  72. package/runtime/pi/.codex/skills/meeting/SKILL.md +41 -0
  73. package/runtime/pi/.codex/skills/minion/SKILL.md +25 -0
  74. package/runtime/pi/.codex/skills/note/SKILL.md +25 -0
  75. package/runtime/pi/.codex/skills/onboarding/SKILL.md +41 -0
  76. package/runtime/pi/.codex/skills/pr/SKILL.md +25 -0
  77. package/runtime/pi/.codex/skills/project/SKILL.md +41 -0
  78. package/runtime/pi/.codex/skills/pull/SKILL.md +25 -0
  79. package/runtime/pi/.codex/skills/push/SKILL.md +25 -0
  80. package/runtime/pi/.codex/skills/quest/SKILL.md +77 -0
  81. package/runtime/pi/.codex/skills/quest-suggest/SKILL.md +25 -0
  82. package/runtime/pi/.codex/skills/reflect/SKILL.md +41 -0
  83. package/runtime/pi/.codex/skills/review-pr/SKILL.md +41 -0
  84. package/runtime/pi/.codex/skills/save/SKILL.md +62 -0
  85. package/runtime/pi/.codex/skills/scroll/SKILL.md +168 -0
  86. package/runtime/pi/.codex/skills/scroll/agents/openai.yaml +4 -0
  87. package/runtime/pi/.codex/skills/scroll/assets/shell.html +542 -0
  88. package/runtime/pi/.codex/skills/search/SKILL.md +56 -0
  89. package/runtime/pi/.codex/skills/session-view/SKILL.md +25 -0
  90. package/runtime/pi/.codex/skills/setup/SKILL.md +25 -0
  91. package/runtime/pi/.codex/skills/summon/SKILL.md +41 -0
  92. package/runtime/pi/.codex/skills/sync-repos/SKILL.md +25 -0
  93. package/runtime/pi/.codex/skills/telegram-connect/SKILL.md +25 -0
  94. package/runtime/pi/.codex/skills/telemetry/SKILL.md +25 -0
  95. package/runtime/pi/.codex/skills/test/SKILL.md +41 -0
  96. package/runtime/pi/.codex/skills/the-spiral/SKILL.md +84 -0
  97. package/runtime/pi/.codex/skills/thread/SKILL.md +25 -0
  98. package/runtime/pi/.codex/skills/todo/SKILL.md +41 -0
  99. package/runtime/pi/.codex/skills/tui-design/SKILL.md +41 -0
  100. package/runtime/pi/.codex/skills/tutorial/SKILL.md +41 -0
  101. package/runtime/pi/.codex/skills/update/SKILL.md +25 -0
  102. package/runtime/pi/.codex/skills/view/SKILL.md +156 -0
  103. package/runtime/pi/.codex/skills/view/agents/openai.yaml +4 -0
  104. package/runtime/pi/.codex/skills/view/assets/compose-scaffold.html +221 -0
  105. package/runtime/pi/.codex/skills/visual-explain/SKILL.md +41 -0
  106. package/runtime/pi/.codex/skills/wrap/SKILL.md +55 -0
  107. package/runtime/pi/.codex/spec-manifest.json +114 -0
  108. package/runtime/pi/.pi/APPEND_SYSTEM.md +255 -0
  109. package/runtime/pi/.pi/extensions/egregore.ts +324 -0
  110. package/runtime/pi/.pi/settings.json +6 -0
  111. package/runtime/pi/.pi/spec-manifest.json +12 -0
  112. package/runtime/pi/AGENTS.md +350 -0
  113. package/runtime/pi/bin/activity-data.sh +242 -0
  114. package/runtime/pi/bin/agent.sh +677 -0
  115. package/runtime/pi/bin/codex-install-skills.sh +78 -0
  116. package/runtime/pi/bin/codex-render-spec.mjs +394 -0
  117. package/runtime/pi/bin/codex-session-start.sh +188 -0
  118. package/runtime/pi/bin/codex-skill-render.mjs +323 -0
  119. package/runtime/pi/bin/codex-sync-skills.sh +227 -0
  120. package/runtime/pi/bin/dashboard-data.sh +178 -0
  121. package/runtime/pi/bin/graph-batch.sh +60 -0
  122. package/runtime/pi/bin/graph-op.sh +431 -0
  123. package/runtime/pi/bin/graph-wal.sh +228 -0
  124. package/runtime/pi/bin/graph.sh +177 -0
  125. package/runtime/pi/bin/handoff-pr-backfill.sh +86 -0
  126. package/runtime/pi/bin/handoff-run.sh +581 -0
  127. package/runtime/pi/bin/handoff-save-egregore.sh +136 -0
  128. package/runtime/pi/bin/index-handoff.sh +312 -0
  129. package/runtime/pi/bin/lib/artifact-id.sh +46 -0
  130. package/runtime/pi/bin/lib/config.sh +71 -0
  131. package/runtime/pi/bin/lib/context.sh +542 -0
  132. package/runtime/pi/bin/lib/dashboard-artifact.sh +142 -0
  133. package/runtime/pi/bin/lib/ensure-develop.sh +183 -0
  134. package/runtime/pi/bin/lib/git-sync.sh +288 -0
  135. package/runtime/pi/bin/lib/greeting.sh +340 -0
  136. package/runtime/pi/bin/lib/hash.sh +13 -0
  137. package/runtime/pi/bin/lib/identity.sh +174 -0
  138. package/runtime/pi/bin/lib/metrics.sh +374 -0
  139. package/runtime/pi/bin/lib/permissions.sh +106 -0
  140. package/runtime/pi/bin/lib/time.sh +56 -0
  141. package/runtime/pi/bin/lib/worktree-links.sh +54 -0
  142. package/runtime/pi/bin/notify.sh +207 -0
  143. package/runtime/pi/bin/pi-branch-policy.mjs +15 -0
  144. package/runtime/pi/bin/pi-product-workflows.mjs +324 -0
  145. package/runtime/pi/bin/pi-render-spec.mjs +114 -0
  146. package/runtime/pi/bin/pi-session-start.sh +24 -0
  147. package/runtime/pi/bin/publish-artifact.sh +201 -0
  148. package/runtime/pi/bin/publish-references.sh +144 -0
  149. package/runtime/pi/bin/repo-state.sh +99 -0
  150. package/runtime/pi/bin/telemetry.sh +391 -0
  151. package/runtime/pi/bin/worktree-create.sh +118 -0
  152. package/runtime/pi/bin/worktree-remove.sh +50 -0
  153. package/runtime/pi/bin/worktree.sh +198 -0
  154. package/runtime/pi/manifest.json +176 -0
@@ -0,0 +1,142 @@
1
+ # shellcheck shell=bash
2
+ # dashboard-artifact.sh — Generate a session dashboard artifact with a shareable URL
3
+ #
4
+ # Runs after context.sh (all $CTX_DIR/* files populated), before greeting.sh.
5
+ # Compiles gathered context into a markdown document, generates HTML via
6
+ # egregore-artifacts, publishes it, and exports DASHBOARD_URL for greeting.sh.
7
+ #
8
+ # Inputs: SCRIPT_DIR, CTX_DIR, AUTHOR, GREETING_NAME, LOCAL_MODE,
9
+ # EGREGORE_SESSION_ID, FIRST_SESSION
10
+ # Outputs: DASHBOARD_URL (exported for greeting.sh)
11
+
12
+ DASHBOARD_URL=""
13
+
14
+ # Skip conditions: first session, onboarding incomplete, no session ID
15
+ if [ "${FIRST_SESSION:-false}" = "true" ]; then
16
+ return 0 2>/dev/null || exit 0
17
+ fi
18
+ if [ -z "${EGREGORE_SESSION_ID:-}" ]; then
19
+ return 0 2>/dev/null || exit 0
20
+ fi
21
+
22
+ # --- Read gathered context ---
23
+ _DB_ADDRESSED=$(cat "$CTX_DIR/addressed_rich" 2>/dev/null || echo "[]")
24
+ _DB_ADDR_COUNT=$(echo "$_DB_ADDRESSED" | jq 'length' 2>/dev/null || echo "0")
25
+ _DB_QUESTS=$(cat "$CTX_DIR/quests" 2>/dev/null || echo "[]")
26
+ _DB_QUEST_COUNT=$(echo "$_DB_QUESTS" | jq 'length' 2>/dev/null || echo "0")
27
+ _DB_TEAM=$(cat "$CTX_DIR/team" 2>/dev/null || echo "[]")
28
+ _DB_TEAM_COUNT=$(echo "$_DB_TEAM" | jq 'length' 2>/dev/null || echo "0")
29
+ _DB_LIFECYCLE=$(cat "$CTX_DIR/lifecycle" 2>/dev/null || echo '{}')
30
+ _DB_MERGED_COUNT=$(echo "$_DB_LIFECYCLE" | jq '.merged_prs.values // [] | length' 2>/dev/null || echo "0")
31
+ _DB_IMPL_COUNT=$(echo "$_DB_LIFECYCLE" | jq '.implemented_handoffs.values // [] | length' 2>/dev/null || echo "0")
32
+ _DB_PULSE=$(cat "$CTX_DIR/pulse_brief" 2>/dev/null || echo '{}')
33
+ _DB_PULSE_BRIEF=$(echo "$_DB_PULSE" | jq -r '.brief // empty' 2>/dev/null || true)
34
+
35
+ # Skip if all context is empty — nothing worth showing
36
+ _DB_HAS_CONTENT="false"
37
+ if [ "$_DB_ADDR_COUNT" -gt 0 ] 2>/dev/null; then _DB_HAS_CONTENT="true"; fi
38
+ if [ "$_DB_QUEST_COUNT" -gt 0 ] 2>/dev/null; then _DB_HAS_CONTENT="true"; fi
39
+ if [ "$_DB_TEAM_COUNT" -gt 0 ] 2>/dev/null; then _DB_HAS_CONTENT="true"; fi
40
+ if [ "$_DB_MERGED_COUNT" -gt 0 ] 2>/dev/null; then _DB_HAS_CONTENT="true"; fi
41
+ if [ "$_DB_IMPL_COUNT" -gt 0 ] 2>/dev/null; then _DB_HAS_CONTENT="true"; fi
42
+
43
+ if [ "$_DB_HAS_CONTENT" = "false" ]; then
44
+ return 0 2>/dev/null || exit 0
45
+ fi
46
+
47
+ # --- Compile markdown ---
48
+ _DB_DATE=$(date +%Y-%m-%d)
49
+ _DB_ORG=$(jq -r '.org_name // "Egregore"' "$SCRIPT_DIR/egregore.json" 2>/dev/null || echo "Egregore")
50
+ _DB_MD="/tmp/egregore-dashboard-${EGREGORE_SESSION_ID}.md"
51
+ mkdir -p /tmp
52
+
53
+ {
54
+ printf '# Session Dashboard — %s\n\n' "$_DB_ORG"
55
+
56
+ # --- Pulse Brief (first — sets the tone) ---
57
+ if [ -n "$_DB_PULSE_BRIEF" ]; then
58
+ printf '## Pulse\n\n'
59
+ printf '✦ %s\n\n' "$_DB_PULSE_BRIEF"
60
+ fi
61
+
62
+ # --- Handoffs & Asks (dense format matching terminal TUI) ---
63
+ if [ "$_DB_ADDR_COUNT" -gt 0 ] 2>/dev/null; then
64
+ printf '## Handoffs & Asks\n\n'
65
+ _DB_NOW_SEC=$(date +%s)
66
+ echo "$_DB_ADDRESSED" | jq -r '.[] | "\(.author // "unknown")\t\(.topic // "Untitled")\t\(.date // "--")"' 2>/dev/null | while IFS=$'\t' read -r _HF_AUTHOR _HF_TOPIC _HF_DATE; do
67
+ [ -z "$_HF_AUTHOR" ] && continue
68
+ # Compute relative time
69
+ _HF_DATE_ONLY="${_HF_DATE%%T*}"
70
+ _HF_EPOCH=$(date -j -f "%Y-%m-%d" "$_HF_DATE_ONLY" +%s 2>/dev/null || \
71
+ date -d "$_HF_DATE_ONLY" +%s 2>/dev/null || echo "0")
72
+ _HF_DAYS=$(( (_DB_NOW_SEC - _HF_EPOCH) / 86400 ))
73
+ if [ "$_HF_DAYS" -le 0 ] 2>/dev/null; then _HF_AGO="today"
74
+ elif [ "$_HF_DAYS" -eq 1 ] 2>/dev/null; then _HF_AGO="yesterday"
75
+ else _HF_AGO="${_HF_DAYS}d ago"
76
+ fi
77
+ _HF_NAME_LC=$(printf '%s' "$_HF_AUTHOR" | awk '{print tolower($1)}')
78
+ printf '● %s → you: %s (%s)\n\n' "$_HF_NAME_LC" "$_HF_TOPIC" "$_HF_AGO"
79
+ done
80
+ fi
81
+
82
+ # --- Team Activity (right after handoffs, recent only) ---
83
+ if [ "$_DB_TEAM_COUNT" -gt 0 ] 2>/dev/null; then
84
+ _DB_NOW=$(date +%s)
85
+ _DB_RECENCY=$((7 * 86400))
86
+ _DB_TEAM_FILTERED=$(echo "$_DB_TEAM" | jq --argjson now "$_DB_NOW" --argjson window "$_DB_RECENCY" \
87
+ '[.[] | select(.last_seen_sort > 0 and ($now - .last_seen_sort) < $window)]' 2>/dev/null || echo "[]")
88
+ _DB_TEAM_FCOUNT=$(echo "$_DB_TEAM_FILTERED" | jq 'length' 2>/dev/null || echo "0")
89
+ if [ "$_DB_TEAM_FCOUNT" -gt 0 ] 2>/dev/null; then
90
+ printf '## Team Activity\n\n'
91
+ printf '| Name | Last Seen | Working On |\n'
92
+ printf '|------|-----------|------------|\n'
93
+ echo "$_DB_TEAM_FILTERED" | jq -r '.[] | ((.working_on // "")) as $w | (.branches // []) as $b | "| \(.name) | \(.last_seen) | \(if $w != "" then $w else ($b | join(", ")) end) |"' 2>/dev/null || true
94
+ printf '\n'
95
+ fi
96
+ fi
97
+
98
+ # --- What Changed ---
99
+ if [ "$_DB_MERGED_COUNT" -gt 0 ] 2>/dev/null || [ "$_DB_IMPL_COUNT" -gt 0 ] 2>/dev/null; then
100
+ printf '## What Changed\n\n'
101
+ if [ "$_DB_MERGED_COUNT" -gt 0 ] 2>/dev/null; then
102
+ echo "$_DB_LIFECYCLE" | jq -r '.merged_prs.values[]? | "- ✓ PR #\(.[0]) \(.[2] // .[1] // "")"' 2>/dev/null || true
103
+ fi
104
+ if [ "$_DB_IMPL_COUNT" -gt 0 ] 2>/dev/null; then
105
+ echo "$_DB_LIFECYCLE" | jq -r '.implemented_handoffs.values[]? | "- ✓ \(.[1] // "someone") worked on: \(.[0] // "handoff")"' 2>/dev/null || true
106
+ fi
107
+ printf '\n'
108
+ fi
109
+
110
+ # --- Active Quests (bottom — reference, exclude meta entries) ---
111
+ if [ "$_DB_QUEST_COUNT" -gt 0 ] 2>/dev/null; then
112
+ _DB_QUESTS_FILTERED=$(echo "$_DB_QUESTS" | jq '[.[] | select(. != "_template" and . != "index")][:10]' 2>/dev/null || echo "[]")
113
+ _DB_QF_COUNT=$(echo "$_DB_QUESTS_FILTERED" | jq 'length' 2>/dev/null || echo "0")
114
+ if [ "$_DB_QF_COUNT" -gt 0 ] 2>/dev/null; then
115
+ printf '## Active Quests\n\n'
116
+ echo "$_DB_QUESTS_FILTERED" | jq -r '.[] | "- \(.)"' 2>/dev/null || true
117
+ printf '\n'
118
+ fi
119
+ fi
120
+
121
+ } > "$_DB_MD" 2>/dev/null
122
+
123
+ # --- Publish markdown → HTML → hosted URL ---
124
+ # publish-artifact.sh handles HTML generation internally (npx egregore-artifacts)
125
+ # then uploads. Don't pre-generate HTML — that causes double-wrapping.
126
+ # https:// URLs are universally cmd+clickable across all terminal emulators.
127
+ # publish-artifact.sh may output npx progress lines before the URL — extract just the URL
128
+ _DB_PUBLISH_OUT=$(bash "$SCRIPT_DIR/bin/publish-artifact.sh" document "$_DB_MD" \
129
+ --title "Session Dashboard" \
130
+ --author "${GREETING_NAME:-$AUTHOR}" 2>/dev/null || true)
131
+ DASHBOARD_URL=$(echo "$_DB_PUBLISH_OUT" | grep -o 'https://[^ ]*' | tail -1)
132
+
133
+ # Fallback: generate HTML locally and use file:// URL (offline, no API key, etc.)
134
+ if [ -z "$DASHBOARD_URL" ]; then
135
+ _DB_HTML="/tmp/egregore-artifacts/dashboard-${EGREGORE_SESSION_ID}.html"
136
+ mkdir -p /tmp/egregore-artifacts
137
+ if npx egregore-artifacts@latest document "$_DB_MD" --output "$_DB_HTML" --no-open >/dev/null 2>&1 && [ -f "$_DB_HTML" ]; then
138
+ DASHBOARD_URL="file://${_DB_HTML}"
139
+ fi
140
+ fi
141
+
142
+ rm -f "$_DB_MD"
@@ -0,0 +1,183 @@
1
+ #!/bin/bash
2
+ # ensure-develop.sh — Ensure a repository has a develop branch.
3
+ #
4
+ # Usage:
5
+ # bash bin/lib/ensure-develop.sh <repo-path>
6
+ # bash bin/lib/ensure-develop.sh <repo-path> --report
7
+ #
8
+ # Behavior:
9
+ # 1. Detects the repo's default branch (main, master, or HEAD)
10
+ # 2. Checks if develop exists locally and/or on origin
11
+ # 3. If missing, creates develop from the default branch and pushes it
12
+ #
13
+ # Flags:
14
+ # --report Print a JSON report of the repo's branching model instead of
15
+ # creating develop. Used by create-egregore to analyze existing repos.
16
+ #
17
+ # Exit codes:
18
+ # 0 — develop exists (already existed or was just created)
19
+ # 1 — could not create develop (no default branch, not a git repo, push failed)
20
+ #
21
+ # Output (on success, without --report):
22
+ # created:<default_branch> — develop was created from <default_branch>
23
+ # exists — develop already existed
24
+ #
25
+ # Output (with --report):
26
+ # JSON object with branching analysis
27
+
28
+ set -o pipefail
29
+
30
+ REPO_PATH="${1:-.}"
31
+ FLAG="${2:-}"
32
+
33
+ if [ ! -d "$REPO_PATH/.git" ] && [ ! -f "$REPO_PATH/.git" ]; then
34
+ echo "error:not_a_git_repo" >&2
35
+ exit 1
36
+ fi
37
+
38
+ # ── Detect default branch ──────────────────────────────────────────
39
+ # Priority: what origin/HEAD points to > main > master > first branch found
40
+ detect_default_branch() {
41
+ local repo="$1"
42
+
43
+ # 1. Check origin/HEAD (set by GitHub on clone)
44
+ local origin_head
45
+ origin_head=$(git -C "$repo" symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||')
46
+ if [ -n "$origin_head" ] && git -C "$repo" show-ref --verify --quiet "refs/remotes/origin/$origin_head" 2>/dev/null; then
47
+ echo "$origin_head"
48
+ return 0
49
+ fi
50
+
51
+ # 2. Check common names on remote
52
+ for branch in main master; do
53
+ if git -C "$repo" show-ref --verify --quiet "refs/remotes/origin/$branch" 2>/dev/null; then
54
+ echo "$branch"
55
+ return 0
56
+ fi
57
+ done
58
+
59
+ # 3. Check local branches (repo might not have a remote yet)
60
+ for branch in main master; do
61
+ if git -C "$repo" show-ref --verify --quiet "refs/heads/$branch" 2>/dev/null; then
62
+ echo "$branch"
63
+ return 0
64
+ fi
65
+ done
66
+
67
+ # 4. Fall back to whatever HEAD points to
68
+ local head_branch
69
+ head_branch=$(git -C "$repo" branch --show-current 2>/dev/null)
70
+ if [ -n "$head_branch" ] && [ "$head_branch" != "develop" ]; then
71
+ echo "$head_branch"
72
+ return 0
73
+ fi
74
+
75
+ return 1
76
+ }
77
+
78
+ # ── Report mode ─────────────────────────────────────────────────────
79
+ if [ "$FLAG" = "--report" ]; then
80
+ default_branch=$(detect_default_branch "$REPO_PATH" 2>/dev/null || echo "")
81
+ has_develop_local="false"
82
+ has_develop_remote="false"
83
+ git -C "$REPO_PATH" show-ref --verify --quiet refs/heads/develop 2>/dev/null && has_develop_local="true"
84
+ git -C "$REPO_PATH" show-ref --verify --quiet refs/remotes/origin/develop 2>/dev/null && has_develop_remote="true"
85
+
86
+ # Count remote branches (excluding HEAD)
87
+ branch_count=$(git -C "$REPO_PATH" branch -r 2>/dev/null | grep -cv 'HEAD' 2>/dev/null || echo "0")
88
+ [ -z "$branch_count" ] && branch_count=0
89
+
90
+ # List remote branch names (excluding origin/HEAD, max 20)
91
+ _branch_list=$(git -C "$REPO_PATH" branch -r --format='%(refname:short)' 2>/dev/null \
92
+ | grep -v 'origin/HEAD' \
93
+ | sed 's|^origin/||' \
94
+ | head -20)
95
+ if [ -n "$_branch_list" ]; then
96
+ branches=$(echo "$_branch_list" | jq -R . | jq -s .)
97
+ else
98
+ branches="[]"
99
+ fi
100
+
101
+ # Detect active contributors from branch names (dev/author/... pattern)
102
+ _contrib_list=$(git -C "$REPO_PATH" branch -r --format='%(refname:short)' 2>/dev/null \
103
+ | grep -E '^origin/dev/' \
104
+ | sed 's|^origin/dev/||; s|/.*||' \
105
+ | sort -u)
106
+ if [ -n "$_contrib_list" ]; then
107
+ contributors=$(echo "$_contrib_list" | jq -R . | jq -s .)
108
+ else
109
+ contributors="[]"
110
+ fi
111
+
112
+ jq -n \
113
+ --arg default_branch "$default_branch" \
114
+ --argjson has_develop_local "$has_develop_local" \
115
+ --argjson has_develop_remote "$has_develop_remote" \
116
+ --argjson branch_count "$branch_count" \
117
+ --argjson branches "$branches" \
118
+ --argjson contributors "$contributors" \
119
+ '{
120
+ default_branch: $default_branch,
121
+ has_develop_local: $has_develop_local,
122
+ has_develop_remote: $has_develop_remote,
123
+ branch_count: $branch_count,
124
+ branches: $branches,
125
+ contributors: $contributors,
126
+ needs_develop: (($has_develop_remote | not) and ($has_develop_local | not))
127
+ }'
128
+ exit 0
129
+ fi
130
+
131
+ # ── Ensure develop exists ──────────────────────────────────────────
132
+
133
+ # Already exists on remote?
134
+ if git -C "$REPO_PATH" show-ref --verify --quiet refs/remotes/origin/develop 2>/dev/null; then
135
+ # Ensure local tracking branch exists too
136
+ if ! git -C "$REPO_PATH" show-ref --verify --quiet refs/heads/develop 2>/dev/null; then
137
+ git -C "$REPO_PATH" branch develop origin/develop --quiet 2>/dev/null || true
138
+ fi
139
+ echo "exists"
140
+ exit 0
141
+ fi
142
+
143
+ # Already exists locally but not on remote? Push it.
144
+ if git -C "$REPO_PATH" show-ref --verify --quiet refs/heads/develop 2>/dev/null; then
145
+ if git -C "$REPO_PATH" push -u origin develop --quiet 2>/dev/null; then
146
+ echo "exists"
147
+ exit 0
148
+ fi
149
+ # Push failed but local exists — still usable
150
+ echo "exists:local_only"
151
+ exit 0
152
+ fi
153
+
154
+ # Need to create develop from default branch
155
+ DEFAULT_BRANCH=$(detect_default_branch "$REPO_PATH")
156
+ if [ -z "$DEFAULT_BRANCH" ]; then
157
+ echo "error:no_default_branch" >&2
158
+ exit 1
159
+ fi
160
+
161
+ # Create develop from the default branch
162
+ # Prefer remote ref if available (more up-to-date after fetch)
163
+ if git -C "$REPO_PATH" show-ref --verify --quiet "refs/remotes/origin/$DEFAULT_BRANCH" 2>/dev/null; then
164
+ git -C "$REPO_PATH" branch develop "origin/$DEFAULT_BRANCH" --quiet 2>/dev/null
165
+ else
166
+ git -C "$REPO_PATH" branch develop "$DEFAULT_BRANCH" --quiet 2>/dev/null
167
+ fi
168
+
169
+ if [ $? -ne 0 ]; then
170
+ echo "error:branch_create_failed" >&2
171
+ exit 1
172
+ fi
173
+
174
+ # Push to remote
175
+ if git -C "$REPO_PATH" push -u origin develop --quiet 2>/dev/null; then
176
+ echo "created:$DEFAULT_BRANCH"
177
+ exit 0
178
+ else
179
+ # Push failed — develop exists locally but not on remote
180
+ # This is still usable; session-start.sh will retry the push
181
+ echo "created:$DEFAULT_BRANCH:local_only"
182
+ exit 0
183
+ fi
@@ -0,0 +1,288 @@
1
+ # shellcheck shell=bash
2
+ # git-sync.sh — Git synchronization for session-start.sh
3
+ #
4
+ # Handles all git operations during session startup:
5
+ # - Pull rebase configuration
6
+ # - Parallel fetches (origin, upstream, memory, managed repos)
7
+ # - Worktree orphan cleanup
8
+ # - Git health check
9
+ # - Develop branch setup + sync
10
+ # - Memory sync
11
+ # - Managed repos sync
12
+ #
13
+ # Inputs: SCRIPT_DIR, IS_WORKTREE, MAIN_PROJECT_DIR, STATE_FILE, ENV_FILE,
14
+ # HEALTH_GIT, MANAGED_REPOS (not yet set — read from config here)
15
+ # Outputs: HEALTH_GIT, DEVELOP_SYNCED, COMMITS_AHEAD, ACTION, SAVED_BRANCH,
16
+ # BRANCH, MEMORY_SYNCED, REPOS_STATUS, MANAGED_REPOS, CURRENT_BRANCH,
17
+ # FRAMEWORK_UPDATED
18
+
19
+ FRAMEWORK_UPDATED="false"
20
+
21
+ # --- Ensure pull.rebase is set (prevents "divergent branches" errors) ---
22
+ git config pull.rebase true 2>/dev/null || true
23
+
24
+ # --- Fetch all remotes in parallel ---
25
+ # Set git transfer timeout so hangs don't block startup indefinitely.
26
+ export GIT_HTTP_LOW_SPEED_LIMIT=1000 # abort if <1KB/s
27
+ export GIT_HTTP_LOW_SPEED_TIME=10 # for 10 seconds
28
+
29
+ # Attendant fast path: the ambient daemon (bin/attendant.sh) fetches origin,
30
+ # memory, and managed repos every few minutes. If its warm marker is fresh,
31
+ # refs are already up to date — skip the network fetches and launch on local
32
+ # ops only. Upstream fetch (framework update check) is not covered by the
33
+ # attendant and stays unconditional below.
34
+ _REFS_FRESH="false"
35
+ _ATT_SLUG=$(jq -r '.slug // "egregore"' "$SCRIPT_DIR/egregore.json" 2>/dev/null || echo "egregore")
36
+ _ATT_MARKER="$HOME/.egregore/attendant/${_ATT_SLUG}-$(echo -n "$SCRIPT_DIR" | cksum | cut -d' ' -f1).warm-ts"
37
+ if [ -f "$_ATT_MARKER" ]; then
38
+ _ATT_AGE=$(( $(date +%s) - $(cat "$_ATT_MARKER" 2>/dev/null || echo 0) ))
39
+ [ "$_ATT_AGE" -lt 360 ] && _REFS_FRESH="true"
40
+ fi
41
+
42
+ [ "$_REFS_FRESH" != "true" ] && git fetch origin --quiet 2>/dev/null &
43
+
44
+ # Fetch upstream framework (for update check — non-blocking)
45
+ # Default: official egregore-core repo. Set upstream_url in egregore.json to override,
46
+ # or set it to "none" to disable (e.g., in the dev repo where this IS the source).
47
+ _UPSTREAM_URL=$(jq -r '.upstream_url // empty' "$SCRIPT_DIR/egregore.json" 2>/dev/null)
48
+ [ -z "$_UPSTREAM_URL" ] && _UPSTREAM_URL="https://github.com/egregore-labs/egregore.git"
49
+ if [ "$_UPSTREAM_URL" != "none" ]; then
50
+ git remote add upstream "$_UPSTREAM_URL" 2>/dev/null || true
51
+ git fetch upstream main --quiet 2>/dev/null &
52
+ fi
53
+
54
+ # Sync memory in parallel (symlink or direct directory — both valid)
55
+ MEMORY_SYNCED="false"
56
+ if [ -d "$SCRIPT_DIR/memory/.git" ] && [ "$_REFS_FRESH" != "true" ]; then
57
+ git -C "$SCRIPT_DIR/memory" fetch origin --quiet 2>/dev/null &
58
+ fi
59
+
60
+ # Fetch managed repos in parallel
61
+ MANAGED_REPOS=$(jq -r '(.repos[]? // empty) | if type == "object" then .name else . end' "$SCRIPT_DIR/egregore.json" 2>/dev/null)
62
+ if [ "$_REFS_FRESH" != "true" ]; then
63
+ for REPO in $MANAGED_REPOS; do
64
+ if [ -d "$SCRIPT_DIR/../$REPO/.git" ]; then
65
+ git -C "$SCRIPT_DIR/../$REPO" fetch origin --quiet 2>/dev/null &
66
+ fi
67
+ done
68
+ fi
69
+
70
+ # Wait for all fetches
71
+ wait 2>/dev/null || true
72
+
73
+ # --- Auto-update framework from upstream (deferred) ---
74
+ # Moved AFTER setup_develop() so framework commits land on develop, not on
75
+ # working branches. See _apply_framework_update() below.
76
+ _AUTO_UPDATE=$(jq -r '.auto_update // true' "$SCRIPT_DIR/egregore.json" 2>/dev/null)
77
+
78
+ _apply_framework_update() {
79
+ # Applies framework paths from upstream to the CURRENT branch (must be develop).
80
+ # Disable with "auto_update": false in egregore.json.
81
+ # Dev repos (upstream_url: "none") skip this entirely.
82
+ if [ "$_UPSTREAM_URL" = "none" ] || [ "$_AUTO_UPDATE" = "false" ]; then
83
+ return 0
84
+ fi
85
+ if ! git show-ref --verify --quiet refs/remotes/upstream/main 2>/dev/null; then
86
+ return 0
87
+ fi
88
+ # Apply upstream changes — checkout is idempotent, skip diff check
89
+ # (git diff with variable path lists breaks in zsh — no word-splitting)
90
+ for _p in bin/ .claude/commands/ .claude/skills/ .claude/hooks/ .claude/context/ .claude/agents/ loom/ CLAUDE.md skills/; do
91
+ git checkout upstream/main -- "$_p" 2>/dev/null || true
92
+ done
93
+ # Only commit if there are actual changes
94
+ if [ -n "$(git status --porcelain bin/ .claude/ loom/ CLAUDE.md skills/ 2>/dev/null | head -1)" ]; then
95
+ git add bin/ .claude/ loom/ CLAUDE.md skills/ 2>/dev/null
96
+ EGREGORE_FRAMEWORK_UPDATE=1 git commit -m "Auto-update Egregore framework" --quiet 2>/dev/null || true
97
+ FRAMEWORK_UPDATED="true"
98
+ fi
99
+ }
100
+
101
+ # --- Worktree prune (just clean git's internal list, don't delete anything) ---
102
+ git worktree prune 2>/dev/null || true
103
+
104
+ # --- Git health check ---
105
+ if git show-ref --verify --quiet refs/remotes/origin/develop 2>/dev/null; then
106
+ HEALTH_GIT="ok"
107
+ else
108
+ HEALTH_GIT="fail"
109
+ fi
110
+
111
+ # --- Guarded staging -------------------------------------------------------
112
+ # git_add_guarded() lives in the shared lib so the /handoff auto-save uses the
113
+ # exact same logic. Fallback to plain `git add -A` if the lib can't be sourced,
114
+ # so an unattended auto-save never silently no-ops.
115
+ # shellcheck source=bin/lib/git-safe.sh
116
+ . "$SCRIPT_DIR/bin/lib/git-safe.sh" 2>/dev/null || true
117
+ if ! type git_add_guarded >/dev/null 2>&1; then
118
+ git_add_guarded() { git add -A 2>/dev/null || true; }
119
+ fi
120
+
121
+ # --- Setup develop (wrapped — failures set HEALTH_GIT but don't crash) ---
122
+ setup_develop() {
123
+ # Ensure develop branch exists locally
124
+ if ! git show-ref --verify --quiet refs/heads/develop 2>/dev/null; then
125
+ if git show-ref --verify --quiet refs/remotes/origin/develop 2>/dev/null; then
126
+ git checkout -b develop origin/develop --quiet 2>/dev/null
127
+ else
128
+ # No develop on remote either — create from main
129
+ git checkout -b develop --quiet 2>/dev/null
130
+ git push -u origin develop --quiet 2>/dev/null
131
+ fi
132
+ fi
133
+
134
+ # Sync develop (without checkout — safe for concurrent sessions)
135
+ CURRENT_BRANCH=$(git branch --show-current)
136
+
137
+ # Update local develop ref from remote without switching branches
138
+ # Use fetch + force-update to handle divergence (e.g., from auto-update commits)
139
+ git fetch origin develop --quiet 2>/dev/null || true
140
+ if [[ "$CURRENT_BRANCH" != "develop" ]]; then
141
+ # Safe to force-update when not checked out
142
+ git branch -f develop origin/develop 2>/dev/null || true
143
+ else
144
+ # On develop — try ff merge, fall back to reset only if no unpushed work
145
+ git merge --ff-only origin/develop --quiet 2>/dev/null || {
146
+ # Only reset if there are no unpushed commits on develop
147
+ LOCAL_AHEAD=$(git rev-list origin/develop..develop --count 2>/dev/null || echo "0")
148
+ if [ "$LOCAL_AHEAD" = "0" ]; then
149
+ git reset --hard origin/develop --quiet 2>/dev/null || true
150
+ fi
151
+ }
152
+ fi
153
+ DEVELOP_SYNCED="true"
154
+
155
+ # Count commits on develop ahead of main
156
+ COMMITS_AHEAD=0
157
+ if git show-ref --verify --quiet refs/remotes/origin/main 2>/dev/null; then
158
+ COMMITS_AHEAD=$(git rev-list origin/main..develop --count 2>/dev/null || echo "0")
159
+ fi
160
+
161
+ # Always start from develop
162
+ # Every session begins on develop. Claude creates a fresh topic branch
163
+ # (dev/{author}/{topic-slug}) when the user says what they're working on.
164
+ # If we're on a working branch with uncommitted work, save it first.
165
+ ACTION="ready"
166
+ SAVED_BRANCH=""
167
+
168
+ if [[ "$CURRENT_BRANCH" != "develop" ]]; then
169
+ # Check for uncommitted work (staged + unstaged + untracked in tracked dirs)
170
+ if [ -n "$(git status --porcelain 2>/dev/null | head -1)" ]; then
171
+ # Save uncommitted work so nothing is lost (guarded: never auto-commit
172
+ # stray submodule pointers from sibling repos in the checkout)
173
+ git_add_guarded
174
+ git commit -m "Auto-save: uncommitted work from $CURRENT_BRANCH" --quiet 2>/dev/null || true
175
+ SAVED_BRANCH="$CURRENT_BRANCH"
176
+ fi
177
+
178
+ # Push the branch if it has unpushed commits (so work is never only local)
179
+ if [[ "$CURRENT_BRANCH" == dev/* ]] || [[ "$CURRENT_BRANCH" == feature/* ]] || [[ "$CURRENT_BRANCH" == bugfix/* ]]; then
180
+ LOCAL_HEAD=$(git rev-parse HEAD 2>/dev/null || echo "")
181
+ REMOTE_HEAD=$(git rev-parse "origin/$CURRENT_BRANCH" 2>/dev/null || echo "none")
182
+ if [ "$LOCAL_HEAD" != "$REMOTE_HEAD" ]; then
183
+ git push origin "$CURRENT_BRANCH" --quiet 2>/dev/null || true
184
+ fi
185
+ fi
186
+
187
+ # Switch to develop (already updated by fetch origin develop:develop above)
188
+ git checkout develop --quiet 2>/dev/null || true
189
+ else
190
+ # Already on develop — fetch couldn't update it (checked-out branch), so pull
191
+ git merge --ff-only origin/develop --quiet 2>/dev/null || true
192
+ fi
193
+
194
+ BRANCH="develop"
195
+ }
196
+
197
+ # Initialize defaults used by setup_develop
198
+ CURRENT_BRANCH=$(git branch --show-current 2>/dev/null || echo "")
199
+ DEVELOP_SYNCED="false"
200
+ COMMITS_AHEAD=0
201
+ ACTION="ready"
202
+ SAVED_BRANCH=""
203
+ BRANCH="${CURRENT_BRANCH:-develop}"
204
+
205
+ if [ "$IS_WORKTREE" = "true" ]; then
206
+ # Inside a worktree — skip develop checkout and framework update.
207
+ # Worktrees are isolated working copies; framework updates belong on develop.
208
+ BRANCH=$(git branch --show-current 2>/dev/null || echo "?")
209
+ DEVELOP_SYNCED="true"
210
+
211
+ # --- Worktree branch health check ---
212
+ # Detect if the branch was already merged into develop (e.g., PR merged
213
+ # but worktree not cleaned up). This prevents working on a stale branch
214
+ # whose commits are already in develop.
215
+ WORKTREE_STALE="false"
216
+ if [ "$BRANCH" != "?" ] && [ "$BRANCH" != "develop" ] && [ "$BRANCH" != "main" ]; then
217
+ # Check if remote branch still exists
218
+ if ! git ls-remote --heads origin "$BRANCH" 2>/dev/null | grep -q "$BRANCH"; then
219
+ # Remote branch gone — check if it was merged into develop
220
+ if git branch -r --merged origin/develop 2>/dev/null | grep -q "origin/$BRANCH" 2>/dev/null || \
221
+ git merge-base --is-ancestor HEAD origin/develop 2>/dev/null; then
222
+ WORKTREE_STALE="merged"
223
+ else
224
+ WORKTREE_STALE="remote_deleted"
225
+ fi
226
+ fi
227
+ fi
228
+
229
+ if [ "$WORKTREE_STALE" = "merged" ]; then
230
+ echo ""
231
+ echo "WARNING: Branch '$BRANCH' was already merged into develop."
232
+ echo "This worktree is stale — your work here is already in develop."
233
+ echo "Start a new session in the main project to get a fresh branch."
234
+ echo ""
235
+ HEALTH_GIT="fail"
236
+ elif [ "$WORKTREE_STALE" = "remote_deleted" ]; then
237
+ echo ""
238
+ echo "WARNING: Remote branch '$BRANCH' was deleted but NOT merged into develop."
239
+ echo "This worktree may contain unique local commits. Check before discarding:"
240
+ echo " git log origin/develop..HEAD --oneline"
241
+ echo ""
242
+ fi
243
+
244
+ # Use main project's .env and state if ours are missing
245
+ if [ ! -f "$SCRIPT_DIR/.env" ] && [ -f "$MAIN_PROJECT_DIR/.env" ]; then
246
+ export ENV_FILE="$MAIN_PROJECT_DIR/.env"
247
+ fi
248
+ elif ! setup_develop 2>/dev/null; then
249
+ HEALTH_GIT="fail"
250
+ fi
251
+
252
+ # Apply framework update now — we're on develop (or worktree skipped above)
253
+ if [ "$IS_WORKTREE" != "true" ]; then
254
+ _apply_framework_update 2>/dev/null || true
255
+ fi
256
+
257
+ # --- Sync memory (symlink or direct directory — both valid) ---
258
+ if [ -d "$SCRIPT_DIR/memory/.git" ]; then
259
+ MEM_LOCAL=$(git -C "$SCRIPT_DIR/memory" rev-parse HEAD 2>/dev/null || echo "")
260
+ MEM_REMOTE=$(git -C "$SCRIPT_DIR/memory" rev-parse origin/main 2>/dev/null || echo "")
261
+ if [ -n "$MEM_LOCAL" ] && [ -n "$MEM_REMOTE" ] && [ "$MEM_LOCAL" != "$MEM_REMOTE" ]; then
262
+ git -C "$SCRIPT_DIR/memory" pull origin main --quiet 2>/dev/null || true
263
+ fi
264
+ MEMORY_SYNCED="true"
265
+ fi
266
+
267
+ # --- Sync managed repos ---
268
+ REPOS_STATUS=""
269
+ for REPO in $MANAGED_REPOS; do
270
+ REPO_DIR="$SCRIPT_DIR/../$REPO"
271
+ if [ -d "$REPO_DIR/.git" ]; then
272
+ # Resolve base branch for this repo (from egregore.json or auto-detect)
273
+ REPO_BASE=$(_get_base_branch "$REPO")
274
+ # Sync base branch with remote (if not checked out)
275
+ R_BRANCH=$(git -C "$REPO_DIR" branch --show-current 2>/dev/null || echo "?")
276
+ if [ "$R_BRANCH" != "$REPO_BASE" ]; then
277
+ git -C "$REPO_DIR" branch -f "$REPO_BASE" "origin/$REPO_BASE" --quiet 2>/dev/null || true
278
+ else
279
+ git -C "$REPO_DIR" merge --ff-only "origin/$REPO_BASE" --quiet 2>/dev/null || true
280
+ fi
281
+ # Collect status
282
+ R_DIRTY=""
283
+ if [ -n "$(git -C "$REPO_DIR" status --porcelain 2>/dev/null | head -1)" ]; then
284
+ R_DIRTY=" *"
285
+ fi
286
+ REPOS_STATUS="${REPOS_STATUS} ◇ ${REPO}: ${R_BRANCH}${R_DIRTY}\n"
287
+ fi
288
+ done