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.
Files changed (171) hide show
  1. package/assets/egregore-launcher.js +118 -13
  2. package/bin/cli.js +19 -3
  3. package/lib/connect-runtime.js +4 -2
  4. package/lib/local.js +15 -1
  5. package/lib/prime-runtime.js +208 -0
  6. package/lib/setup.js +45 -3
  7. package/package.json +4 -3
  8. package/runtime/codex/.codex/skills/ingest-notion/SKILL.md +29 -0
  9. package/runtime/codex/.codex/skills/notion-connect/SKILL.md +29 -0
  10. package/runtime/codex/.codex/skills/teams-connect/SKILL.md +29 -0
  11. package/runtime/codex/.codex/spec-manifest.json +3 -3
  12. package/runtime/codex/AGENTS.md +11 -4
  13. package/runtime/codex/bin/activity-data.sh +4 -8
  14. package/runtime/codex/bin/attendant.sh +13 -0
  15. package/runtime/codex/bin/autosave.sh +27 -1
  16. package/runtime/codex/bin/codex-render-spec.mjs +6 -0
  17. package/runtime/codex/bin/codex-session-start.sh +3 -1
  18. package/runtime/codex/bin/graph-batch.sh +25 -6
  19. package/runtime/codex/bin/graph-op.sh +5 -4
  20. package/runtime/codex/bin/graph.sh +31 -5
  21. package/runtime/codex/bin/ingest.py +81 -5
  22. package/runtime/codex/bin/lib/context.sh +26 -1
  23. package/runtime/codex/bin/lib/greeting.sh +4 -1
  24. package/runtime/codex/bin/lib/worktree-links.sh +25 -0
  25. package/runtime/codex/manifest.json +7 -1
  26. package/runtime/pi/.codex/skills/ingest-notion/SKILL.md +29 -0
  27. package/runtime/pi/.codex/skills/notion-connect/SKILL.md +29 -0
  28. package/runtime/pi/.codex/skills/teams-connect/SKILL.md +29 -0
  29. package/runtime/pi/.codex/spec-manifest.json +3 -3
  30. package/runtime/pi/.pi/APPEND_SYSTEM.md +3 -2
  31. package/runtime/pi/.pi/extensions/egregore.ts +17 -6
  32. package/runtime/pi/.pi/spec-manifest.json +2 -2
  33. package/runtime/pi/AGENTS.md +11 -4
  34. package/runtime/pi/bin/activity-data.sh +4 -8
  35. package/runtime/pi/bin/attendant.sh +13 -0
  36. package/runtime/pi/bin/autosave.sh +27 -1
  37. package/runtime/pi/bin/codex-render-spec.mjs +6 -0
  38. package/runtime/pi/bin/codex-session-start.sh +3 -1
  39. package/runtime/pi/bin/graph-batch.sh +25 -6
  40. package/runtime/pi/bin/graph-op.sh +5 -4
  41. package/runtime/pi/bin/graph.sh +31 -5
  42. package/runtime/pi/bin/ingest.py +81 -5
  43. package/runtime/pi/bin/lib/context.sh +26 -1
  44. package/runtime/pi/bin/lib/greeting.sh +4 -1
  45. package/runtime/pi/bin/lib/worktree-links.sh +25 -0
  46. package/runtime/pi/bin/pi-branch-policy.mjs +138 -1
  47. package/runtime/pi/bin/pi-product-workflows.mjs +35 -7
  48. package/runtime/pi/manifest.json +7 -1
  49. package/runtime/prime/.claude/context/notification-consent.md +66 -0
  50. package/runtime/prime/.codex/config.toml +17 -0
  51. package/runtime/prime/.codex/hooks/branch-guard.js +461 -0
  52. package/runtime/prime/.codex/hooks/search-hint.js +67 -0
  53. package/runtime/prime/.codex/hooks.json +30 -0
  54. package/runtime/prime/.codex/skills/activity/SKILL.md +82 -0
  55. package/runtime/prime/.codex/skills/announce/SKILL.md +67 -0
  56. package/runtime/prime/.codex/skills/archive/SKILL.md +45 -0
  57. package/runtime/prime/.codex/skills/branch/SKILL.md +29 -0
  58. package/runtime/prime/.codex/skills/checkup/SKILL.md +45 -0
  59. package/runtime/prime/.codex/skills/commit/SKILL.md +29 -0
  60. package/runtime/prime/.codex/skills/contribute/SKILL.md +29 -0
  61. package/runtime/prime/.codex/skills/create-skill/SKILL.md +29 -0
  62. package/runtime/prime/.codex/skills/dark-mode/SKILL.md +29 -0
  63. package/runtime/prime/.codex/skills/dashboard/SKILL.md +59 -0
  64. package/runtime/prime/.codex/skills/deep-reflect/SKILL.md +109 -0
  65. package/runtime/prime/.codex/skills/delete-user/SKILL.md +29 -0
  66. package/runtime/prime/.codex/skills/emissary/SKILL.md +45 -0
  67. package/runtime/prime/.codex/skills/handoff/SKILL.md +166 -0
  68. package/runtime/prime/.codex/skills/harvest/QUESTION_PALETTE.md +76 -0
  69. package/runtime/prime/.codex/skills/harvest/SKILL.md +131 -0
  70. package/runtime/prime/.codex/skills/ingest/SKILL.md +29 -0
  71. package/runtime/prime/.codex/skills/ingest-notion/SKILL.md +29 -0
  72. package/runtime/prime/.codex/skills/ingest-user-interview/SKILL.md +45 -0
  73. package/runtime/prime/.codex/skills/invite/SKILL.md +44 -0
  74. package/runtime/prime/.codex/skills/issue/SKILL.md +45 -0
  75. package/runtime/prime/.codex/skills/me/SKILL.md +29 -0
  76. package/runtime/prime/.codex/skills/meeting/SKILL.md +45 -0
  77. package/runtime/prime/.codex/skills/note/SKILL.md +29 -0
  78. package/runtime/prime/.codex/skills/notion-connect/SKILL.md +29 -0
  79. package/runtime/prime/.codex/skills/onboarding/SKILL.md +45 -0
  80. package/runtime/prime/.codex/skills/pr/SKILL.md +29 -0
  81. package/runtime/prime/.codex/skills/pull/SKILL.md +29 -0
  82. package/runtime/prime/.codex/skills/push/SKILL.md +29 -0
  83. package/runtime/prime/.codex/skills/quest/SKILL.md +80 -0
  84. package/runtime/prime/.codex/skills/reflect/SKILL.md +45 -0
  85. package/runtime/prime/.codex/skills/review-pr/SKILL.md +45 -0
  86. package/runtime/prime/.codex/skills/save/SKILL.md +125 -0
  87. package/runtime/prime/.codex/skills/slack-connect/SKILL.md +29 -0
  88. package/runtime/prime/.codex/skills/summon/SKILL.md +45 -0
  89. package/runtime/prime/.codex/skills/sync-repos/SKILL.md +29 -0
  90. package/runtime/prime/.codex/skills/teams-connect/SKILL.md +29 -0
  91. package/runtime/prime/.codex/skills/telegram-connect/SKILL.md +29 -0
  92. package/runtime/prime/.codex/skills/telemetry/SKILL.md +29 -0
  93. package/runtime/prime/.codex/skills/test/SKILL.md +45 -0
  94. package/runtime/prime/.codex/skills/the-spiral/SKILL.md +84 -0
  95. package/runtime/prime/.codex/skills/tui-design/SKILL.md +45 -0
  96. package/runtime/prime/.codex/skills/tutorial/SKILL.md +45 -0
  97. package/runtime/prime/.codex/skills/update/SKILL.md +29 -0
  98. package/runtime/prime/.codex/skills/view/SKILL.md +156 -0
  99. package/runtime/prime/.codex/skills/view/agents/openai.yaml +4 -0
  100. package/runtime/prime/.codex/skills/view/assets/compose-scaffold.html +221 -0
  101. package/runtime/prime/.codex/skills/visual-explain/SKILL.md +45 -0
  102. package/runtime/prime/.codex/skills/wrap/SKILL.md +58 -0
  103. package/runtime/prime/.codex/spec-manifest.json +132 -0
  104. package/runtime/prime/.prime/agent/APPEND_SYSTEM.md +393 -0
  105. package/runtime/prime/.prime/agent/extensions/egregore.ts +460 -0
  106. package/runtime/prime/.prime/agent/settings.json +4 -0
  107. package/runtime/prime/.prime/agent/skills/egregore/SKILL.md +34 -0
  108. package/runtime/prime/.prime/agent/skills/egregore/pyproject.toml +13 -0
  109. package/runtime/prime/.prime/agent/skills/egregore/src/egregore/__init__.py +156 -0
  110. package/runtime/prime/.prime/agent/spec-manifest.json +12 -0
  111. package/runtime/prime/AGENTS.md +489 -0
  112. package/runtime/prime/bin/activity-data.sh +238 -0
  113. package/runtime/prime/bin/agent.sh +775 -0
  114. package/runtime/prime/bin/attendant.sh +707 -0
  115. package/runtime/prime/bin/autosave.sh +112 -0
  116. package/runtime/prime/bin/capture-reconcile.sh +69 -0
  117. package/runtime/prime/bin/capture-run.sh +341 -0
  118. package/runtime/prime/bin/codex-install-skills.sh +78 -0
  119. package/runtime/prime/bin/codex-render-spec.mjs +440 -0
  120. package/runtime/prime/bin/codex-session-start.sh +265 -0
  121. package/runtime/prime/bin/codex-skill-render.mjs +347 -0
  122. package/runtime/prime/bin/codex-sync-skills.sh +232 -0
  123. package/runtime/prime/bin/connect-refresh.sh +33 -0
  124. package/runtime/prime/bin/dashboard-data.sh +178 -0
  125. package/runtime/prime/bin/graph-batch.sh +79 -0
  126. package/runtime/prime/bin/graph-op.sh +1102 -0
  127. package/runtime/prime/bin/graph-wal.sh +229 -0
  128. package/runtime/prime/bin/graph.sh +223 -0
  129. package/runtime/prime/bin/handoff-pr-backfill.sh +86 -0
  130. package/runtime/prime/bin/handoff-run.sh +671 -0
  131. package/runtime/prime/bin/handoff-save-egregore.sh +212 -0
  132. package/runtime/prime/bin/index-handoff.sh +322 -0
  133. package/runtime/prime/bin/ingest-graph.sh +80 -0
  134. package/runtime/prime/bin/ingest-surface.html +495 -0
  135. package/runtime/prime/bin/ingest.py +756 -0
  136. package/runtime/prime/bin/ingest.sh +279 -0
  137. package/runtime/prime/bin/ingest_graph.py +856 -0
  138. package/runtime/prime/bin/ingest_surface.py +544 -0
  139. package/runtime/prime/bin/lib/artifact-id.sh +46 -0
  140. package/runtime/prime/bin/lib/config.sh +116 -0
  141. package/runtime/prime/bin/lib/context.sh +621 -0
  142. package/runtime/prime/bin/lib/dashboard-artifact.sh +242 -0
  143. package/runtime/prime/bin/lib/ensure-develop.sh +183 -0
  144. package/runtime/prime/bin/lib/git-sync.sh +406 -0
  145. package/runtime/prime/bin/lib/greeting.sh +519 -0
  146. package/runtime/prime/bin/lib/hash.sh +13 -0
  147. package/runtime/prime/bin/lib/identity.sh +213 -0
  148. package/runtime/prime/bin/lib/metrics.sh +374 -0
  149. package/runtime/prime/bin/lib/noncode.sh +31 -0
  150. package/runtime/prime/bin/lib/permissions.sh +106 -0
  151. package/runtime/prime/bin/lib/time.sh +56 -0
  152. package/runtime/prime/bin/lib/worktree-links.sh +79 -0
  153. package/runtime/prime/bin/notify.sh +636 -0
  154. package/runtime/prime/bin/person.py +630 -0
  155. package/runtime/prime/bin/person.sh +448 -0
  156. package/runtime/prime/bin/pi-branch-policy.mjs +312 -0
  157. package/runtime/prime/bin/pi-product-workflows.mjs +394 -0
  158. package/runtime/prime/bin/prime-render-spec.mjs +123 -0
  159. package/runtime/prime/bin/prime-session-start.sh +24 -0
  160. package/runtime/prime/bin/publish-artifact.sh +271 -0
  161. package/runtime/prime/bin/publish-references.sh +144 -0
  162. package/runtime/prime/bin/repo-state.sh +96 -0
  163. package/runtime/prime/bin/search.sh +240 -0
  164. package/runtime/prime/bin/session-autosave.sh +209 -0
  165. package/runtime/prime/bin/session-log.sh +199 -0
  166. package/runtime/prime/bin/settings.sh +304 -0
  167. package/runtime/prime/bin/telemetry.sh +391 -0
  168. package/runtime/prime/bin/worktree-create.sh +128 -0
  169. package/runtime/prime/bin/worktree-remove.sh +50 -0
  170. package/runtime/prime/bin/worktree.sh +198 -0
  171. package/runtime/prime/manifest.json +178 -0
