create-egregore 0.19.5 → 0.19.7

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 (130) hide show
  1. package/assets/egregore-launcher.js +411 -25
  2. package/bin/cli.js +34 -20
  3. package/lib/local.js +21 -5
  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 +7 -5
  8. package/runtime/codex/.codex/spec-manifest.json +1 -1
  9. package/runtime/codex/AGENTS.md +7 -2
  10. package/runtime/codex/bin/codex-render-spec.mjs +6 -1
  11. package/runtime/codex/bin/codex-session-start.sh +5 -59
  12. package/runtime/codex/bin/codex-skill-render.mjs +7 -5
  13. package/runtime/codex/bin/lib/context.sh +8 -1
  14. package/runtime/codex/bin/lib/greeting.sh +12 -108
  15. package/runtime/codex/bin/lib/metrics.sh +374 -0
  16. package/runtime/codex/bin/publish-artifact.sh +14 -0
  17. package/runtime/codex/bin/settings.sh +274 -0
  18. package/runtime/codex/manifest.json +2 -0
  19. package/runtime/pi/.codex/config.toml +15 -0
  20. package/runtime/pi/.codex/hooks/branch-guard.js +242 -0
  21. package/runtime/pi/.codex/hooks/search-hint.js +65 -0
  22. package/runtime/pi/.codex/hooks.json +30 -0
  23. package/runtime/pi/.codex/skills/activity/SKILL.md +70 -0
  24. package/runtime/pi/.codex/skills/add/SKILL.md +25 -0
  25. package/runtime/pi/.codex/skills/announce/SKILL.md +54 -0
  26. package/runtime/pi/.codex/skills/answer/SKILL.md +25 -0
  27. package/runtime/pi/.codex/skills/archive/SKILL.md +41 -0
  28. package/runtime/pi/.codex/skills/ask/SKILL.md +59 -0
  29. package/runtime/pi/.codex/skills/branch/SKILL.md +25 -0
  30. package/runtime/pi/.codex/skills/checkup/SKILL.md +41 -0
  31. package/runtime/pi/.codex/skills/commit/SKILL.md +25 -0
  32. package/runtime/pi/.codex/skills/contribute/SKILL.md +25 -0
  33. package/runtime/pi/.codex/skills/dark-mode/SKILL.md +25 -0
  34. package/runtime/pi/.codex/skills/dashboard/SKILL.md +59 -0
  35. package/runtime/pi/.codex/skills/deep-reflect/SKILL.md +74 -0
  36. package/runtime/pi/.codex/skills/delete-user/SKILL.md +25 -0
  37. package/runtime/pi/.codex/skills/egregore-voice/SKILL.md +25 -0
  38. package/runtime/pi/.codex/skills/emissary/SKILL.md +41 -0
  39. package/runtime/pi/.codex/skills/env/SKILL.md +25 -0
  40. package/runtime/pi/.codex/skills/handoff/SKILL.md +67 -0
  41. package/runtime/pi/.codex/skills/harvest/SKILL.md +107 -0
  42. package/runtime/pi/.codex/skills/ingest/SKILL.md +25 -0
  43. package/runtime/pi/.codex/skills/ingest-google/SKILL.md +25 -0
  44. package/runtime/pi/.codex/skills/ingest-user-interview/SKILL.md +41 -0
  45. package/runtime/pi/.codex/skills/invite/SKILL.md +40 -0
  46. package/runtime/pi/.codex/skills/issue/SKILL.md +41 -0
  47. package/runtime/pi/.codex/skills/me/SKILL.md +25 -0
  48. package/runtime/pi/.codex/skills/meeting/SKILL.md +41 -0
  49. package/runtime/pi/.codex/skills/minion/SKILL.md +25 -0
  50. package/runtime/pi/.codex/skills/note/SKILL.md +25 -0
  51. package/runtime/pi/.codex/skills/onboarding/SKILL.md +41 -0
  52. package/runtime/pi/.codex/skills/pr/SKILL.md +25 -0
  53. package/runtime/pi/.codex/skills/project/SKILL.md +41 -0
  54. package/runtime/pi/.codex/skills/pull/SKILL.md +25 -0
  55. package/runtime/pi/.codex/skills/push/SKILL.md +25 -0
  56. package/runtime/pi/.codex/skills/quest/SKILL.md +77 -0
  57. package/runtime/pi/.codex/skills/quest-suggest/SKILL.md +25 -0
  58. package/runtime/pi/.codex/skills/reflect/SKILL.md +41 -0
  59. package/runtime/pi/.codex/skills/review-pr/SKILL.md +41 -0
  60. package/runtime/pi/.codex/skills/save/SKILL.md +62 -0
  61. package/runtime/pi/.codex/skills/scroll/SKILL.md +168 -0
  62. package/runtime/pi/.codex/skills/scroll/agents/openai.yaml +4 -0
  63. package/runtime/pi/.codex/skills/scroll/assets/shell.html +542 -0
  64. package/runtime/pi/.codex/skills/search/SKILL.md +56 -0
  65. package/runtime/pi/.codex/skills/session-view/SKILL.md +25 -0
  66. package/runtime/pi/.codex/skills/setup/SKILL.md +25 -0
  67. package/runtime/pi/.codex/skills/summon/SKILL.md +41 -0
  68. package/runtime/pi/.codex/skills/sync-repos/SKILL.md +25 -0
  69. package/runtime/pi/.codex/skills/telegram-connect/SKILL.md +25 -0
  70. package/runtime/pi/.codex/skills/telemetry/SKILL.md +25 -0
  71. package/runtime/pi/.codex/skills/test/SKILL.md +41 -0
  72. package/runtime/pi/.codex/skills/the-spiral/SKILL.md +84 -0
  73. package/runtime/pi/.codex/skills/thread/SKILL.md +25 -0
  74. package/runtime/pi/.codex/skills/todo/SKILL.md +41 -0
  75. package/runtime/pi/.codex/skills/tui-design/SKILL.md +41 -0
  76. package/runtime/pi/.codex/skills/tutorial/SKILL.md +41 -0
  77. package/runtime/pi/.codex/skills/update/SKILL.md +25 -0
  78. package/runtime/pi/.codex/skills/view/SKILL.md +156 -0
  79. package/runtime/pi/.codex/skills/view/agents/openai.yaml +4 -0
  80. package/runtime/pi/.codex/skills/view/assets/compose-scaffold.html +221 -0
  81. package/runtime/pi/.codex/skills/visual-explain/SKILL.md +41 -0
  82. package/runtime/pi/.codex/skills/wrap/SKILL.md +55 -0
  83. package/runtime/pi/.codex/spec-manifest.json +114 -0
  84. package/runtime/pi/.pi/APPEND_SYSTEM.md +255 -0
  85. package/runtime/pi/.pi/extensions/egregore.ts +324 -0
  86. package/runtime/pi/.pi/settings.json +6 -0
  87. package/runtime/pi/.pi/spec-manifest.json +12 -0
  88. package/runtime/pi/AGENTS.md +350 -0
  89. package/runtime/pi/bin/activity-data.sh +242 -0
  90. package/runtime/pi/bin/agent.sh +677 -0
  91. package/runtime/pi/bin/codex-install-skills.sh +78 -0
  92. package/runtime/pi/bin/codex-render-spec.mjs +394 -0
  93. package/runtime/pi/bin/codex-session-start.sh +188 -0
  94. package/runtime/pi/bin/codex-skill-render.mjs +323 -0
  95. package/runtime/pi/bin/codex-sync-skills.sh +227 -0
  96. package/runtime/pi/bin/dashboard-data.sh +178 -0
  97. package/runtime/pi/bin/graph-batch.sh +60 -0
  98. package/runtime/pi/bin/graph-op.sh +431 -0
  99. package/runtime/pi/bin/graph-wal.sh +228 -0
  100. package/runtime/pi/bin/graph.sh +177 -0
  101. package/runtime/pi/bin/handoff-pr-backfill.sh +86 -0
  102. package/runtime/pi/bin/handoff-run.sh +581 -0
  103. package/runtime/pi/bin/handoff-save-egregore.sh +136 -0
  104. package/runtime/pi/bin/index-handoff.sh +312 -0
  105. package/runtime/pi/bin/lib/artifact-id.sh +46 -0
  106. package/runtime/pi/bin/lib/config.sh +71 -0
  107. package/runtime/pi/bin/lib/context.sh +542 -0
  108. package/runtime/pi/bin/lib/dashboard-artifact.sh +142 -0
  109. package/runtime/pi/bin/lib/ensure-develop.sh +183 -0
  110. package/runtime/pi/bin/lib/git-sync.sh +288 -0
  111. package/runtime/pi/bin/lib/greeting.sh +340 -0
  112. package/runtime/pi/bin/lib/hash.sh +13 -0
  113. package/runtime/pi/bin/lib/identity.sh +174 -0
  114. package/runtime/pi/bin/lib/metrics.sh +374 -0
  115. package/runtime/pi/bin/lib/permissions.sh +106 -0
  116. package/runtime/pi/bin/lib/time.sh +56 -0
  117. package/runtime/pi/bin/lib/worktree-links.sh +54 -0
  118. package/runtime/pi/bin/notify.sh +207 -0
  119. package/runtime/pi/bin/pi-branch-policy.mjs +15 -0
  120. package/runtime/pi/bin/pi-product-workflows.mjs +324 -0
  121. package/runtime/pi/bin/pi-render-spec.mjs +114 -0
  122. package/runtime/pi/bin/pi-session-start.sh +24 -0
  123. package/runtime/pi/bin/publish-artifact.sh +201 -0
  124. package/runtime/pi/bin/publish-references.sh +144 -0
  125. package/runtime/pi/bin/repo-state.sh +99 -0
  126. package/runtime/pi/bin/telemetry.sh +391 -0
  127. package/runtime/pi/bin/worktree-create.sh +118 -0
  128. package/runtime/pi/bin/worktree-remove.sh +50 -0
  129. package/runtime/pi/bin/worktree.sh +198 -0
  130. 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,
