create-egregore 0.19.33 → 0.20.0
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 +118 -13
- package/bin/cli.js +19 -3
- package/lib/connect-runtime.js +4 -2
- package/lib/local.js +15 -1
- package/lib/prime-runtime.js +208 -0
- package/lib/setup.js +45 -3
- package/package.json +4 -3
- package/runtime/codex/.codex/skills/ingest-notion/SKILL.md +29 -0
- package/runtime/codex/.codex/skills/notion-connect/SKILL.md +29 -0
- package/runtime/codex/.codex/skills/teams-connect/SKILL.md +29 -0
- package/runtime/codex/.codex/spec-manifest.json +3 -3
- package/runtime/codex/AGENTS.md +11 -4
- package/runtime/codex/bin/activity-data.sh +4 -8
- package/runtime/codex/bin/attendant.sh +13 -0
- package/runtime/codex/bin/autosave.sh +27 -1
- package/runtime/codex/bin/codex-render-spec.mjs +6 -0
- package/runtime/codex/bin/codex-session-start.sh +3 -1
- package/runtime/codex/bin/graph-batch.sh +25 -6
- package/runtime/codex/bin/graph-op.sh +5 -4
- package/runtime/codex/bin/graph.sh +31 -5
- package/runtime/codex/bin/ingest.py +81 -5
- package/runtime/codex/bin/lib/context.sh +26 -1
- package/runtime/codex/bin/lib/greeting.sh +4 -1
- package/runtime/codex/bin/lib/worktree-links.sh +25 -0
- package/runtime/codex/manifest.json +7 -1
- package/runtime/pi/.codex/skills/ingest-notion/SKILL.md +29 -0
- package/runtime/pi/.codex/skills/notion-connect/SKILL.md +29 -0
- package/runtime/pi/.codex/skills/teams-connect/SKILL.md +29 -0
- package/runtime/pi/.codex/spec-manifest.json +3 -3
- package/runtime/pi/.pi/APPEND_SYSTEM.md +3 -2
- package/runtime/pi/.pi/extensions/egregore.ts +17 -6
- package/runtime/pi/.pi/spec-manifest.json +2 -2
- package/runtime/pi/AGENTS.md +11 -4
- package/runtime/pi/bin/activity-data.sh +4 -8
- package/runtime/pi/bin/attendant.sh +13 -0
- package/runtime/pi/bin/autosave.sh +27 -1
- package/runtime/pi/bin/codex-render-spec.mjs +6 -0
- package/runtime/pi/bin/codex-session-start.sh +3 -1
- package/runtime/pi/bin/graph-batch.sh +25 -6
- package/runtime/pi/bin/graph-op.sh +5 -4
- package/runtime/pi/bin/graph.sh +31 -5
- package/runtime/pi/bin/ingest.py +81 -5
- package/runtime/pi/bin/lib/context.sh +26 -1
- package/runtime/pi/bin/lib/greeting.sh +4 -1
- package/runtime/pi/bin/lib/worktree-links.sh +25 -0
- package/runtime/pi/bin/pi-branch-policy.mjs +138 -1
- package/runtime/pi/bin/pi-product-workflows.mjs +35 -7
- package/runtime/pi/manifest.json +7 -1
- package/runtime/prime/.claude/context/notification-consent.md +66 -0
- package/runtime/prime/.codex/config.toml +17 -0
- package/runtime/prime/.codex/hooks/branch-guard.js +461 -0
- package/runtime/prime/.codex/hooks/search-hint.js +67 -0
- package/runtime/prime/.codex/hooks.json +30 -0
- package/runtime/prime/.codex/skills/activity/SKILL.md +82 -0
- package/runtime/prime/.codex/skills/announce/SKILL.md +67 -0
- package/runtime/prime/.codex/skills/archive/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/branch/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/checkup/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/commit/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/contribute/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/create-skill/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/dark-mode/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/dashboard/SKILL.md +59 -0
- package/runtime/prime/.codex/skills/deep-reflect/SKILL.md +109 -0
- package/runtime/prime/.codex/skills/delete-user/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/emissary/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/handoff/SKILL.md +166 -0
- package/runtime/prime/.codex/skills/harvest/QUESTION_PALETTE.md +76 -0
- package/runtime/prime/.codex/skills/harvest/SKILL.md +131 -0
- package/runtime/prime/.codex/skills/ingest/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/ingest-notion/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/ingest-user-interview/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/invite/SKILL.md +44 -0
- package/runtime/prime/.codex/skills/issue/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/me/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/meeting/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/note/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/notion-connect/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/onboarding/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/pr/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/pull/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/push/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/quest/SKILL.md +80 -0
- package/runtime/prime/.codex/skills/reflect/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/review-pr/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/save/SKILL.md +125 -0
- package/runtime/prime/.codex/skills/slack-connect/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/summon/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/sync-repos/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/teams-connect/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/telegram-connect/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/telemetry/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/test/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/the-spiral/SKILL.md +84 -0
- package/runtime/prime/.codex/skills/tui-design/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/tutorial/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/update/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/view/SKILL.md +156 -0
- package/runtime/prime/.codex/skills/view/agents/openai.yaml +4 -0
- package/runtime/prime/.codex/skills/view/assets/compose-scaffold.html +221 -0
- package/runtime/prime/.codex/skills/visual-explain/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/wrap/SKILL.md +58 -0
- package/runtime/prime/.codex/spec-manifest.json +132 -0
- package/runtime/prime/.prime/agent/APPEND_SYSTEM.md +393 -0
- package/runtime/prime/.prime/agent/extensions/egregore.ts +460 -0
- package/runtime/prime/.prime/agent/settings.json +4 -0
- package/runtime/prime/.prime/agent/skills/egregore/SKILL.md +34 -0
- package/runtime/prime/.prime/agent/skills/egregore/pyproject.toml +13 -0
- package/runtime/prime/.prime/agent/skills/egregore/src/egregore/__init__.py +156 -0
- package/runtime/prime/.prime/agent/spec-manifest.json +12 -0
- package/runtime/prime/AGENTS.md +489 -0
- package/runtime/prime/bin/activity-data.sh +238 -0
- package/runtime/prime/bin/agent.sh +775 -0
- package/runtime/prime/bin/attendant.sh +707 -0
- package/runtime/prime/bin/autosave.sh +112 -0
- package/runtime/prime/bin/capture-reconcile.sh +69 -0
- package/runtime/prime/bin/capture-run.sh +341 -0
- package/runtime/prime/bin/codex-install-skills.sh +78 -0
- package/runtime/prime/bin/codex-render-spec.mjs +440 -0
- package/runtime/prime/bin/codex-session-start.sh +265 -0
- package/runtime/prime/bin/codex-skill-render.mjs +347 -0
- package/runtime/prime/bin/codex-sync-skills.sh +232 -0
- package/runtime/prime/bin/connect-refresh.sh +33 -0
- package/runtime/prime/bin/dashboard-data.sh +178 -0
- package/runtime/prime/bin/graph-batch.sh +79 -0
- package/runtime/prime/bin/graph-op.sh +1102 -0
- package/runtime/prime/bin/graph-wal.sh +229 -0
- package/runtime/prime/bin/graph.sh +223 -0
- package/runtime/prime/bin/handoff-pr-backfill.sh +86 -0
- package/runtime/prime/bin/handoff-run.sh +671 -0
- package/runtime/prime/bin/handoff-save-egregore.sh +212 -0
- package/runtime/prime/bin/index-handoff.sh +322 -0
- package/runtime/prime/bin/ingest-graph.sh +80 -0
- package/runtime/prime/bin/ingest-surface.html +495 -0
- package/runtime/prime/bin/ingest.py +756 -0
- package/runtime/prime/bin/ingest.sh +279 -0
- package/runtime/prime/bin/ingest_graph.py +856 -0
- package/runtime/prime/bin/ingest_surface.py +544 -0
- package/runtime/prime/bin/lib/artifact-id.sh +46 -0
- package/runtime/prime/bin/lib/config.sh +116 -0
- package/runtime/prime/bin/lib/context.sh +621 -0
- package/runtime/prime/bin/lib/dashboard-artifact.sh +242 -0
- package/runtime/prime/bin/lib/ensure-develop.sh +183 -0
- package/runtime/prime/bin/lib/git-sync.sh +406 -0
- package/runtime/prime/bin/lib/greeting.sh +519 -0
- package/runtime/prime/bin/lib/hash.sh +13 -0
- package/runtime/prime/bin/lib/identity.sh +213 -0
- package/runtime/prime/bin/lib/metrics.sh +374 -0
- package/runtime/prime/bin/lib/noncode.sh +31 -0
- package/runtime/prime/bin/lib/permissions.sh +106 -0
- package/runtime/prime/bin/lib/time.sh +56 -0
- package/runtime/prime/bin/lib/worktree-links.sh +79 -0
- package/runtime/prime/bin/notify.sh +636 -0
- package/runtime/prime/bin/person.py +630 -0
- package/runtime/prime/bin/person.sh +448 -0
- package/runtime/prime/bin/pi-branch-policy.mjs +312 -0
- package/runtime/prime/bin/pi-product-workflows.mjs +394 -0
- package/runtime/prime/bin/prime-render-spec.mjs +123 -0
- package/runtime/prime/bin/prime-session-start.sh +24 -0
- package/runtime/prime/bin/publish-artifact.sh +271 -0
- package/runtime/prime/bin/publish-references.sh +144 -0
- package/runtime/prime/bin/repo-state.sh +96 -0
- package/runtime/prime/bin/search.sh +240 -0
- package/runtime/prime/bin/session-autosave.sh +209 -0
- package/runtime/prime/bin/session-log.sh +199 -0
- package/runtime/prime/bin/settings.sh +304 -0
- package/runtime/prime/bin/telemetry.sh +391 -0
- package/runtime/prime/bin/worktree-create.sh +128 -0
- package/runtime/prime/bin/worktree-remove.sh +50 -0
- package/runtime/prime/bin/worktree.sh +198 -0
- package/runtime/prime/manifest.json +178 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Graph Write-Ahead Log — resilient graph writes with local buffer.
|
|
3
|
+
# Mirrors bin/telemetry.sh architecture.
|
|
4
|
+
#
|
|
5
|
+
# Subcommands:
|
|
6
|
+
# append <cypher> <params_json> Append entry to local JSONL buffer (no network)
|
|
7
|
+
# drain Execute pending entries via graph-batch.sh
|
|
8
|
+
# status Show pending count + file size as JSON
|
|
9
|
+
#
|
|
10
|
+
# Storage: ~/.egregore/graph-wal.jsonl
|
|
11
|
+
# Buffer guard: 2MB max, auto-truncate to last 200 entries if exceeded.
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
|
|
14
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
15
|
+
|
|
16
|
+
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
|
|
17
|
+
echo "Usage: graph-wal.sh <command>"
|
|
18
|
+
echo ""
|
|
19
|
+
echo "Graph Write-Ahead Log — resilient graph writes with local buffer."
|
|
20
|
+
echo ""
|
|
21
|
+
echo "Commands:"
|
|
22
|
+
echo " append <cypher> <params> Append entry to WAL (no network)"
|
|
23
|
+
echo " drain Execute pending entries via graph-batch"
|
|
24
|
+
echo " status Show pending count + file size (JSON)"
|
|
25
|
+
exit 0
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
WAL_DIR="$HOME/.egregore"
|
|
29
|
+
# WAL file is per-instance to prevent cross-org data leakage on multi-instance machines
|
|
30
|
+
PROJ_HASH=$(echo -n "$SCRIPT_DIR" | md5 2>/dev/null || echo -n "$SCRIPT_DIR" | md5sum 2>/dev/null | cut -d' ' -f1)
|
|
31
|
+
WAL_FILE="$WAL_DIR/graph-wal-${PROJ_HASH}.jsonl"
|
|
32
|
+
MAX_BUFFER_BYTES=2097152 # 2MB
|
|
33
|
+
MAX_ENTRIES_AFTER_TRUNCATE=200
|
|
34
|
+
LOCK_DIR="$WAL_DIR/graph-wal-${PROJ_HASH}.lock"
|
|
35
|
+
LOCK_ATTEMPTS="${GRAPH_WAL_LOCK_ATTEMPTS:-50}"
|
|
36
|
+
|
|
37
|
+
# --- Cross-platform file locking via mkdir (atomic on all POSIX systems) ---
|
|
38
|
+
_lock() {
|
|
39
|
+
local attempts=0
|
|
40
|
+
while ! mkdir "$LOCK_DIR" 2>/dev/null; do
|
|
41
|
+
attempts=$((attempts + 1))
|
|
42
|
+
if [ "$attempts" -gt "$LOCK_ATTEMPTS" ]; then
|
|
43
|
+
# Stale lock — check if holder PID is alive
|
|
44
|
+
local holder_pid
|
|
45
|
+
holder_pid=$(cat "$LOCK_DIR/pid" 2>/dev/null || echo "")
|
|
46
|
+
if [ -n "$holder_pid" ] && ! kill -0 "$holder_pid" 2>/dev/null; then
|
|
47
|
+
rm -rf "$LOCK_DIR" 2>/dev/null
|
|
48
|
+
continue
|
|
49
|
+
fi
|
|
50
|
+
echo "graph-wal: lock timeout after ${LOCK_ATTEMPTS} attempt(s)" >&2
|
|
51
|
+
return 1
|
|
52
|
+
fi
|
|
53
|
+
sleep 0.1
|
|
54
|
+
done
|
|
55
|
+
echo $$ > "$LOCK_DIR/pid" 2>/dev/null
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
_unlock() {
|
|
59
|
+
rm -rf "$LOCK_DIR" 2>/dev/null
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
trap '_unlock' EXIT
|
|
63
|
+
|
|
64
|
+
# --- Resolve session ID (same pattern as telemetry.sh) ---
|
|
65
|
+
_resolve_session_id() {
|
|
66
|
+
if [ -n "${EGREGORE_SESSION_ID:-}" ]; then
|
|
67
|
+
echo "$EGREGORE_SESSION_ID"
|
|
68
|
+
return
|
|
69
|
+
fi
|
|
70
|
+
local proj_hash
|
|
71
|
+
proj_hash=$(echo -n "$SCRIPT_DIR" | md5 2>/dev/null || echo -n "$SCRIPT_DIR" | md5sum 2>/dev/null | cut -d' ' -f1)
|
|
72
|
+
local sid_file="$WAL_DIR/session-${proj_hash}.id"
|
|
73
|
+
if [ -f "$sid_file" ]; then
|
|
74
|
+
cat "$sid_file" 2>/dev/null || echo "unknown"
|
|
75
|
+
else
|
|
76
|
+
echo "unknown"
|
|
77
|
+
fi
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# --- append: O(1) local write, no network ---
|
|
81
|
+
cmd_append() {
|
|
82
|
+
local cypher="${1:?Usage: graph-wal.sh append <cypher> <params_json>}"
|
|
83
|
+
local params="${2:-"{}"}"
|
|
84
|
+
|
|
85
|
+
mkdir -p "$WAL_DIR"
|
|
86
|
+
|
|
87
|
+
local ts
|
|
88
|
+
ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
89
|
+
local sid
|
|
90
|
+
sid=$(_resolve_session_id)
|
|
91
|
+
|
|
92
|
+
# Build JSON line
|
|
93
|
+
local line
|
|
94
|
+
line=$(jq -n -c \
|
|
95
|
+
--arg ts "$ts" \
|
|
96
|
+
--arg sid "$sid" \
|
|
97
|
+
--arg cypher "$cypher" \
|
|
98
|
+
--argjson params "$params" \
|
|
99
|
+
'{ts: $ts, sid: $sid, cypher: $cypher, params: $params}')
|
|
100
|
+
|
|
101
|
+
# Append under lock — if lock fails, append anyway (durability > mutual exclusion)
|
|
102
|
+
local _locked=true
|
|
103
|
+
_lock || _locked=false
|
|
104
|
+
echo "$line" >> "$WAL_FILE"
|
|
105
|
+
|
|
106
|
+
# Buffer guard: truncate to last N entries if file exceeds max size
|
|
107
|
+
# Only safe under lock — tail+mv is not atomic and races with concurrent writers
|
|
108
|
+
if [ "$_locked" = "true" ] && [ -f "$WAL_FILE" ]; then
|
|
109
|
+
local size
|
|
110
|
+
size=$(wc -c < "$WAL_FILE" 2>/dev/null | tr -d ' ')
|
|
111
|
+
if [ "$size" -gt "$MAX_BUFFER_BYTES" ] 2>/dev/null; then
|
|
112
|
+
local total_entries
|
|
113
|
+
total_entries=$(wc -l < "$WAL_FILE" 2>/dev/null | tr -d ' ')
|
|
114
|
+
local dropped=$((total_entries - MAX_ENTRIES_AFTER_TRUNCATE))
|
|
115
|
+
if [ "$dropped" -gt 0 ]; then
|
|
116
|
+
echo "graph-wal: buffer exceeded ${MAX_BUFFER_BYTES} bytes. Dropping $dropped oldest entries (keeping $MAX_ENTRIES_AFTER_TRUNCATE)." >&2
|
|
117
|
+
fi
|
|
118
|
+
local tmp="$WAL_FILE.truncate.$$"
|
|
119
|
+
tail -n "$MAX_ENTRIES_AFTER_TRUNCATE" "$WAL_FILE" > "$tmp" 2>/dev/null \
|
|
120
|
+
&& mv "$tmp" "$WAL_FILE" \
|
|
121
|
+
|| rm -f "$tmp"
|
|
122
|
+
fi
|
|
123
|
+
fi
|
|
124
|
+
[ "$_locked" = "true" ] && _unlock
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
# --- drain: execute pending entries via graph-batch.sh ---
|
|
128
|
+
cmd_drain() {
|
|
129
|
+
if [ ! -f "$WAL_FILE" ] || [ ! -s "$WAL_FILE" ]; then
|
|
130
|
+
return 0
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
# Atomic rotate under lock: move to temp file so new appends don't conflict
|
|
134
|
+
_lock || return 0
|
|
135
|
+
local drain_file="$WAL_FILE.drain.$$"
|
|
136
|
+
mv "$WAL_FILE" "$drain_file" 2>/dev/null || { _unlock; return 0; }
|
|
137
|
+
_unlock
|
|
138
|
+
|
|
139
|
+
local total
|
|
140
|
+
total=$(wc -l < "$drain_file" 2>/dev/null | tr -d ' ')
|
|
141
|
+
local batch_size=20
|
|
142
|
+
local offset=0
|
|
143
|
+
local all_ok=true
|
|
144
|
+
|
|
145
|
+
while [ "$offset" -lt "$total" ]; do
|
|
146
|
+
# Extract batch of entries (offset+1 through offset+batch_size)
|
|
147
|
+
local batch_lines
|
|
148
|
+
batch_lines=$(tail -n "+$((offset + 1))" "$drain_file" | head -n "$batch_size")
|
|
149
|
+
|
|
150
|
+
# Build batch JSON array: [{statement, parameters}, ...]
|
|
151
|
+
local batch_json
|
|
152
|
+
batch_json=$(echo "$batch_lines" | jq -s '[.[] | {statement: .cypher, parameters: .params}]' 2>/dev/null)
|
|
153
|
+
|
|
154
|
+
if [ -z "$batch_json" ] || [ "$batch_json" = "[]" ]; then
|
|
155
|
+
offset=$((offset + batch_size))
|
|
156
|
+
continue
|
|
157
|
+
fi
|
|
158
|
+
|
|
159
|
+
# Execute via graph-batch.sh
|
|
160
|
+
if bash "$SCRIPT_DIR/bin/graph-batch.sh" "$batch_json" >/dev/null 2>&1; then
|
|
161
|
+
offset=$((offset + batch_size))
|
|
162
|
+
else
|
|
163
|
+
all_ok=false
|
|
164
|
+
break
|
|
165
|
+
fi
|
|
166
|
+
done
|
|
167
|
+
|
|
168
|
+
if [ "$all_ok" = "true" ]; then
|
|
169
|
+
# All batches succeeded — remove drain file
|
|
170
|
+
rm -f "$drain_file"
|
|
171
|
+
else
|
|
172
|
+
# Failure — merge unprocessed entries back
|
|
173
|
+
# Extract remaining entries (from offset+1 to end)
|
|
174
|
+
local remaining
|
|
175
|
+
remaining=$(tail -n "+$((offset + 1))" "$drain_file" 2>/dev/null)
|
|
176
|
+
|
|
177
|
+
if [ -n "$remaining" ]; then
|
|
178
|
+
_lock || { echo "$remaining" > "$WAL_FILE" 2>/dev/null; rm -f "$drain_file"; return 1; }
|
|
179
|
+
if [ -f "$WAL_FILE" ]; then
|
|
180
|
+
# New entries were written while draining; prepend remaining
|
|
181
|
+
local merge_file="$WAL_FILE.merge.$$"
|
|
182
|
+
{ echo "$remaining"; cat "$WAL_FILE"; } > "$merge_file" 2>/dev/null \
|
|
183
|
+
&& mv "$merge_file" "$WAL_FILE" \
|
|
184
|
+
&& rm -f "$drain_file" \
|
|
185
|
+
|| rm -f "$merge_file"
|
|
186
|
+
else
|
|
187
|
+
echo "$remaining" > "$WAL_FILE"
|
|
188
|
+
rm -f "$drain_file"
|
|
189
|
+
fi
|
|
190
|
+
_unlock
|
|
191
|
+
else
|
|
192
|
+
rm -f "$drain_file"
|
|
193
|
+
fi
|
|
194
|
+
fi
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
# --- status: pending count + file size as JSON ---
|
|
198
|
+
cmd_status() {
|
|
199
|
+
if [ -f "$WAL_FILE" ] && [ -s "$WAL_FILE" ]; then
|
|
200
|
+
local count size
|
|
201
|
+
count=$(wc -l < "$WAL_FILE" 2>/dev/null | tr -d ' ')
|
|
202
|
+
size=$(wc -c < "$WAL_FILE" 2>/dev/null | tr -d ' ')
|
|
203
|
+
jq -n --argjson pending "$count" --argjson bytes "$size" '{pending: $pending, bytes: $bytes}'
|
|
204
|
+
else
|
|
205
|
+
echo '{"pending":0,"bytes":0}'
|
|
206
|
+
fi
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
# --- Main dispatch ---
|
|
210
|
+
case "${1:-status}" in
|
|
211
|
+
append)
|
|
212
|
+
shift
|
|
213
|
+
cmd_append "$@"
|
|
214
|
+
;;
|
|
215
|
+
drain)
|
|
216
|
+
cmd_drain
|
|
217
|
+
;;
|
|
218
|
+
status)
|
|
219
|
+
cmd_status
|
|
220
|
+
;;
|
|
221
|
+
*)
|
|
222
|
+
echo "Usage: graph-wal.sh <command>"
|
|
223
|
+
echo ""
|
|
224
|
+
echo "Commands:"
|
|
225
|
+
echo " append <cypher> <params> Append entry to WAL (no network)"
|
|
226
|
+
echo " drain Execute pending entries"
|
|
227
|
+
echo " status Show pending count + file size"
|
|
228
|
+
;;
|
|
229
|
+
esac
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
5
|
+
CONFIG="$SCRIPT_DIR/egregore.json"
|
|
6
|
+
|
|
7
|
+
if [ ! -f "$CONFIG" ]; then
|
|
8
|
+
echo "Error: egregore.json not found. Run onboarding first." >&2
|
|
9
|
+
exit 1
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
# --- Local mode gate: bail immediately, no .env sourcing, no network ---
|
|
13
|
+
_MODE=$(jq -r '.mode // "connected"' "$CONFIG" 2>/dev/null)
|
|
14
|
+
if [ "$_MODE" = "local" ]; then
|
|
15
|
+
case "${1:-}" in
|
|
16
|
+
query) echo '{"results":[]}';;
|
|
17
|
+
schema) echo '{}';;
|
|
18
|
+
test) echo '{"status":"offline","reason":"local_mode"}';;
|
|
19
|
+
*) echo '{"results":[]}';;
|
|
20
|
+
esac
|
|
21
|
+
exit 0
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# Load specific variables from .env if it exists (safe extraction, no arbitrary code execution).
|
|
25
|
+
# A worktree missing its .env symlink resolves through worktree-links.sh, which
|
|
26
|
+
# self-heals the link and falls back to the main checkout's .env.
|
|
27
|
+
_ENV_FILE="$SCRIPT_DIR/.env"
|
|
28
|
+
if [ ! -f "$_ENV_FILE" ] && [ -f "$SCRIPT_DIR/bin/lib/worktree-links.sh" ]; then
|
|
29
|
+
# shellcheck source=bin/lib/worktree-links.sh
|
|
30
|
+
source "$SCRIPT_DIR/bin/lib/worktree-links.sh" 2>/dev/null || true
|
|
31
|
+
_ENV_FILE="$(egregore_env_file "$SCRIPT_DIR" 2>/dev/null || true)"
|
|
32
|
+
fi
|
|
33
|
+
if [ -n "$_ENV_FILE" ] && [ -f "$_ENV_FILE" ]; then
|
|
34
|
+
EGREGORE_API_URL="${EGREGORE_API_URL:-$(grep '^EGREGORE_API_URL=' "$_ENV_FILE" 2>/dev/null | cut -d'=' -f2- || true)}"
|
|
35
|
+
EGREGORE_API_KEY="${EGREGORE_API_KEY:-$(grep '^EGREGORE_API_KEY=' "$_ENV_FILE" 2>/dev/null | cut -d'=' -f2- || true)}"
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# api_url comes from egregore.json (committed, non-secret)
|
|
39
|
+
# api_key comes from .env only (EGREGORE_API_KEY) — never from egregore.json
|
|
40
|
+
API_URL="${EGREGORE_API_URL:-$(jq -r '.api_url // empty' "$CONFIG")}"
|
|
41
|
+
API_KEY="${EGREGORE_API_KEY:-}"
|
|
42
|
+
|
|
43
|
+
# --- Millisecond timer (macOS + Linux compatible) ---
|
|
44
|
+
_millis() {
|
|
45
|
+
if command -v gdate &>/dev/null; then
|
|
46
|
+
gdate +%s%3N
|
|
47
|
+
else
|
|
48
|
+
# macOS date doesn't support %N — use python3 fallback
|
|
49
|
+
python3 -c "import time; print(int(time.time() * 1000))" 2>/dev/null || echo "0"
|
|
50
|
+
fi
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# --- Optional read-through cache (opt-in via EGREGORE_GRAPH_CACHE_TTL) ---
|
|
54
|
+
# Read-only queries can be served from a local cache that bin/attendant.sh
|
|
55
|
+
# keeps warm in the background, so callers on the launch path (session-start
|
|
56
|
+
# greeting) pay zero network round-trips when warm. TTL in seconds; unset/0
|
|
57
|
+
# disables. Mutating queries are never cached.
|
|
58
|
+
_CACHE_TTL="${EGREGORE_GRAPH_CACHE_TTL:-0}"
|
|
59
|
+
# Key the cache by the MAIN checkout so worktree sessions hit the same warm
|
|
60
|
+
# cache the attendant maintains (its replay uses the same derivation).
|
|
61
|
+
# Worktrees have .git as a FILE pointing into the main repo's .git/worktrees/.
|
|
62
|
+
# api_url + slug are part of the key so entries never cross tenants when the
|
|
63
|
+
# same checkout is repointed at a different org/API (cache entries are keyed
|
|
64
|
+
# only by query+params below, so the directory must carry the tenant).
|
|
65
|
+
_CACHE_ROOT="$SCRIPT_DIR"
|
|
66
|
+
if [ -f "$SCRIPT_DIR/.git" ]; then
|
|
67
|
+
_WT_GITDIR=$(sed 's/^gitdir: //' "$SCRIPT_DIR/.git" 2>/dev/null || true)
|
|
68
|
+
if [ -n "$_WT_GITDIR" ]; then
|
|
69
|
+
_CACHE_ROOT=$(cd "$_WT_GITDIR/../../.." 2>/dev/null && pwd || echo "$SCRIPT_DIR")
|
|
70
|
+
fi
|
|
71
|
+
fi
|
|
72
|
+
# The scope is a non-secret fingerprint of the EFFECTIVE endpoint + credential
|
|
73
|
+
# (already resolved above: process env → .env → committed api_url). Committed
|
|
74
|
+
# config alone is not enough — an EGREGORE_API_URL/KEY override or a swapped
|
|
75
|
+
# .env can point this checkout at a different tenant than egregore.json names,
|
|
76
|
+
# and the entry key below carries only query+params. cksum output is a CRC,
|
|
77
|
+
# not a reversible or usable credential.
|
|
78
|
+
_CACHE_SCOPE=$(echo -n "${API_URL:-}|${API_KEY:-}" | cksum | cut -d' ' -f1)
|
|
79
|
+
_CACHE_DIR="$HOME/.egregore/graph-cache/$(echo -n "${_CACHE_ROOT}|${_CACHE_SCOPE}" | cksum | cut -d' ' -f1)"
|
|
80
|
+
|
|
81
|
+
_cache_key() {
|
|
82
|
+
echo -n "$1|${2:-}" | cksum | cut -d' ' -f1
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
_is_read_query() {
|
|
86
|
+
! echo "$1" | grep -qiE '\b(MERGE|CREATE|SET|DELETE|DETACH|REMOVE)\b'
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if [ -n "$API_URL" ] && [ -n "$API_KEY" ]; then
|
|
90
|
+
# === API MODE: Call Egregore API gateway ===
|
|
91
|
+
|
|
92
|
+
run_query() {
|
|
93
|
+
local cypher="$1"
|
|
94
|
+
local params
|
|
95
|
+
if [ -n "${2:-}" ]; then
|
|
96
|
+
params="$2"
|
|
97
|
+
else
|
|
98
|
+
params="{}"
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
local cache_file=""
|
|
102
|
+
if [ "$_CACHE_TTL" != "0" ] && _is_read_query "$cypher"; then
|
|
103
|
+
cache_file="$_CACHE_DIR/$(_cache_key "$cypher" "$params").json"
|
|
104
|
+
if [ -f "$cache_file" ]; then
|
|
105
|
+
local now cts
|
|
106
|
+
now=$(date +%s)
|
|
107
|
+
cts=$(jq -r '.ts // 0' "$cache_file" 2>/dev/null || echo 0)
|
|
108
|
+
if [ $(( now - cts )) -lt "$_CACHE_TTL" ]; then
|
|
109
|
+
jq -c '.result' "$cache_file" 2>/dev/null && return 0
|
|
110
|
+
fi
|
|
111
|
+
fi
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
local body
|
|
115
|
+
body=$(jq -n --arg stmt "$cypher" --argjson params "$params" \
|
|
116
|
+
'{statement: $stmt, parameters: $params}')
|
|
117
|
+
|
|
118
|
+
local start_ms
|
|
119
|
+
start_ms=$(_millis)
|
|
120
|
+
|
|
121
|
+
local response
|
|
122
|
+
response=$(curl -s -X POST "${API_URL}/api/graph/query" \
|
|
123
|
+
-H "Authorization: Bearer $API_KEY" \
|
|
124
|
+
-H "Content-Type: application/json" \
|
|
125
|
+
-d "$body" \
|
|
126
|
+
--max-time 30)
|
|
127
|
+
|
|
128
|
+
local end_ms
|
|
129
|
+
end_ms=$(_millis)
|
|
130
|
+
local latency_ms=$(( end_ms - start_ms ))
|
|
131
|
+
|
|
132
|
+
# Check for HTTP-level errors
|
|
133
|
+
if echo "$response" | jq -e '.detail' >/dev/null 2>&1; then
|
|
134
|
+
echo "API error:" >&2
|
|
135
|
+
echo "$response" | jq '.detail' >&2
|
|
136
|
+
bash "$SCRIPT_DIR/bin/telemetry.sh" emit "error" \
|
|
137
|
+
"$(jq -n --arg source "graph" --arg error_code "api_error" --argjson latency "$latency_ms" \
|
|
138
|
+
'{source: $source, error_code: $error_code, latency_ms: $latency}')" 2>/dev/null &
|
|
139
|
+
exit 1
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
# Emit latency telemetry (fire-and-forget)
|
|
143
|
+
bash "$SCRIPT_DIR/bin/telemetry.sh" emit "graph_query" \
|
|
144
|
+
"$(jq -n --argjson latency "$latency_ms" '{latency_ms: $latency}')" 2>/dev/null &
|
|
145
|
+
|
|
146
|
+
# Store for the attendant to keep warm (query+params travel with the result)
|
|
147
|
+
if [ -n "$cache_file" ]; then
|
|
148
|
+
mkdir -p "$_CACHE_DIR" 2>/dev/null
|
|
149
|
+
jq -n --arg q "$cypher" --argjson p "$params" --argjson ts "$(date +%s)" --argjson r "$response" \
|
|
150
|
+
'{query: $q, params: $p, ts: $ts, result: $r}' > "$cache_file" 2>/dev/null || true
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
echo "$response"
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
get_schema() {
|
|
157
|
+
local response
|
|
158
|
+
response=$(curl -s -X GET "${API_URL}/api/graph/schema" \
|
|
159
|
+
-H "Authorization: Bearer $API_KEY" \
|
|
160
|
+
--max-time 30)
|
|
161
|
+
echo "$response" | jq .
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
test_connection() {
|
|
165
|
+
local response
|
|
166
|
+
response=$(curl -s -X GET "${API_URL}/api/graph/test" \
|
|
167
|
+
-H "Authorization: Bearer $API_KEY" \
|
|
168
|
+
--max-time 10)
|
|
169
|
+
|
|
170
|
+
if echo "$response" | jq -e '.status == "ok"' >/dev/null 2>&1; then
|
|
171
|
+
echo "Connected to Egregore API at $API_URL"
|
|
172
|
+
else
|
|
173
|
+
echo "Failed to connect to Egregore API at $API_URL" >&2
|
|
174
|
+
echo "$response" >&2
|
|
175
|
+
exit 1
|
|
176
|
+
fi
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
elif [ -n "$API_URL" ]; then
|
|
180
|
+
# === CONNECTED CONFIG, MISSING KEY ===
|
|
181
|
+
# api_url is configured, so the org expects a live graph, but no API key was
|
|
182
|
+
# found. Faking '{"results":[]}' here made a missing credential (e.g. a
|
|
183
|
+
# worktree without its .env symlink) indistinguishable from an empty graph —
|
|
184
|
+
# fail loudly instead. `test` keeps its offline JSON contract: dashboards,
|
|
185
|
+
# render-card, and startup-check consume it.
|
|
186
|
+
_missing_credentials() {
|
|
187
|
+
{
|
|
188
|
+
echo "Error: api_url is configured but EGREGORE_API_KEY is empty (looked in: ${_ENV_FILE:-$SCRIPT_DIR/.env})."
|
|
189
|
+
echo " If this is a worktree, its .env symlink may be missing. Run /env to configure."
|
|
190
|
+
} >&2
|
|
191
|
+
exit 1
|
|
192
|
+
}
|
|
193
|
+
run_query() { _missing_credentials; }
|
|
194
|
+
get_schema() { _missing_credentials; }
|
|
195
|
+
test_connection() { echo '{"status":"offline","reason":"no_api_key"}'; }
|
|
196
|
+
|
|
197
|
+
else
|
|
198
|
+
# === OFFLINE MODE: no api_url configured (OSS/unconfigured) ===
|
|
199
|
+
run_query() { echo '{"results":[]}'; }
|
|
200
|
+
get_schema() { echo '{}'; }
|
|
201
|
+
test_connection() { echo '{"status":"offline","reason":"no_api_key"}'; }
|
|
202
|
+
fi
|
|
203
|
+
|
|
204
|
+
case "${1:-help}" in
|
|
205
|
+
query)
|
|
206
|
+
shift
|
|
207
|
+
run_query "$@"
|
|
208
|
+
;;
|
|
209
|
+
schema)
|
|
210
|
+
get_schema
|
|
211
|
+
;;
|
|
212
|
+
test)
|
|
213
|
+
test_connection
|
|
214
|
+
;;
|
|
215
|
+
help|*)
|
|
216
|
+
echo "Usage: graph.sh <command>"
|
|
217
|
+
echo ""
|
|
218
|
+
echo "Commands:"
|
|
219
|
+
echo " query <cypher> [params_json] Run a Cypher query"
|
|
220
|
+
echo " schema Show graph schema"
|
|
221
|
+
echo " test Test connection"
|
|
222
|
+
;;
|
|
223
|
+
esac
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# handoff-pr-backfill.sh — fill in PR numbers in a handoff file's ## Repo State
|
|
3
|
+
# table, then re-commit + push the memory repo. Detached fire-and-forget from
|
|
4
|
+
# /handoff's hot path so user-visible latency doesn't include N × `gh pr list`.
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# handoff-pr-backfill.sh <abs-handoff-file> <topic>
|
|
8
|
+
#
|
|
9
|
+
# Reads the ## Repo State table, looks up each (repo, branch)'s open PR via
|
|
10
|
+
# `gh pr list`, rewrites the PR column from `—` to `#N`, commits the memory
|
|
11
|
+
# repo, pushes to main. Silent on all failures — this is cosmetic.
|
|
12
|
+
#
|
|
13
|
+
# Detached usage (the skill / orchestrator call it this way):
|
|
14
|
+
# ( bash bin/handoff-pr-backfill.sh "$ABS_FILE" "$TOPIC" >/dev/null 2>&1 & ) >/dev/null 2>&1
|
|
15
|
+
|
|
16
|
+
set -uo pipefail
|
|
17
|
+
|
|
18
|
+
ABS_FILE="${1:-}"
|
|
19
|
+
TOPIC="${2:-handoff}"
|
|
20
|
+
|
|
21
|
+
[ -n "$ABS_FILE" ] && [ -f "$ABS_FILE" ] || exit 0
|
|
22
|
+
|
|
23
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
24
|
+
CONFIG="$SCRIPT_DIR/egregore.json"
|
|
25
|
+
|
|
26
|
+
command -v gh >/dev/null 2>&1 || exit 0
|
|
27
|
+
command -v jq >/dev/null 2>&1 || exit 0
|
|
28
|
+
|
|
29
|
+
GITHUB_ORG=$(jq -r '.github_org // empty' "$CONFIG" 2>/dev/null)
|
|
30
|
+
[ -n "$GITHUB_ORG" ] || exit 0
|
|
31
|
+
|
|
32
|
+
# Extract the ## Repo State table rows: format is `| repo | branch | — | base |`.
|
|
33
|
+
# Skip header and separator lines (first two rows of the markdown table).
|
|
34
|
+
ROWS=$(awk '
|
|
35
|
+
/^## Repo State/ { in_section=1; next }
|
|
36
|
+
in_section && /^## / { in_section=0 }
|
|
37
|
+
in_section && /^\|/ && !/^\|[-\s]/ && !/^\| Repo/ { print }
|
|
38
|
+
' "$ABS_FILE" 2>/dev/null)
|
|
39
|
+
|
|
40
|
+
[ -n "$ROWS" ] || exit 0
|
|
41
|
+
|
|
42
|
+
UPDATED=0
|
|
43
|
+
while IFS= read -r row; do
|
|
44
|
+
[ -z "$row" ] && continue
|
|
45
|
+
# Parse: | repo | branch | pr | base |
|
|
46
|
+
repo=$(echo "$row" | awk -F'|' '{gsub(/^ +| +$/,"",$2); print $2}')
|
|
47
|
+
branch=$(echo "$row" | awk -F'|' '{gsub(/^ +| +$/,"",$3); print $3}')
|
|
48
|
+
pr_col=$(echo "$row" | awk -F'|' '{gsub(/^ +| +$/,"",$4); print $4}')
|
|
49
|
+
|
|
50
|
+
# Only backfill `—` cells (leave `#N` alone)
|
|
51
|
+
[ "$pr_col" = "—" ] || continue
|
|
52
|
+
[ -n "$repo" ] && [ -n "$branch" ] || continue
|
|
53
|
+
|
|
54
|
+
pr_num=$(gh pr list --repo "$GITHUB_ORG/$repo" --head "$branch" --json number --jq '.[0].number // empty' 2>/dev/null || echo "")
|
|
55
|
+
[ -n "$pr_num" ] || continue
|
|
56
|
+
|
|
57
|
+
# Rewrite the `—` in this branch's row to `#N`. Match by branch name to
|
|
58
|
+
# avoid cross-repo collisions when repos share branch names.
|
|
59
|
+
awk -v branch="$branch" -v pr="#$pr_num" '
|
|
60
|
+
BEGIN { in_section=0 }
|
|
61
|
+
/^## Repo State/ { in_section=1; print; next }
|
|
62
|
+
in_section && /^## / { in_section=0 }
|
|
63
|
+
in_section && $0 ~ "\\| " branch " " && /—/ {
|
|
64
|
+
sub(/—/, pr)
|
|
65
|
+
print; next
|
|
66
|
+
}
|
|
67
|
+
{ print }
|
|
68
|
+
' "$ABS_FILE" > "$ABS_FILE.tmp" && mv "$ABS_FILE.tmp" "$ABS_FILE"
|
|
69
|
+
UPDATED=1
|
|
70
|
+
done <<< "$ROWS"
|
|
71
|
+
|
|
72
|
+
[ "$UPDATED" = "1" ] || exit 0
|
|
73
|
+
|
|
74
|
+
# Re-commit + push the memory repo with the backfilled PR numbers.
|
|
75
|
+
MEMORY_DIR="$SCRIPT_DIR/memory"
|
|
76
|
+
if [ -d "$MEMORY_DIR" ]; then
|
|
77
|
+
(
|
|
78
|
+
cd "$MEMORY_DIR" || exit 0
|
|
79
|
+
git add -A >/dev/null 2>&1
|
|
80
|
+
git commit -m "Backfill PR numbers for handoff: $TOPIC" >/dev/null 2>&1 || exit 0
|
|
81
|
+
git pull --rebase origin main --quiet >/dev/null 2>&1
|
|
82
|
+
git push origin main --quiet >/dev/null 2>&1
|
|
83
|
+
) || true
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
exit 0
|