create-egregore 0.17.4 → 0.18.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.
Files changed (110) hide show
  1. package/assets/egregore-launcher.js +1965 -0
  2. package/assets/egregore-launcher.sh +385 -0
  3. package/bin/cli.js +472 -81
  4. package/lib/api.js +2 -2
  5. package/lib/auth.js +255 -20
  6. package/lib/codex-runtime.js +268 -0
  7. package/lib/commands.js +31 -0
  8. package/lib/context.js +1 -1
  9. package/lib/local.js +616 -189
  10. package/lib/setup.js +822 -84
  11. package/lib/ui.js +780 -84
  12. package/package.json +11 -3
  13. package/runtime/codex/.codex/config.toml +15 -0
  14. package/runtime/codex/.codex/hooks/branch-guard.js +234 -0
  15. package/runtime/codex/.codex/hooks.json +17 -0
  16. package/runtime/codex/.codex/skills/activity/SKILL.md +70 -0
  17. package/runtime/codex/.codex/skills/add/SKILL.md +25 -0
  18. package/runtime/codex/.codex/skills/announce/SKILL.md +54 -0
  19. package/runtime/codex/.codex/skills/answer/SKILL.md +25 -0
  20. package/runtime/codex/.codex/skills/archive/SKILL.md +41 -0
  21. package/runtime/codex/.codex/skills/ask/SKILL.md +59 -0
  22. package/runtime/codex/.codex/skills/branch/SKILL.md +25 -0
  23. package/runtime/codex/.codex/skills/checkup/SKILL.md +41 -0
  24. package/runtime/codex/.codex/skills/comment/SKILL.md +25 -0
  25. package/runtime/codex/.codex/skills/commit/SKILL.md +25 -0
  26. package/runtime/codex/.codex/skills/contribute/SKILL.md +25 -0
  27. package/runtime/codex/.codex/skills/dark-mode/SKILL.md +25 -0
  28. package/runtime/codex/.codex/skills/dashboard/SKILL.md +59 -0
  29. package/runtime/codex/.codex/skills/deep-reflect/SKILL.md +70 -0
  30. package/runtime/codex/.codex/skills/delete-user/SKILL.md +25 -0
  31. package/runtime/codex/.codex/skills/edit/SKILL.md +25 -0
  32. package/runtime/codex/.codex/skills/edit-process/SKILL.md +25 -0
  33. package/runtime/codex/.codex/skills/egregore-voice/SKILL.md +25 -0
  34. package/runtime/codex/.codex/skills/emissary/SKILL.md +41 -0
  35. package/runtime/codex/.codex/skills/env/SKILL.md +25 -0
  36. package/runtime/codex/.codex/skills/handoff/SKILL.md +67 -0
  37. package/runtime/codex/.codex/skills/harvest/SKILL.md +107 -0
  38. package/runtime/codex/.codex/skills/infra/SKILL.md +41 -0
  39. package/runtime/codex/.codex/skills/ingest/SKILL.md +25 -0
  40. package/runtime/codex/.codex/skills/ingest-google/SKILL.md +25 -0
  41. package/runtime/codex/.codex/skills/ingest-user-interview/SKILL.md +41 -0
  42. package/runtime/codex/.codex/skills/invite/SKILL.md +40 -0
  43. package/runtime/codex/.codex/skills/issue/SKILL.md +41 -0
  44. package/runtime/codex/.codex/skills/me/SKILL.md +25 -0
  45. package/runtime/codex/.codex/skills/meeting/SKILL.md +41 -0
  46. package/runtime/codex/.codex/skills/note/SKILL.md +25 -0
  47. package/runtime/codex/.codex/skills/onboarding/SKILL.md +41 -0
  48. package/runtime/codex/.codex/skills/pr/SKILL.md +25 -0
  49. package/runtime/codex/.codex/skills/project/SKILL.md +41 -0
  50. package/runtime/codex/.codex/skills/pull/SKILL.md +25 -0
  51. package/runtime/codex/.codex/skills/push/SKILL.md +25 -0
  52. package/runtime/codex/.codex/skills/quest/SKILL.md +77 -0
  53. package/runtime/codex/.codex/skills/quest-suggest/SKILL.md +25 -0
  54. package/runtime/codex/.codex/skills/reflect/SKILL.md +41 -0
  55. package/runtime/codex/.codex/skills/review-pr/SKILL.md +41 -0
  56. package/runtime/codex/.codex/skills/save/SKILL.md +62 -0
  57. package/runtime/codex/.codex/skills/setup/SKILL.md +25 -0
  58. package/runtime/codex/.codex/skills/summon/SKILL.md +41 -0
  59. package/runtime/codex/.codex/skills/sync-repos/SKILL.md +25 -0
  60. package/runtime/codex/.codex/skills/telegram-connect/SKILL.md +25 -0
  61. package/runtime/codex/.codex/skills/telemetry/SKILL.md +25 -0
  62. package/runtime/codex/.codex/skills/test/SKILL.md +41 -0
  63. package/runtime/codex/.codex/skills/the-spiral/SKILL.md +84 -0
  64. package/runtime/codex/.codex/skills/todo/SKILL.md +41 -0
  65. package/runtime/codex/.codex/skills/tui-design/SKILL.md +41 -0
  66. package/runtime/codex/.codex/skills/tutorial/SKILL.md +41 -0
  67. package/runtime/codex/.codex/skills/update/SKILL.md +25 -0
  68. package/runtime/codex/.codex/skills/view/SKILL.md +41 -0
  69. package/runtime/codex/.codex/skills/visual-explain/SKILL.md +41 -0
  70. package/runtime/codex/.codex/skills/wrap/SKILL.md +55 -0
  71. package/runtime/codex/.codex/spec-manifest.json +108 -0
  72. package/runtime/codex/AGENTS.md +328 -0
  73. package/runtime/codex/bin/activity-data.sh +180 -0
  74. package/runtime/codex/bin/agent.sh +669 -0
  75. package/runtime/codex/bin/codex-install-skills.sh +78 -0
  76. package/runtime/codex/bin/codex-render-spec.mjs +308 -0
  77. package/runtime/codex/bin/codex-session-start.sh +238 -0
  78. package/runtime/codex/bin/codex-skill-render.mjs +315 -0
  79. package/runtime/codex/bin/codex-sync-skills.sh +223 -0
  80. package/runtime/codex/bin/dashboard-data.sh +178 -0
  81. package/runtime/codex/bin/graph-batch.sh +60 -0
  82. package/runtime/codex/bin/graph-op.sh +397 -0
  83. package/runtime/codex/bin/graph-wal.sh +228 -0
  84. package/runtime/codex/bin/graph.sh +141 -0
  85. package/runtime/codex/bin/handoff-pr-backfill.sh +86 -0
  86. package/runtime/codex/bin/handoff-run.sh +569 -0
  87. package/runtime/codex/bin/handoff-save-egregore.sh +131 -0
  88. package/runtime/codex/bin/index-handoff.sh +306 -0
  89. package/runtime/codex/bin/lib/artifact-id.sh +46 -0
  90. package/runtime/codex/bin/lib/config.sh +71 -0
  91. package/runtime/codex/bin/lib/context.sh +511 -0
  92. package/runtime/codex/bin/lib/dashboard-artifact.sh +142 -0
  93. package/runtime/codex/bin/lib/edit-loopback.mjs +652 -0
  94. package/runtime/codex/bin/lib/ensure-develop.sh +183 -0
  95. package/runtime/codex/bin/lib/git-sync.sh +262 -0
  96. package/runtime/codex/bin/lib/greeting.sh +432 -0
  97. package/runtime/codex/bin/lib/hash.sh +13 -0
  98. package/runtime/codex/bin/lib/identity.sh +164 -0
  99. package/runtime/codex/bin/lib/permissions.sh +106 -0
  100. package/runtime/codex/bin/lib/time.sh +56 -0
  101. package/runtime/codex/bin/lib/worktree-links.sh +54 -0
  102. package/runtime/codex/bin/notify.sh +207 -0
  103. package/runtime/codex/bin/publish-artifact.sh +187 -0
  104. package/runtime/codex/bin/publish-references.sh +144 -0
  105. package/runtime/codex/bin/repo-state.sh +99 -0
  106. package/runtime/codex/bin/telemetry.sh +350 -0
  107. package/runtime/codex/bin/worktree-create.sh +115 -0
  108. package/runtime/codex/bin/worktree-remove.sh +50 -0
  109. package/runtime/codex/bin/worktree.sh +199 -0
  110. package/runtime/codex/manifest.json +162 -0
