create-egregore 0.18.8 → 0.18.9
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.
|
|
2
|
+
// egregore-launcher-version: 0.5.24
|
|
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.
|
|
33
|
+
const VERSION = "0.5.24";
|
|
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";
|
|
@@ -1429,7 +1429,9 @@ function renderRuntimePanel({ inst, cursor, width, height }) {
|
|
|
1429
1429
|
const runtimes = runtimeChoicesFor(inst);
|
|
1430
1430
|
const stored = storedRuntimeFor(inst);
|
|
1431
1431
|
const title = inst.slug || inst.name || path.basename(inst.path);
|
|
1432
|
-
|
|
1432
|
+
// Same cap/margin as renderHeroPanel so the centered box keeps its position
|
|
1433
|
+
// when you move from the instance list to the assistant picker.
|
|
1434
|
+
const outerWidth = Math.min(Math.max(50, width - 2), 126);
|
|
1433
1435
|
const innerWidth = Math.max(48, outerWidth - 2);
|
|
1434
1436
|
const margin = " ".repeat(Math.max(0, Math.floor((width - outerWidth) / 2)));
|
|
1435
1437
|
const label = " assistant ";
|
|
@@ -1521,9 +1523,17 @@ async function browser(args = {}) {
|
|
|
1521
1523
|
|
|
1522
1524
|
// ── render ────────────────────────────
|
|
1523
1525
|
const render = () => {
|
|
1526
|
+
if (done) return; // the parent stays alive under the spawned assistant — never redraw after teardown
|
|
1524
1527
|
const w = process.stdout.columns || 80;
|
|
1525
1528
|
const h = process.stdout.rows || 24;
|
|
1526
1529
|
|
|
1530
|
+
// One centered block: the hero/runtime box, the list, dividers, preview and
|
|
1531
|
+
// footer all share this gutter and width so the whole UI centers together
|
|
1532
|
+
// (matches renderHeroPanel's outerWidth/margin math).
|
|
1533
|
+
const blockWidth = Math.min(Math.max(50, w - 2), 126);
|
|
1534
|
+
const gutter = Math.max(0, Math.floor((w - blockWidth) / 2));
|
|
1535
|
+
const M = " ".repeat(gutter);
|
|
1536
|
+
|
|
1527
1537
|
const out = [];
|
|
1528
1538
|
// Home AND clear-to-end-of-screen each frame. The per-line ANSI.clearLine
|
|
1529
1539
|
// calls below are belt-and-suspenders; some terminals (notably Warp) don't
|
|
@@ -1575,9 +1585,9 @@ async function browser(args = {}) {
|
|
|
1575
1585
|
const listRows = Math.max(4, Math.floor(availableRows * 0.48));
|
|
1576
1586
|
const previewRows = Math.max(3, availableRows - listRows - 2); // -2 for dividers
|
|
1577
1587
|
|
|
1578
|
-
// List
|
|
1588
|
+
// List — kept within the centered block so long paths don't spill past it.
|
|
1579
1589
|
const listPad = 4;
|
|
1580
|
-
const listAvail =
|
|
1590
|
+
const listAvail = blockWidth - 4;
|
|
1581
1591
|
const slugW = Math.min(22, Math.max(6, ...registry.map((r) => (r.slug || "").length)));
|
|
1582
1592
|
const nameW = Math.min(24, Math.max(6, ...registry.map((r) => (r.name || "").length)));
|
|
1583
1593
|
|
|
@@ -1594,7 +1604,7 @@ async function browser(args = {}) {
|
|
|
1594
1604
|
if (idx >= v.length) {
|
|
1595
1605
|
if (v.length === 0 && i === Math.floor(listRows / 2)) {
|
|
1596
1606
|
const msg = state.filter ? "(no matches)" : "(empty)";
|
|
1597
|
-
out.push(" ".repeat(listPad) + DIM + msg + RESET + ANSI.clearLine + "\n");
|
|
1607
|
+
out.push(M + " ".repeat(listPad) + DIM + msg + RESET + ANSI.clearLine + "\n");
|
|
1598
1608
|
} else {
|
|
1599
1609
|
out.push(ANSI.clearLine + "\n");
|
|
1600
1610
|
}
|
|
@@ -1608,7 +1618,7 @@ async function browser(args = {}) {
|
|
|
1608
1618
|
const label = isCursor ? `${BOLD}${item.label}${RESET}` : item.label;
|
|
1609
1619
|
const desc = `${DIM}${item.description}${RESET}`;
|
|
1610
1620
|
const line = ` ${cursorMark} ${pad(plus, slugW)} ${pad(label, nameW)} ${truncate(desc, Math.max(0, listAvail - slugW - nameW - 10))}`;
|
|
1611
|
-
out.push(" ".repeat(listPad - 3) + line + ANSI.clearLine + "\n");
|
|
1621
|
+
out.push(M + " ".repeat(listPad - 3) + line + ANSI.clearLine + "\n");
|
|
1612
1622
|
continue;
|
|
1613
1623
|
}
|
|
1614
1624
|
const { e, origIdx } = item;
|
|
@@ -1623,11 +1633,11 @@ async function browser(args = {}) {
|
|
|
1623
1633
|
const name = pad(e.name || "", nameW);
|
|
1624
1634
|
const pathDim = `${DIM}${truncate(e.path, Math.max(0, listAvail - slugW - nameW - 10))}${RESET}`;
|
|
1625
1635
|
const line = ` ${cursorMark} ${defMark} ${slug} ${name} ${pathDim}`;
|
|
1626
|
-
out.push(" ".repeat(listPad - 3) + line + ANSI.clearLine + "\n");
|
|
1636
|
+
out.push(M + " ".repeat(listPad - 3) + line + ANSI.clearLine + "\n");
|
|
1627
1637
|
}
|
|
1628
1638
|
|
|
1629
1639
|
// Divider
|
|
1630
|
-
out.push(DIM +
|
|
1640
|
+
out.push(DIM + M + "─".repeat(blockWidth) + RESET + ANSI.clearLine + "\n");
|
|
1631
1641
|
|
|
1632
1642
|
// Preview
|
|
1633
1643
|
const previewLines = [];
|
|
@@ -1641,15 +1651,20 @@ async function browser(args = {}) {
|
|
|
1641
1651
|
previewLines.push(`${DIM} (no selection)${RESET}`);
|
|
1642
1652
|
} else {
|
|
1643
1653
|
const label = (k) => `${DIM}${pad(k, 10)}${RESET}`;
|
|
1644
|
-
|
|
1645
|
-
|
|
1654
|
+
// A preview line is " " + label(10) + " " + value = 13 + value; bound the
|
|
1655
|
+
// value to blockWidth-15 so every line's right edge lands two cols inside
|
|
1656
|
+
// the box border, level with the box inner content (gutter+blockWidth-2).
|
|
1657
|
+
const valW = Math.max(16, blockWidth - 15);
|
|
1658
|
+
previewLines.push(` ${label("path")} ${truncate(selected.path, valW)}`);
|
|
1659
|
+
if (pv.org) previewLines.push(` ${label("org")} ${truncate(`${pv.org} ${DIM}(${pv.githubOrg || "?"})${RESET}`, valW)}`);
|
|
1646
1660
|
if (pv.branch) previewLines.push(` ${label("branch")} ${BOLD}${pv.branch}${RESET}`);
|
|
1647
|
-
if (pv.lastCommit) previewLines.push(` ${label("last")} ${DIM}${truncate(pv.lastCommit,
|
|
1661
|
+
if (pv.lastCommit) previewLines.push(` ${label("last")} ${DIM}${truncate(pv.lastCommit, valW)}${RESET}`);
|
|
1648
1662
|
if (pv.repos && pv.repos.length) {
|
|
1649
|
-
|
|
1663
|
+
const reposText = `${pv.repos.slice(0, 4).join(", ")}${pv.repos.length > 4 ? ` (+${pv.repos.length - 4} more)` : ""}`;
|
|
1664
|
+
previewLines.push(` ${label("repos")} ${truncate(reposText, valW)}`);
|
|
1650
1665
|
}
|
|
1651
1666
|
if (pv.skills) {
|
|
1652
|
-
const skillLine = selectedWorkflowLine(pv, Math.max(16,
|
|
1667
|
+
const skillLine = selectedWorkflowLine(pv, Math.max(16, blockWidth - 20));
|
|
1653
1668
|
previewLines.push(` ${label("invoke")} ${skillLine}`);
|
|
1654
1669
|
previewLines.push(` ${label("skills")} Codex ${BOLD}${pv.skills.native.length}${RESET} native, ${BOLD}${pv.skills.adapterCount}${RESET} adapters; Claude ${BOLD}${pv.skills.claudeCount || 0}${RESET}`);
|
|
1655
1670
|
}
|
|
@@ -1663,11 +1678,11 @@ async function browser(args = {}) {
|
|
|
1663
1678
|
}
|
|
1664
1679
|
for (let i = 0; i < previewRows; i++) {
|
|
1665
1680
|
const line = previewLines[i] || "";
|
|
1666
|
-
out.push(line + ANSI.clearLine + "\n");
|
|
1681
|
+
out.push((line ? M + line : "") + ANSI.clearLine + "\n");
|
|
1667
1682
|
}
|
|
1668
1683
|
|
|
1669
1684
|
// Divider
|
|
1670
|
-
out.push(DIM +
|
|
1685
|
+
out.push(DIM + M + "─".repeat(blockWidth) + RESET + ANSI.clearLine + "\n");
|
|
1671
1686
|
|
|
1672
1687
|
// Footer
|
|
1673
1688
|
let footer;
|
|
@@ -1688,13 +1703,13 @@ async function browser(args = {}) {
|
|
|
1688
1703
|
const kindColor = state.messageKind === "success" ? GREEN : state.messageKind === "warn" ? YELLOW : CYAN;
|
|
1689
1704
|
footer = ` ${kindColor}●${RESET} ${state.message}${ANSI.clearLine}`;
|
|
1690
1705
|
} else {
|
|
1691
|
-
footer =
|
|
1706
|
+
footer = blockWidth < 96
|
|
1692
1707
|
? ` ${DIM}↑↓${RESET} nav ${DIM}enter${RESET} select ${DIM}n/+${RESET} new ${DIM}i${RESET} invite ${DIM}?${RESET} help ${DIM}q${RESET} quit${ANSI.clearLine}`
|
|
1693
1708
|
: ` ${DIM}↑↓${RESET} nav ${DIM}enter${RESET} select ${DIM}n/+${RESET} new ${DIM}/${RESET} filter ${DIM}i${RESET} invite ${DIM}u${RESET} default ${DIM}r${RESET} rm ${DIM}p${RESET} prune ${DIM}?${RESET} help ${DIM}q${RESET} quit${ANSI.clearLine}`;
|
|
1694
1709
|
}
|
|
1695
|
-
out.push(footer + "\n");
|
|
1710
|
+
out.push(M + footer + "\n");
|
|
1696
1711
|
if (state.showHelp) {
|
|
1697
|
-
out.push(
|
|
1712
|
+
out.push(`${M} ${DIM}● default · ❯ cursor · n/+ starts guided setup · i opens /invite${RESET}${ANSI.clearLine}`);
|
|
1698
1713
|
} else {
|
|
1699
1714
|
out.push(ANSI.clearLine);
|
|
1700
1715
|
}
|
|
@@ -1706,12 +1721,30 @@ async function browser(args = {}) {
|
|
|
1706
1721
|
process.stdout.write(ANSI.altScreenEnter + ANSI.mouseOff + ANSI.cursorHide + ANSI.clear + ANSI.home);
|
|
1707
1722
|
const isMouseEvent = makeMouseFilter();
|
|
1708
1723
|
let done = false;
|
|
1724
|
+
// Named so cleanup() can detach them. The launcher is NOT a true exec — it
|
|
1725
|
+
// stays alive as the parent of the spawned assistant (claude/codex) for the
|
|
1726
|
+
// whole session. The load-bearing detach is the RESIZE listener: Ghostty (and
|
|
1727
|
+
// other terminals) re-send SIGWINCH when a tab regains focus, and a stale
|
|
1728
|
+
// "resize"→render() repaints the launcher frame on top of the running
|
|
1729
|
+
// assistant — exactly the "I switched tabs and the launcher screen came back"
|
|
1730
|
+
// bug. The SIGINT/SIGTERM/exit handlers are dropped too because they're now
|
|
1731
|
+
// pointless: during the live session the child holds the tty in raw mode, so
|
|
1732
|
+
// Ctrl-C reaches it as a 0x03 byte (the parent never sees a SIGINT); and an
|
|
1733
|
+
// out-of-band kill -INT/-TERM exits the parent 130/143 by Node's default
|
|
1734
|
+
// disposition anyway — identical to the explicit handlers we remove.
|
|
1735
|
+
const onResize = () => render(); // render() itself no-ops once done is set
|
|
1736
|
+
let onSigint;
|
|
1737
|
+
let onSigterm;
|
|
1709
1738
|
const cleanup = () => {
|
|
1710
1739
|
if (done) return;
|
|
1711
1740
|
done = true;
|
|
1712
1741
|
process.stdin.setRawMode(false);
|
|
1713
1742
|
process.stdin.pause();
|
|
1714
1743
|
process.stdin.removeAllListeners("keypress");
|
|
1744
|
+
process.stdout.removeListener("resize", onResize);
|
|
1745
|
+
if (onSigint) process.removeListener("SIGINT", onSigint);
|
|
1746
|
+
if (onSigterm) process.removeListener("SIGTERM", onSigterm);
|
|
1747
|
+
process.removeListener("exit", cleanup);
|
|
1715
1748
|
// Clear the alt buffer BEFORE leaving it. A well-behaved terminal discards
|
|
1716
1749
|
// the alt buffer and restores the primary screen on \x1b[?1049l, so this is
|
|
1717
1750
|
// a harmless no-op there. Warp, however, dumps the alt buffer's contents
|
|
@@ -1720,10 +1753,12 @@ async function browser(args = {}) {
|
|
|
1720
1753
|
// guided "+ new" setup) starts on a clean screen.
|
|
1721
1754
|
process.stdout.write(ANSI.mouseOff + ANSI.cursorShow + ANSI.clear + ANSI.home + ANSI.altScreenLeave);
|
|
1722
1755
|
};
|
|
1756
|
+
onSigint = () => { cleanup(); process.exit(130); };
|
|
1757
|
+
onSigterm = () => { cleanup(); process.exit(143); };
|
|
1723
1758
|
process.on("exit", cleanup);
|
|
1724
|
-
process.on("SIGINT",
|
|
1725
|
-
process.on("SIGTERM",
|
|
1726
|
-
process.stdout.on("resize",
|
|
1759
|
+
process.on("SIGINT", onSigint);
|
|
1760
|
+
process.on("SIGTERM", onSigterm);
|
|
1761
|
+
process.stdout.on("resize", onResize);
|
|
1727
1762
|
|
|
1728
1763
|
readline.emitKeypressEvents(process.stdin);
|
|
1729
1764
|
process.stdin.setRawMode(true);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-egregore",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.9",
|
|
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 test/scroll-repro.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 test/launcher-refocus-redraw.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
|
},
|
|
@@ -36,13 +36,16 @@ EMPTY='{"fields":[],"values":[]}'
|
|
|
36
36
|
if [ -z "$API_URL" ] || [ -z "$API_KEY" ]; then
|
|
37
37
|
GRAPH_REASON="missing_config"
|
|
38
38
|
else
|
|
39
|
-
# Store HTTP status code alongside response body
|
|
39
|
+
# Store HTTP status code alongside response body. curl already emits 000
|
|
40
|
+
# on transport failure; keep the exit status separate so it cannot become
|
|
41
|
+
# 000000 and fall through as an invalid JSON response.
|
|
42
|
+
CURL_STATUS=0
|
|
40
43
|
HTTP_CODE=$(curl -s -o "$TMPDIR/activity_raw.json" -w "%{http_code}" \
|
|
41
44
|
"${API_URL}/api/activity/dashboard?github_username=$(printf '%s' "$GH_USER" | jq -sRr @uri)" \
|
|
42
45
|
-H "Authorization: Bearer $API_KEY" \
|
|
43
|
-
--connect-timeout 5 --max-time 15 2>/dev/null ||
|
|
46
|
+
--connect-timeout 5 --max-time 15 2>/dev/null) || CURL_STATUS=$?
|
|
44
47
|
|
|
45
|
-
if [ "$HTTP_CODE" = "000" ]; then
|
|
48
|
+
if [ "$CURL_STATUS" -ne 0 ] || [ "$HTTP_CODE" = "000" ] || ! [[ "$HTTP_CODE" =~ ^[0-9][0-9][0-9]$ ]]; then
|
|
46
49
|
GRAPH_REASON="unreachable"
|
|
47
50
|
elif [ "$HTTP_CODE" = "401" ] || [ "$HTTP_CODE" = "403" ]; then
|
|
48
51
|
GRAPH_REASON="auth_error"
|
|
@@ -44,12 +44,16 @@ SESSION_ID=$(cat "$HOME/.egregore/session-${PROJ_HASH}.id" 2>/dev/null || echo "
|
|
|
44
44
|
if [ -z "$API_URL" ] || [ -z "$API_KEY" ]; then
|
|
45
45
|
GRAPH_REASON="missing_config"
|
|
46
46
|
else
|
|
47
|
+
# Keep curl's transport status separate from %{http_code}; otherwise a
|
|
48
|
+
# failed request can concatenate 000 + fallback text and look like an
|
|
49
|
+
# unexpected API response.
|
|
50
|
+
CURL_STATUS=0
|
|
47
51
|
HTTP_CODE=$(curl -s -o "$TMPDIR/dashboard_raw.json" -w "%{http_code}" \
|
|
48
52
|
"${API_URL}/api/personal/dashboard?github_username=$(printf '%s' "$GH_USER" | jq -sRr @uri)&time_range=$(printf '%s' "$TIME_RANGE" | jq -sRr @uri)&session_id=$(printf '%s' "$SESSION_ID" | jq -sRr @uri)" \
|
|
49
53
|
-H "Authorization: Bearer $API_KEY" \
|
|
50
|
-
--connect-timeout 5 --max-time 15 2>/dev/null ||
|
|
54
|
+
--connect-timeout 5 --max-time 15 2>/dev/null) || CURL_STATUS=$?
|
|
51
55
|
|
|
52
|
-
if [ "$HTTP_CODE" = "000" ]; then
|
|
56
|
+
if [ "$CURL_STATUS" -ne 0 ] || [ "$HTTP_CODE" = "000" ] || ! [[ "$HTTP_CODE" =~ ^[0-9][0-9][0-9]$ ]]; then
|
|
53
57
|
GRAPH_REASON="unreachable"
|
|
54
58
|
elif [ "$HTTP_CODE" = "401" ] || [ "$HTTP_CODE" = "403" ]; then
|
|
55
59
|
GRAPH_REASON="auth_error"
|