create-egregore 0.19.32 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/egregore-launcher.js +118 -13
- package/bin/cli.js +48 -3
- package/lib/connect-runtime.js +4 -2
- package/lib/local.js +15 -1
- package/lib/prime-runtime.js +208 -0
- package/lib/setup.js +45 -3
- package/package.json +5 -4
- package/runtime/codex/.codex/skills/ingest-notion/SKILL.md +29 -0
- package/runtime/codex/.codex/skills/notion-connect/SKILL.md +29 -0
- package/runtime/codex/.codex/skills/teams-connect/SKILL.md +29 -0
- package/runtime/codex/.codex/spec-manifest.json +3 -3
- package/runtime/codex/AGENTS.md +11 -4
- package/runtime/codex/bin/activity-data.sh +4 -8
- package/runtime/codex/bin/attendant.sh +13 -0
- package/runtime/codex/bin/autosave.sh +27 -1
- package/runtime/codex/bin/codex-render-spec.mjs +6 -0
- package/runtime/codex/bin/codex-session-start.sh +8 -1
- package/runtime/codex/bin/connect-refresh.sh +33 -0
- package/runtime/codex/bin/graph-batch.sh +25 -6
- package/runtime/codex/bin/graph-op.sh +5 -4
- package/runtime/codex/bin/graph.sh +31 -5
- package/runtime/codex/bin/ingest.py +81 -5
- package/runtime/codex/bin/lib/context.sh +26 -1
- package/runtime/codex/bin/lib/greeting.sh +4 -1
- package/runtime/codex/bin/lib/worktree-links.sh +25 -0
- package/runtime/codex/manifest.json +8 -1
- package/runtime/pi/.codex/skills/ingest-notion/SKILL.md +29 -0
- package/runtime/pi/.codex/skills/notion-connect/SKILL.md +29 -0
- package/runtime/pi/.codex/skills/teams-connect/SKILL.md +29 -0
- package/runtime/pi/.codex/spec-manifest.json +3 -3
- package/runtime/pi/.pi/APPEND_SYSTEM.md +3 -2
- package/runtime/pi/.pi/extensions/egregore.ts +17 -6
- package/runtime/pi/.pi/spec-manifest.json +2 -2
- package/runtime/pi/AGENTS.md +11 -4
- package/runtime/pi/bin/activity-data.sh +4 -8
- package/runtime/pi/bin/attendant.sh +13 -0
- package/runtime/pi/bin/autosave.sh +27 -1
- package/runtime/pi/bin/codex-render-spec.mjs +6 -0
- package/runtime/pi/bin/codex-session-start.sh +8 -1
- package/runtime/pi/bin/connect-refresh.sh +33 -0
- package/runtime/pi/bin/graph-batch.sh +25 -6
- package/runtime/pi/bin/graph-op.sh +5 -4
- package/runtime/pi/bin/graph.sh +31 -5
- package/runtime/pi/bin/ingest.py +81 -5
- package/runtime/pi/bin/lib/context.sh +26 -1
- package/runtime/pi/bin/lib/greeting.sh +4 -1
- package/runtime/pi/bin/lib/worktree-links.sh +25 -0
- package/runtime/pi/bin/pi-branch-policy.mjs +138 -1
- package/runtime/pi/bin/pi-product-workflows.mjs +35 -7
- package/runtime/pi/manifest.json +8 -1
- package/runtime/prime/.claude/context/notification-consent.md +66 -0
- package/runtime/prime/.codex/config.toml +17 -0
- package/runtime/prime/.codex/hooks/branch-guard.js +461 -0
- package/runtime/prime/.codex/hooks/search-hint.js +67 -0
- package/runtime/prime/.codex/hooks.json +30 -0
- package/runtime/prime/.codex/skills/activity/SKILL.md +82 -0
- package/runtime/prime/.codex/skills/announce/SKILL.md +67 -0
- package/runtime/prime/.codex/skills/archive/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/branch/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/checkup/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/commit/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/contribute/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/create-skill/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/dark-mode/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/dashboard/SKILL.md +59 -0
- package/runtime/prime/.codex/skills/deep-reflect/SKILL.md +109 -0
- package/runtime/prime/.codex/skills/delete-user/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/emissary/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/handoff/SKILL.md +166 -0
- package/runtime/prime/.codex/skills/harvest/QUESTION_PALETTE.md +76 -0
- package/runtime/prime/.codex/skills/harvest/SKILL.md +131 -0
- package/runtime/prime/.codex/skills/ingest/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/ingest-notion/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/ingest-user-interview/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/invite/SKILL.md +44 -0
- package/runtime/prime/.codex/skills/issue/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/me/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/meeting/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/note/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/notion-connect/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/onboarding/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/pr/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/pull/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/push/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/quest/SKILL.md +80 -0
- package/runtime/prime/.codex/skills/reflect/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/review-pr/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/save/SKILL.md +125 -0
- package/runtime/prime/.codex/skills/slack-connect/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/summon/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/sync-repos/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/teams-connect/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/telegram-connect/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/telemetry/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/test/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/the-spiral/SKILL.md +84 -0
- package/runtime/prime/.codex/skills/tui-design/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/tutorial/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/update/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/view/SKILL.md +156 -0
- package/runtime/prime/.codex/skills/view/agents/openai.yaml +4 -0
- package/runtime/prime/.codex/skills/view/assets/compose-scaffold.html +221 -0
- package/runtime/prime/.codex/skills/visual-explain/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/wrap/SKILL.md +58 -0
- package/runtime/prime/.codex/spec-manifest.json +132 -0
- package/runtime/prime/.prime/agent/APPEND_SYSTEM.md +393 -0
- package/runtime/prime/.prime/agent/extensions/egregore.ts +460 -0
- package/runtime/prime/.prime/agent/settings.json +4 -0
- package/runtime/prime/.prime/agent/skills/egregore/SKILL.md +34 -0
- package/runtime/prime/.prime/agent/skills/egregore/pyproject.toml +13 -0
- package/runtime/prime/.prime/agent/skills/egregore/src/egregore/__init__.py +156 -0
- package/runtime/prime/.prime/agent/spec-manifest.json +12 -0
- package/runtime/prime/AGENTS.md +489 -0
- package/runtime/prime/bin/activity-data.sh +238 -0
- package/runtime/prime/bin/agent.sh +775 -0
- package/runtime/prime/bin/attendant.sh +707 -0
- package/runtime/prime/bin/autosave.sh +112 -0
- package/runtime/prime/bin/capture-reconcile.sh +69 -0
- package/runtime/prime/bin/capture-run.sh +341 -0
- package/runtime/prime/bin/codex-install-skills.sh +78 -0
- package/runtime/prime/bin/codex-render-spec.mjs +440 -0
- package/runtime/prime/bin/codex-session-start.sh +265 -0
- package/runtime/prime/bin/codex-skill-render.mjs +347 -0
- package/runtime/prime/bin/codex-sync-skills.sh +232 -0
- package/runtime/prime/bin/connect-refresh.sh +33 -0
- package/runtime/prime/bin/dashboard-data.sh +178 -0
- package/runtime/prime/bin/graph-batch.sh +79 -0
- package/runtime/prime/bin/graph-op.sh +1102 -0
- package/runtime/prime/bin/graph-wal.sh +229 -0
- package/runtime/prime/bin/graph.sh +223 -0
- package/runtime/prime/bin/handoff-pr-backfill.sh +86 -0
- package/runtime/prime/bin/handoff-run.sh +671 -0
- package/runtime/prime/bin/handoff-save-egregore.sh +212 -0
- package/runtime/prime/bin/index-handoff.sh +322 -0
- package/runtime/prime/bin/ingest-graph.sh +80 -0
- package/runtime/prime/bin/ingest-surface.html +495 -0
- package/runtime/prime/bin/ingest.py +756 -0
- package/runtime/prime/bin/ingest.sh +279 -0
- package/runtime/prime/bin/ingest_graph.py +856 -0
- package/runtime/prime/bin/ingest_surface.py +544 -0
- package/runtime/prime/bin/lib/artifact-id.sh +46 -0
- package/runtime/prime/bin/lib/config.sh +116 -0
- package/runtime/prime/bin/lib/context.sh +621 -0
- package/runtime/prime/bin/lib/dashboard-artifact.sh +242 -0
- package/runtime/prime/bin/lib/ensure-develop.sh +183 -0
- package/runtime/prime/bin/lib/git-sync.sh +406 -0
- package/runtime/prime/bin/lib/greeting.sh +519 -0
- package/runtime/prime/bin/lib/hash.sh +13 -0
- package/runtime/prime/bin/lib/identity.sh +213 -0
- package/runtime/prime/bin/lib/metrics.sh +374 -0
- package/runtime/prime/bin/lib/noncode.sh +31 -0
- package/runtime/prime/bin/lib/permissions.sh +106 -0
- package/runtime/prime/bin/lib/time.sh +56 -0
- package/runtime/prime/bin/lib/worktree-links.sh +79 -0
- package/runtime/prime/bin/notify.sh +636 -0
- package/runtime/prime/bin/person.py +630 -0
- package/runtime/prime/bin/person.sh +448 -0
- package/runtime/prime/bin/pi-branch-policy.mjs +312 -0
- package/runtime/prime/bin/pi-product-workflows.mjs +394 -0
- package/runtime/prime/bin/prime-render-spec.mjs +123 -0
- package/runtime/prime/bin/prime-session-start.sh +24 -0
- package/runtime/prime/bin/publish-artifact.sh +271 -0
- package/runtime/prime/bin/publish-references.sh +144 -0
- package/runtime/prime/bin/repo-state.sh +96 -0
- package/runtime/prime/bin/search.sh +240 -0
- package/runtime/prime/bin/session-autosave.sh +209 -0
- package/runtime/prime/bin/session-log.sh +199 -0
- package/runtime/prime/bin/settings.sh +304 -0
- package/runtime/prime/bin/telemetry.sh +391 -0
- package/runtime/prime/bin/worktree-create.sh +128 -0
- package/runtime/prime/bin/worktree-remove.sh +50 -0
- package/runtime/prime/bin/worktree.sh +198 -0
- package/runtime/prime/manifest.json +178 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# autosave.sh — per-user autosave settings. The consent surface for ambient
|
|
3
|
+
# session capture (see bin/session-autosave.sh, docs/autosave.md).
|
|
4
|
+
#
|
|
5
|
+
# Settings live in .egregore-state.json (gitignored, personal — same pattern
|
|
6
|
+
# as the transcript_sharing consent flag):
|
|
7
|
+
# autosave_enabled true|false ambient capture on/off (default FALSE — opt-in experiment)
|
|
8
|
+
# autosave_scope all|handoffs what it touches (default all)
|
|
9
|
+
# all = memory repo + core-repo non-coding work
|
|
10
|
+
# handoffs = memory repo only; core repo never touched
|
|
11
|
+
# ambiently
|
|
12
|
+
# autosave_publish gate|auto what happens to the PR (default gate)
|
|
13
|
+
# gate = PR opened, merge waits for your explicit word
|
|
14
|
+
# auto = PR merged to develop automatically
|
|
15
|
+
#
|
|
16
|
+
# Usage:
|
|
17
|
+
# autosave.sh status
|
|
18
|
+
# autosave.sh on | off
|
|
19
|
+
# autosave.sh scope all|handoffs
|
|
20
|
+
# autosave.sh publish gate|auto
|
|
21
|
+
# autosave.sh merge # the consent act: merge YOUR staged auto-save PRs
|
|
22
|
+
#
|
|
23
|
+
# Exposed to Claude via the /autosave skill and to Codex/Pi via
|
|
24
|
+
# `bin/agent.sh autosave ...`.
|
|
25
|
+
|
|
26
|
+
set -uo pipefail
|
|
27
|
+
|
|
28
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
29
|
+
STATE_FILE="$SCRIPT_DIR/.egregore-state.json"
|
|
30
|
+
|
|
31
|
+
# The attendant runs against the MAIN checkout and reads its state file, so
|
|
32
|
+
# ambient-capture settings must be written there — a worktree keeps its own
|
|
33
|
+
# copy, and writing the flag into it would silently do nothing.
|
|
34
|
+
MAIN_STATE_DIR="$SCRIPT_DIR"
|
|
35
|
+
if [ -f "$SCRIPT_DIR/.git" ]; then
|
|
36
|
+
_WT_GITDIR=$(sed 's/^gitdir: //' "$SCRIPT_DIR/.git" 2>/dev/null || true)
|
|
37
|
+
[ -n "$_WT_GITDIR" ] && MAIN_STATE_DIR=$(cd "$_WT_GITDIR/../../.." 2>/dev/null && pwd || echo "$SCRIPT_DIR")
|
|
38
|
+
fi
|
|
39
|
+
MAIN_STATE_FILE="$MAIN_STATE_DIR/.egregore-state.json"
|
|
40
|
+
[ -f "$MAIN_STATE_FILE" ] || MAIN_STATE_FILE="$STATE_FILE"
|
|
41
|
+
|
|
42
|
+
[ -f "$STATE_FILE" ] || echo '{}' > "$STATE_FILE"
|
|
43
|
+
|
|
44
|
+
# NB: jq's `//` swallows false — status would show "on" for enabled:false.
|
|
45
|
+
# Test key presence instead.
|
|
46
|
+
get() {
|
|
47
|
+
local v
|
|
48
|
+
v=$(jq -r "if ($1 != null) then ($1|tostring) else empty end" "$STATE_FILE" 2>/dev/null)
|
|
49
|
+
[ -n "$v" ] && echo "$v" || echo "$2"
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
set_key() { # key value(json-encoded)
|
|
53
|
+
jq ".$1 = $2" "$STATE_FILE" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE"
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
_set_main() { # key value(json-encoded) — writes where the attendant reads
|
|
57
|
+
jq ".$1 = $2" "$MAIN_STATE_FILE" > "$MAIN_STATE_FILE.tmp" \
|
|
58
|
+
&& mv "$MAIN_STATE_FILE.tmp" "$MAIN_STATE_FILE"
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
status() {
|
|
62
|
+
local enabled scope publish
|
|
63
|
+
enabled=$(get '.autosave_enabled' 'false')
|
|
64
|
+
scope=$(get '.autosave_scope' 'all')
|
|
65
|
+
publish=$(get '.autosave_publish' 'gate')
|
|
66
|
+
echo "autosave: $([ "$enabled" = "true" ] && echo on || echo "off (opt-in — 'autosave on' to join the experiment)")"
|
|
67
|
+
echo "scope: $scope (all = handoffs + artifacts + docs · handoffs = memory repo only)"
|
|
68
|
+
echo "publish: $publish (gate = PRs wait for your merge · auto = merge to develop automatically)"
|
|
69
|
+
echo "ripcord: $(jq -r 'if (.ripcord_enabled != null) then (.ripcord_enabled|tostring) else "true" end' "$MAIN_STATE_FILE" 2>/dev/null || echo true) (crash-recovery notes + branch auto-push when a session dies without /wrap)"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
case "${1:-status}" in
|
|
73
|
+
status) status ;;
|
|
74
|
+
on) set_key autosave_enabled true && echo "autosave: on" ;;
|
|
75
|
+
off) set_key autosave_enabled false && echo "autosave: off — nothing is captured ambiently; /save and /handoff still work" ;;
|
|
76
|
+
scope)
|
|
77
|
+
case "${2:-}" in
|
|
78
|
+
all|handoffs) set_key autosave_scope "\"$2\"" && echo "scope: $2" ;;
|
|
79
|
+
*) echo "usage: autosave.sh scope all|handoffs" >&2; exit 2 ;;
|
|
80
|
+
esac ;;
|
|
81
|
+
ripcord)
|
|
82
|
+
# Ambient crash-recovery capture (the attendant): ripcord notes AND
|
|
83
|
+
# auto-push of dirty task branches. Separate from the autosave
|
|
84
|
+
# experiment above — this one defaults ON.
|
|
85
|
+
case "${2:-}" in
|
|
86
|
+
on) _set_main ripcord_enabled true && echo "ripcord: on — dead sessions leave a recovery note" ;;
|
|
87
|
+
off) _set_main ripcord_enabled false && echo "ripcord: off — no ambient notes, no branch auto-push" ;;
|
|
88
|
+
*) echo "usage: autosave.sh ripcord on|off" >&2; exit 2 ;;
|
|
89
|
+
esac ;;
|
|
90
|
+
publish)
|
|
91
|
+
case "${2:-}" in
|
|
92
|
+
gate|auto) set_key autosave_publish "\"$2\"" && echo "publish: $2" ;;
|
|
93
|
+
*) echo "usage: autosave.sh publish gate|auto" >&2; exit 2 ;;
|
|
94
|
+
esac ;;
|
|
95
|
+
merge)
|
|
96
|
+
# The consent act under publish=gate: merge this user's own staged
|
|
97
|
+
# Auto-save PRs. Never touches anyone else's.
|
|
98
|
+
command -v gh >/dev/null 2>&1 || { echo "gh not available" >&2; exit 1; }
|
|
99
|
+
GH_SELF=$(get '.github_username' '')
|
|
100
|
+
[ -n "$GH_SELF" ] || { echo "no github_username in .egregore-state.json" >&2; exit 1; }
|
|
101
|
+
PRS=$(gh pr list --state open --author "$GH_SELF" --limit 20 \
|
|
102
|
+
--json number,title --jq '.[] | select(.title | startswith("Auto-save:")) | .number' 2>/dev/null)
|
|
103
|
+
[ -n "$PRS" ] || { echo "no auto-saves waiting"; exit 0; }
|
|
104
|
+
for n in $PRS; do
|
|
105
|
+
if gh pr merge "$n" --merge >/dev/null 2>&1; then
|
|
106
|
+
echo "✓ merged auto-save PR #$n → develop"
|
|
107
|
+
else
|
|
108
|
+
echo "✗ PR #$n did not merge (conflict with develop?) — it stays open"
|
|
109
|
+
fi
|
|
110
|
+
done ;;
|
|
111
|
+
*) echo "usage: autosave.sh [status|on|off|scope all|handoffs|publish gate|auto|merge|ripcord on|off]" >&2; exit 2 ;;
|
|
112
|
+
esac
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Detached reconciliation worker for explicit captures.
|
|
5
|
+
# The foreground path only appends to graph-wal.sh; this worker pays network
|
|
6
|
+
# latency and retries queued writes. It may propose completion notifications,
|
|
7
|
+
# but detached reconciliation never approves or dispatches them.
|
|
8
|
+
|
|
9
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
10
|
+
MODE="$(jq -r '.mode // "connected"' "$SCRIPT_DIR/egregore.json" 2>/dev/null || echo connected)"
|
|
11
|
+
[ "$MODE" = "connected" ] || exit 0
|
|
12
|
+
|
|
13
|
+
SESSION_ID=""
|
|
14
|
+
AUTHOR=""
|
|
15
|
+
SUMMARY=""
|
|
16
|
+
while [ $# -gt 0 ]; do
|
|
17
|
+
case "$1" in
|
|
18
|
+
--session-id) SESSION_ID="${2:-}"; shift 2 ;;
|
|
19
|
+
--author) AUTHOR="${2:-}"; shift 2 ;;
|
|
20
|
+
--summary) SUMMARY="${2:-}"; shift 2 ;;
|
|
21
|
+
*) echo "Unknown reconcile option: $1" >&2; exit 1 ;;
|
|
22
|
+
esac
|
|
23
|
+
done
|
|
24
|
+
[ -n "$SESSION_ID" ] || exit 0
|
|
25
|
+
|
|
26
|
+
# Applies Session capture first, then the idempotent handoff transition.
|
|
27
|
+
bash "$SCRIPT_DIR/bin/graph-wal.sh" drain >/dev/null 2>&1 || exit 0
|
|
28
|
+
|
|
29
|
+
# Claim completion notifications atomically. A stale claim can be retried;
|
|
30
|
+
# successful sends are permanently marked on the IMPLEMENTS relationship.
|
|
31
|
+
# shellcheck disable=SC2016 # $name tokens are Cypher parameters.
|
|
32
|
+
CLAIM_CYPHER='
|
|
33
|
+
MATCH (impl:Session {id: $sessionId})-[r:IMPLEMENTS]->(ho:Session)-[:BY]->(origin:Person)
|
|
34
|
+
WHERE ho.handoffStatus = "done"
|
|
35
|
+
AND r.completionNotifiedAt IS NULL
|
|
36
|
+
AND r.completionNotifyProposedAt IS NULL
|
|
37
|
+
AND (r.completionNotifyClaimedAt IS NULL
|
|
38
|
+
OR r.completionNotifyClaimedAt < datetime() - duration("PT1H"))
|
|
39
|
+
SET r.completionNotifyClaimedAt = datetime()
|
|
40
|
+
RETURN ho.id AS handoffId, ho.topic AS topic,
|
|
41
|
+
coalesce(origin.github, origin.name) AS recipient'
|
|
42
|
+
PARAMS="$(jq -nc --arg sessionId "$SESSION_ID" '{sessionId:$sessionId}')"
|
|
43
|
+
CLAIMS="$(bash "$SCRIPT_DIR/bin/graph.sh" query "$CLAIM_CYPHER" "$PARAMS" 2>/dev/null || echo '{}')"
|
|
44
|
+
|
|
45
|
+
printf '%s' "$CLAIMS" | jq -r '.values[]? | @tsv' 2>/dev/null |
|
|
46
|
+
while IFS=$'\t' read -r handoff_id topic recipient; do
|
|
47
|
+
[ -n "$handoff_id" ] && [ -n "$recipient" ] || continue
|
|
48
|
+
message="${AUTHOR:-A teammate} completed your handoff '${topic:-$handoff_id}'"
|
|
49
|
+
[ -n "$SUMMARY" ] && message="${message} — $(printf '%s' "$SUMMARY" | tr '\n' ' ' | cut -c1-180)"
|
|
50
|
+
|
|
51
|
+
notify_result="$(bash "$SCRIPT_DIR/bin/notify.sh" plan send "$recipient" "$message" 2>/dev/null || echo '{}')"
|
|
52
|
+
if printf '%s' "$notify_result" | jq -e '.status == "approval_required"' >/dev/null 2>&1; then
|
|
53
|
+
bash "$SCRIPT_DIR/bin/graph.sh" query '
|
|
54
|
+
MATCH (impl:Session {id: $sessionId})-[r:IMPLEMENTS]->(ho:Session {id: $handoffId})
|
|
55
|
+
SET r.completionNotifyProposedAt = datetime(),
|
|
56
|
+
r.completionNotifyClaimedAt = null
|
|
57
|
+
RETURN ho.id AS id
|
|
58
|
+
' "$(jq -nc --arg sessionId "$SESSION_ID" --arg handoffId "$handoff_id" \
|
|
59
|
+
'{sessionId:$sessionId,handoffId:$handoffId}')" >/dev/null 2>&1 || true
|
|
60
|
+
else
|
|
61
|
+
bash "$SCRIPT_DIR/bin/graph.sh" query '
|
|
62
|
+
MATCH (impl:Session {id: $sessionId})-[r:IMPLEMENTS]->(ho:Session {id: $handoffId})
|
|
63
|
+
SET r.completionNotifyClaimedAt = null,
|
|
64
|
+
r.completionNotifyProposalFailedAt = datetime()
|
|
65
|
+
RETURN ho.id AS id
|
|
66
|
+
' "$(jq -nc --arg sessionId "$SESSION_ID" --arg handoffId "$handoff_id" \
|
|
67
|
+
'{sessionId:$sessionId,handoffId:$handoffId}')" >/dev/null 2>&1 || true
|
|
68
|
+
fi
|
|
69
|
+
done
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Shared capture engine for the three ways an Egregore session leaves a trace.
|
|
5
|
+
#
|
|
6
|
+
# addressed /handoff: rich team handoff (delegates to the addressed worker)
|
|
7
|
+
# personal /wrap: explicit personal close
|
|
8
|
+
# baseline SessionEnd: automatic, loss-prevention trace
|
|
9
|
+
#
|
|
10
|
+
# Lifecycle reconciliation is never performed on the caller's critical path.
|
|
11
|
+
# Connected-mode captures append idempotent graph statements to graph-wal.sh
|
|
12
|
+
# and optionally start capture-reconcile.sh as a detached worker.
|
|
13
|
+
|
|
14
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
15
|
+
CONFIG="$SCRIPT_DIR/egregore.json"
|
|
16
|
+
CAPTURE_SCHEMA="egregore-capture/v1"
|
|
17
|
+
|
|
18
|
+
queue_handoff_completion() {
|
|
19
|
+
local session_id="$1"
|
|
20
|
+
[ -n "$session_id" ] || return 0
|
|
21
|
+
# shellcheck disable=SC2016 # $name tokens are Cypher parameters.
|
|
22
|
+
local cypher='
|
|
23
|
+
MATCH (impl:Session {id: $sessionId})-[:IMPLEMENTS]->(ho:Session)
|
|
24
|
+
MATCH (impl)-[:BY]->(recipient:Person)
|
|
25
|
+
MATCH (ho)-[:HANDED_TO]->(recipient)
|
|
26
|
+
WHERE coalesce(ho.handoffStatus, "pending") IN ["pending","read","claimed"]
|
|
27
|
+
AND coalesce(ho.handoffLifecycleVersion, 0) >= 1
|
|
28
|
+
WITH DISTINCT ho, size([(ho)-[:HANDED_TO]->(:Person) | 1]) AS recipientCount
|
|
29
|
+
WHERE recipientCount = 1
|
|
30
|
+
SET ho.handoffStatus = "done",
|
|
31
|
+
ho.handoffDoneAt = coalesce(ho.handoffDoneAt, datetime()),
|
|
32
|
+
ho.handoffUpdatedAt = datetime(),
|
|
33
|
+
ho.handoffLifecycleReason = "single_recipient_implemented",
|
|
34
|
+
ho.handoffLifecycleVersion = 1
|
|
35
|
+
RETURN ho.id AS id'
|
|
36
|
+
local params
|
|
37
|
+
params="$(jq -nc --arg sessionId "$session_id" '{sessionId:$sessionId}')"
|
|
38
|
+
GRAPH_WAL_LOCK_ATTEMPTS=1 \
|
|
39
|
+
bash "$SCRIPT_DIR/bin/graph-wal.sh" append "$cypher" "$params" >/dev/null 2>&1 || true
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
start_reconcile() {
|
|
43
|
+
local session_id="$1"
|
|
44
|
+
local author="$2"
|
|
45
|
+
local summary="$3"
|
|
46
|
+
[ -n "$session_id" ] || return 0
|
|
47
|
+
(
|
|
48
|
+
bash "$SCRIPT_DIR/bin/capture-reconcile.sh" \
|
|
49
|
+
--session-id "$session_id" \
|
|
50
|
+
--author "$author" \
|
|
51
|
+
--summary "$summary" >/dev/null 2>&1 &
|
|
52
|
+
) >/dev/null 2>&1
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
usage() {
|
|
56
|
+
cat <<'EOF'
|
|
57
|
+
Usage:
|
|
58
|
+
capture-run.sh --mode addressed [handoff-run options] < handoff.md
|
|
59
|
+
capture-run.sh --mode personal|baseline --author HANDLE --topic TOPIC
|
|
60
|
+
--summary SUMMARY [--session-id ID] [--branch BRANCH] [--date ISO]
|
|
61
|
+
[--duration TEXT] [--open-threads-json JSON] [--no-push]
|
|
62
|
+
[--async-push] [--no-reconcile] < details.md
|
|
63
|
+
EOF
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
|
|
67
|
+
usage
|
|
68
|
+
exit 0
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
CAPTURE_MODE=""
|
|
72
|
+
if [ "${1:-}" = "--mode" ]; then
|
|
73
|
+
CAPTURE_MODE="${2:-}"
|
|
74
|
+
shift 2
|
|
75
|
+
fi
|
|
76
|
+
[ -n "$CAPTURE_MODE" ] || {
|
|
77
|
+
echo "Missing --mode (addressed|personal|baseline)" >&2
|
|
78
|
+
exit 1
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
case "$CAPTURE_MODE" in
|
|
82
|
+
addressed)
|
|
83
|
+
# Keep the mature publishing/notification worker intact behind the shared
|
|
84
|
+
# entry point. It continues writing its compatibility result file.
|
|
85
|
+
ADDRESSED_AUTHOR=""
|
|
86
|
+
ADDRESSED_TOPIC=""
|
|
87
|
+
PREV=""
|
|
88
|
+
for VALUE in "$@"; do
|
|
89
|
+
if [ "$PREV" = "--author" ]; then ADDRESSED_AUTHOR="$VALUE"; fi
|
|
90
|
+
if [ "$PREV" = "--topic" ]; then ADDRESSED_TOPIC="$VALUE"; fi
|
|
91
|
+
PREV="$VALUE"
|
|
92
|
+
done
|
|
93
|
+
|
|
94
|
+
if bash "$SCRIPT_DIR/bin/handoff-run.sh" "$@"; then
|
|
95
|
+
status=0
|
|
96
|
+
else
|
|
97
|
+
status=$?
|
|
98
|
+
fi
|
|
99
|
+
old_result="${TMPDIR:-/tmp}/handoff-run-result.json"
|
|
100
|
+
capture_result="${TMPDIR:-/tmp}/capture-run-result.json"
|
|
101
|
+
if [ -s "$old_result" ]; then
|
|
102
|
+
jq --arg schema "$CAPTURE_SCHEMA" --arg captureMode "$CAPTURE_MODE" \
|
|
103
|
+
'. + {captureSchema:$schema,captureMode:$captureMode}' \
|
|
104
|
+
"$old_result" > "$capture_result" 2>/dev/null || cp "$old_result" "$capture_result"
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
# Addressed capture is also explicit session-close evidence. Queue the
|
|
108
|
+
# same guarded transition as /wrap after the addressed worker has marked
|
|
109
|
+
# the current Session handed_off.
|
|
110
|
+
ADDRESSED_CONFIG_MODE="$(jq -r '.mode // "connected"' "$CONFIG" 2>/dev/null || echo connected)"
|
|
111
|
+
if [ "$status" = "0" ] && [ "$ADDRESSED_CONFIG_MODE" = "connected" ]; then
|
|
112
|
+
CURRENT_SID="$(cat "$SCRIPT_DIR/.egregore-session-id" 2>/dev/null || true)"
|
|
113
|
+
if [ -z "$CURRENT_SID" ]; then
|
|
114
|
+
PROJ_HASH="$(printf '%s' "$SCRIPT_DIR" | md5 2>/dev/null || printf '%s' "$SCRIPT_DIR" | md5sum 2>/dev/null | cut -d' ' -f1)"
|
|
115
|
+
CURRENT_SID="$(cat "$HOME/.egregore/session-${PROJ_HASH}.id" 2>/dev/null || true)"
|
|
116
|
+
fi
|
|
117
|
+
queue_handoff_completion "$CURRENT_SID"
|
|
118
|
+
start_reconcile "$CURRENT_SID" "$ADDRESSED_AUTHOR" "$ADDRESSED_TOPIC"
|
|
119
|
+
fi
|
|
120
|
+
exit "$status"
|
|
121
|
+
;;
|
|
122
|
+
personal|baseline) ;;
|
|
123
|
+
*)
|
|
124
|
+
echo "Invalid --mode: $CAPTURE_MODE (addressed|personal|baseline)" >&2
|
|
125
|
+
exit 1
|
|
126
|
+
;;
|
|
127
|
+
esac
|
|
128
|
+
|
|
129
|
+
AUTHOR=""
|
|
130
|
+
DISPLAY_NAME=""
|
|
131
|
+
TOPIC=""
|
|
132
|
+
SUMMARY=""
|
|
133
|
+
SESSION_ID=""
|
|
134
|
+
BRANCH=""
|
|
135
|
+
CAPTURE_DATE=""
|
|
136
|
+
DURATION=""
|
|
137
|
+
OPEN_THREADS_JSON="[]"
|
|
138
|
+
NO_PUSH=0
|
|
139
|
+
ASYNC_PUSH=0
|
|
140
|
+
NO_RECONCILE=0
|
|
141
|
+
|
|
142
|
+
while [ $# -gt 0 ]; do
|
|
143
|
+
case "$1" in
|
|
144
|
+
--author) AUTHOR="${2:-}"; shift 2 ;;
|
|
145
|
+
--display-name) DISPLAY_NAME="${2:-}"; shift 2 ;;
|
|
146
|
+
--topic) TOPIC="${2:-}"; shift 2 ;;
|
|
147
|
+
--summary) SUMMARY="${2:-}"; shift 2 ;;
|
|
148
|
+
--session-id) SESSION_ID="${2:-}"; shift 2 ;;
|
|
149
|
+
--branch) BRANCH="${2:-}"; shift 2 ;;
|
|
150
|
+
--date) CAPTURE_DATE="${2:-}"; shift 2 ;;
|
|
151
|
+
--duration) DURATION="${2:-}"; shift 2 ;;
|
|
152
|
+
--open-threads-json) OPEN_THREADS_JSON="${2:-[]}"; shift 2 ;;
|
|
153
|
+
--no-push) NO_PUSH=1; shift ;;
|
|
154
|
+
--async-push) ASYNC_PUSH=1; shift ;;
|
|
155
|
+
--no-reconcile) NO_RECONCILE=1; shift ;;
|
|
156
|
+
*) echo "Unknown capture option: $1" >&2; exit 1 ;;
|
|
157
|
+
esac
|
|
158
|
+
done
|
|
159
|
+
|
|
160
|
+
AUTHOR="$(printf '%s' "$AUTHOR" | tr -cd 'a-zA-Z0-9_-')"
|
|
161
|
+
[ -n "$AUTHOR" ] || { echo "Missing --author" >&2; exit 1; }
|
|
162
|
+
[ -n "$TOPIC" ] || { echo "Missing --topic" >&2; exit 1; }
|
|
163
|
+
[ -n "$SUMMARY" ] || SUMMARY="$TOPIC"
|
|
164
|
+
if ! printf '%s' "$OPEN_THREADS_JSON" | jq -e 'type == "array"' >/dev/null 2>&1; then
|
|
165
|
+
echo "Invalid --open-threads-json: expected an array" >&2
|
|
166
|
+
exit 1
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
DETAILS="$(cat /dev/stdin 2>/dev/null || true)"
|
|
170
|
+
NOW="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
171
|
+
[ -n "$CAPTURE_DATE" ] || CAPTURE_DATE="$NOW"
|
|
172
|
+
DATE_ONLY="$(printf '%s' "$CAPTURE_DATE" | grep -oE '^[0-9]{4}-[0-9]{2}-[0-9]{2}' || true)"
|
|
173
|
+
[ -n "$DATE_ONLY" ] || DATE_ONLY="$(date +%Y-%m-%d)"
|
|
174
|
+
YYYY_MM="${DATE_ONLY%??}"
|
|
175
|
+
YYYY_MM="${YYYY_MM%-}"
|
|
176
|
+
DD="${DATE_ONLY##*-}"
|
|
177
|
+
[ -n "$BRANCH" ] || BRANCH="$(git -C "$SCRIPT_DIR" branch --show-current 2>/dev/null || true)"
|
|
178
|
+
[ -n "$BRANCH" ] || BRANCH="develop"
|
|
179
|
+
|
|
180
|
+
slugify() {
|
|
181
|
+
printf '%s' "$1" \
|
|
182
|
+
| tr '[:upper:]' '[:lower:]' \
|
|
183
|
+
| sed -E 's/[^a-z0-9]+/-/g; s/^-+|-+$//g' \
|
|
184
|
+
| cut -c1-50
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
SLUG="$(slugify "$TOPIC")"
|
|
188
|
+
[ -n "$SLUG" ] || SLUG="session"
|
|
189
|
+
AUTHOR_LC="$(printf '%s' "$AUTHOR" | tr '[:upper:]' '[:lower:]')"
|
|
190
|
+
|
|
191
|
+
if [ "$CAPTURE_MODE" = "personal" ]; then
|
|
192
|
+
REL_FILE="wraps/${YYYY_MM}/${DD}-${AUTHOR_LC}-${SLUG}.md"
|
|
193
|
+
TITLE_PREFIX="Wrap"
|
|
194
|
+
else
|
|
195
|
+
REL_FILE="sessions/${YYYY_MM}/${DD}-${AUTHOR_LC}-${SLUG}.md"
|
|
196
|
+
TITLE_PREFIX="Session"
|
|
197
|
+
fi
|
|
198
|
+
ABS_FILE="$SCRIPT_DIR/memory/$REL_FILE"
|
|
199
|
+
mkdir -p "$(dirname "$ABS_FILE")"
|
|
200
|
+
|
|
201
|
+
# Never overwrite an existing capture. Session IDs make concurrent same-topic
|
|
202
|
+
# sessions deterministic while retaining the human-readable prefix.
|
|
203
|
+
if [ -e "$ABS_FILE" ]; then
|
|
204
|
+
suffix="$(printf '%s' "${SESSION_ID:-$NOW}" | tr -cd 'a-zA-Z0-9' | tail -c 7)"
|
|
205
|
+
[ -n "$suffix" ] || suffix="2"
|
|
206
|
+
ABS_FILE="${ABS_FILE%.md}-${suffix}.md"
|
|
207
|
+
REL_FILE="${REL_FILE%.md}-${suffix}.md"
|
|
208
|
+
fi
|
|
209
|
+
|
|
210
|
+
{
|
|
211
|
+
printf '# %s: %s\n\n' "$TITLE_PREFIX" "$TOPIC"
|
|
212
|
+
printf '**Capture Schema**: %s\n' "$CAPTURE_SCHEMA"
|
|
213
|
+
printf '**Capture Mode**: %s\n' "$CAPTURE_MODE"
|
|
214
|
+
printf '**Date**: %s\n' "$CAPTURE_DATE"
|
|
215
|
+
printf '**Author**: %s\n' "${DISPLAY_NAME:-$AUTHOR}"
|
|
216
|
+
printf '**To**: %s\n' "$AUTHOR"
|
|
217
|
+
printf '**Branch**: %s\n' "$BRANCH"
|
|
218
|
+
[ -n "$SESSION_ID" ] && printf '**Session**: %s\n' "$SESSION_ID"
|
|
219
|
+
[ -n "$DURATION" ] && printf '**Duration**: %s\n' "$DURATION"
|
|
220
|
+
printf '\n## Summary\n\n%s\n' "$SUMMARY"
|
|
221
|
+
if [ -n "$DETAILS" ]; then
|
|
222
|
+
if [ "$CAPTURE_MODE" = "personal" ]; then
|
|
223
|
+
printf '\n## Notes\n\n%s\n' "$DETAILS"
|
|
224
|
+
else
|
|
225
|
+
printf '\n%s\n' "$DETAILS"
|
|
226
|
+
fi
|
|
227
|
+
fi
|
|
228
|
+
} > "$ABS_FILE"
|
|
229
|
+
|
|
230
|
+
memory_save() {
|
|
231
|
+
local message="$1"
|
|
232
|
+
(
|
|
233
|
+
cd "$SCRIPT_DIR/memory" 2>/dev/null || exit 0
|
|
234
|
+
git add "$REL_FILE" >/dev/null 2>&1 || exit 0
|
|
235
|
+
git commit --only --quiet -m "$message" -- "$REL_FILE" >/dev/null 2>&1 || true
|
|
236
|
+
if git remote get-url origin >/dev/null 2>&1; then
|
|
237
|
+
local _
|
|
238
|
+
for _ in 1 2 3; do
|
|
239
|
+
git pull --rebase origin main --quiet >/dev/null 2>&1 || true
|
|
240
|
+
git push origin main --quiet >/dev/null 2>&1 && exit 0
|
|
241
|
+
sleep 1
|
|
242
|
+
done
|
|
243
|
+
exit 1
|
|
244
|
+
fi
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
MEMORY_STATUS="skipped"
|
|
249
|
+
if [ "$NO_PUSH" = "0" ]; then
|
|
250
|
+
if [ "$ASYNC_PUSH" = "1" ]; then
|
|
251
|
+
( memory_save "Capture: ${AUTHOR} — ${TOPIC}" >/dev/null 2>&1 & ) >/dev/null 2>&1
|
|
252
|
+
MEMORY_STATUS="queued"
|
|
253
|
+
elif memory_save "Capture: ${AUTHOR} — ${TOPIC}"; then
|
|
254
|
+
MEMORY_STATUS="ok"
|
|
255
|
+
else
|
|
256
|
+
MEMORY_STATUS="failed"
|
|
257
|
+
fi
|
|
258
|
+
fi
|
|
259
|
+
|
|
260
|
+
GRAPH_STATUS="skipped"
|
|
261
|
+
MODE="$(jq -r '.mode // "connected"' "$CONFIG" 2>/dev/null || echo connected)"
|
|
262
|
+
if [ "$MODE" = "connected" ] && [ -n "$SESSION_ID" ]; then
|
|
263
|
+
# shellcheck disable=SC2016 # $name tokens are Cypher parameters.
|
|
264
|
+
CAPTURE_CYPHER='
|
|
265
|
+
MERGE (s:Session {id: $sessionId})
|
|
266
|
+
ON CREATE SET s.date = date($date), s.startedAt = datetime($capturedAt)
|
|
267
|
+
SET s.topic = $topic,
|
|
268
|
+
s.summary = $summary,
|
|
269
|
+
s.filePath = $filePath,
|
|
270
|
+
s.branch = $branch,
|
|
271
|
+
s.captureSchema = $captureSchema,
|
|
272
|
+
s.captureMode = $captureMode,
|
|
273
|
+
s.captureUpdatedAt = datetime()
|
|
274
|
+
FOREACH (_ IN CASE WHEN $captureMode = "personal" THEN [1] ELSE [] END |
|
|
275
|
+
SET s.status = "wrapped",
|
|
276
|
+
s.wrappedAt = coalesce(s.wrappedAt, datetime()),
|
|
277
|
+
s.openThreads = $openThreads)
|
|
278
|
+
FOREACH (_ IN CASE WHEN $captureMode = "baseline" THEN [1] ELSE [] END |
|
|
279
|
+
SET s.status = CASE
|
|
280
|
+
WHEN s.status IN ["wrapped","handed_off","completed"] THEN s.status
|
|
281
|
+
ELSE "captured"
|
|
282
|
+
END,
|
|
283
|
+
s.endedAt = coalesce(s.endedAt, datetime()))
|
|
284
|
+
WITH s
|
|
285
|
+
OPTIONAL MATCH (p:Person)
|
|
286
|
+
WHERE toLower(p.name) = toLower($author)
|
|
287
|
+
OR toLower(coalesce(p.github, "")) = toLower($author)
|
|
288
|
+
FOREACH (_ IN CASE WHEN p IS NOT NULL THEN [1] ELSE [] END |
|
|
289
|
+
MERGE (s)-[:BY]->(p))
|
|
290
|
+
RETURN s.id AS id'
|
|
291
|
+
CAPTURE_PARAMS="$(jq -nc \
|
|
292
|
+
--arg sessionId "$SESSION_ID" \
|
|
293
|
+
--arg date "$DATE_ONLY" \
|
|
294
|
+
--arg capturedAt "$NOW" \
|
|
295
|
+
--arg topic "$TOPIC" \
|
|
296
|
+
--arg summary "$SUMMARY" \
|
|
297
|
+
--arg filePath "$REL_FILE" \
|
|
298
|
+
--arg branch "$BRANCH" \
|
|
299
|
+
--arg captureSchema "$CAPTURE_SCHEMA" \
|
|
300
|
+
--arg captureMode "$CAPTURE_MODE" \
|
|
301
|
+
--arg author "$AUTHOR_LC" \
|
|
302
|
+
--argjson openThreads "$OPEN_THREADS_JSON" \
|
|
303
|
+
'{sessionId:$sessionId,date:$date,capturedAt:$capturedAt,topic:$topic,
|
|
304
|
+
summary:$summary,filePath:$filePath,branch:$branch,
|
|
305
|
+
captureSchema:$captureSchema,captureMode:$captureMode,author:$author,
|
|
306
|
+
openThreads:$openThreads}')"
|
|
307
|
+
GRAPH_WAL_LOCK_ATTEMPTS=1 \
|
|
308
|
+
bash "$SCRIPT_DIR/bin/graph-wal.sh" append "$CAPTURE_CYPHER" "$CAPTURE_PARAMS" >/dev/null 2>&1 || true
|
|
309
|
+
GRAPH_STATUS="queued"
|
|
310
|
+
|
|
311
|
+
# An explicit wrap is strong completion evidence. Queue the transition after
|
|
312
|
+
# the Session update; do not query the graph or wait for reconciliation here.
|
|
313
|
+
if [ "$CAPTURE_MODE" = "personal" ]; then
|
|
314
|
+
queue_handoff_completion "$SESSION_ID"
|
|
315
|
+
fi
|
|
316
|
+
|
|
317
|
+
if [ "$NO_RECONCILE" = "0" ] && [ "$CAPTURE_MODE" = "personal" ]; then
|
|
318
|
+
start_reconcile "$SESSION_ID" "$AUTHOR_LC" "$SUMMARY"
|
|
319
|
+
fi
|
|
320
|
+
fi
|
|
321
|
+
|
|
322
|
+
RESULT_FILE="${TMPDIR:-/tmp}/capture-run-result.json"
|
|
323
|
+
jq -nc \
|
|
324
|
+
--arg schema "$CAPTURE_SCHEMA" \
|
|
325
|
+
--arg captureMode "$CAPTURE_MODE" \
|
|
326
|
+
--arg file "$REL_FILE" \
|
|
327
|
+
--arg absFile "$ABS_FILE" \
|
|
328
|
+
--arg sessionId "$SESSION_ID" \
|
|
329
|
+
--arg memoryStatus "$MEMORY_STATUS" \
|
|
330
|
+
--arg graphStatus "$GRAPH_STATUS" \
|
|
331
|
+
'{
|
|
332
|
+
captureSchema:$schema,
|
|
333
|
+
captureMode:$captureMode,
|
|
334
|
+
file:$file,
|
|
335
|
+
absFile:$absFile,
|
|
336
|
+
sessionId:$sessionId,
|
|
337
|
+
memoryStatus:$memoryStatus,
|
|
338
|
+
graphStatus:$graphStatus
|
|
339
|
+
}' > "$RESULT_FILE"
|
|
340
|
+
|
|
341
|
+
printf 'capture: memory/%s\n' "$REL_FILE"
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Make Egregore's project-local Codex skills available without duplicating
|
|
3
|
+
# them in CODEX_HOME.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
7
|
+
SOURCE_DIR="$SCRIPT_DIR/.codex/skills"
|
|
8
|
+
HOOKS_FILE="$SCRIPT_DIR/.codex/hooks.json"
|
|
9
|
+
HOOKS_DIR="$SCRIPT_DIR/.codex/hooks"
|
|
10
|
+
CODEX_HOME_DIR="${CODEX_HOME:-$HOME/.codex}"
|
|
11
|
+
TARGET_DIR="$CODEX_HOME_DIR/skills"
|
|
12
|
+
|
|
13
|
+
[ -d "$SOURCE_DIR" ] || exit 0
|
|
14
|
+
|
|
15
|
+
# Current Codex discovers project skills via .agents/skills (and follows
|
|
16
|
+
# symlinks). The repo commits .agents/skills -> ../.codex/skills; repair it
|
|
17
|
+
# here for checkouts that lost the symlink (zip downloads, some Windows
|
|
18
|
+
# clones). Never clobber a real directory the user created.
|
|
19
|
+
AGENTS_DIR="$SCRIPT_DIR/.agents"
|
|
20
|
+
AGENTS_LINK="$AGENTS_DIR/skills"
|
|
21
|
+
if [ ! -e "$AGENTS_LINK" ] && [ ! -L "$AGENTS_LINK" ]; then
|
|
22
|
+
mkdir -p "$AGENTS_DIR"
|
|
23
|
+
ln -s ../.codex/skills "$AGENTS_LINK" 2>/dev/null || true
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
is_egregore_skill() {
|
|
27
|
+
local file="$1"
|
|
28
|
+
[ -f "$file" ] || return 1
|
|
29
|
+
grep -q 'Egregore' "$file" 2>/dev/null || return 1
|
|
30
|
+
grep -Eq 'bin/agent\.sh|bin/activity-data\.sh|bin/codex-skill-render\.mjs|Native Codex|generated-by: bin/codex-sync-skills\.sh|\.claude/skills' "$file" 2>/dev/null
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
installed=0
|
|
34
|
+
for skill_dir in "$SOURCE_DIR"/*; do
|
|
35
|
+
[ -d "$skill_dir" ] || continue
|
|
36
|
+
[ -f "$skill_dir/SKILL.md" ] || continue
|
|
37
|
+
installed=$((installed + 1))
|
|
38
|
+
done
|
|
39
|
+
|
|
40
|
+
# Current Codex loads project-local .codex/skills from --cd. Older Egregore
|
|
41
|
+
# builds copied the same skills globally, which makes Codex show duplicates.
|
|
42
|
+
# Default to local-only and remove legacy global copies that are recognizably
|
|
43
|
+
# Egregore-managed. Set EGREGORE_CODEX_INSTALL_GLOBAL=1 for older Codex builds.
|
|
44
|
+
if [ "${EGREGORE_CODEX_INSTALL_GLOBAL:-0}" != "1" ]; then
|
|
45
|
+
removed=0
|
|
46
|
+
if [ -d "$TARGET_DIR" ]; then
|
|
47
|
+
for skill_dir in "$SOURCE_DIR"/*; do
|
|
48
|
+
[ -d "$skill_dir" ] || continue
|
|
49
|
+
[ -f "$skill_dir/SKILL.md" ] || continue
|
|
50
|
+
name="$(basename "$skill_dir")"
|
|
51
|
+
target="$TARGET_DIR/$name/SKILL.md"
|
|
52
|
+
if cmp -s "$skill_dir/SKILL.md" "$target" 2>/dev/null || is_egregore_skill "$target"; then
|
|
53
|
+
rm -rf "$TARGET_DIR/$name"
|
|
54
|
+
removed=$((removed + 1))
|
|
55
|
+
fi
|
|
56
|
+
done
|
|
57
|
+
fi
|
|
58
|
+
hooks_ready=0
|
|
59
|
+
if [ -f "$HOOKS_FILE" ] && [ -d "$HOOKS_DIR" ]; then
|
|
60
|
+
hooks_ready=1
|
|
61
|
+
fi
|
|
62
|
+
echo "codex project skills ready: $installed (removed global duplicates: $removed; hooks ready: $hooks_ready)"
|
|
63
|
+
exit 0
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
mkdir -p "$TARGET_DIR"
|
|
67
|
+
|
|
68
|
+
copied=0
|
|
69
|
+
for skill_dir in "$SOURCE_DIR"/*; do
|
|
70
|
+
[ -d "$skill_dir" ] || continue
|
|
71
|
+
[ -f "$skill_dir/SKILL.md" ] || continue
|
|
72
|
+
name="$(basename "$skill_dir")"
|
|
73
|
+
mkdir -p "$TARGET_DIR/$name"
|
|
74
|
+
cp -R "$skill_dir/." "$TARGET_DIR/$name/"
|
|
75
|
+
copied=$((copied + 1))
|
|
76
|
+
done
|
|
77
|
+
|
|
78
|
+
echo "codex global skills installed: $copied"
|