@@ -0,0 +1,232 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
5
+ CLAUDE_DIR="$SCRIPT_DIR/.claude/skills"
6
+ CODEX_DIR="$SCRIPT_DIR/.codex/skills"
7
+ CHECK=0
8
+
9
+ NATIVE_SKILLS=(
10
+ activity
11
+ handoff
12
+ wrap
13
+ announce
14
+ harvest
15
+ the-spiral
16
+ dashboard
17
+ deep-reflect
18
+ quest
19
+ invite
20
+ ask
21
+ save
22
+ search
23
+ view
24
+ scroll
25
+ plate
26
+ )
27
+
28
+ STRUCTURED_UX_SKILLS=(
29
+ activity
30
+ dashboard
31
+ handoff
32
+ wrap
33
+ todo
34
+ quest
35
+ project
36
+ issue
37
+ test
38
+ qa
39
+ checkup
40
+ infra
41
+ graph-maintain
42
+ telemetry-admin
43
+ waitlist
44
+ hosting
45
+ review-pr
46
+ triage
47
+ eval
48
+ eval-multiagent
49
+ summon
50
+ reflect
51
+ deep-reflect
52
+ archive
53
+ meeting
54
+ ingest-user-interview
55
+ harvest
56
+ tutorial
57
+ onboarding
58
+ emissary
59
+ launch-site
60
+ view
61
+ scroll
62
+ visual-explain
63
+ tui-design
64
+ )
65
+
66
+ GENERATED_MARKER='generated-by: bin/codex-sync-skills.sh'
67
+
68
+ while [ $# -gt 0 ]; do
69
+ case "$1" in
70
+ --check) CHECK=1; shift ;;
71
+ --help|-h)
72
+ echo "usage: bin/codex-sync-skills.sh [--check]"
73
+ exit 0
74
+ ;;
75
+ *) echo "unknown option: $1" >&2; exit 2 ;;
76
+ esac
77
+ done
78
+
79
+ is_native() {
80
+ local name="$1"
81
+ local native
82
+ for native in "${NATIVE_SKILLS[@]}"; do
83
+ [ "$name" = "$native" ] && return 0
84
+ done
85
+ return 1
86
+ }
87
+
88
+ is_structured_ux() {
89
+ local name="$1"
90
+ local structured
91
+ for structured in "${STRUCTURED_UX_SKILLS[@]}"; do
92
+ [ "$name" = "$structured" ] && return 0
93
+ done
94
+ return 1
95
+ }
96
+
97
+ skill_description() {
98
+ local file="$1"
99
+ awk '
100
+ NR == 1 && $0 == "---" { in_fm = 1; next }
101
+ in_fm && $0 == "---" { exit }
102
+ in_fm && /^description:/ {
103
+ sub(/^description:[[:space:]]*/, "")
104
+ gsub(/^"|"$/, "")
105
+ print
106
+ exit
107
+ }
108
+ ' "$file"
109
+ }
110
+
111
+ yaml_single_quote() {
112
+ local value="$1"
113
+ printf "'%s'" "$(printf '%s' "$value" | sed "s/'/''/g")"
114
+ }
115
+
116
+ render_adapter() {
117
+ local name="$1"
118
+ local source="$2"
119
+ local description="$3"
120
+ local structured="$4"
121
+ [ -n "$description" ] || description="Fallback Codex adapter for the Egregore ${name} workflow."
122
+ local description_yaml
123
+ description_yaml="$(yaml_single_quote "$description")"
124
+ cat <<EOF
125
+ ---
126
+ name: ${name}
127
+ description: ${description_yaml}
128
+ ---
129
+
130
+ <!-- ${GENERATED_MARKER} -->
131
+
132
+ # Egregore ${name} Adapter
133
+
134
+ This is fallback coverage for a long-tail Egregore workflow that has not been
135
+ ported to a hand-written Codex-native skill yet.
136
+
137
+ Use the project shell and filesystem directly. Do not invoke Claude Code
138
+ commands. Translate interactive choices to structured Codex question tooling
139
+ when it is available; otherwise render compact numbered choices with an
140
+ \`Other:\` option and wait for the user.
141
+
142
+ EOF
143
+ if [ "$structured" = "1" ]; then
144
+ cat <<EOF
145
+ ## Structured UX parity
146
+
147
+ This workflow has a Claude skill with user-visible structured output. After
148
+ reading \`${source}\`, reproduce the same visible UX in Codex:
149
+
150
+ - Preserve TUI boxes, markdown tables, rich cards, browser artifact rendering,
151
+ exact confirmation blocks, and "no preamble" rules from the source skill.
152
+ - Use the source skill's frame width, section order, labels, status footer,
153
+ and examples as the contract for the final response.
154
+ - Never replace a required box/table/card/artifact view with a prose summary
155
+ unless the user explicitly asks for a summary.
156
+ - When the source says to output a TUI box directly, paste that box as the
157
+ visible response, preferably in a \`text\` fenced block.
158
+ - Never show raw JSON, raw command output, or unformatted script output when
159
+ the source skill requires formatted status or rendered output.
160
+
161
+ EOF
162
+ fi
163
+ cat <<EOF
164
+ 1. Read \`${source}\` for the workflow details.
165
+ 2. Run the referenced \`bin/\` scripts directly from Codex.
166
+ 3. Treat graph and publish steps as best-effort unless that workflow explicitly
167
+ says they are required.
168
+ 4. For every external notification, follow
169
+ \`.claude/context/notification-consent.md\`: plan without sending, then show
170
+ a separate exact Send / Edit / Cancel checkpoint. Never infer notification
171
+ consent from the workflow request or a batch approval.
172
+ 5. Keep local-mode behavior filesystem-first and avoid graph or notification
173
+ calls when \`egregore.json\` declares \`"mode": "local"\`.
174
+ 6. Never call the deprecated \`egregore-handoff\` CLI for Egregore project
175
+ handoffs.
176
+ EOF
177
+ }
178
+
179
+ [ -d "$CLAUDE_DIR" ] || { echo "missing Claude skills directory: $CLAUDE_DIR" >&2; exit 1; }
180
+ mkdir -p "$CODEX_DIR"
181
+
182
+ changed=0
183
+ native_present=0
184
+ adapter_present=0
185
+
186
+ for native in "${NATIVE_SKILLS[@]}"; do
187
+ if [ -f "$CODEX_DIR/$native/SKILL.md" ]; then
188
+ native_present=$((native_present + 1))
189
+ else
190
+ echo "missing native Codex skill: $native" >&2
191
+ changed=1
192
+ fi
193
+ done
194
+
195
+ while IFS= read -r source_file; do
196
+ name="$(basename "$(dirname "$source_file")")"
197
+ is_native "$name" && continue
198
+
199
+ target_dir="$CODEX_DIR/$name"
200
+ target_file="$target_dir/SKILL.md"
201
+ if [ -f "$target_file" ] && ! grep -q "$GENERATED_MARKER" "$target_file" 2>/dev/null; then
202
+ continue
203
+ fi
204
+
205
+ description="$(skill_description "$source_file")"
206
+ rel_source=".claude/skills/$name/SKILL.md"
207
+ structured=0
208
+ is_structured_ux "$name" && structured=1
209
+ tmp="$(mktemp -t codex-skill-adapter-XXXXXX)"
210
+ render_adapter "$name" "$rel_source" "$description" "$structured" > "$tmp"
211
+
212
+ if [ ! -f "$target_file" ] || ! cmp -s "$tmp" "$target_file"; then
213
+ changed=1
214
+ if [ "$CHECK" = "1" ]; then
215
+ echo "adapter out of date: $name"
216
+ else
217
+ mkdir -p "$target_dir"
218
+ cp "$tmp" "$target_file"
219
+ echo "generated adapter: $name"
220
+ fi
221
+ fi
222
+ rm -f "$tmp"
223
+ done < <(find "$CLAUDE_DIR" -mindepth 2 -maxdepth 2 -name SKILL.md | sort)
224
+
225
+ adapter_present="$({ grep -rl "$GENERATED_MARKER" "$CODEX_DIR"/*/SKILL.md 2>/dev/null || true; } | wc -l | tr -d ' ')"
226
+
227
+ if [ "$CHECK" = "1" ] && [ "$changed" -ne 0 ]; then
228
+ echo "codex skills out of sync (native: $native_present/${#NATIVE_SKILLS[@]}, adapters: $adapter_present)" >&2
229
+ exit 1
230
+ fi
231
+
232
+ echo "codex skills synced (native: $native_present/${#NATIVE_SKILLS[@]}, adapters: $adapter_present)"
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env bash
2
+ # Refresh the Connect runtime overlay (skills served by the org's API) for
3
+ # this instance. Fired fire-and-forget from session start on every runtime;
4
+ # local-mode instances exit immediately and nothing here may block or break
5
+ # a session. New Connect features appear without re-running the launcher.
6
+ set -u
7
+
8
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
+ ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
10
+ cd "$ROOT" || exit 0
11
+
12
+ # Connected instances only — resolved the same way the runtime does.
13
+ MODE=$(jq -r '.mode // empty' egregore.json 2>/dev/null)
14
+ API_URL=$(jq -r '.api_url // empty' egregore.json 2>/dev/null)
15
+ if [ "$MODE" != "connected" ] && [ -z "$API_URL" ]; then
16
+ exit 0
17
+ fi
18
+
19
+ LOG_DIR="$ROOT/.egregore"
20
+ mkdir -p "$LOG_DIR" 2>/dev/null || true
21
+ LOG="$LOG_DIR/connect-refresh.log"
22
+
23
+ # The refresh logic lives in the installed create-egregore tooling (client
24
+ # instances do not carry packages/ in their tree). --prefer-offline keeps the
25
+ # common case fast; the first run may fetch the package once.
26
+ {
27
+ echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] connect refresh"
28
+ npx --yes --prefer-offline create-egregore --refresh-connect 2>&1
29
+ } >>"$LOG" 2>&1 || true
30
+
31
+ # Keep the log bounded.
32
+ tail -n 200 "$LOG" >"$LOG.tmp" 2>/dev/null && mv "$LOG.tmp" "$LOG" 2>/dev/null || true
33
+ exit 0
@@ -0,0 +1,178 @@
1
+ #!/usr/bin/env bash
2
+ # Fetches personal dashboard data: single API call + parallel git ops.
3
+ # Usage: bash bin/dashboard-data.sh [username] [time_range]
4
+ # Returns a single JSON object with all query results.
5
+ #
6
+ # time_range: P1D (today), P7D (week, default), P30D (month), P365D (all)
7
+ set -euo pipefail
8
+
9
+ SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
10
+ TMPDIR=$(mktemp -d)
11
+ trap "rm -rf $TMPDIR" EXIT
12
+
13
+ # --- Read config ---
14
+ API_URL=$(jq -r '.api_url // empty' "$SCRIPT_DIR/egregore.json" 2>/dev/null)
15
+ API_KEY=$(grep '^EGREGORE_API_KEY=' "$SCRIPT_DIR/.env" 2>/dev/null | cut -d'=' -f2- || true)
16
+ ORG=$(jq -r '.org_name // "Egregore"' "$SCRIPT_DIR/egregore.json" 2>/dev/null || echo "Egregore")
17
+ DATE=$(date '+%b %d')
18
+
19
+ # --- Detect user ---
20
+ GH_USER="${1:-}"
21
+ if [ -z "$GH_USER" ]; then
22
+ STATE_FILE="$SCRIPT_DIR/.egregore-state.json"
23
+ if [ -f "$STATE_FILE" ]; then
24
+ GH_USER=$(jq -r '.github_username // empty' "$STATE_FILE" 2>/dev/null)
25
+ fi
26
+ if [ -z "$GH_USER" ]; then
27
+ GH_USER=$(git -C "$SCRIPT_DIR" config user.name 2>/dev/null || echo "unknown")
28
+ fi
29
+ fi
30
+
31
+ # --- Time range (default P7D) ---
32
+ TIME_RANGE="${2:-P7D}"
33
+
34
+ # --- Read current session ID (client-side fallback for WAL-not-yet-drained) ---
35
+ PROJ_HASH=$(echo -n "$SCRIPT_DIR" | md5 2>/dev/null || echo -n "$SCRIPT_DIR" | md5sum 2>/dev/null | cut -d' ' -f1)
36
+ SESSION_ID=$(cat "$HOME/.egregore/session-${PROJ_HASH}.id" 2>/dev/null || echo "")
37
+
38
+ # --- Fire API call + git ops in parallel ---
39
+
40
+ # Graph data: single API call (all queries run server-side)
41
+ (
42
+ GRAPH_STATUS="offline"
43
+ GRAPH_REASON=""
44
+ if [ -z "$API_URL" ] || [ -z "$API_KEY" ]; then
45
+ GRAPH_REASON="missing_config"
46
+ else
47
+ # Keep curl's transport status separate from %{http_code}; otherwise a
48
+ # failed request can concatenate 000 + fallback text and look like an
49
+ # unexpected API response.
50
+ CURL_STATUS=0
51
+ HTTP_CODE=$(curl -s -o "$TMPDIR/dashboard_raw.json" -w "%{http_code}" \
52
+ "${API_URL}/api/personal/dashboard?github_username=$(printf '%s' "$GH_USER" | jq -sRr @uri)&time_range=$(printf '%s' "$TIME_RANGE" | jq -sRr @uri)&session_id=$(printf '%s' "$SESSION_ID" | jq -sRr @uri)" \
53
+ -H "Authorization: Bearer $API_KEY" \
54
+ --connect-timeout 5 --max-time 15 2>/dev/null) || CURL_STATUS=$?
55
+
56
+ if [ "$CURL_STATUS" -ne 0 ] || [ "$HTTP_CODE" = "000" ] || ! [[ "$HTTP_CODE" =~ ^[0-9][0-9][0-9]$ ]]; then
57
+ GRAPH_REASON="unreachable"
58
+ elif [ "$HTTP_CODE" = "401" ] || [ "$HTTP_CODE" = "403" ]; then
59
+ GRAPH_REASON="auth_error"
60
+ elif [ "$HTTP_CODE" -ge 500 ] 2>/dev/null; then
61
+ GRAPH_REASON="server_error"
62
+ elif [ -s "$TMPDIR/dashboard_raw.json" ] && jq -e '.me' "$TMPDIR/dashboard_raw.json" >/dev/null 2>&1; then
63
+ cp "$TMPDIR/dashboard_raw.json" "$TMPDIR/dashboard.json"
64
+ GRAPH_STATUS="connected"
65
+ else
66
+ GRAPH_REASON="invalid_response"
67
+ fi
68
+ fi
69
+
70
+ # If not connected, write status-only response
71
+ if [ "$GRAPH_STATUS" != "connected" ]; then
72
+ jq -n --arg status "$GRAPH_STATUS" --arg reason "$GRAPH_REASON" \
73
+ '{graph_status: $status, graph_reason: $reason}' > "$TMPDIR/dashboard.json"
74
+ else
75
+ jq --arg status "$GRAPH_STATUS" '. + {graph_status: $status}' \
76
+ "$TMPDIR/dashboard.json" > "$TMPDIR/dashboard_merged.json" \
77
+ && mv "$TMPDIR/dashboard_merged.json" "$TMPDIR/dashboard.json"
78
+ fi
79
+ ) &
80
+ API_PID=$!
81
+
82
+ # Git: branch + dirty status (parallel, stays client-side)
83
+ (
84
+ CURRENT=$(git -C "$SCRIPT_DIR" branch --show-current 2>/dev/null || echo "unknown")
85
+ DIRTY="false"
86
+ if [ -n "$(git -C "$SCRIPT_DIR" status --porcelain 2>/dev/null | head -1)" ]; then
87
+ DIRTY="true"
88
+ fi
89
+ jq -n --arg branch "$CURRENT" --argjson dirty "$DIRTY" \
90
+ '{branch: $branch, dirty: $dirty}' > "$TMPDIR/git.json"
91
+ ) &
92
+ GIT_PID=$!
93
+
94
+ # Local sessions: parse memory files when graph is offline (parallel)
95
+ (
96
+ GH_LC=$(echo "$GH_USER" | tr '[:upper:]' '[:lower:]')
97
+ DISPLAY_LC=""
98
+ if [ -f "$SCRIPT_DIR/.egregore-state.json" ]; then
99
+ DISPLAY_LC=$(jq -r '.display_name // empty' "$SCRIPT_DIR/.egregore-state.json" 2>/dev/null | tr '[:upper:]' '[:lower:]')
100
+ fi
101
+
102
+ # Compute cutoff date from time range
103
+ case "$TIME_RANGE" in
104
+ P1D) CUTOFF=$(date -v-1d +%Y-%m-%d 2>/dev/null || date -d '1 day ago' +%Y-%m-%d 2>/dev/null || echo "2000-01-01") ;;
105
+ P7D) CUTOFF=$(date -v-7d +%Y-%m-%d 2>/dev/null || date -d '7 days ago' +%Y-%m-%d 2>/dev/null || echo "2000-01-01") ;;
106
+ P30D) CUTOFF=$(date -v-30d +%Y-%m-%d 2>/dev/null || date -d '30 days ago' +%Y-%m-%d 2>/dev/null || echo "2000-01-01") ;;
107
+ P365D) CUTOFF="2000-01-01" ;;
108
+ *) CUTOFF=$(date -v-7d +%Y-%m-%d 2>/dev/null || date -d '7 days ago' +%Y-%m-%d 2>/dev/null || echo "2000-01-01") ;;
109
+ esac
110
+
111
+ # Scan sessions, wraps, and handoffs for this user
112
+ for DIR in "$SCRIPT_DIR/memory/sessions" "$SCRIPT_DIR/memory/wraps" "$SCRIPT_DIR/memory/handoffs"; do
113
+ [ -d "$DIR" ] || continue
114
+ TYPE=$(basename "$DIR")
115
+ find -L "$DIR" -name '*.md' -not -name 'index*' -type f 2>/dev/null | xargs ls -t 2>/dev/null | head -20 | while read -r MFILE; do
116
+ [ -z "$MFILE" ] && continue
117
+ M_AUTH=$(head -10 "$MFILE" 2>/dev/null | grep -m1 '^\*\*Author\*\*:' | sed 's/^\*\*Author\*\*:[[:space:]]*//' || true)
118
+ [ -z "$M_AUTH" ] && continue
119
+ M_AUTH_LC=$(echo "$M_AUTH" | tr '[:upper:]' '[:lower:]')
120
+ # Filter to this user only
121
+ [ "$M_AUTH_LC" != "$GH_LC" ] && { [ -z "$DISPLAY_LC" ] || [ "$M_AUTH_LC" != "$DISPLAY_LC" ]; } && continue
122
+ M_DATE=$(head -10 "$MFILE" 2>/dev/null | grep -m1 '^\*\*Date\*\*:' | sed 's/^\*\*Date\*\*:[[:space:]]*//' || true)
123
+ M_DAY="${M_DATE%%T*}"
124
+ [ -n "$M_DAY" ] && [ "$M_DAY" \< "$CUTOFF" ] && continue
125
+ M_BRANCH=$(head -10 "$MFILE" 2>/dev/null | grep -m1 '^\*\*Branch\*\*:' | sed 's/^\*\*Branch\*\*:[[:space:]]*//' || true)
126
+ M_DURATION=$(head -10 "$MFILE" 2>/dev/null | grep -m1 '^\*\*Duration\*\*:' | sed 's/^\*\*Duration\*\*:[[:space:]]*//' || true)
127
+ M_TOPIC=$(head -3 "$MFILE" 2>/dev/null | grep -m1 '^# ' | sed 's/^# [^:]*:[[:space:]]*//' || true)
128
+ echo "${M_DATE}|${M_TOPIC}|${M_BRANCH}|${M_DURATION}|${TYPE}"
129
+ done
130
+ done | jq -Rn '
131
+ [inputs | split("|") |
132
+ {date: .[0], topic: .[1], branch: .[2], duration: .[3], type: .[4]}
133
+ ] | sort_by(.date) | reverse | {
134
+ sessions: .,
135
+ session_count: length
136
+ }
137
+ ' 2>/dev/null > "$TMPDIR/local_sessions.json" || echo '{"sessions":[],"session_count":0}' > "$TMPDIR/local_sessions.json"
138
+ ) &
139
+ LOCAL_PID=$!
140
+
141
+ # --- Wait for all ---
142
+ wait $API_PID $GIT_PID $LOCAL_PID 2>/dev/null || true
143
+
144
+ # --- Read results ---
145
+ GIT_INFO=$(cat "$TMPDIR/git.json" 2>/dev/null || echo '{"branch":"unknown","dirty":false}')
146
+ BRANCH=$(echo "$GIT_INFO" | jq -r '.branch' 2>/dev/null || echo "unknown")
147
+ LOCAL_SESSIONS=$(cat "$TMPDIR/local_sessions.json" 2>/dev/null || echo '{"sessions":[],"session_count":0}')
148
+ echo "$LOCAL_SESSIONS" | jq . >/dev/null 2>&1 || LOCAL_SESSIONS='{"sessions":[],"session_count":0}'
149
+
150
+ # --- Client-side current session fallback ---
151
+ # If the graph returned no current_session (WAL hasn't drained yet),
152
+ # inject one from the local session ID file so the TUI always renders it.
153
+ DASHBOARD=$(cat "$TMPDIR/dashboard.json" 2>/dev/null || echo '{"graph_status":"offline"}')
154
+ CS_ID=$(echo "$DASHBOARD" | jq -r '.current_session.id // empty' 2>/dev/null)
155
+ if [ -z "$CS_ID" ] && [ -n "$SESSION_ID" ]; then
156
+ DASHBOARD=$(echo "$DASHBOARD" | jq \
157
+ --arg sid "$SESSION_ID" \
158
+ --arg branch "$BRANCH" \
159
+ '.current_session = {id: $sid, status: "active", topic: null, branch: $branch}' 2>/dev/null || echo "$DASHBOARD")
160
+ fi
161
+
162
+ # --- Derive time range label ---
163
+ case "$TIME_RANGE" in
164
+ P1D) RANGE_LABEL="today" ;;
165
+ P7D) RANGE_LABEL="last 7 days" ;;
166
+ P30D) RANGE_LABEL="last 30 days" ;;
167
+ P365D) RANGE_LABEL="all time" ;;
168
+ *) RANGE_LABEL="last 7 days" ;;
169
+ esac
170
+
171
+ # --- Merge server response + local data ---
172
+ echo "$DASHBOARD" | jq \
173
+ --arg org "$ORG" \
174
+ --arg date "$DATE" \
175
+ --arg range_label "$RANGE_LABEL" \
176
+ --argjson git "$GIT_INFO" \
177
+ --argjson local_sessions "$LOCAL_SESSIONS" \
178
+ '. + {org: $org, date: $date, range_label: $range_label, git: $git, local_sessions: $local_sessions}'
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # Execute multiple Cypher queries in a single HTTP call.
5
+ # Usage: graph-batch.sh '<json array of {statement, parameters} objects>'
6
+ #
7
+ # Example:
8
+ # graph-batch.sh '[{"statement":"MATCH (p:Person) RETURN p.name"},{"statement":"MATCH (q:Quest) RETURN q.id"}]'
9
+ #
10
+ # Returns: {"results": [{...}, {...}]}
11
+
12
+ SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
13
+ CONFIG="$SCRIPT_DIR/egregore.json"
14
+
15
+ if [ ! -f "$CONFIG" ]; then
16
+ echo "Error: egregore.json not found. Run onboarding first." >&2
17
+ exit 1
18
+ fi
19
+
20
+ # --- Local mode gate: bail immediately ---
21
+ _MODE=$(jq -r '.mode // "connected"' "$CONFIG" 2>/dev/null)
22
+ if [ "$_MODE" = "local" ]; then
23
+ echo '{"results":[]}'
24
+ exit 0
25
+ fi
26
+
27
+ # Load specific variables from .env if it exists (safe extraction, no arbitrary code execution).
28
+ # A worktree missing its .env symlink resolves through worktree-links.sh, which
29
+ # self-heals the link and falls back to the main checkout's .env.
30
+ _ENV_FILE="$SCRIPT_DIR/.env"
31
+ if [ ! -f "$_ENV_FILE" ] && [ -f "$SCRIPT_DIR/bin/lib/worktree-links.sh" ]; then
32
+ # shellcheck source=bin/lib/worktree-links.sh
33
+ source "$SCRIPT_DIR/bin/lib/worktree-links.sh" 2>/dev/null || true
34
+ _ENV_FILE="$(egregore_env_file "$SCRIPT_DIR" 2>/dev/null || true)"
35
+ fi
36
+ if [ -n "$_ENV_FILE" ] && [ -f "$_ENV_FILE" ]; then
37
+ EGREGORE_API_URL="${EGREGORE_API_URL:-$(grep '^EGREGORE_API_URL=' "$_ENV_FILE" 2>/dev/null | cut -d'=' -f2- || true)}"
38
+ EGREGORE_API_KEY="${EGREGORE_API_KEY:-$(grep '^EGREGORE_API_KEY=' "$_ENV_FILE" 2>/dev/null | cut -d'=' -f2- || true)}"
39
+ fi
40
+
41
+ API_URL="${EGREGORE_API_URL:-$(jq -r '.api_url // empty' "$CONFIG")}"
42
+ API_KEY="${EGREGORE_API_KEY:-}"
43
+
44
+ if [ -z "$API_URL" ]; then
45
+ # === OFFLINE MODE: no api_url configured (OSS/unconfigured) ===
46
+ echo '{"results":[]}'
47
+ exit 0
48
+ fi
49
+
50
+ if [ -z "$API_KEY" ]; then
51
+ # api_url is configured but no key was found — the local-mode gate exited
52
+ # above, so the org expects a live graph. Faking empty results here hides a
53
+ # missing .env (e.g. a worktree without its symlink) from every caller.
54
+ {
55
+ echo "Error: api_url is configured but EGREGORE_API_KEY is empty (looked in: ${_ENV_FILE:-$SCRIPT_DIR/.env})."
56
+ echo " If this is a worktree, its .env symlink may be missing. Run /env to configure."
57
+ } >&2
58
+ exit 1
59
+ fi
60
+
61
+ QUERIES_JSON="${1:?Usage: graph-batch.sh '<json array of queries>'}"
62
+
63
+ # Wrap the array into the batch request format
64
+ BODY=$(jq -n --argjson queries "$QUERIES_JSON" '{queries: $queries}')
65
+
66
+ RESPONSE=$(curl -s -X POST "${API_URL}/api/graph/batch" \
67
+ -H "Authorization: Bearer $API_KEY" \
68
+ -H "Content-Type: application/json" \
69
+ -d "$BODY" \
70
+ --max-time 60)
71
+
72
+ # Check for HTTP-level errors
73
+ if echo "$RESPONSE" | jq -e '.detail' >/dev/null 2>&1; then
74
+ echo "API error:" >&2
75
+ echo "$RESPONSE" | jq '.detail' >&2
76
+ exit 1
77
+ fi
78
+
79
+ echo "$RESPONSE"