@@ -625,13 +626,13 @@ async function maybeSetupTelegramGroup(ui, {
625
626
  return { setup: true, groupLink: storedGroupLink };
626
627
  }
627
628
 
628
- // Claude Code workspace cleanup: remove the Codex AGENTS.md/.codex/.agents the
629
+ // Claude Code workspace cleanup: remove shell-runtime overlays the
629
630
  // GitHub template ships, so the agent follows CLAUDE.md rather than a competing
630
631
  // startup protocol. Founder-only (we own the repo) — commit + push the removal
631
632
  // so it persists for everyone who clones it. A Codex teammate who joins later
632
633
  // regenerates AGENTS.md via /update (bin/codex-render-spec.mjs).
633
634
  function stripCodexRuntimeForClaude(egregoreDir, ui) {
634
- const targets = ["AGENTS.md", ".codex", ".agents"];
635
+ const targets = ["AGENTS.md", ".codex", ".agents", ".pi"];
635
636
  const present = targets.filter((t) => fs.existsSync(path.join(egregoreDir, t)));
636
637
  if (!present.length) {
637
638
  ui.success("Claude Code workspace — using CLAUDE.md");
@@ -639,7 +640,7 @@ function stripCodexRuntimeForClaude(egregoreDir, ui) {
639
640
  }
640
641
  try {
641
642
  execFileSync("git", ["rm", "-r", "-q", "--", ...present], { cwd: egregoreDir, stdio: "pipe" });
642
- 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 });
643
644
  run("git push", { cwd: egregoreDir });
644
645
  ui.success(`Claude Code workspace — using CLAUDE.md (removed ${present.join(", ")})`);
645
646
  } catch {
@@ -1355,7 +1356,7 @@ async function localFounderFlow(ui, ctx = {}) {
1355
1356
  }
1356
1357
  }
1357
1358
 
1358
- // 16. Runtime overlay. Codex installs commit the Codex runtime into the repo
1359
+ // 16. Runtime overlay. Codex and Pi installs commit their derived runtime
1359
1360
  // so it's TRACKED (symmetric with .claude/). Claude installs do the
1360
1361
  // opposite: Claude Code reads CLAUDE.md, not AGENTS.md, so we strip the
1361
1362
  // Codex AGENTS.md/.codex the template ships — otherwise a fresh Claude
@@ -1378,6 +1379,17 @@ async function localFounderFlow(ui, ctx = {}) {
1378
1379
  run("git push", { cwd: egregoreDir });
1379
1380
  }
1380
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 {}
1381
1393
  } else {
1382
1394
  stripCodexRuntimeForClaude(egregoreDir, ui);
1383
1395
  }
