create-egregore 0.18.0 → 0.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/package.json +4 -3
  2. package/runtime/codex/AGENTS.md +4 -292
  3. package/runtime/codex/bin/activity-data.sh +3 -6
  4. package/runtime/codex/bin/agent.sh +0 -0
  5. package/runtime/codex/bin/codex-install-skills.sh +0 -0
  6. package/runtime/codex/bin/codex-render-spec.mjs +0 -0
  7. package/runtime/codex/bin/codex-session-start.sh +0 -0
  8. package/runtime/codex/bin/codex-skill-render.mjs +0 -0
  9. package/runtime/codex/bin/codex-sync-skills.sh +0 -0
  10. package/runtime/codex/bin/dashboard-data.sh +2 -6
  11. package/runtime/codex/bin/graph-batch.sh +0 -0
  12. package/runtime/codex/bin/graph-op.sh +5 -22
  13. package/runtime/codex/bin/graph-wal.sh +0 -0
  14. package/runtime/codex/bin/graph.sh +0 -0
  15. package/runtime/codex/bin/handoff-pr-backfill.sh +0 -0
  16. package/runtime/codex/bin/handoff-run.sh +6 -144
  17. package/runtime/codex/bin/handoff-save-egregore.sh +0 -0
  18. package/runtime/codex/bin/index-handoff.sh +0 -0
  19. package/runtime/codex/bin/lib/artifact-id.sh +0 -0
  20. package/runtime/codex/bin/lib/config.sh +0 -0
  21. package/runtime/codex/bin/lib/context.sh +0 -48
  22. package/runtime/codex/bin/lib/ensure-develop.sh +0 -0
  23. package/runtime/codex/bin/lib/greeting.sh +2 -20
  24. package/runtime/codex/bin/lib/hash.sh +0 -0
  25. package/runtime/codex/bin/lib/time.sh +0 -0
  26. package/runtime/codex/bin/notify.sh +0 -0
  27. package/runtime/codex/bin/publish-artifact.sh +1 -15
  28. package/runtime/codex/bin/publish-references.sh +0 -0
  29. package/runtime/codex/bin/repo-state.sh +0 -0
  30. package/runtime/codex/bin/telemetry.sh +0 -0
  31. package/runtime/codex/bin/worktree-create.sh +8 -5
  32. package/runtime/codex/bin/worktree-remove.sh +0 -0
  33. package/runtime/codex/bin/worktree.sh +4 -5
  34. package/runtime/codex/manifest.json +3 -10
  35. package/runtime/codex/.codex/skills/comment/SKILL.md +0 -25
  36. package/runtime/codex/.codex/skills/edit/SKILL.md +0 -25
  37. package/runtime/codex/.codex/skills/edit-process/SKILL.md +0 -25
  38. package/runtime/codex/bin/lib/edit-loopback.mjs +0 -652
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-egregore",
3
- "version": "0.18.0",
3
+ "version": "0.18.1",
4
4
  "description": "Set up Egregore for your team in one command — installs the egregore launcher and your choice of Claude Code or Codex.",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -10,9 +10,10 @@
10
10
  "node": ">=18"
11
11
  },
12
12
  "scripts": {
13
- "test": "node --test test/context.test.js test/parse-join-target.test.js test/skip-repos.test.js test/onboarding-shortcircuit.test.js test/welcome-screen.test.js test/runtime-selection.test.js test/command-generation.test.js test/auth-gh.test.js test/gauntlet-fixes.test.js test/codex-runtime.test.js test/installer-runtime-source.test.js",
13
+ "build:runtime": "node scripts/build-runtime-bundle.mjs",
14
+ "test": "node scripts/build-runtime-bundle.mjs --check && node --test test/context.test.js test/parse-join-target.test.js test/skip-repos.test.js test/onboarding-shortcircuit.test.js test/welcome-screen.test.js test/runtime-selection.test.js test/command-generation.test.js test/auth-gh.test.js test/gauntlet-fixes.test.js test/codex-runtime.test.js test/installer-runtime-source.test.js",
14
15
  "test:e2e": "node --test test/e2e-local.test.js",
15
- "test:all": "node --test test/"
16
+ "test:all": "node scripts/build-runtime-bundle.mjs --check && node --test test/"
16
17
  },
