create-egregore 0.19.3 → 0.19.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/assets/egregore-launcher.js +410 -26
  2. package/bin/cli.js +34 -20
  3. package/lib/local.js +84 -11
  4. package/lib/pi-runtime.js +186 -0
  5. package/lib/setup.js +56 -11
  6. package/lib/ui.js +1 -1
  7. package/package.json +8 -6
  8. package/runtime/codex/.codex/hooks/search-hint.js +65 -0
  9. package/runtime/codex/.codex/hooks.json +13 -0
  10. package/runtime/codex/.codex/skills/deep-reflect/SKILL.md +14 -10
  11. package/runtime/codex/.codex/skills/minion/SKILL.md +25 -0
  12. package/runtime/codex/.codex/skills/scroll/SKILL.md +168 -0
  13. package/runtime/codex/.codex/skills/scroll/agents/openai.yaml +4 -0
  14. package/runtime/codex/.codex/skills/scroll/assets/shell.html +542 -0
  15. package/runtime/codex/.codex/skills/search/SKILL.md +56 -0
  16. package/runtime/codex/.codex/skills/session-view/SKILL.md +25 -0
  17. package/runtime/codex/.codex/skills/thread/SKILL.md +25 -0
  18. package/runtime/codex/.codex/skills/view/SKILL.md +147 -32
  19. package/runtime/codex/.codex/skills/view/agents/openai.yaml +4 -0
  20. package/runtime/codex/.codex/skills/view/assets/compose-scaffold.html +221 -0
  21. package/runtime/codex/.codex/spec-manifest.json +4 -4
  22. package/runtime/codex/AGENTS.md +31 -5
  23. package/runtime/codex/bin/activity-data.sh +46 -4
  24. package/runtime/codex/bin/agent.sh +10 -2
  25. package/runtime/codex/bin/codex-install-skills.sh +1 -1
  26. package/runtime/codex/bin/codex-render-spec.mjs +24 -2
  27. package/runtime/codex/bin/codex-session-start.sh +6 -60
  28. package/runtime/codex/bin/codex-skill-render.mjs +20 -12
  29. package/runtime/codex/bin/codex-sync-skills.sh +4 -0
  30. package/runtime/codex/bin/graph-op.sh +56 -5
  31. package/runtime/codex/bin/graph.sh +36 -0
  32. package/runtime/codex/bin/handoff-run.sh +157 -7
  33. package/runtime/codex/bin/handoff-save-egregore.sh +6 -1
  34. package/runtime/codex/bin/index-handoff.sh +9 -3
  35. package/runtime/codex/bin/lib/context.sh +97 -18
  36. package/runtime/codex/bin/lib/dashboard-artifact.sh +1 -1
  37. package/runtime/codex/bin/lib/git-sync.sh +35 -9
  38. package/runtime/codex/bin/lib/greeting.sh +30 -104
  39. package/runtime/codex/bin/lib/metrics.sh +374 -0
  40. package/runtime/codex/bin/publish-artifact.sh +43 -1
  41. package/runtime/codex/bin/settings.sh +274 -0
  42. package/runtime/codex/manifest.json +18 -1
  43. package/runtime/pi/.codex/config.toml +15 -0
  44. package/runtime/pi/.codex/hooks/branch-guard.js +242 -0
  45. package/runtime/pi/.codex/hooks/search-hint.js +65 -0
  46. package/runtime/pi/.codex/hooks.json +30 -0
  47. package/runtime/pi/.codex/skills/activity/SKILL.md +70 -0
  48. package/runtime/pi/.codex/skills/add/SKILL.md +25 -0
  49. package/runtime/pi/.codex/skills/announce/SKILL.md +54 -0
  50. package/runtime/pi/.codex/skills/answer/SKILL.md +25 -0
  51. package/runtime/pi/.codex/skills/archive/SKILL.md +41 -0
  52. package/runtime/pi/.codex/skills/ask/SKILL.md +59 -0
  53. package/runtime/pi/.codex/skills/branch/SKILL.md +25 -0
  54. package/runtime/pi/.codex/skills/checkup/SKILL.md +41 -0
  55. package/runtime/pi/.codex/skills/commit/SKILL.md +25 -0
  56. package/runtime/pi/.codex/skills/contribute/SKILL.md +25 -0
  57. package/runtime/pi/.codex/skills/dark-mode/SKILL.md +25 -0
  58. package/runtime/pi/.codex/skills/dashboard/SKILL.md +59 -0
  59. package/runtime/pi/.codex/skills/deep-reflect/SKILL.md +74 -0
  60. package/runtime/pi/.codex/skills/delete-user/SKILL.md +25 -0
  61. package/runtime/pi/.codex/skills/egregore-voice/SKILL.md +25 -0
  62. package/runtime/pi/.codex/skills/emissary/SKILL.md +41 -0
  63. package/runtime/pi/.codex/skills/env/SKILL.md +25 -0
  64. package/runtime/pi/.codex/skills/handoff/SKILL.md +67 -0
  65. package/runtime/pi/.codex/skills/harvest/SKILL.md +107 -0
  66. package/runtime/pi/.codex/skills/ingest/SKILL.md +25 -0
  67. package/runtime/pi/.codex/skills/ingest-google/SKILL.md +25 -0
  68. package/runtime/pi/.codex/skills/ingest-user-interview/SKILL.md +41 -0
  69. package/runtime/pi/.codex/skills/invite/SKILL.md +40 -0
  70. package/runtime/pi/.codex/skills/issue/SKILL.md +41 -0
  71. package/runtime/pi/.codex/skills/me/SKILL.md +25 -0
  72. package/runtime/pi/.codex/skills/meeting/SKILL.md +41 -0
  73. package/runtime/pi/.codex/skills/minion/SKILL.md +25 -0
  74. package/runtime/pi/.codex/skills/note/SKILL.md +25 -0
  75. package/runtime/pi/.codex/skills/onboarding/SKILL.md +41 -0
  76. package/runtime/pi/.codex/skills/pr/SKILL.md +25 -0
  77. package/runtime/pi/.codex/skills/project/SKILL.md +41 -0
  78. package/runtime/pi/.codex/skills/pull/SKILL.md +25 -0
  79. package/runtime/pi/.codex/skills/push/SKILL.md +25 -0
  80. package/runtime/pi/.codex/skills/quest/SKILL.md +77 -0
  81. package/runtime/pi/.codex/skills/quest-suggest/SKILL.md +25 -0
  82. package/runtime/pi/.codex/skills/reflect/SKILL.md +41 -0
  83. package/runtime/pi/.codex/skills/review-pr/SKILL.md +41 -0
  84. package/runtime/pi/.codex/skills/save/SKILL.md +62 -0
  85. package/runtime/pi/.codex/skills/scroll/SKILL.md +168 -0
  86. package/runtime/pi/.codex/skills/scroll/agents/openai.yaml +4 -0
  87. package/runtime/pi/.codex/skills/scroll/assets/shell.html +542 -0
  88. package/runtime/pi/.codex/skills/search/SKILL.md +56 -0
  89. package/runtime/pi/.codex/skills/session-view/SKILL.md +25 -0
  90. package/runtime/pi/.codex/skills/setup/SKILL.md +25 -0
  91. package/runtime/pi/.codex/skills/summon/SKILL.md +41 -0
  92. package/runtime/pi/.codex/skills/sync-repos/SKILL.md +25 -0
  93. package/runtime/pi/.codex/skills/telegram-connect/SKILL.md +25 -0
  94. package/runtime/pi/.codex/skills/telemetry/SKILL.md +25 -0
  95. package/runtime/pi/.codex/skills/test/SKILL.md +41 -0
  96. package/runtime/pi/.codex/skills/the-spiral/SKILL.md +84 -0
  97. package/runtime/pi/.codex/skills/thread/SKILL.md +25 -0
  98. package/runtime/pi/.codex/skills/todo/SKILL.md +41 -0
  99. package/runtime/pi/.codex/skills/tui-design/SKILL.md +41 -0
  100. package/runtime/pi/.codex/skills/tutorial/SKILL.md +41 -0
  101. package/runtime/pi/.codex/skills/update/SKILL.md +25 -0
  102. package/runtime/pi/.codex/skills/view/SKILL.md +156 -0
  103. package/runtime/pi/.codex/skills/view/agents/openai.yaml +4 -0
  104. package/runtime/pi/.codex/skills/view/assets/compose-scaffold.html +221 -0
  105. package/runtime/pi/.codex/skills/visual-explain/SKILL.md +41 -0
  106. package/runtime/pi/.codex/skills/wrap/SKILL.md +55 -0
  107. package/runtime/pi/.codex/spec-manifest.json +114 -0
  108. package/runtime/pi/.pi/APPEND_SYSTEM.md +255 -0
  109. package/runtime/pi/.pi/extensions/egregore.ts +324 -0
  110. package/runtime/pi/.pi/settings.json +6 -0
  111. package/runtime/pi/.pi/spec-manifest.json +12 -0
  112. package/runtime/pi/AGENTS.md +350 -0
  113. package/runtime/pi/bin/activity-data.sh +242 -0
  114. package/runtime/pi/bin/agent.sh +677 -0
  115. package/runtime/pi/bin/codex-install-skills.sh +78 -0
  116. package/runtime/pi/bin/codex-render-spec.mjs +394 -0
  117. package/runtime/pi/bin/codex-session-start.sh +188 -0
  118. package/runtime/pi/bin/codex-skill-render.mjs +323 -0
  119. package/runtime/pi/bin/codex-sync-skills.sh +227 -0
  120. package/runtime/pi/bin/dashboard-data.sh +178 -0
  121. package/runtime/pi/bin/graph-batch.sh +60 -0
  122. package/runtime/pi/bin/graph-op.sh +431 -0
  123. package/runtime/pi/bin/graph-wal.sh +228 -0
  124. package/runtime/pi/bin/graph.sh +177 -0
  125. package/runtime/pi/bin/handoff-pr-backfill.sh +86 -0
  126. package/runtime/pi/bin/handoff-run.sh +581 -0
  127. package/runtime/pi/bin/handoff-save-egregore.sh +136 -0
  128. package/runtime/pi/bin/index-handoff.sh +312 -0
  129. package/runtime/pi/bin/lib/artifact-id.sh +46 -0
  130. package/runtime/pi/bin/lib/config.sh +71 -0
  131. package/runtime/pi/bin/lib/context.sh +542 -0
  132. package/runtime/pi/bin/lib/dashboard-artifact.sh +142 -0
  133. package/runtime/pi/bin/lib/ensure-develop.sh +183 -0
  134. package/runtime/pi/bin/lib/git-sync.sh +288 -0
  135. package/runtime/pi/bin/lib/greeting.sh +340 -0
  136. package/runtime/pi/bin/lib/hash.sh +13 -0
  137. package/runtime/pi/bin/lib/identity.sh +174 -0
  138. package/runtime/pi/bin/lib/metrics.sh +374 -0
  139. package/runtime/pi/bin/lib/permissions.sh +106 -0
  140. package/runtime/pi/bin/lib/time.sh +56 -0
  141. package/runtime/pi/bin/lib/worktree-links.sh +54 -0
  142. package/runtime/pi/bin/notify.sh +207 -0
  143. package/runtime/pi/bin/pi-branch-policy.mjs +15 -0
  144. package/runtime/pi/bin/pi-product-workflows.mjs +324 -0
  145. package/runtime/pi/bin/pi-render-spec.mjs +114 -0
  146. package/runtime/pi/bin/pi-session-start.sh +24 -0
  147. package/runtime/pi/bin/publish-artifact.sh +201 -0
  148. package/runtime/pi/bin/publish-references.sh +144 -0
  149. package/runtime/pi/bin/repo-state.sh +99 -0
  150. package/runtime/pi/bin/telemetry.sh +391 -0
  151. package/runtime/pi/bin/worktree-create.sh +118 -0
  152. package/runtime/pi/bin/worktree-remove.sh +50 -0
  153. package/runtime/pi/bin/worktree.sh +198 -0
  154. package/runtime/pi/manifest.json +176 -0