@@ -1545,7 +1557,11 @@ async function localFounderFlow(ui, ctx = {}) {
1545
1557
  return { label: `./${mrName}/`, description: "managed repo" };
1546
1558
  }),
1547
1559
  ];
1548
- const assistantName = preferredRuntime === "codex" ? "Codex" : "Claude Code";
1560
+ const assistantName = preferredRuntime === "codex"
1561
+ ? "Codex"
1562
+ : preferredRuntime === "pi"
1563
+ ? "Pi"
1564
+ : "Claude Code";
1549
1565
  const setupSummary = {
1550
1566
  workspace: "created",
1551
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.5",
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.7",
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,6 +26,7 @@
25
26
  "egregore",
26
27
  "claude",
27
28
  "codex",
29
+ "pi",
28
30
  "ai",
29
31
  "collaboration"
30
32
  ],
@@ -11,7 +11,7 @@
11
11
  {
12
12
  "section": "Identity & Upstream",
13
13
  "action": "keep",
14
- "sourceHash": "11d07e3fcb58",
14
+ "sourceHash": "ddcbc0083aff",
15
15
  "note": "Kept; /update, /contribute, /save tokens rendered as $skill tokens."
16
16
  },
17
17
  {
@@ -9,6 +9,11 @@
9
9
  > Egregore skill already covers. This file exists only for runtimes that cannot
10
10
  > read `CLAUDE.md`.
11
11
  >
12
+ > **If you are Pi** (the `pi` coding-agent harness): **stop here.** Your
13
+ > authoritative Egregore instructions are loaded from
14
+ > `.pi/APPEND_SYSTEM.md`, and your workflows are exposed through the
15
+ > project-local `.pi/` runtime. Do not follow the Codex-specific block below.
16
+ >
12
17
  > **If you are Codex or another shell-only agent:** this file is yours — continue.
13
18
 
14
19
  Egregore is no longer only a Claude Code workspace. Claude Code remains the
@@ -80,7 +85,7 @@ This block is the Codex-native Egregore behavioral spec, rendered from CLAUDE.md
80
85
 
81
86
  ## Identity & Upstream
82
87
 
83
- 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/agents/`, `loom/`, `CLAUDE.md`, `skills/`) is synced from upstream on every session start and via `$update`. It is not authored in this repo.
88
+ 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/agents/`, `.pi/`, `loom/`, `CLAUDE.md`, `skills/`) is synced from upstream on every session start and via `$update`. It is not authored in this repo.
84
89
 
