create-egregore 0.19.33 → 0.20.1

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 (250) hide show
  1. package/assets/egregore-launcher.js +141 -14
  2. package/bin/cli.js +19 -3
  3. package/lib/connect-runtime.js +4 -2
  4. package/lib/local.js +15 -1
  5. package/lib/prime-runtime.js +208 -0
  6. package/lib/setup.js +66 -5
  7. package/package.json +4 -3
  8. package/runtime/codex/.codex/config.toml +3 -0
  9. package/runtime/codex/.codex/skills/archive/SKILL.md +1 -1
  10. package/runtime/codex/.codex/skills/branch/SKILL.md +1 -1
  11. package/runtime/codex/.codex/skills/checkup/SKILL.md +1 -1
  12. package/runtime/codex/.codex/skills/commit/SKILL.md +1 -1
  13. package/runtime/codex/.codex/skills/contribute/SKILL.md +1 -1
  14. package/runtime/codex/.codex/skills/delete-user/SKILL.md +1 -1
  15. package/runtime/codex/.codex/skills/ingest/SKILL.md +1 -1
  16. package/runtime/codex/.codex/skills/ingest-corpus/SKILL.md +29 -0
  17. package/runtime/codex/.codex/skills/ingest-notion/SKILL.md +29 -0
  18. package/runtime/codex/.codex/skills/ingest-user-interview/SKILL.md +1 -1
  19. package/runtime/codex/.codex/skills/issue/SKILL.md +1 -1
  20. package/runtime/codex/.codex/skills/me/SKILL.md +1 -1
  21. package/runtime/codex/.codex/skills/meeting/SKILL.md +1 -1
  22. package/runtime/codex/.codex/skills/note/SKILL.md +1 -1
  23. package/runtime/codex/.codex/skills/notion-connect/SKILL.md +29 -0
  24. package/runtime/codex/.codex/skills/onboarding/SKILL.md +1 -1
  25. package/runtime/codex/.codex/skills/pr/SKILL.md +1 -1
  26. package/runtime/codex/.codex/skills/pull/SKILL.md +1 -1
  27. package/runtime/codex/.codex/skills/push/SKILL.md +1 -1
  28. package/runtime/codex/.codex/skills/reflect/SKILL.md +1 -1
  29. package/runtime/codex/.codex/skills/review-pr/SKILL.md +1 -1
  30. package/runtime/codex/.codex/skills/save/SKILL.md +3 -1
  31. package/runtime/codex/.codex/skills/slack-connect/SKILL.md +1 -1
  32. package/runtime/codex/.codex/skills/summon/SKILL.md +1 -1
  33. package/runtime/codex/.codex/skills/sync-repos/SKILL.md +1 -1
  34. package/runtime/codex/.codex/skills/teams-connect/SKILL.md +29 -0
  35. package/runtime/codex/.codex/skills/telegram-connect/SKILL.md +1 -1
  36. package/runtime/codex/.codex/skills/telemetry/SKILL.md +1 -1
  37. package/runtime/codex/.codex/skills/test/SKILL.md +1 -1
  38. package/runtime/codex/.codex/skills/tutorial/SKILL.md +1 -1
  39. package/runtime/codex/.codex/skills/update/SKILL.md +1 -1
  40. package/runtime/codex/.codex/spec-manifest.json +5 -5
  41. package/runtime/codex/AGENTS.md +41 -35
  42. package/runtime/codex/bin/activity-data.sh +4 -8
  43. package/runtime/codex/bin/agent.sh +17 -5
  44. package/runtime/codex/bin/attendant.sh +13 -0
  45. package/runtime/codex/bin/autosave.sh +27 -1
  46. package/runtime/codex/bin/codex-render-spec.mjs +33 -32
  47. package/runtime/codex/bin/codex-session-start.sh +3 -1
  48. package/runtime/codex/bin/codex-sync-skills.sh +6 -1
  49. package/runtime/codex/bin/graph-batch.sh +25 -6
  50. package/runtime/codex/bin/graph-op.sh +5 -4
  51. package/runtime/codex/bin/graph.sh +31 -5
  52. package/runtime/codex/bin/handoff-pr-backfill.sh +1 -1
  53. package/runtime/codex/bin/handoff-run.sh +8 -3
  54. package/runtime/codex/bin/handoff-save-egregore.sh +19 -7
  55. package/runtime/codex/bin/ingest.py +341 -10
  56. package/runtime/codex/bin/lib/context.sh +26 -1
  57. package/runtime/codex/bin/lib/git-message.sh +37 -0
  58. package/runtime/codex/bin/lib/git-sync.sh +10 -2
  59. package/runtime/codex/bin/lib/greeting.sh +4 -1
  60. package/runtime/codex/bin/lib/worktree-links.sh +25 -0
  61. package/runtime/codex/bin/session-autosave.sh +7 -1
  62. package/runtime/codex/bin/settings.sh +1 -1
  63. package/runtime/codex/manifest.json +10 -1
  64. package/runtime/pi/.codex/config.toml +3 -0
  65. package/runtime/pi/.codex/skills/archive/SKILL.md +1 -1
  66. package/runtime/pi/.codex/skills/branch/SKILL.md +1 -1
  67. package/runtime/pi/.codex/skills/checkup/SKILL.md +1 -1
  68. package/runtime/pi/.codex/skills/commit/SKILL.md +1 -1
  69. package/runtime/pi/.codex/skills/contribute/SKILL.md +1 -1
  70. package/runtime/pi/.codex/skills/delete-user/SKILL.md +1 -1
  71. package/runtime/pi/.codex/skills/ingest/SKILL.md +1 -1
  72. package/runtime/pi/.codex/skills/ingest-corpus/SKILL.md +29 -0
  73. package/runtime/pi/.codex/skills/ingest-notion/SKILL.md +29 -0
  74. package/runtime/pi/.codex/skills/ingest-user-interview/SKILL.md +1 -1
  75. package/runtime/pi/.codex/skills/issue/SKILL.md +1 -1
  76. package/runtime/pi/.codex/skills/me/SKILL.md +1 -1
  77. package/runtime/pi/.codex/skills/meeting/SKILL.md +1 -1
  78. package/runtime/pi/.codex/skills/note/SKILL.md +1 -1
  79. package/runtime/pi/.codex/skills/notion-connect/SKILL.md +29 -0
  80. package/runtime/pi/.codex/skills/onboarding/SKILL.md +1 -1
  81. package/runtime/pi/.codex/skills/pr/SKILL.md +1 -1
  82. package/runtime/pi/.codex/skills/pull/SKILL.md +1 -1
  83. package/runtime/pi/.codex/skills/push/SKILL.md +1 -1
  84. package/runtime/pi/.codex/skills/reflect/SKILL.md +1 -1
  85. package/runtime/pi/.codex/skills/review-pr/SKILL.md +1 -1
  86. package/runtime/pi/.codex/skills/save/SKILL.md +3 -1
  87. package/runtime/pi/.codex/skills/slack-connect/SKILL.md +1 -1
  88. package/runtime/pi/.codex/skills/summon/SKILL.md +1 -1
  89. package/runtime/pi/.codex/skills/sync-repos/SKILL.md +1 -1
  90. package/runtime/pi/.codex/skills/teams-connect/SKILL.md +29 -0
  91. package/runtime/pi/.codex/skills/telegram-connect/SKILL.md +1 -1
  92. package/runtime/pi/.codex/skills/telemetry/SKILL.md +1 -1
  93. package/runtime/pi/.codex/skills/test/SKILL.md +1 -1
  94. package/runtime/pi/.codex/skills/tutorial/SKILL.md +1 -1
  95. package/runtime/pi/.codex/skills/update/SKILL.md +1 -1
  96. package/runtime/pi/.codex/spec-manifest.json +5 -5
  97. package/runtime/pi/.pi/APPEND_SYSTEM.md +57 -33
  98. package/runtime/pi/.pi/extensions/egregore.ts +17 -6
  99. package/runtime/pi/.pi/spec-manifest.json +2 -2
  100. package/runtime/pi/AGENTS.md +41 -35
  101. package/runtime/pi/bin/activity-data.sh +4 -8
  102. package/runtime/pi/bin/agent.sh +17 -5
  103. package/runtime/pi/bin/attendant.sh +13 -0
  104. package/runtime/pi/bin/autosave.sh +27 -1
  105. package/runtime/pi/bin/codex-render-spec.mjs +33 -32
  106. package/runtime/pi/bin/codex-session-start.sh +3 -1
  107. package/runtime/pi/bin/codex-sync-skills.sh +6 -1
  108. package/runtime/pi/bin/graph-batch.sh +25 -6
  109. package/runtime/pi/bin/graph-op.sh +5 -4
  110. package/runtime/pi/bin/graph.sh +31 -5
  111. package/runtime/pi/bin/handoff-pr-backfill.sh +1 -1
  112. package/runtime/pi/bin/handoff-run.sh +8 -3
  113. package/runtime/pi/bin/handoff-save-egregore.sh +19 -7
  114. package/runtime/pi/bin/ingest.py +341 -10
  115. package/runtime/pi/bin/lib/context.sh +26 -1
  116. package/runtime/pi/bin/lib/git-message.sh +37 -0
  117. package/runtime/pi/bin/lib/git-sync.sh +10 -2
  118. package/runtime/pi/bin/lib/greeting.sh +4 -1
  119. package/runtime/pi/bin/lib/worktree-links.sh +25 -0
  120. package/runtime/pi/bin/pi-branch-policy.mjs +138 -1
  121. package/runtime/pi/bin/pi-product-workflows.mjs +35 -7
  122. package/runtime/pi/bin/pi-render-spec.mjs +52 -26
  123. package/runtime/pi/bin/session-autosave.sh +7 -1
  124. package/runtime/pi/bin/settings.sh +1 -1
  125. package/runtime/pi/manifest.json +10 -1
  126. package/runtime/prime/.claude/context/notification-consent.md +66 -0
  127. package/runtime/prime/.codex/config.toml +20 -0
  128. package/runtime/prime/.codex/hooks/branch-guard.js +461 -0
  129. package/runtime/prime/.codex/hooks/search-hint.js +67 -0
  130. package/runtime/prime/.codex/hooks.json +30 -0
  131. package/runtime/prime/.codex/skills/activity/SKILL.md +82 -0
  132. package/runtime/prime/.codex/skills/announce/SKILL.md +67 -0
  133. package/runtime/prime/.codex/skills/archive/SKILL.md +45 -0
  134. package/runtime/prime/.codex/skills/branch/SKILL.md +29 -0
  135. package/runtime/prime/.codex/skills/checkup/SKILL.md +45 -0
  136. package/runtime/prime/.codex/skills/commit/SKILL.md +29 -0
  137. package/runtime/prime/.codex/skills/contribute/SKILL.md +29 -0
  138. package/runtime/prime/.codex/skills/create-skill/SKILL.md +29 -0
  139. package/runtime/prime/.codex/skills/dark-mode/SKILL.md +29 -0
  140. package/runtime/prime/.codex/skills/dashboard/SKILL.md +59 -0
  141. package/runtime/prime/.codex/skills/deep-reflect/SKILL.md +109 -0
  142. package/runtime/prime/.codex/skills/delete-user/SKILL.md +29 -0
  143. package/runtime/prime/.codex/skills/emissary/SKILL.md +45 -0
  144. package/runtime/prime/.codex/skills/handoff/SKILL.md +166 -0
  145. package/runtime/prime/.codex/skills/harvest/QUESTION_PALETTE.md +76 -0
  146. package/runtime/prime/.codex/skills/harvest/SKILL.md +131 -0
  147. package/runtime/prime/.codex/skills/ingest/SKILL.md +29 -0
  148. package/runtime/prime/.codex/skills/ingest-corpus/SKILL.md +29 -0
  149. package/runtime/prime/.codex/skills/ingest-notion/SKILL.md +29 -0
  150. package/runtime/prime/.codex/skills/ingest-user-interview/SKILL.md +45 -0
  151. package/runtime/prime/.codex/skills/invite/SKILL.md +44 -0
  152. package/runtime/prime/.codex/skills/issue/SKILL.md +45 -0
  153. package/runtime/prime/.codex/skills/me/SKILL.md +29 -0
  154. package/runtime/prime/.codex/skills/meeting/SKILL.md +45 -0
  155. package/runtime/prime/.codex/skills/note/SKILL.md +29 -0
  156. package/runtime/prime/.codex/skills/notion-connect/SKILL.md +29 -0
  157. package/runtime/prime/.codex/skills/onboarding/SKILL.md +45 -0
  158. package/runtime/prime/.codex/skills/pr/SKILL.md +29 -0
  159. package/runtime/prime/.codex/skills/pull/SKILL.md +29 -0
  160. package/runtime/prime/.codex/skills/push/SKILL.md +29 -0
  161. package/runtime/prime/.codex/skills/quest/SKILL.md +80 -0
  162. package/runtime/prime/.codex/skills/reflect/SKILL.md +45 -0
  163. package/runtime/prime/.codex/skills/review-pr/SKILL.md +45 -0
  164. package/runtime/prime/.codex/skills/save/SKILL.md +127 -0
  165. package/runtime/prime/.codex/skills/slack-connect/SKILL.md +29 -0
  166. package/runtime/prime/.codex/skills/summon/SKILL.md +45 -0
  167. package/runtime/prime/.codex/skills/sync-repos/SKILL.md +29 -0
  168. package/runtime/prime/.codex/skills/teams-connect/SKILL.md +29 -0
  169. package/runtime/prime/.codex/skills/telegram-connect/SKILL.md +29 -0
  170. package/runtime/prime/.codex/skills/telemetry/SKILL.md +29 -0
  171. package/runtime/prime/.codex/skills/test/SKILL.md +45 -0
  172. package/runtime/prime/.codex/skills/the-spiral/SKILL.md +84 -0
  173. package/runtime/prime/.codex/skills/tui-design/SKILL.md +45 -0
  174. package/runtime/prime/.codex/skills/tutorial/SKILL.md +45 -0
  175. package/runtime/prime/.codex/skills/update/SKILL.md +29 -0
  176. package/runtime/prime/.codex/skills/view/SKILL.md +156 -0
  177. package/runtime/prime/.codex/skills/view/agents/openai.yaml +4 -0
  178. package/runtime/prime/.codex/skills/view/assets/compose-scaffold.html +221 -0
  179. package/runtime/prime/.codex/skills/visual-explain/SKILL.md +45 -0
  180. package/runtime/prime/.codex/skills/wrap/SKILL.md +58 -0
  181. package/runtime/prime/.codex/spec-manifest.json +132 -0
  182. package/runtime/prime/.prime/agent/APPEND_SYSTEM.md +416 -0
  183. package/runtime/prime/.prime/agent/extensions/egregore.ts +460 -0
  184. package/runtime/prime/.prime/agent/settings.json +4 -0
  185. package/runtime/prime/.prime/agent/skills/egregore/SKILL.md +34 -0
  186. package/runtime/prime/.prime/agent/skills/egregore/pyproject.toml +13 -0
  187. package/runtime/prime/.prime/agent/skills/egregore/src/egregore/__init__.py +156 -0
  188. package/runtime/prime/.prime/agent/spec-manifest.json +12 -0
  189. package/runtime/prime/AGENTS.md +488 -0
  190. package/runtime/prime/bin/activity-data.sh +238 -0
  191. package/runtime/prime/bin/agent.sh +787 -0
  192. package/runtime/prime/bin/attendant.sh +707 -0
  193. package/runtime/prime/bin/autosave.sh +112 -0
  194. package/runtime/prime/bin/capture-reconcile.sh +69 -0
  195. package/runtime/prime/bin/capture-run.sh +341 -0
  196. package/runtime/prime/bin/codex-install-skills.sh +78 -0
  197. package/runtime/prime/bin/codex-render-spec.mjs +435 -0
  198. package/runtime/prime/bin/codex-session-start.sh +265 -0
  199. package/runtime/prime/bin/codex-skill-render.mjs +347 -0
  200. package/runtime/prime/bin/codex-sync-skills.sh +237 -0
  201. package/runtime/prime/bin/connect-refresh.sh +33 -0
  202. package/runtime/prime/bin/dashboard-data.sh +178 -0
  203. package/runtime/prime/bin/graph-batch.sh +79 -0
  204. package/runtime/prime/bin/graph-op.sh +1102 -0
  205. package/runtime/prime/bin/graph-wal.sh +229 -0
  206. package/runtime/prime/bin/graph.sh +223 -0
  207. package/runtime/prime/bin/handoff-pr-backfill.sh +86 -0
  208. package/runtime/prime/bin/handoff-run.sh +676 -0
  209. package/runtime/prime/bin/handoff-save-egregore.sh +224 -0
  210. package/runtime/prime/bin/index-handoff.sh +322 -0
  211. package/runtime/prime/bin/ingest-graph.sh +80 -0
  212. package/runtime/prime/bin/ingest-surface.html +495 -0
  213. package/runtime/prime/bin/ingest.py +1011 -0
  214. package/runtime/prime/bin/ingest.sh +279 -0
  215. package/runtime/prime/bin/ingest_graph.py +856 -0
  216. package/runtime/prime/bin/ingest_surface.py +544 -0
  217. package/runtime/prime/bin/lib/artifact-id.sh +46 -0
  218. package/runtime/prime/bin/lib/config.sh +116 -0
  219. package/runtime/prime/bin/lib/context.sh +621 -0
  220. package/runtime/prime/bin/lib/dashboard-artifact.sh +242 -0
  221. package/runtime/prime/bin/lib/ensure-develop.sh +183 -0
  222. package/runtime/prime/bin/lib/git-message.sh +37 -0
  223. package/runtime/prime/bin/lib/git-sync.sh +414 -0
  224. package/runtime/prime/bin/lib/greeting.sh +519 -0
  225. package/runtime/prime/bin/lib/hash.sh +13 -0
  226. package/runtime/prime/bin/lib/identity.sh +213 -0
  227. package/runtime/prime/bin/lib/metrics.sh +374 -0
  228. package/runtime/prime/bin/lib/noncode.sh +31 -0
  229. package/runtime/prime/bin/lib/permissions.sh +106 -0
  230. package/runtime/prime/bin/lib/time.sh +56 -0
  231. package/runtime/prime/bin/lib/worktree-links.sh +79 -0
  232. package/runtime/prime/bin/notify.sh +636 -0
  233. package/runtime/prime/bin/person.py +630 -0
  234. package/runtime/prime/bin/person.sh +448 -0
  235. package/runtime/prime/bin/pi-branch-policy.mjs +312 -0
  236. package/runtime/prime/bin/pi-product-workflows.mjs +394 -0
  237. package/runtime/prime/bin/prime-render-spec.mjs +147 -0
  238. package/runtime/prime/bin/prime-session-start.sh +24 -0
  239. package/runtime/prime/bin/publish-artifact.sh +271 -0
  240. package/runtime/prime/bin/publish-references.sh +144 -0
  241. package/runtime/prime/bin/repo-state.sh +96 -0
  242. package/runtime/prime/bin/search.sh +240 -0
  243. package/runtime/prime/bin/session-autosave.sh +215 -0
  244. package/runtime/prime/bin/session-log.sh +199 -0
  245. package/runtime/prime/bin/settings.sh +304 -0
  246. package/runtime/prime/bin/telemetry.sh +391 -0
  247. package/runtime/prime/bin/worktree-create.sh +128 -0
  248. package/runtime/prime/bin/worktree-remove.sh +50 -0
  249. package/runtime/prime/bin/worktree.sh +198 -0
  250. package/runtime/prime/manifest.json +181 -0