@@ -28,6 +28,8 @@ Runtime-neutral commands:
28
28
  protocol Print the portable agent protocol
29
29
  sync Pull latest shared memory
30
30
  activity [--for <person>] Show recent handoffs and questions
31
+ search "query" [-n N] [--fast|--semantic]
32
+ Hybrid recall over shared memory (qmd)
31
33
  people List known people from memory/people
32
34
  branch --topic TEXT Create or reuse a task branch/worktree
33
35
  save [--message TEXT] Commit and push current repo + memory changes
@@ -165,8 +167,9 @@ Egregore portable agent protocol
165
167
  4. Use `bin/agent.sh handoff` to leave structured context for another agent.
166
168
  5. Use `bin/agent.sh ask` and `bin/agent.sh answer` for asynchronous questions.
167
169
  6. Run `bin/agent.sh activity --for <person>` to see pending work.
168
- 7. Run `bin/agent.sh branch --topic "<what you are working on>"` before code changes when you need a task branch/worktree.
169
- 8. Run `bin/agent.sh save` and `bin/agent.sh wrap` for portable save/wrap flows.
170
+ 7. Run `bin/agent.sh search "<query>"` to recall shared memory (decisions, handoffs, knowledge) before answering questions about past work.
171
+ 8. Run `bin/agent.sh branch --topic "<what you are working on>"` before code changes when you need a task branch/worktree.
172
+ 9. Run `bin/agent.sh save` and `bin/agent.sh wrap` for portable save/wrap flows.
170
173
 
