create-egregore 0.17.4 → 0.18.1
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 +1965 -0
- package/assets/egregore-launcher.sh +385 -0
- package/bin/cli.js +472 -81
- package/lib/api.js +2 -2
- package/lib/auth.js +255 -20
- package/lib/codex-runtime.js +268 -0
- package/lib/commands.js +31 -0
- package/lib/context.js +1 -1
- package/lib/local.js +616 -189
- package/lib/setup.js +822 -84
- package/lib/ui.js +780 -84
- package/package.json +12 -3
- package/runtime/codex/.codex/config.toml +15 -0
- package/runtime/codex/.codex/hooks/branch-guard.js +234 -0
- package/runtime/codex/.codex/hooks.json +17 -0
- package/runtime/codex/.codex/skills/activity/SKILL.md +70 -0
- package/runtime/codex/.codex/skills/add/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/announce/SKILL.md +54 -0
- package/runtime/codex/.codex/skills/answer/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/archive/SKILL.md +41 -0
- package/runtime/codex/.codex/skills/ask/SKILL.md +59 -0
- package/runtime/codex/.codex/skills/branch/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/checkup/SKILL.md +41 -0
- package/runtime/codex/.codex/skills/commit/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/contribute/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/dark-mode/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/dashboard/SKILL.md +59 -0
- package/runtime/codex/.codex/skills/deep-reflect/SKILL.md +70 -0
- package/runtime/codex/.codex/skills/delete-user/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/egregore-voice/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/emissary/SKILL.md +41 -0
- package/runtime/codex/.codex/skills/env/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/handoff/SKILL.md +67 -0
- package/runtime/codex/.codex/skills/harvest/SKILL.md +107 -0
- package/runtime/codex/.codex/skills/infra/SKILL.md +41 -0
- package/runtime/codex/.codex/skills/ingest/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/ingest-google/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/ingest-user-interview/SKILL.md +41 -0
- package/runtime/codex/.codex/skills/invite/SKILL.md +40 -0
- package/runtime/codex/.codex/skills/issue/SKILL.md +41 -0
- package/runtime/codex/.codex/skills/me/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/meeting/SKILL.md +41 -0
- package/runtime/codex/.codex/skills/note/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/onboarding/SKILL.md +41 -0
- package/runtime/codex/.codex/skills/pr/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/project/SKILL.md +41 -0
- package/runtime/codex/.codex/skills/pull/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/push/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/quest/SKILL.md +77 -0
- package/runtime/codex/.codex/skills/quest-suggest/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/reflect/SKILL.md +41 -0
- package/runtime/codex/.codex/skills/review-pr/SKILL.md +41 -0
- package/runtime/codex/.codex/skills/save/SKILL.md +62 -0
- package/runtime/codex/.codex/skills/setup/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/summon/SKILL.md +41 -0
- package/runtime/codex/.codex/skills/sync-repos/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/telegram-connect/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/telemetry/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/test/SKILL.md +41 -0
- package/runtime/codex/.codex/skills/the-spiral/SKILL.md +84 -0
- package/runtime/codex/.codex/skills/todo/SKILL.md +41 -0
- package/runtime/codex/.codex/skills/tui-design/SKILL.md +41 -0
- package/runtime/codex/.codex/skills/tutorial/SKILL.md +41 -0
- package/runtime/codex/.codex/skills/update/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/view/SKILL.md +41 -0
- package/runtime/codex/.codex/skills/visual-explain/SKILL.md +41 -0
- package/runtime/codex/.codex/skills/wrap/SKILL.md +55 -0
- package/runtime/codex/.codex/spec-manifest.json +108 -0
- package/runtime/codex/AGENTS.md +40 -0
- package/runtime/codex/bin/activity-data.sh +177 -0
- package/runtime/codex/bin/agent.sh +669 -0
- package/runtime/codex/bin/codex-install-skills.sh +78 -0
- package/runtime/codex/bin/codex-render-spec.mjs +308 -0
- package/runtime/codex/bin/codex-session-start.sh +238 -0
- package/runtime/codex/bin/codex-skill-render.mjs +315 -0
- package/runtime/codex/bin/codex-sync-skills.sh +223 -0
- package/runtime/codex/bin/dashboard-data.sh +174 -0
- package/runtime/codex/bin/graph-batch.sh +60 -0
- package/runtime/codex/bin/graph-op.sh +380 -0
- package/runtime/codex/bin/graph-wal.sh +228 -0
- package/runtime/codex/bin/graph.sh +141 -0
- package/runtime/codex/bin/handoff-pr-backfill.sh +86 -0
- package/runtime/codex/bin/handoff-run.sh +431 -0
- package/runtime/codex/bin/handoff-save-egregore.sh +131 -0
- package/runtime/codex/bin/index-handoff.sh +306 -0
- package/runtime/codex/bin/lib/artifact-id.sh +46 -0
- package/runtime/codex/bin/lib/config.sh +71 -0
- package/runtime/codex/bin/lib/context.sh +463 -0
- package/runtime/codex/bin/lib/dashboard-artifact.sh +142 -0
- package/runtime/codex/bin/lib/ensure-develop.sh +183 -0
- package/runtime/codex/bin/lib/git-sync.sh +262 -0
- package/runtime/codex/bin/lib/greeting.sh +414 -0
- package/runtime/codex/bin/lib/hash.sh +13 -0
- package/runtime/codex/bin/lib/identity.sh +164 -0
- package/runtime/codex/bin/lib/permissions.sh +106 -0
- package/runtime/codex/bin/lib/time.sh +56 -0
- package/runtime/codex/bin/lib/worktree-links.sh +54 -0
- package/runtime/codex/bin/notify.sh +207 -0
- package/runtime/codex/bin/publish-artifact.sh +173 -0
- package/runtime/codex/bin/publish-references.sh +144 -0
- package/runtime/codex/bin/repo-state.sh +99 -0
- package/runtime/codex/bin/telemetry.sh +350 -0
- package/runtime/codex/bin/worktree-create.sh +118 -0
- package/runtime/codex/bin/worktree-remove.sh +50 -0
- package/runtime/codex/bin/worktree.sh +198 -0
- package/runtime/codex/manifest.json +155 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Index a single handoff file into the Neo4j graph.
|
|
5
|
+
# Usage: bash bin/index-handoff.sh <file-path>
|
|
6
|
+
# Returns: {"sessionId":"...","resolved":N} or {"error":"..."}
|
|
7
|
+
#
|
|
8
|
+
# The file-path should be relative to memory/ (e.g. handoffs/2026-02/14-oz-topic.md)
|
|
9
|
+
# or an absolute/relative path that contains "handoffs/" somewhere in it.
|
|
10
|
+
|
|
11
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
12
|
+
|
|
13
|
+
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
|
|
14
|
+
echo "Usage: index-handoff.sh <handoff-file-path>"
|
|
15
|
+
echo ""
|
|
16
|
+
echo "Index a single handoff file into the Neo4j graph."
|
|
17
|
+
echo "Parses metadata (author, date, topic, recipients) from"
|
|
18
|
+
echo "markdown headers or YAML front matter, creates Session"
|
|
19
|
+
echo "and relationship nodes, and auto-resolves old handoffs."
|
|
20
|
+
echo ""
|
|
21
|
+
echo "Returns: {\"sessionId\":\"...\",\"resolved\":N}"
|
|
22
|
+
exit 0
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# --- Local mode gate: bail immediately ---
|
|
26
|
+
_MODE=$(jq -r '.mode // "connected"' "$SCRIPT_DIR/egregore.json" 2>/dev/null)
|
|
27
|
+
if [ "$_MODE" = "local" ]; then
|
|
28
|
+
echo '{"sessionId":"","resolved":0,"mode":"local"}'
|
|
29
|
+
exit 0
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# --- Validate input ---
|
|
33
|
+
FILE_ARG="${1:?Usage: index-handoff.sh <handoff-file-path>}"
|
|
34
|
+
|
|
35
|
+
# Resolve to absolute path if needed
|
|
36
|
+
if [[ "$FILE_ARG" = /* ]]; then
|
|
37
|
+
FILE_PATH="$FILE_ARG"
|
|
38
|
+
elif [[ "$FILE_ARG" = memory/* ]]; then
|
|
39
|
+
FILE_PATH="$SCRIPT_DIR/$FILE_ARG"
|
|
40
|
+
else
|
|
41
|
+
FILE_PATH="$SCRIPT_DIR/memory/$FILE_ARG"
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
if [ ! -f "$FILE_PATH" ]; then
|
|
45
|
+
echo '{"error":"File not found: '"$FILE_ARG"'"}'
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# --- Derive filePath (relative to memory/) and sessionId ---
|
|
50
|
+
REL_PATH="${FILE_PATH##*/memory/}"
|
|
51
|
+
if [[ "$REL_PATH" = "$FILE_PATH" ]]; then
|
|
52
|
+
REL_PATH="handoffs/${FILE_PATH##*handoffs/}"
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
FILENAME="$(basename "$FILE_PATH" .md)"
|
|
56
|
+
DIR_MONTH="$(basename "$(dirname "$FILE_PATH")")"
|
|
57
|
+
|
|
58
|
+
# sessionId = YYYY-MM-DD-author-topic
|
|
59
|
+
# If file is in a YYYY-MM subdirectory, derive from dir + filename
|
|
60
|
+
# If file is in root handoffs/ (no month subdir), extract date from filename
|
|
61
|
+
if [[ "$DIR_MONTH" =~ ^[0-9]{4}-[0-9]{2}$ ]]; then
|
|
62
|
+
DAY="${FILENAME%%-*}"
|
|
63
|
+
REST="${FILENAME#*-}"
|
|
64
|
+
SESSION_ID="${DIR_MONTH}-${DAY}-${REST}"
|
|
65
|
+
else
|
|
66
|
+
# Filename starts with YYYY-MM-DD — use it directly
|
|
67
|
+
SESSION_ID="$FILENAME"
|
|
68
|
+
DAY="$(echo "$FILENAME" | grep -oE '^[0-9]{4}-[0-9]{2}-[0-9]{2}' || true)"
|
|
69
|
+
REST="${FILENAME#*-*-*-}"
|
|
70
|
+
DIR_MONTH="$(echo "$DAY" | grep -oE '^[0-9]{4}-[0-9]{2}' || true)"
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
# --- Parse handoff metadata from markdown ---
|
|
74
|
+
CONTENT="$(cat "$FILE_PATH")"
|
|
75
|
+
|
|
76
|
+
# Helper: extract from markdown headers, handles both **Key**: Value and **Key:** Value
|
|
77
|
+
md_extract() {
|
|
78
|
+
echo "$CONTENT" | grep -m1 "$1" 2>/dev/null | sed "$2" || true
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# Helper: extract markdown field by name (handles **Key**: and **Key:** patterns)
|
|
82
|
+
md_field() {
|
|
83
|
+
local key="$1"
|
|
84
|
+
local val
|
|
85
|
+
# Try **Key**: Value (colon outside bold)
|
|
86
|
+
val="$(echo "$CONTENT" | grep -m1 "^\*\*${key}\*\*:" 2>/dev/null | sed "s/\*\*${key}\*\*:[[:space:]]*//" | tr -d '\n' || true)"
|
|
87
|
+
# Try **Key:** Value (colon inside bold)
|
|
88
|
+
if [ -z "$val" ]; then
|
|
89
|
+
val="$(echo "$CONTENT" | grep -m1 "^\*\*${key}:\*\*" 2>/dev/null | sed "s/\*\*${key}:\*\*[[:space:]]*//" | tr -d '\n' || true)"
|
|
90
|
+
fi
|
|
91
|
+
echo "$val"
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# Helper: extract from YAML front matter (key: value between --- delimiters)
|
|
95
|
+
yaml_extract() {
|
|
96
|
+
local key="$1"
|
|
97
|
+
sed -n '/^---$/,/^---$/p' "$FILE_PATH" 2>/dev/null | grep -m1 "^${key}:" 2>/dev/null | sed "s/^${key}:[[:space:]]*//" | sed 's/^"//' | sed 's/"$//' | xargs 2>/dev/null || true
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
# H1 topic (always from markdown body, not front matter)
|
|
101
|
+
TOPIC="$(md_extract '^# ' 's/^# //; s/^Handoff: //')"
|
|
102
|
+
# Also check YAML front matter topic
|
|
103
|
+
[ -z "$TOPIC" ] && TOPIC="$(yaml_extract 'topic')"
|
|
104
|
+
[ -z "$TOPIC" ] && TOPIC="$REST"
|
|
105
|
+
|
|
106
|
+
# Date: try markdown header, then YAML
|
|
107
|
+
DATE="$(md_field 'Date')"
|
|
108
|
+
[ -z "$DATE" ] && DATE="$(yaml_extract 'date')"
|
|
109
|
+
[ -z "$DATE" ] && DATE="${DIR_MONTH}-${DAY}"
|
|
110
|
+
|
|
111
|
+
# Author: try markdown header (Author or From), then YAML (author or from)
|
|
112
|
+
AUTHOR="$(md_field 'Author')"
|
|
113
|
+
[ -z "$AUTHOR" ] && AUTHOR="$(md_field 'From')"
|
|
114
|
+
[ -z "$AUTHOR" ] && AUTHOR="$(yaml_extract 'author')"
|
|
115
|
+
[ -z "$AUTHOR" ] && AUTHOR="$(yaml_extract 'from')"
|
|
116
|
+
# Strip " → recipient" if present (e.g. "cem → oz" → "cem")
|
|
117
|
+
AUTHOR_HANDLE="$(echo "$AUTHOR" | sed 's/[[:space:]]*→.*//' | awk '{print tolower($1)}')"
|
|
118
|
+
[ -z "$AUTHOR_HANDLE" ] && AUTHOR_HANDLE="unknown"
|
|
119
|
+
|
|
120
|
+
# Recipients: try markdown header (To or For), then YAML (to or for)
|
|
121
|
+
RECIPIENTS_RAW="$(md_field 'To')"
|
|
122
|
+
[ -z "$RECIPIENTS_RAW" ] && RECIPIENTS_RAW="$(md_field 'For')"
|
|
123
|
+
[ -z "$RECIPIENTS_RAW" ] && RECIPIENTS_RAW="$(yaml_extract 'to')"
|
|
124
|
+
[ -z "$RECIPIENTS_RAW" ] && RECIPIENTS_RAW="$(yaml_extract 'for')"
|
|
125
|
+
# Also extract from "From: author → recipient" pattern
|
|
126
|
+
if [ -z "$RECIPIENTS_RAW" ] && echo "$AUTHOR" | grep -q '→' 2>/dev/null; then
|
|
127
|
+
RECIPIENTS_RAW="$(echo "$AUTHOR" | sed 's/.*→[[:space:]]*//')"
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
# Project: try markdown header, then YAML
|
|
131
|
+
PROJECT="$(md_field 'Project')"
|
|
132
|
+
[ -z "$PROJECT" ] && PROJECT="$(yaml_extract 'project')"
|
|
133
|
+
|
|
134
|
+
# Summary: 5-level precedence chain
|
|
135
|
+
# 1. Frontmatter summary field
|
|
136
|
+
SUMMARY="$(yaml_extract 'summary')"
|
|
137
|
+
# 2. First non-empty paragraph after ## Briefing
|
|
138
|
+
[ -z "$SUMMARY" ] && SUMMARY="$(echo "$CONTENT" | awk '/^## Briefing/{found=1; next} found && /^#/{exit} found && /^[^#]/ && !/^[[:space:]]*$/{print; exit}' || true)"
|
|
139
|
+
# 3. First non-empty paragraph after ## Session Summary (legacy)
|
|
140
|
+
[ -z "$SUMMARY" ] && SUMMARY="$(echo "$CONTENT" | awk '/^## Session Summary/{found=1; next} found && /^#/{exit} found && /^[^#]/ && !/^[[:space:]]*$/{print; exit}' || true)"
|
|
141
|
+
# 4. First non-heading, non-metadata, non-empty line in body
|
|
142
|
+
[ -z "$SUMMARY" ] && SUMMARY="$(echo "$CONTENT" | awk '!/^[[:space:]]*$/ && !/^#/ && !/^\*\*/ && !/^---/ && !/^\|/ && !/^- /{print; exit}' || true)"
|
|
143
|
+
# 5. Topic fallback
|
|
144
|
+
[ -z "$SUMMARY" ] && SUMMARY="$TOPIC"
|
|
145
|
+
|
|
146
|
+
# Repo State: parse markdown table after "## Repo State"
|
|
147
|
+
# Extracts repo, branch, PR number, and base branch from each table row
|
|
148
|
+
REPO_STATE_JSON="[]"
|
|
149
|
+
REPO_TABLE=$(echo "$CONTENT" | awk '/^## Repo State/{found=1; next} found && /^#/{exit} found && /^\|[^-]/ && !/^\| Repo/{print}' || true)
|
|
150
|
+
if [ -n "$REPO_TABLE" ]; then
|
|
151
|
+
REPO_STATE_JSON=$(echo "$REPO_TABLE" | while IFS='|' read -r _ R_REPO R_BRANCH R_PR R_BASE _; do
|
|
152
|
+
R_REPO=$(echo "$R_REPO" | xargs 2>/dev/null || true)
|
|
153
|
+
R_BRANCH=$(echo "$R_BRANCH" | xargs 2>/dev/null || true)
|
|
154
|
+
R_PR=$(echo "$R_PR" | xargs 2>/dev/null | sed 's/^#//' | sed 's/—//' || true)
|
|
155
|
+
R_BASE=$(echo "$R_BASE" | xargs 2>/dev/null || true)
|
|
156
|
+
[ -z "$R_REPO" ] && continue
|
|
157
|
+
if [ -n "$R_PR" ] && [ "$R_PR" -eq "$R_PR" ] 2>/dev/null; then
|
|
158
|
+
jq -n --arg repo "$R_REPO" --arg branch "$R_BRANCH" --argjson pr "$R_PR" --arg base "$R_BASE" \
|
|
159
|
+
'{repo:$repo, branch:$branch, pr:$pr, base:$base}'
|
|
160
|
+
else
|
|
161
|
+
jq -n --arg repo "$R_REPO" --arg branch "$R_BRANCH" --arg base "$R_BASE" \
|
|
162
|
+
'{repo:$repo, branch:$branch, pr:null, base:$base}'
|
|
163
|
+
fi
|
|
164
|
+
done | jq -s '.' 2>/dev/null || echo "[]")
|
|
165
|
+
fi
|
|
166
|
+
|
|
167
|
+
# --- Build Cypher queries ---
|
|
168
|
+
|
|
169
|
+
# Q1: MERGE Session + relationships
|
|
170
|
+
HANDED_TO_CYPHER=""
|
|
171
|
+
_RECIP_ARGS=()
|
|
172
|
+
if [ -n "$RECIPIENTS_RAW" ]; then
|
|
173
|
+
IFS=',' read -ra RECIPS <<< "$RECIPIENTS_RAW"
|
|
174
|
+
for i in "${!RECIPS[@]}"; do
|
|
175
|
+
R="$(echo "${RECIPS[$i]}" | xargs | tr '[:upper:]' '[:lower:]')"
|
|
176
|
+
[ -z "$R" ] && continue
|
|
177
|
+
HANDED_TO_CYPHER="${HANDED_TO_CYPHER}
|
|
178
|
+
WITH s
|
|
179
|
+
OPTIONAL MATCH (r${i}:Person) WHERE toLower(r${i}.name) = \$recipient${i} OR r${i}.github = \$recipient${i} OR toLower(r${i}.fullName) = \$recipient${i}
|
|
180
|
+
FOREACH (_ IN CASE WHEN r${i} IS NOT NULL THEN [1] ELSE [] END |
|
|
181
|
+
MERGE (s)-[:HANDED_TO]->(r${i})
|
|
182
|
+
)"
|
|
183
|
+
_RECIP_ARGS+=("--arg" "recipient${i}" "$R")
|
|
184
|
+
done
|
|
185
|
+
fi
|
|
186
|
+
|
|
187
|
+
PROJECT_CYPHER=""
|
|
188
|
+
_PROJECT_ARGS=()
|
|
189
|
+
if [ -n "$PROJECT" ]; then
|
|
190
|
+
PROJECT_CYPHER="
|
|
191
|
+
WITH s
|
|
192
|
+
OPTIONAL MATCH (proj:Project) WHERE toLower(proj.name) = toLower(\$project)
|
|
193
|
+
FOREACH (_ IN CASE WHEN proj IS NOT NULL THEN [1] ELSE [] END |
|
|
194
|
+
MERGE (s)-[:ABOUT]->(proj)
|
|
195
|
+
)"
|
|
196
|
+
_PROJECT_ARGS=("--arg" "project" "$PROJECT")
|
|
197
|
+
fi
|
|
198
|
+
|
|
199
|
+
Q1_CYPHER="MATCH (p:Person) WHERE toLower(p.name) = \$author OR p.github = \$author OR toLower(p.fullName) = \$author OR \$author IN [x IN coalesce(p.previousNames, []) | toLower(x)]
|
|
200
|
+
MERGE (s:Session {id: \$sessionId})
|
|
201
|
+
ON CREATE SET s.date = date(\$date), s.topic = \$topic, s.summary = \$summary, s.filePath = \$filePath, s.handoffStatus = 'pending', s.repoState = \$repoState, s.startedAt = datetime(\$date + 'T00:00:00Z')
|
|
202
|
+
ON MATCH SET s.topic = \$topic, s.summary = \$summary, s.filePath = \$filePath, s.repoState = \$repoState
|
|
203
|
+
MERGE (s)-[:BY]->(p)${PROJECT_CYPHER}${HANDED_TO_CYPHER}
|
|
204
|
+
RETURN s.id AS sessionId"
|
|
205
|
+
|
|
206
|
+
Q1_PARAMS=$(jq -n \
|
|
207
|
+
--arg sessionId "$SESSION_ID" \
|
|
208
|
+
--arg author "$AUTHOR_HANDLE" \
|
|
209
|
+
--arg date "$DATE" \
|
|
210
|
+
--arg topic "$TOPIC" \
|
|
211
|
+
--arg summary "$SUMMARY" \
|
|
212
|
+
--arg filePath "$REL_PATH" \
|
|
213
|
+
--arg repoState "$REPO_STATE_JSON" \
|
|
214
|
+
"${_PROJECT_ARGS[@]:+${_PROJECT_ARGS[@]}}" \
|
|
215
|
+
"${_RECIP_ARGS[@]:+${_RECIP_ARGS[@]}}" \
|
|
216
|
+
'$ARGS.named')
|
|
217
|
+
|
|
218
|
+
# Q2: Auto-resolve old read handoffs from this author
|
|
219
|
+
Q2_CYPHER="MATCH (s:Session)-[:HANDED_TO]->(p:Person) WHERE (toLower(p.name) = \$author OR p.github = \$author OR toLower(p.fullName) = \$author) AND s.handoffStatus = 'read' AND s.id <> \$sessionId
|
|
220
|
+
WITH s, p, coalesce(s.handoffReadDate, s.date) AS sinceDate
|
|
221
|
+
MATCH (later:Session)-[:BY]->(p)
|
|
222
|
+
WHERE later.date > sinceDate
|
|
223
|
+
WITH s, count(later) AS laterSessions WHERE laterSessions > 0
|
|
224
|
+
SET s.handoffStatus = 'done'
|
|
225
|
+
RETURN count(s) AS resolved"
|
|
226
|
+
|
|
227
|
+
Q2_PARAMS=$(jq -n --arg author "$AUTHOR_HANDLE" --arg sessionId "$SESSION_ID" \
|
|
228
|
+
'{author: $author, sessionId: $sessionId}')
|
|
229
|
+
|
|
230
|
+
# Q3: Capture the handoff's knowledge-graph neighborhood for artifact rendering.
|
|
231
|
+
# Only the "graph-only" relations — things not already visible in the markdown
|
|
232
|
+
# header. Author/recipients/project live in the metadata row above the graph,
|
|
233
|
+
# so including them would be noise.
|
|
234
|
+
Q3_CYPHER="MATCH (s:Session {id: \$sessionId})
|
|
235
|
+
OPTIONAL MATCH (s)-[:IMPLEMENTS]->(prior:Session)
|
|
236
|
+
OPTIONAL MATCH (prior)-[:BY]->(priorAuthor:Person)
|
|
237
|
+
OPTIONAL MATCH (later:Session)-[:IMPLEMENTS]->(s)
|
|
238
|
+
OPTIONAL MATCH (later)-[:BY]->(laterAuthor:Person)
|
|
239
|
+
OPTIONAL MATCH (s)-[:CONTINUES]->(cont:Session)
|
|
240
|
+
OPTIONAL MATCH (cont)-[:BY]->(contAuthor:Person)
|
|
241
|
+
OPTIONAL MATCH (s)-[:ADVANCED|INVOLVES]->(quest:Quest)
|
|
242
|
+
OPTIONAL MATCH (s)-[:HAS_ACTIVITY]->(art:Artifact)
|
|
243
|
+
OPTIONAL MATCH (s)-[:PRODUCED]->(pr:PR)
|
|
244
|
+
RETURN
|
|
245
|
+
collect(DISTINCT {id: prior.id, topic: prior.topic, author: priorAuthor.name}) AS implementsHandoff,
|
|
246
|
+
collect(DISTINCT {id: later.id, topic: later.topic, author: laterAuthor.name}) AS implementedBy,
|
|
247
|
+
collect(DISTINCT {id: cont.id, topic: cont.topic, author: contAuthor.name}) AS continues,
|
|
248
|
+
collect(DISTINCT {id: quest.id, title: quest.title}) AS quests,
|
|
249
|
+
collect(DISTINCT {id: art.id, title: art.title, type: art.type, path: art.path}) AS artifacts,
|
|
250
|
+
collect(DISTINCT {id: pr.id, number: pr.number, title: pr.title, repo: pr.repo}) AS prs"
|
|
251
|
+
|
|
252
|
+
Q3_PARAMS=$(jq -n --arg sessionId "$SESSION_ID" '{sessionId: $sessionId}')
|
|
253
|
+
|
|
254
|
+
# --- Build batch request ---
|
|
255
|
+
BATCH_JSON=$(jq -n \
|
|
256
|
+
--arg q1 "$Q1_CYPHER" \
|
|
257
|
+
--argjson p1 "$Q1_PARAMS" \
|
|
258
|
+
--arg q2 "$Q2_CYPHER" \
|
|
259
|
+
--argjson p2 "$Q2_PARAMS" \
|
|
260
|
+
--arg q3 "$Q3_CYPHER" \
|
|
261
|
+
--argjson p3 "$Q3_PARAMS" \
|
|
262
|
+
'[{statement: $q1, parameters: $p1}, {statement: $q2, parameters: $p2}, {statement: $q3, parameters: $p3}]')
|
|
263
|
+
|
|
264
|
+
# --- Execute ---
|
|
265
|
+
RESPONSE=$(bash "$SCRIPT_DIR/bin/graph-batch.sh" "$BATCH_JSON" 2>/dev/null) || {
|
|
266
|
+
echo '{"error":"graph-batch.sh failed"}'
|
|
267
|
+
exit 1
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
# --- Mark auto-captured personal Session as handed_off ---
|
|
271
|
+
PROJ_HASH=$(echo -n "$SCRIPT_DIR" | md5 2>/dev/null || echo -n "$SCRIPT_DIR" | md5sum 2>/dev/null | cut -d' ' -f1)
|
|
272
|
+
CURRENT_SID=$(cat "$HOME/.egregore/session-${PROJ_HASH}.id" 2>/dev/null || echo "")
|
|
273
|
+
if [ -n "$CURRENT_SID" ]; then
|
|
274
|
+
bash "$SCRIPT_DIR/bin/graph.sh" query \
|
|
275
|
+
"MATCH (s:Session {id: \$sid}) SET s.status = 'handed_off' RETURN s.id" \
|
|
276
|
+
"$(jq -n --arg sid "$CURRENT_SID" '{sid: $sid}')" 2>/dev/null || true
|
|
277
|
+
fi
|
|
278
|
+
|
|
279
|
+
# --- Parse response ---
|
|
280
|
+
RESOLVED=$(echo "$RESPONSE" | jq -r '.results[1].values[0][0] // 0' 2>/dev/null || echo "0")
|
|
281
|
+
|
|
282
|
+
# Parse Q3 neighborhood into a subgraph object. Collect() entries that didn't
|
|
283
|
+
# match anything come back as {name: null, ...} — filter those out.
|
|
284
|
+
EMPTY_SG='{"implementsHandoff":[],"implementedBy":[],"continues":[],"quests":[],"artifacts":[],"prs":[]}'
|
|
285
|
+
SUBGRAPH_JSON=$(echo "$RESPONSE" | jq -c '
|
|
286
|
+
(.results[2] // null) as $r
|
|
287
|
+
| if $r == null or (($r.values // []) | length) == 0 then
|
|
288
|
+
{implementsHandoff:[], implementedBy:[], continues:[], quests:[], artifacts:[], prs:[]}
|
|
289
|
+
else
|
|
290
|
+
($r.fields // []) as $f
|
|
291
|
+
| ($r.values[0] // []) as $v
|
|
292
|
+
| reduce range(0; ($f | length)) as $i ({}; .[$f[$i]] = $v[$i])
|
|
293
|
+
| {
|
|
294
|
+
implementsHandoff: ((.implementsHandoff // []) | map(select(.id != null))),
|
|
295
|
+
implementedBy: ((.implementedBy // []) | map(select(.id != null))),
|
|
296
|
+
continues: ((.continues // []) | map(select(.id != null))),
|
|
297
|
+
quests: ((.quests // []) | map(select(.id != null))),
|
|
298
|
+
artifacts: ((.artifacts // []) | map(select(.id != null or .path != null))),
|
|
299
|
+
prs: ((.prs // []) | map(select(.number != null or .id != null)))
|
|
300
|
+
}
|
|
301
|
+
end
|
|
302
|
+
' 2>/dev/null || echo "$EMPTY_SG")
|
|
303
|
+
[ -z "$SUBGRAPH_JSON" ] && SUBGRAPH_JSON="$EMPTY_SG"
|
|
304
|
+
|
|
305
|
+
jq -n --arg sessionId "$SESSION_ID" --argjson resolved "$RESOLVED" --argjson subgraph "$SUBGRAPH_JSON" \
|
|
306
|
+
'{sessionId: $sessionId, resolved: $resolved, subgraph: $subgraph}'
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# artifact-id.sh — Deterministic artifact IDs for memory files.
|
|
3
|
+
#
|
|
4
|
+
# Emits `{prefix}-{12 hex}` where prefix is derived from the file extension
|
|
5
|
+
# (m = .md, h = .html) and the hex is the first 12 chars of sha256(canonical_path).
|
|
6
|
+
#
|
|
7
|
+
# Kept in parity with packages/egregore-artifacts/lib/artifact-id.js — both MUST
|
|
8
|
+
# produce identical output for the same input. Any change here requires an
|
|
9
|
+
# equivalent change there (and a passing parity test in test/artifact-id.test.js).
|
|
10
|
+
|
|
11
|
+
_artifact_id_sha256() {
|
|
12
|
+
local input="$1"
|
|
13
|
+
printf '%s' "$input" | shasum -a 256 2>/dev/null | cut -d' ' -f1 \
|
|
14
|
+
|| printf '%s' "$input" | sha256sum 2>/dev/null | cut -d' ' -f1
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
# artifact_id_from_path <path>
|
|
18
|
+
# Returns the deterministic artifact id for a repo-relative memory path, or
|
|
19
|
+
# empty (exit 1) if the extension is unsupported.
|
|
20
|
+
artifact_id_from_path() {
|
|
21
|
+
local raw="$1"
|
|
22
|
+
[ -n "$raw" ] || return 1
|
|
23
|
+
|
|
24
|
+
local canonical
|
|
25
|
+
canonical="${raw#./}"
|
|
26
|
+
canonical="$(printf '%s' "$canonical" | sed 's|/\{2,\}|/|g')"
|
|
27
|
+
|
|
28
|
+
# Extension match is case-insensitive to keep parity with the JS renderer
|
|
29
|
+
# (`extension()` in artifact-id.js lowercases before lookup). The canonical
|
|
30
|
+
# path itself is hashed verbatim — only the extension lookup is case-folded.
|
|
31
|
+
local ext_lower
|
|
32
|
+
ext_lower="$(printf '%s' "${canonical##*.}" | tr '[:upper:]' '[:lower:]')"
|
|
33
|
+
|
|
34
|
+
local prefix=""
|
|
35
|
+
case "$ext_lower" in
|
|
36
|
+
md) prefix="m" ;;
|
|
37
|
+
html) prefix="h" ;;
|
|
38
|
+
*) return 1 ;;
|
|
39
|
+
esac
|
|
40
|
+
|
|
41
|
+
local hash
|
|
42
|
+
hash="$(_artifact_id_sha256 "$canonical")"
|
|
43
|
+
[ -n "$hash" ] || return 1
|
|
44
|
+
|
|
45
|
+
printf '%s-%s' "$prefix" "${hash:0:12}"
|
|
46
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Shared config helpers — sourced by session-start.sh and other scripts.
|
|
3
|
+
# Expects SCRIPT_DIR to be set by the caller.
|
|
4
|
+
|
|
5
|
+
# Load specific variables from .env (safe extraction, no code execution)
|
|
6
|
+
_load_env_var() {
|
|
7
|
+
local var_name="$1"
|
|
8
|
+
local env_file="${ENV_FILE:-$SCRIPT_DIR/.env}"
|
|
9
|
+
if [ -f "$env_file" ]; then
|
|
10
|
+
grep "^${var_name}=" "$env_file" 2>/dev/null | cut -d'=' -f2- || true
|
|
11
|
+
fi
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
# Read a value from egregore.json
|
|
15
|
+
_config_val() {
|
|
16
|
+
local key="$1"
|
|
17
|
+
local default="${2:-}"
|
|
18
|
+
local config="${CONFIG:-$SCRIPT_DIR/egregore.json}"
|
|
19
|
+
if [ -f "$config" ]; then
|
|
20
|
+
local val
|
|
21
|
+
val=$(jq -r ".${key} // empty" "$config" 2>/dev/null || true)
|
|
22
|
+
echo "${val:-$default}"
|
|
23
|
+
else
|
|
24
|
+
echo "$default"
|
|
25
|
+
fi
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# Get base branch for a repo. Reads base_branch from egregore.json repos[] config.
|
|
29
|
+
# Defaults to "develop" for backwards compatibility (existing egregores use develop).
|
|
30
|
+
# Only returns something other than "develop" when base_branch is explicitly set
|
|
31
|
+
# (e.g., for teams who added existing repos with a different default branch).
|
|
32
|
+
# Usage: _get_base_branch "repo-name" (omit for core repo)
|
|
33
|
+
_get_base_branch() {
|
|
34
|
+
local repo_name="${1:-}"
|
|
35
|
+
local config="${CONFIG:-$SCRIPT_DIR/egregore.json}"
|
|
36
|
+
|
|
37
|
+
# Core egregore repo — always develop
|
|
38
|
+
if [ -z "$repo_name" ]; then
|
|
39
|
+
echo "develop"
|
|
40
|
+
return
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# Managed repo — check egregore.json for explicit base_branch
|
|
44
|
+
if [ -f "$config" ]; then
|
|
45
|
+
local base
|
|
46
|
+
base=$(jq -r --arg name "$repo_name" \
|
|
47
|
+
'(.repos[]? // empty) | select((if type == "object" then .name else . end) == $name) | if type == "object" then .base_branch // empty else empty end' \
|
|
48
|
+
"$config" 2>/dev/null || true)
|
|
49
|
+
if [ -n "$base" ]; then
|
|
50
|
+
echo "$base"
|
|
51
|
+
return
|
|
52
|
+
fi
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
# No explicit config — default to develop (backwards compatible)
|
|
56
|
+
echo "develop"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# Detect local vs connected mode
|
|
60
|
+
_detect_mode() {
|
|
61
|
+
local config="${CONFIG:-$SCRIPT_DIR/egregore.json}"
|
|
62
|
+
local mode
|
|
63
|
+
mode=$(jq -r '.mode // empty' "$config" 2>/dev/null || true)
|
|
64
|
+
local api_url
|
|
65
|
+
api_url=$(jq -r '.api_url // empty' "$config" 2>/dev/null || true)
|
|
66
|
+
if [ "$mode" = "local" ] || [ -z "$api_url" ]; then
|
|
67
|
+
echo "local"
|
|
68
|
+
else
|
|
69
|
+
echo "connected"
|
|
70
|
+
fi
|
|
71
|
+
}
|