@@ -0,0 +1,519 @@
1
+ # shellcheck shell=bash
2
+ # greeting.sh — Greeting output renderer for session-start.sh
3
+ #
4
+ # Renders the full session greeting:
5
+ # - ASCII art banner
6
+ # - Identity line (org/repo + user + branch)
7
+ # - Momentum board (sessions, commits, handoffs, knowledge)
8
+ # - Pending questions signal
9
+ # - Health footer
10
+ # - Alias migration
11
+ # - Session context JSON (hidden, for Claude)
12
+ # - Soul file inclusion
13
+ # - Telemetry emit
14
+ # - First session welcome
15
+ #
16
+ # Inputs: SCRIPT_DIR, STATE_FILE, CONFIG, CTX_DIR, BRANCH, COMMITS_AHEAD,
17
+ # AUTHOR, LOCAL_MODE, MEMORY_SYNCED, SAVED_BRANCH,
18
+ # HEALTH_*, FRAMEWORK_VERSION, TIME_OF_DAY, EGREGORE_SESSION_ID,
19
+ # FIRST_SESSION, DISPLAY_NAME_STATE, DASHBOARD_URL
20
+
21
+ # --- Output greeting for Claude to display ---
22
+ cat << 'GREETING'
23
+
24
+ ███████╗ ██████╗ ██████╗ ███████╗ ██████╗ ██████╗ ██████╗ ███████╗
25
+ ██╔════╝██╔════╝ ██╔══██╗██╔════╝██╔════╝ ██╔═══██╗██╔══██╗██╔════╝
26
+ █████╗ ██║ ███╗██████╔╝█████╗ ██║ ███╗██║ ██║██████╔╝█████╗
27
+ ██╔══╝ ██║ ██║██╔══██╗██╔══╝ ██║ ██║██║ ██║██╔══██╗██╔══╝
28
+ ███████╗╚██████╔╝██║ ██║███████╗╚██████╔╝╚██████╔╝██║ ██║███████╗
29
+ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝
30
+
31
+ GREETING
32
+
33
+ # --- Ornamented status ---
34
+ REPO_NAME=$(jq -r '.repo_name // "egregore"' "$SCRIPT_DIR/egregore.json" 2>/dev/null)
35
+ GITHUB_ORG_DISPLAY=$(jq -r '.github_org // ""' "$SCRIPT_DIR/egregore.json" 2>/dev/null)
36
+ ORG_NAME=$(jq -r '.org_name // ""' "$SCRIPT_DIR/egregore.json" 2>/dev/null)
37
+
38
+ DISPLAY_NAME=""
39
+ if [ -f "$STATE_FILE" ]; then
40
+ DISPLAY_NAME=$(jq -r '.display_name // .name // empty' "$STATE_FILE" 2>/dev/null)
41
+ fi
42
+ GREETING_NAME="${DISPLAY_NAME:-$AUTHOR}"
43
+
44
+ # --- Line 1: Org/Repo + User + Branch (primary info) ---
45
+ SEPARATOR=" ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄"
46
+
47
+ # Build identity line: Org/repo on left, user + branch on right
48
+ if [ "$LOCAL_MODE" = "true" ]; then
49
+ IDENTITY_LEFT=" Egregore"
50
+ else
51
+ IDENTITY_LEFT=" ${GITHUB_ORG_DISPLAY}/${REPO_NAME}"
52
+ fi
53
+ BRANCH_COMPACT="$BRANCH"
54
+ if [ "$COMMITS_AHEAD" -gt 0 ] 2>/dev/null; then
55
+ BRANCH_COMPACT="${BRANCH} · ${COMMITS_AHEAD}↑"
56
+ fi
57
+ IDENTITY_RIGHT="${GREETING_NAME} · ${BRANCH_COMPACT}"
58
+ LINE_WIDTH=67
59
+ ID_LEFT_LEN=${#IDENTITY_LEFT}
60
+ ID_RIGHT_LEN=${#IDENTITY_RIGHT}
61
+ ID_PADDING=$((LINE_WIDTH - ID_LEFT_LEN - ID_RIGHT_LEN))
62
+ if [ "$ID_PADDING" -lt 1 ]; then ID_PADDING=1; fi
63
+ printf "\n%s%*s%s\n" "$IDENTITY_LEFT" "$ID_PADDING" "" "$IDENTITY_RIGHT"
64
+ echo "$SEPARATOR"
65
+
66
+ # --- Momentum board ---
67
+ source "$SCRIPT_DIR/bin/lib/metrics.sh"
68
+ _render_momentum_board
69
+
70
+ # Pending questions and handoffs addressed to you. These are lightweight
71
+ # discovery beats; management stays in /answer and /activity.
72
+ PENDING_Q=$(cat "$CTX_DIR/pending_questions" 2>/dev/null || echo "[]")
73
+ PENDING_Q_COUNT=$(echo "$PENDING_Q" | jq 'length' 2>/dev/null || echo "0")
74
+ if [ "$PENDING_Q_COUNT" -gt 0 ] 2>/dev/null && [ "$PENDING_Q_COUNT" != "0" ]; then
75
+ PQ_SENDERS=$(echo "$PENDING_Q" | jq -r '[.[].from] | unique | join(", ")' 2>/dev/null)
76
+ if [ "$PENDING_Q_COUNT" = "1" ]; then
77
+ PQ_NOUN="question"
78
+ else
79
+ PQ_NOUN="questions"
80
+ fi
81
+ if [ -n "$PQ_SENDERS" ]; then
82
+ printf " ◐ %s pending %s from %s — /answer to engage\n" "$PENDING_Q_COUNT" "$PQ_NOUN" "$PQ_SENDERS"
83
+ else
84
+ printf " ◐ %s pending %s — /answer to engage\n" "$PENDING_Q_COUNT" "$PQ_NOUN"
85
+ fi
86
+ fi
87
+ ADDRESSED_RICH=$(cat "$CTX_DIR/addressed_rich" 2>/dev/null || echo "[]")
88
+ ADDRESSED_COUNT=$(echo "$ADDRESSED_RICH" | jq 'length' 2>/dev/null || echo "0")
89
+ if [ "$ADDRESSED_COUNT" -gt 0 ] 2>/dev/null; then
90
+ printf " ◇ %s handoffs for you — say \"show my handoffs\" to review or close\n" "$ADDRESSED_COUNT"
91
+ fi
92
+
93
+ # Show auto-save notice if work was committed from a previous branch
94
+ if [ -n "$SAVED_BRANCH" ]; then
95
+ echo " ✓ Auto-saved uncommitted work on $SAVED_BRANCH"
96
+ fi
97
+
98
+ # --- Footer: health + repos + memory (tertiary) ---
99
+ echo "$SEPARATOR"
100
+
101
+ # Build compact footer line
102
+ if [ "$LOCAL_MODE" = "true" ]; then
103
+ # Local mode: only check github + git, skip api-key/graph/telegram
104
+ HAS_FAILURE="false"
105
+ FAILED_SERVICES=""
106
+ for pair in "github:$HEALTH_GITHUB" "git:$HEALTH_GIT"; do
107
+ svc="${pair%%:*}"
108
+ svc_status="${pair#*:}"
109
+ if [ "$svc_status" = "fail" ]; then
110
+ HAS_FAILURE="true"
111
+ FAILED_SERVICES="${FAILED_SERVICES} ${svc} ✗"
112
+ fi
113
+ done
114
+
115
+ if [ "$HAS_FAILURE" = "true" ]; then
116
+ echo " ⚠${FAILED_SERVICES} — run /checkup"
117
+ else
118
+ if [ "${FRAMEWORK_UPDATED:-false}" = "true" ]; then
119
+ printf " ◆ updated\n"
120
+ else
121
+ printf " ✓ ready\n"
122
+ fi
123
+ fi
124
+ else
125
+ # Connected mode: check all services
126
+ HAS_FAILURE="false"
127
+ FAILED_SERVICES=""
128
+ for pair in "github:$HEALTH_GITHUB" "git:$HEALTH_GIT" "api-key:$HEALTH_APIKEY" "graph:$HEALTH_GRAPH" "telegram:$HEALTH_TELEGRAM"; do
129
+ svc="${pair%%:*}"
130
+ svc_status="${pair#*:}"
131
+ if [ "$svc_status" = "fail" ]; then
132
+ HAS_FAILURE="true"
133
+ FAILED_SERVICES="${FAILED_SERVICES} ${svc} ✗"
134
+ fi
135
+ done
136
+
137
+ if [ "$HAS_FAILURE" = "true" ]; then
138
+ echo " ⚠${FAILED_SERVICES} — run /checkup"
139
+ else
140
+ # Compact footer: ready + memory on one line, repos on next if present
141
+ FOOTER_LEFT=" ✓ ready"
142
+ FOOTER_RIGHT=""
143
+ if [ "$MEMORY_SYNCED" = "true" ]; then
144
+ FOOTER_RIGHT="◆ memory synced"
145
+ fi
146
+
147
+ FL_LEN=${#FOOTER_LEFT}
148
+ FR_LEN=${#FOOTER_RIGHT}
149
+ F_PAD=$((LINE_WIDTH - FL_LEN - FR_LEN))
150
+ if [ "$F_PAD" -lt 1 ]; then F_PAD=1; fi
151
+ printf "%s%*s%s\n" "$FOOTER_LEFT" "$F_PAD" "" "$FOOTER_RIGHT"
152
+ fi
153
+ fi
154
+
155
+ # Framework update trail — an auto-update overwrites every framework path from
156
+ # upstream, local edits included. It used to surface as the footer word
157
+ # "updated" in local mode and as nothing at all in connected mode, which is not
158
+ # enough notice for files being replaced under you. Name the count, name the
159
+ # first few, and point at the commit that did it.
160
+ if [ "${FRAMEWORK_UPDATED:-false}" = "true" ] && [ "${FRAMEWORK_UPDATED_COUNT:-0}" -gt 0 ]; then
161
+ FU_SHA=$(git rev-parse --short HEAD 2>/dev/null || echo "")
162
+ printf " ◆ framework updated from upstream — %s file(s) replaced: %s\n" \
163
+ "$FRAMEWORK_UPDATED_COUNT" "${FRAMEWORK_UPDATED_FILES% }"
164
+ if [ -n "$FU_SHA" ]; then
165
+ printf " local edits to framework paths are replaced by design — git show %s\n" "$FU_SHA"
166
+ fi
167
+ fi
168
+
169
+ # Autosave trail — background saves (Stop-hook, sweep, SessionEnd) since the
170
+ # last greeting. The ledger is machine-scoped; this is where sweep rescues of
171
+ # abandoned sessions become visible to the user.
172
+ AUTOSAVE_LOG="${EGREGORE_AUTOSAVE_LOG:-$HOME/.egregore/autosave.log}"
173
+ AUTOSAVE_SEEN="${AUTOSAVE_LOG}.seen"
174
+ if [ -f "$AUTOSAVE_LOG" ]; then
175
+ TOTAL_AS=$(grep -c '|autosave|' "$AUTOSAVE_LOG" 2>/dev/null || echo 0)
176
+ SEEN_AS=$(cat "$AUTOSAVE_SEEN" 2>/dev/null || echo 0)
177
+ case "$SEEN_AS" in ''|*[!0-9]*) SEEN_AS=0 ;; esac
178
+ if [ "$TOTAL_AS" -gt "$SEEN_AS" ]; then
179
+ NEW_AS=$((TOTAL_AS - SEEN_AS))
180
+ MERGED_AS=$(grep '|autosave|' "$AUTOSAVE_LOG" 2>/dev/null | tail -n "$NEW_AS" | grep -c '|merged|' || echo 0)
181
+ if [ "$MERGED_AS" -gt 0 ]; then
182
+ printf " ⟲ auto-saved %s non-coding save(s) → develop while you were away\n" "$MERGED_AS"
183
+ fi
184
+ fi
185
+ printf '%s' "$TOTAL_AS" > "$AUTOSAVE_SEEN" 2>/dev/null || true
186
+ fi
187
+
188
+ # Staged auto-saves awaiting the user's merge (the /autosave consent gate).
189
+ # Live gh query so the line persists until the PRs actually merge — the
190
+ # ledger cursor above only reports once. Fail-soft when gh/network is out.
191
+ # Shown only while the autosave experiment is enabled: stale PRs from a
192
+ # disabled experiment should not nag every greeting.
193
+ AUTOSAVE_ON=$(jq -r '.autosave_enabled // false' "$STATE_FILE" 2>/dev/null)
194
+ if [ "$AUTOSAVE_ON" = "true" ] && command -v gh >/dev/null 2>&1; then
195
+ GH_SELF=$(jq -r '.github_username // empty' "$STATE_FILE" 2>/dev/null)
196
+ if [ -n "$GH_SELF" ]; then
197
+ WAITING_AS=$(gh pr list --state open --author "$GH_SELF" --limit 20 \
198
+ --json number,title --jq '[.[] | select(.title | startswith("Auto-save:")) | .number] | join(", #")' 2>/dev/null || true)
199
+ if [ -n "$WAITING_AS" ]; then
200
+ printf " ⟲ auto-saves awaiting your merge: PR #%s — say \"merge my auto-saves\"\n" "$WAITING_AS"
201
+ fi
202
+ fi
203
+ fi
204
+
205
+ # Greeting links. Org config `pinned_links` in egregore.json replaces the
206
+ # default dashboard + board links. Entries are strings or {url, label}.
207
+ # (OSC 8 hyperlink — clickable text in supported terminals)
208
+ PINNED_LINKS=$(jq -c '.pinned_links // []' "$SCRIPT_DIR/egregore.json" 2>/dev/null || echo "[]")
209
+ if [ "$(printf '%s' "$PINNED_LINKS" | jq 'length' 2>/dev/null || echo 0)" -gt 0 ]; then
210
+ printf '%s' "$PINNED_LINKS" | jq -r '.[] |
211
+ if type == "string" then " ◆ \(.)"
212
+ elif (.label // "") != "" then " ◆ \(.label): \(.url)"
213
+ else " ◆ \(.url)" end' 2>/dev/null
214
+ else
215
+ if [ -n "${DASHBOARD_URL:-}" ]; then
216
+ printf " ◆ %s\n" "$DASHBOARD_URL"
217
+ fi
218
+
219
+ if [ -n "${BOARD_URL:-}" ]; then
220
+ printf " ◆ %s (board)\n" "$BOARD_URL"
221
+ fi
222
+ fi
223
+
224
+ if [ -n "${LOOM_DOCTOR_BRIEF:-}" ]; then
225
+ # Indent every line — printf ' %s' indents only the first line of a
226
+ # multiline brief, which misaligns the card and hides follow-up warnings
227
+ # from the fast-path signal-line scraper (it keys on "^ ").
228
+ printf '%s\n' "$LOOM_DOCTOR_BRIEF" | sed 's/^/ /'
229
+ fi
230
+
231
+ # --- Creator review gate: pending turns on owned scrolls/surfaces ---
232
+ # This is deliberately filesystem-only and fail-soft. The two environment seams
233
+ # make the scan safe to exercise without touching the instance memory repo.
234
+ _print_pending_scroll_turns() {
235
+ local memory_root="${EGREGORE_MEMORY_ROOT:-$SCRIPT_DIR/memory}"
236
+ local person="${EGREGORE_PERSON:-${AUTHOR:-}}"
237
+ local rows="" file slug owner count surface_file
238
+
239
+ if [ -z "$person" ] && [ -f "${STATE_FILE:-}" ]; then
240
+ person=$(jq -r '.github_username // .display_name // .name // empty' "$STATE_FILE" 2>/dev/null || true)
241
+ fi
242
+ [ -n "$person" ] || return 0
243
+ [ -d "$memory_root" ] || return 0
244
+
245
+ for file in "$memory_root"/scrolls/.events/*.jsonl; do
246
+ [ -f "$file" ] || continue
247
+ slug=$(basename "$file" .jsonl)
248
+ [[ "$slug" =~ ^[A-Za-z0-9._-]+$ ]] || continue
249
+ safe_slug=$(printf '%s' "$slug" | tr -d '\000-\037\177')
250
+ [ "$safe_slug" = "$slug" ] || continue
251
+ owner=$(sed -nE 's/^[[:space:]-]*(\*\*)?creator(\*\*)?[[:space:]]*:[[:space:]]*//p' "$memory_root/scrolls/$slug.md" 2>/dev/null | head -1 | sed 's/[[:space:]]*$//')
252
+ [ "$owner" = "$person" ] || continue
253
+ count=$(jq -s -r '
254
+ def event_id:
255
+ if (.turn | type) == "object" then .turn.id
256
+ elif (.turn | type) == "string" then .turn
257
+ else .id end;
258
+ def received($events):
259
+ [$events[] | select(.type == "turn-received")
260
+ | {id: event_id, answers: (.turn.answers // .answers // [])}
261
+ | select(.id != null)] | unique_by(.id);
262
+ def review_id: event_id;
263
+ def is_absorbed($events; $id):
264
+ any($events[]; .type == "version-published" and ((.absorbs // .version.absorbs // .record.absorbs // []) | index($id) != null));
265
+ def is_applied($events; $id):
266
+ any($events[]; .type == "turn-applied" and (event_id == $id));
267
+ def is_declined($events; $received; $id):
268
+ ([$received[] | select(.id == $id)] | .[0]) as $turn |
269
+ ([$events[] | select(.type == "turn-reviewed" and review_id == $id)] | .[0]) as $review |
270
+ if $review == null then false
271
+ else if (($turn.answers // []) | length) == 0 then $review.disposition == "declined"
272
+ else (($turn.answers // [])
273
+ | map(($review.answers // {})[.fork] // $review.disposition)
274
+ | map(select(. == "accepted")) | length) == 0
275
+ end
276
+ end;
277
+ . as $events
278
+ | ($events | received($events)) as $received
279
+ | [$received[] as $turn
280
+ | select((is_absorbed($events; $turn.id) | not))
281
+ | select((is_declined($events; $received; $turn.id) | not))
282
+ | select(if $mode == "scroll" then true else (is_applied($events; $turn.id) | not) end)]
283
+ | length
284
+ ' --arg mode scroll "$file" 2>/dev/null) || continue
285
+ [[ "$count" =~ ^[0-9]+$ ]] || return 0
286
+ [ "$count" -gt 0 ] && rows+="${safe_slug}"$'\t'"${count}"$'\n'
287
+ done
288
+
289
+ for file in "$memory_root"/harvests/.events/*.jsonl; do
290
+ [ -f "$file" ] || continue
291
+ slug=$(basename "$file" .jsonl)
292
+ [[ "$slug" =~ ^[A-Za-z0-9._-]+$ ]] || continue
293
+ safe_slug=$(printf '%s' "$slug" | tr -d '\000-\037\177')
294
+ [ "$safe_slug" = "$slug" ] || continue
295
+ surface_file="$memory_root/harvests/$slug.json"
296
+ [ -f "$surface_file" ] || continue
297
+ owner=$(jq -r '.author // empty' "$surface_file" 2>/dev/null) || continue
298
+ [ "$owner" = "$person" ] || continue
299
+ count=$(jq -s -r '
300
+ def event_id:
301
+ if (.turn | type) == "object" then .turn.id
302
+ elif (.turn | type) == "string" then .turn
303
+ else .id end;
304
+ def received($events):
305
+ [$events[] | select(.type == "turn-received")
306
+ | {id: event_id, answers: (.turn.answers // .answers // [])}
307
+ | select(.id != null)] | unique_by(.id);
308
+ def review_id: event_id;
309
+ def is_absorbed($events; $id): false;
310
+ def is_applied($events; $id):
311
+ any($events[]; .type == "turn-applied" and (event_id == $id));
312
+ def is_declined($events; $received; $id):
313
+ ([$received[] | select(.id == $id)] | .[0]) as $turn |
314
+ ([$events[] | select(.type == "turn-reviewed" and review_id == $id)] | .[0]) as $review |
315
+ if $review == null then false
316
+ else if (($turn.answers // []) | length) == 0 then $review.disposition == "declined"
317
+ else (($turn.answers // [])
318
+ | map(($review.answers // {})[.fork] // $review.disposition)
319
+ | map(select(. == "accepted")) | length) == 0
320
+ end
321
+ end;
322
+ . as $events
323
+ | ($events | received($events)) as $received
324
+ | [$received[] as $turn
325
+ | select((is_applied($events; $turn.id) | not))
326
+ | select((is_declined($events; $received; $turn.id) | not))]
327
+ | length
328
+ ' --arg mode harvest "$file" 2>/dev/null) || continue
329
+ [[ "$count" =~ ^[0-9]+$ ]] || return 0
330
+ [ "$count" -gt 0 ] && rows+="${safe_slug}"$'\t'"${count}"$'\n'
331
+ done
332
+
333
+ [ -n "$rows" ] || return 0
334
+ local summary total details
335
+ summary=$(printf '%s' "$rows" | awk -F '\t' 'NF == 2 { totals[$1] += $2 } END { for (slug in totals) print slug "\t" totals[slug] }' | sort -t $'\t' -k1,1) || return 0
336
+ [ -n "$summary" ] || return 0
337
+ total=$(printf '%s\n' "$summary" | awk -F '\t' '{ total += $2 } END { print total + 0 }') || return 0
338
+ details=$(printf '%s\n' "$summary" | awk -F '\t' '{printf "%s%s (%s)", (NR == 1 ? "" : ", "), $1, $2}') || return 0
339
+ [ -n "$details" ] || return 0
340
+ printf " ⧖ %s pending turn(s) on your scrolls: %s\n" "$total" "$details"
341
+ }
342
+
343
+ _print_pending_scroll_turns
344
+
345
+ # Framework updates come through PRs to develop — no separate auto-update channel.
346
+ # The develop sync above already keeps framework files current.
347
+ # Use /update for manual upstream pulls when needed.
348
+
349
+ # --- One-time migration: fix aliases to use 'claude "start"' ---
350
+ # v1: 'claude start' → 'claude' (cross-instance bug)
351
+ # v2: 'claude' → 'claude "start"' (blank prompt — auto-sends first message)
352
+ ALIAS_VERSION=$(jq -r '.alias_version // 0' "$STATE_FILE" 2>/dev/null || echo "0")
353
+ if [ "$ALIAS_VERSION" -lt 2 ] 2>/dev/null; then
354
+ for profile in "$HOME/.zshrc" "$HOME/.bashrc" "$HOME/.bash_profile"; do
355
+ if [ -f "$profile" ] && grep -q "$SCRIPT_DIR" "$profile" 2>/dev/null; then
356
+ # Replace any egregore alias pointing to this directory with the correct command
357
+ # Handles: && claude start, && claude, && claude "start" (idempotent)
358
+ sed -i.bak "s|&& claude start|\\&\\& claude \"start\"|g; s|&& claude'|\\&\\& claude \"start\"'|g" "$profile" 2>/dev/null && rm -f "${profile}.bak" || true
359
+ fi
360
+ done
361
+ if [ -f "$STATE_FILE" ] && jq . "$STATE_FILE" >/dev/null 2>&1; then
362
+ jq '. + {"alias_fixed": true, "alias_version": 2}' "$STATE_FILE" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE"
363
+ fi
364
+ fi
365
+
366
+ echo ""
367
+
368
+ # --- Session context (hidden, for Claude) ---
369
+ CONTEXT_HANDOFFS=$(cat "$CTX_DIR/handoffs" 2>/dev/null || echo "[]")
370
+ CONTEXT_HANDOFFS=$(printf '%s' "$CONTEXT_HANDOFFS" | jq -c 'map(if .preview then .preview |= .[0:80] else . end)' 2>/dev/null || printf '%s' "$CONTEXT_HANDOFFS")
371
+ CONTEXT_ADDRESSED=$(cat "$CTX_DIR/addressed" 2>/dev/null || echo "[]")
372
+ CONTEXT_QUESTS=$(cat "$CTX_DIR/quests" 2>/dev/null || echo "[]")
373
+ CONTEXT_QUESTS=$(printf '%s' "$CONTEXT_QUESTS" | jq -c 'if length > 20 then .[0:20] + ["+\(length - 20) more"] else . end' 2>/dev/null || printf '%s' "$CONTEXT_QUESTS")
374
+ CONTEXT_ACTIVITY=$(cat "$CTX_DIR/activity" 2>/dev/null || echo "")
375
+ CONTEXT_TEAM=$(cat "$CTX_DIR/team" 2>/dev/null || echo "[]")
376
+ # Trim the team blob for greeting injection: 8 most-recent people, drop sort
377
+ # keys, one branch each. This trimmed view is all the session ever sees —
378
+ # CTX_DIR is deleted at the end of this script — a deliberate cap to keep
379
+ # hook stdout inline-sized. Untrimmed this is the single largest field in
380
+ # session-context.
381
+ CONTEXT_TEAM=$(printf '%s' "$CONTEXT_TEAM" | jq -c '.[0:8] | map({name, last_seen, working_on: ((.working_on // "")[0:80]), branches: ((.branches // [])[0:1])})' 2>/dev/null || printf '%s' "$CONTEXT_TEAM")
382
+ CONTEXT_SOUL=$(cat "$CTX_DIR/soul_summary" 2>/dev/null || echo "")
383
+ CONTEXT_LIFECYCLE=$(cat "$CTX_DIR/lifecycle" 2>/dev/null || echo '{"merged_prs":[],"implemented_handoffs":[]}')
384
+ CONTEXT_PULSE=$(cat "$CTX_DIR/pulse_brief" 2>/dev/null || echo '{}')
385
+ if [ -f "$CTX_DIR/metrics" ]; then
386
+ CONTEXT_METRICS=$(jq -c . "$CTX_DIR/metrics" 2>/dev/null || echo "{}")
387
+ [ -n "$CONTEXT_METRICS" ] || CONTEXT_METRICS="{}"
388
+ else
389
+ CONTEXT_METRICS="{}"
390
+ fi
391
+
392
+ # --- Write compact subagent context cache (reuses already-gathered data) ---
393
+ SUBAGENT_CTX="/tmp/egregore-subagent-ctx-${EGREGORE_SESSION_ID}.txt"
394
+ (
395
+ SA_ORG_NAME=$(jq -r '.org_name // "Unknown"' "$SCRIPT_DIR/egregore.json" 2>/dev/null || echo "Unknown")
396
+ SA_GITHUB_ORG=$(jq -r '.github_org // "unknown"' "$SCRIPT_DIR/egregore.json" 2>/dev/null || echo "unknown")
397
+
398
+ # Format quests list
399
+ SA_QUESTS="none"
400
+ if [ "$CONTEXT_QUESTS" != "[]" ]; then
401
+ SA_QUESTS=$(echo "$CONTEXT_QUESTS" | jq -r '.[]' 2>/dev/null | paste -sd', ' - 2>/dev/null || echo "none")
402
+ fi
403
+
404
+ # Format recent handoffs
405
+ SA_HANDOFFS="none"
406
+ if [ "$CONTEXT_HANDOFFS" != "[]" ]; then
407
+ SA_HANDOFFS=$(echo "$CONTEXT_HANDOFFS" | jq -r '.[] | .name' 2>/dev/null | head -5 | paste -sd', ' - 2>/dev/null || echo "none")
408
+ fi
409
+
410
+ cat > "$SUBAGENT_CTX" << SAEOF
411
+ <!-- egregore-context
412
+ Organization: $SA_ORG_NAME (github: $SA_GITHUB_ORG)
413
+ Session: $BRANCH — ${EGREGORE_SESSION_ID}
414
+ Active quests: $SA_QUESTS
415
+ Recent handoffs: $SA_HANDOFFS
416
+ Memory: memory/ is a symlink to shared knowledge base. Use bin/graph.sh for Neo4j queries.
417
+ -->
418
+ SAEOF
419
+ ) 2>/dev/null || true
420
+
421
+ # Emit the context JSON compacted (jq -c) — nested blobs arrive pretty-printed
422
+ # from CTX_DIR and the whitespace alone is ~1KB of hook stdout. Fall back to
423
+ # the raw heredoc if jq can't parse (a malformed fragment must not eat context).
424
+ _CTX_JSON=$(cat << CTXEOF
425
+ {
426
+ "framework_version": "$FRAMEWORK_VERSION",
427
+ "time_of_day": "$TIME_OF_DAY",
428
+ "dashboard_url": "${DASHBOARD_URL:-}",
429
+ "recent_handoffs": $CONTEXT_HANDOFFS,
430
+ "addressed_to_user": $CONTEXT_ADDRESSED,
431
+ "quests": $CONTEXT_QUESTS,
432
+ "last_user_activity": "$CONTEXT_ACTIVITY",
433
+ "team_recent_memory": $CONTEXT_TEAM,
434
+ "soul_self_summary": "$CONTEXT_SOUL",
435
+ "lifecycle": $CONTEXT_LIFECYCLE,
436
+ "momentum": $CONTEXT_METRICS,
437
+ "pulse": $CONTEXT_PULSE
438
+ }
439
+ CTXEOF
440
+ )
441
+ printf '\n<!-- session-context\n%s\n-->\n' \
442
+ "$(printf '%s' "$_CTX_JSON" | jq -c . 2>/dev/null || printf '%s' "$_CTX_JSON")"
443
+
444
+ # Include soul file if present
445
+ if [ -f "$SCRIPT_DIR/egregore.md" ]; then
446
+ echo ""
447
+ echo "<!-- egregore-soul"
448
+ cat "$SCRIPT_DIR/egregore.md"
449
+ echo "-->"
450
+ fi
451
+
452
+ # Point at the latest soul reflection instead of inlining it. The full
453
+ # document is several KB; injected verbatim it pushed hook stdout past the
454
+ # harness inline threshold, which turns the greeting into a file the model
455
+ # must Read back — a whole extra API round-trip on every boot.
456
+ if [ -d "$SCRIPT_DIR/memory/soul" ]; then
457
+ LATEST_REFLECTION=$(ls -t "$SCRIPT_DIR/memory/soul/"*.md 2>/dev/null | head -1 || true)
458
+ if [ -n "$LATEST_REFLECTION" ]; then
459
+ echo "<!-- latest-reflection: memory/soul/$(basename "$LATEST_REFLECTION") — read when org identity, culture, or history context matters -->"
460
+ fi
461
+ fi
462
+
463
+ # --- Flush leftover telemetry from previous session (background) ---
464
+ # If the previous session exited without a clean flush (terminal closed,
465
+ # crash, kill -9), events are still in the local buffer. Drain them now.
466
+ bash "$SCRIPT_DIR/bin/telemetry.sh" flush 2>/dev/null &
467
+
468
+ # --- Emit session_start telemetry (background, non-blocking) ---
469
+ bash "$SCRIPT_DIR/bin/telemetry.sh" emit "session_start" \
470
+ "$(jq -n --arg branch "$BRANCH" '{branch: $branch}')" 2>/dev/null &
471
+
472
+ # --- Health check-in (background, non-blocking) ---
473
+ bash "$SCRIPT_DIR/bin/startup-check.sh" >/dev/null 2>&1 &
474
+
475
+ # --- Warm npx cache for egregore-artifacts (background, throttled) ---
476
+ # publish-artifact.sh spawns `npx egregore-artifacts` — first run in a
477
+ # fresh npx cache pays a 5-10s npm download. Warmup is throttled to
478
+ # once per hour via a marker file so we don't storm npm when a user
479
+ # opens many tabs, and don't start runaway retry loops when offline.
480
+ _WARM_TS_FILE="$HOME/.cache/egregore/npx-warm-ts"
481
+ mkdir -p "$(dirname "$_WARM_TS_FILE")" 2>/dev/null
482
+ _WARM_LAST=$(cat "$_WARM_TS_FILE" 2>/dev/null || echo 0)
483
+ _WARM_NOW=$(date +%s)
484
+ if [ $(( _WARM_NOW - _WARM_LAST )) -gt 3600 ]; then
485
+ (npx -y egregore-artifacts@latest --help >/dev/null 2>&1 \
486
+ && echo "$_WARM_NOW" > "$_WARM_TS_FILE" &) >/dev/null 2>&1
487
+ fi
488
+
489
+ # Clean up temp files
490
+ rm -rf "$CTX_DIR"
491
+
492
+ # --- First session welcome ---
493
+ if [ -z "$FIRST_SESSION" ] && [ -f "$STATE_FILE" ]; then
494
+ FIRST_SESSION=$(jq -r '.first_session // false' "$STATE_FILE" 2>/dev/null)
495
+ fi
496
+
497
+ if [ "$FIRST_SESSION" = "true" ]; then
498
+ echo ""
499
+ echo " Welcome! This is your first session."
500
+ echo ""
501
+ echo "IMPORTANT: Display the above greeting exactly as-is (ASCII art + ornamented status). Then ask the user if they'd like a quick onboarding tour (run /onboarding), or if they want to jump straight in."
502
+ # Clear the flag so it only shows once
503
+ jq '.first_session = false' "$STATE_FILE" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE"
504
+ else
505
+ # --- Tutorial tip (if onboarding done but tutorial not) ---
506
+ TUTORIAL_COMPLETE="true"
507
+ if [ -f "$STATE_FILE" ]; then
508
+ TUTORIAL_COMPLETE=$(jq -r '.tutorial_complete // false' "$STATE_FILE" 2>/dev/null || echo "true")
509
+ fi
510
+
511
+ if [ "$TUTORIAL_COMPLETE" != "true" ]; then
512
+ echo " Tip: Run /tutorial to learn the core loop."
513
+ fi
514
+
515
+ echo ""
516
+ echo "IMPORTANT: Display the above greeting to the user exactly as-is (preserve the ASCII art formatting and ornamented status) on their first message. Then ask: What are you working on?"
517
+ echo ""
518
+ echo "BRANCH RULE: When the user responds with what they're working on, your FIRST action is to create a working branch: git fetch origin develop --quiet && git checkout -b dev/{author}/{topic-slug} origin/develop. Do this BEFORE any other work. Derive the topic slug from their description. If they ask a pure question with no work intent, skip branching."
519
+ fi
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env bash
2
+ # Cross-platform hash helpers — sourced by scripts that need hashing.
3
+
4
+ # Compute MD5 hash of a string (macOS + Linux compatible)
5
+ _md5() {
6
+ local input="$1"
7
+ echo -n "$input" | md5 2>/dev/null || echo -n "$input" | md5sum 2>/dev/null | cut -d' ' -f1
8
+ }
9
+
10
+ # Compute project hash (used for per-instance file isolation)
11
+ _proj_hash() {
12
+ _md5 "${SCRIPT_DIR:-$PWD}"
13
+ }