create-egregore 0.19.20 → 0.19.21

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.19.20
2
+ // egregore-launcher-version: 0.19.21
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.19.20";
36
+ const VERSION = "0.19.21";
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");
@@ -629,7 +629,14 @@ async function renderCodexStartupCard(egregoreDir) {
629
629
  const result = await runCapturedWithSpinner("bash", [script], {
630
630
  cwd: egregoreDir,
631
631
  }, "Preparing Codex startup");
632
- if (result.stdout) process.stdout.write(result.stdout);
632
+ let output = result.stdout || "";
633
+ if (isConnectedPath(egregoreDir) && !output.includes("◆ CONNECTED")) {
634
+ const connectedLine = " ◆ CONNECTED · hosted graph active\n";
635
+ output = output.includes("\nWhat are you working on?")
636
+ ? output.replace("\nWhat are you working on?", `\n${connectedLine}\nWhat are you working on?`)
637
+ : `${output}${output.endsWith("\n") || !output ? "" : "\n"}${connectedLine}`;
638
+ }
639
+ if (output) process.stdout.write(output);
633
640
  if (result.status !== 0 && result.stderr) process.stderr.write(result.stderr);
634
641
  if (result.status === 0) return true;
635
642
  } catch {}
@@ -751,6 +758,7 @@ async function launch(inst, runtimeOverride) {
751
758
  if (!runtime) return;
752
759
  if (runtime === "claude-code") {
753
760
  normalizeClaudeDenyRules(inst.path);
761
+ ensureClaudeModeStatusLine(inst.path);
754
762
  }
755
763
  const binary = runtimeBinary(runtime);
756
764
  if (!commandExists(binary)) {
@@ -816,13 +824,17 @@ function cmdLs() {
816
824
  const slugW = Math.min(24, Math.max(...registry.map((r) => (r.slug || "").length)));
817
825
  const nameW = Math.min(24, Math.max(...registry.map((r) => (r.name || "").length)));
818
826
  const runtimeW = Math.max(9, ...registry.map((r) => runtimeShort(runtimeFor(r)).length));
827
+ const modeW = "◆ CONNECTED".length;
819
828
  console.log(`\n ${BOLD}${registry.length}${RESET} egregore${registry.length === 1 ? "" : "s"} registered\n`);
820
- console.log(` ${DIM}${pad("SLUG", slugW)} ${pad("NAME", nameW)} ${pad("ASSISTANT", runtimeW)} PATH${RESET}`);
829
+ console.log(` ${DIM}${pad("SLUG", slugW)} ${pad("NAME", nameW)} ${pad("MODE", modeW)} ${pad("ASSISTANT", runtimeW)} PATH${RESET}`);
821
830
  for (const inst of registry) {
822
831
  const marker = inst.slug === def ? `${GREEN}●${RESET}` : " ";
823
832
  const slug = inst.slug === def ? `${BOLD}${pad(inst.slug, slugW)}${RESET}` : pad(inst.slug, slugW);
824
833
  const runtime = pad(runtimeShort(runtimeFor(inst)), runtimeW);
825
- console.log(` ${marker} ${slug} ${pad(inst.name || "", nameW)} ${runtime} ${DIM}${inst.path}${RESET}`);
834
+ const mode = isConnectedInstance(inst)
835
+ ? `${CYAN}${pad("◆ CONNECTED", modeW)}${RESET}`
836
+ : `${DIM}${pad("◇ LOCAL", modeW)}${RESET}`;
837
+ console.log(` ${marker} ${slug} ${pad(inst.name || "", nameW)} ${mode} ${runtime} ${DIM}${inst.path}${RESET}`);
826
838
  }
827
839
  if (def) console.log(`\n ${GREEN}●${RESET} = default marker (used by non-interactive ${BOLD}egregore${RESET})\n`);
828
840
  else console.log("");
@@ -936,6 +948,22 @@ function readJsonFile(file) {
936
948
  }
937
949
  }
938
950
 
951
+ function isConnectedConfig(config) {
952
+ return (
953
+ config?.mode === "connected" &&
954
+ typeof config.api_url === "string" &&
955
+ config.api_url.trim().length > 0
956
+ );
957
+ }
958
+
959
+ function isConnectedPath(instancePath) {
960
+ return isConnectedConfig(readJsonFile(path.join(instancePath, "egregore.json")));
961
+ }
962
+
963
+ function isConnectedInstance(inst) {
964
+ return !!inst?.path && isConnectedPath(inst.path);
965
+ }
966
+
939
967
  function readEnvValues(file) {
940
968
  const values = {};
941
969
  if (!fs.existsSync(file)) return values;
@@ -1233,6 +1261,47 @@ function normalizeClaudeDenyRules(instancePath) {
1233
1261
  }
1234
1262
  }
1235
1263
 
1264
+ const CLAUDE_MODE_STATUS_MARKER = "# egregore-connected-status-v1";
1265
+ const CLAUDE_DEFAULT_STATUS_COMMAND = "\"${CLAUDE_PROJECT_DIR}/bin/statusline.sh\"";
1266
+
1267
+ function claudeModeStatusCommand(baseCommand) {
1268
+ return [
1269
+ CLAUDE_MODE_STATUS_MARKER,
1270
+ "MODE=$(jq -r 'if (.mode == \"connected\" and ((.api_url // \"\") | length > 0)) then \"connected\" else \"local\" end' \"${CLAUDE_PROJECT_DIR}/egregore.json\" 2>/dev/null)",
1271
+ `BASE=$( ( ${baseCommand} ) 2>/dev/null || true)`,
1272
+ "if [ \"$MODE\" = \"connected\" ]; then",
1273
+ " case \"$BASE\" in",
1274
+ " *\"◆ CONNECTED\"*) printf '%s\\n' \"$BASE\" ;;",
1275
+ " \"\") printf '◆ CONNECTED\\n' ;;",
1276
+ " *) printf '◆ CONNECTED · %s\\n' \"$BASE\" ;;",
1277
+ " esac",
1278
+ "else",
1279
+ " printf '%s\\n' \"$BASE\"",
1280
+ "fi",
1281
+ ].join("\n");
1282
+ }
1283
+
1284
+ function ensureClaudeModeStatusLine(instancePath) {
1285
+ if (!isConnectedPath(instancePath)) return false;
1286
+ const file = path.join(instancePath, ".claude", "settings.local.json");
1287
+ const settings = readJsonFile(file);
1288
+ const current = settings?.statusLine;
1289
+ if (current?.type === "command" && current?.command?.includes(CLAUDE_MODE_STATUS_MARKER)) {
1290
+ return false;
1291
+ }
1292
+ const baseCommand = current?.type === "command" && current?.command?.trim()
1293
+ ? current.command
1294
+ : CLAUDE_DEFAULT_STATUS_COMMAND;
1295
+ settings.statusLine = {
1296
+ ...(current || {}),
1297
+ type: "command",
1298
+ command: claudeModeStatusCommand(baseCommand),
1299
+ };
1300
+ fs.mkdirSync(path.dirname(file), { recursive: true });
1301
+ writeJsonFileAtomic(file, settings);
1302
+ return true;
1303
+ }
1304
+
1236
1305
  function writeEnvValueAtomic(file, key, value) {
1237
1306
  const lines = fs.existsSync(file)
1238
1307
  ? fs.readFileSync(file, "utf-8").split(/\r?\n/)
@@ -1323,6 +1392,8 @@ async function cmdConnect(args) {
1323
1392
  }
1324
1393
  const activation = state.connected_activation || {};
1325
1394
  if (cfg.mode === "connected" && activation.status === "connected") {
1395
+ normalizeClaudeDenyRules(inst.path);
1396
+ ensureClaudeModeStatusLine(inst.path);
1326
1397
  console.log(`\n ${GREEN}✓${RESET} ${BOLD}${inst.slug}${RESET} is already connected.\n`);
1327
1398
  return;
1328
1399
  }
@@ -1462,6 +1533,7 @@ async function cmdConnect(args) {
1462
1533
  });
1463
1534
  writeEnvValueAtomic(envFile, "EGREGORE_API_KEY", claim.data.api_key);
1464
1535
  normalizeClaudeDenyRules(inst.path);
1536
+ ensureClaudeModeStatusLine(inst.path);
1465
1537
  persistConnectState(inst, {
1466
1538
  status: "backfilling",
1467
1539
  activation_protocol: claim.data.activation_protocol,
@@ -1938,7 +2010,8 @@ function loadPreview(inst) {
1938
2010
  data.telegram = !!(cfg.telegram_chat_id || cfg.telegram_group_link);
1939
2011
  data.org = cfg.org_name;
1940
2012
  data.githubOrg = cfg.github_org;
1941
- data.mode = cfg.mode;
2013
+ data.connected = isConnectedConfig(cfg);
2014
+ data.mode = data.connected ? "connected" : "local";
1942
2015
  data.flowVariant = cfg.flow_variant;
1943
2016
  }
1944
2017
  } catch {}
@@ -2011,7 +2084,8 @@ function renderHeroPanel({ selected, pv, width, height, countSuffix, filter, fil
2011
2084
  }
2012
2085
  lines.push(emptyBoxLine(innerWidth, margin));
2013
2086
  lines.push(updateBand || mid);
2014
- lines.push(boxLine(`${BOLD}${title}${RESET} ${DIM}· ${org} · ${runtime} · ${countSuffix} · ${filterText}${RESET}`, innerWidth, margin));
2087
+ const connectedHero = pv?.connected ? ` · ${CYAN}◆ CONNECTED${RESET}` : "";
2088
+ lines.push(boxLine(`${BOLD}${title}${RESET} ${DIM}· ${org} · ${runtime}${RESET}${connectedHero}${DIM} · ${countSuffix} · ${filterText}${RESET}`, innerWidth, margin));
2015
2089
  lines.push(boxLine(`${DIM}skills${RESET} ${skillStatus}`, innerWidth, margin));
2016
2090
  lines.push(emptyBoxLine(innerWidth, margin));
2017
2091
 
@@ -2065,6 +2139,13 @@ function renderRuntimePanel({ inst, cursor, width, height }) {
2065
2139
  emptyBoxLine(innerWidth, margin),
2066
2140
  boxLine(`${BOLD}${CYAN}Launch ${title}${RESET}`, innerWidth, margin),
2067
2141
  boxLine(`${DIM}${inst.path}${RESET}`, innerWidth, margin),
2142
+ boxLine(
2143
+ isConnectedInstance(inst)
2144
+ ? `${CYAN}◆ CONNECTED${RESET} ${DIM}· hosted graph active${RESET}`
2145
+ : `${DIM}◇ LOCAL · Markdown + Git${RESET}`,
2146
+ innerWidth,
2147
+ margin,
2148
+ ),
2068
2149
  emptyBoxLine(innerWidth, margin),
2069
2150
  ];
2070
2151
 
@@ -2381,10 +2462,13 @@ async function browser(args = {}) {
2381
2462
  ? `${DIM}${pad(e.name || "", nameW)}${RESET}`
2382
2463
  : pad(e.name || "", nameW);
2383
2464
  const pathAvail = Math.max(0, listAvail - slugW - nameW - 10);
2465
+ const connected = isConnectedInstance(e);
2466
+ const modeBadge = connected ? `${CYAN}◆ CONNECTED${RESET}` : "";
2467
+ const adjustedPathAvail = Math.max(0, pathAvail - (connected ? "◆ CONNECTED ".length : 0));
2384
2468
  const pathDim = isMissing
2385
- ? `${RED}✗${RESET} ${DIM}${truncate(e.path, Math.max(8, pathAvail - 12))}${RESET} ${RED}(missing)${RESET}`
2386
- : `${DIM}${truncate(e.path, pathAvail)}${RESET}`;
2387
- const line = ` ${cursorMark} ${defMark} ${slug} ${name} ${pathDim}`;
2469
+ ? `${RED}✗${RESET} ${DIM}${truncate(e.path, Math.max(8, adjustedPathAvail - 12))}${RESET} ${RED}(missing)${RESET}`
2470
+ : `${DIM}${truncate(e.path, adjustedPathAvail)}${RESET}`;
2471
+ const line = ` ${cursorMark} ${defMark} ${slug} ${name} ${modeBadge}${modeBadge ? " " : ""}${pathDim}`;
2388
2472
  out.push(M + " ".repeat(listPad - 3) + line + ANSI.clearLine + "\n");
2389
2473
  }
2390
2474
 
@@ -2411,6 +2495,9 @@ async function browser(args = {}) {
2411
2495
  ? ` ${label("path")} ${RED}✗${RESET} ${truncate(selected.path, Math.max(8, valW - 12))} ${RED}(missing)${RESET}`
2412
2496
  : ` ${label("path")} ${truncate(selected.path, valW)}`);
2413
2497
  if (pv.org) previewLines.push(` ${label("org")} ${truncate(`${pv.org} ${DIM}(${pv.githubOrg || "?"})${RESET}`, valW)}`);
2498
+ previewLines.push(pv.connected
2499
+ ? ` ${label("mode")} ${CYAN}◆ CONNECTED${RESET} ${DIM}· hosted graph active${RESET}`
2500
+ : ` ${label("mode")} ${DIM}◇ LOCAL · Markdown + Git${RESET}`);
2414
2501
  if (pv.branch) previewLines.push(` ${label("branch")} ${BOLD}${pv.branch}${RESET}`);
2415
2502
  if (pv.lastCommit) previewLines.push(` ${label("last")} ${DIM}${truncate(pv.lastCommit, valW)}${RESET}`);
2416
2503
  if (pv.repos && pv.repos.length) {
@@ -2424,7 +2511,6 @@ async function browser(args = {}) {
2424
2511
  }
2425
2512
  const modeBits = [];
2426
2513
  modeBits.push(runtimeShort(runtimeFor(selected, args.runtimeOverride)));
2427
- if (pv.mode) modeBits.push(pv.mode);
2428
2514
  if (pv.flowVariant && pv.flowVariant !== "legacy") modeBits.push(pv.flowVariant);
2429
2515
  if (pv.telegram) modeBits.push("telegram");
2430
2516
  if (selected.slug === def) modeBits.push("default");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-egregore",
3
- "version": "0.19.20",
3
+ "version": "0.19.21",
4
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": {
@@ -12,7 +12,7 @@
12
12
  "scripts": {
13
13
  "build:runtime": "node scripts/build-runtime-bundle.mjs && node scripts/build-pi-runtime-bundle.mjs",
14
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 test/launcher-connect.test.js test/launcher-permissions.test.js test/launcher-update.test.js test/connected-config.test.js",
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 test/launcher-connect.test.js test/launcher-connected-status.test.js test/launcher-permissions.test.js test/launcher-update.test.js test/connected-config.test.js",
16
16
  "test:e2e": "node --test test/e2e-local.test.js",
17
17
  "test:all": "node scripts/build-runtime-bundle.mjs --check && node scripts/build-pi-runtime-bundle.mjs --check && node --test test/"
18
18
  },