17
18
  "files": [
18
19
  "bin/",
@@ -12,22 +12,12 @@ protocol.
12
12
  3. Read `memory/people/` to learn collaborator handles.
13
13
  4. Run `bin/agent.sh activity --for <your-handle>` to inspect handoffs and
14
14
  pending questions addressed to you.
15
- 5. Before code changes, use `bin/agent.sh branch --topic "<work topic>"`
16
- when you need the runtime-neutral equivalent of Claude Code's worktree hook.
17
- Continue work from the printed `worktree:` path.
18
15
 
19
16
  ## Communication
20
17
 
21
- Shell-level agents should use the runtime-neutral bridge instead of Claude Code
22
- slash commands:
18
+ Use the runtime-neutral bridge instead of Claude Code slash commands:
23
19
 
24
20
  ```bash
25
- bin/agent.sh branch --topic "auth review"
26
- bin/agent.sh save --message "Save: auth review" --topic "auth review"
27
- bin/agent.sh wrap --from alice --topic "auth review" \
28
- --summary "Implemented the OAuth callback parser and documented follow-ups." \
29
- --body "Open threads: review error cases and browser redirects."
30
-
31
21
  bin/agent.sh handoff --from alice --to bob --topic "auth review" \
32
22
  --body "Implemented the OAuth callback parser. Bob should review error cases."
33
23
 
@@ -43,286 +33,8 @@ Each command writes to the existing Git-backed `memory/` repository and pushes
43
33
  when the memory repo has an `origin` remote. Agents that cannot run shell may
44
34
  write the same markdown files directly, following `docs/AGENT-PROTOCOL.md`.
45
35
 
46
- ## Handoffs vs Emissaries
47
-
48
- `bin/agent.sh handoff` is the runtime-neutral path for internal team
49
- session-handoffs: it writes to `memory/handoffs/`, updates the index, and uses
50
- the same `bin/handoff-run.sh` machinery as Claude Code's `/handoff`.
51
-
52
- Portable external capsules are **emissaries**, not team handoffs. Claude Code
53
- routes those through `/emissary`; non-Claude agents should use the
54
- `egregore-emissary` CLI/skill when it is installed. Do not use the deprecated
55
- `egregore-handoff` CLI for Egregore project `/handoff` work.
56
-
57
36
  ## Compatibility
58
37
 
59
- Claude Code and Codex users can use Egregore slash workflows such as
60
- `/handoff`, `/activity`, `/ask`, and `/save`. Shell-only agents should use
61
- `bin/agent.sh` and the file protocol. Both paths converge on the same
62
- `memory/` files.
63
-
64
- <!-- BEGIN GENERATED EGREGORE CODEX SPEC — derived from CLAUDE.md by bin/codex-render-spec.mjs; do not edit by hand -->
65
-
66
- ## Egregore on Codex
67
-
68
- You are a collaborator inside Egregore — a shared intelligence layer for organizations using AI coding agents. You operate through Git-based shared memory, Egregore skills, and conventions that accumulate knowledge across sessions and people. You are not a tool. You are a participant.
69
-
70
- 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.
71
-
72
- ---
73
-
74
- ## Identity & Upstream
75
-
76
- This is an Egregore instance — a downstream fork of the upstream framework at `egregore-labs/egregore`. The framework (`bin/`, `.claude/skills/`, `.claude/hooks/`, `.claude/context/`, `CLAUDE.md`, `skills/`) is synced from upstream on every session start and via `$update`. It is not authored in this repo.
77
-
78
- **Where changes belong:**
79
- - **Framework is outdated or a skill is missing:** run `$update` first. It pulls the latest from upstream.
80
- - **Framework behavior needs changing or has a bug:** `$contribute` opens a PR against `egregore-labs/egregore`, or file a GitHub issue on that repo. Never patch framework files locally to "just fix it here" — the next `$update` will overwrite your edits and other instances won't get the fix.
81
- - **Org-level work** (memory, org-specific knowledge, managed repos, `egregore.json`): `$save` to this repo.
82
-
83
- When a user reports broken or missing framework behavior, the first question is "when did you last `$update`?"
84
-
85
- ---
86
-
87
- ## On Launch — MANDATORY FIRST ACTION
88
-
89
- 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`.
90
-
91
- ---
92
-
93
- ## After Greeting — BRANCH ON FIRST RESPONSE
94
-
95
- **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:
96
-
97
- 1. Derive a topic slug from what the user said (kebab-case, 2–4 words)
98
- 2. Run `bin/agent.sh branch --topic "<topic>"` — it creates a work branch from `origin/develop` in a task worktree (`dev/{author}/{slug}`, or `feature/{slug}` / `bugfix/{slug}` when the topic reads as a feature or fix) and prints the worktree path. Continue all file work from that printed path.
99
- 3. Confirm: `On {branch} (worktree).`
100
-
101
- **Fallback:** If `bin/agent.sh branch` fails, use `git checkout -b dev/{author}/{slug} origin/develop`.
102
-
103
- 4. Update graph (fire-and-forget): `bash bin/graph-op.sh set-topic "$(cat .egregore-session-id 2>/dev/null)" "topic from slug" "dev/author/slug" 2>/dev/null &`
104
-
105
- ### Handoff claiming
106
-
107
- If `addressed_to_user` handoffs exist and the user is picking one up, create the IMPLEMENTS link after branch creation:
108
- ```bash
109
- bash bin/graph-op.sh claim-handoff "$SESSION_ID" "$HANDOFF_SESSION_ID" 2>/dev/null &
110
- ```
111
-
112
- **Auto-checkout repos from handoff**: After claiming, check the `addressed_rich` context for `repoState`. If the handoff includes repo state (non-empty `repoState` array), check out the handoff's branches in each managed repo:
113
-
114
- ```bash
115
- PARENT_DIR="$(cd .. && pwd)"
116
- # For each entry in repoState:
117
- REPO_DIR="$PARENT_DIR/$REPO_NAME"
118
- if [ -d "$REPO_DIR/.git" ] || [ -f "$REPO_DIR/.git" ]; then
119
- git -C "$REPO_DIR" fetch origin "$BRANCH" --quiet 2>/dev/null
120
- git -C "$REPO_DIR" checkout "$BRANCH" 2>/dev/null || \
121
- git -C "$REPO_DIR" checkout -b "$BRANCH" "origin/$BRANCH" 2>/dev/null
122
- fi
123
- ```
124
-
125
- Report results: `✓ Checked out {branch} in {repo1}, {repo2}`. If a branch no longer exists (PR was merged): `◐ {repo}: PR #{N} merged — on {base}`. If `repoState` is absent or empty, skip auto-checkout silently.
126
-
127
- **Exceptions** — skip branching when:
128
- - The user explicitly created or named a branch themselves
129
- - Already on a working branch AND the user's intent continues the current branch's topic
130
-
131
- **Topic pivot while on a working branch:** If the user describes work **unrelated** to the current branch's topic, treat it as a new topic and create a new branch (`bin/agent.sh branch --topic "<new topic>"`). Do NOT mix unrelated work on one branch.
132
-
133
- If on develop after two messages, create a branch immediately from whatever context you have.
134
-
135
- ### Branch-guard protocol
136
-
137
- The `.codex/hooks/branch-guard.js` PreToolUse hook (enabled by the launcher via `--enable hooks`) denies writes, patches, and commits on `develop`/`main`/`master`. When it fires, choose between two paths based on how clear the topic is:
138
-
139
- - **Topic is obvious from the last turn** (user said "fix X", "add Y feature") → auto-branch silently with `bin/agent.sh branch --topic "<topic>"` (or the git checkout fallback), then continue. Say one sentence ("Creating `dev/{author}/{slug}`.") so the user isn't confused about why a new branch appeared.
140
- - **Topic is ambiguous** (open-ended exploration, user didn't specify what we're building yet) → ask **before** branching, in plain text with numbered options (Codex has no structured question tool):
141
-
142
- ```text
143
- Need a working branch first. Topic?
144
- 1. <suggested slug from context>
145
- 2. <alternative slug>
146
- 3. Other: (name it)
147
- ```
148
-
149
- Wait for the reply, then branch.
150
-
151
- If this Codex build does not support hooks, follow the same discipline as a standing instruction: never write or commit on develop/main/master.
152
-
153
- ### Onboarding exception
154
-
155
- If the startup card output contains `onboarding_needed`, invoke the `$onboarding` skill instead of greeting.
156
-
157
- ---
158
-
159
- ## Config Files
160
-
161
- - **`egregore.json`** — committed. Non-secret org config: `org_name`, `github_org`, `memory_repo`, `slug`, `mode`. `api_url` is connected-mode only. **Never put secrets here.**
162
- - **`.env`** — gitignored. Personal secrets. Local mode: `GITHUB_TOKEN` only. Connected mode: `GITHUB_TOKEN` + `EGREGORE_API_KEY`. **Never use `source .env`** — use `grep '^KEY=' .env | cut -d'=' -f2-`.
163
-
164
- In connected mode, infrastructure credentials (Neo4j, Telegram) live on the API server only — `bin/graph.sh` and `bin/notify.sh` route through the API gateway.
165
-
166
- ---
167
-
168
- ## Knowledge Graph
169
-
170
- **Connected mode only.** Always use `bin/graph.sh` for Neo4j queries — never construct curl calls directly. See DEVELOPMENT.md §1 for usage examples and current schema. In local mode there is no graph; read `memory/` directly.
171
-
172
- ---
173
-
174
- ## Notifications
175
-
176
- **Connected mode only.** Always use `bin/notify.sh` for Telegram notifications — never construct API calls directly. See DEVELOPMENT.md §1 for usage examples. In local mode there are no live notifications.
177
-
178
- ---
179
-
180
- ---
181
-
182
- ## Onboarding
183
-
184
- When `onboarding_complete` is false in `.egregore-state.json`, invoke `$onboarding`. The command is the single source of truth — do NOT run steps inline.
185
-
186
- ---
187
-
188
- ## Transparency Beat
189
-
190
- After the first silent bash command in any session, mention once:
191
-
192
- > I run commands directly to keep things fast — you can see everything in the session log, and change permissions in `.claude/settings.json` anytime.
193
-
194
- Never repeat it.
195
-
196
- ---
197
-
198
- ## Memory
199
-
200
- `memory/` is a symlink to the memory repo defined in `egregore.json`. Key directories:
201
- - `people/` — team directory
202
- - `handoffs/` — session handoffs + `index.md`
203
- - `knowledge/decisions/` — org decisions
204
- - `knowledge/patterns/` — emergent patterns
205
- - `infrastructure/` — service registry (URLs, names, credential locations)
206
-
207
- Always use HTTPS for git operations — `github-auth.sh` handles credential storage.
208
-
209
- ---
210
-
211
- ## Git Workflow
212
-
213
- `develop` branch model with deferred, topic-based branching. Users never interact with git directly.
214
-
215
- ```
216
- main ← stable (/release)
217
- develop ← integration (PRs land here)
218
- dev/{author}/{topic-slug} | feature/{slug} | bugfix/{slug}
219
- ```
220
-
221
- - **On launch**: syncs develop + memory. Does NOT create a branch.
222
- - **Branch creation**: MANDATORY on first work-related message (see above).
223
- - **Resuming**: rebase onto develop and continue.
224
- - **If on develop after two messages**: create branch immediately.
225
- - **`$save`**: pushes working branch, PR to develop. Auto-merges markdown-only PRs.
226
- - **Memory repo**: stays on main (separate repo, auto-merge).
227
- - **Never push directly to main or develop.** All changes flow through PRs.
228
-
229
- ### Managed Repos
230
-
231
- Repos in `egregore.json` → `repos[]` are cloned as siblings (`../{repo}/`). Each entry can be a string or `{"name": "...", "description": "..."}`. Match user intent to the right repo using `description`. Same branching strategy. Use `git -C` with absolute paths — never `cd` into repos. `$save` scans all managed repos for uncommitted changes.
232
-
233
- ---
234
-
235
- ## Working Conventions
236
-
237
- - Check `memory/knowledge/` before starting unfamiliar work
238
- - Document significant decisions in `memory/knowledge/decisions/`
239
- - After substantial sessions, log to `memory/handoffs/` and update `index.md`
240
-
241
- ---
242
-
243
- ## Command Awareness
244
-
245
- 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.
246
-
247
- Invoke commands from user intent — don't wait for the slash. Each command file has a `## When to invoke` section. Load it for the full spec.
248
-
249
- **Core loop** — `$activity` `$dashboard` `$handoff` `$wrap` `$save` `$reflect` `$todo`
250
- **Knowledge** — `$deep-reflect` `$archive` `$note` `$add` `$meeting` `$ingest`
251
- **Identity** — `$me` (view profile or set display name)
252
- **Coordination** — `$ask` `$quest` `$issue` `$invite` `$delete-user` `$announce`
253
- **Connectors** — `$telegram-connect` (Telegram group setup)
254
- **Git** — `$branch` `$commit` `$push` `$pr` `$save` `$review-pr` `$contribute`
255
- **Spirits** — `$summon` (persistent agent processes)
256
- **Infra** — `$setup` `$update` `$pull` `$env` `$infra` `$sync-repos` `$release` `$checkup`
257
-
258
- **Disambiguation:**
259
- - Knowledge: `$reflect` (share-ready) · `$note` (half-baked) · `$deep-reflect` (cross-reference) · `$archive` (AI patterns)
260
- - Status: `$dashboard` (personal) · `$activity` (org-wide)
261
- - Ending: `$wrap` (personal closure) · `$handoff` (notes for others) · `$save` (still working)
262
- - Tasks: `$todo` (personal) · `$quest` (team exploration) · `$issue` (something broken)
263
- - Questions: `$ask [person]` (async) · just ask (agent answers from context)
264
- - Ingestion: `$ingest meeting` · `$ingest user-interview` · `$ingest google` · ambiguous → ask which type
265
- - Connectors: `$telegram-connect` (Telegram group) · `$ingest` (bring content in)
266
- - Identity: `$me` — "who am I", "call me oz"
267
- - People: `$invite` (add) · `$delete-user` (remove)
268
- - PRs: `$pr` (create) · `$review-pr` (review)
269
- - Contributing: `$contribute` (upstream framework) · `$save` (org repo) · `$issue` (report bug)
270
- - Agents: `$summon` (design through questions) · `/loop` (quick recurring schedule)
271
- - Announcements: `$announce` (broadcast to group) · `$handoff` (structured to a person) · `bin/notify.sh send` (DM one person)
272
-
273
- ---
274
-
275
- ## Socratic Questioning (MANDATORY)
276
-
277
- **Triggers**: "ask me questions", "question me", "help me think through", or any request to be questioned.
278
-
279
- Codex has no structured question tool — render each batch as compact numbered questions in plain text, each with 2–4 lettered options plus an `Other:` line, then STOP and wait for the user's answers. Derive 2–4 context-specific questions per batch. Iteratively deepen based on answers. Converge toward decisions. After 4–5 rounds, synthesize and propose next steps. Route insights to `$reflect`.
280
-
281
- **Rules:** Max 4 questions per batch. When choices aren't mutually exclusive, say "pick any that apply".
282
-
283
- ---
284
-
285
- ## Telemetry
286
-
287
- Privacy-respecting, opt-out telemetry. After every slash command, emit fire-and-forget:
288
- `bash bin/telemetry.sh emit "command" '{"command":"save"}' 2>/dev/null &`
289
-
290
- Never collected: file contents, code, env var values, conversation content.
291
- On first session (if `telemetry_noticed` not set in state file), mention the notice once, then set `telemetry_noticed: true`. Full spec: `.claude/context/telemetry.md`.
292
-
293
- ---
294
-
295
- ## Mode
296
-
297
- Egregore runs in one of two configurations, set by `mode` in `egregore.json`. Detect with `_detect_mode` in `bin/lib/config.sh`, or check `.mode` / `.api_url` directly.
298
-
299
- **Local mode** (`"mode": "local"` or no `api_url`) — the default, self-contained configuration. The OSS experience. Memory files are the source of truth. All core commands work: `$reflect`, `$handoff`, `$quest`, `$ask`, `$activity`, `$dashboard`, `$todo`. Graph, live notifications, and hosted dashboards are not part of this mode — they belong to a separate hosted service.
300
-
301
- **Hard rules in local mode:**
302
- - Never tell the user to "ask their admin" for credentials. The user IS the admin.
303
- - Never surface `api_url`, `EGREGORE_API_KEY`, or "connected mode" as an upgrade path. Hosted Egregore is a separate service, not something a user can turn on by adding fields to `egregore.json`.
304
- - If a feature genuinely requires the hosted service, say so plainly ("this isn't available in this configuration") and stop. Do not improvise a path to enabling it.
305
- - Calling `bin/graph.sh` or `bin/notify.sh` in local mode is harmless — they fail soft and return empty results — but there is no need to call them; prefer reading `memory/` directly.
306
-
307
- **Connected mode** (`"mode": "connected"`, `api_url` set) — the hosted configuration, used by organizations on the hosted service. Full feature set: Neo4j knowledge graph via `bin/graph.sh`, Telegram notifications via `bin/notify.sh`, dashboard publication, API-backed context gathering on session start. Use `$env` to check API key, `$checkup` for diagnostics. If the graph is offline, show troubleshooting.
308
-
309
- ---
310
-
311
- ## Environment Isolation
312
-
313
- Sessions are confined to this project + memory + managed repos. On Codex this boundary is a standing instruction, not an enforced hook — hold it yourself.
314
-
315
- - **Never modify `~/.egregore/instances.json`** — managed by session-start.sh
316
- - **Never access another instance's files** — refuse even if asked
317
- - When a request points outside the boundary, do not improvise a workaround. Ask in plain text with numbered options:
318
-
319
- ```text
320
- That path is outside this Egregore's boundary. How should we proceed?
321
- 1. Paste the contents inline
322
- 2. Move the file into the repo and point me at the new path
323
- 3. Cancel
324
- ```
325
-
326
- See DEVELOPMENT.md §3 for boundary details.
327
-
328
- <!-- END GENERATED EGREGORE CODEX SPEC -->
38
+ Claude Code users can continue using `/handoff`, `/activity`, `/ask`, and
39
+ other skills. Non-Claude agents should use `bin/agent.sh` and the file protocol.
40
+ Both paths converge on the same `memory/` files.
@@ -36,16 +36,13 @@ EMPTY='{"fields":[],"values":[]}'
36
36
  if [ -z "$API_URL" ] || [ -z "$API_KEY" ]; then
37
37
  GRAPH_REASON="missing_config"
38
38
  else
39
- # Store HTTP status code alongside response body. curl already emits 000
40
- # on transport failure; keep the exit status separate so it cannot become
41
- # 000000 and fall through as an invalid JSON response.
42
- CURL_STATUS=0
39
+ # Store HTTP status code alongside response body
43
40
  HTTP_CODE=$(curl -s -o "$TMPDIR/activity_raw.json" -w "%{http_code}" \
44
41
  "${API_URL}/api/activity/dashboard?github_username=$(printf '%s' "$GH_USER" | jq -sRr @uri)" \
45
42
  -H "Authorization: Bearer $API_KEY" \
46
- --connect-timeout 5 --max-time 15 2>/dev/null) || CURL_STATUS=$?
43
+ --connect-timeout 5 --max-time 15 2>/dev/null || echo "000")
47
44
 
