create-egregore 0.18.3 → 0.18.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.5.17
2
+ // egregore-launcher-version: 0.5.19
3
3
  //
4
4
  // egregore — launch and browse Egregore instances from your terminal.
5
5
  // Installed by create-egregore. Reads ~/.egregore/instances.json.
@@ -30,7 +30,7 @@ const https = require("node:https");
30
30
  const { spawnSync, spawn } = require("node:child_process");
31
31
  const readline = require("node:readline");
32
32
 
33
- const VERSION = "0.5.17";
33
+ const VERSION = "0.5.19";
34
34
  const REGISTRY = path.join(os.homedir(), ".egregore", "instances.json");
35
35
  const DEFAULT_FILE = path.join(os.homedir(), ".egregore", "default");
36
36
  const PUBLIC_NPX_PACKAGE = "create-egregore";
@@ -99,9 +99,57 @@ const ANSI = {
99
99
  clear: "\x1b[2J",
100
100
  home: "\x1b[H",
101
101
  clearLine: "\x1b[K",
102
+ // Disable every terminal mouse-reporting mode. Some terminals (tmux,
103
+ // iTerm2) or a TUI run earlier in the session leave mouse reporting ON,
104
+ // which turns the scroll wheel into escape sequences that corrupt our
105
+ // pickers. Emitting this on picker entry makes the wheel do native
106
+ // scrollback instead. Modes: 1000 (click), 1002 (drag), 1003 (any-motion),
107
+ // 1006 (SGR encoding), 1015 (urxvt encoding).
108
+ mouseOff: "\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1006l\x1b[?1015l",
102
109
  moveTo: (row, col) => `\x1b[${row};${col}H`,
103
110
  };
104
111
 
112
+ // Scroll-wheel / mouse-event filter (defense-in-depth for ANSI.mouseOff).
113
+ // With mouse reporting on, the scroll wheel and shift+arrow arrive as escape
114
+ // sequences. Node's readline decomposes them into stray keypresses:
115
+ // • SGR mouse "\x1b[<b;x;yM" → an event with key.code "[<", then each
116
+ // coordinate DIGIT as its own keypress (a "1"/"2" hits the picker's
117
+ // number quick-select and LAUNCHES a runtime), then a final "M".
118
+ // • X11 mouse "\x1b[M" + 3 raw bytes → key.code "[M" then 3 stray chars.
119
+ // • shift+arrow "\x1b[1;2A" / "\x1b[1;2B" → key.name "up"/"down" with
120
+ // key.shift set, which would advance the selection.
121
+ // This factory returns a stateful predicate: pass it every (chunk, key) at the
122
+ // top of a keypress handler and early-return when it reports true, so no
123
+ // mouse-derived byte is ever treated as navigation.
124
+ function makeMouseFilter() {
125
+ let sgr = false; // inside an SGR mouse report (ESC [ < ... M|m)
126
+ let x11 = 0; // raw bytes left to swallow after ESC [ M
127
+ return function isMouseEvent(chunk, key) {
128
+ const seq = (key && key.sequence) || (typeof chunk === "string" ? chunk : "") || "";
129
+ if (sgr) {
130
+ if (/[Mm]/.test(seq)) sgr = false; // SGR terminates on M (press) / m (release)
131
+ return true;
132
+ }
133
+ if (x11 > 0) {
134
+ x11 -= seq.length || 1;
135
+ return true;
136
+ }
137
+ if ((key && key.code === "[<") || seq.startsWith("\x1b[<")) {
138
+ sgr = !/[Mm]$/.test(seq); // single-chunk variant already includes terminator
139
+ return true;
140
+ }
141
+ if ((key && key.code === "[M") || seq.startsWith("\x1b[M")) {
142
+ const extra = Math.max(0, seq.length - 3); // bytes past "ESC [ M" in this chunk
143
+ x11 = Math.max(0, 3 - extra);
144
+ return true;
145
+ }
146
+ if (key && key.shift && (key.name === "up" || key.name === "down")) {
147
+ return true; // shift+arrow scroll fallback
148
+ }
149
+ return false;
150
+ };
151
+ }
152
+
105
153
  const stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*m/g, "");
106
154
  const visLen = (s) => stripAnsi(s).length;
107
155
  const pad = (s, n) => s + " ".repeat(Math.max(0, n - visLen(s)));
