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
@@ -6,7 +6,7 @@ set -o pipefail
6
6
  # libraries as Claude's SessionStart, then renders a terminal greeting before
7
7
  # Codex itself starts.
8
8
 
9
- FRAMEWORK_VERSION="3"
9
+ FRAMEWORK_VERSION="6"
10
10
  SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
11
11
  cd "$SCRIPT_DIR" || exit 1
12
12
 
@@ -56,6 +56,7 @@ fi
56
56
 
57
57
  source "$SCRIPT_DIR/bin/lib/git-sync.sh" >/dev/null 2>/dev/null
58
58
  source "$SCRIPT_DIR/bin/lib/context.sh" >/dev/null 2>/dev/null
59
+ source "$SCRIPT_DIR/bin/lib/metrics.sh" >/dev/null 2>/dev/null
59
60
  trap 'rm -rf "${CTX_DIR:-}"' EXIT
60
61
 
61
62
  read_json() {
@@ -69,23 +70,6 @@ read_json() {
69
70
  fi
70
71
  }
71
72
 
72
- shorten() {
73
- local value="$1"
74
- local max="$2"
75
- if command -v jq >/dev/null 2>&1; then
76
- jq -nr --arg value "$value" --argjson max "$max" '
77
- $value
78
- | if length > $max then .[0:($max - 3)] + "..." else . end
79
- '
80
- return
81
- fi
82
- if [ "${#value}" -gt "$max" ]; then
83
- printf '%s...\n' "${value:0:$((max - 3))}"
84
- else
85
- printf '%s\n' "$value"
86
- fi
87
- }
88
-
89
73
  ORG_NAME="$(read_json "$CONFIG" '.org_name' '')"
90
74
  [ -z "$ORG_NAME" ] && ORG_NAME="$(read_json "$CONFIG" '.slug' 'Egregore')"
91
75
  GITHUB_ORG="$(read_json "$CONFIG" '.github_org' '')"
@@ -96,14 +80,12 @@ DISPLAY_NAME="$(read_json "$STATE_FILE" '.display_name' '')"
96
80
  ONBOARDING_COMPLETE="$(read_json "$STATE_FILE" '.onboarding_complete' '')"
97
81
  [ -z "$ONBOARDING_COMPLETE" ] && ONBOARDING_COMPLETE="unknown"
98
82
 
99
- TEAM_DATA="$(cat "$CTX_DIR/team" 2>/dev/null || echo "[]")"
100
83
  ADDRESSED_RICH="$(cat "$CTX_DIR/addressed_rich" 2>/dev/null || echo "[]")"
101
84
  PENDING_Q="$(cat "$CTX_DIR/pending_questions" 2>/dev/null || echo "[]")"
102
- LAST_ACTIVITY="$(cat "$CTX_DIR/activity" 2>/dev/null || true)"
103
85
  ADDRESSED_COUNT="$(echo "$ADDRESSED_RICH" | jq 'length' 2>/dev/null || echo "0")"
104
86
  PENDING_Q_COUNT="$(echo "$PENDING_Q" | jq 'length' 2>/dev/null || echo "0")"
105
87
 
106
- render_for_you_summary() {
88
+ render_pending_line() {
107
89
  if [ "$PENDING_Q_COUNT" -gt 0 ] 2>/dev/null; then
108
90
  local senders noun
109
91
  senders="$(echo "$PENDING_Q" | jq -r '[.[].from] | unique | join(", ")' 2>/dev/null)"
@@ -115,41 +97,9 @@ render_for_you_summary() {
115
97
  fi
116
98
  elif [ "$ADDRESSED_COUNT" -gt 0 ] 2>/dev/null; then
117
99
  printf ' ◇ %s handoffs for %s - bin/agent.sh activity --for %s\n' "$ADDRESSED_COUNT" "$AUTHOR" "$AUTHOR"
118
- else
119
- printf ' ◇ nothing pending for %s\n' "$AUTHOR"
120
- fi
121
- }
122
-
123
- render_handoff_rows() {
124
- if [ "$ADDRESSED_COUNT" -gt 0 ] 2>/dev/null; then
125
- echo "$ADDRESSED_RICH" | jq -r '.[] | [.author, .date, .topic] | @tsv' 2>/dev/null | head -4 | while IFS=$'\t' read -r author date topic; do
126
- [ -z "$author" ] && continue
127
- printf ' ○ %-10s%-12s%s\n' "$(shorten "$author" 9)" "$date" "$(shorten "$topic" 40)"
128
- done
129
- elif [ -n "$LAST_ACTIVITY" ]; then
130
- local last_when last_msg
131
- last_when="$(printf '%s\n' "$LAST_ACTIVITY" | cut -d'|' -f1 | sed 's/ hours\{0,1\} ago/h ago/; s/ days\{0,1\} ago/d ago/; s/ minutes\{0,1\} ago/m ago/')"
132
- last_msg="$(printf '%s\n' "$LAST_ACTIVITY" | cut -d'|' -f2-)"
133
- printf ' ◇ %-10s%-12s%s\n' "you" "$last_when" "$(shorten "$last_msg" 40)"
134
100
  fi
135
101
  }
136
102
 
137
- render_team_rows() {
138
- local now_render online_threshold
139
- now_render=$(date +%s)
140
- online_threshold=7200
141
- echo "$TEAM_DATA" | jq -r '.[] | [.name, .last_seen_sort, .last_seen, (.branches | join(", "))] | @tsv' 2>/dev/null | head -4 | while IFS=$'\t' read -r name epoch seen branches; do
142
- [ -z "$name" ] && continue
143
- local dot
144
- if [ "$epoch" -gt 0 ] 2>/dev/null && [ $((now_render - epoch)) -lt "$online_threshold" ] 2>/dev/null; then
145
- dot="●"
146
- else
147
- dot="○"
148
- fi
149
- printf ' %s %-10s%-12s%s\n' "$dot" "$(shorten "$name" 9)" "$seen" "$(shorten "$branches" 40)"
150
- done
151
- }
152
-
153
103
  banner() {
154
104
  cat <<'EOF'
155
105
 
@@ -175,12 +125,8 @@ render_card() {
175
125
  banner
176
126
  printf '%s%*s%s\n' "$identity_left" "$pad" "" "$identity_right"
177
127
  printf '%s\n' "$separator"
178
- printf ' ◦ for you\n'
179
- render_for_you_summary
180
- render_handoff_rows
181
- printf '\n'
182
- printf ' ◦ around\n'
183
- render_team_rows
128
+ SEPARATOR="$separator" LINE_WIDTH=$line_width _render_momentum_board
129
+ render_pending_line
184
130
  printf '%s\n' "$separator"
185
131
 
186
132
  has_failure="false"
@@ -72,9 +72,10 @@ function truncate(value, max) {
72
72
  }
73
73
 
74
74
  function padLine(left, right = '') {
75
- const l = truncate(left, WIDTH - 4);
76
- const r = truncate(right, WIDTH - 4);
77
- const space = Math.max(1, WIDTH - 4 - l.length - r.length);
75
+ const inner = WIDTH - 4;
76
+ const r = right ? truncate(right, Math.min(18, inner - 1)) : '';
77
+ const l = truncate(left, inner - (r ? r.length + 1 : 0));
78
+ const space = Math.max(0, inner - l.length - r.length);
78
79
  return `│ ${l}${' '.repeat(space)}${r} │`;
79
80
  }
80
81
 
@@ -149,7 +150,8 @@ function renderActivity(data) {
149
150
  const mine = firstArray(data, ['local_sessions.my_sessions', 'my_sessions', 'sessions']);
150
151
  const team = firstArray(data, ['local_sessions.team_sessions', 'team_sessions', 'team.sessions']);
151
152
 
152
- const lines = [topRule(), padLine('ACTIVITY', [org, me, date].filter(Boolean).join(' · ')), rule()];
153
+ const brand = `${truncate(org, 20).toUpperCase()} EGREGORE ✦ ACTIVITY DASHBOARD`;
154
+ const lines = [topRule(), padLine(brand), padLine([me, date].filter(Boolean).join(' · ')), rule()];
153
155
  if (graph.status !== 'connected') {
154
156
  lines.push(padLine(`graph: ${graph.status}`, graph.reason));
155
157
  lines.push(rule());
@@ -290,7 +292,7 @@ function renderHandoff(data) {
290
292
  data.notifyStatus ? `notify=${data.notifyStatus}` : '',
291
293
  ].filter(Boolean).join(' · ');
292
294
 
293
- const lines = [topRule(), padLine('HANDOFF', data.author || ''), rule()];
295
+ const lines = [topRule(), padLine('⇌ HANDOFF', data.author || ''), rule()];
294
296
  lines.push(padLine(`Topic: ${data.topic || '(untitled)'}`));
295
297
  if (data.recipient) lines.push(padLine(`To: ${data.recipient}`));
296
298
  if (file) lines.push(padLine(`Path: ${file}`));
@@ -1,7 +1,7 @@
1
1
  # shellcheck shell=bash
2
2
  # context.sh — Context gathering for session-start.sh
3
3
  #
4
- # Runs 10 parallel background subshells to gather session context:
4
+ # Runs 11 parallel background subshells to gather session context:
5
5
  # 1. Recent handoffs
6
6
  # 2. Active quests
7
7
  # 3. User's last git activity
@@ -13,6 +13,7 @@
13
13
  # 8. Telegram health check
14
14
  # 9. Lifecycle events (merged PRs, implemented handoffs)
15
15
  # 10. Last Pulse brief
16
+ # 11. Momentum metrics
16
17
  #
17
18
  # All subshells write to $CTX_DIR (a temp directory).
18
19
  # Ends with `wait` + reading health results back.
@@ -525,6 +526,12 @@ fi
525
526
  fi
526
527
  ) > "$CTX_DIR/pulse_brief" 2>/dev/null &
527
528
 
529
+ # 11. Momentum metrics (background)
530
+ (
531
+ source "$SCRIPT_DIR/bin/lib/metrics.sh"
532
+ _metrics_gather
533
+ ) > "$CTX_DIR/metrics" 2>/dev/null &
534
+
528
535
  # Wait for all context gathering + health checks to finish
529
536
  wait
530
537
 
@@ -4,9 +4,8 @@
4
4
  # Renders the full session greeting:
5
5
  # - ASCII art banner
6
6
  # - Identity line (org/repo + user + branch)
7
- # - "For you" section (handoffs, last activity)
8
- # - "Around" section (team presence)
9
- # - "Merged" section (PRs, implemented handoffs)
7
+ # - Momentum board (sessions, commits, handoffs, knowledge)
8
+ # - Pending questions signal
10
9
  # - Health footer
11
10
  # - Alias migration
12
11
  # - Session context JSON (hidden, for Claude)
@@ -64,53 +63,9 @@ if [ "$ID_PADDING" -lt 1 ]; then ID_PADDING=1; fi
64
63
  printf "\n%s%*s%s\n" "$IDENTITY_LEFT" "$ID_PADDING" "" "$IDENTITY_RIGHT"
65
64
  echo "$SEPARATOR"
66
65
 
67
- # --- Shared rendering state ---
68
- TEAM_DATA=$(cat "$CTX_DIR/team" 2>/dev/null || echo "[]")
69
- TEAM_DATA_COUNT=$(echo "$TEAM_DATA" | jq 'length' 2>/dev/null || echo "0")
70
- NOW_RENDER=$(date +%s)
71
- ONLINE_THRESHOLD=7200 # 2 hours — considered "online"
72
- RECENCY_DAYS=3
73
- RECENCY_THRESHOLD=$((RECENCY_DAYS * 86400))
74
- TODAY_DATE=$(date +%Y-%m-%d)
75
- TODAY_MIDNIGHT=$(date -j -f "%Y-%m-%d" "$TODAY_DATE" +%s 2>/dev/null || \
76
- date -d "$TODAY_DATE" +%s 2>/dev/null || echo "$NOW_RENDER")
77
- # Column layout: 2(indent) + 2(dot+space) + 9(name) + 12(time) + 40(info) = 65
78
- MAX_INFO=40
79
-
80
- # --- Section 1: For you (always present) ---
81
- echo " ◦ for you"
82
- ADDRESSED_RICH=$(cat "$CTX_DIR/addressed_rich" 2>/dev/null || echo "[]")
83
- ADDRESSED_COUNT=$(echo "$ADDRESSED_RICH" | jq 'length' 2>/dev/null || echo "0")
84
-
85
- # Handoffs addressed to you
86
- if [ "$ADDRESSED_COUNT" -gt 0 ] 2>/dev/null && [ "$ADDRESSED_COUNT" != "0" ]; then
87
- echo "$ADDRESSED_RICH" | jq -r '.[] | "\(.author)\t\(.date)\t\(.topic)"' 2>/dev/null | while IFS=$'\t' read -r H_AUTHOR H_DATE H_TOPIC; do
88
- [ -z "$H_AUTHOR" ] && continue
89
- # Convert date to epoch (UTC) and compute calendar-day diff
90
- H_DATE_ONLY="${H_DATE%%T*}"
91
- H_EVENT_MIDNIGHT=$(date -j -f "%Y-%m-%d" "$H_DATE_ONLY" +%s 2>/dev/null || \
92
- date -d "$H_DATE_ONLY" +%s 2>/dev/null || echo "0")
93
- H_CAL_DAYS=$(( (TODAY_MIDNIGHT - H_EVENT_MIDNIGHT) / 86400 ))
94
- # Skip if older than recency threshold
95
- [ "$H_CAL_DAYS" -gt "$RECENCY_DAYS" ] 2>/dev/null && continue
96
- H_NAME=$(echo "$H_AUTHOR" | awk '{print tolower($1)}')
97
- if [ ${#H_NAME} -gt 8 ]; then H_NAME="${H_NAME:0:8}"; fi
98
- # Online status from team presence data
99
- H_SEEN_EPOCH=$(echo "$TEAM_DATA" | jq -r --arg n "$H_NAME" '[.[] | select((.name | ascii_downcase) == $n) | .last_seen_sort] | .[0] // 0' 2>/dev/null || echo "0")
100
- if [ "$H_SEEN_EPOCH" -gt 0 ] 2>/dev/null && [ $(( NOW_RENDER - H_SEEN_EPOCH )) -lt $ONLINE_THRESHOLD ] 2>/dev/null; then
101
- DOT="●"
102
- else
103
- DOT="○"
104
- fi
105
- # Calendar-day relative label
106
- if [ "$H_CAL_DAYS" -le 0 ] 2>/dev/null; then H_AGO="today"
107
- elif [ "$H_CAL_DAYS" -eq 1 ] 2>/dev/null; then H_AGO="yesterday"
108
- else H_AGO="${H_CAL_DAYS}d ago"
109
- fi
110
- if [ ${#H_TOPIC} -gt $MAX_INFO ]; then H_TOPIC="${H_TOPIC:0:$((MAX_INFO - 1))}…"; fi
111
- printf " %s %-9s%-12s%s\n" "$DOT" "$H_NAME" "$H_AGO" "$H_TOPIC"
112
- done
113
- fi
66
+ # --- Momentum board ---
67
+ source "$SCRIPT_DIR/bin/lib/metrics.sh"
68
+ _render_momentum_board
114
69
 
115
70
  # Pending questions addressed to you (one summary line — /answer to engage)
116
71
  PENDING_Q=$(cat "$CTX_DIR/pending_questions" 2>/dev/null || echo "[]")
@@ -129,64 +84,6 @@ if [ "$PENDING_Q_COUNT" -gt 0 ] 2>/dev/null && [ "$PENDING_Q_COUNT" != "0" ]; th
129
84
  fi
130
85
  fi
131
86
 
132
- # Fallback: last activity if no handoffs and no pending questions
133
- if { [ "$ADDRESSED_COUNT" = "0" ] || [ -z "$ADDRESSED_COUNT" ]; } \
134
- && { [ "$PENDING_Q_COUNT" = "0" ] || [ -z "$PENDING_Q_COUNT" ]; }; then
135
- LAST_ACTIVITY=$(cat "$CTX_DIR/activity" 2>/dev/null || echo "")
136
- if [ -n "$LAST_ACTIVITY" ]; then
137
- LA_TIME=$(echo "$LAST_ACTIVITY" | cut -d'|' -f1)
138
- # Compact relative time: "20 hours ago" → "20h ago", "3 days ago" → "3d ago"
139
- LA_TIME=$(echo "$LA_TIME" | sed 's/ hours\{0,1\} ago/h ago/; s/ days\{0,1\} ago/d ago/; s/ minutes\{0,1\} ago/m ago/; s/ weeks\{0,1\} ago/w ago/; s/ months\{0,1\} ago/mo ago/')
140
- LA_MSG=$(echo "$LAST_ACTIVITY" | cut -d'|' -f2-)
141
- if [ ${#LA_MSG} -gt $MAX_INFO ]; then LA_MSG="${LA_MSG:0:$((MAX_INFO - 1))}…"; fi
142
- printf " ◇ %-9s%-12s%s\n" "you" "$LA_TIME" "$LA_MSG"
143
- fi
144
- fi
145
-
146
- echo ""
147
- # --- Section 2: Around (team presence with online status) ---
148
- if [ "$TEAM_DATA_COUNT" -gt 0 ] 2>/dev/null && [ "$TEAM_DATA_COUNT" != "0" ]; then
149
- echo " ◦ around"
150
- # Presence roster with online/offline dots (hide inactive >3 days)
151
- STALE_THRESHOLD=$((RECENCY_DAYS * 86400))
152
- echo "$TEAM_DATA" | jq -r '.[] | ((.working_on // "")) as $w | (.branches // []) as $b | "\(.name)\t\(.last_seen_sort)\t\(.last_seen)\t\(if $w != "" then $w else ($b | join(", ")) end)"' 2>/dev/null | while IFS=$'\t' read -r P_NAME P_EPOCH P_SEEN P_BRANCHES; do
153
- [ -z "$P_NAME" ] && continue
154
- # Skip if no recent activity (within recency window)
155
- if [ "$P_EPOCH" -le 0 ] 2>/dev/null; then
156
- continue
157
- elif [ $(( NOW_RENDER - P_EPOCH )) -ge $STALE_THRESHOLD ] 2>/dev/null; then
158
- continue
159
- fi
160
- if [ ${#P_NAME} -gt 8 ]; then P_NAME="${P_NAME:0:8}"; fi
161
- # Online: last seen within threshold
162
- if [ $(( NOW_RENDER - P_EPOCH )) -lt $ONLINE_THRESHOLD ] 2>/dev/null; then
163
- DOT="●"
164
- else
165
- DOT="○"
166
- fi
167
- if [ ${#P_BRANCHES} -gt $MAX_INFO ]; then
168
- P_BRANCHES="${P_BRANCHES:0:$((MAX_INFO - 9))}... +more"
169
- fi
170
- printf " %s %-9s%-12s%s\n" "$DOT" "$P_NAME" "$P_SEEN" "$P_BRANCHES"
171
- done
172
- fi
173
-
174
- # --- Section 3: Recently merged PRs + implemented handoffs ---
175
- LIFECYCLE_JSON=$(cat "$CTX_DIR/lifecycle" 2>/dev/null || echo '{}')
176
- MERGED_COUNT=$(echo "$LIFECYCLE_JSON" | jq '.merged_prs.values // [] | length' 2>/dev/null || echo "0")
177
- IMPL_COUNT=$(echo "$LIFECYCLE_JSON" | jq '.implemented_handoffs.values // [] | length' 2>/dev/null || echo "0")
178
-
179
- if [ "$MERGED_COUNT" -gt 0 ] 2>/dev/null || [ "$IMPL_COUNT" -gt 0 ] 2>/dev/null; then
180
- echo ""
181
- echo " ◦ merged"
182
- if [ "$MERGED_COUNT" -gt 0 ] 2>/dev/null; then
183
- echo "$LIFECYCLE_JSON" | jq -r '.merged_prs.values[]? // empty | " ✓ PR #\(.[0]) \(.[1])"' 2>/dev/null || true
184
- fi
185
- if [ "$IMPL_COUNT" -gt 0 ] 2>/dev/null; then
186
- echo "$LIFECYCLE_JSON" | jq -r '.implemented_handoffs.values[]? // empty | " ✓ \(.[1]) worked on your handoff"' 2>/dev/null || true
187
- fi
188
- fi
189
-
190
87
  # Show auto-save notice if work was committed from a previous branch
191
88
  if [ -n "$SAVED_BRANCH" ]; then
192
89
  echo " ✓ Auto-saved uncommitted work on $SAVED_BRANCH"
@@ -310,6 +207,12 @@ CONTEXT_TEAM=$(cat "$CTX_DIR/team" 2>/dev/null || echo "[]")
310
207
  CONTEXT_SOUL=$(cat "$CTX_DIR/soul_summary" 2>/dev/null || echo "")
311
208
  CONTEXT_LIFECYCLE=$(cat "$CTX_DIR/lifecycle" 2>/dev/null || echo '{"merged_prs":[],"implemented_handoffs":[]}')
312
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
313
216
 
314
217
  # --- Write compact subagent context cache (reuses already-gathered data) ---
315
218
  SUBAGENT_CTX="/tmp/egregore-subagent-ctx-${EGREGORE_SESSION_ID}.txt"
@@ -354,6 +257,7 @@ cat << CTXEOF
354
257
  "team_recent_memory": $CONTEXT_TEAM,
355
258
  "soul_self_summary": "$CONTEXT_SOUL",
356
259
  "lifecycle": $CONTEXT_LIFECYCLE,
260
+ "momentum": $CONTEXT_METRICS,
357
261
  "pulse": $CONTEXT_PULSE
358
262
  }
359
263
  -->