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
package/bin/cli.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * create-egregore — set up Egregore for your team in one command.
5
5
  *
6
6
  * - Installs an `egregore` PATH binary instead of a shell alias
7
- * - Auto-execs into the selected assistant (Claude Code or Codex) at the end
7
+ * - Auto-execs into the selected harness (Claude Code, Codex, or Pi) at the end
8
8
  *
9
9
  * Four modes:
10
10
  * npx -y create-egregore@latest --token st_xxxx (from website)
@@ -30,6 +30,11 @@ const {
30
30
  installCodexRuntime,
31
31
  reportCodexRuntimeSummary,
32
32
  } = require("../lib/codex-runtime");
33
+ const {
34
+ hasPiRuntimeSupport,
35
+ installPiRuntime,
36
+ reportPiRuntimeSummary,
37
+ } = require("../lib/pi-runtime");
33
38
  const {
34
39
  install,
35
40
  ghApi,
@@ -96,10 +101,13 @@ function binaryOnPath(bin) {
96
101
  }
97
102
 
98
103
  function assistantLabel(runtime) {
99
- return normalizeRuntime(runtime) === "codex" ? "Codex" : "Claude Code";
104
+ const normalized = normalizeRuntime(runtime);
105
+ if (normalized === "codex") return "Codex";
106
+ if (normalized === "pi") return "Pi";
107
+ return "Claude Code";
100
108
  }
101
109
 
102
- function upgradeCodexRuntimeForExisting(instances) {
110
+ function upgradePortableRuntimesForExisting(instances) {
103
111
  for (const inst of instances) {
104
112
  if (!inst || !inst.path || !fs.existsSync(inst.path)) continue;
105
113
  try {
@@ -109,8 +117,14 @@ function upgradeCodexRuntimeForExisting(instances) {
109
117
  label: `Codex runtime for ${inst.slug || inst.name || path.basename(inst.path)}`,
110
118
  includeConflicts: !ready,
111
119
  });
120
+ const piSummary = installPiRuntime(inst.path);
121
+ const piReady = hasPiRuntimeSupport(inst.path);
122
+ reportPiRuntimeSummary(piSummary, ui, {
123
+ label: `Pi meta-harness for ${inst.slug || inst.name || path.basename(inst.path)}`,
124
+ includeConflicts: !piReady,
125
+ });
112
126
  } catch (err) {
113
- ui.warn(`Could not install Codex runtime for ${inst.path}: ${err.message}`);
127
+ ui.warn(`Could not refresh shell-runtime support for ${inst.path}: ${err.message}`);
114
128
  }
115
129
  }
116
130
  }
@@ -127,37 +141,34 @@ async function chooseRuntime(args) {
127
141
 
128
142
  if (!process.stdin.isTTY) return "claude-code";
129
143
 
130
- const hasClaude = binaryOnPath("claude");
131
- const hasCodex = binaryOnPath("codex");
132
- const recommended = !hasClaude && hasCodex ? "codex" : "claude-code";
133
- const choices = ["claude-code", "codex"]
144
+ const installedByRuntime = {
145
+ "claude-code": binaryOnPath("claude"),
146
+ codex: binaryOnPath("codex"),
147
+ pi: binaryOnPath("pi"),
148
+ };
149
+ const recommended = Object.keys(installedByRuntime).find((runtime) => installedByRuntime[runtime]) || "claude-code";
150
+ const choices = ["claude-code", "codex", "pi"]
134
151
  .map((runtime) => {
135
- const installed = runtime === "codex" ? hasCodex : hasClaude;
152
+ const installed = installedByRuntime[runtime];
136
153
  const label = assistantLabel(runtime);
137
- const missingCopy = runtime === "codex"
138
- ? "Install Codex later, then run egregore and choose Codex"
139
- : "Install Claude Code later, then run egregore and choose Claude Code";
154
+ const missingCopy = `Install ${label} later, then run egregore and choose ${label}`;
140
155
  return {
141
156
  label: `${label}${installed ? "" : " (not installed)"}${recommended === runtime ? " (recommended)" : ""}`,
142
157
  description: installed
143
- ? (runtime === "codex" ? "Installed · portable agent protocol" : "Installed · Claude Code integration")
158
+ ? (runtime === "claude-code" ? "Installed · Claude Code integration" : runtime === "pi" ? "Installed · project-local meta-harness" : "Installed · portable agent protocol")
144
159
  : missingCopy,
145
160
  runtime,
146
161
  installed,
147
162
  };
148
163
  })
149
164
  .sort((a, b) => Number(b.installed) - Number(a.installed));
150
- // Coming-soon harnesses — shown for visibility, not yet selectable.
151
- choices.push(
152
- { label: "Hermes", description: "coming soon", runtime: "hermes", disabled: true },
153
- { label: "Pi", description: "coming soon", runtime: "pi", disabled: true },
154
- );
165
+ choices.push({ label: "Hermes", description: "coming soon", runtime: "hermes", disabled: true });
155
166
  const choice = await ui.choose("Which harness do you want to use?", choices);
156
167
  return choice.runtime;
157
168
  }
158
169
 
159
170
  async function upgradeLauncherFromExisting(instances) {
160
- upgradeCodexRuntimeForExisting(instances);
171
+ upgradePortableRuntimesForExisting(instances);
161
172
  const inst = instances.find((entry) => entry && entry.path && fs.existsSync(entry.path));
162
173
  if (!inst) return null;
163
174
  try {
@@ -214,6 +225,8 @@ function parseArgs(argv) {
214
225
  args.noWelcome = true;
215
226
  } else if (argv[i] === "--codex") {
216
227
  args.runtime = "codex";
228
+ } else if (argv[i] === "--pi") {
229
+ args.runtime = "pi";
217
230
  } else if (argv[i] === "--claude" || argv[i] === "--claude-code") {
218
231
  args.runtime = "claude-code";
219
232
  } else if (argv[i] === "--runtime" && argv[i + 1]) {
@@ -376,8 +389,9 @@ function showHelp() {
376
389
  ui.info(" --token <token> Setup token from egregore.xyz");
377
390
  ui.info(" --api <url> Use connected API setup with this URL");
378
391
  ui.info(" --codex Prefer Codex for this local install");
392
+ ui.info(" --pi Prefer Pi for this local install");
379
393
  ui.info(" --claude Prefer Claude Code for this local install");
380
- ui.info(" --runtime <name> Harness override: claude-code or codex");
394
+ ui.info(" --runtime <name> Harness override: claude-code, codex, or pi");
381
395
  ui.info(" --force Skip the in-repo detection check");
382
396
  ui.info(" --no-autolaunch Don't launch the harness at the end (for CI)");
383
397
  ui.info(" --no-screens Skip the full-screen welcome/ready bookends");
package/lib/local.js CHANGED
@@ -16,6 +16,7 @@ const {
16
16
  tryGhAuthOrLogin,
17
17
  } = require("./auth");
18
18
  const { installCodexRuntime, reportCodexRuntimeSummary } = require("./codex-runtime");
19
+ const { installPiRuntime, reportPiRuntimeSummary } = require("./pi-runtime");
19
20
  const {
20
21
  install,
21
22
  ghApi,
@@ -82,6 +83,19 @@ async function repoExists(token, owner, repo) {
82
83
  return status === 200;
83
84
  }
84
85
 
86
+ // Full repo metadata (or null). Used to detect `archived` when reclaiming a repo
87
+ // that a prior egregore delete archived rather than hard-deleted.
88
+ async function getRepoMeta(token, owner, repo) {
89
+ const { status, data } = await ghApi("GET", `/repos/${owner}/${repo}`, token);
90
+ return status === 200 ? data : null;
91
+ }
92
+
93
+ // Unarchive a repo so pushes/invites work again. Returns true on success.
94
+ async function unarchiveRepo(token, owner, repo) {
95
+ const { status } = await ghApi("PATCH", `/repos/${owner}/${repo}`, token, { archived: false });
96
+ return status === 200;
97
+ }
98
+
85
99
  async function createFromTemplate(token, owner, name, description) {
86
100
  const { status, data } = await ghApi(
87
101
  "POST",
@@ -612,13 +626,13 @@ async function maybeSetupTelegramGroup(ui, {
612
626
  return { setup: true, groupLink: storedGroupLink };
613
627
  }
614
628
 
615
- // Claude Code workspace cleanup: remove the Codex AGENTS.md/.codex/.agents the
629
+ // Claude Code workspace cleanup: remove shell-runtime overlays the
616
630
  // GitHub template ships, so the agent follows CLAUDE.md rather than a competing
617
631
  // startup protocol. Founder-only (we own the repo) — commit + push the removal
618
632
  // so it persists for everyone who clones it. A Codex teammate who joins later
619
633
  // regenerates AGENTS.md via /update (bin/codex-render-spec.mjs).
620
634
  function stripCodexRuntimeForClaude(egregoreDir, ui) {
621
- const targets = ["AGENTS.md", ".codex", ".agents"];
635
+ const targets = ["AGENTS.md", ".codex", ".agents", ".pi"];
622
636
  const present = targets.filter((t) => fs.existsSync(path.join(egregoreDir, t)));
623
637
  if (!present.length) {
624
638
  ui.success("Claude Code workspace — using CLAUDE.md");
@@ -626,7 +640,7 @@ function stripCodexRuntimeForClaude(egregoreDir, ui) {
626
640
  }
627
641
  try {
628
642
  execFileSync("git", ["rm", "-r", "-q", "--", ...present], { cwd: egregoreDir, stdio: "pipe" });
629
- run('git commit -m "Claude Code workspace: use CLAUDE.md, drop Codex AGENTS.md/.codex"', { cwd: egregoreDir });
643
+ run('git commit -m "Claude Code workspace: use CLAUDE.md, drop shell-runtime overlays"', { cwd: egregoreDir });
630
644
  run("git push", { cwd: egregoreDir });
631
645
  ui.success(`Claude Code workspace — using CLAUDE.md (removed ${present.join(", ")})`);
632
646
  } catch {
@@ -1032,13 +1046,57 @@ async function localFounderFlow(ui, ctx = {}) {
1032
1046
  if (existingConfig) {
1033
1047
  s1.stop(`${githubOrg}/${repoName} already has Egregore set up`);
1034
1048
  console.log("");
1035
- ui.info("To join it:");
1036
- ui.info(` ${ui.bold(joinCommand(githubOrg, repoName))}`);
1037
- console.log("");
1038
- ui.info("To set up a different one, choose a different team name.");
1039
- process.exit(0);
1049
+
1050
+ // The repo already carries an Egregore. You're legitimately here for one
1051
+ // of three reasons: reclaim your own (e.g. the platform record was deleted
1052
+ // but the repo survives), join someone else's, or you meant a fresh one.
1053
+ // Reclaim reuses the repo in place — keeping its collaborators — so you
1054
+ // don't get forced onto a new name and re-invite everyone.
1055
+ const canPick = ui.canInteract && ui.canInteract();
1056
+ const RECLAIM = { label: "Reuse this one — reconnect and finish setup", description: "Adopt the existing repo, keep its collaborators" };
1057
+ const JOIN = { label: "Just join it", description: joinCommand(githubOrg, repoName) };
1058
+ const RENAME = { label: "Set up a different Egregore", description: "Pick a different team name" };
1059
+
1060
+ let action = "join";
1061
+ if (canPick) {
1062
+ const picked = await ui.choose(
1063
+ `${githubOrg}/${repoName} already exists — what do you want to do?`,
1064
+ [RECLAIM, JOIN, RENAME],
1065
+ );
1066
+ action = picked.label === RECLAIM.label ? "reclaim"
1067
+ : picked.label === RENAME.label ? "rename" : "join";
1068
+ }
1069
+
1070
+ if (action === "join") {
1071
+ ui.info("To join it:");
1072
+ ui.info(` ${ui.bold(joinCommand(githubOrg, repoName))}`);
1073
+ console.log("");
1074
+ ui.info("To set up a different one, choose a different team name.");
1075
+ process.exit(0);
1076
+ }
1077
+ if (action === "rename") {
1078
+ ui.info("Re-run setup and choose a different team name.");
1079
+ process.exit(0);
1080
+ }
1081
+
1082
+ // Reclaim: adopt the existing repo. If a prior delete archived it, unarchive
1083
+ // so pushes/invites work. Fall through — the rest of the founder flow reuses
1084
+ // the repo as-is and reconnects the platform record, preserving existing
1085
+ // collaborators (so no redundant re-invite).
1086
+ const meta = await getRepoMeta(githubToken, githubOrg, repoName);
1087
+ if (meta && meta.archived) {
1088
+ const s1b = ui.spinner(`Unarchiving ${githubOrg}/${repoName}...`);
1089
+ if (await unarchiveRepo(githubToken, githubOrg, repoName)) {
1090
+ s1b.stop(`Unarchived ${githubOrg}/${repoName}`);
1091
+ } else {
1092
+ s1b.fail(`Could not unarchive ${githubOrg}/${repoName} — unarchive it on GitHub, then re-run.`);
1093
+ process.exit(1);
1094
+ }
1095
+ }
1096
+ ui.success(`Reusing ${githubOrg}/${repoName} — reconnecting`);
1097
+ } else {
1098
+ s1.stop("Egregore repo exists but has no config — using it");
1040
1099
  }
1041
- s1.stop("Egregore repo exists but has no config — using it");
1042
1100
  } else {
1043
1101
  try {
1044
1102
  await createFromTemplate(githubToken, githubOrg, repoName, description);
@@ -1298,7 +1356,7 @@ async function localFounderFlow(ui, ctx = {}) {
1298
1356
  }
1299
1357
  }
1300
1358
 
1301
- // 16. Runtime overlay. Codex installs commit the Codex runtime into the repo
1359
+ // 16. Runtime overlay. Codex and Pi installs commit their derived runtime
1302
1360
  // so it's TRACKED (symmetric with .claude/). Claude installs do the
1303
1361
  // opposite: Claude Code reads CLAUDE.md, not AGENTS.md, so we strip the
1304
1362
  // Codex AGENTS.md/.codex the template ships — otherwise a fresh Claude
@@ -1321,6 +1379,17 @@ async function localFounderFlow(ui, ctx = {}) {
1321
1379
  run("git push", { cwd: egregoreDir });
1322
1380
  }
1323
1381
  } catch {}
1382
+ } else if (preferredRuntime === "pi") {
1383
+ const piRuntime = installPiRuntime(egregoreDir);
1384
+ reportPiRuntimeSummary(piRuntime, ui);
1385
+ try {
1386
+ const toCommit = Array.isArray(piRuntime.files) ? [...piRuntime.files] : [];
1387
+ if (toCommit.length) {
1388
+ execFileSync("git", ["add", "--", ...toCommit], { cwd: egregoreDir, stdio: "pipe" });
1389
+ run('git commit -m "Add Pi meta-harness runtime"', { cwd: egregoreDir });
1390
+ run("git push", { cwd: egregoreDir });
1391
+ }
1392
+ } catch {}
1324
1393
  } else {
1325
1394
  stripCodexRuntimeForClaude(egregoreDir, ui);
1326
1395
  }
@@ -1488,7 +1557,11 @@ async function localFounderFlow(ui, ctx = {}) {
1488
1557
  return { label: `./${mrName}/`, description: "managed repo" };
1489
1558
  }),
1490
1559
  ];
1491
- const assistantName = preferredRuntime === "codex" ? "Codex" : "Claude Code";
1560
+ const assistantName = preferredRuntime === "codex"
1561
+ ? "Codex"
1562
+ : preferredRuntime === "pi"
1563
+ ? "Pi"
1564
+ : "Claude Code";
1492
1565
  const setupSummary = {
1493
1566
  workspace: "created",
1494
1567
  invite: invitedUsers.length
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+
3
+ const crypto = require("node:crypto");
4
+ const fs = require("node:fs");
5
+ const path = require("node:path");
6
+
7
+ const PACKAGE = require("../package.json");
8
+
9
+ const BUNDLE_DIR = path.join(__dirname, "..", "runtime", "pi");
10
+ const BUNDLE_MANIFEST = path.join(BUNDLE_DIR, "manifest.json");
11
+ const LOCAL_MANIFEST = path.join(".egregore", "pi-runtime-manifest.json");
12
+
13
+ const REQUIRED_RUNTIME_PATHS = [
14
+ "AGENTS.md",
15
+ ".pi/APPEND_SYSTEM.md",
16
+ ".pi/settings.json",
17
+ ".pi/extensions/egregore.ts",
18
+ ".codex/skills",
19
+ "bin/agent.sh",
20
+ "bin/pi-branch-policy.mjs",
21
+ "bin/pi-session-start.sh",
22
+ "bin/codex-session-start.sh",
23
+ "bin/codex-skill-render.mjs",
24
+ ];
25
+
26
+ function hashBuffer(buffer) {
27
+ return crypto.createHash("sha256").update(buffer).digest("hex");
28
+ }
29
+
30
+ function readJson(file, fallback) {
31
+ try {
32
+ return JSON.parse(fs.readFileSync(file, "utf8"));
33
+ } catch {
34
+ return fallback;
35
+ }
36
+ }
37
+
38
+ function listFiles(dir, prefix = "") {
39
+ if (!fs.existsSync(dir)) return [];
40
+ const result = [];
41
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
42
+ const rel = prefix ? path.join(prefix, entry.name) : entry.name;
43
+ const full = path.join(dir, entry.name);
44
+ if (entry.isDirectory()) result.push(...listFiles(full, rel));
45
+ else if (entry.isFile()) result.push(rel);
46
+ }
47
+ return result.sort();
48
+ }
49
+
50
+ function bundleManifest() {
51
+ return readJson(BUNDLE_MANIFEST, { files: [], skills: [], skillCount: 0 });
52
+ }
53
+
54
+ function bundleFiles() {
55
+ return listFiles(BUNDLE_DIR).filter((rel) => rel !== "manifest.json");
56
+ }
57
+
58
+ function installManifestPath(egregoreDir) {
59
+ const gitPath = path.join(egregoreDir, ".git");
60
+ try {
61
+ const stat = fs.lstatSync(gitPath);
62
+ if (stat.isDirectory()) return path.join(gitPath, "info", "egregore-pi-runtime.json");
63
+ if (stat.isFile()) {
64
+ const match = fs.readFileSync(gitPath, "utf8").trim().match(/^gitdir:\s*(.+)$/);
65
+ if (match) {
66
+ const gitDir = path.isAbsolute(match[1]) ? match[1] : path.resolve(egregoreDir, match[1]);
67
+ return path.join(gitDir, "info", "egregore-pi-runtime.json");
68
+ }
69
+ }
70
+ } catch {}
71
+ return path.join(egregoreDir, LOCAL_MANIFEST);
72
+ }
73
+
74
+ function pathExists(file) {
75
+ try {
76
+ fs.lstatSync(file);
77
+ return true;
78
+ } catch {
79
+ return false;
80
+ }
81
+ }
82
+
83
+ function copyFile(src, dest) {
84
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
85
+ fs.copyFileSync(src, dest);
86
+ try { fs.chmodSync(dest, fs.statSync(src).mode & 0o777); } catch {}
87
+ }
88
+
89
+ function installPiRuntime(egregoreDir) {
90
+ if (!fs.existsSync(BUNDLE_DIR)) throw new Error(`Pi runtime bundle missing: ${BUNDLE_DIR}`);
91
+
92
+ const previousPath = installManifestPath(egregoreDir);
93
+ const previous = readJson(previousPath, { files: {} });
94
+ const previousFiles = previous.files || {};
95
+ const next = {
96
+ package: PACKAGE.name,
97
+ version: PACKAGE.version,
98
+ installedAt: new Date().toISOString(),
99
+ files: {},
100
+ };
101
+ const summary = {
102
+ installed: 0,
103
+ updated: 0,
104
+ skipped: 0,
105
+ conflicted: 0,
106
+ conflicts: [],
107
+ files: [],
108
+ bundleSkills: bundleManifest().skills || [],
109
+ bundleSkillCount: bundleManifest().skillCount || 0,
110
+ };
111
+
112
+ for (const rel of bundleFiles()) {
113
+ const src = path.join(BUNDLE_DIR, rel);
114
+ const dest = path.join(egregoreDir, rel);
115
+ const sourceHash = hashBuffer(fs.readFileSync(src));
116
+ const previousHash = previousFiles[rel]?.hash;
117
+ const record = () => {
118
+ next.files[rel] = { hash: sourceHash };
119
+ summary.files.push(rel);
120
+ };
121
+
122
+ if (!pathExists(dest)) {
123
+ copyFile(src, dest);
124
+ summary.installed += 1;
125
+ record();
126
+ continue;
127
+ }
128
+
129
+ let stat;
130
+ try { stat = fs.lstatSync(dest); } catch {}
131
+ if (!stat?.isFile()) {
132
+ summary.conflicted += 1;
133
+ summary.conflicts.push(rel);
134
+ if (previousHash) next.files[rel] = { hash: previousHash };
135
+ continue;
136
+ }
137
+
138
+ const currentHash = hashBuffer(fs.readFileSync(dest));
139
+ if (currentHash === sourceHash) {
140
+ try { fs.chmodSync(dest, fs.statSync(src).mode & 0o777); } catch {}
141
+ summary.skipped += 1;
142
+ record();
143
+ } else if (previousHash && currentHash === previousHash) {
144
+ copyFile(src, dest);
145
+ summary.updated += 1;
146
+ record();
147
+ } else {
148
+ summary.conflicted += 1;
149
+ summary.conflicts.push(rel);
150
+ if (previousHash) next.files[rel] = { hash: previousHash };
151
+ }
152
+ }
153
+
154
+ fs.mkdirSync(path.dirname(previousPath), { recursive: true });
155
+ fs.writeFileSync(previousPath, JSON.stringify(next, null, 2) + "\n");
156
+ return summary;
157
+ }
158
+
159
+ function hasPiRuntimeSupport(egregoreDir) {
160
+ return REQUIRED_RUNTIME_PATHS.every((rel) => fs.existsSync(path.join(egregoreDir, rel)));
161
+ }
162
+
163
+ function reportPiRuntimeSummary(summary, ui, opts = {}) {
164
+ if (!summary || !ui) return;
165
+ const label = opts.label || "Pi meta-harness";
166
+ const changed = summary.installed + summary.updated;
167
+ if (changed > 0) {
168
+ ui.success(`${label} ready (${summary.installed} installed, ${summary.updated} updated, ${summary.skipped} unchanged)`);
169
+ }
170
+ if (opts.includeConflicts !== false && summary.conflicted > 0) {
171
+ const count = summary.conflicted;
172
+ ui.info(ui.dim
173
+ ? ui.dim(`${label}: kept ${count} framework file${count === 1 ? "" : "s"} this Egregore already manages.`)
174
+ : `${label}: kept ${count} framework file${count === 1 ? "" : "s"} this Egregore already manages.`);
175
+ }
176
+ }
177
+
178
+ module.exports = {
179
+ BUNDLE_DIR,
180
+ bundleFiles,
181
+ bundleManifest,
182
+ hasPiRuntimeSupport,
183
+ installManifestPath,
184
+ installPiRuntime,
185
+ reportPiRuntimeSummary,
186
+ };
package/lib/setup.js CHANGED
@@ -12,6 +12,11 @@ const {
12
12
  installCodexRuntime,
13
13
  reportCodexRuntimeSummary,
14
14
  } = require("./codex-runtime");
15
+ const {
16
+ hasPiRuntimeSupport,
17
+ installPiRuntime,
18
+ reportPiRuntimeSummary,
19
+ } = require("./pi-runtime");
15
20
 
16
21
  const https = require("node:https");
17
22
 
@@ -438,7 +443,7 @@ async function install(data, ui, targetDir, opts = {}) {
438
443
  } catch {}
439
444
  }
440
445
 
441
- // 4d. Overlay packaged Codex support — Codex installs only. Claude Code reads
446
+ // 4d. Overlay the selected shell-runtime support. Claude Code reads
442
447
  // CLAUDE.md, not AGENTS.md; overlaying the Codex AGENTS.md/.codex into a Claude
443
448
  // workspace gives a wandering agent a competing startup protocol (the
444
449
  // xenocracy onboarding regression). Claude joiners keep the cloned repo as-is —
@@ -447,6 +452,9 @@ async function install(data, ui, targetDir, opts = {}) {
447
452
  if (preferredRuntime === "codex") {
448
453
  const codexRuntime = installCodexRuntime(egregoreDir);
449
454
  reportCodexRuntimeSummary(codexRuntime, ui);
455
+ } else if (preferredRuntime === "pi") {
456
+ const piRuntime = installPiRuntime(egregoreDir);
457
+ reportPiRuntimeSummary(piRuntime, ui);
450
458
  }
451
459
 
452
460
  // 5. Register instance + install launcher binary
@@ -675,23 +683,40 @@ function normalizeRuntime(runtime) {
675
683
  const raw = String(runtime || "claude-code").trim().toLowerCase();
676
684
  if (raw === "claude" || raw === "claude-code" || raw === "claude_code") return "claude-code";
677
685
  if (raw === "codex" || raw === "openai-codex" || raw === "openai_codex") return "codex";
686
+ if (raw === "pi" || raw === "pi-coding-agent" || raw === "pi_coding_agent") return "pi";
678
687
  throw new Error(`Unsupported Egregore runtime: ${runtime}`);
679
688
  }
680
689
 
681
690
  function runtimeDisplay(runtime) {
682
- return normalizeRuntime(runtime) === "codex" ? "Codex" : "Claude Code";
691
+ const normalized = normalizeRuntime(runtime);
692
+ if (normalized === "codex") return "Codex";
693
+ if (normalized === "pi") return "Pi";
694
+ return "Claude Code";
683
695
  }
684
696
 
685
697
  function runtimeBinary(runtime) {
686
- return normalizeRuntime(runtime) === "codex" ? "codex" : "claude";
698
+ const normalized = normalizeRuntime(runtime);
699
+ if (normalized === "codex") return "codex";
700
+ if (normalized === "pi") return "pi";
701
+ return "claude";
687
702
  }
688
703
 
689
704
  // One-line install command for the chosen assistant — shown when the
690
705
  // install finishes on a machine that doesn't have the assistant yet.
691
706
  function assistantInstallCommand(runtime) {
692
- return normalizeRuntime(runtime) === "codex"
693
- ? "npm install -g @openai/codex"
694
- : "npm install -g @anthropic-ai/claude-code";
707
+ const normalized = normalizeRuntime(runtime);
708
+ if (normalized === "codex") return "npm install -g @openai/codex";
709
+ if (normalized === "pi") return "npm install -g --ignore-scripts @earendil-works/pi-coding-agent";
710
+ return "npm install -g @anthropic-ai/claude-code";
711
+ }
712
+
713
+ function piTrustArgs() {
714
+ try {
715
+ const help = spawnSync("pi", ["--help"], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
716
+ return `${help.stdout || ""}\n${help.stderr || ""}`.includes("--approve") ? ["--approve"] : [];
717
+ } catch {
718
+ return [];
719
+ }
695
720
  }
696
721
 
697
722
  function runtimeLaunchArgs(runtime, egregoreDir) {
@@ -703,6 +728,7 @@ function runtimeLaunchArgs(runtime, egregoreDir) {
703
728
  "--cd", egregoreDir,
704
729
  ];
705
730
  }
731
+ if (normalizeRuntime(runtime) === "pi") return piTrustArgs();
706
732
  return ["start"];
707
733
  }
708
734
 
@@ -790,6 +816,16 @@ function hasCodexCoreSupport(egregoreDir) {
790
816
  return hasCodexRuntimeSupport(egregoreDir);
791
817
  }
792
818
 
819
+ function piSupportMessage(egregoreDir) {
820
+ return [
821
+ "Pi meta-harness support is missing from this Egregore checkout.",
822
+ ` path: ${egregoreDir}`,
823
+ "",
824
+ "Run npx -y create-egregore@latest to install the packaged Pi runtime, then rerun egregore.",
825
+ "Pi loads Egregore through project-local .pi resources and the shared portable skill corpus.",
826
+ ].join("\n");
827
+ }
828
+
793
829
  function installCodexSkills(egregoreDir) {
794
830
  const script = path.join(egregoreDir, "bin", "codex-install-skills.sh");
795
831
  if (!fs.existsSync(script)) return false;
@@ -887,12 +923,11 @@ async function launchOrInstruct(egregoreDir, alias, ui, ctx = {}) {
887
923
  }
888
924
 
889
925
  if (!commandExists(binary)) {
890
- const hasClaude = commandExists("claude");
891
- const hasCodex = commandExists("codex");
926
+ const available = ["claude-code", "codex", "pi"].filter((candidate) => commandExists(runtimeBinary(candidate)));
892
927
  // The user already chose `runtime` at the picker — lead with installing
893
928
  // THAT one ("install Codex first"), then offer the other as an alternative.
894
- const other = runtime === "codex" ? "claude-code" : "codex";
895
- if (!hasClaude && !hasCodex) {
929
+ const other = available[0] || ["claude-code", "codex", "pi"].find((candidate) => candidate !== runtime);
930
+ if (available.length === 0) {
896
931
  ui.warn(`Almost there — install ${display} to start sessions.`);
897
932
  ui.info(` Install ${display} first:`);
898
933
  ui.info(` ${ui.bold(assistantInstallCommand(runtime))}`);
@@ -910,7 +945,8 @@ async function launchOrInstruct(egregoreDir, alias, ui, ctx = {}) {
910
945
  return;
911
946
  }
912
947
 
913
- ui.info(`Starting Egregore in ${ui.bold(display)} now — press ${ui.bold("Ctrl+D")} (or /exit) to return to your shell.`);
948
+ const quitCommand = runtime === "pi" ? "/quit" : "/exit";
949
+ ui.info(`Starting Egregore in ${ui.bold(display)} now — press ${ui.bold("Ctrl+D")} (or ${quitCommand}) to return to your shell.`);
914
950
  ui.info(`Next time, ${launcherNextStep(ui, aliasName)}`);
915
951
  console.log("");
916
952
 
@@ -931,6 +967,15 @@ async function launchOrInstruct(egregoreDir, alias, ui, ctx = {}) {
931
967
  console.log("");
932
968
  return;
933
969
  }
970
+ } else if (runtime === "pi") {
971
+ const piRuntime = installPiRuntime(egregoreDir);
972
+ reportPiRuntimeSummary(piRuntime, ui);
973
+ if (!hasPiRuntimeSupport(egregoreDir)) {
974
+ ui.warn(piSupportMessage(egregoreDir));
975
+ ui.info(`Run ${ui.bold(aliasName)} after updating the checkout.`);
976
+ console.log("");
977
+ return;
978
+ }
934
979
  }
935
980
 
936
981
  const result = spawnSync(binary, runtimeLaunchArgs(runtime, egregoreDir), {
package/lib/ui.js CHANGED
@@ -1358,7 +1358,7 @@ function _renderWelcomeBody({ w, h, version }) {
1358
1358
  lines.push(`${DIM}└${"─".repeat(panelInner)}┘${RESET}`);
1359
1359
 
1360
1360
  lines.push("");
1361
- lines.push(`${DIM}a shared memory layer for teams using Claude Code or Codex${RESET}`);
1361
+ lines.push(`${DIM}shared memory across Claude Code, Codex, and Pi${RESET}`);
1362
1362
  lines.push("");
1363
1363
  lines.push(`${DIM}press ${RESET}${BOLD}↵${RESET}${DIM} to begin · ctrl-c to exit${RESET}`);
1364
1364
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "create-egregore",
3
- "version": "0.19.3",
4
- "description": "Set up Egregore for your team in one command — installs the egregore launcher and your choice of Claude Code or Codex.",
3
+ "version": "0.19.6",
4
+ "description": "Set up Egregore for your team in one command — installs the egregore launcher and your choice of Claude Code, Codex, or Pi.",
5
5
  "license": "MIT",
6
6
  "bin": {
7
7
  "create-egregore": "./bin/cli.js"
@@ -10,10 +10,11 @@
10
10
  "node": ">=18"
11
11
  },
12
12
  "scripts": {
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 test/scroll-repro.test.js test/launcher-refocus-redraw.test.js test/multiselect-remote-search.test.js test/back-navigation.test.js test/manifest-form.test.js test/launcher-delete.test.js",
13
+ "build:runtime": "node scripts/build-runtime-bundle.mjs && node scripts/build-pi-runtime-bundle.mjs",
14
+ "build:pi-runtime": "node scripts/build-pi-runtime-bundle.mjs",
15
+ "test": "node scripts/build-runtime-bundle.mjs --check && node scripts/build-pi-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/pi-runtime.test.js test/pi-product-workflows.test.js test/installer-runtime-source.test.js test/scroll-repro.test.js test/launcher-refocus-redraw.test.js test/multiselect-remote-search.test.js test/back-navigation.test.js test/manifest-form.test.js test/launcher-delete.test.js",
15
16
  "test:e2e": "node --test test/e2e-local.test.js",
16
- "test:all": "node scripts/build-runtime-bundle.mjs --check && node --test test/"
17
+ "test:all": "node scripts/build-runtime-bundle.mjs --check && node scripts/build-pi-runtime-bundle.mjs --check && node --test test/"
17
18
  },
18
19
  "files": [
19
20
  "bin/",
@@ -25,12 +26,13 @@
25
26
  "egregore",
26
27
  "claude",
27
28
  "codex",
29
+ "pi",
28
30
  "ai",
29
31
  "collaboration"
30
32
  ],
31
33
  "repository": {
32
34
  "type": "git",
33
- "url": "git+https://github.com/egregore-labs/egregore.git"
35
+ "url": "git+https://github.com/Curve-Labs/egregore.git"
34
36
  },
35
37
  "homepage": "https://github.com/egregore-labs/egregore"
36
38
  }