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.
- package/assets/egregore-launcher.js +411 -25
- package/bin/cli.js +34 -20
- package/lib/local.js +21 -5
- package/lib/pi-runtime.js +186 -0
- package/lib/setup.js +56 -11
- package/lib/ui.js +1 -1
- package/package.json +7 -5
- package/runtime/codex/.codex/spec-manifest.json +1 -1
- package/runtime/codex/AGENTS.md +7 -2
- package/runtime/codex/bin/codex-render-spec.mjs +6 -1
- package/runtime/codex/bin/codex-session-start.sh +5 -59
- package/runtime/codex/bin/codex-skill-render.mjs +7 -5
- package/runtime/codex/bin/lib/context.sh +8 -1
- package/runtime/codex/bin/lib/greeting.sh +12 -108
- package/runtime/codex/bin/lib/metrics.sh +374 -0
- package/runtime/codex/bin/publish-artifact.sh +14 -0
- package/runtime/codex/bin/settings.sh +274 -0
- package/runtime/codex/manifest.json +2 -0
- 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
|