create-egregore 0.19.5 → 0.19.7

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 (130) hide show
  1. package/assets/egregore-launcher.js +411 -25
  2. package/bin/cli.js +34 -20
  3. package/lib/local.js +21 -5
  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 +7 -5
  8. package/runtime/codex/.codex/spec-manifest.json +1 -1
  9. package/runtime/codex/AGENTS.md +7 -2
  10. package/runtime/codex/bin/codex-render-spec.mjs +6 -1
  11. package/runtime/codex/bin/codex-session-start.sh +5 -59
  12. package/runtime/codex/bin/codex-skill-render.mjs +7 -5
  13. package/runtime/codex/bin/lib/context.sh +8 -1
  14. package/runtime/codex/bin/lib/greeting.sh +12 -108
  15. package/runtime/codex/bin/lib/metrics.sh +374 -0
  16. package/runtime/codex/bin/publish-artifact.sh +14 -0
  17. package/runtime/codex/bin/settings.sh +274 -0
  18. package/runtime/codex/manifest.json +2 -0
  19. package/runtime/pi/.codex/config.toml +15 -0
  20. package/runtime/pi/.codex/hooks/branch-guard.js +242 -0
  21. package/runtime/pi/.codex/hooks/search-hint.js +65 -0
  22. package/runtime/pi/.codex/hooks.json +30 -0
  23. package/runtime/pi/.codex/skills/activity/SKILL.md +70 -0
  24. package/runtime/pi/.codex/skills/add/SKILL.md +25 -0
  25. package/runtime/pi/.codex/skills/announce/SKILL.md +54 -0
  26. package/runtime/pi/.codex/skills/answer/SKILL.md +25 -0
  27. package/runtime/pi/.codex/skills/archive/SKILL.md +41 -0
  28. package/runtime/pi/.codex/skills/ask/SKILL.md +59 -0
  29. package/runtime/pi/.codex/skills/branch/SKILL.md +25 -0
  30. package/runtime/pi/.codex/skills/checkup/SKILL.md +41 -0
  31. package/runtime/pi/.codex/skills/commit/SKILL.md +25 -0
  32. package/runtime/pi/.codex/skills/contribute/SKILL.md +25 -0
  33. package/runtime/pi/.codex/skills/dark-mode/SKILL.md +25 -0
  34. package/runtime/pi/.codex/skills/dashboard/SKILL.md +59 -0
  35. package/runtime/pi/.codex/skills/deep-reflect/SKILL.md +74 -0
  36. package/runtime/pi/.codex/skills/delete-user/SKILL.md +25 -0
  37. package/runtime/pi/.codex/skills/egregore-voice/SKILL.md +25 -0
  38. package/runtime/pi/.codex/skills/emissary/SKILL.md +41 -0
  39. package/runtime/pi/.codex/skills/env/SKILL.md +25 -0
  40. package/runtime/pi/.codex/skills/handoff/SKILL.md +67 -0
  41. package/runtime/pi/.codex/skills/harvest/SKILL.md +107 -0
  42. package/runtime/pi/.codex/skills/ingest/SKILL.md +25 -0
  43. package/runtime/pi/.codex/skills/ingest-google/SKILL.md +25 -0
  44. package/runtime/pi/.codex/skills/ingest-user-interview/SKILL.md +41 -0
  45. package/runtime/pi/.codex/skills/invite/SKILL.md +40 -0
  46. package/runtime/pi/.codex/skills/issue/SKILL.md +41 -0
  47. package/runtime/pi/.codex/skills/me/SKILL.md +25 -0
  48. package/runtime/pi/.codex/skills/meeting/SKILL.md +41 -0
  49. package/runtime/pi/.codex/skills/minion/SKILL.md +25 -0
  50. package/runtime/pi/.codex/skills/note/SKILL.md +25 -0
  51. package/runtime/pi/.codex/skills/onboarding/SKILL.md +41 -0
  52. package/runtime/pi/.codex/skills/pr/SKILL.md +25 -0
  53. package/runtime/pi/.codex/skills/project/SKILL.md +41 -0
  54. package/runtime/pi/.codex/skills/pull/SKILL.md +25 -0
  55. package/runtime/pi/.codex/skills/push/SKILL.md +25 -0
  56. package/runtime/pi/.codex/skills/quest/SKILL.md +77 -0
  57. package/runtime/pi/.codex/skills/quest-suggest/SKILL.md +25 -0
  58. package/runtime/pi/.codex/skills/reflect/SKILL.md +41 -0
  59. package/runtime/pi/.codex/skills/review-pr/SKILL.md +41 -0
  60. package/runtime/pi/.codex/skills/save/SKILL.md +62 -0
  61. package/runtime/pi/.codex/skills/scroll/SKILL.md +168 -0
  62. package/runtime/pi/.codex/skills/scroll/agents/openai.yaml +4 -0
  63. package/runtime/pi/.codex/skills/scroll/assets/shell.html +542 -0
  64. package/runtime/pi/.codex/skills/search/SKILL.md +56 -0
  65. package/runtime/pi/.codex/skills/session-view/SKILL.md +25 -0
  66. package/runtime/pi/.codex/skills/setup/SKILL.md +25 -0
  67. package/runtime/pi/.codex/skills/summon/SKILL.md +41 -0
  68. package/runtime/pi/.codex/skills/sync-repos/SKILL.md +25 -0
  69. package/runtime/pi/.codex/skills/telegram-connect/SKILL.md +25 -0
  70. package/runtime/pi/.codex/skills/telemetry/SKILL.md +25 -0
  71. package/runtime/pi/.codex/skills/test/SKILL.md +41 -0
  72. package/runtime/pi/.codex/skills/the-spiral/SKILL.md +84 -0
  73. package/runtime/pi/.codex/skills/thread/SKILL.md +25 -0
  74. package/runtime/pi/.codex/skills/todo/SKILL.md +41 -0
  75. package/runtime/pi/.codex/skills/tui-design/SKILL.md +41 -0
  76. package/runtime/pi/.codex/skills/tutorial/SKILL.md +41 -0
  77. package/runtime/pi/.codex/skills/update/SKILL.md +25 -0
  78. package/runtime/pi/.codex/skills/view/SKILL.md +156 -0
  79. package/runtime/pi/.codex/skills/view/agents/openai.yaml +4 -0
  80. package/runtime/pi/.codex/skills/view/assets/compose-scaffold.html +221 -0
  81. package/runtime/pi/.codex/skills/visual-explain/SKILL.md +41 -0
  82. package/runtime/pi/.codex/skills/wrap/SKILL.md +55 -0
  83. package/runtime/pi/.codex/spec-manifest.json +114 -0
  84. package/runtime/pi/.pi/APPEND_SYSTEM.md +255 -0
  85. package/runtime/pi/.pi/extensions/egregore.ts +324 -0
  86. package/runtime/pi/.pi/settings.json +6 -0
  87. package/runtime/pi/.pi/spec-manifest.json +12 -0
  88. package/runtime/pi/AGENTS.md +350 -0
  89. package/runtime/pi/bin/activity-data.sh +242 -0
  90. package/runtime/pi/bin/agent.sh +677 -0
  91. package/runtime/pi/bin/codex-install-skills.sh +78 -0
  92. package/runtime/pi/bin/codex-render-spec.mjs +394 -0
  93. package/runtime/pi/bin/codex-session-start.sh +188 -0
  94. package/runtime/pi/bin/codex-skill-render.mjs +323 -0
  95. package/runtime/pi/bin/codex-sync-skills.sh +227 -0
  96. package/runtime/pi/bin/dashboard-data.sh +178 -0
  97. package/runtime/pi/bin/graph-batch.sh +60 -0
  98. package/runtime/pi/bin/graph-op.sh +431 -0
  99. package/runtime/pi/bin/graph-wal.sh +228 -0
  100. package/runtime/pi/bin/graph.sh +177 -0
  101. package/runtime/pi/bin/handoff-pr-backfill.sh +86 -0
  102. package/runtime/pi/bin/handoff-run.sh +581 -0
  103. package/runtime/pi/bin/handoff-save-egregore.sh +136 -0
  104. package/runtime/pi/bin/index-handoff.sh +312 -0
  105. package/runtime/pi/bin/lib/artifact-id.sh +46 -0
  106. package/runtime/pi/bin/lib/config.sh +71 -0
  107. package/runtime/pi/bin/lib/context.sh +542 -0
  108. package/runtime/pi/bin/lib/dashboard-artifact.sh +142 -0
  109. package/runtime/pi/bin/lib/ensure-develop.sh +183 -0
  110. package/runtime/pi/bin/lib/git-sync.sh +288 -0
  111. package/runtime/pi/bin/lib/greeting.sh +340 -0
  112. package/runtime/pi/bin/lib/hash.sh +13 -0
  113. package/runtime/pi/bin/lib/identity.sh +174 -0
  114. package/runtime/pi/bin/lib/metrics.sh +374 -0
  115. package/runtime/pi/bin/lib/permissions.sh +106 -0
  116. package/runtime/pi/bin/lib/time.sh +56 -0
  117. package/runtime/pi/bin/lib/worktree-links.sh +54 -0
  118. package/runtime/pi/bin/notify.sh +207 -0
  119. package/runtime/pi/bin/pi-branch-policy.mjs +15 -0
  120. package/runtime/pi/bin/pi-product-workflows.mjs +324 -0
  121. package/runtime/pi/bin/pi-render-spec.mjs +114 -0
  122. package/runtime/pi/bin/pi-session-start.sh +24 -0
  123. package/runtime/pi/bin/publish-artifact.sh +201 -0
  124. package/runtime/pi/bin/publish-references.sh +144 -0
  125. package/runtime/pi/bin/repo-state.sh +99 -0
  126. package/runtime/pi/bin/telemetry.sh +391 -0
  127. package/runtime/pi/bin/worktree-create.sh +118 -0
  128. package/runtime/pi/bin/worktree-remove.sh +50 -0
  129. package/runtime/pi/bin/worktree.sh +198 -0
  130. package/runtime/pi/manifest.json +176 -0
