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.
Files changed (173) hide show
  1. package/assets/egregore-launcher.js +118 -13
  2. package/bin/cli.js +48 -3
  3. package/lib/connect-runtime.js +4 -2
  4. package/lib/local.js +15 -1
  5. package/lib/prime-runtime.js +208 -0
  6. package/lib/setup.js +45 -3
  7. package/package.json +5 -4
  8. package/runtime/codex/.codex/skills/ingest-notion/SKILL.md +29 -0
  9. package/runtime/codex/.codex/skills/notion-connect/SKILL.md +29 -0
  10. package/runtime/codex/.codex/skills/teams-connect/SKILL.md +29 -0
  11. package/runtime/codex/.codex/spec-manifest.json +3 -3
  12. package/runtime/codex/AGENTS.md +11 -4
  13. package/runtime/codex/bin/activity-data.sh +4 -8
  14. package/runtime/codex/bin/attendant.sh +13 -0
  15. package/runtime/codex/bin/autosave.sh +27 -1
  16. package/runtime/codex/bin/codex-render-spec.mjs +6 -0
  17. package/runtime/codex/bin/codex-session-start.sh +8 -1
  18. package/runtime/codex/bin/connect-refresh.sh +33 -0
  19. package/runtime/codex/bin/graph-batch.sh +25 -6
  20. package/runtime/codex/bin/graph-op.sh +5 -4
  21. package/runtime/codex/bin/graph.sh +31 -5
  22. package/runtime/codex/bin/ingest.py +81 -5
  23. package/runtime/codex/bin/lib/context.sh +26 -1
  24. package/runtime/codex/bin/lib/greeting.sh +4 -1
  25. package/runtime/codex/bin/lib/worktree-links.sh +25 -0
  26. package/runtime/codex/manifest.json +8 -1
  27. package/runtime/pi/.codex/skills/ingest-notion/SKILL.md +29 -0
  28. package/runtime/pi/.codex/skills/notion-connect/SKILL.md +29 -0
  29. package/runtime/pi/.codex/skills/teams-connect/SKILL.md +29 -0
  30. package/runtime/pi/.codex/spec-manifest.json +3 -3
  31. package/runtime/pi/.pi/APPEND_SYSTEM.md +3 -2
  32. package/runtime/pi/.pi/extensions/egregore.ts +17 -6
  33. package/runtime/pi/.pi/spec-manifest.json +2 -2
  34. package/runtime/pi/AGENTS.md +11 -4
  35. package/runtime/pi/bin/activity-data.sh +4 -8
  36. package/runtime/pi/bin/attendant.sh +13 -0
  37. package/runtime/pi/bin/autosave.sh +27 -1
  38. package/runtime/pi/bin/codex-render-spec.mjs +6 -0
  39. package/runtime/pi/bin/codex-session-start.sh +8 -1
  40. package/runtime/pi/bin/connect-refresh.sh +33 -0
  41. package/runtime/pi/bin/graph-batch.sh +25 -6
  42. package/runtime/pi/bin/graph-op.sh +5 -4
  43. package/runtime/pi/bin/graph.sh +31 -5
  44. package/runtime/pi/bin/ingest.py +81 -5
  45. package/runtime/pi/bin/lib/context.sh +26 -1
  46. package/runtime/pi/bin/lib/greeting.sh +4 -1
  47. package/runtime/pi/bin/lib/worktree-links.sh +25 -0
  48. package/runtime/pi/bin/pi-branch-policy.mjs +138 -1
  49. package/runtime/pi/bin/pi-product-workflows.mjs +35 -7
  50. package/runtime/pi/manifest.json +8 -1
  51. package/runtime/prime/.claude/context/notification-consent.md +66 -0
  52. package/runtime/prime/.codex/config.toml +17 -0
  53. package/runtime/prime/.codex/hooks/branch-guard.js +461 -0
  54. package/runtime/prime/.codex/hooks/search-hint.js +67 -0
  55. package/runtime/prime/.codex/hooks.json +30 -0
  56. package/runtime/prime/.codex/skills/activity/SKILL.md +82 -0
  57. package/runtime/prime/.codex/skills/announce/SKILL.md +67 -0
  58. package/runtime/prime/.codex/skills/archive/SKILL.md +45 -0
  59. package/runtime/prime/.codex/skills/branch/SKILL.md +29 -0
  60. package/runtime/prime/.codex/skills/checkup/SKILL.md +45 -0
  61. package/runtime/prime/.codex/skills/commit/SKILL.md +29 -0
  62. package/runtime/prime/.codex/skills/contribute/SKILL.md +29 -0
  63. package/runtime/prime/.codex/skills/create-skill/SKILL.md +29 -0
  64. package/runtime/prime/.codex/skills/dark-mode/SKILL.md +29 -0
  65. package/runtime/prime/.codex/skills/dashboard/SKILL.md +59 -0
  66. package/runtime/prime/.codex/skills/deep-reflect/SKILL.md +109 -0
  67. package/runtime/prime/.codex/skills/delete-user/SKILL.md +29 -0
  68. package/runtime/prime/.codex/skills/emissary/SKILL.md +45 -0
  69. package/runtime/prime/.codex/skills/handoff/SKILL.md +166 -0
  70. package/runtime/prime/.codex/skills/harvest/QUESTION_PALETTE.md +76 -0
  71. package/runtime/prime/.codex/skills/harvest/SKILL.md +131 -0
  72. package/runtime/prime/.codex/skills/ingest/SKILL.md +29 -0
  73. package/runtime/prime/.codex/skills/ingest-notion/SKILL.md +29 -0
  74. package/runtime/prime/.codex/skills/ingest-user-interview/SKILL.md +45 -0
  75. package/runtime/prime/.codex/skills/invite/SKILL.md +44 -0
  76. package/runtime/prime/.codex/skills/issue/SKILL.md +45 -0
  77. package/runtime/prime/.codex/skills/me/SKILL.md +29 -0
  78. package/runtime/prime/.codex/skills/meeting/SKILL.md +45 -0
  79. package/runtime/prime/.codex/skills/note/SKILL.md +29 -0
  80. package/runtime/prime/.codex/skills/notion-connect/SKILL.md +29 -0
  81. package/runtime/prime/.codex/skills/onboarding/SKILL.md +45 -0
  82. package/runtime/prime/.codex/skills/pr/SKILL.md +29 -0
  83. package/runtime/prime/.codex/skills/pull/SKILL.md +29 -0
  84. package/runtime/prime/.codex/skills/push/SKILL.md +29 -0
  85. package/runtime/prime/.codex/skills/quest/SKILL.md +80 -0
  86. package/runtime/prime/.codex/skills/reflect/SKILL.md +45 -0
  87. package/runtime/prime/.codex/skills/review-pr/SKILL.md +45 -0
  88. package/runtime/prime/.codex/skills/save/SKILL.md +125 -0
  89. package/runtime/prime/.codex/skills/slack-connect/SKILL.md +29 -0
  90. package/runtime/prime/.codex/skills/summon/SKILL.md +45 -0
  91. package/runtime/prime/.codex/skills/sync-repos/SKILL.md +29 -0
  92. package/runtime/prime/.codex/skills/teams-connect/SKILL.md +29 -0
  93. package/runtime/prime/.codex/skills/telegram-connect/SKILL.md +29 -0
  94. package/runtime/prime/.codex/skills/telemetry/SKILL.md +29 -0
  95. package/runtime/prime/.codex/skills/test/SKILL.md +45 -0
  96. package/runtime/prime/.codex/skills/the-spiral/SKILL.md +84 -0
  97. package/runtime/prime/.codex/skills/tui-design/SKILL.md +45 -0
  98. package/runtime/prime/.codex/skills/tutorial/SKILL.md +45 -0
  99. package/runtime/prime/.codex/skills/update/SKILL.md +29 -0
  100. package/runtime/prime/.codex/skills/view/SKILL.md +156 -0
  101. package/runtime/prime/.codex/skills/view/agents/openai.yaml +4 -0
  102. package/runtime/prime/.codex/skills/view/assets/compose-scaffold.html +221 -0
  103. package/runtime/prime/.codex/skills/visual-explain/SKILL.md +45 -0
  104. package/runtime/prime/.codex/skills/wrap/SKILL.md +58 -0
  105. package/runtime/prime/.codex/spec-manifest.json +132 -0
  106. package/runtime/prime/.prime/agent/APPEND_SYSTEM.md +393 -0
  107. package/runtime/prime/.prime/agent/extensions/egregore.ts +460 -0
  108. package/runtime/prime/.prime/agent/settings.json +4 -0
  109. package/runtime/prime/.prime/agent/skills/egregore/SKILL.md +34 -0
  110. package/runtime/prime/.prime/agent/skills/egregore/pyproject.toml +13 -0
  111. package/runtime/prime/.prime/agent/skills/egregore/src/egregore/__init__.py +156 -0
  112. package/runtime/prime/.prime/agent/spec-manifest.json +12 -0
  113. package/runtime/prime/AGENTS.md +489 -0
  114. package/runtime/prime/bin/activity-data.sh +238 -0
  115. package/runtime/prime/bin/agent.sh +775 -0
  116. package/runtime/prime/bin/attendant.sh +707 -0
  117. package/runtime/prime/bin/autosave.sh +112 -0
  118. package/runtime/prime/bin/capture-reconcile.sh +69 -0
  119. package/runtime/prime/bin/capture-run.sh +341 -0
  120. package/runtime/prime/bin/codex-install-skills.sh +78 -0
  121. package/runtime/prime/bin/codex-render-spec.mjs +440 -0
  122. package/runtime/prime/bin/codex-session-start.sh +265 -0
  123. package/runtime/prime/bin/codex-skill-render.mjs +347 -0
  124. package/runtime/prime/bin/codex-sync-skills.sh +232 -0
  125. package/runtime/prime/bin/connect-refresh.sh +33 -0
  126. package/runtime/prime/bin/dashboard-data.sh +178 -0
  127. package/runtime/prime/bin/graph-batch.sh +79 -0
  128. package/runtime/prime/bin/graph-op.sh +1102 -0
  129. package/runtime/prime/bin/graph-wal.sh +229 -0
  130. package/runtime/prime/bin/graph.sh +223 -0
  131. package/runtime/prime/bin/handoff-pr-backfill.sh +86 -0
  132. package/runtime/prime/bin/handoff-run.sh +671 -0
  133. package/runtime/prime/bin/handoff-save-egregore.sh +212 -0
  134. package/runtime/prime/bin/index-handoff.sh +322 -0
  135. package/runtime/prime/bin/ingest-graph.sh +80 -0
  136. package/runtime/prime/bin/ingest-surface.html +495 -0
  137. package/runtime/prime/bin/ingest.py +756 -0
  138. package/runtime/prime/bin/ingest.sh +279 -0
  139. package/runtime/prime/bin/ingest_graph.py +856 -0
  140. package/runtime/prime/bin/ingest_surface.py +544 -0
  141. package/runtime/prime/bin/lib/artifact-id.sh +46 -0
  142. package/runtime/prime/bin/lib/config.sh +116 -0
  143. package/runtime/prime/bin/lib/context.sh +621 -0
  144. package/runtime/prime/bin/lib/dashboard-artifact.sh +242 -0
  145. package/runtime/prime/bin/lib/ensure-develop.sh +183 -0
  146. package/runtime/prime/bin/lib/git-sync.sh +406 -0
  147. package/runtime/prime/bin/lib/greeting.sh +519 -0
  148. package/runtime/prime/bin/lib/hash.sh +13 -0
  149. package/runtime/prime/bin/lib/identity.sh +213 -0
  150. package/runtime/prime/bin/lib/metrics.sh +374 -0
  151. package/runtime/prime/bin/lib/noncode.sh +31 -0
  152. package/runtime/prime/bin/lib/permissions.sh +106 -0
  153. package/runtime/prime/bin/lib/time.sh +56 -0
  154. package/runtime/prime/bin/lib/worktree-links.sh +79 -0
  155. package/runtime/prime/bin/notify.sh +636 -0
  156. package/runtime/prime/bin/person.py +630 -0
  157. package/runtime/prime/bin/person.sh +448 -0
  158. package/runtime/prime/bin/pi-branch-policy.mjs +312 -0
  159. package/runtime/prime/bin/pi-product-workflows.mjs +394 -0
  160. package/runtime/prime/bin/prime-render-spec.mjs +123 -0
  161. package/runtime/prime/bin/prime-session-start.sh +24 -0
  162. package/runtime/prime/bin/publish-artifact.sh +271 -0
  163. package/runtime/prime/bin/publish-references.sh +144 -0
  164. package/runtime/prime/bin/repo-state.sh +96 -0
  165. package/runtime/prime/bin/search.sh +240 -0
  166. package/runtime/prime/bin/session-autosave.sh +209 -0
  167. package/runtime/prime/bin/session-log.sh +199 -0
  168. package/runtime/prime/bin/settings.sh +304 -0
  169. package/runtime/prime/bin/telemetry.sh +391 -0
  170. package/runtime/prime/bin/worktree-create.sh +128 -0
  171. package/runtime/prime/bin/worktree-remove.sh +50 -0
  172. package/runtime/prime/bin/worktree.sh +198 -0
  173. 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"