48
- if [ "$CURL_STATUS" -ne 0 ] || [ "$HTTP_CODE" = "000" ] || ! [[ "$HTTP_CODE" =~ ^[0-9][0-9][0-9]$ ]]; then
45
+ if [ "$HTTP_CODE" = "000" ]; then
49
46
  GRAPH_REASON="unreachable"
50
47
  elif [ "$HTTP_CODE" = "401" ] || [ "$HTTP_CODE" = "403" ]; then
51
48
  GRAPH_REASON="auth_error"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -44,16 +44,12 @@ SESSION_ID=$(cat "$HOME/.egregore/session-${PROJ_HASH}.id" 2>/dev/null || echo "
44
44
  if [ -z "$API_URL" ] || [ -z "$API_KEY" ]; then
45
45
  GRAPH_REASON="missing_config"
46
46
  else
47
- # Keep curl's transport status separate from %{http_code}; otherwise a
48
- # failed request can concatenate 000 + fallback text and look like an
49
- # unexpected API response.
50
- CURL_STATUS=0
51
47
  HTTP_CODE=$(curl -s -o "$TMPDIR/dashboard_raw.json" -w "%{http_code}" \
52
48
  "${API_URL}/api/personal/dashboard?github_username=$(printf '%s' "$GH_USER" | jq -sRr @uri)&time_range=$(printf '%s' "$TIME_RANGE" | jq -sRr @uri)&session_id=$(printf '%s' "$SESSION_ID" | jq -sRr @uri)" \
53
49
  -H "Authorization: Bearer $API_KEY" \
54
- --connect-timeout 5 --max-time 15 2>/dev/null) || CURL_STATUS=$?
50
+ --connect-timeout 5 --max-time 15 2>/dev/null || echo "000")
55
51
 