85
90
  **Where changes belong:**
86
91
  - **Framework is outdated or a skill is missing:** run `$update` first. It pulls the latest from upstream.
@@ -93,7 +98,7 @@ When a user reports broken or missing framework behavior, the first question is
93
98
 
94
99
  ## On Launch — MANDATORY FIRST ACTION
95
100
 
96
- 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`.
101
+ The `egregore` launcher renders the Egregore startup card (identity, team momentum, and pending work) via `bin/codex-session-start.sh` before Codex starts, and installs project skills. Do not rerun startup checks and do not narrate startup. The card ends with **"What are you working on?"** — that question is already on screen; treat the user's first message as the answer to it. To re-show the card, run `bash bin/codex-session-start.sh --card`.
97
102
 
98
103
  ---
99
104
 
@@ -53,6 +53,11 @@ const AGENTS_PREAMBLE = `# Egregore Agent Protocol
53
53
  > Egregore skill already covers. This file exists only for runtimes that cannot
54
54
  > read \`CLAUDE.md\`.
55
55
  >
56
+ > **If you are Pi** (the \`pi\` coding-agent harness): **stop here.** Your
57
+ > authoritative Egregore instructions are loaded from
58
+ > \`.pi/APPEND_SYSTEM.md\`, and your workflows are exposed through the
59
+ > project-local \`.pi/\` runtime. Do not follow the Codex-specific block below.
60
+ >
56
61
  > **If you are Codex or another shell-only agent:** this file is yours — continue.
57
62
 
58
63
  Egregore is no longer only a Claude Code workspace. Claude Code remains the
@@ -169,7 +174,7 @@ const INTRO_BODY = `You are a collaborator inside Egregore — a shared intellig
169
174
 
170
175
  This block is the Codex-native Egregore behavioral spec, rendered from CLAUDE.md (the source of truth for egregore behavior) by \`bin/codex-render-spec.mjs\`. The per-section adaptation manifest is \`.codex/spec-manifest.json\`. The thin agent protocol above this block applies to every shell-capable agent; this block is the full behavioral contract for Codex sessions.`;
171
176
 
172
- const ON_LAUNCH_BODY = `The \`egregore\` launcher renders the Egregore startup card (identity, handoffs, team activity) via \`bin/codex-session-start.sh\` before Codex starts, and installs project skills. Do not rerun startup checks and do not narrate startup. The card ends with **"What are you working on?"** — that question is already on screen; treat the user's first message as the answer to it. To re-show the card, run \`bash bin/codex-session-start.sh --card\`.`;
177
+ const ON_LAUNCH_BODY = `The \`egregore\` launcher renders the Egregore startup card (identity, team momentum, and pending work) via \`bin/codex-session-start.sh\` before Codex starts, and installs project skills. Do not rerun startup checks and do not narrate startup. The card ends with **"What are you working on?"** — that question is already on screen; treat the user's first message as the answer to it. To re-show the card, run \`bash bin/codex-session-start.sh --card\`.`;
173
178
 
174
179
  const AFTER_GREETING_BODY = `**Mandatory behavioral rule.** When the user describes work, your **first action** — before reading files, exploring code, or anything else — is to get onto a working branch:
175
180