create-egregore 0.18.10 → 0.18.13

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.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // egregore-launcher-version: 0.5.24
2
+ // egregore-launcher-version: 0.5.26
3
3
  //
4
4
  // egregore — launch and browse Egregore instances from your terminal.
5
5
  // Installed by create-egregore. Reads ~/.egregore/instances.json.
@@ -9,8 +9,8 @@
9
9
  // full-screen browser with filter, preview, and inline actions.
10
10
  //
11
11
  // Subcommands:
12
- // egregore pick an instance, then pick an assistant
13
- // egregore <slug|name> pick an assistant for the matching instance
12
+ // egregore pick an instance, then pick a harness
13
+ // egregore <slug|name> pick a harness for the matching instance
14
14
  // egregore ls | list list instances (non-interactive table)
15
15
  // egregore use <slug> set the default for bare `egregore`
16
16
  // egregore /invite <user> invite a GitHub user to an instance
@@ -30,7 +30,7 @@ const https = require("node:https");
30
30
  const { spawnSync, spawn } = require("node:child_process");
31
31
  const readline = require("node:readline");
32
32
 
33
- const VERSION = "0.5.24";
33
+ const VERSION = "0.5.26";
34
34
  const REGISTRY = path.join(os.homedir(), ".egregore", "instances.json");
35
35
  const DEFAULT_FILE = path.join(os.homedir(), ".egregore", "default");
36
36
  const PUBLIC_NPX_PACKAGE = "create-egregore";