@@ -0,0 +1,228 @@
1
+ #!/bin/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
+
36
+ # --- Cross-platform file locking via mkdir (atomic on all POSIX systems) ---
37
+ _lock() {
38
+ local attempts=0
39
+ while ! mkdir "$LOCK_DIR" 2>/dev/null; do
40
+ attempts=$((attempts + 1))
41
+ if [ "$attempts" -gt 50 ]; then
42
+ # Stale lock — check if holder PID is alive
43
+ local holder_pid
44
+ holder_pid=$(cat "$LOCK_DIR/pid" 2>/dev/null || echo "")
45
+ if [ -n "$holder_pid" ] && ! kill -0 "$holder_pid" 2>/dev/null; then
46
+ rm -rf "$LOCK_DIR" 2>/dev/null
47
+ continue
48
+ fi
49
+ echo "graph-wal: lock timeout after 5s" >&2
50
+ return 1
51
+ fi
52
+ sleep 0.1
53
+ done
54
+ echo $$ > "$LOCK_DIR/pid" 2>/dev/null
55
+ }
56
+
57
+ _unlock() {
58
+ rm -rf "$LOCK_DIR" 2>/dev/null
59
+ }
60
+
61
+ trap '_unlock' EXIT
62
+
63
+ # --- Resolve session ID (same pattern as telemetry.sh) ---
64
+ _resolve_session_id() {
65
+ if [ -n "${EGREGORE_SESSION_ID:-}" ]; then
66
+ echo "$EGREGORE_SESSION_ID"
67
+ return
68
+ fi
69
+ local proj_hash
70
+ proj_hash=$(echo -n "$SCRIPT_DIR" | md5 2>/dev/null || echo -n "$SCRIPT_DIR" | md5sum 2>/dev/null | cut -d' ' -f1)
71
+ local sid_file="$WAL_DIR/session-${proj_hash}.id"
72
+ if [ -f "$sid_file" ]; then
73
+ cat "$sid_file" 2>/dev/null || echo "unknown"
74
+ else
75
+ echo "unknown"
76
+ fi
77
+ }
78
+
79
+ # --- append: O(1) local write, no network ---
80
+ cmd_append() {
81
+ local cypher="${1:?Usage: graph-wal.sh append <cypher> <params_json>}"
82
+ local params="${2:-"{}"}"
83
+
84
+ mkdir -p "$WAL_DIR"
85
+
86
+ local ts
87
+ ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
88
+ local sid
89
+ sid=$(_resolve_session_id)
90
+
91
+ # Build JSON line
92
+ local line
93
+ line=$(jq -n -c \
94
+ --arg ts "$ts" \
95
+ --arg sid "$sid" \
96
+ --arg cypher "$cypher" \
97
+ --argjson params "$params" \
98
+ '{ts: $ts, sid: $sid, cypher: $cypher, params: $params}')
99
+
100
+ # Append under lock — if lock fails, append anyway (durability > mutual exclusion)
101
+ local _locked=true
102
+ _lock || _locked=false
103
+ echo "$line" >> "$WAL_FILE"
104
+
105
+ # Buffer guard: truncate to last N entries if file exceeds max size
106
+ # Only safe under lock — tail+mv is not atomic and races with concurrent writers
107
+ if [ "$_locked" = "true" ] && [ -f "$WAL_FILE" ]; then
108
+ local size
109
+ size=$(wc -c < "$WAL_FILE" 2>/dev/null | tr -d ' ')
110
+ if [ "$size" -gt "$MAX_BUFFER_BYTES" ] 2>/dev/null; then
111
+ local total_entries
112
+ total_entries=$(wc -l < "$WAL_FILE" 2>/dev/null | tr -d ' ')
113
+ local dropped=$((total_entries - MAX_ENTRIES_AFTER_TRUNCATE))
114
+ if [ "$dropped" -gt 0 ]; then
115
+ echo "graph-wal: buffer exceeded ${MAX_BUFFER_BYTES} bytes. Dropping $dropped oldest entries (keeping $MAX_ENTRIES_AFTER_TRUNCATE)." >&2
116
+ fi
117
+ local tmp="$WAL_FILE.truncate.$$"
118
+ tail -n "$MAX_ENTRIES_AFTER_TRUNCATE" "$WAL_FILE" > "$tmp" 2>/dev/null \
119
+ && mv "$tmp" "$WAL_FILE" \
120
+ || rm -f "$tmp"
121
+ fi
122
+ fi
123
+ [ "$_locked" = "true" ] && _unlock
124
+ }
125
+
126
+ # --- drain: execute pending entries via graph-batch.sh ---
127
+ cmd_drain() {
128
+ if [ ! -f "$WAL_FILE" ] || [ ! -s "$WAL_FILE" ]; then
129
+ return 0
130
+ fi
131
+
132
+ # Atomic rotate under lock: move to temp file so new appends don't conflict
133
+ _lock || return 0
134
+ local drain_file="$WAL_FILE.drain.$$"
135
+ mv "$WAL_FILE" "$drain_file" 2>/dev/null || { _unlock; return 0; }
136
+ _unlock
137
+
138
+ local total
139
+ total=$(wc -l < "$drain_file" 2>/dev/null | tr -d ' ')
140
+ local batch_size=20
141
+ local offset=0
142
+ local all_ok=true
143
+
144
+ while [ "$offset" -lt "$total" ]; do
145
+ # Extract batch of entries (offset+1 through offset+batch_size)
146
+ local batch_lines
147
+ batch_lines=$(tail -n "+$((offset + 1))" "$drain_file" | head -n "$batch_size")
148
+
149
+ # Build batch JSON array: [{statement, parameters}, ...]
150
+ local batch_json
151
+ batch_json=$(echo "$batch_lines" | jq -s '[.[] | {statement: .cypher, parameters: .params}]' 2>/dev/null)
152
+
153
+ if [ -z "$batch_json" ] || [ "$batch_json" = "[]" ]; then
154
+ offset=$((offset + batch_size))
155
+ continue
156
+ fi
157
+
158
+ # Execute via graph-batch.sh
159
+ if bash "$SCRIPT_DIR/bin/graph-batch.sh" "$batch_json" >/dev/null 2>&1; then
160
+ offset=$((offset + batch_size))
161
+ else
162
+ all_ok=false
163
+ break
164
+ fi
165
+ done
166
+
167
+ if [ "$all_ok" = "true" ]; then
168
+ # All batches succeeded — remove drain file
169
+ rm -f "$drain_file"
170
+ else
171
+ # Failure — merge unprocessed entries back
172
+ # Extract remaining entries (from offset+1 to end)
173
+ local remaining
174
+ remaining=$(tail -n "+$((offset + 1))" "$drain_file" 2>/dev/null)
175
+
176
+ if [ -n "$remaining" ]; then
177
+ _lock || { echo "$remaining" > "$WAL_FILE" 2>/dev/null; rm -f "$drain_file"; return 1; }
178
+ if [ -f "$WAL_FILE" ]; then
179
+ # New entries were written while draining; prepend remaining
180
+ local merge_file="$WAL_FILE.merge.$$"
181
+ { echo "$remaining"; cat "$WAL_FILE"; } > "$merge_file" 2>/dev/null \
182
+ && mv "$merge_file" "$WAL_FILE" \
183
+ && rm -f "$drain_file" \
184
+ || rm -f "$merge_file"
185
+ else
186
+ echo "$remaining" > "$WAL_FILE"
187
+ rm -f "$drain_file"
188
+ fi
189
+ _unlock
190
+ else
191
+ rm -f "$drain_file"
192
+ fi
193
+ fi
194
+ }
195
+
196
+ # --- status: pending count + file size as JSON ---
197
+ cmd_status() {
198
+ if [ -f "$WAL_FILE" ] && [ -s "$WAL_FILE" ]; then
199
+ local count size
200
+ count=$(wc -l < "$WAL_FILE" 2>/dev/null | tr -d ' ')
201
+ size=$(wc -c < "$WAL_FILE" 2>/dev/null | tr -d ' ')
202
+ jq -n --argjson pending "$count" --argjson bytes "$size" '{pending: $pending, bytes: $bytes}'
203
+ else
204
+ echo '{"pending":0,"bytes":0}'
205
+ fi
206
+ }
207
+
208
+ # --- Main dispatch ---
209
+ case "${1:-status}" in
210
+ append)
211
+ shift
212
+ cmd_append "$@"
213
+ ;;
214
+ drain)
215
+ cmd_drain
216
+ ;;
217
+ status)
218
+ cmd_status
219
+ ;;
220
+ *)
221
+ echo "Usage: graph-wal.sh <command>"
222
+ echo ""
223
+ echo "Commands:"
224
+ echo " append <cypher> <params> Append entry to WAL (no network)"
225
+ echo " drain Execute pending entries"
226
+ echo " status Show pending count + file size"
227
+ ;;
228
+ esac
@@ -0,0 +1,141 @@
1
+ #!/bin/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
+ if [ -f "$SCRIPT_DIR/.env" ]; then
26
+ EGREGORE_API_URL="${EGREGORE_API_URL:-$(grep '^EGREGORE_API_URL=' "$SCRIPT_DIR/.env" 2>/dev/null | cut -d'=' -f2- || true)}"
27
+ EGREGORE_API_KEY="${EGREGORE_API_KEY:-$(grep '^EGREGORE_API_KEY=' "$SCRIPT_DIR/.env" 2>/dev/null | cut -d'=' -f2- || true)}"
28
+ fi
29
+
30
+ # api_url comes from egregore.json (committed, non-secret)
31
+ # api_key comes from .env only (EGREGORE_API_KEY) — never from egregore.json
32
+ API_URL="${EGREGORE_API_URL:-$(jq -r '.api_url // empty' "$CONFIG")}"
33
+ API_KEY="${EGREGORE_API_KEY:-}"
34
+
35
+ # --- Millisecond timer (macOS + Linux compatible) ---
36
+ _millis() {
37
+ if command -v gdate &>/dev/null; then
38
+ gdate +%s%3N
39
+ else
40
+ # macOS date doesn't support %N — use python3 fallback
41
+ python3 -c "import time; print(int(time.time() * 1000))" 2>/dev/null || echo "0"
42
+ fi
43
+ }
44
+
45
+ if [ -n "$API_URL" ] && [ -n "$API_KEY" ]; then
46
+ # === API MODE: Call Egregore API gateway ===
47
+
48
+ run_query() {
49
+ local cypher="$1"
50
+ local params
51
+ if [ -n "${2:-}" ]; then
52
+ params="$2"
53
+ else
54
+ params="{}"
55
+ fi
56
+
57
+ local body
58
+ body=$(jq -n --arg stmt "$cypher" --argjson params "$params" \
59
+ '{statement: $stmt, parameters: $params}')
60
+
61
+ local start_ms
62
+ start_ms=$(_millis)
63
+
64
+ local response
65
+ response=$(curl -s -X POST "${API_URL}/api/graph/query" \
66
+ -H "Authorization: Bearer $API_KEY" \
67
+ -H "Content-Type: application/json" \
68
+ -d "$body" \
69
+ --max-time 30)
70
+
71
+ local end_ms
72
+ end_ms=$(_millis)
73
+ local latency_ms=$(( end_ms - start_ms ))
74
+
75
+ # Check for HTTP-level errors
76
+ if echo "$response" | jq -e '.detail' >/dev/null 2>&1; then
77
+ echo "API error:" >&2
78
+ echo "$response" | jq '.detail' >&2
79
+ bash "$SCRIPT_DIR/bin/telemetry.sh" emit "error" \
80
+ "$(jq -n --arg source "graph" --arg error_code "api_error" --argjson latency "$latency_ms" \
81
+ '{source: $source, error_code: $error_code, latency_ms: $latency}')" 2>/dev/null &
82
+ exit 1
83
+ fi
84
+
85
+ # Emit latency telemetry (fire-and-forget)
86
+ bash "$SCRIPT_DIR/bin/telemetry.sh" emit "graph_query" \
87
+ "$(jq -n --argjson latency "$latency_ms" '{latency_ms: $latency}')" 2>/dev/null &
88
+
89
+ echo "$response"
90
+ }
91
+
92
+ get_schema() {
93
+ local response
94
+ response=$(curl -s -X GET "${API_URL}/api/graph/schema" \
95
+ -H "Authorization: Bearer $API_KEY" \
96
+ --max-time 30)
97
+ echo "$response" | jq .
98
+ }
99
+
100
+ test_connection() {
101
+ local response
102
+ response=$(curl -s -X GET "${API_URL}/api/graph/test" \
103
+ -H "Authorization: Bearer $API_KEY" \
104
+ --max-time 10)
105
+
106
+ if echo "$response" | jq -e '.status == "ok"' >/dev/null 2>&1; then
107
+ echo "Connected to Egregore API at $API_URL"
108
+ else
109
+ echo "Failed to connect to Egregore API at $API_URL" >&2
110
+ echo "$response" >&2
111
+ exit 1
112
+ fi
113
+ }
114
+
115
+ else
116
+ # === OFFLINE MODE: No API key — return empty results (OSS/local) ===
117
+ run_query() { echo '{"results":[]}'; }
118
+ get_schema() { echo '{}'; }
119
+ test_connection() { echo '{"status":"offline","reason":"no_api_key"}'; }
120
+ fi
121
+
122
+ case "${1:-help}" in
123
+ query)
124
+ shift
125
+ run_query "$@"
126
+ ;;
127
+ schema)
128
+ get_schema
129
+ ;;
130
+ test)
131
+ test_connection
132
+ ;;
133
+ help|*)
134
+ echo "Usage: graph.sh <command>"
135
+ echo ""
136
+ echo "Commands:"
137
+ echo " query <cypher> [params_json] Run a Cypher query"
138
+ echo " schema Show graph schema"
139
+ echo " test Test connection"
140
+ ;;
141
+ esac
@@ -0,0 +1,86 @@
1
+ #!/bin/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