@@ -363,6 +411,7 @@ function pickRuntimeInteractive(inst, runtimes, stored) {
363
411
  let done = false;
364
412
  const title = inst.slug || inst.name || path.basename(inst.path);
365
413
  const previousRaw = !!process.stdin.isRaw;
414
+ const isMouseEvent = makeMouseFilter();
366
415
 
367
416
  const erase = () => {
368
417
  if (!rendered) return;
@@ -378,6 +427,7 @@ function pickRuntimeInteractive(inst, runtimes, stored) {
378
427
  if (done) return;
379
428
  done = true;
380
429
  process.stdin.removeListener("keypress", onKey);
430
+ process.stdout.write(ANSI.mouseOff);
381
431
  if (process.stdin.setRawMode) process.stdin.setRawMode(previousRaw);
382
432
  if (!previousRaw) process.stdin.pause();
383
433
  erase();
@@ -399,6 +449,7 @@ function pickRuntimeInteractive(inst, runtimes, stored) {
399
449
  };
400
450
 
401
451
  function onKey(chunk, key = {}) {
452
+ if (isMouseEvent(chunk, key)) return; // drop scroll-wheel / mouse escape sequences
402
453
  if (key.ctrl && key.name === "c") {
403
454
  cleanup();
404
455
  process.exit(130);
@@ -424,6 +475,7 @@ function pickRuntimeInteractive(inst, runtimes, stored) {
424
475
  readline.emitKeypressEvents(process.stdin);
425
476
  if (process.stdin.setRawMode) process.stdin.setRawMode(true);
426
477
  process.stdin.resume();
478
+ process.stdout.write(ANSI.mouseOff);
427
479
  process.stdin.on("keypress", onKey);
428
480
  render();
429
481
  });
@@ -1338,6 +1390,10 @@ function renderRuntimePanel({ inst, cursor, width, height }) {
1338
1390
  const desc = runtimeChoiceDescription(runtime, inst, stored);
1339
1391
  lines.push(boxLine(`${marker} ${pad(labelText, 16)} ${DIM}${desc}${RESET}`, innerWidth, margin));
1340
1392
  }
1393
+ // Coming-soon harnesses — shown for visibility, not selectable yet.
1394
+ for (const soon of ["Hermes", "Pi"]) {
1395
+ lines.push(boxLine(`${DIM}· ${pad(soon, 16)} coming soon${RESET}`, innerWidth, margin));
1396
+ }
1341
1397
 
1342
1398
  lines.push(emptyBoxLine(innerWidth, margin));
1343
1399
  lines.push(boxLine(`${DIM}↑↓ navigate · enter select · 1-${runtimes.length} quick select · esc back · q quit${RESET}`, innerWidth, margin));
@@ -1582,7 +1638,8 @@ async function browser(args = {}) {
1582
1638
  };
1583
1639
 
1584
1640
  // ── setup / teardown ───────────────────
1585
- process.stdout.write(ANSI.altScreenEnter + ANSI.cursorHide + ANSI.clear + ANSI.home);
1641
+ process.stdout.write(ANSI.altScreenEnter + ANSI.mouseOff + ANSI.cursorHide + ANSI.clear + ANSI.home);
1642
+ const isMouseEvent = makeMouseFilter();
1586
1643
  let done = false;
1587
1644
  const cleanup = () => {
1588
1645
  if (done) return;
@@ -1590,7 +1647,7 @@ async function browser(args = {}) {
1590
1647
  process.stdin.setRawMode(false);
1591
1648
  process.stdin.pause();
1592
1649
  process.stdin.removeAllListeners("keypress");
1593
- process.stdout.write(ANSI.cursorShow + ANSI.altScreenLeave);
1650
+ process.stdout.write(ANSI.mouseOff + ANSI.cursorShow + ANSI.altScreenLeave);
1594
1651
  };
1595
1652
  process.on("exit", cleanup);
1596
1653
  process.on("SIGINT", () => { cleanup(); process.exit(130); });
@@ -1614,6 +1671,7 @@ async function browser(args = {}) {
1614
1671
  return new Promise((resolve) => {
1615
1672
  process.stdin.on("keypress", (chunk, key) => {
1616
1673
  if (!key) return;
1674
+ if (isMouseEvent(chunk, key)) return; // drop scroll-wheel / mouse escape sequences
1617
1675
 
1618
1676
  if (state.runtimeInst) {
1619
1677
  const runtimes = runtimeChoicesFor(state.runtimeInst, args.runtimeOverride);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-egregore",
3
- "version": "0.18.3",
3
+ "version": "0.18.5",
4
4
  "description": "Set up Egregore for your team in one command — installs the egregore launcher and your choice of Claude Code or Codex.",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -11,7 +11,7 @@
11
11
  },
12
12
  "scripts": {
13
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",
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",
15
15
  "test:e2e": "node --test test/e2e-local.test.js",
16
16
  "test:all": "node scripts/build-runtime-bundle.mjs --check && node --test test/"
17
17
  },
@@ -154,6 +154,26 @@ wait $API_PID $GIT_PID $LOCAL_PID 2>/dev/null || true
154
154
  # --- Read results ---
155
155
  PRS=$(cat "$TMPDIR/prs.json" 2>/dev/null || echo "[]")
156
156
  echo "$PRS" | jq . >/dev/null 2>&1 || PRS="[]"
157
+
158
+ # Resolve PR author GitHub logins -> egregore display names. `gh pr list` returns
159
+ # the raw github login (e.g. "fcdagdelen"); without this it leaks into activity
160
+ # instead of the person's display name ("cem"). Map via the graph when connected;
161
+ # fall back to the github profile name, then the login. Author becomes a string.
162
+ if [ -n "$PRS" ] && [ "$PRS" != "[]" ]; then
163
+ AUTHOR_MAP='{}'
164
+ if [ -n "$API_URL" ] && [ -n "$API_KEY" ]; then
165
+ MAP_RAW=$(bash "$SCRIPT_DIR/bin/graph.sh" query \
166
+ "MATCH (p:Person) WHERE p.github IS NOT NULL RETURN p.github AS gh, p.name AS name" 2>/dev/null)
167
+ AUTHOR_MAP=$(printf '%s' "$MAP_RAW" | jq -c \
168
+ 'reduce (.values[]?) as $v ({}; .[($v[0]|ascii_downcase)] = $v[1])' 2>/dev/null || echo '{}')
169
+ fi
170
+ PRS=$(printf '%s' "$PRS" | jq -c --argjson map "$AUTHOR_MAP" '
171
+ map(
172
+ (if (.author|type)=="object" then .author else {login: (.author|tostring), name: null} end) as $a
173
+ | .author = ([$map[(($a.login // "") | ascii_downcase)], $a.name, $a.login]
174
+ | map(select(. != null and . != "")) | first // "unknown")
175
+ )' 2>/dev/null || printf '%s' "$PRS")
176
+ fi
157
177
  DISK_HANDOFFS=$(cat "$TMPDIR/disk_handoffs.txt" 2>/dev/null || echo "")
158
178
  DISK_DECISIONS=$(cat "$TMPDIR/disk_decisions.txt" 2>/dev/null || echo "")
159
179
  LOCAL_SESSIONS=$(cat "$TMPDIR/local_sessions.json" 2>/dev/null || echo '{"my_sessions":[],"team_sessions":[]}')
@@ -134,7 +134,7 @@ DASHBOARD_URL=$(echo "$_DB_PUBLISH_OUT" | grep -o 'https://[^ ]*' | tail -1)
134
134
  if [ -z "$DASHBOARD_URL" ]; then
135
135
  _DB_HTML="/tmp/egregore-artifacts/dashboard-${EGREGORE_SESSION_ID}.html"
136
136
  mkdir -p /tmp/egregore-artifacts
137
- if npx egregore-artifacts document "$_DB_MD" --output "$_DB_HTML" --no-open >/dev/null 2>&1 && [ -f "$_DB_HTML" ]; then
137
+ if npx egregore-artifacts@latest document "$_DB_MD" --output "$_DB_HTML" --no-open >/dev/null 2>&1 && [ -f "$_DB_HTML" ]; then
138
138
  DASHBOARD_URL="file://${_DB_HTML}"
139
139
  fi
140
140
  fi
@@ -109,7 +109,7 @@ else
109
109
  exit 0
110
110
  fi
111
111
  else
112
- if ! npx egregore-artifacts "${RENDER_ARGS[@]}" >/dev/null 2>&1; then
112
+ if ! npx egregore-artifacts@latest "${RENDER_ARGS[@]}" >/dev/null 2>&1; then
113
113
  exit 0
114
114
  fi
115
115
  fi