@@ -0,0 +1,340 @@
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, REPOS_STATUS, 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 addressed to you (one summary line — /answer to engage)
71
+ PENDING_Q=$(cat "$CTX_DIR/pending_questions" 2>/dev/null || echo "[]")
72
+ PENDING_Q_COUNT=$(echo "$PENDING_Q" | jq 'length' 2>/dev/null || echo "0")
73
+ if [ "$PENDING_Q_COUNT" -gt 0 ] 2>/dev/null && [ "$PENDING_Q_COUNT" != "0" ]; then
74
+ PQ_SENDERS=$(echo "$PENDING_Q" | jq -r '[.[].from] | unique | join(", ")' 2>/dev/null)
75
+ if [ "$PENDING_Q_COUNT" = "1" ]; then
76
+ PQ_NOUN="question"
77
+ else
78
+ PQ_NOUN="questions"
79
+ fi
80
+ if [ -n "$PQ_SENDERS" ]; then
81
+ printf " ◐ %s pending %s from %s — /answer to engage\n" "$PENDING_Q_COUNT" "$PQ_NOUN" "$PQ_SENDERS"
82
+ else
83
+ printf " ◐ %s pending %s — /answer to engage\n" "$PENDING_Q_COUNT" "$PQ_NOUN"
84
+ fi
85
+ fi
86
+
87
+ # Show auto-save notice if work was committed from a previous branch
88
+ if [ -n "$SAVED_BRANCH" ]; then
89
+ echo " ✓ Auto-saved uncommitted work on $SAVED_BRANCH"
90
+ fi
91
+
92
+ # --- Footer: health + repos + memory (tertiary) ---
93
+ echo "$SEPARATOR"
94
+
95
+ # Build compact footer line
96
+ if [ "$LOCAL_MODE" = "true" ]; then
97
+ # Local mode: only check github + git, skip api-key/graph/telegram
98
+ HAS_FAILURE="false"
99
+ FAILED_SERVICES=""
100
+ for pair in "github:$HEALTH_GITHUB" "git:$HEALTH_GIT"; do
101
+ svc="${pair%%:*}"
102
+ svc_status="${pair#*:}"
103
+ if [ "$svc_status" = "fail" ]; then
104
+ HAS_FAILURE="true"
105
+ FAILED_SERVICES="${FAILED_SERVICES} ${svc} ✗"
106
+ fi
107
+ done
108
+
109
+ if [ "$HAS_FAILURE" = "true" ]; then
110
+ echo " ⚠${FAILED_SERVICES} — run /checkup"
111
+ else
112
+ if [ "${FRAMEWORK_UPDATED:-false}" = "true" ]; then
113
+ printf " ◆ updated\n"
114
+ else
115
+ printf " ✓ ready\n"
116
+ fi
117
+
118
+ # Managed repos on separate line
119
+ if [ -n "$REPOS_STATUS" ]; then
120
+ REPOS_COMPACT=$(printf '%s' "$REPOS_STATUS" | sed 's/^ ◇ //;s/^[[:space:]]*//' | paste -sd' ' - | sed 's/[[:space:]]*$//')
121
+ if [ -n "$REPOS_COMPACT" ]; then
122
+ printf " %s\n" "$REPOS_COMPACT"
123
+ fi
124
+ fi
125
+ fi
126
+ else
127
+ # Connected mode: check all services
128
+ HAS_FAILURE="false"
129
+ FAILED_SERVICES=""
130
+ for pair in "github:$HEALTH_GITHUB" "git:$HEALTH_GIT" "api-key:$HEALTH_APIKEY" "graph:$HEALTH_GRAPH" "telegram:$HEALTH_TELEGRAM"; do
131
+ svc="${pair%%:*}"
132
+ svc_status="${pair#*:}"
133
+ if [ "$svc_status" = "fail" ]; then
134
+ HAS_FAILURE="true"
135
+ FAILED_SERVICES="${FAILED_SERVICES} ${svc} ✗"
136
+ fi
137
+ done
138
+
139
+ if [ "$HAS_FAILURE" = "true" ]; then
140
+ echo " ⚠${FAILED_SERVICES} — run /checkup"
141
+ else
142
+ # Compact footer: ready + memory on one line, repos on next if present
143
+ FOOTER_LEFT=" ✓ ready"
144
+ FOOTER_RIGHT=""
145
+ if [ "$MEMORY_SYNCED" = "true" ]; then
146
+ FOOTER_RIGHT="◆ memory synced"
147
+ fi
148
+
149
+ FL_LEN=${#FOOTER_LEFT}
150
+ FR_LEN=${#FOOTER_RIGHT}
151
+ F_PAD=$((LINE_WIDTH - FL_LEN - FR_LEN))
152
+ if [ "$F_PAD" -lt 1 ]; then F_PAD=1; fi
153
+ printf "%s%*s%s\n" "$FOOTER_LEFT" "$F_PAD" "" "$FOOTER_RIGHT"
154
+
155
+ # Managed repos on separate line (avoids line overflow)
156
+ if [ -n "$REPOS_STATUS" ]; then
157
+ REPOS_COMPACT=$(printf '%s' "$REPOS_STATUS" | sed 's/^ ◇ //;s/^[[:space:]]*//' | paste -sd' ' - | sed 's/[[:space:]]*$//')
158
+ if [ -n "$REPOS_COMPACT" ]; then
159
+ printf " %s\n" "$REPOS_COMPACT"
160
+ fi
161
+ fi
162
+ fi
163
+ fi
164
+
165
+ # Dashboard link (OSC 8 hyperlink — clickable text in supported terminals)
166
+ if [ -n "${DASHBOARD_URL:-}" ]; then
167
+ printf " ◆ %s\n" "$DASHBOARD_URL"
168
+ fi
169
+
170
+ if [ -n "${BOARD_URL:-}" ]; then
171
+ printf " ◆ %s (board)\n" "$BOARD_URL"
172
+ fi
173
+
174
+ if [ -n "${LOOM_DOCTOR_BRIEF:-}" ]; then
175
+ printf " %s\n" "$LOOM_DOCTOR_BRIEF"
176
+ fi
177
+
178
+ # Framework updates come through PRs to develop — no separate auto-update channel.
179
+ # The develop sync above already keeps framework files current.
180
+ # Use /update for manual upstream pulls when needed.
181
+
182
+ # --- One-time migration: fix aliases to use 'claude "start"' ---
183
+ # v1: 'claude start' → 'claude' (cross-instance bug)
184
+ # v2: 'claude' → 'claude "start"' (blank prompt — auto-sends first message)
185
+ ALIAS_VERSION=$(jq -r '.alias_version // 0' "$STATE_FILE" 2>/dev/null || echo "0")
186
+ if [ "$ALIAS_VERSION" -lt 2 ] 2>/dev/null; then
187
+ for profile in "$HOME/.zshrc" "$HOME/.bashrc" "$HOME/.bash_profile"; do
188
+ if [ -f "$profile" ] && grep -q "$SCRIPT_DIR" "$profile" 2>/dev/null; then
189
+ # Replace any egregore alias pointing to this directory with the correct command
190
+ # Handles: && claude start, && claude, && claude "start" (idempotent)
191
+ sed -i.bak "s|&& claude start|\\&\\& claude \"start\"|g; s|&& claude'|\\&\\& claude \"start\"'|g" "$profile" 2>/dev/null && rm -f "${profile}.bak" || true
192
+ fi
193
+ done
194
+ if [ -f "$STATE_FILE" ] && jq . "$STATE_FILE" >/dev/null 2>&1; then
195
+ jq '. + {"alias_fixed": true, "alias_version": 2}' "$STATE_FILE" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE"
196
+ fi
197
+ fi
198
+
199
+ echo ""
200
+
201
+ # --- Session context (hidden, for Claude) ---
202
+ CONTEXT_HANDOFFS=$(cat "$CTX_DIR/handoffs" 2>/dev/null || echo "[]")
203
+ CONTEXT_ADDRESSED=$(cat "$CTX_DIR/addressed" 2>/dev/null || echo "[]")
204
+ CONTEXT_QUESTS=$(cat "$CTX_DIR/quests" 2>/dev/null || echo "[]")
205
+ CONTEXT_ACTIVITY=$(cat "$CTX_DIR/activity" 2>/dev/null || echo "")
206
+ CONTEXT_TEAM=$(cat "$CTX_DIR/team" 2>/dev/null || echo "[]")
207
+ CONTEXT_SOUL=$(cat "$CTX_DIR/soul_summary" 2>/dev/null || echo "")
208
+ CONTEXT_LIFECYCLE=$(cat "$CTX_DIR/lifecycle" 2>/dev/null || echo '{"merged_prs":[],"implemented_handoffs":[]}')
209
+ CONTEXT_PULSE=$(cat "$CTX_DIR/pulse_brief" 2>/dev/null || echo '{}')
210
+ if [ -f "$CTX_DIR/metrics" ]; then
211
+ CONTEXT_METRICS=$(jq -c . "$CTX_DIR/metrics" 2>/dev/null || echo "{}")
212
+ [ -n "$CONTEXT_METRICS" ] || CONTEXT_METRICS="{}"
213
+ else
214
+ CONTEXT_METRICS="{}"
215
+ fi
216
+
217
+ # --- Write compact subagent context cache (reuses already-gathered data) ---
218
+ SUBAGENT_CTX="/tmp/egregore-subagent-ctx-${EGREGORE_SESSION_ID}.txt"
219
+ (
220
+ SA_ORG_NAME=$(jq -r '.org_name // "Unknown"' "$SCRIPT_DIR/egregore.json" 2>/dev/null || echo "Unknown")
221
+ SA_GITHUB_ORG=$(jq -r '.github_org // "unknown"' "$SCRIPT_DIR/egregore.json" 2>/dev/null || echo "unknown")
222
+
223
+ # Format quests list
224
+ SA_QUESTS="none"
225
+ if [ "$CONTEXT_QUESTS" != "[]" ]; then
226
+ SA_QUESTS=$(echo "$CONTEXT_QUESTS" | jq -r '.[]' 2>/dev/null | paste -sd', ' - 2>/dev/null || echo "none")
227
+ fi
228
+
229
+ # Format recent handoffs
230
+ SA_HANDOFFS="none"
231
+ if [ "$CONTEXT_HANDOFFS" != "[]" ]; then
232
+ SA_HANDOFFS=$(echo "$CONTEXT_HANDOFFS" | jq -r '.[] | .name' 2>/dev/null | head -5 | paste -sd', ' - 2>/dev/null || echo "none")
233
+ fi
234
+
235
+ cat > "$SUBAGENT_CTX" << SAEOF
236
+ <!-- egregore-context
237
+ Organization: $SA_ORG_NAME (github: $SA_GITHUB_ORG)
238
+ Session: $BRANCH — ${EGREGORE_SESSION_ID}
239
+ Active quests: $SA_QUESTS
240
+ Recent handoffs: $SA_HANDOFFS
241
+ Memory: memory/ is a symlink to shared knowledge base. Use bin/graph.sh for Neo4j queries.
242
+ -->
243
+ SAEOF
244
+ ) 2>/dev/null || true
245
+
246
+ cat << CTXEOF
247
+
248
+ <!-- session-context
249
+ {
250
+ "framework_version": "$FRAMEWORK_VERSION",
251
+ "time_of_day": "$TIME_OF_DAY",
252
+ "dashboard_url": "${DASHBOARD_URL:-}",
253
+ "recent_handoffs": $CONTEXT_HANDOFFS,
254
+ "addressed_to_user": $CONTEXT_ADDRESSED,
255
+ "quests": $CONTEXT_QUESTS,
256
+ "last_user_activity": "$CONTEXT_ACTIVITY",
257
+ "team_recent_memory": $CONTEXT_TEAM,
258
+ "soul_self_summary": "$CONTEXT_SOUL",
259
+ "lifecycle": $CONTEXT_LIFECYCLE,
260
+ "momentum": $CONTEXT_METRICS,
261
+ "pulse": $CONTEXT_PULSE
262
+ }
263
+ -->
264
+ CTXEOF
265
+
266
+ # Include soul file if present
267
+ if [ -f "$SCRIPT_DIR/egregore.md" ]; then
268
+ echo ""
269
+ echo "<!-- egregore-soul"
270
+ cat "$SCRIPT_DIR/egregore.md"
271
+ echo "-->"
272
+ fi
273
+
274
+ # Include latest soul reflection if present
275
+ if [ -d "$SCRIPT_DIR/memory/soul" ]; then
276
+ LATEST_REFLECTION=$(ls -t "$SCRIPT_DIR/memory/soul/"*.md 2>/dev/null | head -1 || true)
277
+ if [ -n "$LATEST_REFLECTION" ]; then
278
+ echo "<!-- latest-reflection"
279
+ cat "$LATEST_REFLECTION"
280
+ echo "-->"
281
+ fi
282
+ fi
283
+
284
+ # --- Flush leftover telemetry from previous session (background) ---
285
+ # If the previous session exited without a clean flush (terminal closed,
286
+ # crash, kill -9), events are still in the local buffer. Drain them now.
287
+ bash "$SCRIPT_DIR/bin/telemetry.sh" flush 2>/dev/null &
288
+
289
+ # --- Emit session_start telemetry (background, non-blocking) ---
290
+ bash "$SCRIPT_DIR/bin/telemetry.sh" emit "session_start" \
291
+ "$(jq -n --arg branch "$BRANCH" '{branch: $branch}')" 2>/dev/null &
292
+
293
+ # --- Health check-in (background, non-blocking) ---
294
+ bash "$SCRIPT_DIR/bin/startup-check.sh" >/dev/null 2>&1 &
295
+
296
+ # --- Warm npx cache for egregore-artifacts (background, throttled) ---
297
+ # publish-artifact.sh spawns `npx egregore-artifacts` — first run in a
298
+ # fresh npx cache pays a 5-10s npm download. Warmup is throttled to
299
+ # once per hour via a marker file so we don't storm npm when a user
300
+ # opens many tabs, and don't start runaway retry loops when offline.
301
+ _WARM_TS_FILE="$HOME/.cache/egregore/npx-warm-ts"
302
+ mkdir -p "$(dirname "$_WARM_TS_FILE")" 2>/dev/null
303
+ _WARM_LAST=$(cat "$_WARM_TS_FILE" 2>/dev/null || echo 0)
304
+ _WARM_NOW=$(date +%s)
305
+ if [ $(( _WARM_NOW - _WARM_LAST )) -gt 3600 ]; then
306
+ (npx -y egregore-artifacts@latest --help >/dev/null 2>&1 \
307
+ && echo "$_WARM_NOW" > "$_WARM_TS_FILE" &) >/dev/null 2>&1
308
+ fi
309
+
310
+ # Clean up temp files
311
+ rm -rf "$CTX_DIR"
312
+
313
+ # --- First session welcome ---
314
+ if [ -z "$FIRST_SESSION" ] && [ -f "$STATE_FILE" ]; then
315
+ FIRST_SESSION=$(jq -r '.first_session // false' "$STATE_FILE" 2>/dev/null)
316
+ fi
317
+
318
+ if [ "$FIRST_SESSION" = "true" ]; then
319
+ echo ""
320
+ echo " Welcome! This is your first session."
321
+ echo ""
322
+ 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."
323
+ # Clear the flag so it only shows once
324
+ jq '.first_session = false' "$STATE_FILE" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE"
325
+ else
326
+ # --- Tutorial tip (if onboarding done but tutorial not) ---
327
+ TUTORIAL_COMPLETE="true"
328
+ if [ -f "$STATE_FILE" ]; then
329
+ TUTORIAL_COMPLETE=$(jq -r '.tutorial_complete // false' "$STATE_FILE" 2>/dev/null || echo "true")
330
+ fi
331
+
332
+ if [ "$TUTORIAL_COMPLETE" != "true" ]; then
333
+ echo " Tip: Run /tutorial to learn the core loop."
334
+ fi
335
+
336
+ echo ""
337
+ 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?"
338
+ echo ""
339
+ 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."
340
+ fi
@@ -0,0 +1,13 @@
1
+ #!/bin/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
+ }
@@ -0,0 +1,174 @@
1
+ # shellcheck shell=bash
2
+ # identity.sh — User identity resolution for session-start.sh
3
+ #
4
+ # Resolves the current user's identity through multiple fallback layers:
5
+ # 1. .egregore-state.json github_username (stored during setup)
6
+ # 2. GitHub API auto-detect via GITHUB_TOKEN
7
+ # 3. Graph person lookup (existing team member on new machine)
8
+ # 4. Global git config user.name (last resort)
9
+ #
10
+ # Inputs: STATE_FILE, ENV_FILE, SCRIPT_DIR, HEALTH_GITHUB
11
+ # Outputs: AUTHOR, DISPLAY_NAME_STATE, FIRST_SESSION, HEALTH_GITHUB,
12
+ # EGREGORE_USER, EGREGORE_ORG, EGREGORE_SESSION_ID
13
+ # Writes .egregore-session-id and ~/.egregore/session-*.id
14
+
15
+ STORED_USERNAME=""
16
+ FIRST_SESSION=""
17
+ AUTHOR=""
18
+ if [ -f "$STATE_FILE" ]; then
19
+ STORED_USERNAME=$(jq -r '.github_username // empty' "$STATE_FILE" 2>/dev/null)
20
+ fi
21
+
22
+ DISPLAY_NAME_STATE=""
23
+ if [ -f "$STATE_FILE" ]; then
24
+ DISPLAY_NAME_STATE=$(jq -r '.display_name // empty' "$STATE_FILE" 2>/dev/null)
25
+ fi
26
+
27
+ if [ -n "$STORED_USERNAME" ]; then
28
+ # Identity stored during setup — use it and ensure repo-local git config matches
29
+ AUTHOR="$STORED_USERNAME"
30
+ HEALTH_GITHUB="ok"
31
+ CURRENT_LOCAL=$(git config --local user.name 2>/dev/null || echo "")
32
+ if [ "$CURRENT_LOCAL" != "$STORED_USERNAME" ]; then
33
+ STORED_NAME=$(jq -r '.github_name // empty' "$STATE_FILE" 2>/dev/null)
34
+ git config user.name "${STORED_NAME:-$STORED_USERNAME}" 2>/dev/null || true
35
+ git config user.email "${STORED_USERNAME}@users.noreply.github.com" 2>/dev/null || true
36
+ fi
37
+ else
38
+ # No stored identity — try GitHub API to auto-detect (self-healing for pre-fix installs)
39
+ if [ -f "$ENV_FILE" ]; then
40
+ GH_TOKEN=$(grep '^GITHUB_TOKEN=' "$ENV_FILE" 2>/dev/null | cut -d'=' -f2-)
41
+ if [ -n "$GH_TOKEN" ]; then
42
+ GH_USER_JSON=$(curl -s -H "Authorization: token $GH_TOKEN" https://api.github.com/user --max-time 5 2>/dev/null || echo "")
43
+ GH_LOGIN=$(echo "$GH_USER_JSON" | jq -r '.login // empty' 2>/dev/null)
44
+ GH_NAME=$(echo "$GH_USER_JSON" | jq -r '.name // empty' 2>/dev/null)
45
+ if [ -n "$GH_LOGIN" ]; then
46
+ AUTHOR="$GH_LOGIN"
47
+ HEALTH_GITHUB="ok"
48
+ # Set repo-local config and save to state for next time
49
+ git config user.name "${GH_NAME:-$GH_LOGIN}" 2>/dev/null || true
50
+ git config user.email "${GH_LOGIN}@users.noreply.github.com" 2>/dev/null || true
51
+ # Save to state file so we don't need API call next time
52
+ # Include onboarding_complete + name so it doesn't re-trigger onboarding
53
+ # Determine if founder or joiner: if github_username != github_org, they're a joiner
54
+ GITHUB_ORG_CFG=$(jq -r '.github_org // empty' "$SCRIPT_DIR/egregore.json" 2>/dev/null)
55
+ if [ -n "$GITHUB_ORG_CFG" ] && [ "$GH_LOGIN" != "$GITHUB_ORG_CFG" ]; then
56
+ USAGE_TYPE="joiner_group"
57
+ else
58
+ USAGE_TYPE="founder_group"
59
+ fi
60
+ if [ -f "$STATE_FILE" ]; then
61
+ # Existing state file — update identity but preserve onboarding status
62
+ HAS_ONBOARDING=$(jq -r '.onboarding_complete // "unset"' "$STATE_FILE" 2>/dev/null)
63
+ if [ "$HAS_ONBOARDING" = "unset" ]; then
64
+ # State file exists but no onboarding flag — set to false to trigger it
65
+ jq --arg u "$GH_LOGIN" --arg n "${GH_NAME:-$GH_LOGIN}" --arg ut "$USAGE_TYPE" \
66
+ '.github_username = $u | .github_name = $n | .name = $n | .onboarding_complete = false | .usage_type = $ut' "$STATE_FILE" > "$STATE_FILE.tmp" \
67
+ && mv "$STATE_FILE.tmp" "$STATE_FILE"
68
+ else
69
+ jq --arg u "$GH_LOGIN" --arg n "${GH_NAME:-$GH_LOGIN}" --arg ut "$USAGE_TYPE" \
70
+ '.github_username = $u | .github_name = $n | .name = $n | .usage_type = $ut' "$STATE_FILE" > "$STATE_FILE.tmp" \
71
+ && mv "$STATE_FILE.tmp" "$STATE_FILE"
72
+ fi
73
+ FIRST_SESSION="false"
74
+ else
75
+ # No state file — check graph to see if this person already exists
76
+ # (handles fresh clone / new machine for existing team members)
77
+ GRAPH_PERSON=""
78
+ GRAPH_DISPLAY_NAME=""
79
+ GRAPH_PERSON=$(bash "$SCRIPT_DIR/bin/graph.sh" query \
80
+ "MATCH (p:Person {github: \$github}) RETURN p.name AS name" \
81
+ "$(jq -n --arg github "$GH_LOGIN" '{github: $github}')" 2>/dev/null || echo "")
82
+ if echo "$GRAPH_PERSON" | jq -e '.values | length > 0' &>/dev/null; then
83
+ GRAPH_DISPLAY_NAME=$(echo "$GRAPH_PERSON" | jq -r '.values[0][0] // empty' 2>/dev/null)
84
+ # Existing team member — skip onboarding
85
+ cat > "$STATE_FILE" << STATEEOF
86
+ {
87
+ "github_username": "$GH_LOGIN",
88
+ "github_name": "${GH_NAME:-$GH_LOGIN}",
89
+ "name": "${GRAPH_DISPLAY_NAME:-${GH_NAME:-$GH_LOGIN}}",
90
+ "display_name": "${GRAPH_DISPLAY_NAME:-}",
91
+ "onboarding_complete": true,
92
+ "usage_type": "$USAGE_TYPE",
93
+ "session_tracking": true,
94
+ "transcript_sharing": true,
95
+ "telemetry": true,
96
+ "contact_preference": "all",
97
+ "telemetry_noticed": true
98
+ }
99
+ STATEEOF
100
+ FIRST_SESSION="false"
101
+ else
102
+ # Genuinely new user — trigger onboarding
103
+ cat > "$STATE_FILE" << STATEEOF
104
+ {
105
+ "github_username": "$GH_LOGIN",
106
+ "github_name": "${GH_NAME:-$GH_LOGIN}",
107
+ "name": "${GH_NAME:-$GH_LOGIN}",
108
+ "onboarding_complete": false,
109
+ "usage_type": "$USAGE_TYPE",
110
+ "first_session": true
111
+ }
112
+ STATEEOF
113
+ FIRST_SESSION="true"
114
+ fi
115
+ fi
116
+ fi
117
+ fi
118
+ fi
119
+
120
+ # Final fallback: git config user.name (global or local)
121
+ if [ -z "$AUTHOR" ]; then
122
+ FULLNAME=$(git config user.name 2>/dev/null || echo "")
123
+ AUTHOR=$(echo "$FULLNAME" | tr '[:upper:]' '[:lower:]' | cut -d' ' -f1)
124
+ fi
125
+ fi
126
+
127
+ if [ -z "$AUTHOR" ]; then
128
+ AUTHOR="unknown"
129
+ HEALTH_GITHUB="fail"
130
+ fi
131
+
132
+ # --- Validate gh CLI identity matches git identity ---
133
+ # The gh CLI has its own auth (keyring) separate from .env GITHUB_TOKEN.
134
+ # If they diverge, PRs get created under the wrong account.
135
+ if command -v gh &>/dev/null; then
136
+ GH_CLI_USER=$(gh api user --jq '.login' 2>/dev/null || true)
137
+ if [ -n "$GH_CLI_USER" ] && [ -n "$AUTHOR" ] && [ "$AUTHOR" != "unknown" ]; then
138
+ AUTHOR_LC=$(echo "$AUTHOR" | tr '[:upper:]' '[:lower:]')
139
+ GH_CLI_LC=$(echo "$GH_CLI_USER" | tr '[:upper:]' '[:lower:]')
140
+ if [ "$AUTHOR_LC" != "$GH_CLI_LC" ]; then
141
+ echo ""
142
+ echo "WARNING: Identity mismatch detected."
143
+ echo " Git identity (.env): $AUTHOR"
144
+ echo " gh CLI (active): $GH_CLI_USER"
145
+ echo " PRs will be created as '$GH_CLI_USER', not '$AUTHOR'."
146
+ echo " Fix: gh auth switch --user $AUTHOR"
147
+ echo ""
148
+ fi
149
+ fi
150
+ fi
151
+
152
+ # --- Export telemetry identity env vars ---
153
+ export EGREGORE_USER="$AUTHOR"
154
+ export EGREGORE_ORG="$(jq -r '.slug // .github_org // empty' "$SCRIPT_DIR/egregore.json" 2>/dev/null || true)"
155
+ export EGREGORE_SESSION_ID="$(date -u +%Y%m%dT%H%M%S)-${AUTHOR}-$$"
156
+
157
+ # Persist session ID to file so telemetry.sh can read it
158
+ # (env vars from hooks don't propagate into the Claude Code agent)
159
+ SESSION_ID_DIR="$HOME/.egregore"
160
+ mkdir -p "$SESSION_ID_DIR"
161
+ PROJ_HASH=$(echo -n "$SCRIPT_DIR" | md5 2>/dev/null || echo -n "$SCRIPT_DIR" | md5sum 2>/dev/null | cut -d' ' -f1)
162
+ echo "$EGREGORE_SESSION_ID" > "$SESSION_ID_DIR/session-${PROJ_HASH}.id"
163
+ # Also write project-local file (CLAUDE.md reads this — no glob needed)
164
+ echo "$EGREGORE_SESSION_ID" > "$SCRIPT_DIR/.egregore-session-id"
165
+
166
+ # Model stamp hashes the git common-dir root because telemetry.sh resolves it that way.
167
+ MODEL_REPO_ROOT=$(git -C "$SCRIPT_DIR" rev-parse --path-format=absolute --git-common-dir 2>/dev/null | sed 's|/\.git$||') || MODEL_REPO_ROOT="$SCRIPT_DIR"
168
+ MODEL_PROJ_HASH=$(echo -n "$MODEL_REPO_ROOT" | md5 2>/dev/null || echo -n "$MODEL_REPO_ROOT" | md5sum 2>/dev/null | cut -d' ' -f1)
169
+ MODEL_STAMP_FILE="$SESSION_ID_DIR/session-model-${MODEL_PROJ_HASH}"
170
+ if [ -n "${CLAUDE_MODEL:-}" ]; then
171
+ echo "$CLAUDE_MODEL" > "$MODEL_STAMP_FILE"
172
+ else
173
+ rm -f "$MODEL_STAMP_FILE"
174
+ fi