56
- if [ "$CURL_STATUS" -ne 0 ] || [ "$HTTP_CODE" = "000" ] || ! [[ "$HTTP_CODE" =~ ^[0-9][0-9][0-9]$ ]]; then
52
+ if [ "$HTTP_CODE" = "000" ]; then
57
53
  GRAPH_REASON="unreachable"
58
54
  elif [ "$HTTP_CODE" = "401" ] || [ "$HTTP_CODE" = "403" ]; then
59
55
  GRAPH_REASON="auth_error"
File without changes
@@ -19,11 +19,8 @@ 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> [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'.
22
+ # create-harvest-session <harvest-id> <session-id> <person-name>
23
+ # Create a HarvestSession linked to a Harvest and Person
27
24
  # record-harvest-turn <session-id> <turn> <question> <intent> [answer] [eval]
28
25
  # Record a question-answer turn in a HarvestSession
29
26
  # complete-harvest <harvest-id> <artifact-path>
@@ -308,30 +305,16 @@ case "$OP" in
308
305
  HID="${1:?missing harvest-id}"
309
306
  HSID="${2:?missing session-id}"
310
307
  PERSON="${3:?missing person-name}"
311
- STATUS="${4:-active}"
312
- case "$STATUS" in
313
- pending|active|answered|complete|incorporated) ;;
314
- *) jq -n --arg s "$STATUS" '{error:("invalid status: "+$s),valid:["pending","active","answered","complete","incorporated"]}' >&2; exit 2 ;;
315
- esac
316
- CYPHER="
308
+ bash "$GS" query "
317
309
  MATCH (h:Harvest {id: \$hid})