@@ -425,7 +425,7 @@ function runtimePickerLines(inst, runtimes, stored, cursor) {
425
425
  const lines = [
426
426
  "",
427
427
  ` ${DIM}↑↓${RESET} navigate · ${DIM}enter${RESET} select · ${DIM}1-${runtimes.length}${RESET} quick select · ${DIM}esc${RESET} cancel`,
428
- ` ${CYAN}◆${RESET} ${BOLD}Launch ${title} with which assistant?${RESET}`,
428
+ ` ${CYAN}◆${RESET} ${BOLD}Launch ${title} with which harness?${RESET}`,
429
429
  "",
430
430
  ];
431
431
 
@@ -438,7 +438,7 @@ function runtimePickerLines(inst, runtimes, stored, cursor) {
438
438
  }
439
439
  // Coming-soon harnesses — shown for visibility, not selectable yet.
440
440
  for (const soon of ["Hermes", "Pi"]) {
441
- lines.push(` ${DIM}· ${pad(soon, labelWidth)} coming soon${RESET}`);
441
+ lines.push(` ${DIM}· ${soon} — Coming Soon${RESET}`);
442
442
  }
443
443
  lines.push("");
444
444
  return lines;
@@ -606,7 +606,7 @@ async function renderCodexStartupCard(egregoreDir) {
606
606
 
607
607
  function codexSupportMessage(egregoreDir) {
608
608
  return [
609
- "Codex assistant support is missing from this Egregore checkout.",
609
+ "Codex harness support is missing from this Egregore checkout.",
610
610
  ` path: ${egregoreDir}`,
611
611
  "",
612
612
  "This checkout needs a newer Egregore core with:",
@@ -815,9 +815,9 @@ function cmdHelp() {
815
815
  ` ${BOLD}${CYAN}egregore${RESET} ${DIM}launch and browse Egregore instances${RESET}`,
816
816
  ``,
817
817
  ` ${BOLD}USAGE${RESET}`,
818
- ` ${CYAN}egregore${RESET} pick an instance, then pick an assistant`,
819
- ` ${CYAN}egregore${RESET} ${DIM}<slug|name>${RESET} pick an assistant for the matching instance`,
820
- ` ${CYAN}egregore --runtime${RESET} ${DIM}<name> <slug>${RESET} advanced assistant override`,
818
+ ` ${CYAN}egregore${RESET} pick an instance, then pick a harness`,
819
+ ` ${CYAN}egregore${RESET} ${DIM}<slug|name>${RESET} pick a harness for the matching instance`,
820
+ ` ${CYAN}egregore --runtime${RESET} ${DIM}<name> <slug>${RESET} advanced harness override`,
821
821
  ` ${CYAN}egregore browse${RESET} force the full-screen browser`,
822
822
  ` ${CYAN}egregore ls${RESET} ${DIM}| list${RESET} list installed instances`,
823
823
  ` ${CYAN}egregore use${RESET} ${DIM}<slug>${RESET} set the default for bare ${BOLD}egregore${RESET}`,
@@ -1377,8 +1377,14 @@ function renderHeroPanel({ selected, pv, width, height, countSuffix, filter, fil
1377
1377
 
1378
1378
  lines.push(emptyBoxLine(innerWidth, margin));
1379
1379
  if (canShowBigLogo) {
1380
+ // Center every art line by the SAME offset (the banner's max line width),
1381
+ // not per-line length. The lines can differ in length (trailing padding
1382
+ // gets trimmed by editors/linters); centering each independently shifts the
1383
+ // shorter rows sideways and the block strokes no longer line up vertically.
1384
+ const artWidth = Math.max(...EGREGORE_ART.map((line) => line.length));
1385
+ const artPad = " ".repeat(Math.max(0, Math.floor((bodyWidth - artWidth) / 2)));
1380
1386
  for (const artLine of EGREGORE_ART) {
1381
- const centered = " ".repeat(Math.max(0, Math.floor((bodyWidth - artLine.length) / 2))) + `${BOLD}${CYAN}${artLine}${RESET}`;
1387
+ const centered = artPad + `${BOLD}${CYAN}${artLine}${RESET}`;
1382
1388
  lines.push(boxLine(centered, innerWidth, margin));
1383
1389
  }
1384
1390
  } else {
@@ -1399,11 +1405,11 @@ function renderHeroPanel({ selected, pv, width, height, countSuffix, filter, fil
1399
1405
  `${BOLD}Available Skills${RESET}`,
1400
1406
  ...skillGroups,
1401
1407
  "",
1402
- `${BOLD}Assistants${RESET}`,
1408
+ `${BOLD}Harnesses${RESET}`,
1403
1409
  `${DIM}claude-code${RESET}: ${commandExists("claude") ? "installed" : "not installed"}`,
1404
1410
  `${DIM}codex${RESET}: ${commandExists("codex") ? "installed" : "not installed"}`,
1405
- `${DIM}hermes${RESET}: ${DIM}coming soon${RESET}`,
1406
- `${DIM}pi${RESET}: ${DIM}coming soon${RESET}`,
1411
+ `${DIM}Hermes — Coming Soon${RESET}`,
1412
+ `${DIM}Pi — Coming Soon${RESET}`,
1407
1413
  ];
1408
1414
  const rows = Math.max(EGREGORE_SIGIL.length + 2, right.length);
1409
1415
  for (let i = 0; i < rows; i++) {
@@ -1415,7 +1421,7 @@ function renderHeroPanel({ selected, pv, width, height, countSuffix, filter, fil
1415
1421
  lines.push(boxLine(`${BOLD}Available Skills${RESET}`, innerWidth, margin));
1416
1422
  for (const group of skillGroups) lines.push(boxLine(group, innerWidth, margin));
1417
1423
  lines.push(emptyBoxLine(innerWidth, margin));
1418
- lines.push(boxLine(`${BOLD}Assistants${RESET} ${DIM}claude-code${RESET} ${commandExists("claude") ? "installed" : "missing"} · ${DIM}codex${RESET} ${commandExists("codex") ? "installed" : "missing"} · ${DIM}hermes/pi coming soon${RESET}`, innerWidth, margin));
1424
+ lines.push(boxLine(`${BOLD}Harnesses${RESET} ${DIM}claude-code${RESET} ${commandExists("claude") ? "installed" : "missing"} · ${DIM}codex${RESET} ${commandExists("codex") ? "installed" : "missing"} · ${DIM}Hermes/Pi — Coming Soon${RESET}`, innerWidth, margin));
1419
1425
  }
1420
1426
 
1421
1427
  lines.push(emptyBoxLine(innerWidth, margin));
@@ -1434,7 +1440,7 @@ function renderRuntimePanel({ inst, cursor, width, height }) {
1434
1440
  const outerWidth = Math.min(Math.max(50, width - 2), 126);
1435
1441
  const innerWidth = Math.max(48, outerWidth - 2);
1436
1442
  const margin = " ".repeat(Math.max(0, Math.floor((width - outerWidth) / 2)));
1437
- const label = " assistant ";
1443
+ const label = " harness ";
1438
1444
  const lines = [
1439
1445
  `${margin}${DIM}┌${"─".repeat(2)}${label}${"─".repeat(Math.max(0, innerWidth - label.length - 2))}┐${RESET}`,
1440
1446
  emptyBoxLine(innerWidth, margin),
@@ -1453,7 +1459,7 @@ function renderRuntimePanel({ inst, cursor, width, height }) {
1453
1459
  }
1454
1460
  // Coming-soon harnesses — shown for visibility, not selectable yet.
1455
1461
  for (const soon of ["Hermes", "Pi"]) {
1456
- lines.push(boxLine(`${DIM}· ${pad(soon, 16)} coming soon${RESET}`, innerWidth, margin));
1462
+ lines.push(boxLine(`${DIM}· ${soon} — Coming Soon${RESET}`, innerWidth, margin));
1457
1463
  }
1458
1464
 
1459
1465
  lines.push(emptyBoxLine(innerWidth, margin));
@@ -1497,7 +1503,7 @@ async function browser(args = {}) {
1497
1503
  const newAction = {
1498
1504
  action: "new",
1499
1505
  label: "Set up a new Egregore",
1500
- description: "guided install, Telegram, invite, assistant setup",
1506
+ description: "guided install, Telegram, invite, harness setup",
1501
1507
  };
1502
1508
 
1503
1509
  const visible = () => {
@@ -1645,7 +1651,7 @@ async function browser(args = {}) {
1645
1651
  const label = (k) => `${DIM}${pad(k, 10)}${RESET}`;
1646
1652
  previewLines.push(` ${label("action")} ${BOLD}Set up a new Egregore${RESET}`);
1647
1653
  previewLines.push(` ${label("flow")} guided install, Telegram, invite, ready/start`);
1648
- previewLines.push(` ${label("assistant")} choose Claude Code or Codex during setup`);
1654
+ previewLines.push(` ${label("harness")} choose Claude Code or Codex during setup`);
1649
1655
  previewLines.push(` ${label("start")} press enter to begin`);
1650
1656
  } else if (!selected) {
1651
1657
  previewLines.push(`${DIM} (no selection)${RESET}`);
package/lib/local.js CHANGED
@@ -535,6 +535,32 @@ async function maybeSetupTelegramGroup(ui, {
535
535
  return { setup: true, groupLink: storedGroupLink };
536
536
  }
537
537
 
538
+ // Claude Code workspace cleanup: remove the Codex AGENTS.md/.codex/.agents the
539
+ // GitHub template ships, so the agent follows CLAUDE.md rather than a competing
540
+ // startup protocol. Founder-only (we own the repo) — commit + push the removal
541
+ // so it persists for everyone who clones it. A Codex teammate who joins later
542
+ // regenerates AGENTS.md via /update (bin/codex-render-spec.mjs).
543
+ function stripCodexRuntimeForClaude(egregoreDir, ui) {
544
+ const targets = ["AGENTS.md", ".codex", ".agents"];
545
+ const present = targets.filter((t) => fs.existsSync(path.join(egregoreDir, t)));
546
+ if (!present.length) {
547
+ ui.success("Claude Code workspace — using CLAUDE.md");
548
+ return;
549
+ }
550
+ try {
551
+ execFileSync("git", ["rm", "-r", "-q", "--", ...present], { cwd: egregoreDir, stdio: "pipe" });
552
+ run('git commit -m "Claude Code workspace: use CLAUDE.md, drop Codex AGENTS.md/.codex"', { cwd: egregoreDir });
553
+ run("git push", { cwd: egregoreDir });
554
+ ui.success(`Claude Code workspace — using CLAUDE.md (removed ${present.join(", ")})`);
555
+ } catch {
556
+ // Best-effort: at least remove from the working tree so this session is clean.
557
+ for (const t of present) {
558
+ try { fs.rmSync(path.join(egregoreDir, t), { recursive: true, force: true }); } catch {}
559
+ }
560
+ ui.success("Claude Code workspace — using CLAUDE.md");
561
+ }
562
+ }
563
+
538
564
  // ── Flow 1: Founder (local mode) ───────────────────────────────────
539
565
 
540
566
  async function localFounderFlow(ui, ctx = {}) {
@@ -1014,23 +1040,32 @@ async function localFounderFlow(ui, ctx = {}) {
1014
1040
  }
1015
1041
  }
1016
1042
 
1017
- const codexRuntime = installCodexRuntime(egregoreDir);
1018
- reportCodexRuntimeSummary(codexRuntime, ui);
1019
-
1020
- // 16b. Commit the Codex runtime into the repo so it's TRACKED — symmetric
1021
- // with .claude/ (which ships in the template). An untracked overlay can
1022
- // be lost to later git operations, and joiners would never get it. This
1023
- // way every Egregore supports BOTH assistants, persistently and for the
1024
- // whole team: pick Claude Code or Codex, now or later, and it just works.
1025
- try {
1026
- const toCommit = Array.isArray(codexRuntime.files) ? [...codexRuntime.files] : [];
1027
- if (fs.existsSync(path.join(egregoreDir, ".agents", "skills"))) toCommit.push(".agents/skills");
1028
- if (toCommit.length) {
1029
- execFileSync("git", ["add", "--", ...toCommit], { cwd: egregoreDir, stdio: "pipe" });
1030
- run('git commit -m "Add Codex runtime so this Egregore supports both assistants"', { cwd: egregoreDir });
1031
- run("git push", { cwd: egregoreDir });
1032
- }
1033
- } catch {}
1043
+ // 16. Runtime overlay. Codex installs commit the Codex runtime into the repo
1044
+ // so it's TRACKED (symmetric with .claude/). Claude installs do the
1045
+ // opposite: Claude Code reads CLAUDE.md, not AGENTS.md, so we strip the
1046
+ // Codex AGENTS.md/.codex the template ships — otherwise a fresh Claude
1047
+ // agent (e.g. when onboarding suppresses the greeting) wanders into
1048
+ // AGENTS.md's startup steps. A Codex teammate who joins later regenerates
1049
+ // AGENTS.md via /update.
1050
+ if (preferredRuntime === "codex") {
1051
+ const codexRuntime = installCodexRuntime(egregoreDir);
1052
+ reportCodexRuntimeSummary(codexRuntime, ui);
1053
+
1054
+ // 16b. Commit the Codex runtime into the repo so it's TRACKED — symmetric
1055
+ // with .claude/ (which ships in the template). An untracked overlay can
1056
+ // be lost to later git operations, and joiners would never get it.
1057
+ try {
1058
+ const toCommit = Array.isArray(codexRuntime.files) ? [...codexRuntime.files] : [];
1059
+ if (fs.existsSync(path.join(egregoreDir, ".agents", "skills"))) toCommit.push(".agents/skills");
1060
+ if (toCommit.length) {
1061
+ execFileSync("git", ["add", "--", ...toCommit], { cwd: egregoreDir, stdio: "pipe" });
1062
+ run('git commit -m "Add Codex runtime so this Egregore supports both assistants"', { cwd: egregoreDir });
1063
+ run("git push", { cwd: egregoreDir });
1064
+ }
1065
+ } catch {}
1066
+ } else {
1067
+ stripCodexRuntimeForClaude(egregoreDir, ui);
1068
+ }
1034
1069
 
1035
1070
  // 17. Register instance + install launcher binary
1036
1071
  ui.step(5 + selectedRepos.length, totalSteps, "Registering instance...");
package/lib/setup.js CHANGED
@@ -438,10 +438,16 @@ async function install(data, ui, targetDir, opts = {}) {
438
438
  } catch {}
439
439
  }
440
440
 
441
- // 4d. Overlay packaged Codex support before registering or launching. This
442
- // makes cloned public templates usable even before the public repo syncs.
443
- const codexRuntime = installCodexRuntime(egregoreDir);
444
- reportCodexRuntimeSummary(codexRuntime, ui);
441
+ // 4d. Overlay packaged Codex support — Codex installs only. Claude Code reads
442
+ // CLAUDE.md, not AGENTS.md; overlaying the Codex AGENTS.md/.codex into a Claude
443
+ // workspace gives a wandering agent a competing startup protocol (the
444
+ // xenocracy onboarding regression). Claude joiners keep the cloned repo as-is —
445
+ // any inherited AGENTS.md carries the runtime-precedence guard that redirects
446
+ // Claude back to CLAUDE.md.
447
+ if (preferredRuntime === "codex") {
448
+ const codexRuntime = installCodexRuntime(egregoreDir);
449
+ reportCodexRuntimeSummary(codexRuntime, ui);
450
+ }
445
451
 
446
452
  // 5. Register instance + install launcher binary
447
453
  ui.step(5, totalSteps, "Registering instance...");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-egregore",
3
- "version": "0.18.10",
3
+ "version": "0.18.13",
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": {
@@ -5,7 +5,7 @@
5
5
  {
6
6
  "section": "(intro)",
7
7
  "action": "replace",
8
- "sourceHash": "3992e3abfcdd",
8
+ "sourceHash": "a33ca691aacf",
9
9
  "note": "Reframed for Codex: names AGENTS.md as the delivery surface and the manifest; \"organizations using Claude Code\" generalized."
10
10
  },
11
11
  {
@@ -23,7 +23,7 @@
23
23
  {
24
24
  "section": "After Greeting — BRANCH ON FIRST RESPONSE",
25
25
  "action": "replace",
26
- "sourceHash": "09c2618759bd",
26
+ "sourceHash": "c71340e68c9e",
27
27
  "note": "EnterWorktree -> bin/agent.sh branch with git checkout fallback; AskUserQuestion -> numbered options in plain text; branch-guard mapped to .codex/hooks/branch-guard.js with instruction fallback; plan-mode note dropped (no plan mode on Codex); /branch + /onboarding -> skill tokens. Handoff claiming and repoState auto-checkout kept verbatim (pure git)."
28
28
  },
29
29
  {
@@ -1,9 +1,20 @@
1
1
  # Egregore Agent Protocol
2
2
 
3
+ > **Runtime precedence — read this first.**
4
+ >
5
+ > **If you are Claude Code** (or any agent that loads `.claude/`): **stop here.**
6
+ > Your authoritative instructions are `CLAUDE.md` and `.claude/`. Follow the
7
+ > SessionStart greeting and the branch-on-first-message rule there. Do **not**
8
+ > follow the startup steps below and do **not** use `bin/agent.sh` for work an
9
+ > Egregore skill already covers. This file exists only for runtimes that cannot
10
+ > read `CLAUDE.md`.
11
+ >
12
+ > **If you are Codex or another shell-only agent:** this file is yours — continue.
13
+
3
14
  Egregore is no longer only a Claude Code workspace. Claude Code remains the
4
- first-class integrated runtime through `.claude/`, but any agent that can run
5
- shell commands in this checkout can participate through the portable memory
6
- protocol.
15
+ first-class integrated runtime through `.claude/` and `CLAUDE.md`; any other
16
+ agent that can run shell commands in this checkout participates through the
17
+ portable memory protocol below.
7
18
 
8
19
  ## Startup
9
20
 
@@ -35,6 +46,273 @@ write the same markdown files directly, following `docs/AGENT-PROTOCOL.md`.
35
46
 
36
47
  ## Compatibility
37
48
 
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.
49
+ Claude Code users continue using `/handoff`, `/activity`, `/ask`, `/save`, and
50
+ other skills — driven by `CLAUDE.md`, not this file. Non-Claude agents use
51
+ `bin/agent.sh` and the file protocol, with the full Codex behavioral spec in the
52
+ generated block below. Both paths converge on the same `memory/` files.
53
+
54
+ <!-- BEGIN GENERATED EGREGORE CODEX SPEC — derived from CLAUDE.md by bin/codex-render-spec.mjs; do not edit by hand -->
55
+
56
+ ## Egregore on Codex
57
+
58
+ 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.
59
+
60
+ 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.
61
+
62
+ ---
63
+
64
+ ## Identity & Upstream
65
+
66
+ 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.
67
+
68
+ **Where changes belong:**
69
+ - **Framework is outdated or a skill is missing:** run `$update` first. It pulls the latest from upstream.
70
+ - **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.
71
+ - **Org-level work** (memory, org-specific knowledge, managed repos, `egregore.json`): `$save` to this repo.
72
+
73
+ When a user reports broken or missing framework behavior, the first question is "when did you last `$update`?"
74
+
75
+ ---
76
+
77
+ ## On Launch — MANDATORY FIRST ACTION
78
+
79
+ 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`.
80
+
81
+ ---
82
+
83
+ ## After Greeting — BRANCH ON FIRST RESPONSE
84
+
85
+ **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:
86
+
87
+ 1. Derive a topic slug from what the user said (kebab-case, 2–4 words)
88
+ 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.
89
+ 3. Confirm: `On {branch} (worktree).`
90
+
91
+ **Fallback:** If `bin/agent.sh branch` fails, use `git checkout -b dev/{author}/{slug} origin/develop`.
92
+
93
+ 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 &`
94
+
95
+ ### Handoff claiming
96
+
97
+ If `addressed_to_user` handoffs exist and the user is picking one up, create the IMPLEMENTS link after branch creation:
98
+ ```bash
99
+ bash bin/graph-op.sh claim-handoff "$SESSION_ID" "$HANDOFF_SESSION_ID" 2>/dev/null &
100
+ ```
101
+
102
+ **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:
103
+
104
+ ```bash
105
+ PARENT_DIR="$(cd .. && pwd)"
106
+ # For each entry in repoState:
107
+ REPO_DIR="$PARENT_DIR/$REPO_NAME"
108
+ if [ -d "$REPO_DIR/.git" ] || [ -f "$REPO_DIR/.git" ]; then
109
+ git -C "$REPO_DIR" fetch origin "$BRANCH" --quiet 2>/dev/null
110
+ git -C "$REPO_DIR" checkout "$BRANCH" 2>/dev/null || \
111
+ git -C "$REPO_DIR" checkout -b "$BRANCH" "origin/$BRANCH" 2>/dev/null
112
+ fi
113
+ ```
114
+
115
+ 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.
116
+
117
+ **Exceptions** — skip branching when:
118
+ - The user explicitly created or named a branch themselves
119
+ - Already on a working branch AND the user's intent continues the current branch's topic
120
+
121
+ **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.
122
+
123
+ If on develop after two messages, create a branch immediately from whatever context you have.
124
+
125
+ ### Branch-guard protocol
126
+
127
+ 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:
128
+
129
+ - **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.
130
+ - **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):
131
+
132
+ ```text
133
+ Need a working branch first. Topic?
134
+ 1. <suggested slug from context>
135
+ 2. <alternative slug>
136
+ 3. Other: (name it)
137
+ ```
138
+
139
+ Wait for the reply, then branch.
140
+
141
+ If this Codex build does not support hooks, follow the same discipline as a standing instruction: never write or commit on develop/main/master.
142
+
143
+ ### Onboarding exception
144
+
145
+ If the startup card output contains `onboarding_needed`, invoke the `$onboarding` skill instead of greeting.
146
+
147
+ ---
148
+
149
+ ## Config Files
150
+
151
+ - **`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.**
152
+ - **`.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-`.
153
+
154
+ 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.
155
+
156
+ ---
157
+
158
+ ## Knowledge Graph
159
+
160
+ **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.
161
+
162
+ ---
163
+
164
+ ## Notifications
165
+
166
+ **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.
167
+
168
+ ---
169
+
170
+ ---
171
+
172
+ ## Onboarding
173
+
174
+ When `onboarding_complete` is false in `.egregore-state.json`, invoke `$onboarding`. The command is the single source of truth — do NOT run steps inline.
175
+
176
+ ---
177
+
178
+ ## Transparency Beat
179
+
180
+ After the first silent bash command in any session, mention once:
181
+
182
+ > I run commands directly to keep things fast — you can see everything in the session log, and change permissions in `.claude/settings.json` anytime.
183
+
184
+ Never repeat it.
185
+
186
+ ---
187
+
188
+ ## Memory
189
+
190
+ `memory/` is a symlink to the memory repo defined in `egregore.json`. Key directories:
191
+ - `people/` — team directory
192
+ - `handoffs/` — session handoffs + `index.md`
193
+ - `knowledge/decisions/` — org decisions
194
+ - `knowledge/patterns/` — emergent patterns
195
+ - `infrastructure/` — service registry (URLs, names, credential locations)
196
+
197
+ Always use HTTPS for git operations — `github-auth.sh` handles credential storage.
198
+
199
+ ---
200
+
201
+ ## Git Workflow
202
+
203
+ `develop` branch model with deferred, topic-based branching. Users never interact with git directly.
204
+
205
+ ```
206
+ main ← stable (/release)
207
+ develop ← integration (PRs land here)
208
+ dev/{author}/{topic-slug} | feature/{slug} | bugfix/{slug}
209
+ ```
210
+
211
+ - **On launch**: syncs develop + memory. Does NOT create a branch.
212
+ - **Branch creation**: MANDATORY on first work-related message (see above).
213
+ - **Resuming**: rebase onto develop and continue.
214
+ - **If on develop after two messages**: create branch immediately.
215
+ - **`$save`**: pushes working branch, PR to develop. Auto-merges markdown-only PRs.
216
+ - **Memory repo**: stays on main (separate repo, auto-merge).
217
+ - **Never push directly to main or develop.** All changes flow through PRs.
218
+
219
+ ### Managed Repos
220
+
221
+ 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.
222
+
223
+ ---
224
+
225
+ ## Working Conventions
226
+
227
+ - Check `memory/knowledge/` before starting unfamiliar work
228
+ - Document significant decisions in `memory/knowledge/decisions/`
229
+ - After substantial sessions, log to `memory/handoffs/` and update `index.md`
230
+
231
+ ---
232
+
233
+ ## Command Awareness
234
+
235
+ 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.
236
+
237
+ 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.
238
+
239
+ **Core loop** — `$activity` `$dashboard` `$handoff` `$wrap` `$save` `$reflect` `$todo`
240
+ **Knowledge** — `$deep-reflect` `$archive` `$note` `$add` `$meeting` `$ingest`
241
+ **Identity** — `$me` (view profile or set display name)
242
+ **Coordination** — `$ask` `$quest` `$issue` `$invite` `$delete-user` `$announce`
243
+ **Connectors** — `$telegram-connect` (Telegram group setup)
244
+ **Git** — `$branch` `$commit` `$push` `$pr` `$save` `$review-pr` `$contribute`
245
+ **Spirits** — `$summon` (persistent agent processes)
246
+ **Infra** — `$setup` `$update` `$pull` `$env` `$infra` `$sync-repos` `$release` `$checkup`
247
+
248
+ **Disambiguation:**
249
+ - Knowledge: `$reflect` (share-ready) · `$note` (half-baked) · `$deep-reflect` (cross-reference) · `$archive` (AI patterns)
250
+ - Status: `$dashboard` (personal) · `$activity` (org-wide)
251
+ - Ending: `$wrap` (personal closure) · `$handoff` (notes for others) · `$save` (still working)
252
+ - Tasks: `$todo` (personal) · `$quest` (team exploration) · `$issue` (something broken)
253
+ - Questions: `$ask [person]` (async) · just ask (agent answers from context)
254
+ - Ingestion: `$ingest meeting` · `$ingest user-interview` · `$ingest google` · ambiguous → ask which type
255
+ - Connectors: `$telegram-connect` (Telegram group) · `$ingest` (bring content in)
256
+ - Identity: `$me` — "who am I", "call me oz"
257
+ - People: `$invite` (add) · `$delete-user` (remove)
258
+ - PRs: `$pr` (create) · `$review-pr` (review)
259
+ - Contributing: `$contribute` (upstream framework) · `$save` (org repo) · `$issue` (report bug)
260
+ - Agents: `$summon` (design through questions) · `/loop` (quick recurring schedule)
261
+ - Announcements: `$announce` (broadcast to group) · `$handoff` (structured to a person) · `bin/notify.sh send` (DM one person)
262
+
263
+ ---
264
+
265
+ ## Socratic Questioning (MANDATORY)
266
+
267
+ **Triggers**: "ask me questions", "question me", "help me think through", or any request to be questioned.
268
+
269
+ 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`.
270
+
271
+ **Rules:** Max 4 questions per batch. When choices aren't mutually exclusive, say "pick any that apply".
272
+
273
+ ---
274
+
275
+ ## Telemetry
276
+
277
+ Privacy-respecting, opt-out telemetry. After every slash command, emit fire-and-forget:
278
+ `bash bin/telemetry.sh emit "command" '{"command":"save"}' 2>/dev/null &`
279
+
280
+ Never collected: file contents, code, env var values, conversation content.
281
+ 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`.
282
+
283
+ ---
284
+
285
+ ## Mode
286
+
287
+ 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.
288
+
289
+ **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.
290
+
291
+ **Hard rules in local mode:**
292
+ - Never tell the user to "ask their admin" for credentials. The user IS the admin.
293
+ - 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`.
294
+ - 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.
295
+ - 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.
296
+
297
+ **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.
298
+
299
+ ---
300
+
301
+ ## Environment Isolation
302
+
303
+ Sessions are confined to this project + memory + managed repos. On Codex this boundary is a standing instruction, not an enforced hook — hold it yourself.
304
+
305
+ - **Never modify `~/.egregore/instances.json`** — managed by session-start.sh
306
+ - **Never access another instance's files** — refuse even if asked
307
+ - When a request points outside the boundary, do not improvise a workaround. Ask in plain text with numbered options:
308
+
309
+ ```text
310
+ That path is outside this Egregore's boundary. How should we proceed?
311
+ 1. Paste the contents inline
312
+ 2. Move the file into the repo and point me at the new path
313
+ 3. Cancel
314
+ ```
315
+
316
+ See DEVELOPMENT.md §3 for boundary details.
317
+
318
+ <!-- END GENERATED EGREGORE CODEX SPEC -->
@@ -37,6 +37,64 @@ const MANIFEST = path.join(ROOT, '.codex', 'spec-manifest.json');
37
37
  const BEGIN = '<!-- BEGIN GENERATED EGREGORE CODEX SPEC — derived from CLAUDE.md by bin/codex-render-spec.mjs; do not edit by hand -->';
38
38
  const END = '<!-- END GENERATED EGREGORE CODEX SPEC -->';
39
39
 
40
+ // Hand-authored top of AGENTS.md, owned by this script so it can never be lost
41
+ // on regeneration. Opens with the runtime-precedence guard: Claude Code reads
42
+ // CLAUDE.md (and now loads root AGENTS.md automatically), so the first thing it
43
+ // must see here is an instruction to defer. Codex / shell-only agents read past
44
+ // the guard into the thin protocol and the generated spec below.
45
+ const AGENTS_PREAMBLE = `# Egregore Agent Protocol
46
+
47
+ > **Runtime precedence — read this first.**
48
+ >
49
+ > **If you are Claude Code** (or any agent that loads \`.claude/\`): **stop here.**
50
+ > Your authoritative instructions are \`CLAUDE.md\` and \`.claude/\`. Follow the
51
+ > SessionStart greeting and the branch-on-first-message rule there. Do **not**
52
+ > follow the startup steps below and do **not** use \`bin/agent.sh\` for work an
53
+ > Egregore skill already covers. This file exists only for runtimes that cannot
54
+ > read \`CLAUDE.md\`.
55
+ >
56
+ > **If you are Codex or another shell-only agent:** this file is yours — continue.
57
+
58
+ Egregore is no longer only a Claude Code workspace. Claude Code remains the
59
+ first-class integrated runtime through \`.claude/\` and \`CLAUDE.md\`; any other
60
+ agent that can run shell commands in this checkout participates through the
61
+ portable memory protocol below.
62
+
63
+ ## Startup
64
+
65
+ 1. Read \`egregore.json\` for the instance name, GitHub owner, and managed repos.
66
+ 2. Run \`bin/agent.sh sync\` to pull the latest shared memory.
67
+ 3. Read \`memory/people/\` to learn collaborator handles.
68
+ 4. Run \`bin/agent.sh activity --for <your-handle>\` to inspect handoffs and
69
+ pending questions addressed to you.
70
+
71
+ ## Communication
72
+
73
+ Use the runtime-neutral bridge instead of Claude Code slash commands:
74
+
75
+ \`\`\`bash
76
+ bin/agent.sh handoff --from alice --to bob --topic "auth review" \\
77
+ --body "Implemented the OAuth callback parser. Bob should review error cases."
78
+
79
+ bin/agent.sh ask --from bob --to alice --topic "auth review" \\
80
+ --question "Should invalid state redirect to login or return 400?"
81
+
82
+ bin/agent.sh answer --from alice \\
83
+ --question memory/knowledge/questions/2026-04-26-bob-to-alice-auth-review.md \\
84
+ --body "Return 400 in the API path; redirect only in browser routes."
85
+ \`\`\`
86
+
87
+ Each command writes to the existing Git-backed \`memory/\` repository and pushes
88
+ when the memory repo has an \`origin\` remote. Agents that cannot run shell may
89
+ write the same markdown files directly, following \`docs/AGENT-PROTOCOL.md\`.
90
+
91
+ ## Compatibility
92
+
93
+ Claude Code users continue using \`/handoff\`, \`/activity\`, \`/ask\`, \`/save\`, and
94
+ other skills — driven by \`CLAUDE.md\`, not this file. Non-Claude agents use
95
+ \`bin/agent.sh\` and the file protocol, with the full Codex behavioral spec in the
96
+ generated block below. Both paths converge on the same \`memory/\` files.`;
97
+
40
98
  function sha(text) {
41
99
  return crypto.createHash('sha256').update(text).digest('hex').slice(0, 12);
42
100
  }
@@ -271,13 +329,12 @@ function render() {
271
329
  return { block, manifest };
272
330
  }
273
331
 
274
- function spliceAgentsMd(agentsMd, block) {
275
- const beginIdx = agentsMd.indexOf(BEGIN);
276
- const endIdx = agentsMd.indexOf(END);
277
- if (beginIdx !== -1 && endIdx !== -1) {
278
- return agentsMd.slice(0, beginIdx) + block + agentsMd.slice(endIdx + END.length);
279
- }
280
- return `${agentsMd.replace(/\n+$/, '')}\n\n${block}\n`;
332
+ // AGENTS.md is fully owned by this script: the guard preamble plus the generated
333
+ // spec block. Reconstructing it deterministically (rather than splicing into the
334
+ // existing file) guarantees the runtime-precedence guard is always present and
335
+ // correct — it cannot be stripped by an upstream sync or a hand edit.
336
+ function assembleAgentsMd(block) {
337
+ return `${AGENTS_PREAMBLE.trim()}\n\n${block}\n`;
281
338
  }
282
339
 
283
340
  const check = process.argv.includes('--check');
@@ -285,7 +342,7 @@ const { block, manifest } = render();
285
342
  const manifestJson = `${JSON.stringify({ generatedBy: 'bin/codex-render-spec.mjs', source: 'CLAUDE.md', sections: manifest }, null, 2)}\n`;
286
343
 
287
344
  const currentAgents = fs.existsSync(AGENTS_MD) ? fs.readFileSync(AGENTS_MD, 'utf-8') : '';
288
- const nextAgents = spliceAgentsMd(currentAgents, block);
345
+ const nextAgents = assembleAgentsMd(block);
289
346
 
290
347
  const CODEX_DOC_BUDGET = 32 * 1024;
291
348
  if (Buffer.byteLength(nextAgents, 'utf-8') > CODEX_DOC_BUDGET) {