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
|
@@ -48,6 +48,7 @@ PROJECT=""
|
|
|
48
48
|
NO_PUSH=0
|
|
49
49
|
NO_NOTIFY=0
|
|
50
50
|
NO_PUBLISH=0
|
|
51
|
+
COMPOSED="" # optional house-kit JSON — the agent-composed render spec
|
|
51
52
|
|
|
52
53
|
while [ $# -gt 0 ]; do
|
|
53
54
|
case "$1" in
|
|
@@ -55,6 +56,7 @@ while [ $# -gt 0 ]; do
|
|
|
55
56
|
--topic) TOPIC="${2:?}"; shift 2 ;;
|
|
56
57
|
--recipient) RECIPIENT="${2:-}"; shift 2 ;;
|
|
57
58
|
--project) PROJECT="${2:-}"; shift 2 ;;
|
|
59
|
+
--composed) COMPOSED="${2:-}"; shift 2 ;;
|
|
58
60
|
--no-push) NO_PUSH=1; shift ;;
|
|
59
61
|
--no-notify) NO_NOTIFY=1; shift ;;
|
|
60
62
|
--no-publish) NO_PUBLISH=1; shift ;;
|
|
@@ -62,6 +64,16 @@ while [ $# -gt 0 ]; do
|
|
|
62
64
|
esac
|
|
63
65
|
done
|
|
64
66
|
|
|
67
|
+
# The render source: the agent-composed house-kit JSON when supplied (rich
|
|
68
|
+
# component render), else the markdown handoff (deterministic floor). The
|
|
69
|
+
# markdown file is ALWAYS the memory record + graph index either way.
|
|
70
|
+
RENDER_TYPE="handoff"
|
|
71
|
+
RENDER_SRC="" # resolved to $ABS_FILE after the file is written, unless composed
|
|
72
|
+
if [ -n "$COMPOSED" ] && [ -f "$COMPOSED" ]; then
|
|
73
|
+
RENDER_TYPE="composed"
|
|
74
|
+
RENDER_SRC="$COMPOSED"
|
|
75
|
+
fi
|
|
76
|
+
|
|
65
77
|
[ -z "$AUTHOR" ] && { echo "Missing --author" >&2; exit 1; }
|
|
66
78
|
[ -z "$TOPIC" ] && { echo "Missing --topic" >&2; exit 1; }
|
|
67
79
|
|
|
@@ -203,14 +215,13 @@ PID_GRAPH=$!
|
|
|
203
215
|
ARTIFACTS_JSON="[]"
|
|
204
216
|
if [ "$MODE" = "connected" ]; then
|
|
205
217
|
YY=$(date +%Y); MM=$(date +%-m); DD_NUM=$(date +%-d)
|
|
206
|
-
PARAMS=$(jq -nc --arg gh "$AUTHOR" '{gh:$gh}')
|
|
207
218
|
RAW=$(bash "$SCRIPT_DIR/bin/graph.sh" query "
|
|
208
219
|
MATCH (a:Artifact)-[:CONTRIBUTED_BY]->(p:Person {github: \$gh})
|
|
209
220
|
WHERE a.created >= datetime({year: $YY, month: $MM, day: $DD_NUM})
|
|
210
221
|
AND NOT 'tutorial-generated' IN coalesce(a.topics, [])
|
|
211
222
|
RETURN a.title AS title, a.type AS type, a.filePath AS path
|
|
212
223
|
ORDER BY a.created DESC
|
|
213
|
-
LIMIT 5" "$
|
|
224
|
+
LIMIT 5" "$(jq -nc --arg gh "$AUTHOR" '{gh:$gh}')" 2>/dev/null || echo '{}')
|
|
214
225
|
# Response shape: {"fields":[...], "values":[[...], ...]}. Convert to
|
|
215
226
|
# an array of {title, type, path} objects.
|
|
216
227
|
ARTIFACTS_JSON=$(echo "$RAW" | jq -c '
|
|
@@ -282,24 +293,145 @@ PID_ARTIFACTS=$!
|
|
|
282
293
|
PID_MEMORY=$!
|
|
283
294
|
|
|
284
295
|
# --- Branch C: Publish artifact → Notify ---
|
|
296
|
+
#
|
|
297
|
+
# Strategy: handoffs publish ONCE, through the emissary write API, using
|
|
298
|
+
# `render_mode: "custom"` so the server serves OUR pre-rendered HTML
|
|
299
|
+
# verbatim (the same egregore-artifacts handoff template /view/ has
|
|
300
|
+
# always used). This gives us:
|
|
301
|
+
# - one artifact, one URL (no legacy + new double-publish)
|
|
302
|
+
# - permanent egregore.xyz/emissary/e/<id> URL with /raw endpoint
|
|
303
|
+
# - handoff design fully owned client-side (egregore-artifacts) — the
|
|
304
|
+
# emissary server template is never invoked for handoffs and remains
|
|
305
|
+
# completely untouched
|
|
306
|
+
#
|
|
307
|
+
# Fallback: if emissary publish fails (token missing, network, size cap,
|
|
308
|
+
# rendering failure), fall back to the legacy publish-artifact.sh path so
|
|
309
|
+
# a handoff still gets a Telegram-able URL.
|
|
285
310
|
(
|
|
311
|
+
V1URL=""
|
|
286
312
|
AURL=""
|
|
287
|
-
if [ "$NO_PUBLISH" = "0" ]
|
|
288
|
-
|
|
313
|
+
if [ "$NO_PUBLISH" = "0" ]; then
|
|
314
|
+
EMISSARY_CONFIG="$HOME/.egregore/emissary-config.json"
|
|
315
|
+
EMISSARY_TOKEN=""
|
|
316
|
+
if [ -f "$EMISSARY_CONFIG" ]; then
|
|
317
|
+
EMISSARY_TOKEN=$(jq -r '.auth_token // empty' "$EMISSARY_CONFIG" 2>/dev/null)
|
|
318
|
+
fi
|
|
319
|
+
EMISSARY_API_URL="${EMISSARY_API_URL:-https://egregore-production-55f2.up.railway.app/api/v1/emissary}"
|
|
320
|
+
|
|
321
|
+
if [ -n "$EMISSARY_TOKEN" ]; then
|
|
322
|
+
# Step 1: render the handoff to HTML locally using egregore-artifacts.
|
|
323
|
+
# Same renderer + template publish-artifact.sh uses today — handoffs
|
|
324
|
+
# keep their familiar look.
|
|
325
|
+
RENDERED_HTML="$TMPD/handoff.html"
|
|
326
|
+
RENDER_OK=0
|
|
327
|
+
LOCAL_CLI="$SCRIPT_DIR/packages/egregore-artifacts/bin/cli.js"
|
|
328
|
+
if [ "${EGREGORE_USE_PUBLISHED:-0}" != "1" ] && [ -f "$LOCAL_CLI" ] && [ -d "$SCRIPT_DIR/packages/egregore-artifacts/node_modules/react" ]; then
|
|
329
|
+
if node "$LOCAL_CLI" "$RENDER_TYPE" "${RENDER_SRC:-$ABS_FILE}" --output "$RENDERED_HTML" >/dev/null 2>&1; then
|
|
330
|
+
RENDER_OK=1
|
|
331
|
+
fi
|
|
332
|
+
else
|
|
333
|
+
if npx egregore-artifacts "$RENDER_TYPE" "${RENDER_SRC:-$ABS_FILE}" --output "$RENDERED_HTML" >/dev/null 2>&1; then
|
|
334
|
+
RENDER_OK=1
|
|
335
|
+
fi
|
|
336
|
+
fi
|
|
337
|
+
|
|
338
|
+
# Step 2: build the emissary payload. If rendering succeeded AND the
|
|
339
|
+
# HTML fits the server's 100KB cap, send it with render_mode=custom.
|
|
340
|
+
# If not, fall through to the structured-only path (server-rendered
|
|
341
|
+
# via the emissary template — undesirable for handoffs but still
|
|
342
|
+
# better than no URL).
|
|
343
|
+
if [ "$RENDER_OK" = "1" ] && [ -f "$RENDERED_HTML" ]; then
|
|
344
|
+
RENDER_SIZE=$(wc -c < "$RENDERED_HTML" | tr -d ' ')
|
|
345
|
+
if [ "$RENDER_SIZE" -gt 0 ] && [ "$RENDER_SIZE" -lt 100000 ]; then
|
|
346
|
+
# Author display name
|
|
347
|
+
DISPLAY_NAME="$AUTHOR"
|
|
348
|
+
PERSON_FILE="$SCRIPT_DIR/memory/people/${AUTHOR}.md"
|
|
349
|
+
if [ -f "$PERSON_FILE" ]; then
|
|
350
|
+
DISPLAY_NAME=$(head -1 "$PERSON_FILE" | sed 's/^# //')
|
|
351
|
+
fi
|
|
352
|
+
|
|
353
|
+
# addressed_to: only set if there's a recipient
|
|
354
|
+
ADDRESSED_TO="[]"
|
|
355
|
+
if [ -n "$RECIPIENT" ]; then
|
|
356
|
+
RECIP_HANDLE=$(echo "$RECIPIENT" | tr '[:upper:]' '[:lower:]' | awk '{print $1}')
|
|
357
|
+
ADDRESSED_TO=$(jq -nc --arg h "$RECIP_HANDLE" --arg d "$RECIPIENT" '[{"handle":$h,"display":$d}]')
|
|
358
|
+
fi
|
|
359
|
+
|
|
360
|
+
CLAIM=$(echo "$BRIEFING_LEAD" | cut -c1-200)
|
|
361
|
+
|
|
362
|
+
# body.prose is schema-required non-empty even when render_mode is
|
|
363
|
+
# "custom" (server validates structurally regardless of render
|
|
364
|
+
# mode). The visible page is the custom HTML; prose just needs to
|
|
365
|
+
# be non-empty plain text for /raw consumers and validation. Use
|
|
366
|
+
# the briefing lead (which the lead/claim is already derived from).
|
|
367
|
+
PROSE_TEXT="${BRIEFING_LEAD:-$TOPIC}"
|
|
368
|
+
|
|
369
|
+
# Use --rawfile so the rendered HTML is read directly from disk
|
|
370
|
+
# into a JSON-string. Faster than shell-var roundtrip and avoids
|
|
371
|
+
# any quoting headaches for HTML.
|
|
372
|
+
V1JSON=$(jq -nc \
|
|
373
|
+
--arg topic "$TOPIC" \
|
|
374
|
+
--arg claim "$CLAIM" \
|
|
375
|
+
--arg summary "$CLAIM" \
|
|
376
|
+
--arg prose "$PROSE_TEXT" \
|
|
377
|
+
--argjson addressed "$ADDRESSED_TO" \
|
|
378
|
+
--rawfile render_html "$RENDERED_HTML" \
|
|
379
|
+
'{
|
|
380
|
+
kind: "continuation",
|
|
381
|
+
topic: $topic,
|
|
382
|
+
claim: $claim,
|
|
383
|
+
summary: $summary,
|
|
384
|
+
body: { prose: $prose },
|
|
385
|
+
audience: {
|
|
386
|
+
addressed_to: $addressed,
|
|
387
|
+
visible_to: "public",
|
|
388
|
+
extendable_by: "anyone"
|
|
389
|
+
},
|
|
390
|
+
parents: [],
|
|
391
|
+
render_mode: "custom",
|
|
392
|
+
render_html: $render_html
|
|
393
|
+
}')
|
|
394
|
+
|
|
395
|
+
V1RESULT=$(curl -sf -X POST "$EMISSARY_API_URL/emissaries" \
|
|
396
|
+
-H "Content-Type: application/json" \
|
|
397
|
+
-H "Authorization: Bearer $EMISSARY_TOKEN" \
|
|
398
|
+
-d "$V1JSON" 2>/dev/null || echo '{}')
|
|
399
|
+
|
|
400
|
+
V1URL=$(echo "$V1RESULT" | jq -r '.url // ""' 2>/dev/null || echo "")
|
|
401
|
+
fi
|
|
402
|
+
fi
|
|
403
|
+
fi
|
|
404
|
+
fi
|
|
405
|
+
|
|
406
|
+
# Fallback: if the emissary publish didn't produce a URL (no token, no
|
|
407
|
+
# network, size cap, render failure), fire the legacy publish-artifact.sh
|
|
408
|
+
# so handoffs still get a Telegram-able URL.
|
|
409
|
+
if [ -z "$V1URL" ] && [ "$NO_PUBLISH" = "0" ] && [ -x "$SCRIPT_DIR/bin/publish-artifact.sh" ]; then
|
|
410
|
+
AURL=$(bash "$SCRIPT_DIR/bin/publish-artifact.sh" "$RENDER_TYPE" "${RENDER_SRC:-$ABS_FILE}" \
|
|
289
411
|
--title "$TOPIC" \
|
|
290
412
|
--author "$AUTHOR" \
|
|
291
413
|
--description "$BRIEFING_LEAD" 2>/dev/null || echo "")
|
|
292
414
|
fi
|
|
293
|
-
|
|
415
|
+
|
|
416
|
+
# Prefer v1 (emissary-rendered) URL when available; fall back to the
|
|
417
|
+
# legacy egregore-artifacts URL only if the v1 publish failed.
|
|
418
|
+
NOTIFY_URL=""
|
|
419
|
+
if [ -n "$V1URL" ]; then
|
|
420
|
+
echo "$V1URL" > "$TMPD/artifact"
|
|
421
|
+
NOTIFY_URL="$V1URL"
|
|
422
|
+
else
|
|
423
|
+
echo "$AURL" > "$TMPD/artifact"
|
|
424
|
+
NOTIFY_URL="$AURL"
|
|
425
|
+
fi
|
|
294
426
|
|
|
295
427
|
NS="skipped"
|
|
296
428
|
if [ "$NO_NOTIFY" = "0" ]; then
|
|
297
|
-
if [ -n "$
|
|
429
|
+
if [ -n "$NOTIFY_URL" ]; then
|
|
298
430
|
MSG_NOTIFY="Handoff from ${AUTHOR}: ${TOPIC}
|
|
299
431
|
|
|
300
432
|
\"${BRIEFING_SHORT}\"
|
|
301
433
|
|
|
302
|
-
View: ${
|
|
434
|
+
View: ${NOTIFY_URL}"
|
|
303
435
|
else
|
|
304
436
|
MSG_NOTIFY="Handoff from ${AUTHOR}: ${TOPIC}
|
|
305
437
|
|
|
@@ -345,9 +477,27 @@ if [ -s "$TMPD/graph" ]; then
|
|
|
345
477
|
GRAPH_STATUS=$(sed -n '3p' "$TMPD/graph")
|
|
346
478
|
fi
|
|
347
479
|
[ -z "$RESOLVED" ] && RESOLVED=0
|
|
480
|
+
# Sanitize: --argjson requires a number. If RESOLVED came back as a slug or
|
|
481
|
+
# anything non-numeric (the index-handoff.sh output format has drifted in
|
|
482
|
+
# the past — defensive guard), fall back to 0 so the result file still builds.
|
|
483
|
+
if ! echo "$RESOLVED" | grep -qE '^[0-9]+$'; then
|
|
484
|
+
RESOLVED=0
|
|
485
|
+
fi
|
|
348
486
|
|
|
349
487
|
SUBGRAPH_JSON=$(cat "$TMPD/subgraph" 2>/dev/null || echo "null")
|
|
350
488
|
[ -z "$SUBGRAPH_JSON" ] && SUBGRAPH_JSON="null"
|
|
489
|
+
# Sanitize: --argjson requires valid JSON. Multiple writes to the same
|
|
490
|
+
# tmpfile have produced concatenated values in the past (e.g. "null\n{...}"),
|
|
491
|
+
# which jq rejects on multi-document input. Validate; on failure, take the
|
|
492
|
+
# LAST non-empty line and re-validate; if still bad, fall back to null.
|
|
493
|
+
if ! printf '%s' "$SUBGRAPH_JSON" | jq -s 'length == 1' 2>/dev/null | grep -q '^true$'; then
|
|
494
|
+
LAST_LINE=$(printf '%s\n' "$SUBGRAPH_JSON" | awk 'NF{last=$0} END{print last}')
|
|
495
|
+
if printf '%s' "$LAST_LINE" | jq -e '.' >/dev/null 2>&1; then
|
|
496
|
+
SUBGRAPH_JSON="$LAST_LINE"
|
|
497
|
+
else
|
|
498
|
+
SUBGRAPH_JSON="null"
|
|
499
|
+
fi
|
|
500
|
+
fi
|
|
351
501
|
|
|
352
502
|
MEMORY_STATUS=$(cat "$TMPD/memory" 2>/dev/null || echo "skipped")
|
|
353
503
|
ARTIFACT_URL=$(cat "$TMPD/artifact" 2>/dev/null || echo "")
|
|
@@ -45,6 +45,11 @@ SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
|
45
45
|
[ -z "$REPO_DIR" ] && REPO_DIR="$SCRIPT_DIR"
|
|
46
46
|
cd "$REPO_DIR" || exit 1
|
|
47
47
|
|
|
48
|
+
# Shared safe-staging guard — never auto-commit stray sibling-repo gitlinks.
|
|
49
|
+
# shellcheck source=bin/lib/git-safe.sh
|
|
50
|
+
. "$SCRIPT_DIR/bin/lib/git-safe.sh" 2>/dev/null || true
|
|
51
|
+
type git_add_guarded >/dev/null 2>&1 || git_add_guarded() { git add -A 2>/dev/null || true; }
|
|
52
|
+
|
|
48
53
|
# --- Early exit if nothing to do -----------------------------------------
|
|
49
54
|
DIRTY=0
|
|
50
55
|
[ -n "$(git status --porcelain 2>/dev/null)" ] && DIRTY=1
|
|
@@ -75,7 +80,7 @@ esac
|
|
|
75
80
|
|
|
76
81
|
# --- Commit whatever is uncommitted --------------------------------------
|
|
77
82
|
if [ -n "$(git status --porcelain 2>/dev/null)" ]; then
|
|
78
|
-
|
|
83
|
+
git_add_guarded
|
|
79
84
|
git commit -m "Handoff: ${TOPIC}" --quiet 2>/dev/null || true
|
|
80
85
|
fi
|
|
81
86
|
|
|
@@ -131,6 +131,10 @@ fi
|
|
|
131
131
|
PROJECT="$(md_field 'Project')"
|
|
132
132
|
[ -z "$PROJECT" ] && PROJECT="$(yaml_extract 'project')"
|
|
133
133
|
|
|
134
|
+
# Handoff kind: used by readers to distinguish automatic captures from manual handoffs.
|
|
135
|
+
KIND="$(md_field 'Kind')"
|
|
136
|
+
[ -z "$KIND" ] && KIND="$(yaml_extract 'kind')"
|
|
137
|
+
|
|
134
138
|
# Summary: 5-level precedence chain
|
|
135
139
|
# 1. Frontmatter summary field
|
|
136
140
|
SUMMARY="$(yaml_extract 'summary')"
|
|
@@ -198,8 +202,9 @@ fi
|
|
|
198
202
|
|
|
199
203
|
Q1_CYPHER="MATCH (p:Person) WHERE toLower(p.name) = \$author OR p.github = \$author OR toLower(p.fullName) = \$author OR \$author IN [x IN coalesce(p.previousNames, []) | toLower(x)]
|
|
200
204
|
MERGE (s:Session {id: \$sessionId})
|
|
201
|
-
ON CREATE SET s.date = date(\$date), s.topic = \$topic, s.summary = \$summary, s.filePath = \$filePath, s.handoffStatus = 'pending', s.repoState = \$repoState, s.startedAt = datetime(\$date + 'T00:00:00Z')
|
|
202
|
-
ON MATCH SET s.topic = \$topic, s.summary = \$summary, s.filePath = \$filePath, s.repoState = \$repoState
|
|
205
|
+
ON CREATE SET s.date = date(\$date), s.topic = \$topic, s.summary = \$summary, s.filePath = \$filePath, s.author = \$author, s.handoffKind = CASE WHEN \$kind = '' THEN null ELSE \$kind END, s.handoffStatus = 'pending', s.repoState = \$repoState, s.startedAt = datetime(\$date + 'T00:00:00Z')
|
|
206
|
+
ON MATCH SET s.topic = \$topic, s.summary = \$summary, s.filePath = \$filePath, s.author = \$author, s.handoffKind = CASE WHEN \$kind = '' THEN null ELSE \$kind END, s.repoState = \$repoState
|
|
207
|
+
WITH s, p
|
|
203
208
|
MERGE (s)-[:BY]->(p)${PROJECT_CYPHER}${HANDED_TO_CYPHER}
|
|
204
209
|
RETURN s.id AS sessionId"
|
|
205
210
|
|
|
@@ -210,6 +215,7 @@ Q1_PARAMS=$(jq -n \
|
|
|
210
215
|
--arg topic "$TOPIC" \
|
|
211
216
|
--arg summary "$SUMMARY" \
|
|
212
217
|
--arg filePath "$REL_PATH" \
|
|
218
|
+
--arg kind "$KIND" \
|
|
213
219
|
--arg repoState "$REPO_STATE_JSON" \
|
|
214
220
|
"${_PROJECT_ARGS[@]:+${_PROJECT_ARGS[@]}}" \
|
|
215
221
|
"${_RECIP_ARGS[@]:+${_RECIP_ARGS[@]}}" \
|
|
@@ -273,7 +279,7 @@ CURRENT_SID=$(cat "$HOME/.egregore/session-${PROJ_HASH}.id" 2>/dev/null || echo
|
|
|
273
279
|
if [ -n "$CURRENT_SID" ]; then
|
|
274
280
|
bash "$SCRIPT_DIR/bin/graph.sh" query \
|
|
275
281
|
"MATCH (s:Session {id: \$sid}) SET s.status = 'handed_off' RETURN s.id" \
|
|
276
|
-
"$(jq -n --arg sid "$CURRENT_SID" '{sid: $sid}')"
|
|
282
|
+
"$(jq -n --arg sid "$CURRENT_SID" '{sid: $sid}')" >/dev/null 2>&1 || true
|
|
277
283
|
fi
|
|
278
284
|
|
|
279
285
|
# --- Parse response ---
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# shellcheck shell=bash
|
|
2
2
|
# context.sh — Context gathering for session-start.sh
|
|
3
3
|
#
|
|
4
|
-
# Runs
|
|
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.
|
|
@@ -23,6 +24,8 @@
|
|
|
23
24
|
|
|
24
25
|
CTX_DIR=$(mktemp -d)
|
|
25
26
|
|
|
27
|
+
source "$SCRIPT_DIR/bin/lib/handoff-meta.sh" >/dev/null 2>/dev/null || true
|
|
28
|
+
|
|
26
29
|
# Time of day
|
|
27
30
|
HOUR=$(date +%H)
|
|
28
31
|
if [ "$HOUR" -lt 12 ]; then TIME_OF_DAY="morning"
|
|
@@ -177,6 +180,31 @@ fi
|
|
|
177
180
|
)
|
|
178
181
|
fi
|
|
179
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
|
+
|
|
180
208
|
# --- Relative time + epoch helpers (cross-platform) ---
|
|
181
209
|
NOW_EPOCH=$(date +%s)
|
|
182
210
|
|
|
@@ -270,8 +298,9 @@ fi
|
|
|
270
298
|
BRANCHES_JSON=$(echo "$BRANCHES_JSON" | jq --arg e "+${EXTRA} more" '[.[0:2][], $e]' 2>/dev/null || echo "$BRANCHES_JSON")
|
|
271
299
|
fi
|
|
272
300
|
|
|
273
|
-
|
|
274
|
-
|
|
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}')
|
|
275
304
|
$FIRST || PRESENCE="$PRESENCE,"
|
|
276
305
|
PRESENCE="$PRESENCE$ENTRY"
|
|
277
306
|
FIRST=false
|
|
@@ -320,18 +349,9 @@ fi
|
|
|
320
349
|
for AF in $ADDRESSED; do
|
|
321
350
|
[ -z "$AF" ] && continue
|
|
322
351
|
AF_NAME=$(basename "$AF" .md)
|
|
323
|
-
AF_AUTHOR=$(
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
fi
|
|
327
|
-
AF_TOPIC=$(sed -n 's/^# Handoff: *//p' "$AF" 2>/dev/null | head -1)
|
|
328
|
-
if [ -z "$AF_TOPIC" ]; then
|
|
329
|
-
AF_TOPIC=$(sed -n 's/^# *//p' "$AF" 2>/dev/null | head -1)
|
|
330
|
-
fi
|
|
331
|
-
AF_DATE=$(sed -n 's/^\*\*Date\*\*: *//p' "$AF" 2>/dev/null | head -1)
|
|
332
|
-
if [ -z "$AF_DATE" ]; then
|
|
333
|
-
AF_DATE=$(sed -n 's/^Date: *//p' "$AF" 2>/dev/null | head -1)
|
|
334
|
-
fi
|
|
352
|
+
AF_AUTHOR=$(eg_handoff_author "$AF")
|
|
353
|
+
AF_TOPIC=$(eg_handoff_topic "$AF")
|
|
354
|
+
AF_DATE=$(eg_handoff_date "$AF")
|
|
335
355
|
# Extract repo state table if present
|
|
336
356
|
AF_REPO_STATE="[]"
|
|
337
357
|
AF_REPO_TABLE=$(awk '/^## Repo State/{found=1; next} found && /^#/{exit} found && /^\|[^-]/ && !/^\| Repo/{print}' "$AF" 2>/dev/null || true)
|
|
@@ -352,9 +372,14 @@ fi
|
|
|
352
372
|
$FIRST || JSON="$JSON,"
|
|
353
373
|
$FIRST || RICH="$RICH,"
|
|
354
374
|
JSON="$JSON\"$AF_NAME\""
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
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"
|
|
358
383
|
FIRST=false
|
|
359
384
|
done
|
|
360
385
|
JSON="$JSON]"
|
|
@@ -364,6 +389,54 @@ fi
|
|
|
364
389
|
echo "$RICH" > "$CTX_DIR/addressed_rich"
|
|
365
390
|
) &
|
|
366
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
|
+
|
|
367
440
|
# 7. Graph health (background — zero added latency, runs in parallel)
|
|
368
441
|
(
|
|
369
442
|
if [ "$LOCAL_MODE" = "true" ]; then
|
|
@@ -453,6 +526,12 @@ fi
|
|
|
453
526
|
fi
|
|
454
527
|
) > "$CTX_DIR/pulse_brief" 2>/dev/null &
|
|
455
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
|
+
|
|
456
535
|
# Wait for all context gathering + health checks to finish
|
|
457
536
|
wait
|
|
458
537
|
|
|
@@ -90,7 +90,7 @@ mkdir -p /tmp
|
|
|
90
90
|
printf '## Team Activity\n\n'
|
|
91
91
|
printf '| Name | Last Seen | Working On |\n'
|
|
92
92
|
printf '|------|-----------|------------|\n'
|
|
93
|
-
echo "$_DB_TEAM_FILTERED" | jq -r '.[] | "| \(.name) | \(.last_seen) | \(
|
|
93
|
+
echo "$_DB_TEAM_FILTERED" | jq -r '.[] | ((.working_on // "")) as $w | (.branches // []) as $b | "| \(.name) | \(.last_seen) | \(if $w != "" then $w else ($b | join(", ")) end) |"' 2>/dev/null || true
|
|
94
94
|
printf '\n'
|
|
95
95
|
fi
|
|
96
96
|
fi
|
|
@@ -26,7 +26,20 @@ git config pull.rebase true 2>/dev/null || true
|
|
|
26
26
|
export GIT_HTTP_LOW_SPEED_LIMIT=1000 # abort if <1KB/s
|
|
27
27
|
export GIT_HTTP_LOW_SPEED_TIME=10 # for 10 seconds
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
# Attendant fast path: the ambient daemon (bin/attendant.sh) fetches origin,
|
|
30
|
+
# memory, and managed repos every few minutes. If its warm marker is fresh,
|
|
31
|
+
# refs are already up to date — skip the network fetches and launch on local
|
|
32
|
+
# ops only. Upstream fetch (framework update check) is not covered by the
|
|
33
|
+
# attendant and stays unconditional below.
|
|
34
|
+
_REFS_FRESH="false"
|
|
35
|
+
_ATT_SLUG=$(jq -r '.slug // "egregore"' "$SCRIPT_DIR/egregore.json" 2>/dev/null || echo "egregore")
|
|
36
|
+
_ATT_MARKER="$HOME/.egregore/attendant/${_ATT_SLUG}-$(echo -n "$SCRIPT_DIR" | cksum | cut -d' ' -f1).warm-ts"
|
|
37
|
+
if [ -f "$_ATT_MARKER" ]; then
|
|
38
|
+
_ATT_AGE=$(( $(date +%s) - $(cat "$_ATT_MARKER" 2>/dev/null || echo 0) ))
|
|
39
|
+
[ "$_ATT_AGE" -lt 360 ] && _REFS_FRESH="true"
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
[ "$_REFS_FRESH" != "true" ] && git fetch origin --quiet 2>/dev/null &
|
|
30
43
|
|
|
31
44
|
# Fetch upstream framework (for update check — non-blocking)
|
|
32
45
|
# Default: official egregore-core repo. Set upstream_url in egregore.json to override,
|
|
@@ -40,17 +53,19 @@ fi
|
|
|
40
53
|
|
|
41
54
|
# Sync memory in parallel (symlink or direct directory — both valid)
|
|
42
55
|
MEMORY_SYNCED="false"
|
|
43
|
-
if [ -d "$SCRIPT_DIR/memory/.git" ]; then
|
|
56
|
+
if [ -d "$SCRIPT_DIR/memory/.git" ] && [ "$_REFS_FRESH" != "true" ]; then
|
|
44
57
|
git -C "$SCRIPT_DIR/memory" fetch origin --quiet 2>/dev/null &
|
|
45
58
|
fi
|
|
46
59
|
|
|
47
60
|
# Fetch managed repos in parallel
|
|
48
61
|
MANAGED_REPOS=$(jq -r '(.repos[]? // empty) | if type == "object" then .name else . end' "$SCRIPT_DIR/egregore.json" 2>/dev/null)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
62
|
+
if [ "$_REFS_FRESH" != "true" ]; then
|
|
63
|
+
for REPO in $MANAGED_REPOS; do
|
|
64
|
+
if [ -d "$SCRIPT_DIR/../$REPO/.git" ]; then
|
|
65
|
+
git -C "$SCRIPT_DIR/../$REPO" fetch origin --quiet 2>/dev/null &
|
|
66
|
+
fi
|
|
67
|
+
done
|
|
68
|
+
fi
|
|
54
69
|
|
|
55
70
|
# Wait for all fetches
|
|
56
71
|
wait 2>/dev/null || true
|
|
@@ -93,6 +108,16 @@ else
|
|
|
93
108
|
HEALTH_GIT="fail"
|
|
94
109
|
fi
|
|
95
110
|
|
|
111
|
+
# --- Guarded staging -------------------------------------------------------
|
|
112
|
+
# git_add_guarded() lives in the shared lib so the /handoff auto-save uses the
|
|
113
|
+
# exact same logic. Fallback to plain `git add -A` if the lib can't be sourced,
|
|
114
|
+
# so an unattended auto-save never silently no-ops.
|
|
115
|
+
# shellcheck source=bin/lib/git-safe.sh
|
|
116
|
+
. "$SCRIPT_DIR/bin/lib/git-safe.sh" 2>/dev/null || true
|
|
117
|
+
if ! type git_add_guarded >/dev/null 2>&1; then
|
|
118
|
+
git_add_guarded() { git add -A 2>/dev/null || true; }
|
|
119
|
+
fi
|
|
120
|
+
|
|
96
121
|
# --- Setup develop (wrapped — failures set HEALTH_GIT but don't crash) ---
|
|
97
122
|
setup_develop() {
|
|
98
123
|
# Ensure develop branch exists locally
|
|
@@ -143,8 +168,9 @@ setup_develop() {
|
|
|
143
168
|
if [[ "$CURRENT_BRANCH" != "develop" ]]; then
|
|
144
169
|
# Check for uncommitted work (staged + unstaged + untracked in tracked dirs)
|
|
145
170
|
if [ -n "$(git status --porcelain 2>/dev/null | head -1)" ]; then
|
|
146
|
-
# Save uncommitted work so nothing is lost
|
|
147
|
-
|
|
171
|
+
# Save uncommitted work so nothing is lost (guarded: never auto-commit
|
|
172
|
+
# stray submodule pointers from sibling repos in the checkout)
|
|
173
|
+
git_add_guarded
|
|
148
174
|
git commit -m "Auto-save: uncommitted work from $CURRENT_BRANCH" --quiet 2>/dev/null || true
|
|
149
175
|
SAVED_BRANCH="$CURRENT_BRANCH"
|
|
150
176
|
fi
|