318
310
  MERGE (hs:HarvestSession {id: \$hsid})
319
- ON CREATE SET hs.status = \$status, hs.created = datetime()
311
+ ON CREATE SET hs.status = 'active', hs.created = datetime()
320
312
  MERGE (h)-[:HAS_SESSION]->(hs)
321
313
  WITH hs
322
314
  MATCH (p:Person) WHERE toLower(p.name) = toLower(\$person) OR p.github = \$person
323
315
  MERGE (hs)-[:WITH]->(p)
324
316
  RETURN hs.id AS id, hs.status AS status
325
- "
326
- PARAMS="$(jq -n --arg hid "$HID" --arg hsid "$HSID" --arg person "$PERSON" --arg status "$STATUS" '{hid: $hid, hsid: $hsid, person: $person, status: $status}')"
327
- bash "$SCRIPT_DIR/bin/graph-wal.sh" append "$CYPHER" "$PARAMS" 2>/dev/null || true
328
- OUT="$(bash "$GS" query "$CYPHER" "$PARAMS")"
329
- if [ "$(echo "$OUT" | jq -r '.results | length' 2>/dev/null)" = "0" ]; then
330
- jq -n --arg hid "$HID" --arg hsid "$HSID" --arg person "$PERSON" \
331
- '{error:"create-harvest-session: harvest or person not found",hid:$hid,hsid:$hsid,person:$person}' >&2
332
- exit 3
333
- fi
334
- echo "$OUT"
317
+ " "$(jq -n --arg hid "$HID" --arg hsid "$HSID" --arg person "$PERSON" '{hid: $hid, hsid: $hsid, person: $person}')"
335
318
  ;;
