create-egregore 0.19.8 → 0.19.11
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/lib/local.js +11 -1
- package/lib/setup.js +33 -0
- package/package.json +1 -1
- package/runtime/codex/.codex/hooks/branch-guard.js +242 -34
- package/runtime/codex/.codex/skills/activity/SKILL.md +18 -6
- package/runtime/codex/.codex/skills/deep-reflect/SKILL.md +90 -55
- package/runtime/codex/.codex/skills/deploy-bridge/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/handoff/SKILL.md +6 -2
- package/runtime/codex/.codex/skills/save/SKILL.md +21 -3
- package/runtime/codex/.codex/skills/scroll/SKILL.md +54 -3
- package/runtime/codex/.codex/skills/task/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/tui-design/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/wrap/SKILL.md +4 -1
- package/runtime/codex/.codex/spec-manifest.json +15 -3
- package/runtime/codex/AGENTS.md +45 -9
- package/runtime/codex/bin/activity-data.sh +1 -1
- package/runtime/codex/bin/agent.sh +66 -23
- package/runtime/codex/bin/attendant.sh +525 -0
- package/runtime/codex/bin/capture-reconcile.sh +67 -0
- package/runtime/codex/bin/capture-run.sh +341 -0
- package/runtime/codex/bin/codex-render-spec.mjs +15 -4
- package/runtime/codex/bin/codex-session-start.sh +45 -3
- package/runtime/codex/bin/codex-skill-render.mjs +23 -4
- package/runtime/codex/bin/graph-op.sh +719 -49
- package/runtime/codex/bin/graph-wal.sh +3 -2
- package/runtime/codex/bin/handoff-run.sh +72 -0
- package/runtime/codex/bin/handoff-save-egregore.sh +51 -11
- package/runtime/codex/bin/index-handoff.sh +26 -17
- package/runtime/codex/bin/ingest-graph.sh +80 -0
- package/runtime/codex/bin/ingest-surface.html +429 -0
- package/runtime/codex/bin/ingest.py +614 -0
- package/runtime/codex/bin/ingest.sh +231 -0
- package/runtime/codex/bin/ingest_graph.py +776 -0
- package/runtime/codex/bin/ingest_surface.py +464 -0
- package/runtime/codex/bin/lib/git-sync.sh +2 -6
- package/runtime/codex/bin/lib/greeting.sh +157 -22
- package/runtime/codex/bin/lib/identity.sh +50 -29
- package/runtime/codex/bin/lib/noncode.sh +31 -0
- package/runtime/codex/bin/person.py +610 -0
- package/runtime/codex/bin/person.sh +448 -0
- package/runtime/codex/bin/session-autosave.sh +184 -0
- package/runtime/codex/bin/session-log.sh +199 -0
- package/runtime/codex/bin/settings.sh +1 -1
- package/runtime/codex/manifest.json +19 -1
- package/runtime/pi/.codex/hooks/branch-guard.js +242 -34
- package/runtime/pi/.codex/skills/activity/SKILL.md +18 -6
- package/runtime/pi/.codex/skills/deep-reflect/SKILL.md +90 -55
- package/runtime/pi/.codex/skills/deploy-bridge/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/handoff/SKILL.md +6 -2
- package/runtime/pi/.codex/skills/save/SKILL.md +21 -3
- package/runtime/pi/.codex/skills/scroll/SKILL.md +54 -3
- package/runtime/pi/.codex/skills/task/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/tui-design/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/wrap/SKILL.md +4 -1
- package/runtime/pi/.codex/spec-manifest.json +15 -3
- package/runtime/pi/.pi/APPEND_SYSTEM.md +41 -5
- package/runtime/pi/.pi/extensions/egregore.ts +98 -8
- package/runtime/pi/.pi/spec-manifest.json +2 -2
- package/runtime/pi/AGENTS.md +45 -9
- package/runtime/pi/bin/activity-data.sh +1 -1
- package/runtime/pi/bin/agent.sh +66 -23
- package/runtime/pi/bin/attendant.sh +525 -0
- package/runtime/pi/bin/capture-reconcile.sh +67 -0
- package/runtime/pi/bin/capture-run.sh +341 -0
- package/runtime/pi/bin/codex-render-spec.mjs +15 -4
- package/runtime/pi/bin/codex-session-start.sh +45 -3
- package/runtime/pi/bin/codex-skill-render.mjs +23 -4
- package/runtime/pi/bin/graph-op.sh +719 -49
- package/runtime/pi/bin/graph-wal.sh +3 -2
- package/runtime/pi/bin/handoff-run.sh +72 -0
- package/runtime/pi/bin/handoff-save-egregore.sh +51 -11
- package/runtime/pi/bin/index-handoff.sh +26 -17
- package/runtime/pi/bin/ingest-graph.sh +80 -0
- package/runtime/pi/bin/ingest-surface.html +429 -0
- package/runtime/pi/bin/ingest.py +614 -0
- package/runtime/pi/bin/ingest.sh +231 -0
- package/runtime/pi/bin/ingest_graph.py +776 -0
- package/runtime/pi/bin/ingest_surface.py +464 -0
- package/runtime/pi/bin/lib/git-sync.sh +2 -6
- package/runtime/pi/bin/lib/greeting.sh +157 -22
- package/runtime/pi/bin/lib/identity.sh +50 -29
- package/runtime/pi/bin/lib/noncode.sh +31 -0
- package/runtime/pi/bin/person.py +610 -0
- package/runtime/pi/bin/person.sh +448 -0
- package/runtime/pi/bin/pi-branch-policy.mjs +164 -4
- package/runtime/pi/bin/pi-product-workflows.mjs +31 -2
- package/runtime/pi/bin/publish-artifact.sh +14 -0
- package/runtime/pi/bin/session-autosave.sh +184 -0
- package/runtime/pi/bin/session-log.sh +199 -0
- package/runtime/pi/bin/settings.sh +274 -0
- package/runtime/pi/manifest.json +20 -1
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Shared capture engine for the three ways an Egregore session leaves a trace.
|
|
5
|
+
#
|
|
6
|
+
# addressed /handoff: rich team handoff (delegates to the addressed worker)
|
|
7
|
+
# personal /wrap: explicit personal close
|
|
8
|
+
# baseline SessionEnd: automatic, loss-prevention trace
|
|
9
|
+
#
|
|
10
|
+
# Lifecycle reconciliation is never performed on the caller's critical path.
|
|
11
|
+
# Connected-mode captures append idempotent graph statements to graph-wal.sh
|
|
12
|
+
# and optionally start capture-reconcile.sh as a detached worker.
|
|
13
|
+
|
|
14
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
15
|
+
CONFIG="$SCRIPT_DIR/egregore.json"
|
|
16
|
+
CAPTURE_SCHEMA="egregore-capture/v1"
|
|
17
|
+
|
|
18
|
+
queue_handoff_completion() {
|
|
19
|
+
local session_id="$1"
|
|
20
|
+
[ -n "$session_id" ] || return 0
|
|
21
|
+
# shellcheck disable=SC2016 # $name tokens are Cypher parameters.
|
|
22
|
+
local cypher='
|
|
23
|
+
MATCH (impl:Session {id: $sessionId})-[:IMPLEMENTS]->(ho:Session)
|
|
24
|
+
MATCH (impl)-[:BY]->(recipient:Person)
|
|
25
|
+
MATCH (ho)-[:HANDED_TO]->(recipient)
|
|
26
|
+
WHERE coalesce(ho.handoffStatus, "pending") IN ["pending","read","claimed"]
|
|
27
|
+
AND coalesce(ho.handoffLifecycleVersion, 0) >= 1
|
|
28
|
+
WITH DISTINCT ho, size([(ho)-[:HANDED_TO]->(:Person) | 1]) AS recipientCount
|
|
29
|
+
WHERE recipientCount = 1
|
|
30
|
+
SET ho.handoffStatus = "done",
|
|
31
|
+
ho.handoffDoneAt = coalesce(ho.handoffDoneAt, datetime()),
|
|
32
|
+
ho.handoffUpdatedAt = datetime(),
|
|
33
|
+
ho.handoffLifecycleReason = "single_recipient_implemented",
|
|
34
|
+
ho.handoffLifecycleVersion = 1
|
|
35
|
+
RETURN ho.id AS id'
|
|
36
|
+
local params
|
|
37
|
+
params="$(jq -nc --arg sessionId "$session_id" '{sessionId:$sessionId}')"
|
|
38
|
+
GRAPH_WAL_LOCK_ATTEMPTS=1 \
|
|
39
|
+
bash "$SCRIPT_DIR/bin/graph-wal.sh" append "$cypher" "$params" >/dev/null 2>&1 || true
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
start_reconcile() {
|
|
43
|
+
local session_id="$1"
|
|
44
|
+
local author="$2"
|
|
45
|
+
local summary="$3"
|
|
46
|
+
[ -n "$session_id" ] || return 0
|
|
47
|
+
(
|
|
48
|
+
bash "$SCRIPT_DIR/bin/capture-reconcile.sh" \
|
|
49
|
+
--session-id "$session_id" \
|
|
50
|
+
--author "$author" \
|
|
51
|
+
--summary "$summary" >/dev/null 2>&1 &
|
|
52
|
+
) >/dev/null 2>&1
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
usage() {
|
|
56
|
+
cat <<'EOF'
|
|
57
|
+
Usage:
|
|
58
|
+
capture-run.sh --mode addressed [handoff-run options] < handoff.md
|
|
59
|
+
capture-run.sh --mode personal|baseline --author HANDLE --topic TOPIC
|
|
60
|
+
--summary SUMMARY [--session-id ID] [--branch BRANCH] [--date ISO]
|
|
61
|
+
[--duration TEXT] [--open-threads-json JSON] [--no-push]
|
|
62
|
+
[--async-push] [--no-reconcile] < details.md
|
|
63
|
+
EOF
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
|
|
67
|
+
usage
|
|
68
|
+
exit 0
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
CAPTURE_MODE=""
|
|
72
|
+
if [ "${1:-}" = "--mode" ]; then
|
|
73
|
+
CAPTURE_MODE="${2:-}"
|
|
74
|
+
shift 2
|
|
75
|
+
fi
|
|
76
|
+
[ -n "$CAPTURE_MODE" ] || {
|
|
77
|
+
echo "Missing --mode (addressed|personal|baseline)" >&2
|
|
78
|
+
exit 1
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
case "$CAPTURE_MODE" in
|
|
82
|
+
addressed)
|
|
83
|
+
# Keep the mature publishing/notification worker intact behind the shared
|
|
84
|
+
# entry point. It continues writing its compatibility result file.
|
|
85
|
+
ADDRESSED_AUTHOR=""
|
|
86
|
+
ADDRESSED_TOPIC=""
|
|
87
|
+
PREV=""
|
|
88
|
+
for VALUE in "$@"; do
|
|
89
|
+
if [ "$PREV" = "--author" ]; then ADDRESSED_AUTHOR="$VALUE"; fi
|
|
90
|
+
if [ "$PREV" = "--topic" ]; then ADDRESSED_TOPIC="$VALUE"; fi
|
|
91
|
+
PREV="$VALUE"
|
|
92
|
+
done
|
|
93
|
+
|
|
94
|
+
if bash "$SCRIPT_DIR/bin/handoff-run.sh" "$@"; then
|
|
95
|
+
status=0
|
|
96
|
+
else
|
|
97
|
+
status=$?
|
|
98
|
+
fi
|
|
99
|
+
old_result="${TMPDIR:-/tmp}/handoff-run-result.json"
|
|
100
|
+
capture_result="${TMPDIR:-/tmp}/capture-run-result.json"
|
|
101
|
+
if [ -s "$old_result" ]; then
|
|
102
|
+
jq --arg schema "$CAPTURE_SCHEMA" --arg captureMode "$CAPTURE_MODE" \
|
|
103
|
+
'. + {captureSchema:$schema,captureMode:$captureMode}' \
|
|
104
|
+
"$old_result" > "$capture_result" 2>/dev/null || cp "$old_result" "$capture_result"
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
# Addressed capture is also explicit session-close evidence. Queue the
|
|
108
|
+
# same guarded transition as /wrap after the addressed worker has marked
|
|
109
|
+
# the current Session handed_off.
|
|
110
|
+
ADDRESSED_CONFIG_MODE="$(jq -r '.mode // "connected"' "$CONFIG" 2>/dev/null || echo connected)"
|
|
111
|
+
if [ "$status" = "0" ] && [ "$ADDRESSED_CONFIG_MODE" = "connected" ]; then
|
|
112
|
+
CURRENT_SID="$(cat "$SCRIPT_DIR/.egregore-session-id" 2>/dev/null || true)"
|
|
113
|
+
if [ -z "$CURRENT_SID" ]; then
|
|
114
|
+
PROJ_HASH="$(printf '%s' "$SCRIPT_DIR" | md5 2>/dev/null || printf '%s' "$SCRIPT_DIR" | md5sum 2>/dev/null | cut -d' ' -f1)"
|
|
115
|
+
CURRENT_SID="$(cat "$HOME/.egregore/session-${PROJ_HASH}.id" 2>/dev/null || true)"
|
|
116
|
+
fi
|
|
117
|
+
queue_handoff_completion "$CURRENT_SID"
|
|
118
|
+
start_reconcile "$CURRENT_SID" "$ADDRESSED_AUTHOR" "$ADDRESSED_TOPIC"
|
|
119
|
+
fi
|
|
120
|
+
exit "$status"
|
|
121
|
+
;;
|
|
122
|
+
personal|baseline) ;;
|
|
123
|
+
*)
|
|
124
|
+
echo "Invalid --mode: $CAPTURE_MODE (addressed|personal|baseline)" >&2
|
|
125
|
+
exit 1
|
|
126
|
+
;;
|
|
127
|
+
esac
|
|
128
|
+
|
|
129
|
+
AUTHOR=""
|
|
130
|
+
DISPLAY_NAME=""
|
|
131
|
+
TOPIC=""
|
|
132
|
+
SUMMARY=""
|
|
133
|
+
SESSION_ID=""
|
|
134
|
+
BRANCH=""
|
|
135
|
+
CAPTURE_DATE=""
|
|
136
|
+
DURATION=""
|
|
137
|
+
OPEN_THREADS_JSON="[]"
|
|
138
|
+
NO_PUSH=0
|
|
139
|
+
ASYNC_PUSH=0
|
|
140
|
+
NO_RECONCILE=0
|
|
141
|
+
|
|
142
|
+
while [ $# -gt 0 ]; do
|
|
143
|
+
case "$1" in
|
|
144
|
+
--author) AUTHOR="${2:-}"; shift 2 ;;
|
|
145
|
+
--display-name) DISPLAY_NAME="${2:-}"; shift 2 ;;
|
|
146
|
+
--topic) TOPIC="${2:-}"; shift 2 ;;
|
|
147
|
+
--summary) SUMMARY="${2:-}"; shift 2 ;;
|
|
148
|
+
--session-id) SESSION_ID="${2:-}"; shift 2 ;;
|
|
149
|
+
--branch) BRANCH="${2:-}"; shift 2 ;;
|
|
150
|
+
--date) CAPTURE_DATE="${2:-}"; shift 2 ;;
|
|
151
|
+
--duration) DURATION="${2:-}"; shift 2 ;;
|
|
152
|
+
--open-threads-json) OPEN_THREADS_JSON="${2:-[]}"; shift 2 ;;
|
|
153
|
+
--no-push) NO_PUSH=1; shift ;;
|
|
154
|
+
--async-push) ASYNC_PUSH=1; shift ;;
|
|
155
|
+
--no-reconcile) NO_RECONCILE=1; shift ;;
|
|
156
|
+
*) echo "Unknown capture option: $1" >&2; exit 1 ;;
|
|
157
|
+
esac
|
|
158
|
+
done
|
|
159
|
+
|
|
160
|
+
AUTHOR="$(printf '%s' "$AUTHOR" | tr -cd 'a-zA-Z0-9_-')"
|
|
161
|
+
[ -n "$AUTHOR" ] || { echo "Missing --author" >&2; exit 1; }
|
|
162
|
+
[ -n "$TOPIC" ] || { echo "Missing --topic" >&2; exit 1; }
|
|
163
|
+
[ -n "$SUMMARY" ] || SUMMARY="$TOPIC"
|
|
164
|
+
if ! printf '%s' "$OPEN_THREADS_JSON" | jq -e 'type == "array"' >/dev/null 2>&1; then
|
|
165
|
+
echo "Invalid --open-threads-json: expected an array" >&2
|
|
166
|
+
exit 1
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
DETAILS="$(cat /dev/stdin 2>/dev/null || true)"
|
|
170
|
+
NOW="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
171
|
+
[ -n "$CAPTURE_DATE" ] || CAPTURE_DATE="$NOW"
|
|
172
|
+
DATE_ONLY="$(printf '%s' "$CAPTURE_DATE" | grep -oE '^[0-9]{4}-[0-9]{2}-[0-9]{2}' || true)"
|
|
173
|
+
[ -n "$DATE_ONLY" ] || DATE_ONLY="$(date +%Y-%m-%d)"
|
|
174
|
+
YYYY_MM="${DATE_ONLY%??}"
|
|
175
|
+
YYYY_MM="${YYYY_MM%-}"
|
|
176
|
+
DD="${DATE_ONLY##*-}"
|
|
177
|
+
[ -n "$BRANCH" ] || BRANCH="$(git -C "$SCRIPT_DIR" branch --show-current 2>/dev/null || true)"
|
|
178
|
+
[ -n "$BRANCH" ] || BRANCH="develop"
|
|
179
|
+
|
|
180
|
+
slugify() {
|
|
181
|
+
printf '%s' "$1" \
|
|
182
|
+
| tr '[:upper:]' '[:lower:]' \
|
|
183
|
+
| sed -E 's/[^a-z0-9]+/-/g; s/^-+|-+$//g' \
|
|
184
|
+
| cut -c1-50
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
SLUG="$(slugify "$TOPIC")"
|
|
188
|
+
[ -n "$SLUG" ] || SLUG="session"
|
|
189
|
+
AUTHOR_LC="$(printf '%s' "$AUTHOR" | tr '[:upper:]' '[:lower:]')"
|
|
190
|
+
|
|
191
|
+
if [ "$CAPTURE_MODE" = "personal" ]; then
|
|
192
|
+
REL_FILE="wraps/${YYYY_MM}/${DD}-${AUTHOR_LC}-${SLUG}.md"
|
|
193
|
+
TITLE_PREFIX="Wrap"
|
|
194
|
+
else
|
|
195
|
+
REL_FILE="sessions/${YYYY_MM}/${DD}-${AUTHOR_LC}-${SLUG}.md"
|
|
196
|
+
TITLE_PREFIX="Session"
|
|
197
|
+
fi
|
|
198
|
+
ABS_FILE="$SCRIPT_DIR/memory/$REL_FILE"
|
|
199
|
+
mkdir -p "$(dirname "$ABS_FILE")"
|
|
200
|
+
|
|
201
|
+
# Never overwrite an existing capture. Session IDs make concurrent same-topic
|
|
202
|
+
# sessions deterministic while retaining the human-readable prefix.
|
|
203
|
+
if [ -e "$ABS_FILE" ]; then
|
|
204
|
+
suffix="$(printf '%s' "${SESSION_ID:-$NOW}" | tr -cd 'a-zA-Z0-9' | tail -c 7)"
|
|
205
|
+
[ -n "$suffix" ] || suffix="2"
|
|
206
|
+
ABS_FILE="${ABS_FILE%.md}-${suffix}.md"
|
|
207
|
+
REL_FILE="${REL_FILE%.md}-${suffix}.md"
|
|
208
|
+
fi
|
|
209
|
+
|
|
210
|
+
{
|
|
211
|
+
printf '# %s: %s\n\n' "$TITLE_PREFIX" "$TOPIC"
|
|
212
|
+
printf '**Capture Schema**: %s\n' "$CAPTURE_SCHEMA"
|
|
213
|
+
printf '**Capture Mode**: %s\n' "$CAPTURE_MODE"
|
|
214
|
+
printf '**Date**: %s\n' "$CAPTURE_DATE"
|
|
215
|
+
printf '**Author**: %s\n' "${DISPLAY_NAME:-$AUTHOR}"
|
|
216
|
+
printf '**To**: %s\n' "$AUTHOR"
|
|
217
|
+
printf '**Branch**: %s\n' "$BRANCH"
|
|
218
|
+
[ -n "$SESSION_ID" ] && printf '**Session**: %s\n' "$SESSION_ID"
|
|
219
|
+
[ -n "$DURATION" ] && printf '**Duration**: %s\n' "$DURATION"
|
|
220
|
+
printf '\n## Summary\n\n%s\n' "$SUMMARY"
|
|
221
|
+
if [ -n "$DETAILS" ]; then
|
|
222
|
+
if [ "$CAPTURE_MODE" = "personal" ]; then
|
|
223
|
+
printf '\n## Notes\n\n%s\n' "$DETAILS"
|
|
224
|
+
else
|
|
225
|
+
printf '\n%s\n' "$DETAILS"
|
|
226
|
+
fi
|
|
227
|
+
fi
|
|
228
|
+
} > "$ABS_FILE"
|
|
229
|
+
|
|
230
|
+
memory_save() {
|
|
231
|
+
local message="$1"
|
|
232
|
+
(
|
|
233
|
+
cd "$SCRIPT_DIR/memory" 2>/dev/null || exit 0
|
|
234
|
+
git add "$REL_FILE" >/dev/null 2>&1 || exit 0
|
|
235
|
+
git commit --only --quiet -m "$message" -- "$REL_FILE" >/dev/null 2>&1 || true
|
|
236
|
+
if git remote get-url origin >/dev/null 2>&1; then
|
|
237
|
+
local _
|
|
238
|
+
for _ in 1 2 3; do
|
|
239
|
+
git pull --rebase origin main --quiet >/dev/null 2>&1 || true
|
|
240
|
+
git push origin main --quiet >/dev/null 2>&1 && exit 0
|
|
241
|
+
sleep 1
|
|
242
|
+
done
|
|
243
|
+
exit 1
|
|
244
|
+
fi
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
MEMORY_STATUS="skipped"
|
|
249
|
+
if [ "$NO_PUSH" = "0" ]; then
|
|
250
|
+
if [ "$ASYNC_PUSH" = "1" ]; then
|
|
251
|
+
( memory_save "Capture: ${AUTHOR} — ${TOPIC}" >/dev/null 2>&1 & ) >/dev/null 2>&1
|
|
252
|
+
MEMORY_STATUS="queued"
|
|
253
|
+
elif memory_save "Capture: ${AUTHOR} — ${TOPIC}"; then
|
|
254
|
+
MEMORY_STATUS="ok"
|
|
255
|
+
else
|
|
256
|
+
MEMORY_STATUS="failed"
|
|
257
|
+
fi
|
|
258
|
+
fi
|
|
259
|
+
|
|
260
|
+
GRAPH_STATUS="skipped"
|
|
261
|
+
MODE="$(jq -r '.mode // "connected"' "$CONFIG" 2>/dev/null || echo connected)"
|
|
262
|
+
if [ "$MODE" = "connected" ] && [ -n "$SESSION_ID" ]; then
|
|
263
|
+
# shellcheck disable=SC2016 # $name tokens are Cypher parameters.
|
|
264
|
+
CAPTURE_CYPHER='
|
|
265
|
+
MERGE (s:Session {id: $sessionId})
|
|
266
|
+
ON CREATE SET s.date = date($date), s.startedAt = datetime($capturedAt)
|
|
267
|
+
SET s.topic = $topic,
|
|
268
|
+
s.summary = $summary,
|
|
269
|
+
s.filePath = $filePath,
|
|
270
|
+
s.branch = $branch,
|
|
271
|
+
s.captureSchema = $captureSchema,
|
|
272
|
+
s.captureMode = $captureMode,
|
|
273
|
+
s.captureUpdatedAt = datetime()
|
|
274
|
+
FOREACH (_ IN CASE WHEN $captureMode = "personal" THEN [1] ELSE [] END |
|
|
275
|
+
SET s.status = "wrapped",
|
|
276
|
+
s.wrappedAt = coalesce(s.wrappedAt, datetime()),
|
|
277
|
+
s.openThreads = $openThreads)
|
|
278
|
+
FOREACH (_ IN CASE WHEN $captureMode = "baseline" THEN [1] ELSE [] END |
|
|
279
|
+
SET s.status = CASE
|
|
280
|
+
WHEN s.status IN ["wrapped","handed_off","completed"] THEN s.status
|
|
281
|
+
ELSE "captured"
|
|
282
|
+
END,
|
|
283
|
+
s.endedAt = coalesce(s.endedAt, datetime()))
|
|
284
|
+
WITH s
|
|
285
|
+
OPTIONAL MATCH (p:Person)
|
|
286
|
+
WHERE toLower(p.name) = toLower($author)
|
|
287
|
+
OR toLower(coalesce(p.github, "")) = toLower($author)
|
|
288
|
+
FOREACH (_ IN CASE WHEN p IS NOT NULL THEN [1] ELSE [] END |
|
|
289
|
+
MERGE (s)-[:BY]->(p))
|
|
290
|
+
RETURN s.id AS id'
|
|
291
|
+
CAPTURE_PARAMS="$(jq -nc \
|
|
292
|
+
--arg sessionId "$SESSION_ID" \
|
|
293
|
+
--arg date "$DATE_ONLY" \
|
|
294
|
+
--arg capturedAt "$NOW" \
|
|
295
|
+
--arg topic "$TOPIC" \
|
|
296
|
+
--arg summary "$SUMMARY" \
|
|
297
|
+
--arg filePath "$REL_FILE" \
|
|
298
|
+
--arg branch "$BRANCH" \
|
|
299
|
+
--arg captureSchema "$CAPTURE_SCHEMA" \
|
|
300
|
+
--arg captureMode "$CAPTURE_MODE" \
|
|
301
|
+
--arg author "$AUTHOR_LC" \
|
|
302
|
+
--argjson openThreads "$OPEN_THREADS_JSON" \
|
|
303
|
+
'{sessionId:$sessionId,date:$date,capturedAt:$capturedAt,topic:$topic,
|
|
304
|
+
summary:$summary,filePath:$filePath,branch:$branch,
|
|
305
|
+
captureSchema:$captureSchema,captureMode:$captureMode,author:$author,
|
|
306
|
+
openThreads:$openThreads}')"
|
|
307
|
+
GRAPH_WAL_LOCK_ATTEMPTS=1 \
|
|
308
|
+
bash "$SCRIPT_DIR/bin/graph-wal.sh" append "$CAPTURE_CYPHER" "$CAPTURE_PARAMS" >/dev/null 2>&1 || true
|
|
309
|
+
GRAPH_STATUS="queued"
|
|
310
|
+
|
|
311
|
+
# An explicit wrap is strong completion evidence. Queue the transition after
|
|
312
|
+
# the Session update; do not query the graph or wait for reconciliation here.
|
|
313
|
+
if [ "$CAPTURE_MODE" = "personal" ]; then
|
|
314
|
+
queue_handoff_completion "$SESSION_ID"
|
|
315
|
+
fi
|
|
316
|
+
|
|
317
|
+
if [ "$NO_RECONCILE" = "0" ] && [ "$CAPTURE_MODE" = "personal" ]; then
|
|
318
|
+
start_reconcile "$SESSION_ID" "$AUTHOR_LC" "$SUMMARY"
|
|
319
|
+
fi
|
|
320
|
+
fi
|
|
321
|
+
|
|
322
|
+
RESULT_FILE="${TMPDIR:-/tmp}/capture-run-result.json"
|
|
323
|
+
jq -nc \
|
|
324
|
+
--arg schema "$CAPTURE_SCHEMA" \
|
|
325
|
+
--arg captureMode "$CAPTURE_MODE" \
|
|
326
|
+
--arg file "$REL_FILE" \
|
|
327
|
+
--arg absFile "$ABS_FILE" \
|
|
328
|
+
--arg sessionId "$SESSION_ID" \
|
|
329
|
+
--arg memoryStatus "$MEMORY_STATUS" \
|
|
330
|
+
--arg graphStatus "$GRAPH_STATUS" \
|
|
331
|
+
'{
|
|
332
|
+
captureSchema:$schema,
|
|
333
|
+
captureMode:$captureMode,
|
|
334
|
+
file:$file,
|
|
335
|
+
absFile:$absFile,
|
|
336
|
+
sessionId:$sessionId,
|
|
337
|
+
memoryStatus:$memoryStatus,
|
|
338
|
+
graphStatus:$graphStatus
|
|
339
|
+
}' > "$RESULT_FILE"
|
|
340
|
+
|
|
341
|
+
printf 'capture: memory/%s\n' "$REL_FILE"
|
|
@@ -218,19 +218,22 @@ If on develop after two messages, create a branch immediately from whatever cont
|
|
|
218
218
|
|
|
219
219
|
### Branch-guard protocol
|
|
220
220
|
|
|
221
|
-
The \`.codex/hooks/branch-guard.js\` PreToolUse hook (enabled by the launcher via \`--enable hooks\`)
|
|
221
|
+
The \`.codex/hooks/branch-guard.js\` PreToolUse hook (enabled by the launcher via \`--enable hooks\`) protects project writes on \`develop\`/\`main\`/\`master\`. Its block message is operational guidance, not a reason to interrupt the user with routine Git choices:
|
|
222
222
|
|
|
223
|
-
- **Topic is
|
|
224
|
-
- **Topic is ambiguous**
|
|
223
|
+
- **Topic is clear** (the user said "fix X", "add Y feature") → run \`bin/agent.sh branch --topic "<topic>"\` automatically, continue in the printed worktree, and say one short sentence so the branch change is visible. Do not ask the user to approve routine branching.
|
|
224
|
+
- **Topic is genuinely ambiguous** → ask only for the topic, using compact numbered options:
|
|
225
225
|
|
|
226
226
|
\`\`\`text
|
|
227
|
-
|
|
227
|
+
What should I call this work?
|
|
228
228
|
1. <suggested slug from context>
|
|
229
229
|
2. <alternative slug>
|
|
230
230
|
3. Other: (name it)
|
|
231
231
|
\`\`\`
|
|
232
232
|
|
|
233
233
|
Wait for the reply, then branch.
|
|
234
|
+
- **The user explicitly requested the protected branch** → that request is consent. Record it with \`echo '{branch}' > .egregore-branch-consent\`, then retry. Never create the marker merely to silence the hook.
|
|
235
|
+
|
|
236
|
+
Memory, managed-repo, and runtime-state writes should bypass the project guard. If one triggers it, correct the operation target/context instead of asking for protected-branch consent.
|
|
234
237
|
|
|
235
238
|
If this Codex build does not support hooks, follow the same discipline as a standing instruction: never write or commit on develop/main/master.
|
|
236
239
|
|
|
@@ -274,6 +277,14 @@ const RULES = {
|
|
|
274
277
|
action: 'keep',
|
|
275
278
|
note: 'Kept; /update, /contribute, /save tokens rendered as $skill tokens.',
|
|
276
279
|
},
|
|
280
|
+
'Cross-Runtime Compatibility': {
|
|
281
|
+
action: 'keep',
|
|
282
|
+
note: 'Kept verbatim — framework parity applies equally to Claude Code, Codex, and Pi.',
|
|
283
|
+
},
|
|
284
|
+
'Voice': {
|
|
285
|
+
action: 'keep',
|
|
286
|
+
note: 'Kept — all runtimes can consult the shared voice rules and register-specific skills.',
|
|
287
|
+
},
|
|
277
288
|
'On Launch — MANDATORY FIRST ACTION': {
|
|
278
289
|
action: 'replace',
|
|
279
290
|
body: ON_LAUNCH_BODY,
|
|
@@ -43,6 +43,15 @@ source "$SCRIPT_DIR/bin/lib/hash.sh"
|
|
|
43
43
|
source "$SCRIPT_DIR/bin/lib/time.sh"
|
|
44
44
|
source "$SCRIPT_DIR/bin/lib/identity.sh" >/dev/null 2>/dev/null
|
|
45
45
|
|
|
46
|
+
# Keep the durable local identity current before gathering context, then project
|
|
47
|
+
# it to Supabase + Neo4j in the background so startup is never network-blocked.
|
|
48
|
+
if [ -f "$STATE_FILE" ] && [ -f "$SCRIPT_DIR/bin/person.py" ]; then
|
|
49
|
+
python3 "$SCRIPT_DIR/bin/person.py" sync-local >/dev/null 2>&1 || true
|
|
50
|
+
fi
|
|
51
|
+
if [ -f "$SCRIPT_DIR/bin/person.sh" ]; then
|
|
52
|
+
( bash "$SCRIPT_DIR/bin/person.sh" sync >/dev/null 2>&1 & ) 2>/dev/null
|
|
53
|
+
fi
|
|
54
|
+
|
|
46
55
|
LOCAL_MODE="false"
|
|
47
56
|
[ "$(_detect_mode)" = "local" ] && LOCAL_MODE="true"
|
|
48
57
|
|
|
@@ -59,6 +68,10 @@ source "$SCRIPT_DIR/bin/lib/context.sh" >/dev/null 2>/dev/null
|
|
|
59
68
|
source "$SCRIPT_DIR/bin/lib/metrics.sh" >/dev/null 2>/dev/null
|
|
60
69
|
trap 'rm -rf "${CTX_DIR:-}"' EXIT
|
|
61
70
|
|
|
71
|
+
# Rescue non-coding leftovers from sessions that died without ceremony
|
|
72
|
+
# (terminal closed). Gated + idle-guarded inside the script; fire-and-forget.
|
|
73
|
+
( bash "$SCRIPT_DIR/bin/session-autosave.sh" --sweep >/dev/null 2>&1 & ) 2>/dev/null
|
|
74
|
+
|
|
62
75
|
read_json() {
|
|
63
76
|
local file="$1"
|
|
64
77
|
local expr="$2"
|
|
@@ -95,8 +108,9 @@ render_pending_line() {
|
|
|
95
108
|
else
|
|
96
109
|
printf ' ◐ %s pending %s - bin/agent.sh answer\n' "$PENDING_Q_COUNT" "$noun"
|
|
97
110
|
fi
|
|
98
|
-
|
|
99
|
-
|
|
111
|
+
fi
|
|
112
|
+
if [ "$ADDRESSED_COUNT" -gt 0 ] 2>/dev/null; then
|
|
113
|
+
printf ' ◇ %s handoffs for %s - say "show my handoffs" to review or close\n' "$ADDRESSED_COUNT" "$AUTHOR"
|
|
100
114
|
fi
|
|
101
115
|
}
|
|
102
116
|
|
|
@@ -154,7 +168,35 @@ render_card() {
|
|
|
154
168
|
printf '%s%*s%s\n' "$footer_left" "$pad" "" "$footer_right"
|
|
155
169
|
fi
|
|
156
170
|
|
|
157
|
-
|
|
171
|
+
# Autosave trail — background saves since the last greeting (same ledger
|
|
172
|
+
# the Claude greeting reads; sweep rescues become visible here).
|
|
173
|
+
autosave_log="${EGREGORE_AUTOSAVE_LOG:-$HOME/.egregore/autosave.log}"
|
|
174
|
+
autosave_seen="${autosave_log}.seen"
|
|
175
|
+
if [ -f "$autosave_log" ]; then
|
|
176
|
+
total_as=$(grep -c '|autosave|' "$autosave_log" 2>/dev/null || echo 0)
|
|
177
|
+
seen_as=$(cat "$autosave_seen" 2>/dev/null || echo 0)
|
|
178
|
+
case "$seen_as" in ''|*[!0-9]*) seen_as=0 ;; esac
|
|
179
|
+
if [ "$total_as" -gt "$seen_as" ]; then
|
|
180
|
+
new_as=$((total_as - seen_as))
|
|
181
|
+
merged_as=$(grep '|autosave|' "$autosave_log" 2>/dev/null | tail -n "$new_as" | grep -c '|merged|' || echo 0)
|
|
182
|
+
if [ "$merged_as" -ge "$new_as" ]; then
|
|
183
|
+
printf ' ⟲ auto-saved %s non-coding save(s) → develop while you were away\n' "$new_as"
|
|
184
|
+
else
|
|
185
|
+
printf ' ⟲ auto-saved %s non-coding save(s) — %s merged, rest in open PRs\n' "$new_as" "$merged_as"
|
|
186
|
+
fi
|
|
187
|
+
fi
|
|
188
|
+
printf '%s' "$total_as" > "$autosave_seen" 2>/dev/null || true
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
# Greeting links. Org config `pinned_links` in egregore.json replaces the
|
|
192
|
+
# default board link — same contract as bin/lib/greeting.sh.
|
|
193
|
+
pinned_links=$(jq -c '.pinned_links // []' "$CONFIG" 2>/dev/null || echo "[]")
|
|
194
|
+
if [ "$(printf '%s' "$pinned_links" | jq 'length' 2>/dev/null || echo 0)" -gt 0 ]; then
|
|
195
|
+
printf '%s' "$pinned_links" | jq -r '.[] |
|
|
196
|
+
if type == "string" then " ◆ \(.)"
|
|
197
|
+
elif (.label // "") != "" then " ◆ \(.label): \(.url)"
|
|
198
|
+
else " ◆ \(.url)" end' 2>/dev/null
|
|
199
|
+
elif [ -n "$SLUG" ]; then
|
|
158
200
|
board_url="https://egregore.xyz/view/${SLUG}/board"
|
|
159
201
|
printf ' ◆ %s\n' "$board_url"
|
|
160
202
|
fi
|
|
@@ -143,7 +143,9 @@ function renderActivity(data) {
|
|
|
143
143
|
const graph = classifyGraph(data, { mode: data.mode || 'connected', attempt: 'retry' });
|
|
144
144
|
const org = data.org || data.organization || 'Egregore';
|
|
145
145
|
const date = data.date || '';
|
|
146
|
-
const me =
|
|
146
|
+
const me = typeof data.me === 'string'
|
|
147
|
+
? data.me
|
|
148
|
+
: data.me?.github || data.me?.name || data.github_username || '';
|
|
147
149
|
const handoffs = firstArray(data, ['handoffs_to_me', 'handoffs', 'pending_handoffs', 'me.handoffs', 'addressed_handoffs']);
|
|
148
150
|
const questions = firstArray(data, ['questions', 'pending_questions', 'me.questions']);
|
|
149
151
|
const prs = asArray(data.prs);
|
|
@@ -169,8 +171,20 @@ function renderActivity(data) {
|
|
|
169
171
|
}
|
|
170
172
|
if (handoffs.length > 0) {
|
|
171
173
|
lines.push(padLine('HANDOFFS'));
|
|
172
|
-
|
|
173
|
-
|
|
174
|
+
const glyph = { pending: '●', read: '◐', claimed: '◆', done: '✓', completed: '✓', expired: '×' };
|
|
175
|
+
handoffs.slice(0, 6).forEach((h, index) => {
|
|
176
|
+
const status = valueOf(h, ['status'], 'pending');
|
|
177
|
+
const age = valueOf(h, ['ageDays', 'age_days'], '');
|
|
178
|
+
const intent = valueOf(h, ['intent'], '');
|
|
179
|
+
const meta = [
|
|
180
|
+
age === '' ? '' : `${age}d`,
|
|
181
|
+
intent && intent !== 'unclassified' ? intent : '',
|
|
182
|
+
status,
|
|
183
|
+
].filter(Boolean).join(' · ');
|
|
184
|
+
lines.push(padLine(
|
|
185
|
+
`[${index + 1}] ${glyph[status] || '○'} ⇌ ${valueOf(h, ['author', 'from'], 'someone')}: ${valueOf(h, ['topic', 'title'], 'handoff')}`,
|
|
186
|
+
meta,
|
|
187
|
+
));
|
|
174
188
|
});
|
|
175
189
|
}
|
|
176
190
|
}
|
|
@@ -197,7 +211,12 @@ function renderActivity(data) {
|
|
|
197
211
|
}
|
|
198
212
|
|
|
199
213
|
lines.push(rule());
|
|
200
|
-
|
|
214
|
+
if (handoffs.length > 0) {
|
|
215
|
+
lines.push(padLine('Handoff actions: done N · expire N · reopen N'));
|
|
216
|
+
lines.push(padLine('Type a number to act, or keep working.'));
|
|
217
|
+
} else {
|
|
218
|
+
lines.push(padLine('Focus choices: questions, recent work, dashboard, done'));
|
|
219
|
+
}
|
|
201
220
|
lines.push(bottomRule());
|
|
202
221
|
return lines.join('\n');
|
|
203
222
|
}
|