create-egregore 0.20.4 → 0.20.5

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.20.4
2
+ // egregore-launcher-version: 0.20.5
3
3
  //
4
4
  // egregore — launch and browse Egregore instances from your terminal.
5
5
  // Installed by create-egregore. Reads ~/.egregore/instances.json.
@@ -33,7 +33,7 @@ const crypto = require("node:crypto");
33
33
  const { spawnSync, spawn } = require("node:child_process");
34
34
  const readline = require("node:readline");
35
35
 
36
- const VERSION = "0.20.4";
36
+ const VERSION = "0.20.5";
37
37
  const REGISTRY = path.join(os.homedir(), ".egregore", "instances.json");
38
38
  const DEFAULT_FILE = path.join(os.homedir(), ".egregore", "default");
39
39
  const UPDATE_CACHE_FILE = path.join(os.homedir(), ".egregore", "update-check.json");
@@ -689,72 +689,120 @@ async function renderCodexStartupCard(egregoreDir) {
689
689
  return false;
690
690
  }
691
691
 
692
- function codexSupportMessage(egregoreDir) {
693
- return [
694
- "Codex harness support is missing from this Egregore checkout.",
695
- ` path: ${egregoreDir}`,
696
- "",
697
- "This checkout needs a newer Egregore core with:",
698
- " bin/codex-session-start.sh",
699
- " bin/codex-install-skills.sh",
700
- " .codex/config.toml",
701
- " .codex/skills/",
702
- " .codex/hooks.json",
703
- " .codex/hooks/",
704
- "",
705
- "Restore this instance using its installed runtime version, then rerun egregore.",
706
- "A global CLI refresh does not repair instance files.",
707
- "Egregore workflows appear as slash commands. In Codex, invoke them with the matching $skill token or ask in natural language.",
708
- ].join("\n");
692
+ const CORE_SUPPORT_PATHS = {
693
+ codex: [
694
+ "bin/codex-session-start.sh",
695
+ "bin/codex-install-skills.sh",
696
+ ".codex/config.toml",
697
+ ".codex/skills",
698
+ ".codex/hooks.json",
699
+ ".codex/hooks",
700
+ ],
701
+ pi: [
702
+ "bin/pi-session-start.sh",
703
+ ".pi/APPEND_SYSTEM.md",
704
+ ".pi/settings.json",
705
+ ".pi/extensions/egregore.ts",
706
+ ".codex/skills",
707
+ ],
708
+ prime: [
709
+ "bin/prime-session-start.sh",
710
+ ".prime/agent/APPEND_SYSTEM.md",
711
+ ".prime/agent/settings.json",
712
+ ".prime/agent/extensions/egregore.ts",
713
+ ".codex/skills",
714
+ ],
715
+ };
716
+
717
+ function missingCorePaths(runtime, egregoreDir) {
718
+ return (CORE_SUPPORT_PATHS[runtime] || []).filter(
719
+ (rel) => !fs.existsSync(path.join(egregoreDir, ...rel.split("/"))),
720
+ );
709
721
  }
710
722
 
711
723
  function hasCodexCoreSupport(egregoreDir) {
712
- return (
713
- fs.existsSync(path.join(egregoreDir, "bin", "codex-session-start.sh")) &&
714
- fs.existsSync(path.join(egregoreDir, "bin", "codex-install-skills.sh")) &&
715
- fs.existsSync(path.join(egregoreDir, ".codex", "config.toml")) &&
716
- fs.existsSync(path.join(egregoreDir, ".codex", "skills")) &&
717
- fs.existsSync(path.join(egregoreDir, ".codex", "hooks.json")) &&
718
- fs.existsSync(path.join(egregoreDir, ".codex", "hooks"))
719
- );
724
+ return missingCorePaths("codex", egregoreDir).length === 0;
720
725
  }
721
726
 
722
727
  function hasPiCoreSupport(egregoreDir) {
723
- return (
724
- fs.existsSync(path.join(egregoreDir, "bin", "pi-session-start.sh")) &&
725
- fs.existsSync(path.join(egregoreDir, ".pi", "APPEND_SYSTEM.md")) &&
726
- fs.existsSync(path.join(egregoreDir, ".pi", "settings.json")) &&
727
- fs.existsSync(path.join(egregoreDir, ".pi", "extensions", "egregore.ts")) &&
728
- fs.existsSync(path.join(egregoreDir, ".codex", "skills"))
729
- );
728
+ return missingCorePaths("pi", egregoreDir).length === 0;
730
729
  }
731
730
 
732
- function piSupportMessage(egregoreDir) {
733
- return [
734
- "Pi meta-harness support is missing from this Egregore checkout.",
731
+ function hasPrimeCoreSupport(egregoreDir) {
732
+ return missingCorePaths("prime", egregoreDir).length === 0;
733
+ }
734
+
735
+ // The repair command is pinned to this launcher's own release. `@latest`
736
+ // once pulled another release channel's files into an instance; the
737
+ // launcher and the files it installs must always be the same version.
738
+ function harnessAddCommand(runtime) {
739
+ return `npx -y ${PUBLIC_NPX_PACKAGE}@${VERSION} --add-harness ${runtimeShort(runtime)}`;
740
+ }
741
+
742
+ function harnessSupportMessage(runtime, egregoreDir) {
743
+ const label = runtimeLabel(runtime);
744
+ const missing = missingCorePaths(runtime, egregoreDir);
745
+ const lines = [
746
+ `${label} support is missing from this Egregore checkout.`,
735
747
  ` path: ${egregoreDir}`,
748
+ ];
749
+ if (missing.length > 0) {
750
+ lines.push("", "Missing:");
751
+ for (const rel of missing) lines.push(` ${rel}`);
752
+ }
753
+ lines.push(
736
754
  "",
737
- "Restore this instance using its installed runtime version, then rerun egregore.",
738
- ].join("\n");
755
+ `Add ${label} support to this instance (missing files only, nothing overwritten):`,
756
+ ` cd ${egregoreDir}`,
757
+ ` ${harnessAddCommand(runtime)}`,
758
+ "Then rerun egregore.",
759
+ );
760
+ if (runtime === "codex") {
761
+ lines.push("Egregore workflows appear as slash commands. In Codex, invoke them with the matching $skill token or ask in natural language.");
762
+ }
763
+ return lines.join("\n");
739
764
  }
740
765
 
741
- function hasPrimeCoreSupport(egregoreDir) {
742
- return (
743
- fs.existsSync(path.join(egregoreDir, "bin", "prime-session-start.sh")) &&
744
- fs.existsSync(path.join(egregoreDir, ".prime", "agent", "APPEND_SYSTEM.md")) &&
745
- fs.existsSync(path.join(egregoreDir, ".prime", "agent", "settings.json")) &&
746
- fs.existsSync(path.join(egregoreDir, ".prime", "agent", "extensions", "egregore.ts")) &&
747
- fs.existsSync(path.join(egregoreDir, ".codex", "skills"))
748
- );
766
+ function codexSupportMessage(egregoreDir) {
767
+ return harnessSupportMessage("codex", egregoreDir);
768
+ }
769
+
770
+ function piSupportMessage(egregoreDir) {
771
+ return harnessSupportMessage("pi", egregoreDir);
749
772
  }
750
773
 
751
774
  function primeSupportMessage(egregoreDir) {
752
- return [
753
- "Prime Agent runtime support is missing from this Egregore checkout.",
754
- ` path: ${egregoreDir}`,
775
+ return harnessSupportMessage("prime", egregoreDir);
776
+ }
777
+
778
+ // A launch that fails the support check offers to add exactly that harness
779
+ // to exactly this instance, using this launcher's own release. Without a
780
+ // terminal to ask, print the command and stop; never install unattended.
781
+ async function ensureHarnessSupport(inst, runtime) {
782
+ if (missingCorePaths(runtime, inst.path).length === 0) return;
783
+ const message = harnessSupportMessage(runtime, inst.path);
784
+ if (!canPrompt()) die(message);
785
+ const label = runtimeLabel(runtime);
786
+ const name = inst.name || inst.slug || path.basename(inst.path);
787
+ process.stdout.write([
755
788
  "",
756
- "Restore this instance using its installed runtime version, then rerun egregore.",
757
- ].join("\n");
789
+ ` ${BOLD}${label} support is missing from ${name}.${RESET}`,
790
+ ` ${DIM}Missing: ${missingCorePaths(runtime, inst.path).join(", ")}${RESET}`,
791
+ ` ${DIM}Egregore can add it now. Only missing files are written; nothing is overwritten:${RESET}`,
792
+ ` ${harnessAddCommand(runtime)}`,
793
+ "",
794
+ ].join("\n"));
795
+ const yes = await confirmLine(`Add ${label} support to ${name} now?`);
796
+ if (!yes) die(message);
797
+ const result = spawnSync(
798
+ "npx",
799
+ ["-y", `${PUBLIC_NPX_PACKAGE}@${VERSION}`, "--add-harness", runtimeShort(runtime)],
800
+ { cwd: inst.path, stdio: "inherit", env: process.env, timeout: 3 * 60 * 1000 },
801
+ );
802
+ if (result.error || result.status !== 0 || missingCorePaths(runtime, inst.path).length > 0) {
803
+ die(`${label} support could not be added automatically.\n\n${harnessSupportMessage(runtime, inst.path)}`);
804
+ }
805
+ process.stdout.write(` ${GREEN}✓${RESET} ${label} support added to ${name}\n\n`);
758
806
  }
759
807
 
760
808
  // Minimal y/N line prompt for pre-launch decisions (the TUI select machinery
@@ -809,7 +857,7 @@ function primeAuthHint() {
809
857
 
810
858
  function installCodexSkills(egregoreDir) {
811
859
  const script = path.join(egregoreDir, "bin", "codex-install-skills.sh");
812
- if (!fs.existsSync(script)) return false;
860
+ if (!fs.existsSync(script)) return { ok: false, stderr: `missing ${script}` };
813
861
  try {
814
862
  const result = spawnSync("bash", [script], {
815
863
  cwd: egregoreDir,
@@ -817,9 +865,9 @@ function installCodexSkills(egregoreDir) {
817
865
  encoding: "utf-8",
818
866
  maxBuffer: 1024 * 1024,
819
867
  });
820
- return result.status === 0;
821
- } catch {
822
- return false;
868
+ return { ok: result.status === 0, stderr: (result.stderr || "").trim() };
869
+ } catch (err) {
870
+ return { ok: false, stderr: String(err && err.message ? err.message : err) };
823
871
  }
824
872
  }
825
873
 
@@ -886,29 +934,31 @@ async function launch(inst, runtimeOverride) {
886
934
  }
887
935
  }
888
936
  if (runtime === "codex") {
889
- // Launching never changes an instance's runtime or release channel.
890
- if (!hasCodexCoreSupport(inst.path)) die(codexSupportMessage(inst.path));
891
- const skillsInstalled = installCodexSkills(inst.path);
937
+ // Launching never changes an instance's runtime or release channel. A
938
+ // missing harness is added only with the user's consent, one harness
939
+ // into this one instance, at this launcher's own version.
940
+ await ensureHarnessSupport(inst, runtime);
941
+ const skills = installCodexSkills(inst.path);
942
+ if (!skills.ok) {
943
+ die(`Codex skill setup failed in ${inst.path} (bin/codex-install-skills.sh):\n${skills.stderr || " (no error output)"}`);
944
+ }
892
945
  const cardRendered = await renderCodexStartupCard(inst.path);
893
- if (!skillsInstalled || !cardRendered) {
894
- die(codexSupportMessage(inst.path));
946
+ if (!cardRendered) {
947
+ die(`Codex startup card failed in ${inst.path} (bin/codex-session-start.sh exited non-zero; its output is above).`);
895
948
  }
896
949
  } else if (runtime === "pi") {
897
- if (!hasPiCoreSupport(inst.path)) {
898
- die(piSupportMessage(inst.path));
899
- }
950
+ await ensureHarnessSupport(inst, runtime);
900
951
  }
901
952
  let launchArgs = null;
902
953
  if (runtime === "prime") {
954
+ await ensureHarnessSupport(inst, runtime);
903
955
  // Cover the gap between choosing the harness and Prime's first paint:
904
- // the loader runs through the launcher-side pre-flight (core-support
905
- // check + the trust-flag probe, which shells out to the binary), then a
906
- // single handoff line explains the remaining in-harness startup.
956
+ // the loader runs through the launcher-side pre-flight (the trust-flag
957
+ // probe, which shells out to the binary), then a single handoff line
958
+ // explains the remaining in-harness startup.
907
959
  const stopLoader = startSpinner("Starting Prime Agent");
908
- const coreOk = hasPrimeCoreSupport(inst.path);
909
- launchArgs = coreOk ? runtimeLaunchArgs(runtime, inst.path) : null;
960
+ launchArgs = runtimeLaunchArgs(runtime, inst.path);
910
961
  stopLoader();
911
- if (!coreOk) die(primeSupportMessage(inst.path));
912
962
  primeAuthHint();
913
963
  process.stdout.write(` ${CYAN}◆${RESET} ${BOLD}Prime Agent starting${RESET}${DIM} — the Egregore greeting loads inside in a few seconds${RESET}\n`);
914
964
  }
package/bin/cli.js CHANGED
@@ -89,6 +89,32 @@ function detectCwdEgregore(cwd) {
89
89
  }
90
90
  }
91
91
 
92
+ // Resolve which instance `--add-harness` writes into. Explicit `--instance`
93
+ // wins; otherwise the current directory when it is an instance; otherwise the
94
+ // only registered instance. Several registered instances and no explicit
95
+ // choice is an error — this command must never fan out.
96
+ function resolveAddHarnessTarget(explicitPath) {
97
+ const isInstance = (dir) => dir && fs.existsSync(path.join(dir, "egregore.json"));
98
+ if (explicitPath) {
99
+ const dir = path.resolve(explicitPath);
100
+ if (isInstance(dir)) return dir;
101
+ ui.error(`Not an Egregore instance: ${dir} (no egregore.json).`);
102
+ return null;
103
+ }
104
+ let cwd = null;
105
+ try { cwd = process.cwd(); } catch {}
106
+ if (isInstance(cwd)) return cwd;
107
+ const registered = readLocalInstances().filter((inst) => isInstance(inst.path));
108
+ if (registered.length === 1) return path.resolve(registered[0].path);
109
+ if (registered.length === 0) {
110
+ ui.error("No Egregore instance found. Run this inside the instance directory, or pass --instance <path>.");
111
+ return null;
112
+ }
113
+ ui.error("Several Egregore instances are registered. Pass --instance <path> to choose one:");
114
+ for (const inst of registered) ui.info(` ${inst.path}`);
115
+ return null;
116
+ }
117
+
92
118
  function binaryOnPath(bin) {
93
119
  return commandExists(bin);
94
120
  }
@@ -199,6 +225,14 @@ function parseArgs(argv) {
199
225
  args.refreshLauncher = true;
200
226
  } else if (argv[i] === "--refresh-connect") {
201
227
  args.refreshConnect = true;
228
+ } else if (argv[i] === "--add-harness" && argv[i + 1]) {
229
+ args.addHarness = argv[++i];
230
+ } else if (argv[i].startsWith("--add-harness=")) {
231
+ args.addHarness = argv[i].split("=")[1];
232
+ } else if (argv[i] === "--instance" && argv[i + 1]) {
233
+ args.instance = argv[++i];
234
+ } else if (argv[i].startsWith("--instance=")) {
235
+ args.instance = argv[i].split("=")[1];
202
236
  } else if (argv[i] === "--codex") {
203
237
  args.runtime = "codex";
204
238
  } else if (argv[i] === "--pi") {
@@ -375,6 +409,8 @@ function showHelp() {
375
409
  ui.info(" --no-autolaunch Don't launch the harness at the end (for CI)");
376
410
  ui.info(" --no-screens Skip the full-screen welcome/ready bookends");
377
411
  ui.info(" --refresh-launcher Refresh only the global Egregore CLI, then exit");
412
+ ui.info(" --add-harness <h> Add codex, pi, or prime support to one existing instance (missing files only)");
413
+ ui.info(" --instance <path> Instance for --add-harness (default: cwd, or the only registered instance)");
378
414
  ui.info(" -h, --help Show this help");
379
415
  ui.info("");
380
416
  }
@@ -421,6 +457,39 @@ async function main() {
421
457
  }
422
458
  }
423
459
 
460
+ if (args.addHarness) {
461
+ // Explicit, instance-scoped harness addition. This is the only path that
462
+ // writes harness files into an existing instance from the global CLI: one
463
+ // harness, one instance, missing files only. The launcher invokes it with
464
+ // its own pinned version when a launch fails the support check.
465
+ const { addHarnessRuntime, harnessLabel, normalizeHarness } = require("../lib/harness-add");
466
+ const harness = normalizeHarness(args.addHarness);
467
+ if (!harness) {
468
+ ui.error(`Unknown harness "${args.addHarness}". Use --add-harness codex, pi, or prime.`);
469
+ process.exit(1);
470
+ }
471
+ const targetDir = resolveAddHarnessTarget(args.instance);
472
+ if (!targetDir) process.exit(1);
473
+ try {
474
+ const summary = addHarnessRuntime(targetDir, harness);
475
+ const label = harnessLabel(harness);
476
+ if (summary.installed.length === 0) {
477
+ ui.success(`${label} support already present in ${targetDir}`);
478
+ } else {
479
+ ui.success(`Added ${label} support to ${targetDir} ${ui.dim(`(${summary.installed.length} files, ${summary.present} kept)`)}`);
480
+ }
481
+ if (!summary.ready) {
482
+ ui.warn(`${label} support is still incomplete in ${targetDir}; an existing file may be a directory or symlink where a file is expected.`);
483
+ process.exit(1);
484
+ }
485
+ ui.info(`Run: egregore --runtime ${harness}`);
486
+ return;
487
+ } catch (err) {
488
+ ui.error(String(err.message || err));
489
+ process.exit(1);
490
+ }
491
+ }
492
+
424
493
  if (args.refreshConnect) {
425
494
  // Session-start maintenance mode: refresh the Connect overlay for an
426
495
  // existing instance. Local instances no-op instantly; network failure is
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+
3
+ // Add one harness (Codex, Pi, Prime Agent) to an existing instance.
4
+ //
5
+ // Setup overlays only the harness chosen at install time, so a Claude-only
6
+ // instance has no Codex files. The September 2026 launcher auto-repair fixed
7
+ // that by running the *latest* installer against *every* registered instance,
8
+ // which overwrote and deleted files in instances on another release channel.
9
+ // This module is the scoped replacement:
10
+ //
11
+ // - one harness, one instance, chosen explicitly;
12
+ // - copies only files that are missing from that harness's packaged bundle;
13
+ // - never overwrites, never deletes, never touches another harness;
14
+ // - records what it wrote in its own manifest so nothing else claims
15
+ // ownership of pre-existing files.
16
+
17
+ const fs = require("node:fs");
18
+ const path = require("node:path");
19
+
20
+ const PACKAGE = require("../package.json");
21
+
22
+ const HARNESSES = {
23
+ codex: { label: "Codex", module: "./codex-runtime", bundle: "codex" },
24
+ pi: { label: "Pi", module: "./pi-runtime", bundle: "pi" },
25
+ prime: { label: "Prime Agent", module: "./prime-runtime", bundle: "prime" },
26
+ };
27
+
28
+ function normalizeHarness(name) {
29
+ const value = String(name || "").trim().toLowerCase();
30
+ if (value === "codex") return "codex";
31
+ if (value === "pi") return "pi";
32
+ if (value === "prime" || value === "prime-agent") return "prime";
33
+ return null;
34
+ }
35
+
36
+ function harnessLabel(harness) {
37
+ return (HARNESSES[harness] && HARNESSES[harness].label) || harness;
38
+ }
39
+
40
+ function bundleDirFor(harness) {
41
+ return path.join(__dirname, "..", "runtime", HARNESSES[harness].bundle);
42
+ }
43
+
44
+ function listFiles(dir, prefix = "") {
45
+ if (!fs.existsSync(dir)) return [];
46
+ const result = [];
47
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
48
+ const rel = prefix ? path.join(prefix, entry.name) : entry.name;
49
+ const full = path.join(dir, entry.name);
50
+ if (entry.isDirectory()) {
51
+ result.push(...listFiles(full, rel));
52
+ } else if (entry.isFile()) {
53
+ result.push(rel);
54
+ }
55
+ }
56
+ return result.sort();
57
+ }
58
+
59
+ function pathExistsOrSymlink(file) {
60
+ try {
61
+ fs.lstatSync(file);
62
+ return true;
63
+ } catch {
64
+ return false;
65
+ }
66
+ }
67
+
68
+ function copyMissingFile(src, dest) {
69
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
70
+ fs.copyFileSync(src, dest);
71
+ try {
72
+ const mode = fs.statSync(src).mode & 0o777;
73
+ fs.chmodSync(dest, mode);
74
+ } catch {}
75
+ }
76
+
77
+ // Required paths come from the harness module so the CLI and the launcher's
78
+ // pre-launch check agree on what "support present" means.
79
+ function requiredPathsFor(harness) {
80
+ const mod = require(HARNESSES[harness].module);
81
+ const checker = mod.hasCodexRuntimeSupport || mod.hasPiRuntimeSupport || mod.hasPrimeRuntimeSupport;
82
+ return { hasSupport: (dir) => Boolean(checker && checker(dir)) };
83
+ }
84
+
85
+ function manifestPath(egregoreDir, harness) {
86
+ return path.join(egregoreDir, ".egregore", `${harness}-harness-add.json`);
87
+ }
88
+
89
+ // Current Codex discovers project skills via .agents/skills -> ../.codex/skills.
90
+ // Create the link only when nothing is there; never replace a real directory.
91
+ function ensureAgentsSkillsLink(egregoreDir, summary) {
92
+ const agentsDir = path.join(egregoreDir, ".agents");
93
+ const link = path.join(agentsDir, "skills");
94
+ if (pathExistsOrSymlink(link)) return;
95
+ try {
96
+ fs.mkdirSync(agentsDir, { recursive: true });
97
+ fs.symlinkSync(path.join("..", ".codex", "skills"), link);
98
+ summary.installed.push(".agents/skills");
99
+ } catch {}
100
+ }
101
+
102
+ /**
103
+ * Copy every file of `harness`'s packaged bundle that is missing from
104
+ * `egregoreDir`. Existing files are left untouched regardless of content.
105
+ *
106
+ * Returns { harness, installed: [rel...], present: n, ready: bool }.
107
+ */
108
+ function addHarnessRuntime(egregoreDir, harnessName) {
109
+ const harness = normalizeHarness(harnessName);
110
+ if (!harness) {
111
+ throw new Error(`Unknown harness "${harnessName}". Expected codex, pi, or prime.`);
112
+ }
113
+ const bundleDir = bundleDirFor(harness);
114
+ if (!fs.existsSync(bundleDir)) {
115
+ throw new Error(`This create-egregore release ships no ${harnessLabel(harness)} bundle (${bundleDir}).`);
116
+ }
117
+ if (!fs.existsSync(path.join(egregoreDir, "egregore.json"))) {
118
+ throw new Error(`Not an Egregore instance: ${egregoreDir} (no egregore.json).`);
119
+ }
120
+
121
+ const summary = { harness, installed: [], present: 0, ready: false };
122
+ for (const rel of listFiles(bundleDir)) {
123
+ if (rel === "manifest.json") continue;
124
+ const dest = path.join(egregoreDir, rel);
125
+ if (pathExistsOrSymlink(dest)) {
126
+ summary.present += 1;
127
+ continue;
128
+ }
129
+ copyMissingFile(path.join(bundleDir, rel), dest);
130
+ summary.installed.push(rel);
131
+ }
132
+ if (harness === "codex") ensureAgentsSkillsLink(egregoreDir, summary);
133
+
134
+ summary.ready = requiredPathsFor(harness).hasSupport(egregoreDir);
135
+
136
+ if (summary.installed.length > 0) {
137
+ const file = manifestPath(egregoreDir, harness);
138
+ const previous = (() => {
139
+ try { return JSON.parse(fs.readFileSync(file, "utf-8")); } catch { return { files: [] }; }
140
+ })();
141
+ const files = Array.from(new Set([...(previous.files || []), ...summary.installed])).sort();
142
+ fs.mkdirSync(path.dirname(file), { recursive: true });
143
+ fs.writeFileSync(file, JSON.stringify({
144
+ package: PACKAGE.name,
145
+ version: PACKAGE.version,
146
+ harness,
147
+ addedAt: new Date().toISOString(),
148
+ files,
149
+ }, null, 2) + "\n");
150
+ }
151
+ return summary;
152
+ }
153
+
154
+ module.exports = {
155
+ HARNESSES,
156
+ addHarnessRuntime,
157
+ harnessLabel,
158
+ normalizeHarness,
159
+ };
package/lib/setup.js CHANGED
@@ -1092,7 +1092,7 @@ function codexSupportMessage(egregoreDir) {
1092
1092
  " .codex/hooks.json",
1093
1093
  " .codex/hooks/",
1094
1094
  "",
1095
- "Restore this instance using its installed runtime version, then rerun egregore.",
1095
+ `Add it with: npx -y create-egregore@${require("../package.json").version} --add-harness codex (run inside the instance; missing files only), then rerun egregore.`,
1096
1096
  "Egregore workflows appear as slash commands. In Codex, invoke them with the matching $skill token or ask in natural language.",
1097
1097
  ].join("\n");
1098
1098
  }
@@ -1106,7 +1106,7 @@ function piSupportMessage(egregoreDir) {
1106
1106
  "Pi meta-harness support is missing from this Egregore checkout.",
1107
1107
  ` path: ${egregoreDir}`,
1108
1108
  "",
1109
- "Restore this instance using its installed runtime version, then rerun egregore.",
1109
+ `Add it with: npx -y create-egregore@${require("../package.json").version} --add-harness pi (run inside the instance; missing files only), then rerun egregore.`,
1110
1110
  "Pi loads Egregore through project-local .pi resources and the shared portable skill corpus.",
1111
1111
  ].join("\n");
1112
1112
  }
@@ -1116,7 +1116,7 @@ function primeSupportMessage(egregoreDir) {
1116
1116
  "Prime Agent runtime support is missing from this Egregore checkout.",
1117
1117
  ` path: ${egregoreDir}`,
1118
1118
  "",
1119
- "Restore this instance using its installed runtime version, then rerun egregore.",
1119
+ `Add it with: npx -y create-egregore@${require("../package.json").version} --add-harness prime (run inside the instance; missing files only), then rerun egregore.`,
1120
1120
  "Prime Agent loads Egregore through project-local .prime/agent resources and the shared portable skill corpus.",
1121
1121
  ].join("\n");
1122
1122
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-egregore",
3
- "version": "0.20.4",
3
+ "version": "0.20.5",
4
4
  "description": "Set up Egregore for your team in one command \u2014 installs the egregore launcher and your choice of Claude Code, Codex, or Pi.",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -13,7 +13,7 @@
13
13
  "build:runtime": "node ../../bin/capability-distribution.mjs generate-skill-view --root ../.. && node scripts/build-runtime-bundle.mjs && node scripts/build-pi-runtime-bundle.mjs && node scripts/build-prime-runtime-bundle.mjs && node ../../api/scripts/build-connect-runtime-pack.mjs",
14
14
  "build:pi-runtime": "node scripts/build-pi-runtime-bundle.mjs",
15
15
  "build:prime-runtime": "node scripts/build-prime-runtime-bundle.mjs",
16
- "test": "node ../../bin/capability-distribution.mjs validate --root ../.. && node scripts/build-runtime-bundle.mjs --check && node scripts/build-pi-runtime-bundle.mjs --check && node scripts/build-prime-runtime-bundle.mjs --check && node ../../bin/prime-render-spec.mjs --check && node ../../bin/pi-render-spec.mjs --check && node ../../api/scripts/build-connect-runtime-pack.mjs --check && node --test test/invite-install.test.js test/capability-distribution.test.js test/connect-runtime.test.js test/context.test.js test/path-lookup.test.js test/nixos-portability.test.js test/parse-join-target.test.js test/skip-repos.test.js test/install-origin-guard.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/prime-runtime.test.js test/prime-branch-policy.test.js test/prime-bridge-skill.test.js test/prime-session-smoke.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 test/launcher-connect.test.js test/installer-connect-choice.test.js test/launcher-connected-status.test.js test/launcher-permissions.test.js test/launcher-update.test.js test/connected-config.test.js test/workflow-settings.test.js",
16
+ "test": "node ../../bin/capability-distribution.mjs validate --root ../.. && node scripts/build-runtime-bundle.mjs --check && node scripts/build-pi-runtime-bundle.mjs --check && node scripts/build-prime-runtime-bundle.mjs --check && node ../../bin/prime-render-spec.mjs --check && node ../../bin/pi-render-spec.mjs --check && node ../../api/scripts/build-connect-runtime-pack.mjs --check && node --test test/invite-install.test.js test/capability-distribution.test.js test/connect-runtime.test.js test/context.test.js test/path-lookup.test.js test/nixos-portability.test.js test/parse-join-target.test.js test/skip-repos.test.js test/install-origin-guard.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/prime-runtime.test.js test/prime-branch-policy.test.js test/prime-bridge-skill.test.js test/prime-session-smoke.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 test/launcher-connect.test.js test/installer-connect-choice.test.js test/launcher-connected-status.test.js test/launcher-permissions.test.js test/launcher-update.test.js test/harness-add.test.js test/connected-config.test.js test/workflow-settings.test.js",
17
17
  "test:e2e": "node --test test/e2e-local.test.js",
18
18
  "test:all": "node scripts/build-runtime-bundle.mjs --check && node scripts/build-pi-runtime-bundle.mjs --check && node --test test/"
19
19
  },