336
319
 
337
320
  record-harvest-turn)
File without changes
File without changes
File without changes
@@ -203,13 +203,14 @@ PID_GRAPH=$!
203
203
  ARTIFACTS_JSON="[]"
204
204
  if [ "$MODE" = "connected" ]; then
205
205
  YY=$(date +%Y); MM=$(date +%-m); DD_NUM=$(date +%-d)
206
+ PARAMS=$(jq -nc --arg gh "$AUTHOR" '{gh:$gh}')
206
207
  RAW=$(bash "$SCRIPT_DIR/bin/graph.sh" query "
207
208
  MATCH (a:Artifact)-[:CONTRIBUTED_BY]->(p:Person {github: \$gh})
208
209
  WHERE a.created >= datetime({year: $YY, month: $MM, day: $DD_NUM})
209
210
  AND NOT 'tutorial-generated' IN coalesce(a.topics, [])
210
211
  RETURN a.title AS title, a.type AS type, a.filePath AS path
211
212
  ORDER BY a.created DESC
212
- LIMIT 5" "$(jq -nc --arg gh "$AUTHOR" '{gh:$gh}')" 2>/dev/null || echo '{}')
213
+ LIMIT 5" "$PARAMS" 2>/dev/null || echo '{}')
213
214
  # Response shape: {"fields":[...], "values":[[...], ...]}. Convert to
214
215
  # an array of {title, type, path} objects.
215
216
  ARTIFACTS_JSON=$(echo "$RAW" | jq -c '
@@ -281,145 +282,24 @@ PID_ARTIFACTS=$!
281
282
  PID_MEMORY=$!
282
283
 
283
284
  # --- Branch C: Publish artifact → Notify ---
284
- #
285
- # Strategy: handoffs publish ONCE, through the emissary write API, using
286
- # `render_mode: "custom"` so the server serves OUR pre-rendered HTML
287
- # verbatim (the same egregore-artifacts handoff template /view/ has
288
- # always used). This gives us:
289
- # - one artifact, one URL (no legacy + new double-publish)
290
- # - permanent egregore.xyz/emissary/e/<id> URL with /raw endpoint
291
- # - handoff design fully owned client-side (egregore-artifacts) — the
292
- # emissary server template is never invoked for handoffs and remains
293
- # completely untouched
294
- #
295
- # Fallback: if emissary publish fails (token missing, network, size cap,
296
- # rendering failure), fall back to the legacy publish-artifact.sh path so
297
- # a handoff still gets a Telegram-able URL.
298
285
  (
299
- V1URL=""
300
286
  AURL=""
301
- if [ "$NO_PUBLISH" = "0" ]; then
302
- EMISSARY_CONFIG="$HOME/.egregore/emissary-config.json"
303
- EMISSARY_TOKEN=""
304
- if [ -f "$EMISSARY_CONFIG" ]; then
305
- EMISSARY_TOKEN=$(jq -r '.auth_token // empty' "$EMISSARY_CONFIG" 2>/dev/null)
306
- fi
307
- EMISSARY_API_URL="${EMISSARY_API_URL:-https://egregore-production-55f2.up.railway.app/api/v1/emissary}"
308
-
309
- if [ -n "$EMISSARY_TOKEN" ]; then
310
- # Step 1: render the handoff to HTML locally using egregore-artifacts.
311
- # Same renderer + template publish-artifact.sh uses today — handoffs
312
- # keep their familiar look.
313
- RENDERED_HTML="$TMPD/handoff.html"
314
- RENDER_OK=0
315
- LOCAL_CLI="$SCRIPT_DIR/packages/egregore-artifacts/bin/cli.js"
316
- if [ "${EGREGORE_USE_PUBLISHED:-0}" != "1" ] && [ -f "$LOCAL_CLI" ] && [ -d "$SCRIPT_DIR/packages/egregore-artifacts/node_modules/react" ]; then
317
- if node "$LOCAL_CLI" handoff "$ABS_FILE" --output "$RENDERED_HTML" >/dev/null 2>&1; then
318
- RENDER_OK=1
319
- fi
320
- else
321
- if npx egregore-artifacts handoff "$ABS_FILE" --output "$RENDERED_HTML" >/dev/null 2>&1; then
322
- RENDER_OK=1
323
- fi
324
- fi
325
-
326
- # Step 2: build the emissary payload. If rendering succeeded AND the
327
- # HTML fits the server's 100KB cap, send it with render_mode=custom.
328
- # If not, fall through to the structured-only path (server-rendered
329
- # via the emissary template — undesirable for handoffs but still
330
- # better than no URL).
331
- if [ "$RENDER_OK" = "1" ] && [ -f "$RENDERED_HTML" ]; then
332
- RENDER_SIZE=$(wc -c < "$RENDERED_HTML" | tr -d ' ')
333
- if [ "$RENDER_SIZE" -gt 0 ] && [ "$RENDER_SIZE" -lt 100000 ]; then
334
- # Author display name
335
- DISPLAY_NAME="$AUTHOR"
336
- PERSON_FILE="$SCRIPT_DIR/memory/people/${AUTHOR}.md"
337
- if [ -f "$PERSON_FILE" ]; then
338
- DISPLAY_NAME=$(head -1 "$PERSON_FILE" | sed 's/^# //')
339
- fi
340
-
341
- # addressed_to: only set if there's a recipient
342
- ADDRESSED_TO="[]"
343
- if [ -n "$RECIPIENT" ]; then
344
- RECIP_HANDLE=$(echo "$RECIPIENT" | tr '[:upper:]' '[:lower:]' | awk '{print $1}')
345
- ADDRESSED_TO=$(jq -nc --arg h "$RECIP_HANDLE" --arg d "$RECIPIENT" '[{"handle":$h,"display":$d}]')
346
- fi
347
-
348
- CLAIM=$(echo "$BRIEFING_LEAD" | cut -c1-200)
349
-
350
- # body.prose is schema-required non-empty even when render_mode is
351
- # "custom" (server validates structurally regardless of render
352
- # mode). The visible page is the custom HTML; prose just needs to
353
- # be non-empty plain text for /raw consumers and validation. Use
354
- # the briefing lead (which the lead/claim is already derived from).
355
- PROSE_TEXT="${BRIEFING_LEAD:-$TOPIC}"
356
-
357
- # Use --rawfile so the rendered HTML is read directly from disk
358
- # into a JSON-string. Faster than shell-var roundtrip and avoids
359
- # any quoting headaches for HTML.
360
- V1JSON=$(jq -nc \
361
- --arg topic "$TOPIC" \
362
- --arg claim "$CLAIM" \
363
- --arg summary "$CLAIM" \
364
- --arg prose "$PROSE_TEXT" \
365
- --argjson addressed "$ADDRESSED_TO" \
366
- --rawfile render_html "$RENDERED_HTML" \
367
- '{
368
- kind: "continuation",
369
- topic: $topic,
370
- claim: $claim,
371
- summary: $summary,
372
- body: { prose: $prose },
373
- audience: {
374
- addressed_to: $addressed,
375
- visible_to: "public",
376
- extendable_by: "anyone"
377
- },
378
- parents: [],
379
- render_mode: "custom",
380
- render_html: $render_html
381
- }')
382
-
383
- V1RESULT=$(curl -sf -X POST "$EMISSARY_API_URL/emissaries" \
384
- -H "Content-Type: application/json" \
385
- -H "Authorization: Bearer $EMISSARY_TOKEN" \
386
- -d "$V1JSON" 2>/dev/null || echo '{}')
387
-
388
- V1URL=$(echo "$V1RESULT" | jq -r '.url // ""' 2>/dev/null || echo "")
389
- fi
390
- fi
391
- fi
392
- fi
393
-
394
- # Fallback: if the emissary publish didn't produce a URL (no token, no
395
- # network, size cap, render failure), fire the legacy publish-artifact.sh
396
- # so handoffs still get a Telegram-able URL.
397
- if [ -z "$V1URL" ] && [ "$NO_PUBLISH" = "0" ] && [ -x "$SCRIPT_DIR/bin/publish-artifact.sh" ]; then
287
+ if [ "$NO_PUBLISH" = "0" ] && [ -x "$SCRIPT_DIR/bin/publish-artifact.sh" ]; then
398
288
  AURL=$(bash "$SCRIPT_DIR/bin/publish-artifact.sh" handoff "$ABS_FILE" \
399
289
  --title "$TOPIC" \
400
290
  --author "$AUTHOR" \
401
291
  --description "$BRIEFING_LEAD" 2>/dev/null || echo "")
402
292
  fi
403
-
404
- # Prefer v1 (emissary-rendered) URL when available; fall back to the
405
- # legacy egregore-artifacts URL only if the v1 publish failed.
406
- NOTIFY_URL=""
407
- if [ -n "$V1URL" ]; then
408
- echo "$V1URL" > "$TMPD/artifact"
409
- NOTIFY_URL="$V1URL"
410
- else
411
- echo "$AURL" > "$TMPD/artifact"
412
- NOTIFY_URL="$AURL"
413
- fi
293
+ echo "$AURL" > "$TMPD/artifact"
414
294
 
415
295
  NS="skipped"
416
296
  if [ "$NO_NOTIFY" = "0" ]; then
417
- if [ -n "$NOTIFY_URL" ]; then
297
+ if [ -n "$AURL" ]; then
418
298
  MSG_NOTIFY="Handoff from ${AUTHOR}: ${TOPIC}
419
299
 
420
300
  \"${BRIEFING_SHORT}\"
421
301
 
422
- View: ${NOTIFY_URL}"
302
+ View: ${AURL}"
423
303
  else
424
304
  MSG_NOTIFY="Handoff from ${AUTHOR}: ${TOPIC}
425
305
 
@@ -465,27 +345,9 @@ if [ -s "$TMPD/graph" ]; then
465
345
  GRAPH_STATUS=$(sed -n '3p' "$TMPD/graph")
466
346
  fi
467
347
  [ -z "$RESOLVED" ] && RESOLVED=0
468
- # Sanitize: --argjson requires a number. If RESOLVED came back as a slug or
469
- # anything non-numeric (the index-handoff.sh output format has drifted in
470
- # the past — defensive guard), fall back to 0 so the result file still builds.
471
- if ! echo "$RESOLVED" | grep -qE '^[0-9]+$'; then
472
- RESOLVED=0
473
- fi
474
348
 
475
349
  SUBGRAPH_JSON=$(cat "$TMPD/subgraph" 2>/dev/null || echo "null")
476
350
  [ -z "$SUBGRAPH_JSON" ] && SUBGRAPH_JSON="null"
477
- # Sanitize: --argjson requires valid JSON. Multiple writes to the same
478
- # tmpfile have produced concatenated values in the past (e.g. "null\n{...}"),
479
- # which jq rejects on multi-document input. Validate; on failure, take the
480
- # LAST non-empty line and re-validate; if still bad, fall back to null.
481
- if ! printf '%s' "$SUBGRAPH_JSON" | jq -s 'length == 1' 2>/dev/null | grep -q '^true$'; then
482
- LAST_LINE=$(printf '%s\n' "$SUBGRAPH_JSON" | awk 'NF{last=$0} END{print last}')
483
- if printf '%s' "$LAST_LINE" | jq -e '.' >/dev/null 2>&1; then
484
- SUBGRAPH_JSON="$LAST_LINE"
485
- else
486
- SUBGRAPH_JSON="null"
487
- fi
488
- fi
489
351
 
490
352
  MEMORY_STATUS=$(cat "$TMPD/memory" 2>/dev/null || echo "skipped")
491
353
  ARTIFACT_URL=$(cat "$TMPD/artifact" 2>/dev/null || echo "")
File without changes
File without changes
File without changes
File without changes