create-egregore 0.19.3 → 0.19.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/egregore-launcher.js +410 -26
- package/bin/cli.js +34 -20
- package/lib/local.js +84 -11
- package/lib/pi-runtime.js +186 -0
- package/lib/setup.js +56 -11
- package/lib/ui.js +1 -1
- package/package.json +8 -6
- package/runtime/codex/.codex/hooks/search-hint.js +65 -0
- package/runtime/codex/.codex/hooks.json +13 -0
- package/runtime/codex/.codex/skills/deep-reflect/SKILL.md +14 -10
- package/runtime/codex/.codex/skills/minion/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/scroll/SKILL.md +168 -0
- package/runtime/codex/.codex/skills/scroll/agents/openai.yaml +4 -0
- package/runtime/codex/.codex/skills/scroll/assets/shell.html +542 -0
- package/runtime/codex/.codex/skills/search/SKILL.md +56 -0
- package/runtime/codex/.codex/skills/session-view/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/thread/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/view/SKILL.md +147 -32
- package/runtime/codex/.codex/skills/view/agents/openai.yaml +4 -0
- package/runtime/codex/.codex/skills/view/assets/compose-scaffold.html +221 -0
- package/runtime/codex/.codex/spec-manifest.json +4 -4
- package/runtime/codex/AGENTS.md +31 -5
- package/runtime/codex/bin/activity-data.sh +46 -4
- package/runtime/codex/bin/agent.sh +10 -2
- package/runtime/codex/bin/codex-install-skills.sh +1 -1
- package/runtime/codex/bin/codex-render-spec.mjs +24 -2
- package/runtime/codex/bin/codex-session-start.sh +6 -60
- package/runtime/codex/bin/codex-skill-render.mjs +20 -12
- package/runtime/codex/bin/codex-sync-skills.sh +4 -0
- package/runtime/codex/bin/graph-op.sh +56 -5
- package/runtime/codex/bin/graph.sh +36 -0
- package/runtime/codex/bin/handoff-run.sh +157 -7
- package/runtime/codex/bin/handoff-save-egregore.sh +6 -1
- package/runtime/codex/bin/index-handoff.sh +9 -3
- package/runtime/codex/bin/lib/context.sh +97 -18
- package/runtime/codex/bin/lib/dashboard-artifact.sh +1 -1
- package/runtime/codex/bin/lib/git-sync.sh +35 -9
- package/runtime/codex/bin/lib/greeting.sh +30 -104
- package/runtime/codex/bin/lib/metrics.sh +374 -0
- package/runtime/codex/bin/publish-artifact.sh +43 -1
- package/runtime/codex/bin/settings.sh +274 -0
- package/runtime/codex/manifest.json +18 -1
- package/runtime/pi/.codex/config.toml +15 -0
- package/runtime/pi/.codex/hooks/branch-guard.js +242 -0
- package/runtime/pi/.codex/hooks/search-hint.js +65 -0
- package/runtime/pi/.codex/hooks.json +30 -0
- package/runtime/pi/.codex/skills/activity/SKILL.md +70 -0
- package/runtime/pi/.codex/skills/add/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/announce/SKILL.md +54 -0
- package/runtime/pi/.codex/skills/answer/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/archive/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/ask/SKILL.md +59 -0
- package/runtime/pi/.codex/skills/branch/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/checkup/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/commit/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/contribute/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/dark-mode/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/dashboard/SKILL.md +59 -0
- package/runtime/pi/.codex/skills/deep-reflect/SKILL.md +74 -0
- package/runtime/pi/.codex/skills/delete-user/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/egregore-voice/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/emissary/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/env/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/handoff/SKILL.md +67 -0
- package/runtime/pi/.codex/skills/harvest/SKILL.md +107 -0
- package/runtime/pi/.codex/skills/ingest/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/ingest-google/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/ingest-user-interview/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/invite/SKILL.md +40 -0
- package/runtime/pi/.codex/skills/issue/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/me/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/meeting/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/minion/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/note/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/onboarding/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/pr/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/project/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/pull/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/push/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/quest/SKILL.md +77 -0
- package/runtime/pi/.codex/skills/quest-suggest/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/reflect/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/review-pr/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/save/SKILL.md +62 -0
- package/runtime/pi/.codex/skills/scroll/SKILL.md +168 -0
- package/runtime/pi/.codex/skills/scroll/agents/openai.yaml +4 -0
- package/runtime/pi/.codex/skills/scroll/assets/shell.html +542 -0
- package/runtime/pi/.codex/skills/search/SKILL.md +56 -0
- package/runtime/pi/.codex/skills/session-view/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/setup/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/summon/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/sync-repos/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/telegram-connect/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/telemetry/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/test/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/the-spiral/SKILL.md +84 -0
- package/runtime/pi/.codex/skills/thread/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/todo/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/tui-design/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/tutorial/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/update/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/view/SKILL.md +156 -0
- package/runtime/pi/.codex/skills/view/agents/openai.yaml +4 -0
- package/runtime/pi/.codex/skills/view/assets/compose-scaffold.html +221 -0
- package/runtime/pi/.codex/skills/visual-explain/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/wrap/SKILL.md +55 -0
- package/runtime/pi/.codex/spec-manifest.json +114 -0
- package/runtime/pi/.pi/APPEND_SYSTEM.md +255 -0
- package/runtime/pi/.pi/extensions/egregore.ts +324 -0
- package/runtime/pi/.pi/settings.json +6 -0
- package/runtime/pi/.pi/spec-manifest.json +12 -0
- package/runtime/pi/AGENTS.md +350 -0
- package/runtime/pi/bin/activity-data.sh +242 -0
- package/runtime/pi/bin/agent.sh +677 -0
- package/runtime/pi/bin/codex-install-skills.sh +78 -0
- package/runtime/pi/bin/codex-render-spec.mjs +394 -0
- package/runtime/pi/bin/codex-session-start.sh +188 -0
- package/runtime/pi/bin/codex-skill-render.mjs +323 -0
- package/runtime/pi/bin/codex-sync-skills.sh +227 -0
- package/runtime/pi/bin/dashboard-data.sh +178 -0
- package/runtime/pi/bin/graph-batch.sh +60 -0
- package/runtime/pi/bin/graph-op.sh +431 -0
- package/runtime/pi/bin/graph-wal.sh +228 -0
- package/runtime/pi/bin/graph.sh +177 -0
- package/runtime/pi/bin/handoff-pr-backfill.sh +86 -0
- package/runtime/pi/bin/handoff-run.sh +581 -0
- package/runtime/pi/bin/handoff-save-egregore.sh +136 -0
- package/runtime/pi/bin/index-handoff.sh +312 -0
- package/runtime/pi/bin/lib/artifact-id.sh +46 -0
- package/runtime/pi/bin/lib/config.sh +71 -0
- package/runtime/pi/bin/lib/context.sh +542 -0
- package/runtime/pi/bin/lib/dashboard-artifact.sh +142 -0
- package/runtime/pi/bin/lib/ensure-develop.sh +183 -0
- package/runtime/pi/bin/lib/git-sync.sh +288 -0
- package/runtime/pi/bin/lib/greeting.sh +340 -0
- package/runtime/pi/bin/lib/hash.sh +13 -0
- package/runtime/pi/bin/lib/identity.sh +174 -0
- package/runtime/pi/bin/lib/metrics.sh +374 -0
- package/runtime/pi/bin/lib/permissions.sh +106 -0
- package/runtime/pi/bin/lib/time.sh +56 -0
- package/runtime/pi/bin/lib/worktree-links.sh +54 -0
- package/runtime/pi/bin/notify.sh +207 -0
- package/runtime/pi/bin/pi-branch-policy.mjs +15 -0
- package/runtime/pi/bin/pi-product-workflows.mjs +324 -0
- package/runtime/pi/bin/pi-render-spec.mjs +114 -0
- package/runtime/pi/bin/pi-session-start.sh +24 -0
- package/runtime/pi/bin/publish-artifact.sh +201 -0
- package/runtime/pi/bin/publish-references.sh +144 -0
- package/runtime/pi/bin/repo-state.sh +99 -0
- package/runtime/pi/bin/telemetry.sh +391 -0
- package/runtime/pi/bin/worktree-create.sh +118 -0
- package/runtime/pi/bin/worktree-remove.sh +50 -0
- package/runtime/pi/bin/worktree.sh +198 -0
- package/runtime/pi/manifest.json +176 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
#!/bin/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,60 @@
|
|
|
1
|
+
#!/bin/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
|
+
if [ -f "$SCRIPT_DIR/.env" ]; then
|
|
29
|
+
EGREGORE_API_URL="${EGREGORE_API_URL:-$(grep '^EGREGORE_API_URL=' "$SCRIPT_DIR/.env" 2>/dev/null | cut -d'=' -f2- || true)}"
|
|
30
|
+
EGREGORE_API_KEY="${EGREGORE_API_KEY:-$(grep '^EGREGORE_API_KEY=' "$SCRIPT_DIR/.env" 2>/dev/null | cut -d'=' -f2- || true)}"
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
API_URL="${EGREGORE_API_URL:-$(jq -r '.api_url // empty' "$CONFIG")}"
|
|
34
|
+
API_KEY="${EGREGORE_API_KEY:-}"
|
|
35
|
+
|
|
36
|
+
if [ -z "$API_URL" ] || [ -z "$API_KEY" ]; then
|
|
37
|
+
# === OFFLINE MODE: No API key — return empty results (OSS/local) ===
|
|
38
|
+
echo '{"results":[]}'
|
|
39
|
+
exit 0
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
QUERIES_JSON="${1:?Usage: graph-batch.sh '<json array of queries>'}"
|
|
43
|
+
|
|
44
|
+
# Wrap the array into the batch request format
|
|
45
|
+
BODY=$(jq -n --argjson queries "$QUERIES_JSON" '{queries: $queries}')
|
|
46
|
+
|
|
47
|
+
RESPONSE=$(curl -s -X POST "${API_URL}/api/graph/batch" \
|
|
48
|
+
-H "Authorization: Bearer $API_KEY" \
|
|
49
|
+
-H "Content-Type: application/json" \
|
|
50
|
+
-d "$BODY" \
|
|
51
|
+
--max-time 60)
|
|
52
|
+
|
|
53
|
+
# Check for HTTP-level errors
|
|
54
|
+
if echo "$RESPONSE" | jq -e '.detail' >/dev/null 2>&1; then
|
|
55
|
+
echo "API error:" >&2
|
|
56
|
+
echo "$RESPONSE" | jq '.detail' >&2
|
|
57
|
+
exit 1
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
echo "$RESPONSE"
|
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
# Named graph operations — clean interface over raw Cypher.
|
|
4
|
+
# Keeps implementation details out of the TUI.
|
|
5
|
+
#
|
|
6
|
+
# Usage: bash bin/graph-op.sh <operation> [args...]
|
|
7
|
+
#
|
|
8
|
+
# Operations:
|
|
9
|
+
# mark-read <session-id> Mark a handoff as read
|
|
10
|
+
# mark-done <session-id> Mark a handoff as done/resolved
|
|
11
|
+
# answer-question <set-id> Mark a question set as answered
|
|
12
|
+
# resolve-handoffs <user> Auto-resolve read handoffs with later sessions
|
|
13
|
+
# set-topic <session-id> <topic> [branch]
|
|
14
|
+
# Set topic (and optionally branch) on a Session node
|
|
15
|
+
# record-focus <session-id> <shown-json> <selected> [dismissed-json]
|
|
16
|
+
# Track Focus option selection for adaptive options
|
|
17
|
+
# merge-person <keep-name> <remove-name>
|
|
18
|
+
# Merge two Person nodes — transfers relationships from
|
|
19
|
+
# remove-name to keep-name, stores remove-name as alias
|
|
20
|
+
# create-harvest <id> <topic> <intent> <initiator>
|
|
21
|
+
# Create a Harvest node and link to initiator (WAL-backed)
|
|
22
|
+
# create-harvest-session <harvest-id> <session-id> <person-name> [status]
|
|
23
|
+
# Create a HarvestSession linked to a Harvest and Person.
|
|
24
|
+
# status defaults to 'active' (back-compat). Valid values:
|
|
25
|
+
# pending | active | answered | complete | incorporated.
|
|
26
|
+
# Async respondents should be created with 'pending'.
|
|
27
|
+
# record-harvest-turn <session-id> <turn> <question> <intent> [answer] [eval]
|
|
28
|
+
# Record a question-answer turn in a HarvestSession
|
|
29
|
+
# complete-harvest <harvest-id> <artifact-path>
|
|
30
|
+
# Mark harvest complete and link synthesis artifact
|
|
31
|
+
|
|
32
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
33
|
+
|
|
34
|
+
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
|
|
35
|
+
echo "Usage: graph-op.sh <operation> [args...]"
|
|
36
|
+
echo ""
|
|
37
|
+
echo "Named graph operations — clean interface over raw Cypher."
|
|
38
|
+
echo ""
|
|
39
|
+
echo "Operations:"
|
|
40
|
+
echo " mark-read <sid> Mark handoff as read"
|
|
41
|
+
echo " mark-done <sid> Mark handoff as done"
|
|
42
|
+
echo " answer-question <qid> Mark question set as answered"
|
|
43
|
+
echo " resolve-handoffs <user> Auto-resolve read handoffs"
|
|
44
|
+
echo " set-topic <sid> <topic> Set topic on a Session node"
|
|
45
|
+
echo " merge-person <keep> <rm> Merge two Person nodes"
|
|
46
|
+
echo " claim-handoff <sid> <ho> Link implementing session to handoff"
|
|
47
|
+
echo " create-pr <sid> <num> <repo> <author> [title]"
|
|
48
|
+
echo " register-artifact <id> <url> <title> <type> <author> <topics> <excerpt>"
|
|
49
|
+
echo " create-harvest / complete-harvest / record-harvest-turn"
|
|
50
|
+
echo " wal-status Show WAL pending count"
|
|
51
|
+
exit 0
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# --- Local mode gate: bail immediately ---
|
|
55
|
+
_MODE=$(jq -r '.mode // "connected"' "$SCRIPT_DIR/egregore.json" 2>/dev/null)
|
|
56
|
+
if [ "$_MODE" = "local" ]; then
|
|
57
|
+
echo '{"results":[]}'
|
|
58
|
+
exit 0
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
GS="$SCRIPT_DIR/bin/graph.sh"
|
|
62
|
+
|
|
63
|
+
OP="${1:-}"
|
|
64
|
+
shift || true
|
|
65
|
+
|
|
66
|
+
case "$OP" in
|
|
67
|
+
|
|
68
|
+
mark-read)
|
|
69
|
+
SID="${1:?missing session-id}"
|
|
70
|
+
bash "$GS" query "
|
|
71
|
+
MATCH (s:Session {id: \$sid})
|
|
72
|
+
SET s.handoffStatus = 'read', s.handoffReadDate = date()
|
|
73
|
+
RETURN s.id AS id, s.topic AS topic
|
|
74
|
+
" "$(jq -n --arg sid "$SID" '{sid: $sid}')"
|
|
75
|
+
;;
|
|
76
|
+
|
|
77
|
+
mark-done)
|
|
78
|
+
SID="${1:?missing session-id}"
|
|
79
|
+
bash "$GS" query "
|
|
80
|
+
MATCH (s:Session {id: \$sid})
|
|
81
|
+
SET s.handoffStatus = 'done'
|
|
82
|
+
RETURN s.id AS id, s.topic AS topic
|
|
83
|
+
" "$(jq -n --arg sid "$SID" '{sid: $sid}')"
|
|
84
|
+
;;
|
|
85
|
+
|
|
86
|
+
answer-question)
|
|
87
|
+
QID="${1:?missing question-set-id}"
|
|
88
|
+
bash "$GS" query "
|
|
89
|
+
MATCH (qs:QuestionSet {id: \$qid})
|
|
90
|
+
SET qs.status = 'answered'
|
|
91
|
+
RETURN qs.id AS id, qs.topic AS topic
|
|
92
|
+
" "$(jq -n --arg qid "$QID" '{qid: $qid}')"
|
|
93
|
+
;;
|
|
94
|
+
|
|
95
|
+
resolve-handoffs)
|
|
96
|
+
USER="${1:?missing username}"
|
|
97
|
+
bash "$GS" query "
|
|
98
|
+
MATCH (s:Session)-[:HANDED_TO]->(p:Person {name: \$user})
|
|
99
|
+
WHERE s.handoffStatus = 'read'
|
|
100
|
+
WITH s, p, coalesce(s.handoffReadDate, s.date) AS sinceDate
|
|
101
|
+
MATCH (later:Session)-[:BY]->(p)
|
|
102
|
+
WHERE later.date > sinceDate
|
|
103
|
+
WITH s, count(later) AS laterSessions WHERE laterSessions > 0
|
|
104
|
+
SET s.handoffStatus = 'done'
|
|
105
|
+
RETURN s.id AS resolved
|
|
106
|
+
" "$(jq -n --arg user "$USER" '{user: $user}')"
|
|
107
|
+
;;
|
|
108
|
+
|
|
109
|
+
set-topic)
|
|
110
|
+
SID="${1:?missing session-id}"
|
|
111
|
+
TOPIC="${2:?missing topic}"
|
|
112
|
+
BRANCH="${3:-}"
|
|
113
|
+
if [ -n "$BRANCH" ]; then
|
|
114
|
+
bash "$GS" query "
|
|
115
|
+
MATCH (s:Session {id: \$sid})
|
|
116
|
+
SET s.topic = \$topic, s.branch = \$branch
|
|
117
|
+
RETURN s.id AS id, s.topic AS topic, s.branch AS branch
|
|
118
|
+
" "$(jq -n --arg sid "$SID" --arg topic "$TOPIC" --arg branch "$BRANCH" '{sid: $sid, topic: $topic, branch: $branch}')"
|
|
119
|
+
else
|
|
120
|
+
bash "$GS" query "
|
|
121
|
+
MATCH (s:Session {id: \$sid})
|
|
122
|
+
SET s.topic = \$topic
|
|
123
|
+
RETURN s.id AS id, s.topic AS topic
|
|
124
|
+
" "$(jq -n --arg sid "$SID" --arg topic "$TOPIC" '{sid: $sid, topic: $topic}')"
|
|
125
|
+
fi
|
|
126
|
+
;;
|
|
127
|
+
|
|
128
|
+
record-focus)
|
|
129
|
+
SID="${1:?missing session-id}"
|
|
130
|
+
SHOWN="${2:?missing shown options}"
|
|
131
|
+
SELECTED="${3:?missing selected option}"
|
|
132
|
+
DISMISSED="${4:-[]}"
|
|
133
|
+
bash "$GS" query "
|
|
134
|
+
MATCH (s:Session {id: \$sid})
|
|
135
|
+
SET s.focusShown = \$shown,
|
|
136
|
+
s.focusSelected = \$selected,
|
|
137
|
+
s.focusDismissed = \$dismissed
|
|
138
|
+
RETURN s.id AS id
|
|
139
|
+
" "$(jq -n --arg sid "$SID" --argjson shown "$SHOWN" --arg selected "$SELECTED" --argjson dismissed "$DISMISSED" '{sid: $sid, shown: $shown, selected: $selected, dismissed: $dismissed}')"
|
|
140
|
+
;;
|
|
141
|
+
|
|
142
|
+
merge-person)
|
|
143
|
+
KEEP="${1:?missing keep-name (the Person to keep)}"
|
|
144
|
+
REMOVE="${2:?missing remove-name (the Person to absorb)}"
|
|
145
|
+
bash "$GS" query "
|
|
146
|
+
MATCH (keep:Person) WHERE toLower(keep.name) = toLower(\$keep) OR keep.github = \$keep
|
|
147
|
+
MATCH (remove:Person) WHERE toLower(remove.name) = toLower(\$remove) OR remove.github = \$remove
|
|
148
|
+
WITH keep, remove WHERE keep <> remove
|
|
149
|
+
SET keep.previousNames = coalesce(keep.previousNames, []) + remove.name
|
|
150
|
+
WITH keep, remove
|
|
151
|
+
OPTIONAL MATCH (s1)-[:BY]->(remove)
|
|
152
|
+
FOREACH (_ IN CASE WHEN s1 IS NOT NULL THEN [1] ELSE [] END | MERGE (s1)-[:BY]->(keep))
|
|
153
|
+
WITH keep, remove
|
|
154
|
+
OPTIONAL MATCH (s2)-[:HANDED_TO]->(remove)
|
|
155
|
+
FOREACH (_ IN CASE WHEN s2 IS NOT NULL THEN [1] ELSE [] END | MERGE (s2)-[:HANDED_TO]->(keep))
|
|
156
|
+
WITH keep, remove
|
|
157
|
+
OPTIONAL MATCH (m)-[:INVOLVES]->(remove)
|
|
158
|
+
FOREACH (_ IN CASE WHEN m IS NOT NULL THEN [1] ELSE [] END | MERGE (m)-[:INVOLVES]->(keep))
|
|
159
|
+
WITH keep, remove
|
|
160
|
+
OPTIONAL MATCH (a)-[:CONTRIBUTED_BY]->(remove)
|
|
161
|
+
FOREACH (_ IN CASE WHEN a IS NOT NULL THEN [1] ELSE [] END | MERGE (a)-[:CONTRIBUTED_BY]->(keep))
|
|
162
|
+
WITH keep, remove
|
|
163
|
+
OPTIONAL MATCH (i)-[:CONDUCTED_BY]->(remove)
|
|
164
|
+
FOREACH (_ IN CASE WHEN i IS NOT NULL THEN [1] ELSE [] END | MERGE (i)-[:CONDUCTED_BY]->(keep))
|
|
165
|
+
WITH keep, remove
|
|
166
|
+
OPTIONAL MATCH (remove)-[:MEMBER_OF]->(o:Org)
|
|
167
|
+
FOREACH (_ IN CASE WHEN o IS NOT NULL THEN [1] ELSE [] END | MERGE (keep)-[:MEMBER_OF]->(o))
|
|
168
|
+
WITH keep, remove
|
|
169
|
+
DETACH DELETE remove
|
|
170
|
+
RETURN keep.name AS name, keep.github AS github, keep.previousNames AS aliases
|
|
171
|
+
" "$(jq -n --arg keep "$KEEP" --arg remove "$REMOVE" '{keep: $keep, remove: $remove}')"
|
|
172
|
+
;;
|
|
173
|
+
|
|
174
|
+
claim-handoff)
|
|
175
|
+
IMPL_SID="${1:?missing implementing-session-id}"
|
|
176
|
+
HO_SID="${2:?missing handoff-session-id}"
|
|
177
|
+
CYPHER="
|
|
178
|
+
MATCH (impl:Session {id: \$implSid}), (ho:Session {id: \$hoSid})
|
|
179
|
+
WHERE ho.handoffStatus IS NOT NULL
|
|
180
|
+
MERGE (impl)-[:IMPLEMENTS]->(ho)
|
|
181
|
+
RETURN impl.id AS implementor, ho.id AS handoff, ho.topic AS topic
|
|
182
|
+
"
|
|
183
|
+
PARAMS="$(jq -n --arg implSid "$IMPL_SID" --arg hoSid "$HO_SID" '{implSid: $implSid, hoSid: $hoSid}')"
|
|
184
|
+
bash "$SCRIPT_DIR/bin/graph-wal.sh" append "$CYPHER" "$PARAMS" 2>/dev/null || true
|
|
185
|
+
bash "$GS" query "$CYPHER" "$PARAMS"
|
|
186
|
+
;;
|
|
187
|
+
|
|
188
|
+
create-pr)
|
|
189
|
+
SID="${1:?missing session-id}"
|
|
190
|
+
PR_NUM="${2:?missing pr-number}"
|
|
191
|
+
REPO="${3:?missing repo}"
|
|
192
|
+
AUTHOR_GH="${4:?missing author-github}"
|
|
193
|
+
TITLE="${5:-}"
|
|
194
|
+
CYPHER="
|
|
195
|
+
MERGE (pr:PR {number: toInteger(\$num), repo: \$repo})
|
|
196
|
+
ON CREATE SET pr.author = \$author, pr.status = 'open',
|
|
197
|
+
pr.createdAt = datetime(), pr.title = \$title
|
|
198
|
+
WITH pr
|
|
199
|
+
OPTIONAL MATCH (s:Session {id: \$sid})
|
|
200
|
+
FOREACH (_ IN CASE WHEN s IS NOT NULL THEN [1] ELSE [] END |
|
|
201
|
+
MERGE (s)-[:PRODUCED]->(pr))
|
|
202
|
+
RETURN pr.number AS number, pr.repo AS repo
|
|
203
|
+
"
|
|
204
|
+
PARAMS="$(jq -n --arg sid "$SID" --argjson num "$PR_NUM" --arg repo "$REPO" --arg author "$AUTHOR_GH" --arg title "$TITLE" '{sid: $sid, num: $num, repo: $repo, author: $author, title: $title}')"
|
|
205
|
+
bash "$SCRIPT_DIR/bin/graph-wal.sh" append "$CYPHER" "$PARAMS" 2>/dev/null || true
|
|
206
|
+
bash "$GS" query "$CYPHER" "$PARAMS"
|
|
207
|
+
;;
|
|
208
|
+
|
|
209
|
+
register-artifact)
|
|
210
|
+
# Index a published (hosted) artifact as an Artifact node so the finder can
|
|
211
|
+
# retrieve it from the graph in connected mode — by content (title/topics/
|
|
212
|
+
# excerpt CONTAINS) and by relationship (who made it, which quest). The node
|
|
213
|
+
# id is the registry FILENAME STEM (date-author-slug), matching sync-graph.sh
|
|
214
|
+
# so a later /save sync upserts the same node instead of duplicating it.
|
|
215
|
+
# args: <id> <url> <title> <type> <author-handle> <topics-csv> <excerpt>
|
|
216
|
+
A_ID="${1:?missing id}"
|
|
217
|
+
A_URL="${2:?missing url}"
|
|
218
|
+
A_TITLE="${3:-}"
|
|
219
|
+
A_TYPE="${4:-artifact}"
|
|
220
|
+
A_AUTHOR="$(printf '%s' "${5:-}" | tr '[:upper:]' '[:lower:]' | awk '{print $1}')"
|
|
221
|
+
A_TOPICS_JSON="$(printf '%s' "${6:-}" | jq -Rc 'split(",") | map(gsub("^\\s+|\\s+$";"")) | map(select(length > 0))' 2>/dev/null || echo '[]')"
|
|
222
|
+
A_EXCERPT="$(printf '%s' "${7:-}" | tr '\n' ' ' | cut -c1-2000)"
|
|
223
|
+
CYPHER="
|
|
224
|
+
MERGE (a:Artifact {id: \$id})
|
|
225
|
+
ON CREATE SET a.created = datetime()
|
|
226
|
+
SET a.title = \$title, a.type = \$type, a.url = \$url,
|
|
227
|
+
a.topics = \$topics, a.excerpt = \$excerpt, a.published = true
|
|
228
|
+
WITH a
|
|
229
|
+
OPTIONAL MATCH (p:Person)
|
|
230
|
+
WHERE \$author <> '' AND (toLower(p.name) = \$author OR p.github = \$author)
|
|
231
|
+
FOREACH (_ IN CASE WHEN p IS NOT NULL THEN [1] ELSE [] END |
|
|
232
|
+
MERGE (a)-[:CONTRIBUTED_BY]->(p))
|
|
233
|
+
RETURN a.id AS id
|
|
234
|
+
"
|
|
235
|
+
PARAMS="$(jq -n --arg id "$A_ID" --arg url "$A_URL" --arg title "$A_TITLE" --arg type "$A_TYPE" \
|
|
236
|
+
--arg author "$A_AUTHOR" --argjson topics "$A_TOPICS_JSON" --arg excerpt "$A_EXCERPT" \
|
|
237
|
+
'{id: $id, url: $url, title: $title, type: $type, author: $author, topics: $topics, excerpt: $excerpt}')"
|
|
238
|
+
bash "$SCRIPT_DIR/bin/graph-wal.sh" append "$CYPHER" "$PARAMS" 2>/dev/null || true
|
|
239
|
+
bash "$GS" query "$CYPHER" "$PARAMS"
|
|
240
|
+
;;
|
|
241
|
+
|
|
242
|
+
check-implements)
|
|
243
|
+
SID="${1:?missing session-id}"
|
|
244
|
+
bash "$GS" query "
|
|
245
|
+
MATCH (impl:Session {id: \$sid})-[:IMPLEMENTS]->(ho:Session)-[:BY]->(author:Person)
|
|
246
|
+
RETURN ho.id AS handoffId, ho.topic AS topic, author.name AS author,
|
|
247
|
+
author.github AS authorGithub
|
|
248
|
+
" "$(jq -n --arg sid "$SID" '{sid: $sid}')"
|
|
249
|
+
;;
|
|
250
|
+
|
|
251
|
+
update-pr)
|
|
252
|
+
PR_NUM="${1:?missing pr-number}"
|
|
253
|
+
REPO="${2:?missing repo}"
|
|
254
|
+
STATUS="${3:?missing status}"
|
|
255
|
+
MERGED_AT="${4:-}"
|
|
256
|
+
if [ -n "$MERGED_AT" ]; then
|
|
257
|
+
bash "$GS" query "
|
|
258
|
+
MATCH (pr:PR {number: toInteger(\$num), repo: \$repo})
|
|
259
|
+
SET pr.status = \$status, pr.mergedAt = datetime(\$mergedAt)
|
|
260
|
+
RETURN pr.number AS number, pr.status AS status
|
|
261
|
+
" "$(jq -n --argjson num "$PR_NUM" --arg repo "$REPO" --arg status "$STATUS" --arg mergedAt "$MERGED_AT" '{num: $num, repo: $repo, status: $status, mergedAt: $mergedAt}')"
|
|
262
|
+
else
|
|
263
|
+
bash "$GS" query "
|
|
264
|
+
MATCH (pr:PR {number: toInteger(\$num), repo: \$repo})
|
|
265
|
+
SET pr.status = \$status
|
|
266
|
+
RETURN pr.number AS number, pr.status AS status
|
|
267
|
+
" "$(jq -n --argjson num "$PR_NUM" --arg repo "$REPO" --arg status "$STATUS" '{num: $num, repo: $repo, status: $status}')"
|
|
268
|
+
fi
|
|
269
|
+
;;
|
|
270
|
+
|
|
271
|
+
my-merged-prs)
|
|
272
|
+
AUTHOR_GH="${1:?missing author-github}"
|
|
273
|
+
SINCE="${2:-}"
|
|
274
|
+
if [ -n "$SINCE" ]; then
|
|
275
|
+
bash "$GS" query "
|
|
276
|
+
MATCH (pr:PR {author: \$author})
|
|
277
|
+
WHERE pr.status = 'merged' AND pr.mergedAt >= datetime(\$since)
|
|
278
|
+
RETURN pr.number AS number, pr.repo AS repo, pr.title AS title,
|
|
279
|
+
toString(pr.mergedAt) AS mergedAt
|
|
280
|
+
ORDER BY pr.mergedAt DESC LIMIT 10
|
|
281
|
+
" "$(jq -n --arg author "$AUTHOR_GH" --arg since "${SINCE}T00:00:00Z" '{author: $author, since: $since}')"
|
|
282
|
+
else
|
|
283
|
+
bash "$GS" query "
|
|
284
|
+
MATCH (pr:PR {author: \$author})
|
|
285
|
+
WHERE pr.status = 'merged'
|
|
286
|
+
RETURN pr.number AS number, pr.repo AS repo, pr.title AS title,
|
|
287
|
+
toString(pr.mergedAt) AS mergedAt
|
|
288
|
+
ORDER BY pr.mergedAt DESC LIMIT 5
|
|
289
|
+
" "$(jq -n --arg author "$AUTHOR_GH" '{author: $author}')"
|
|
290
|
+
fi
|
|
291
|
+
;;
|
|
292
|
+
|
|
293
|
+
my-implemented-handoffs)
|
|
294
|
+
AUTHOR_NAME="${1:?missing author-name}"
|
|
295
|
+
SINCE="${2:-}"
|
|
296
|
+
if [ -n "$SINCE" ]; then
|
|
297
|
+
bash "$GS" query "
|
|
298
|
+
MATCH (impl:Session)-[:IMPLEMENTS]->(ho:Session)-[:BY]->(author:Person)
|
|
299
|
+
WHERE toLower(author.name) = toLower(\$author)
|
|
300
|
+
AND impl.wrappedAt >= datetime(\$since)
|
|
301
|
+
MATCH (impl)-[:BY]->(implementor:Person)
|
|
302
|
+
RETURN ho.topic AS handoffTopic, implementor.name AS implementedBy,
|
|
303
|
+
toString(impl.wrappedAt) AS completedAt, impl.summary AS summary
|
|
304
|
+
ORDER BY impl.wrappedAt DESC LIMIT 10
|
|
305
|
+
" "$(jq -n --arg author "$AUTHOR_NAME" --arg since "${SINCE}T00:00:00Z" '{author: $author, since: $since}')"
|
|
306
|
+
else
|
|
307
|
+
bash "$GS" query "
|
|
308
|
+
MATCH (impl:Session)-[:IMPLEMENTS]->(ho:Session)-[:BY]->(author:Person)
|
|
309
|
+
WHERE toLower(author.name) = toLower(\$author)
|
|
310
|
+
MATCH (impl)-[:BY]->(implementor:Person)
|
|
311
|
+
RETURN ho.topic AS handoffTopic, implementor.name AS implementedBy,
|
|
312
|
+
toString(impl.wrappedAt) AS completedAt, impl.summary AS summary
|
|
313
|
+
ORDER BY impl.wrappedAt DESC LIMIT 5
|
|
314
|
+
" "$(jq -n --arg author "$AUTHOR_NAME" '{author: $author}')"
|
|
315
|
+
fi
|
|
316
|
+
;;
|
|
317
|
+
|
|
318
|
+
wal-status)
|
|
319
|
+
bash "$SCRIPT_DIR/bin/graph-wal.sh" status
|
|
320
|
+
;;
|
|
321
|
+
|
|
322
|
+
create-harvest)
|
|
323
|
+
HID="${1:?missing harvest-id}"
|
|
324
|
+
TOPIC="${2:?missing topic}"
|
|
325
|
+
INTENT="${3:?missing intent}"
|
|
326
|
+
INITIATOR="${4:?missing initiator}"
|
|
327
|
+
CYPHER="
|
|
328
|
+
MERGE (h:Harvest {id: \$hid})
|
|
329
|
+
ON CREATE SET h.topic = \$topic, h.intent = \$intent,
|
|
330
|
+
h.status = 'active', h.created = datetime()
|
|
331
|
+
WITH h
|
|
332
|
+
MATCH (p:Person) WHERE toLower(p.name) = toLower(\$initiator) OR p.github = \$initiator
|
|
333
|
+
MERGE (h)-[:INITIATED_BY]->(p)
|
|
334
|
+
RETURN h.id AS id, h.topic AS topic, h.status AS status
|
|
335
|
+
"
|
|
336
|
+
PARAMS="$(jq -n --arg hid "$HID" --arg topic "$TOPIC" --arg intent "$INTENT" --arg initiator "$INITIATOR" '{hid: $hid, topic: $topic, intent: $intent, initiator: $initiator}')"
|
|
337
|
+
bash "$SCRIPT_DIR/bin/graph-wal.sh" append "$CYPHER" "$PARAMS" 2>/dev/null || true
|
|
338
|
+
bash "$GS" query "$CYPHER" "$PARAMS"
|
|
339
|
+
;;
|
|
340
|
+
|
|
341
|
+
create-harvest-session)
|
|
342
|
+
HID="${1:?missing harvest-id}"
|
|
343
|
+
HSID="${2:?missing session-id}"
|
|
344
|
+
PERSON="${3:?missing person-name}"
|
|
345
|
+
STATUS="${4:-active}"
|
|
346
|
+
case "$STATUS" in
|
|
347
|
+
pending|active|answered|complete|incorporated) ;;
|
|
348
|
+
*) jq -n --arg s "$STATUS" '{error:("invalid status: "+$s),valid:["pending","active","answered","complete","incorporated"]}' >&2; exit 2 ;;
|
|
349
|
+
esac
|
|
350
|
+
CYPHER="
|
|
351
|
+
MATCH (h:Harvest {id: \$hid})
|
|
352
|
+
MERGE (hs:HarvestSession {id: \$hsid})
|
|
353
|
+
ON CREATE SET hs.status = \$status, hs.created = datetime()
|
|
354
|
+
MERGE (h)-[:HAS_SESSION]->(hs)
|
|
355
|
+
WITH hs
|
|
356
|
+
MATCH (p:Person) WHERE toLower(p.name) = toLower(\$person) OR p.github = \$person
|
|
357
|
+
MERGE (hs)-[:WITH]->(p)
|
|
358
|
+
RETURN hs.id AS id, hs.status AS status
|
|
359
|
+
"
|
|
360
|
+
PARAMS="$(jq -n --arg hid "$HID" --arg hsid "$HSID" --arg person "$PERSON" --arg status "$STATUS" '{hid: $hid, hsid: $hsid, person: $person, status: $status}')"
|
|
361
|
+
bash "$SCRIPT_DIR/bin/graph-wal.sh" append "$CYPHER" "$PARAMS" 2>/dev/null || true
|
|
362
|
+
OUT="$(bash "$GS" query "$CYPHER" "$PARAMS")"
|
|
363
|
+
if [ "$(echo "$OUT" | jq -r '.results | length' 2>/dev/null)" = "0" ]; then
|
|
364
|
+
jq -n --arg hid "$HID" --arg hsid "$HSID" --arg person "$PERSON" \
|
|
365
|
+
'{error:"create-harvest-session: harvest or person not found",hid:$hid,hsid:$hsid,person:$person}' >&2
|
|
366
|
+
exit 3
|
|
367
|
+
fi
|
|
368
|
+
echo "$OUT"
|
|
369
|
+
;;
|
|
370
|
+
|
|
371
|
+
record-harvest-turn)
|
|
372
|
+
HSID="${1:?missing session-id}"
|
|
373
|
+
TURN="${2:?missing turn-number}"
|
|
374
|
+
QUESTION="${3:?missing question}"
|
|
375
|
+
QINTENT="${4:?missing question-intent}"
|
|
376
|
+
ANSWER="${5:-}"
|
|
377
|
+
EVAL="${6:-}"
|
|
378
|
+
TURN_ID="${HSID}-turn-${TURN}"
|
|
379
|
+
PARAMS=$(jq -n \
|
|
380
|
+
--arg hsid "$HSID" --arg tid "$TURN_ID" --arg turn "$TURN" \
|
|
381
|
+
--arg question "$QUESTION" --arg qintent "$QINTENT" \
|
|
382
|
+
--arg answer "$ANSWER" --arg eval "$EVAL" \
|
|
383
|
+
'{hsid:$hsid, tid:$tid, turn:$turn, question:$question, qintent:$qintent, answer:$answer, eval:$eval}')
|
|
384
|
+
if [ -n "$ANSWER" ]; then
|
|
385
|
+
bash "$GS" query "
|
|
386
|
+
MATCH (hs:HarvestSession {id: \$hsid})
|
|
387
|
+
MERGE (t:HarvestTurn {id: \$tid})
|
|
388
|
+
ON CREATE SET t.turnNumber = toInteger(\$turn), t.question = \$question,
|
|
389
|
+
t.questionIntent = \$qintent, t.created = datetime()
|
|
390
|
+
SET t.answer = \$answer, t.answeredAt = datetime()
|
|
391
|
+
SET t.evaluation = CASE WHEN \$eval <> '' THEN \$eval ELSE t.evaluation END
|
|
392
|
+
MERGE (hs)-[:HAS_TURN]->(t)
|
|
393
|
+
RETURN t.id AS id, t.turnNumber AS turnNumber
|
|
394
|
+
" "$PARAMS"
|
|
395
|
+
else
|
|
396
|
+
bash "$GS" query "
|
|
397
|
+
MATCH (hs:HarvestSession {id: \$hsid})
|
|
398
|
+
MERGE (t:HarvestTurn {id: \$tid})
|
|
399
|
+
ON CREATE SET t.turnNumber = toInteger(\$turn), t.question = \$question,
|
|
400
|
+
t.questionIntent = \$qintent, t.created = datetime()
|
|
401
|
+
MERGE (hs)-[:HAS_TURN]->(t)
|
|
402
|
+
RETURN t.id AS id, t.turnNumber AS turnNumber
|
|
403
|
+
" "$PARAMS"
|
|
404
|
+
fi
|
|
405
|
+
;;
|
|
406
|
+
|
|
407
|
+
complete-harvest)
|
|
408
|
+
HID="${1:?missing harvest-id}"
|
|
409
|
+
ARTIFACT_PATH="${2:?missing artifact-path}"
|
|
410
|
+
bash "$GS" query "
|
|
411
|
+
MATCH (h:Harvest {id: \$hid})
|
|
412
|
+
SET h.status = 'complete', h.completedAt = datetime(), h.synthesisPath = \$path
|
|
413
|
+
WITH h
|
|
414
|
+
OPTIONAL MATCH (h)-[:HAS_SESSION]->(hs:HarvestSession)
|
|
415
|
+
WHERE hs.status <> 'complete'
|
|
416
|
+
SET hs.status = 'complete', hs.completedAt = datetime()
|
|
417
|
+
WITH h
|
|
418
|
+
MERGE (a:Artifact {id: \$hid + '-synthesis'})
|
|
419
|
+
ON CREATE SET a.type = 'harvest', a.title = h.topic, a.filePath = \$path,
|
|
420
|
+
a.created = datetime(), a.topics = coalesce([h.topic], [])
|
|
421
|
+
MERGE (h)-[:PRODUCED]->(a)
|
|
422
|
+
RETURN h.id AS id, h.status AS status, a.id AS artifact
|
|
423
|
+
" "$(jq -n --arg hid "$HID" --arg path "$ARTIFACT_PATH" '{hid: $hid, path: $path}')"
|
|
424
|
+
;;
|
|
425
|
+
|
|
426
|
+
*)
|
|
427
|
+
echo '{"error":"unknown operation: '"$OP"'","operations":["mark-read","mark-done","answer-question","resolve-handoffs","set-topic","record-focus","merge-person","claim-handoff","check-implements","create-pr","update-pr","my-merged-prs","my-implemented-handoffs","wal-status","create-harvest","create-harvest-session","record-harvest-turn","complete-harvest"]}'
|
|
428
|
+
exit 1
|
|
429
|
+
;;
|
|
430
|
+
|
|
431
|
+
esac
|