create-egregore 0.19.3 → 0.19.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/egregore-launcher.js +410 -26
- package/bin/cli.js +34 -20
- package/lib/local.js +84 -11
- package/lib/pi-runtime.js +186 -0
- package/lib/setup.js +56 -11
- package/lib/ui.js +1 -1
- package/package.json +8 -6
- package/runtime/codex/.codex/hooks/search-hint.js +65 -0
- package/runtime/codex/.codex/hooks.json +13 -0
- package/runtime/codex/.codex/skills/deep-reflect/SKILL.md +14 -10
- package/runtime/codex/.codex/skills/minion/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/scroll/SKILL.md +168 -0
- package/runtime/codex/.codex/skills/scroll/agents/openai.yaml +4 -0
- package/runtime/codex/.codex/skills/scroll/assets/shell.html +542 -0
- package/runtime/codex/.codex/skills/search/SKILL.md +56 -0
- package/runtime/codex/.codex/skills/session-view/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/thread/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/view/SKILL.md +147 -32
- package/runtime/codex/.codex/skills/view/agents/openai.yaml +4 -0
- package/runtime/codex/.codex/skills/view/assets/compose-scaffold.html +221 -0
- package/runtime/codex/.codex/spec-manifest.json +4 -4
- package/runtime/codex/AGENTS.md +31 -5
- package/runtime/codex/bin/activity-data.sh +46 -4
- package/runtime/codex/bin/agent.sh +10 -2
- package/runtime/codex/bin/codex-install-skills.sh +1 -1
- package/runtime/codex/bin/codex-render-spec.mjs +24 -2
- package/runtime/codex/bin/codex-session-start.sh +6 -60
- package/runtime/codex/bin/codex-skill-render.mjs +20 -12
- package/runtime/codex/bin/codex-sync-skills.sh +4 -0
- package/runtime/codex/bin/graph-op.sh +56 -5
- package/runtime/codex/bin/graph.sh +36 -0
- package/runtime/codex/bin/handoff-run.sh +157 -7
- package/runtime/codex/bin/handoff-save-egregore.sh +6 -1
- package/runtime/codex/bin/index-handoff.sh +9 -3
- package/runtime/codex/bin/lib/context.sh +97 -18
- package/runtime/codex/bin/lib/dashboard-artifact.sh +1 -1
- package/runtime/codex/bin/lib/git-sync.sh +35 -9
- package/runtime/codex/bin/lib/greeting.sh +30 -104
- package/runtime/codex/bin/lib/metrics.sh +374 -0
- package/runtime/codex/bin/publish-artifact.sh +43 -1
- package/runtime/codex/bin/settings.sh +274 -0
- package/runtime/codex/manifest.json +18 -1
- package/runtime/pi/.codex/config.toml +15 -0
- package/runtime/pi/.codex/hooks/branch-guard.js +242 -0
- package/runtime/pi/.codex/hooks/search-hint.js +65 -0
- package/runtime/pi/.codex/hooks.json +30 -0
- package/runtime/pi/.codex/skills/activity/SKILL.md +70 -0
- package/runtime/pi/.codex/skills/add/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/announce/SKILL.md +54 -0
- package/runtime/pi/.codex/skills/answer/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/archive/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/ask/SKILL.md +59 -0
- package/runtime/pi/.codex/skills/branch/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/checkup/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/commit/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/contribute/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/dark-mode/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/dashboard/SKILL.md +59 -0
- package/runtime/pi/.codex/skills/deep-reflect/SKILL.md +74 -0
- package/runtime/pi/.codex/skills/delete-user/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/egregore-voice/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/emissary/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/env/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/handoff/SKILL.md +67 -0
- package/runtime/pi/.codex/skills/harvest/SKILL.md +107 -0
- package/runtime/pi/.codex/skills/ingest/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/ingest-google/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/ingest-user-interview/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/invite/SKILL.md +40 -0
- package/runtime/pi/.codex/skills/issue/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/me/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/meeting/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/minion/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/note/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/onboarding/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/pr/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/project/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/pull/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/push/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/quest/SKILL.md +77 -0
- package/runtime/pi/.codex/skills/quest-suggest/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/reflect/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/review-pr/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/save/SKILL.md +62 -0
- package/runtime/pi/.codex/skills/scroll/SKILL.md +168 -0
- package/runtime/pi/.codex/skills/scroll/agents/openai.yaml +4 -0
- package/runtime/pi/.codex/skills/scroll/assets/shell.html +542 -0
- package/runtime/pi/.codex/skills/search/SKILL.md +56 -0
- package/runtime/pi/.codex/skills/session-view/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/setup/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/summon/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/sync-repos/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/telegram-connect/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/telemetry/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/test/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/the-spiral/SKILL.md +84 -0
- package/runtime/pi/.codex/skills/thread/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/todo/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/tui-design/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/tutorial/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/update/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/view/SKILL.md +156 -0
- package/runtime/pi/.codex/skills/view/agents/openai.yaml +4 -0
- package/runtime/pi/.codex/skills/view/assets/compose-scaffold.html +221 -0
- package/runtime/pi/.codex/skills/visual-explain/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/wrap/SKILL.md +55 -0
- package/runtime/pi/.codex/spec-manifest.json +114 -0
- package/runtime/pi/.pi/APPEND_SYSTEM.md +255 -0
- package/runtime/pi/.pi/extensions/egregore.ts +324 -0
- package/runtime/pi/.pi/settings.json +6 -0
- package/runtime/pi/.pi/spec-manifest.json +12 -0
- package/runtime/pi/AGENTS.md +350 -0
- package/runtime/pi/bin/activity-data.sh +242 -0
- package/runtime/pi/bin/agent.sh +677 -0
- package/runtime/pi/bin/codex-install-skills.sh +78 -0
- package/runtime/pi/bin/codex-render-spec.mjs +394 -0
- package/runtime/pi/bin/codex-session-start.sh +188 -0
- package/runtime/pi/bin/codex-skill-render.mjs +323 -0
- package/runtime/pi/bin/codex-sync-skills.sh +227 -0
- package/runtime/pi/bin/dashboard-data.sh +178 -0
- package/runtime/pi/bin/graph-batch.sh +60 -0
- package/runtime/pi/bin/graph-op.sh +431 -0
- package/runtime/pi/bin/graph-wal.sh +228 -0
- package/runtime/pi/bin/graph.sh +177 -0
- package/runtime/pi/bin/handoff-pr-backfill.sh +86 -0
- package/runtime/pi/bin/handoff-run.sh +581 -0
- package/runtime/pi/bin/handoff-save-egregore.sh +136 -0
- package/runtime/pi/bin/index-handoff.sh +312 -0
- package/runtime/pi/bin/lib/artifact-id.sh +46 -0
- package/runtime/pi/bin/lib/config.sh +71 -0
- package/runtime/pi/bin/lib/context.sh +542 -0
- package/runtime/pi/bin/lib/dashboard-artifact.sh +142 -0
- package/runtime/pi/bin/lib/ensure-develop.sh +183 -0
- package/runtime/pi/bin/lib/git-sync.sh +288 -0
- package/runtime/pi/bin/lib/greeting.sh +340 -0
- package/runtime/pi/bin/lib/hash.sh +13 -0
- package/runtime/pi/bin/lib/identity.sh +174 -0
- package/runtime/pi/bin/lib/metrics.sh +374 -0
- package/runtime/pi/bin/lib/permissions.sh +106 -0
- package/runtime/pi/bin/lib/time.sh +56 -0
- package/runtime/pi/bin/lib/worktree-links.sh +54 -0
- package/runtime/pi/bin/notify.sh +207 -0
- package/runtime/pi/bin/pi-branch-policy.mjs +15 -0
- package/runtime/pi/bin/pi-product-workflows.mjs +324 -0
- package/runtime/pi/bin/pi-render-spec.mjs +114 -0
- package/runtime/pi/bin/pi-session-start.sh +24 -0
- package/runtime/pi/bin/publish-artifact.sh +201 -0
- package/runtime/pi/bin/publish-references.sh +144 -0
- package/runtime/pi/bin/repo-state.sh +99 -0
- package/runtime/pi/bin/telemetry.sh +391 -0
- package/runtime/pi/bin/worktree-create.sh +118 -0
- package/runtime/pi/bin/worktree-remove.sh +50 -0
- package/runtime/pi/bin/worktree.sh +198 -0
- package/runtime/pi/manifest.json +176 -0
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
# shellcheck shell=bash
|
|
2
|
+
# context.sh — Context gathering for session-start.sh
|
|
3
|
+
#
|
|
4
|
+
# Runs 11 parallel background subshells to gather session context:
|
|
5
|
+
# 1. Recent handoffs
|
|
6
|
+
# 2. Active quests
|
|
7
|
+
# 3. User's last git activity
|
|
8
|
+
# 3b. Personal todos from graph
|
|
9
|
+
# 4. Team presence (graph + git branches)
|
|
10
|
+
# 5. Soul self-summary
|
|
11
|
+
# 6. Handoffs addressed to user
|
|
12
|
+
# 7. Graph health check
|
|
13
|
+
# 8. Telegram health check
|
|
14
|
+
# 9. Lifecycle events (merged PRs, implemented handoffs)
|
|
15
|
+
# 10. Last Pulse brief
|
|
16
|
+
# 11. Momentum metrics
|
|
17
|
+
#
|
|
18
|
+
# All subshells write to $CTX_DIR (a temp directory).
|
|
19
|
+
# Ends with `wait` + reading health results back.
|
|
20
|
+
#
|
|
21
|
+
# Inputs: SCRIPT_DIR, AUTHOR, LOCAL_MODE, STATE_FILE, CONFIG, ENV_FILE,
|
|
22
|
+
# HEALTH_GRAPH, HEALTH_TELEGRAM
|
|
23
|
+
# Outputs: CTX_DIR (populated), TIME_OF_DAY, HEALTH_GRAPH, HEALTH_TELEGRAM
|
|
24
|
+
|
|
25
|
+
CTX_DIR=$(mktemp -d)
|
|
26
|
+
|
|
27
|
+
source "$SCRIPT_DIR/bin/lib/handoff-meta.sh" >/dev/null 2>/dev/null || true
|
|
28
|
+
|
|
29
|
+
# Time of day
|
|
30
|
+
HOUR=$(date +%H)
|
|
31
|
+
if [ "$HOUR" -lt 12 ]; then TIME_OF_DAY="morning"
|
|
32
|
+
elif [ "$HOUR" -lt 17 ]; then TIME_OF_DAY="afternoon"
|
|
33
|
+
else TIME_OF_DAY="evening"
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# 1. Recent handoffs (background)
|
|
37
|
+
(
|
|
38
|
+
JSON="[]"
|
|
39
|
+
if [ -d "$SCRIPT_DIR/memory/handoffs" ]; then
|
|
40
|
+
FILES=$(find -L "$SCRIPT_DIR/memory/handoffs" -name '*.md' -not -name 'index*' -type f 2>/dev/null | xargs ls -t 2>/dev/null | head -3)
|
|
41
|
+
JSON="["
|
|
42
|
+
FIRST=true
|
|
43
|
+
for F in $FILES; do
|
|
44
|
+
[ -z "$F" ] && continue
|
|
45
|
+
NAME=$(basename "$F" .md)
|
|
46
|
+
PREVIEW=$(head -c 120 "$F" 2>/dev/null | tr '\n' ' ' | sed 's/"/\\"/g')
|
|
47
|
+
$FIRST || JSON="$JSON,"
|
|
48
|
+
JSON="$JSON{\"name\":\"$NAME\",\"preview\":\"$PREVIEW\"}"
|
|
49
|
+
FIRST=false
|
|
50
|
+
done
|
|
51
|
+
JSON="$JSON]"
|
|
52
|
+
fi
|
|
53
|
+
echo "$JSON" > "$CTX_DIR/handoffs"
|
|
54
|
+
) &
|
|
55
|
+
|
|
56
|
+
# 2. Quests (background)
|
|
57
|
+
(
|
|
58
|
+
JSON="[]"
|
|
59
|
+
if [ -d "$SCRIPT_DIR/memory/quests" ]; then
|
|
60
|
+
JSON="["
|
|
61
|
+
FIRST=true
|
|
62
|
+
for F in "$SCRIPT_DIR/memory/quests/"*.md; do
|
|
63
|
+
[ -e "$F" ] || continue
|
|
64
|
+
echo "$F" | grep -q draft && continue
|
|
65
|
+
NAME=$(basename "$F" .md)
|
|
66
|
+
$FIRST || JSON="$JSON,"
|
|
67
|
+
JSON="$JSON\"$NAME\""
|
|
68
|
+
FIRST=false
|
|
69
|
+
done
|
|
70
|
+
JSON="$JSON]"
|
|
71
|
+
fi
|
|
72
|
+
echo "$JSON" > "$CTX_DIR/quests"
|
|
73
|
+
) &
|
|
74
|
+
|
|
75
|
+
# 3. User's last activity (background)
|
|
76
|
+
(
|
|
77
|
+
git log --author="$AUTHOR" --format="%ar|%s" -1 2>/dev/null > "$CTX_DIR/activity" || echo "" > "$CTX_DIR/activity"
|
|
78
|
+
) &
|
|
79
|
+
|
|
80
|
+
# 3b. Personal todos from graph (background)
|
|
81
|
+
(
|
|
82
|
+
_API_URL=$(jq -r '.api_url // empty' "$SCRIPT_DIR/egregore.json" 2>/dev/null)
|
|
83
|
+
_API_KEY=$(grep '^EGREGORE_API_KEY=' "${ENV_FILE:-$SCRIPT_DIR/.env}" 2>/dev/null | cut -d'=' -f2-)
|
|
84
|
+
if [ -n "$_API_URL" ] && [ -n "$_API_KEY" ]; then
|
|
85
|
+
TODO_RAW=$(bash "$SCRIPT_DIR/bin/graph.sh" query \
|
|
86
|
+
"MATCH (t:Todo)-[:BY]->(p:Person {github: \$me}) WHERE t.status IN ['open', 'blocked'] OPTIONAL MATCH (t)-[:PART_OF]->(q:Quest) RETURN t.text AS text, t.priority AS priority, t.status AS status, t.created AS created, q.id AS quest ORDER BY t.priority DESC, t.created DESC LIMIT 5" \
|
|
87
|
+
"$(jq -n --arg me "$AUTHOR" '{me: $me}')" 2>/dev/null || echo "")
|
|
88
|
+
if [ -n "$TODO_RAW" ]; then
|
|
89
|
+
echo "$TODO_RAW" | jq '[.values[] | {text: .[0], priority: (.[1] // 0), status: .[2], created: .[3], quest: (.[4] // "")}]' 2>/dev/null || echo "[]"
|
|
90
|
+
else
|
|
91
|
+
echo "[]"
|
|
92
|
+
fi
|
|
93
|
+
else
|
|
94
|
+
echo "[]"
|
|
95
|
+
fi
|
|
96
|
+
) > "$CTX_DIR/todos" 2>/dev/null &
|
|
97
|
+
|
|
98
|
+
# 4. Team presence — last seen + active branches (background)
|
|
99
|
+
(
|
|
100
|
+
# --- Read config inside subshell ---
|
|
101
|
+
_API_URL=$(jq -r '.api_url // empty' "$SCRIPT_DIR/egregore.json" 2>/dev/null)
|
|
102
|
+
_API_KEY=$(grep '^EGREGORE_API_KEY=' "${ENV_FILE:-$SCRIPT_DIR/.env}" 2>/dev/null | cut -d'=' -f2-)
|
|
103
|
+
|
|
104
|
+
# --- Graph: last-seen per person (excluding self) ---
|
|
105
|
+
GRAPH_DATA="[]"
|
|
106
|
+
if [ -n "$_API_URL" ] && [ -n "$_API_KEY" ]; then
|
|
107
|
+
CYPHER="MATCH (s:Session)-[:BY]->(p:Person) WHERE p.github <> \$me RETURN p.name AS name, toString(max(coalesce(s.startedAt, datetime(s.date)))) AS lastSeen ORDER BY lastSeen DESC"
|
|
108
|
+
GRAPH_RAW=$(bash "$SCRIPT_DIR/bin/graph.sh" query "$CYPHER" "$(jq -n --arg me "$AUTHOR" '{me: $me}')" 2>/dev/null || echo "")
|
|
109
|
+
if [ -n "$GRAPH_RAW" ]; then
|
|
110
|
+
GRAPH_DATA=$(echo "$GRAPH_RAW" | jq '[.values[] | {name: .[0], lastSeen: .[1]}]' 2>/dev/null || echo "[]")
|
|
111
|
+
fi
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
# --- Git: branch tip commit dates per person (primary recency signal) ---
|
|
115
|
+
# git for-each-ref is fast and gives the actual last commit date per branch
|
|
116
|
+
GIT_COMMIT_DATA="{}"
|
|
117
|
+
GIT_REF_DATA=$(git -C "$SCRIPT_DIR" for-each-ref --sort=-committerdate \
|
|
118
|
+
--format='%(refname:short)|%(committerdate:unix)' refs/remotes/origin/dev/ 2>/dev/null || echo "")
|
|
119
|
+
if [ -n "$GIT_REF_DATA" ]; then
|
|
120
|
+
GIT_COMMIT_DATA=$(echo "$GIT_REF_DATA" | sed 's|^origin/dev/||' | while IFS='|' read -r REF_PATH REF_EPOCH || [ -n "$REF_PATH" ]; do
|
|
121
|
+
[ -z "$REF_PATH" ] && continue
|
|
122
|
+
REF_AUTHOR=$(echo "$REF_PATH" | cut -d'/' -f1 | tr '[:upper:]' '[:lower:]')
|
|
123
|
+
echo "${REF_AUTHOR}|${REF_EPOCH}"
|
|
124
|
+
done | sort -t'|' -k1,1 -k2,2rn | sort -t'|' -k1,1 -u | jq -Rn '
|
|
125
|
+
[inputs | split("|") | {(.[0]): .[1]}]
|
|
126
|
+
| add // {}
|
|
127
|
+
' 2>/dev/null || echo "{}")
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
# --- Git: active dev/* branches (excluding self) ---
|
|
131
|
+
SELF_LC=$(echo "$AUTHOR" | tr '[:upper:]' '[:lower:]')
|
|
132
|
+
SELF_DISPLAY_LC=""
|
|
133
|
+
if [ -f "$SCRIPT_DIR/.egregore-state.json" ]; then
|
|
134
|
+
SELF_DISPLAY_LC=$(jq -r '.display_name // empty' "$SCRIPT_DIR/.egregore-state.json" 2>/dev/null | tr '[:upper:]' '[:lower:]')
|
|
135
|
+
fi
|
|
136
|
+
BRANCH_DATA=$(git -C "$SCRIPT_DIR" branch -r --format='%(refname:short)' 2>/dev/null | grep "^origin/dev/" | sed 's|^origin/dev/||' || echo "")
|
|
137
|
+
|
|
138
|
+
BRANCH_MAP="{}"
|
|
139
|
+
if [ -n "$BRANCH_DATA" ]; then
|
|
140
|
+
BRANCH_MAP=$(echo "$BRANCH_DATA" | while IFS='/' read -r B_AUTHOR B_SLUG_REST || [ -n "$B_AUTHOR" ]; do
|
|
141
|
+
[ -z "$B_AUTHOR" ] && continue
|
|
142
|
+
B_AUTHOR_LC=$(echo "$B_AUTHOR" | tr '[:upper:]' '[:lower:]')
|
|
143
|
+
[ "$B_AUTHOR_LC" = "$SELF_LC" ] && continue
|
|
144
|
+
[ -n "$SELF_DISPLAY_LC" ] && [ "$B_AUTHOR_LC" = "$SELF_DISPLAY_LC" ] && continue
|
|
145
|
+
B_HUMAN=$(echo "$B_SLUG_REST" | tr '-' ' ')
|
|
146
|
+
echo "${B_AUTHOR_LC}|${B_HUMAN}"
|
|
147
|
+
done | jq -Rn '
|
|
148
|
+
[inputs | split("|") | {author: .[0], branch: .[1]}]
|
|
149
|
+
| group_by(.author)
|
|
150
|
+
| map({(.[0].author): [.[] | .branch]})
|
|
151
|
+
| add // {}
|
|
152
|
+
' 2>/dev/null || echo "{}")
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
# --- Local-mode presence: scan session + wrap files for lastSeen ---
|
|
156
|
+
# When graph has no data, these files provide temporal presence information
|
|
157
|
+
FILE_PRESENCE="{}"
|
|
158
|
+
if [ "$GRAPH_DATA" = "[]" ]; then
|
|
159
|
+
FILE_PRESENCE=$(
|
|
160
|
+
{
|
|
161
|
+
# Scan session logs, wraps, and handoffs (all use **Author**: / **Date**: format)
|
|
162
|
+
for DIR in "$SCRIPT_DIR/memory/sessions" "$SCRIPT_DIR/memory/wraps" "$SCRIPT_DIR/memory/handoffs"; do
|
|
163
|
+
[ -d "$DIR" ] || continue
|
|
164
|
+
find -L "$DIR" -name '*.md' -type f 2>/dev/null | xargs ls -t 2>/dev/null | head -20 | while read -r SFILE; do
|
|
165
|
+
[ -z "$SFILE" ] && continue
|
|
166
|
+
S_AUTH=$(grep -m1 '^\*\*Author\*\*:' "$SFILE" 2>/dev/null | sed 's/^\*\*Author\*\*:[[:space:]]*//')
|
|
167
|
+
S_DATE=$(grep -m1 '^\*\*Date\*\*:' "$SFILE" 2>/dev/null | sed 's/^\*\*Date\*\*:[[:space:]]*//')
|
|
168
|
+
[ -z "$S_AUTH" ] && continue
|
|
169
|
+
echo "${S_AUTH}|${S_DATE}"
|
|
170
|
+
done
|
|
171
|
+
done
|
|
172
|
+
} | sort -t'|' -k1,1 -k2,2r | sort -t'|' -k1,1 -u | while IFS='|' read -r FP_NAME FP_DATE; do
|
|
173
|
+
[ -z "$FP_NAME" ] && continue
|
|
174
|
+
FP_LC=$(echo "$FP_NAME" | tr '[:upper:]' '[:lower:]')
|
|
175
|
+
echo "${FP_LC}|${FP_DATE}"
|
|
176
|
+
done | jq -Rn '
|
|
177
|
+
[inputs | split("|") | {(.[0]): .[1]}]
|
|
178
|
+
| add // {}
|
|
179
|
+
' 2>/dev/null || echo "{}"
|
|
180
|
+
)
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
# --- "Working on": latest session/handoff/wrap topic per person (file-based) ---
|
|
184
|
+
# Gives every team surface (greeting "around", dashboard) something legible in
|
|
185
|
+
# local mode, where git branches are usually empty for teammates.
|
|
186
|
+
_WM_TAB=$(printf '\t')
|
|
187
|
+
WORK_MAP=$(
|
|
188
|
+
{
|
|
189
|
+
for DIR in "$SCRIPT_DIR/memory/sessions" "$SCRIPT_DIR/memory/handoffs" "$SCRIPT_DIR/memory/wraps"; do
|
|
190
|
+
[ -d "$DIR" ] || continue
|
|
191
|
+
find -L "$DIR" -name '*.md' -not -name 'index*' -type f 2>/dev/null | xargs ls -t 2>/dev/null | head -30 | while read -r WFILE; do
|
|
192
|
+
[ -z "$WFILE" ] && continue
|
|
193
|
+
W_AUTH=$(grep -m1 '^\*\*Author\*\*:' "$WFILE" 2>/dev/null | sed 's/^\*\*Author\*\*:[[:space:]]*//')
|
|
194
|
+
W_DATE=$(grep -m1 '^\*\*Date\*\*:' "$WFILE" 2>/dev/null | sed 's/^\*\*Date\*\*:[[:space:]]*//')
|
|
195
|
+
W_TOPIC=$(grep -m1 '^# ' "$WFILE" 2>/dev/null | sed 's/^#[[:space:]]*//; s/^Session:[[:space:]]*//; s/^Handoff:[[:space:]]*//; s/^Wrap:[[:space:]]*//')
|
|
196
|
+
[ -z "$W_AUTH" ] && continue
|
|
197
|
+
[ -z "$W_TOPIC" ] && continue
|
|
198
|
+
printf '%s%s%s%s%s\n' "$W_AUTH" "$_WM_TAB" "$W_DATE" "$_WM_TAB" "$W_TOPIC"
|
|
199
|
+
done
|
|
200
|
+
done
|
|
201
|
+
} | sort -t"$_WM_TAB" -k1,1 -k2,2r | sort -t"$_WM_TAB" -k1,1 -u | while IFS="$_WM_TAB" read -r WM_NAME WM_DATE WM_TOPIC; do
|
|
202
|
+
[ -z "$WM_NAME" ] && continue
|
|
203
|
+
WM_LC=$(echo "$WM_NAME" | tr '[:upper:]' '[:lower:]')
|
|
204
|
+
printf '%s%s%s\n' "$WM_LC" "$_WM_TAB" "$WM_TOPIC"
|
|
205
|
+
done | jq -Rn --arg tab "$_WM_TAB" '[inputs | split($tab) | select(length>=2) | {(.[0]): (.[1] // "")}] | add // {}' 2>/dev/null || echo "{}"
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
# --- Relative time + epoch helpers (cross-platform) ---
|
|
209
|
+
NOW_EPOCH=$(date +%s)
|
|
210
|
+
|
|
211
|
+
iso_to_epoch() {
|
|
212
|
+
local iso="$1"
|
|
213
|
+
local clean=$(echo "$iso" | sed 's/Z$//; s/+00:00$//; s/\.[0-9]*//')
|
|
214
|
+
[[ "$clean" != *T* ]] && clean="${clean}T00:00:00"
|
|
215
|
+
TZ=UTC date -j -f "%Y-%m-%dT%H:%M:%S" "$clean" "+%s" 2>/dev/null || \
|
|
216
|
+
TZ=UTC date -d "$clean" +%s 2>/dev/null || echo "0"
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
epoch_to_relative() {
|
|
220
|
+
local epoch="$1"
|
|
221
|
+
[ "$epoch" = "0" ] && echo "--" && return
|
|
222
|
+
local delta=$(( NOW_EPOCH - epoch ))
|
|
223
|
+
if [ "$delta" -lt 60 ]; then echo "just now"
|
|
224
|
+
elif [ "$delta" -lt 3600 ]; then echo "$(( delta / 60 ))m ago"
|
|
225
|
+
elif [ "$delta" -lt 86400 ]; then echo "$(( delta / 3600 ))h ago"
|
|
226
|
+
else
|
|
227
|
+
# Calendar-day based comparison for accurate "yesterday" / "Xd ago"
|
|
228
|
+
local event_date today_date
|
|
229
|
+
event_date=$(date -r "$epoch" +%Y-%m-%d 2>/dev/null || \
|
|
230
|
+
date -d "@$epoch" +%Y-%m-%d 2>/dev/null || echo "")
|
|
231
|
+
today_date=$(date +%Y-%m-%d)
|
|
232
|
+
if [ -z "$event_date" ] || [ "$event_date" = "$today_date" ]; then echo "today"
|
|
233
|
+
else
|
|
234
|
+
local today_e event_e
|
|
235
|
+
today_e=$(date -j -f "%Y-%m-%d" "$today_date" +%s 2>/dev/null || \
|
|
236
|
+
date -d "$today_date" +%s 2>/dev/null || echo "$NOW_EPOCH")
|
|
237
|
+
event_e=$(date -j -f "%Y-%m-%d" "$event_date" +%s 2>/dev/null || \
|
|
238
|
+
date -d "$event_date" +%s 2>/dev/null || echo "$epoch")
|
|
239
|
+
local days=$(( (today_e - event_e) / 86400 ))
|
|
240
|
+
[ "$days" -lt 1 ] && days=1
|
|
241
|
+
if [ "$days" -eq 1 ]; then echo "yesterday"
|
|
242
|
+
else echo "${days}d ago"
|
|
243
|
+
fi
|
|
244
|
+
fi
|
|
245
|
+
fi
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
# --- Merge git commits + graph + file presence + branches into presence array ---
|
|
249
|
+
ALL_NAMES=$(echo "$GRAPH_DATA" | jq -r '.[].name' 2>/dev/null || echo "")
|
|
250
|
+
FILE_NAMES=$(echo "$FILE_PRESENCE" | jq -r 'keys[]' 2>/dev/null || echo "")
|
|
251
|
+
BRANCH_NAMES=$(echo "$BRANCH_MAP" | jq -r 'keys[]' 2>/dev/null || echo "")
|
|
252
|
+
GIT_NAMES=$(echo "$GIT_COMMIT_DATA" | jq -r 'keys[]' 2>/dev/null || echo "")
|
|
253
|
+
|
|
254
|
+
UNION_NAMES=$(printf "%s\n%s\n%s\n%s" "$ALL_NAMES" "$FILE_NAMES" "$BRANCH_NAMES" "$GIT_NAMES" | tr '[:upper:]' '[:lower:]' | sort -u | grep -v '^$' | grep -v "^${SELF_LC}$" | grep -v "^${SELF_DISPLAY_LC}$" || echo "")
|
|
255
|
+
|
|
256
|
+
if [ -z "$UNION_NAMES" ]; then
|
|
257
|
+
echo "[]" > "$CTX_DIR/team"
|
|
258
|
+
exit 0
|
|
259
|
+
fi
|
|
260
|
+
|
|
261
|
+
# --- Build presence JSON ---
|
|
262
|
+
PRESENCE="["
|
|
263
|
+
FIRST=true
|
|
264
|
+
for PNAME in $UNION_NAMES; do
|
|
265
|
+
# Git commit date is authoritative (actual work shipped).
|
|
266
|
+
# Graph/file presence are fallbacks only (session opens ≠ real activity).
|
|
267
|
+
GIT_EPOCH=$(echo "$GIT_COMMIT_DATA" | jq -r --arg n "$PNAME" '.[$n] // empty' 2>/dev/null || true)
|
|
268
|
+
|
|
269
|
+
LAST_SEEN_EPOCH="0"
|
|
270
|
+
if [ -n "$GIT_EPOCH" ] && [ "$GIT_EPOCH" -gt 0 ] 2>/dev/null; then
|
|
271
|
+
LAST_SEEN_EPOCH="$GIT_EPOCH"
|
|
272
|
+
else
|
|
273
|
+
# Fallback: graph session startedAt
|
|
274
|
+
GRAPH_ISO=$(echo "$GRAPH_DATA" | jq -r --arg n "$PNAME" '.[] | select((.name | ascii_downcase) == $n) | .lastSeen // empty' 2>/dev/null | head -1)
|
|
275
|
+
if [ -n "$GRAPH_ISO" ]; then
|
|
276
|
+
LAST_SEEN_EPOCH=$(iso_to_epoch "$GRAPH_ISO")
|
|
277
|
+
fi
|
|
278
|
+
# Fallback: file-based presence (local mode)
|
|
279
|
+
if [ "$LAST_SEEN_EPOCH" -le 0 ] 2>/dev/null; then
|
|
280
|
+
FILE_ISO=$(echo "$FILE_PRESENCE" | jq -r --arg n "$PNAME" '.[$n] // empty' 2>/dev/null || true)
|
|
281
|
+
if [ -n "$FILE_ISO" ]; then
|
|
282
|
+
LAST_SEEN_EPOCH=$(iso_to_epoch "$FILE_ISO")
|
|
283
|
+
fi
|
|
284
|
+
fi
|
|
285
|
+
fi
|
|
286
|
+
|
|
287
|
+
if [ "$LAST_SEEN_EPOCH" -gt 0 ] 2>/dev/null; then
|
|
288
|
+
LAST_SEEN_REL=$(epoch_to_relative "$LAST_SEEN_EPOCH")
|
|
289
|
+
else
|
|
290
|
+
LAST_SEEN_EPOCH="0"
|
|
291
|
+
LAST_SEEN_REL="--"
|
|
292
|
+
fi
|
|
293
|
+
|
|
294
|
+
BRANCHES_JSON=$(echo "$BRANCH_MAP" | jq --arg n "$PNAME" '.[$n] // []' 2>/dev/null || echo "[]")
|
|
295
|
+
BR_COUNT=$(echo "$BRANCHES_JSON" | jq 'length' 2>/dev/null || echo "0")
|
|
296
|
+
if [ "$BR_COUNT" -gt 2 ]; then
|
|
297
|
+
EXTRA=$((BR_COUNT - 2))
|
|
298
|
+
BRANCHES_JSON=$(echo "$BRANCHES_JSON" | jq --arg e "+${EXTRA} more" '[.[0:2][], $e]' 2>/dev/null || echo "$BRANCHES_JSON")
|
|
299
|
+
fi
|
|
300
|
+
|
|
301
|
+
WORK=$(echo "$WORK_MAP" | jq -r --arg n "$PNAME" '.[$n] // ""' 2>/dev/null)
|
|
302
|
+
ENTRY=$(jq -n --arg name "$PNAME" --arg seen "$LAST_SEEN_REL" --argjson sort "$LAST_SEEN_EPOCH" --argjson branches "$BRANCHES_JSON" --arg work "$WORK" \
|
|
303
|
+
'{name: $name, last_seen: $seen, last_seen_sort: $sort, branches: $branches, working_on: $work}')
|
|
304
|
+
$FIRST || PRESENCE="$PRESENCE,"
|
|
305
|
+
PRESENCE="$PRESENCE$ENTRY"
|
|
306
|
+
FIRST=false
|
|
307
|
+
done
|
|
308
|
+
PRESENCE="$PRESENCE]"
|
|
309
|
+
|
|
310
|
+
PRESENCE=$(echo "$PRESENCE" | jq 'sort_by(-.last_seen_sort)' 2>/dev/null || echo "$PRESENCE")
|
|
311
|
+
echo "$PRESENCE" > "$CTX_DIR/team"
|
|
312
|
+
) &
|
|
313
|
+
|
|
314
|
+
# 5. Soul self-summary (background)
|
|
315
|
+
(
|
|
316
|
+
SUMMARY=""
|
|
317
|
+
if [ -f "$SCRIPT_DIR/egregore.md" ]; then
|
|
318
|
+
SUMMARY=$(sed -n '/^## Self-Summary/,/^##/p' "$SCRIPT_DIR/egregore.md" | sed '1d;/^##/d' | sed 's/^[[:space:]]*//' | tr '\n' ' ' | sed 's/[[:space:]]*$//')
|
|
319
|
+
fi
|
|
320
|
+
echo "$SUMMARY" > "$CTX_DIR/soul_summary"
|
|
321
|
+
) &
|
|
322
|
+
|
|
323
|
+
# 6. Handoffs addressed to user (background, enriched with author/topic)
|
|
324
|
+
(
|
|
325
|
+
JSON="[]"
|
|
326
|
+
RICH="[]"
|
|
327
|
+
if [ -d "$SCRIPT_DIR/memory/handoffs" ]; then
|
|
328
|
+
# Match github username, display name, github_name first word, and people-file name
|
|
329
|
+
# Handoff files may use "to: name", "**To**: name", or "To: name"
|
|
330
|
+
_DISPLAY=$(jq -r '.display_name // empty' "$STATE_FILE" 2>/dev/null)
|
|
331
|
+
_GH_NAME=$(jq -r '.github_name // empty' "$STATE_FILE" 2>/dev/null)
|
|
332
|
+
_FIRST_NAME=$(echo "$_GH_NAME" | awk '{print $1}')
|
|
333
|
+
# Also check people file: memory/people/{author}.md has "# Name" on first line
|
|
334
|
+
_PEOPLE_NAME=""
|
|
335
|
+
_PEOPLE_FILE="$SCRIPT_DIR/memory/people/$(echo "$AUTHOR" | tr '[:upper:]' '[:lower:]').md"
|
|
336
|
+
[ -f "$_PEOPLE_FILE" ] && _PEOPLE_NAME=$(sed -n 's/^# *//p' "$_PEOPLE_FILE" 2>/dev/null | head -1)
|
|
337
|
+
_GREP_PAT="[Tt]o[*]*: *$AUTHOR\|[Tt]o[*]*:$AUTHOR"
|
|
338
|
+
for _VARIANT in "$_DISPLAY" "$_FIRST_NAME" "$_PEOPLE_NAME"; do
|
|
339
|
+
[ -z "$_VARIANT" ] && continue
|
|
340
|
+
[ "$_VARIANT" = "$AUTHOR" ] && continue
|
|
341
|
+
# Skip if already in pattern (avoid duplicates)
|
|
342
|
+
echo "$_GREP_PAT" | grep -qF "$_VARIANT" && continue
|
|
343
|
+
_GREP_PAT="$_GREP_PAT\|[Tt]o[*]*: *$_VARIANT\|[Tt]o[*]*:$_VARIANT"
|
|
344
|
+
done
|
|
345
|
+
ADDRESSED=$(grep -rli "$_GREP_PAT" "$SCRIPT_DIR/memory/handoffs/" 2>/dev/null | sort -r | head -5 || true)
|
|
346
|
+
JSON="["
|
|
347
|
+
RICH="["
|
|
348
|
+
FIRST=true
|
|
349
|
+
for AF in $ADDRESSED; do
|
|
350
|
+
[ -z "$AF" ] && continue
|
|
351
|
+
AF_NAME=$(basename "$AF" .md)
|
|
352
|
+
AF_AUTHOR=$(eg_handoff_author "$AF")
|
|
353
|
+
AF_TOPIC=$(eg_handoff_topic "$AF")
|
|
354
|
+
AF_DATE=$(eg_handoff_date "$AF")
|
|
355
|
+
# Extract repo state table if present
|
|
356
|
+
AF_REPO_STATE="[]"
|
|
357
|
+
AF_REPO_TABLE=$(awk '/^## Repo State/{found=1; next} found && /^#/{exit} found && /^\|[^-]/ && !/^\| Repo/{print}' "$AF" 2>/dev/null || true)
|
|
358
|
+
if [ -n "$AF_REPO_TABLE" ]; then
|
|
359
|
+
AF_REPO_STATE=$(echo "$AF_REPO_TABLE" | while IFS='|' read -r _ R_REPO R_BRANCH R_PR R_BASE _; do
|
|
360
|
+
R_REPO=$(echo "$R_REPO" | xargs 2>/dev/null || true)
|
|
361
|
+
R_BRANCH=$(echo "$R_BRANCH" | xargs 2>/dev/null || true)
|
|
362
|
+
R_PR=$(echo "$R_PR" | xargs 2>/dev/null | sed 's/^#//' | sed 's/—//' || true)
|
|
363
|
+
R_BASE=$(echo "$R_BASE" | xargs 2>/dev/null || true)
|
|
364
|
+
[ -z "$R_REPO" ] && continue
|
|
365
|
+
if [ -n "$R_PR" ] && [ "$R_PR" -eq "$R_PR" ] 2>/dev/null; then
|
|
366
|
+
printf '{"repo":"%s","branch":"%s","pr":%s,"base":"%s"}\n' "$R_REPO" "$R_BRANCH" "$R_PR" "$R_BASE"
|
|
367
|
+
else
|
|
368
|
+
printf '{"repo":"%s","branch":"%s","pr":null,"base":"%s"}\n' "$R_REPO" "$R_BRANCH" "$R_BASE"
|
|
369
|
+
fi
|
|
370
|
+
done | jq -sc '.' 2>/dev/null || echo "[]")
|
|
371
|
+
fi
|
|
372
|
+
$FIRST || JSON="$JSON,"
|
|
373
|
+
$FIRST || RICH="$RICH,"
|
|
374
|
+
JSON="$JSON\"$AF_NAME\""
|
|
375
|
+
AF_ENTRY=$(jq -cn \
|
|
376
|
+
--arg name "$AF_NAME" \
|
|
377
|
+
--arg author "$AF_AUTHOR" \
|
|
378
|
+
--arg topic "$AF_TOPIC" \
|
|
379
|
+
--arg date "$AF_DATE" \
|
|
380
|
+
--argjson repoState "$AF_REPO_STATE" \
|
|
381
|
+
'{name:$name, author:$author, topic:$topic, date:$date, repoState:$repoState}' 2>/dev/null || echo "{}")
|
|
382
|
+
RICH="$RICH$AF_ENTRY"
|
|
383
|
+
FIRST=false
|
|
384
|
+
done
|
|
385
|
+
JSON="$JSON]"
|
|
386
|
+
RICH="$RICH]"
|
|
387
|
+
fi
|
|
388
|
+
echo "$JSON" > "$CTX_DIR/addressed"
|
|
389
|
+
echo "$RICH" > "$CTX_DIR/addressed_rich"
|
|
390
|
+
) &
|
|
391
|
+
|
|
392
|
+
# 6.5. Pending questions addressed to user (background)
|
|
393
|
+
(
|
|
394
|
+
PENDING_JSON="[]"
|
|
395
|
+
QDIR="$SCRIPT_DIR/memory/knowledge/questions"
|
|
396
|
+
if [ -d "$QDIR" ]; then
|
|
397
|
+
# Match against the same identity variants used for handoffs (display name,
|
|
398
|
+
# github first-name, people-file name) so "to: oz" matches author "oguzhan".
|
|
399
|
+
_DISPLAY_Q=$(jq -r '.display_name // empty' "$STATE_FILE" 2>/dev/null)
|
|
400
|
+
_GH_NAME_Q=$(jq -r '.github_name // empty' "$STATE_FILE" 2>/dev/null)
|
|
401
|
+
_FIRST_NAME_Q=$(echo "$_GH_NAME_Q" | awk '{print tolower($1)}')
|
|
402
|
+
_PEOPLE_FILE_Q="$SCRIPT_DIR/memory/people/$(echo "$AUTHOR" | tr '[:upper:]' '[:lower:]').md"
|
|
403
|
+
_PEOPLE_NAME_Q=""
|
|
404
|
+
[ -f "$_PEOPLE_FILE_Q" ] && _PEOPLE_NAME_Q=$(sed -n 's/^# *//p' "$_PEOPLE_FILE_Q" 2>/dev/null | head -1 | awk '{print tolower($1)}')
|
|
405
|
+
|
|
406
|
+
LINES=""
|
|
407
|
+
for QF in "$QDIR"/*.md; do
|
|
408
|
+
[ -f "$QF" ] || continue
|
|
409
|
+
Q_TO=$(sed -n 's/^to:[[:space:]]*//p' "$QF" 2>/dev/null | head -1 | tr -d '"' | tr '[:upper:]' '[:lower:]' | awk '{print $1}')
|
|
410
|
+
[ -z "$Q_TO" ] && continue
|
|
411
|
+
_MATCH="false"
|
|
412
|
+
for _V in "$AUTHOR" "$_DISPLAY_Q" "$_FIRST_NAME_Q" "$_PEOPLE_NAME_Q"; do
|
|
413
|
+
[ -z "$_V" ] && continue
|
|
414
|
+
_V_LC=$(echo "$_V" | tr '[:upper:]' '[:lower:]')
|
|
415
|
+
[ "$Q_TO" = "$_V_LC" ] && _MATCH="true" && break
|
|
416
|
+
done
|
|
417
|
+
[ "$_MATCH" = "true" ] || continue
|
|
418
|
+
Q_STATUS=$(sed -n 's/^status:[[:space:]]*//p' "$QF" 2>/dev/null | head -1 | tr -d '"')
|
|
419
|
+
[ "$Q_STATUS" = "pending" ] || continue
|
|
420
|
+
Q_FROM=$(sed -n 's/^from:[[:space:]]*//p' "$QF" 2>/dev/null | head -1 | tr -d '"')
|
|
421
|
+
Q_TOPIC=$(sed -n 's/^topic:[[:space:]]*//p' "$QF" 2>/dev/null | head -1 | tr -d '"')
|
|
422
|
+
Q_HID=$(sed -n 's/^harvest_id:[[:space:]]*//p' "$QF" 2>/dev/null | head -1 | tr -d '"')
|
|
423
|
+
Q_REL="${QF#$SCRIPT_DIR/}"
|
|
424
|
+
LINE=$(jq -nc \
|
|
425
|
+
--arg from "$Q_FROM" \
|
|
426
|
+
--arg topic "$Q_TOPIC" \
|
|
427
|
+
--arg hid "$Q_HID" \
|
|
428
|
+
--arg file "$Q_REL" \
|
|
429
|
+
'{from:$from, topic:$topic, harvest_id:$hid, file:$file}' 2>/dev/null)
|
|
430
|
+
[ -n "$LINE" ] && LINES="$LINES$LINE
|
|
431
|
+
"
|
|
432
|
+
done
|
|
433
|
+
if [ -n "$LINES" ]; then
|
|
434
|
+
PENDING_JSON=$(echo "$LINES" | jq -sc '.' 2>/dev/null || echo "[]")
|
|
435
|
+
fi
|
|
436
|
+
fi
|
|
437
|
+
echo "$PENDING_JSON" > "$CTX_DIR/pending_questions"
|
|
438
|
+
) &
|
|
439
|
+
|
|
440
|
+
# 7. Graph health (background — zero added latency, runs in parallel)
|
|
441
|
+
(
|
|
442
|
+
if [ "$LOCAL_MODE" = "true" ]; then
|
|
443
|
+
echo "skip"
|
|
444
|
+
elif bash "$SCRIPT_DIR/bin/graph.sh" test 2>/dev/null | grep -q "Connected"; then
|
|
445
|
+
echo "ok"
|
|
446
|
+
else
|
|
447
|
+
echo "fail"
|
|
448
|
+
fi
|
|
449
|
+
) > "$CTX_DIR/graph_health" 2>/dev/null &
|
|
450
|
+
|
|
451
|
+
# 8. Telegram health (background)
|
|
452
|
+
(
|
|
453
|
+
if [ "$LOCAL_MODE" = "true" ]; then
|
|
454
|
+
echo "skip"
|
|
455
|
+
elif bash "$SCRIPT_DIR/bin/notify.sh" test 2>/dev/null | grep -q "connected"; then
|
|
456
|
+
echo "ok"
|
|
457
|
+
else
|
|
458
|
+
echo "fail"
|
|
459
|
+
fi
|
|
460
|
+
) > "$CTX_DIR/telegram_health" 2>/dev/null &
|
|
461
|
+
|
|
462
|
+
# 9. Lifecycle events: merged PRs + implemented handoffs (background)
|
|
463
|
+
(
|
|
464
|
+
GH_USER_LC=""
|
|
465
|
+
if [ -f "$STATE_FILE" ]; then
|
|
466
|
+
GH_USER_LC=$(jq -r '.github_username // empty' "$STATE_FILE" 2>/dev/null)
|
|
467
|
+
fi
|
|
468
|
+
DISPLAY_NAME_LC=""
|
|
469
|
+
if [ -f "$STATE_FILE" ]; then
|
|
470
|
+
DISPLAY_NAME_LC=$(jq -r '.display_name // empty' "$STATE_FILE" 2>/dev/null)
|
|
471
|
+
fi
|
|
472
|
+
# Get last session end date (use 7 days ago as fallback)
|
|
473
|
+
LAST_END=$(bash "$SCRIPT_DIR/bin/graph.sh" query "
|
|
474
|
+
MATCH (s:Session)-[:BY]->(p:Person {github: \$gh})
|
|
475
|
+
WHERE s.wrappedAt IS NOT NULL
|
|
476
|
+
RETURN toString(s.wrappedAt) AS t
|
|
477
|
+
ORDER BY s.wrappedAt DESC SKIP 1 LIMIT 1
|
|
478
|
+
" "$(jq -n --arg gh "$GH_USER_LC" '{gh: $gh}')" 2>/dev/null | jq -r '.values[0][0] // empty' 2>/dev/null)
|
|
479
|
+
if [ -z "$LAST_END" ]; then
|
|
480
|
+
LAST_END="$(date -v-7d +%Y-%m-%d 2>/dev/null || date -d '7 days ago' +%Y-%m-%d 2>/dev/null || echo '2026-03-03')T00:00:00Z"
|
|
481
|
+
fi
|
|
482
|
+
|
|
483
|
+
MERGED_JSON="[]"
|
|
484
|
+
if [ -n "$GH_USER_LC" ]; then
|
|
485
|
+
MERGED_JSON=$(bash "$SCRIPT_DIR/bin/graph-op.sh" my-merged-prs "$GH_USER_LC" "$(echo "$LAST_END" | cut -dT -f1)" 2>/dev/null || echo "[]")
|
|
486
|
+
fi
|
|
487
|
+
|
|
488
|
+
IMPL_JSON="[]"
|
|
489
|
+
IMPL_NAME="${DISPLAY_NAME_LC:-$GH_USER_LC}"
|
|
490
|
+
if [ -n "$IMPL_NAME" ]; then
|
|
491
|
+
IMPL_JSON=$(bash "$SCRIPT_DIR/bin/graph-op.sh" my-implemented-handoffs "$IMPL_NAME" "$(echo "$LAST_END" | cut -dT -f1)" 2>/dev/null || echo "[]")
|
|
492
|
+
fi
|
|
493
|
+
|
|
494
|
+
jq -n --argjson merged "$MERGED_JSON" --argjson impl "$IMPL_JSON" \
|
|
495
|
+
'{merged_prs: $merged, implemented_handoffs: $impl}' 2>/dev/null || echo '{"merged_prs":[],"implemented_handoffs":[]}'
|
|
496
|
+
) > "$CTX_DIR/lifecycle" 2>/dev/null &
|
|
497
|
+
|
|
498
|
+
# 10. Last Pulse brief (background) — gated on feature flag + API key
|
|
499
|
+
(
|
|
500
|
+
PULSE_ENABLED=$(jq -r '.features.pulse // "false"' "$CONFIG" 2>/dev/null || echo "false")
|
|
501
|
+
HAS_API_KEY=false
|
|
502
|
+
_env_file="${ENV_FILE:-$SCRIPT_DIR/.env}"
|
|
503
|
+
if [ -f "$_env_file" ]; then
|
|
504
|
+
_key=$(grep '^EGREGORE_API_KEY=' "$_env_file" 2>/dev/null | cut -d'=' -f2- || true)
|
|
505
|
+
[ -n "$_key" ] && HAS_API_KEY=true
|
|
506
|
+
fi
|
|
507
|
+
|
|
508
|
+
if [ "$PULSE_ENABLED" = "true" ] && [ "$HAS_API_KEY" = "true" ] && [ -n "$GH_USER_LC" ]; then
|
|
509
|
+
BRIEF_RESULT=$(bash "$SCRIPT_DIR/bin/graph.sh" query "
|
|
510
|
+
MATCH (p:Person {github: \$gh})
|
|
511
|
+
WHERE p.lastBrief IS NOT NULL
|
|
512
|
+
AND p.lastBriefDate >= date() - duration('P7D')
|
|
513
|
+
RETURN p.lastBrief, toString(p.lastBriefDate), p.lastRecommendations
|
|
514
|
+
" "$(jq -n --arg gh "$GH_USER_LC" '{gh: $gh}')" 2>/dev/null || echo '{"values":[]}')
|
|
515
|
+
BRIEF=$(echo "$BRIEF_RESULT" | jq -r '.values[0][0] // empty' 2>/dev/null || true)
|
|
516
|
+
BRIEF_DATE=$(echo "$BRIEF_RESULT" | jq -r '.values[0][1] // empty' 2>/dev/null || true)
|
|
517
|
+
RECS=$(echo "$BRIEF_RESULT" | jq -c '.values[0][2] // []' 2>/dev/null || echo '[]')
|
|
518
|
+
if [ -n "$BRIEF" ]; then
|
|
519
|
+
jq -n --arg brief "$BRIEF" --arg date "$BRIEF_DATE" --argjson recs "$RECS" \
|
|
520
|
+
'{brief: $brief, date: $date, recommendations: $recs}'
|
|
521
|
+
else
|
|
522
|
+
echo '{}'
|
|
523
|
+
fi
|
|
524
|
+
else
|
|
525
|
+
echo '{}'
|
|
526
|
+
fi
|
|
527
|
+
) > "$CTX_DIR/pulse_brief" 2>/dev/null &
|
|
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
|
+
|
|
535
|
+
# Wait for all context gathering + health checks to finish
|
|
536
|
+
wait
|
|
537
|
+
|
|
538
|
+
# --- Read service health results ---
|
|
539
|
+
GRAPH_RESULT=$(cat "$CTX_DIR/graph_health" 2>/dev/null || echo "")
|
|
540
|
+
if [ -n "$GRAPH_RESULT" ]; then HEALTH_GRAPH="$GRAPH_RESULT"; fi
|
|
541
|
+
TELEGRAM_RESULT=$(cat "$CTX_DIR/telegram_health" 2>/dev/null || echo "")
|
|
542
|
+
if [ -n "$TELEGRAM_RESULT" ]; then HEALTH_TELEGRAM="$TELEGRAM_RESULT"; fi
|