171
174
  This protocol is runtime-neutral. Claude Code may still use .claude hooks and
172
175
  skills, while Codex or another agent can call this script directly.
@@ -653,11 +656,16 @@ cmd_answer() {
653
656
  echo "answered: memory/$rel"
654
657
  }
655
658
 
659
+ cmd_search() {
660
+ bash "$SCRIPT_DIR/bin/search.sh" query "$@"
661
+ }
662
+
656
663
  case "${1:-}" in
657
664
  --help|-h|help|"") usage ;;
658
665
  protocol) shift; cmd_protocol "$@" ;;
659
666
  sync) shift; cmd_sync "$@" ;;
660
667
  activity) shift; cmd_activity "$@" ;;
668
+ search) shift; cmd_search "$@" ;;
661
669
  people) shift; cmd_people "$@" ;;
662
670
  branch) shift; cmd_branch "$@" ;;
663
671
  save) shift; cmd_save "$@" ;;
@@ -71,7 +71,7 @@ for skill_dir in "$SOURCE_DIR"/*; do
71
71
  [ -f "$skill_dir/SKILL.md" ] || continue
72
72
  name="$(basename "$skill_dir")"
73
73
  mkdir -p "$TARGET_DIR/$name"
74
- cp "$skill_dir/SKILL.md" "$TARGET_DIR/$name/SKILL.md"
74
+ cp -R "$skill_dir/." "$TARGET_DIR/$name/"
75
75
  copied=$((copied + 1))
76
76
  done
77
77
 
@@ -53,6 +53,11 @@ const AGENTS_PREAMBLE = `# Egregore Agent Protocol
53
53
  > Egregore skill already covers. This file exists only for runtimes that cannot
54
54
  > read \`CLAUDE.md\`.
55
55
  >
56
+ > **If you are Pi** (the \`pi\` coding-agent harness): **stop here.** Your
57
+ > authoritative Egregore instructions are loaded from
58
+ > \`.pi/APPEND_SYSTEM.md\`, and your workflows are exposed through the
59
+ > project-local \`.pi/\` runtime. Do not follow the Codex-specific block below.
60
+ >
56
61
  > **If you are Codex or another shell-only agent:** this file is yours — continue.
57
62
 
58
63
  Egregore is no longer only a Claude Code workspace. Claude Code remains the
@@ -73,6 +78,12 @@ portable memory protocol below.
73
78
  Use the runtime-neutral bridge instead of Claude Code slash commands:
74
79
 
75
80
  \`\`\`bash
81
+ bin/agent.sh branch --topic "auth review"
82
+ bin/agent.sh save --message "Save: auth review" --topic "auth review"
83
+ bin/agent.sh wrap --from alice --topic "auth review" \\
84
+ --summary "Implemented the OAuth callback parser and documented follow-ups." \\
85
+ --body "Open threads: review error cases and browser redirects."
86
+
76
87
  bin/agent.sh handoff --from alice --to bob --topic "auth review" \\
77
88
  --body "Implemented the OAuth callback parser. Bob should review error cases."
78
89
 
@@ -88,6 +99,17 @@ Each command writes to the existing Git-backed \`memory/\` repository and pushes
88
99
  when the memory repo has an \`origin\` remote. Agents that cannot run shell may
89
100
  write the same markdown files directly, following \`docs/AGENT-PROTOCOL.md\`.
90
101
 
102
+ ## Handoffs vs Emissaries
103
+
104
+ \`bin/agent.sh handoff\` is the runtime-neutral path for internal team
105
+ session-handoffs: it writes to \`memory/handoffs/\`, updates the index, and uses
106
+ the same \`bin/handoff-run.sh\` machinery as Claude Code's \`/handoff\`.
107
+
108
+ Portable external capsules are **emissaries**, not team handoffs. Claude Code
109
+ routes those through \`/emissary\`; non-Claude agents should use the
110
+ \`egregore-emissary\` CLI/skill when it is installed. Do not use the deprecated
111
+ \`egregore-handoff\` CLI for Egregore project \`/handoff\` work.
112
+
91
113
  ## Compatibility
92
114
 
93
115
  Claude Code users continue using \`/handoff\`, \`/activity\`, \`/ask\`, \`/save\`, and
@@ -152,7 +174,7 @@ const INTRO_BODY = `You are a collaborator inside Egregore — a shared intellig
152
174
 
153
175
  This block is the Codex-native Egregore behavioral spec, rendered from CLAUDE.md (the source of truth for egregore behavior) by \`bin/codex-render-spec.mjs\`. The per-section adaptation manifest is \`.codex/spec-manifest.json\`. The thin agent protocol above this block applies to every shell-capable agent; this block is the full behavioral contract for Codex sessions.`;
154
176
 
155
- const ON_LAUNCH_BODY = `The \`egregore\` launcher renders the Egregore startup card (identity, handoffs, team activity) via \`bin/codex-session-start.sh\` before Codex starts, and installs project skills. Do not rerun startup checks and do not narrate startup. The card ends with **"What are you working on?"** — that question is already on screen; treat the user's first message as the answer to it. To re-show the card, run \`bash bin/codex-session-start.sh --card\`.`;
177
+ const ON_LAUNCH_BODY = `The \`egregore\` launcher renders the Egregore startup card (identity, team momentum, and pending work) via \`bin/codex-session-start.sh\` before Codex starts, and installs project skills. Do not rerun startup checks and do not narrate startup. The card ends with **"What are you working on?"** — that question is already on screen; treat the user's first message as the answer to it. To re-show the card, run \`bash bin/codex-session-start.sh --card\`.`;
156
178
 
157
179
  const AFTER_GREETING_BODY = `**Mandatory behavioral rule.** When the user describes work, your **first action** — before reading files, exploring code, or anything else — is to get onto a working branch:
158
180
 
@@ -216,7 +238,7 @@ If this Codex build does not support hooks, follow the same discipline as a stan
216
238
 
217
239
  If the startup card output contains \`onboarding_needed\`, invoke the \`$onboarding\` skill instead of greeting.`;
218
240
 
219
- const COMMAND_AWARENESS_PREPEND = `Codex reserves leading \`/\` for built-ins, so Egregore workflows are **skills**, not slash commands. Invoke them with the matching \`$name\` skill token or from natural language intent ("show activity", "make a handoff"). Hand-written native Codex skills: \`$activity\`, \`$handoff\`, \`$wrap\`, \`$announce\`, \`$harvest\`, \`$the-spiral\`, \`$dashboard\`, \`$deep-reflect\`, \`$quest\`, \`$invite\`, \`$ask\`, \`$save\`. Every other workflow has a generated adapter skill of the same name. \`$save\` is the user-facing abstraction for committing, pushing, opening or reusing pull requests, and syncing memory — never make users manage the git workflow by hand.`;
241
+ const COMMAND_AWARENESS_PREPEND = `Codex reserves leading \`/\` for built-ins, so Egregore workflows are **skills**, not slash commands. Invoke them with the matching \`$name\` skill token or from natural language intent ("show activity", "make a handoff"). Hand-written native Codex skills: \`$activity\`, \`$handoff\`, \`$wrap\`, \`$announce\`, \`$harvest\`, \`$the-spiral\`, \`$dashboard\`, \`$deep-reflect\`, \`$quest\`, \`$invite\`, \`$ask\`, \`$save\`, \`$view\`, and \`$scroll\`. Every other workflow has a generated adapter skill of the same name. \`$save\` is the user-facing abstraction for committing, pushing, opening or reusing pull requests, and syncing memory — never make users manage the git workflow by hand.`;
220
242
 
221
243
  const SOCRATIC_BODY = `**Triggers**: "ask me questions", "question me", "help me think through", or any request to be questioned.
222
244
 
@@ -6,7 +6,7 @@ set -o pipefail
6
6
  # libraries as Claude's SessionStart, then renders a terminal greeting before
7
7
  # Codex itself starts.
8
8
 
9
- FRAMEWORK_VERSION="3"
9
+ FRAMEWORK_VERSION="6"
10
10
  SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
11
11
  cd "$SCRIPT_DIR" || exit 1
12
12
 
@@ -56,6 +56,7 @@ fi
56
56
 
57
57
  source "$SCRIPT_DIR/bin/lib/git-sync.sh" >/dev/null 2>/dev/null
58
58
  source "$SCRIPT_DIR/bin/lib/context.sh" >/dev/null 2>/dev/null
59
+ source "$SCRIPT_DIR/bin/lib/metrics.sh" >/dev/null 2>/dev/null
59
60
  trap 'rm -rf "${CTX_DIR:-}"' EXIT
60
61
 
61
62
  read_json() {
@@ -69,23 +70,6 @@ read_json() {
69
70
  fi
70
71
  }
71
72
 
72
- shorten() {
73
- local value="$1"
74
- local max="$2"
75
- if command -v jq >/dev/null 2>&1; then
76
- jq -nr --arg value "$value" --argjson max "$max" '
77
- $value
78
- | if length > $max then .[0:($max - 3)] + "..." else . end
79
- '
80
- return
81
- fi
82
- if [ "${#value}" -gt "$max" ]; then
83
- printf '%s...\n' "${value:0:$((max - 3))}"
84
- else
85
- printf '%s\n' "$value"
86
- fi
87
- }
88
-
89
73
  ORG_NAME="$(read_json "$CONFIG" '.org_name' '')"
90
74
  [ -z "$ORG_NAME" ] && ORG_NAME="$(read_json "$CONFIG" '.slug' 'Egregore')"
91
75
  GITHUB_ORG="$(read_json "$CONFIG" '.github_org' '')"
@@ -96,14 +80,12 @@ DISPLAY_NAME="$(read_json "$STATE_FILE" '.display_name' '')"
96
80
  ONBOARDING_COMPLETE="$(read_json "$STATE_FILE" '.onboarding_complete' '')"
97
81
  [ -z "$ONBOARDING_COMPLETE" ] && ONBOARDING_COMPLETE="unknown"
98
82
 
99
- TEAM_DATA="$(cat "$CTX_DIR/team" 2>/dev/null || echo "[]")"
100
83
  ADDRESSED_RICH="$(cat "$CTX_DIR/addressed_rich" 2>/dev/null || echo "[]")"
101
84
  PENDING_Q="$(cat "$CTX_DIR/pending_questions" 2>/dev/null || echo "[]")"
102
- LAST_ACTIVITY="$(cat "$CTX_DIR/activity" 2>/dev/null || true)"
103
85
  ADDRESSED_COUNT="$(echo "$ADDRESSED_RICH" | jq 'length' 2>/dev/null || echo "0")"
104
86
  PENDING_Q_COUNT="$(echo "$PENDING_Q" | jq 'length' 2>/dev/null || echo "0")"
105
87
 
106
- render_for_you_summary() {
88
+ render_pending_line() {
107
89
  if [ "$PENDING_Q_COUNT" -gt 0 ] 2>/dev/null; then
108
90
  local senders noun
109
91
  senders="$(echo "$PENDING_Q" | jq -r '[.[].from] | unique | join(", ")' 2>/dev/null)"
@@ -115,41 +97,9 @@ render_for_you_summary() {
115
97
  fi
116
98
  elif [ "$ADDRESSED_COUNT" -gt 0 ] 2>/dev/null; then
117
99
  printf ' ◇ %s handoffs for %s - bin/agent.sh activity --for %s\n' "$ADDRESSED_COUNT" "$AUTHOR" "$AUTHOR"
118
- else
119
- printf ' ◇ nothing pending for %s\n' "$AUTHOR"
120
- fi
121
- }
122
-
123
- render_handoff_rows() {
124
- if [ "$ADDRESSED_COUNT" -gt 0 ] 2>/dev/null; then
125
- echo "$ADDRESSED_RICH" | jq -r '.[] | [.author, .date, .topic] | @tsv' 2>/dev/null | head -4 | while IFS=$'\t' read -r author date topic; do
126
- [ -z "$author" ] && continue
127
- printf ' ○ %-10s%-12s%s\n' "$(shorten "$author" 9)" "$date" "$(shorten "$topic" 40)"
128
- done
129
- elif [ -n "$LAST_ACTIVITY" ]; then
130
- local last_when last_msg
131
- last_when="$(printf '%s\n' "$LAST_ACTIVITY" | cut -d'|' -f1 | sed 's/ hours\{0,1\} ago/h ago/; s/ days\{0,1\} ago/d ago/; s/ minutes\{0,1\} ago/m ago/')"
132
- last_msg="$(printf '%s\n' "$LAST_ACTIVITY" | cut -d'|' -f2-)"
133
- printf ' ◇ %-10s%-12s%s\n' "you" "$last_when" "$(shorten "$last_msg" 40)"
134
100
  fi
135
101
  }
136
102
 
137
- render_team_rows() {
138
- local now_render online_threshold
139
- now_render=$(date +%s)
140
- online_threshold=7200
141
- echo "$TEAM_DATA" | jq -r '.[] | [.name, .last_seen_sort, .last_seen, (.branches | join(", "))] | @tsv' 2>/dev/null | head -4 | while IFS=$'\t' read -r name epoch seen branches; do
142
- [ -z "$name" ] && continue
143
- local dot
144
- if [ "$epoch" -gt 0 ] 2>/dev/null && [ $((now_render - epoch)) -lt "$online_threshold" ] 2>/dev/null; then
145
- dot="●"
146
- else
147
- dot="○"
148
- fi
149
- printf ' %s %-10s%-12s%s\n' "$dot" "$(shorten "$name" 9)" "$seen" "$(shorten "$branches" 40)"
150
- done
151
- }
152
-
153
103
  banner() {
154
104
  cat <<'EOF'
155
105
 
@@ -175,12 +125,8 @@ render_card() {
175
125
  banner
176
126
  printf '%s%*s%s\n' "$identity_left" "$pad" "" "$identity_right"
177
127
  printf '%s\n' "$separator"
178
- printf ' ◦ for you\n'
179
- render_for_you_summary
180
- render_handoff_rows
181
- printf '\n'
182
- printf ' ◦ around\n'
183
- render_team_rows
128
+ SEPARATOR="$separator" LINE_WIDTH=$line_width _render_momentum_board
129
+ render_pending_line
184
130
  printf '%s\n' "$separator"
185
131
 
186
132
  has_failure="false"
@@ -229,7 +175,7 @@ Follow AGENTS.md before code changes.
229
175
  After the user names what they are working on, use bin/agent.sh branch --topic "<work topic>" if you are not already in an appropriate task worktree, then continue file work from the printed worktree path.
230
176
  Use bin/agent.sh to update shared activity: handoff, ask, and answer write to memory/ and push when possible.
231
177
  Use bin/agent.sh handoff for internal team session-handoffs. Portable external capsules are emissaries; use egregore-emissary when installed, not the deprecated egregore-handoff CLI.
232
- Codex reserves leading slash commands for built-ins, so custom Egregore /activity-style commands will not reach the agent. Egregore native Codex skills are: \$activity, \$handoff, \$wrap, \$announce, \$harvest, \$the-spiral, \$dashboard, \$deep-reflect, \$quest, \$invite, \$ask, and \$save. Natural-language requests such as "show activity" or "make a handoff" work too. Remaining mirrored workflows are adapter skills for long-tail coverage.
178
+ Codex reserves leading slash commands for built-ins, so custom Egregore /activity-style commands will not reach the agent. Egregore native Codex skills are: \$activity, \$handoff, \$wrap, \$announce, \$harvest, \$the-spiral, \$dashboard, \$deep-reflect, \$quest, \$invite, \$ask, \$save, \$view, and \$scroll. Natural-language requests such as "show activity", "make a handoff", or "render this as a scroll" work too. Remaining mirrored workflows are adapter skills for long-tail coverage.
233
179
  \$save is the user-facing abstraction for committing, pushing, opening or reusing pull requests, and syncing memory; do not make users manage the git workflow by hand.
234
180
  Egregore Codex workflows are skill invocations, not Claude Code slash commands. Do not rely on Claude Code-only command machinery.
235
181
  If onboarding_complete is false, use the memory protocol directly and make sure memory/people/${AUTHOR}.md exists before the first handoff.
@@ -72,9 +72,10 @@ function truncate(value, max) {
72
72
  }
73
73
 
74
74
  function padLine(left, right = '') {
75
- const l = truncate(left, WIDTH - 4);
76
- const r = truncate(right, WIDTH - 4);
77
- const space = Math.max(1, WIDTH - 4 - l.length - r.length);
75
+ const inner = WIDTH - 4;
76
+ const r = right ? truncate(right, Math.min(18, inner - 1)) : '';
77
+ const l = truncate(left, inner - (r ? r.length + 1 : 0));
78
+ const space = Math.max(0, inner - l.length - r.length);
78
79
  return `│ ${l}${' '.repeat(space)}${r} │`;
79
80
  }
80
81
 
@@ -143,13 +144,14 @@ function renderActivity(data) {
143
144
  const org = data.org || data.organization || 'Egregore';
144
145
  const date = data.date || '';
145
146
  const me = data.me?.github || data.me?.name || data.github_username || '';
146
- const handoffs = firstArray(data, ['handoffs', 'pending_handoffs', 'me.handoffs', 'addressed_handoffs']);
147
+ const handoffs = firstArray(data, ['handoffs_to_me', 'handoffs', 'pending_handoffs', 'me.handoffs', 'addressed_handoffs']);
147
148
  const questions = firstArray(data, ['questions', 'pending_questions', 'me.questions']);
148
149
  const prs = asArray(data.prs);
149
150
  const mine = firstArray(data, ['local_sessions.my_sessions', 'my_sessions', 'sessions']);
150
151
  const team = firstArray(data, ['local_sessions.team_sessions', 'team_sessions', 'team.sessions']);
151
152
 
152
- const lines = [topRule(), padLine('ACTIVITY', [org, me, date].filter(Boolean).join(' · ')), rule()];
153
+ const brand = `${truncate(org, 20).toUpperCase()} EGREGORE ✦ ACTIVITY DASHBOARD`;
154
+ const lines = [topRule(), padLine(brand), padLine([me, date].filter(Boolean).join(' · ')), rule()];
153
155
  if (graph.status !== 'connected') {
154
156
  lines.push(padLine(`graph: ${graph.status}`, graph.reason));
155
157
  lines.push(rule());
@@ -159,12 +161,18 @@ function renderActivity(data) {
159
161
  if (questions.length === 0 && handoffs.length === 0) {
160
162
  lines.push(padLine('nothing pending'));
161
163
  } else {
162
- questions.slice(0, 4).forEach((q) => {
163
- lines.push(padLine(`? ${valueOf(q, ['from', 'author', 'asker'], 'someone')}: ${valueOf(q, ['topic', 'title', 'question'], 'question')}`));
164
- });
165
- handoffs.slice(0, 4).forEach((h) => {
166
- lines.push(padLine(`> ${valueOf(h, ['author', 'from'], 'someone')}: ${valueOf(h, ['topic', 'title'], 'handoff')}`));
167
- });
164
+ if (questions.length > 0) {
165
+ lines.push(padLine('QUESTIONS'));
166
+ questions.slice(0, 4).forEach((q) => {
167
+ lines.push(padLine(`● ${valueOf(q, ['from', 'author', 'asker'], 'someone')}: ${valueOf(q, ['topic', 'title', 'question'], 'question')}`));
168
+ });
169
+ }
170
+ if (handoffs.length > 0) {
171
+ lines.push(padLine('HANDOFFS'));
172
+ handoffs.slice(0, 4).forEach((h) => {
173
+ lines.push(padLine(`○ ${valueOf(h, ['author', 'from'], 'someone')}: ${valueOf(h, ['topic', 'title'], 'handoff')}`));
174
+ });
175
+ }
168
176
  }
169
177
 
170
178
  lines.push(rule());
@@ -284,7 +292,7 @@ function renderHandoff(data) {
284
292
  data.notifyStatus ? `notify=${data.notifyStatus}` : '',
285
293
  ].filter(Boolean).join(' · ');
286
294
 
287
- const lines = [topRule(), padLine('HANDOFF', data.author || ''), rule()];
295
+ const lines = [topRule(), padLine('⇌ HANDOFF', data.author || ''), rule()];
288
296
  lines.push(padLine(`Topic: ${data.topic || '(untitled)'}`));
289
297
  if (data.recipient) lines.push(padLine(`To: ${data.recipient}`));
290
298
  if (file) lines.push(padLine(`Path: ${file}`));
@@ -19,6 +19,9 @@ NATIVE_SKILLS=(
19
19
  invite
20
20
  ask
21
21
  save
22
+ search
23
+ view
24
+ scroll
22
25
  )
23
26
 
24
27
  STRUCTURED_UX_SKILLS=(
@@ -54,6 +57,7 @@ STRUCTURED_UX_SKILLS=(
54
57
  emissary
55
58
  launch-site
56
59
  view
60
+ scroll
57
61
  visual-explain
58
62
  tui-design
59
63
  )
@@ -19,8 +19,11 @@ set -euo pipefail
19
19
  # remove-name to keep-name, stores remove-name as alias
20
20
  # create-harvest <id> <topic> <intent> <initiator>
21
21
  # Create a Harvest node and link to initiator (WAL-backed)
22
- # create-harvest-session <harvest-id> <session-id> <person-name>
23
- # Create a HarvestSession linked to a Harvest and Person
22
+ # create-harvest-session <harvest-id> <session-id> <person-name> [status]
23
+ # Create a HarvestSession linked to a Harvest and Person.
24
+ # status defaults to 'active' (back-compat). Valid values:
25
+ # pending | active | answered | complete | incorporated.
26
+ # Async respondents should be created with 'pending'.
24
27
  # record-harvest-turn <session-id> <turn> <question> <intent> [answer] [eval]
25
28
  # Record a question-answer turn in a HarvestSession
26
29
  # complete-harvest <harvest-id> <artifact-path>
@@ -42,6 +45,7 @@ if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
42
45
  echo " merge-person <keep> <rm> Merge two Person nodes"
43
46
  echo " claim-handoff <sid> <ho> Link implementing session to handoff"
44
47
  echo " create-pr <sid> <num> <repo> <author> [title]"
48
+ echo " register-artifact <id> <url> <title> <type> <author> <topics> <excerpt>"
45
49
  echo " create-harvest / complete-harvest / record-harvest-turn"
46
50
  echo " wal-status Show WAL pending count"
47
51
  exit 0
@@ -202,6 +206,39 @@ case "$OP" in
202
206
  bash "$GS" query "$CYPHER" "$PARAMS"
203
207
  ;;
204
208
 
209
+ register-artifact)
210
+ # Index a published (hosted) artifact as an Artifact node so the finder can
211
+ # retrieve it from the graph in connected mode — by content (title/topics/
212
+ # excerpt CONTAINS) and by relationship (who made it, which quest). The node
213
+ # id is the registry FILENAME STEM (date-author-slug), matching sync-graph.sh
214
+ # so a later /save sync upserts the same node instead of duplicating it.
215
+ # args: <id> <url> <title> <type> <author-handle> <topics-csv> <excerpt>
216
+ A_ID="${1:?missing id}"
217
+ A_URL="${2:?missing url}"
218
+ A_TITLE="${3:-}"
219
+ A_TYPE="${4:-artifact}"
220
+ A_AUTHOR="$(printf '%s' "${5:-}" | tr '[:upper:]' '[:lower:]' | awk '{print $1}')"
221
+ A_TOPICS_JSON="$(printf '%s' "${6:-}" | jq -Rc 'split(",") | map(gsub("^\\s+|\\s+$";"")) | map(select(length > 0))' 2>/dev/null || echo '[]')"
222
+ A_EXCERPT="$(printf '%s' "${7:-}" | tr '\n' ' ' | cut -c1-2000)"
223
+ CYPHER="
224
+ MERGE (a:Artifact {id: \$id})
225
+ ON CREATE SET a.created = datetime()
226
+ SET a.title = \$title, a.type = \$type, a.url = \$url,
227
+ a.topics = \$topics, a.excerpt = \$excerpt, a.published = true
228
+ WITH a
229
+ OPTIONAL MATCH (p:Person)
230
+ WHERE \$author <> '' AND (toLower(p.name) = \$author OR p.github = \$author)
231
+ FOREACH (_ IN CASE WHEN p IS NOT NULL THEN [1] ELSE [] END |
232
+ MERGE (a)-[:CONTRIBUTED_BY]->(p))
233
+ RETURN a.id AS id
234
+ "
235
+ PARAMS="$(jq -n --arg id "$A_ID" --arg url "$A_URL" --arg title "$A_TITLE" --arg type "$A_TYPE" \
236
+ --arg author "$A_AUTHOR" --argjson topics "$A_TOPICS_JSON" --arg excerpt "$A_EXCERPT" \
237
+ '{id: $id, url: $url, title: $title, type: $type, author: $author, topics: $topics, excerpt: $excerpt}')"
238
+ bash "$SCRIPT_DIR/bin/graph-wal.sh" append "$CYPHER" "$PARAMS" 2>/dev/null || true
239
+ bash "$GS" query "$CYPHER" "$PARAMS"
240
+ ;;
241
+
205
242
  check-implements)
206
243
  SID="${1:?missing session-id}"
207
244
  bash "$GS" query "
@@ -305,16 +342,30 @@ case "$OP" in
305
342
  HID="${1:?missing harvest-id}"
306
343
  HSID="${2:?missing session-id}"
307
344
  PERSON="${3:?missing person-name}"
308
- bash "$GS" query "
345
+ STATUS="${4:-active}"
346
+ case "$STATUS" in
347
+ pending|active|answered|complete|incorporated) ;;
348
+ *) jq -n --arg s "$STATUS" '{error:("invalid status: "+$s),valid:["pending","active","answered","complete","incorporated"]}' >&2; exit 2 ;;
349
+ esac
350
+ CYPHER="
309
351
  MATCH (h:Harvest {id: \$hid})
310
352
  MERGE (hs:HarvestSession {id: \$hsid})
311
- ON CREATE SET hs.status = 'active', hs.created = datetime()
353
+ ON CREATE SET hs.status = \$status, hs.created = datetime()
312
354
  MERGE (h)-[:HAS_SESSION]->(hs)
313
355
  WITH hs
314
356
  MATCH (p:Person) WHERE toLower(p.name) = toLower(\$person) OR p.github = \$person
315
357
  MERGE (hs)-[:WITH]->(p)
316
358
  RETURN hs.id AS id, hs.status AS status
317
- " "$(jq -n --arg hid "$HID" --arg hsid "$HSID" --arg person "$PERSON" '{hid: $hid, hsid: $hsid, person: $person}')"
359
+ "
360
+ PARAMS="$(jq -n --arg hid "$HID" --arg hsid "$HSID" --arg person "$PERSON" --arg status "$STATUS" '{hid: $hid, hsid: $hsid, person: $person, status: $status}')"
361
+ bash "$SCRIPT_DIR/bin/graph-wal.sh" append "$CYPHER" "$PARAMS" 2>/dev/null || true
362
+ OUT="$(bash "$GS" query "$CYPHER" "$PARAMS")"
363
+ if [ "$(echo "$OUT" | jq -r '.results | length' 2>/dev/null)" = "0" ]; then
364
+ jq -n --arg hid "$HID" --arg hsid "$HSID" --arg person "$PERSON" \
365
+ '{error:"create-harvest-session: harvest or person not found",hid:$hid,hsid:$hsid,person:$person}' >&2
366
+ exit 3
367
+ fi
368
+ echo "$OUT"
318
369
  ;;
319
370
 
320
371
  record-harvest-turn)
@@ -42,6 +42,22 @@ _millis() {
42
42
  fi
43
43
  }
44
44
 
45
+ # --- Optional read-through cache (opt-in via EGREGORE_GRAPH_CACHE_TTL) ---
46
+ # Read-only queries can be served from a local cache that bin/attendant.sh
47
+ # keeps warm in the background, so callers on the launch path (session-start
48
+ # greeting) pay zero network round-trips when warm. TTL in seconds; unset/0
49
+ # disables. Mutating queries are never cached.
50
+ _CACHE_TTL="${EGREGORE_GRAPH_CACHE_TTL:-0}"
51
+ _CACHE_DIR="$HOME/.egregore/graph-cache/$(echo -n "$SCRIPT_DIR" | cksum | cut -d' ' -f1)"
52
+
53
+ _cache_key() {
54
+ echo -n "$1|${2:-}" | cksum | cut -d' ' -f1
55
+ }
56
+
57
+ _is_read_query() {
58
+ ! echo "$1" | grep -qiE '\b(MERGE|CREATE|SET|DELETE|DETACH|REMOVE)\b'
59
+ }
60
+
45
61
  if [ -n "$API_URL" ] && [ -n "$API_KEY" ]; then
46
62
  # === API MODE: Call Egregore API gateway ===
47
63
 
@@ -54,6 +70,19 @@ if [ -n "$API_URL" ] && [ -n "$API_KEY" ]; then
54
70
  params="{}"
55
71
  fi
56
72
 
73
+ local cache_file=""
74
+ if [ "$_CACHE_TTL" != "0" ] && _is_read_query "$cypher"; then
75
+ cache_file="$_CACHE_DIR/$(_cache_key "$cypher" "$params").json"
76
+ if [ -f "$cache_file" ]; then
77
+ local now cts
78
+ now=$(date +%s)
79
+ cts=$(jq -r '.ts // 0' "$cache_file" 2>/dev/null || echo 0)
80
+ if [ $(( now - cts )) -lt "$_CACHE_TTL" ]; then
81
+ jq -c '.result' "$cache_file" 2>/dev/null && return 0
82
+ fi
83
+ fi
84
+ fi
85
+
57
86
  local body
58
87
  body=$(jq -n --arg stmt "$cypher" --argjson params "$params" \
59
88
  '{statement: $stmt, parameters: $params}')
@@ -86,6 +115,13 @@ if [ -n "$API_URL" ] && [ -n "$API_KEY" ]; then
86
115
  bash "$SCRIPT_DIR/bin/telemetry.sh" emit "graph_query" \
87
116
  "$(jq -n --argjson latency "$latency_ms" '{latency_ms: $latency}')" 2>/dev/null &
88
117
 
118
+ # Store for the attendant to keep warm (query+params travel with the result)
119
+ if [ -n "$cache_file" ]; then
120
+ mkdir -p "$_CACHE_DIR" 2>/dev/null
121
+ jq -n --arg q "$cypher" --argjson p "$params" --argjson ts "$(date +%s)" --argjson r "$response" \
122
+ '{query: $q, params: $p, ts: $ts, result: $r}' > "$cache_file" 2>/dev/null || true
123
+ fi
124
+
89
125
  echo "$response"
90
126
  }
91
127