create-egregore 0.19.12 → 0.19.15
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.
- package/assets/egregore-launcher.js +228 -15
- package/assets/egregore-launcher.sh +7 -3
- package/bin/cli.js +18 -0
- package/lib/setup.js +94 -14
- package/package.json +2 -2
- package/runtime/codex/.codex/hooks/search-hint.js +4 -2
- package/runtime/codex/.codex/skills/search/SKILL.md +28 -5
- package/runtime/codex/.codex/spec-manifest.json +7 -1
- package/runtime/codex/AGENTS.md +50 -3
- package/runtime/codex/bin/codex-render-spec.mjs +1 -0
- package/runtime/codex/bin/search.sh +240 -0
- package/runtime/codex/manifest.json +1 -0
- package/runtime/pi/.codex/hooks/search-hint.js +4 -2
- package/runtime/pi/.codex/skills/search/SKILL.md +28 -5
- package/runtime/pi/.codex/spec-manifest.json +7 -1
- package/runtime/pi/.pi/APPEND_SYSTEM.md +47 -0
- package/runtime/pi/.pi/spec-manifest.json +2 -2
- package/runtime/pi/AGENTS.md +50 -3
- package/runtime/pi/bin/codex-render-spec.mjs +1 -0
- package/runtime/pi/bin/search.sh +240 -0
- package/runtime/pi/manifest.json +1 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// egregore-launcher-version: 0.
|
|
2
|
+
// egregore-launcher-version: 0.19.15
|
|
3
3
|
//
|
|
4
4
|
// egregore — launch and browse Egregore instances from your terminal.
|
|
5
5
|
// Installed by create-egregore. Reads ~/.egregore/instances.json.
|
|
@@ -33,10 +33,17 @@ 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.
|
|
36
|
+
const VERSION = "0.19.15";
|
|
37
37
|
const REGISTRY = path.join(os.homedir(), ".egregore", "instances.json");
|
|
38
38
|
const DEFAULT_FILE = path.join(os.homedir(), ".egregore", "default");
|
|
39
|
+
const UPDATE_CACHE_FILE = path.join(os.homedir(), ".egregore", "update-check.json");
|
|
39
40
|
const PUBLIC_NPX_PACKAGE = "create-egregore";
|
|
41
|
+
const UPDATE_REGISTRY_URL =
|
|
42
|
+
process.env.EGREGORE_UPDATE_REGISTRY_URL ||
|
|
43
|
+
`https://registry.npmjs.org/${PUBLIC_NPX_PACKAGE}/latest`;
|
|
44
|
+
const UPDATE_CACHE_TTL_MS = 6 * 60 * 60 * 1000;
|
|
45
|
+
const UPDATE_FAILURE_TTL_MS = 30 * 60 * 1000;
|
|
46
|
+
const UPDATE_CHECK_TIMEOUT_MS = 1500;
|
|
40
47
|
const CONNECT_API_URL = (
|
|
41
48
|
process.env.EGREGORE_CONNECT_API_URL ||
|
|
42
49
|
"https://egregore-production-55f2.up.railway.app"
|
|
@@ -694,6 +701,24 @@ function installCodexSkills(egregoreDir) {
|
|
|
694
701
|
}
|
|
695
702
|
}
|
|
696
703
|
|
|
704
|
+
function claudeLaunchArgs(egregoreDir) {
|
|
705
|
+
const args = ["start"];
|
|
706
|
+
const memoryLink = path.join(egregoreDir, "memory");
|
|
707
|
+
try {
|
|
708
|
+
const memoryDir = fs.realpathSync(memoryLink);
|
|
709
|
+
if (fs.statSync(memoryDir).isDirectory()) {
|
|
710
|
+
// Egregore memory is intentionally a sibling Git repository. Declare it
|
|
711
|
+
// as part of Claude's workspace so auto mode does not classify normal
|
|
712
|
+
// organizational recall as an unexplained cross-project read.
|
|
713
|
+
args.push("--add-dir", memoryDir);
|
|
714
|
+
}
|
|
715
|
+
} catch {
|
|
716
|
+
// A missing/dangling memory link is diagnosed by session startup. Do not
|
|
717
|
+
// prevent the harness from opening.
|
|
718
|
+
}
|
|
719
|
+
return args;
|
|
720
|
+
}
|
|
721
|
+
|
|
697
722
|
function runtimeLaunchArgs(runtime, egregoreDir) {
|
|
698
723
|
if (runtime === "codex") {
|
|
699
724
|
return [
|
|
@@ -704,7 +729,7 @@ function runtimeLaunchArgs(runtime, egregoreDir) {
|
|
|
704
729
|
];
|
|
705
730
|
}
|
|
706
731
|
if (runtime === "pi") return piTrustArgs();
|
|
707
|
-
return
|
|
732
|
+
return claudeLaunchArgs(egregoreDir);
|
|
708
733
|
}
|
|
709
734
|
|
|
710
735
|
async function launch(inst, runtimeOverride) {
|
|
@@ -976,11 +1001,145 @@ function requestJson(options, body) {
|
|
|
976
1001
|
});
|
|
977
1002
|
});
|
|
978
1003
|
req.on("error", (err) => resolve({ status: 0, data: { message: err.message } }));
|
|
1004
|
+
if (options.unref) {
|
|
1005
|
+
req.on("socket", (socket) => socket.unref());
|
|
1006
|
+
}
|
|
1007
|
+
if (options.timeoutMs) {
|
|
1008
|
+
req.setTimeout(options.timeoutMs, () => req.destroy(new Error("request timed out")));
|
|
1009
|
+
}
|
|
979
1010
|
if (payload) req.write(payload);
|
|
980
1011
|
req.end();
|
|
981
1012
|
});
|
|
982
1013
|
}
|
|
983
1014
|
|
|
1015
|
+
function parseReleaseVersion(value) {
|
|
1016
|
+
const match = String(value || "").trim().match(/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/);
|
|
1017
|
+
if (!match) return null;
|
|
1018
|
+
return {
|
|
1019
|
+
parts: match.slice(1, 4).map(Number),
|
|
1020
|
+
prerelease: match[4] || "",
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
function compareReleaseVersions(a, b) {
|
|
1025
|
+
const left = parseReleaseVersion(a);
|
|
1026
|
+
const right = parseReleaseVersion(b);
|
|
1027
|
+
if (!left || !right) return 0;
|
|
1028
|
+
for (let i = 0; i < 3; i++) {
|
|
1029
|
+
if (left.parts[i] !== right.parts[i]) return left.parts[i] > right.parts[i] ? 1 : -1;
|
|
1030
|
+
}
|
|
1031
|
+
if (left.prerelease === right.prerelease) return 0;
|
|
1032
|
+
if (!left.prerelease) return 1;
|
|
1033
|
+
if (!right.prerelease) return -1;
|
|
1034
|
+
return left.prerelease.localeCompare(right.prerelease);
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
function cliUpdateState(latest) {
|
|
1038
|
+
if (!parseReleaseVersion(latest)) return null;
|
|
1039
|
+
return {
|
|
1040
|
+
current: VERSION,
|
|
1041
|
+
latest,
|
|
1042
|
+
available: compareReleaseVersions(latest, VERSION) > 0,
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
function readUpdateCache() {
|
|
1047
|
+
try {
|
|
1048
|
+
const cache = JSON.parse(fs.readFileSync(UPDATE_CACHE_FILE, "utf-8"));
|
|
1049
|
+
if (!cache || !cache.checked_at) return null;
|
|
1050
|
+
const age = Date.now() - new Date(cache.checked_at).getTime();
|
|
1051
|
+
const ttl = cache.latest ? UPDATE_CACHE_TTL_MS : UPDATE_FAILURE_TTL_MS;
|
|
1052
|
+
if (!Number.isFinite(age) || age < 0 || age > ttl) return null;
|
|
1053
|
+
if (!cache.latest) return { current: VERSION, latest: null, available: false };
|
|
1054
|
+
return cliUpdateState(cache.latest);
|
|
1055
|
+
} catch {
|
|
1056
|
+
return null;
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
function writeUpdateCache(latest) {
|
|
1061
|
+
try {
|
|
1062
|
+
fs.mkdirSync(path.dirname(UPDATE_CACHE_FILE), { recursive: true });
|
|
1063
|
+
atomicWrite(
|
|
1064
|
+
UPDATE_CACHE_FILE,
|
|
1065
|
+
JSON.stringify({ checked_at: new Date().toISOString(), latest: latest || null }, null, 2) + "\n",
|
|
1066
|
+
0o600,
|
|
1067
|
+
);
|
|
1068
|
+
} catch {}
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
async function checkForCliUpdate() {
|
|
1072
|
+
if (process.env.EGREGORE_DISABLE_UPDATE_CHECK === "1") return null;
|
|
1073
|
+
if (process.env.EGREGORE_UPDATE_LATEST_VERSION) {
|
|
1074
|
+
return cliUpdateState(process.env.EGREGORE_UPDATE_LATEST_VERSION);
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
const cached = readUpdateCache();
|
|
1078
|
+
if (cached) return cached;
|
|
1079
|
+
|
|
1080
|
+
try {
|
|
1081
|
+
const url = new URL(UPDATE_REGISTRY_URL);
|
|
1082
|
+
const result = await requestJson({
|
|
1083
|
+
hostname: url.hostname,
|
|
1084
|
+
port: url.port || undefined,
|
|
1085
|
+
path: `${url.pathname}${url.search}`,
|
|
1086
|
+
method: "GET",
|
|
1087
|
+
protocol: url.protocol,
|
|
1088
|
+
timeoutMs: UPDATE_CHECK_TIMEOUT_MS,
|
|
1089
|
+
unref: true,
|
|
1090
|
+
headers: { Accept: "application/json" },
|
|
1091
|
+
});
|
|
1092
|
+
const latest = result.status === 200 && result.data && result.data.version;
|
|
1093
|
+
const update = cliUpdateState(latest);
|
|
1094
|
+
writeUpdateCache(update?.latest || null);
|
|
1095
|
+
return update;
|
|
1096
|
+
} catch {
|
|
1097
|
+
return null;
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
function cmdCliUpdate(latest) {
|
|
1102
|
+
if (!parseReleaseVersion(latest)) {
|
|
1103
|
+
console.error(`\n ${YELLOW}!${RESET} Could not verify the CLI release version.\n`);
|
|
1104
|
+
process.exitCode = 1;
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
console.log("");
|
|
1109
|
+
console.log(` ${BOLD}${CYAN}↑ CLI UPDATE${RESET}`);
|
|
1110
|
+
console.log(` ${DIM}${VERSION} → ${latest}${RESET}`);
|
|
1111
|
+
console.log("");
|
|
1112
|
+
process.stdout.write(` [1/2] Installing ${PUBLIC_NPX_PACKAGE}@${latest}…`);
|
|
1113
|
+
const result = spawnSync(
|
|
1114
|
+
"npx",
|
|
1115
|
+
["-y", `${PUBLIC_NPX_PACKAGE}@${latest}`, "--refresh-launcher"],
|
|
1116
|
+
{ encoding: "utf-8", stdio: ["inherit", "pipe", "pipe"], env: { ...process.env } },
|
|
1117
|
+
);
|
|
1118
|
+
if (result.status !== 0) {
|
|
1119
|
+
console.log(`\r${ANSI.clearLine} [1/2] ${YELLOW}!${RESET} Install failed`);
|
|
1120
|
+
const detail = String(result.stderr || result.stdout || "").trim().split("\n").filter(Boolean).pop();
|
|
1121
|
+
if (detail) console.error(` ${DIM}${detail}${RESET}`);
|
|
1122
|
+
console.error(` Retry: npx -y ${PUBLIC_NPX_PACKAGE}@${latest} --refresh-launcher\n`);
|
|
1123
|
+
process.exitCode = 1;
|
|
1124
|
+
return;
|
|
1125
|
+
}
|
|
1126
|
+
console.log(`\r${ANSI.clearLine} [1/2] ${GREEN}✓${RESET} CLI ${latest} installed`);
|
|
1127
|
+
|
|
1128
|
+
if (process.env.EGREGORE_UPDATE_NO_RELAUNCH === "1") {
|
|
1129
|
+
console.log(` [2/2] ${GREEN}✓${RESET} Ready for the next launch\n`);
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
console.log(` [2/2] ${GREEN}✓${RESET} Reopening Egregore\n`);
|
|
1134
|
+
const relaunched = spawnSync(process.execPath, [process.argv[1], "browse"], {
|
|
1135
|
+
stdio: "inherit",
|
|
1136
|
+
env: { ...process.env },
|
|
1137
|
+
});
|
|
1138
|
+
if (relaunched.error) {
|
|
1139
|
+
console.error(` ${YELLOW}!${RESET} Updated successfully. Run ${BOLD}egregore${RESET} to reopen.\n`);
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
|
|
984
1143
|
function githubRequest(token, method, apiPath, body) {
|
|
985
1144
|
return requestJson({
|
|
986
1145
|
hostname: "api.github.com",
|
|
@@ -1109,12 +1268,42 @@ async function cmdConnect(args) {
|
|
|
1109
1268
|
|
|
1110
1269
|
let intentId = activation.intent_id || "";
|
|
1111
1270
|
let connectUrl = activation.connect_url || "";
|
|
1112
|
-
|
|
1271
|
+
let ticketExpiresAt = Number(activation.connect_ticket_expires_at || 0) * 1000;
|
|
1272
|
+
const resumable = ["created", "checkout_pending", "entitled", "provisioning", "backfilling"].includes(
|
|
1273
|
+
activation.status,
|
|
1274
|
+
);
|
|
1275
|
+
if (
|
|
1276
|
+
intentId &&
|
|
1277
|
+
resumable &&
|
|
1278
|
+
(!activation.connect_ticket || !ticketExpiresAt || ticketExpiresAt <= Date.now() + 60_000)
|
|
1279
|
+
) {
|
|
1280
|
+
const refreshed = await connectedApiRequest(
|
|
1281
|
+
githubToken,
|
|
1282
|
+
"POST",
|
|
1283
|
+
`/api/billing/connect/${encodeURIComponent(intentId)}/ticket`,
|
|
1284
|
+
{},
|
|
1285
|
+
);
|
|
1286
|
+
if (refreshed.status !== 200) {
|
|
1287
|
+
die(`could not refresh Connected link: ${connectError(refreshed, `HTTP ${refreshed.status}`)}`);
|
|
1288
|
+
}
|
|
1289
|
+
connectUrl = refreshed.data.connect_url;
|
|
1290
|
+
ticketExpiresAt = Number(refreshed.data.connect_ticket_expires_at || 0) * 1000;
|
|
1291
|
+
persistConnectState(inst, {
|
|
1292
|
+
connect_ticket: refreshed.data.connect_ticket,
|
|
1293
|
+
connect_ticket_expires_at: refreshed.data.connect_ticket_expires_at,
|
|
1294
|
+
connect_url: connectUrl,
|
|
1295
|
+
status: refreshed.data.status || activation.status,
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1298
|
+
if (
|
|
1299
|
+
!intentId ||
|
|
1300
|
+
!resumable
|
|
1301
|
+
) {
|
|
1113
1302
|
console.log(` [1/5] ${DIM}Creating connection intent…${RESET}`);
|
|
1114
1303
|
const checkout = await connectedApiRequest(
|
|
1115
1304
|
githubToken,
|
|
1116
1305
|
"POST",
|
|
1117
|
-
"/api/billing/
|
|
1306
|
+
"/api/billing/connect-intents",
|
|
1118
1307
|
{
|
|
1119
1308
|
org_slug: cfg.slug || inst.slug,
|
|
1120
1309
|
plan: "connect",
|
|
@@ -1134,9 +1323,10 @@ async function cmdConnect(args) {
|
|
|
1134
1323
|
connectUrl = checkout.data.connect_url;
|
|
1135
1324
|
persistConnectState(inst, {
|
|
1136
1325
|
intent_id: intentId,
|
|
1137
|
-
|
|
1326
|
+
connect_ticket: checkout.data.connect_ticket,
|
|
1327
|
+
connect_ticket_expires_at: checkout.data.connect_ticket_expires_at,
|
|
1138
1328
|
connect_url: connectUrl,
|
|
1139
|
-
status: "
|
|
1329
|
+
status: checkout.data.status || "created",
|
|
1140
1330
|
});
|
|
1141
1331
|
console.log(` [1/5] ${GREEN}✓${RESET} Connection intent`);
|
|
1142
1332
|
} else {
|
|
@@ -1228,12 +1418,17 @@ async function cmdConnect(args) {
|
|
|
1228
1418
|
let backfillSummary = { replayed: true };
|
|
1229
1419
|
try { backfillSummary = JSON.parse((sync.stdout || "").trim()); } catch {}
|
|
1230
1420
|
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1421
|
+
let complete;
|
|
1422
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
1423
|
+
complete = await connectedApiRequest(
|
|
1424
|
+
githubToken,
|
|
1425
|
+
"POST",
|
|
1426
|
+
`/api/billing/connect/${encodeURIComponent(intentId)}/complete`,
|
|
1427
|
+
{ backfill_summary: backfillSummary },
|
|
1428
|
+
);
|
|
1429
|
+
if (complete.status === 200 || complete.status !== 0) break;
|
|
1430
|
+
await sleep(250 * (attempt + 1));
|
|
1431
|
+
}
|
|
1237
1432
|
if (complete.status !== 200) {
|
|
1238
1433
|
die(`graph replay finished, but activation could not be finalized: ${connectError(complete, `HTTP ${complete.status}`)}`);
|
|
1239
1434
|
}
|
|
@@ -1689,7 +1884,7 @@ function selectedWorkflowLine(pv, width) {
|
|
|
1689
1884
|
return joinShort(available.map(skillToken), Math.max(12, width));
|
|
1690
1885
|
}
|
|
1691
1886
|
|
|
1692
|
-
function renderHeroPanel({ selected, pv, width, height, countSuffix, filter, filterMode }) {
|
|
1887
|
+
function renderHeroPanel({ selected, pv, width, height, countSuffix, filter, filterMode, cliUpdate }) {
|
|
1693
1888
|
const outerWidth = Math.min(Math.max(50, width - 2), 126);
|
|
1694
1889
|
const innerWidth = Math.max(48, outerWidth - 2);
|
|
1695
1890
|
const bodyWidth = Math.max(8, innerWidth - 2);
|
|
@@ -1698,6 +1893,11 @@ function renderHeroPanel({ selected, pv, width, height, countSuffix, filter, fil
|
|
|
1698
1893
|
const top = `${margin}${DIM}┌${"─".repeat(2)}${label}${"─".repeat(Math.max(0, innerWidth - label.length - 2))}┐${RESET}`;
|
|
1699
1894
|
const bottom = `${margin}${DIM}└${"─".repeat(innerWidth)}┘${RESET}`;
|
|
1700
1895
|
const mid = `${margin}${DIM}├${"─".repeat(innerWidth)}┤${RESET}`;
|
|
1896
|
+
const updateBand = cliUpdate?.available
|
|
1897
|
+
? (colorOn
|
|
1898
|
+
? `${margin}${DIM}│${RESET}${E(43)}${E(30)}${pad(` CLI UPDATE AVAILABLE · ${cliUpdate.current} → ${cliUpdate.latest} · ${E(1)}U${E(22)} INSTALL`, innerWidth)}${RESET}${DIM}│${RESET}`
|
|
1899
|
+
: `${margin}│${pad(` CLI UPDATE AVAILABLE · ${cliUpdate.current} → ${cliUpdate.latest} · U INSTALL`, innerWidth)}│`)
|
|
1900
|
+
: null;
|
|
1701
1901
|
const lines = [top];
|
|
1702
1902
|
|
|
1703
1903
|
const title = selected ? `${selected.slug || selected.name || "egregore"}` : "egregore";
|
|
@@ -1738,7 +1938,7 @@ function renderHeroPanel({ selected, pv, width, height, countSuffix, filter, fil
|
|
|
1738
1938
|
lines.push(boxLine(`${BOLD}${CYAN}EGREGORE${RESET} ${DIM}v${VERSION} · ${countSuffix} · ${filterText}${RESET}`, innerWidth, margin));
|
|
1739
1939
|
}
|
|
1740
1940
|
lines.push(emptyBoxLine(innerWidth, margin));
|
|
1741
|
-
lines.push(mid);
|
|
1941
|
+
lines.push(updateBand || mid);
|
|
1742
1942
|
lines.push(boxLine(`${BOLD}${title}${RESET} ${DIM}· ${org} · ${runtime} · ${countSuffix} · ${filterText}${RESET}`, innerWidth, margin));
|
|
1743
1943
|
lines.push(boxLine(`${DIM}skills${RESET} ${skillStatus}`, innerWidth, margin));
|
|
1744
1944
|
lines.push(emptyBoxLine(innerWidth, margin));
|
|
@@ -1938,6 +2138,7 @@ async function browser(args = {}) {
|
|
|
1938
2138
|
message: null, // ephemeral info line (e.g. "default set to X")
|
|
1939
2139
|
messageKind: "info", // "info" | "success" | "warn"
|
|
1940
2140
|
messageUntil: 0,
|
|
2141
|
+
cliUpdate: null,
|
|
1941
2142
|
};
|
|
1942
2143
|
|
|
1943
2144
|
const newAction = {
|
|
@@ -2041,6 +2242,7 @@ async function browser(args = {}) {
|
|
|
2041
2242
|
countSuffix,
|
|
2042
2243
|
filter: state.filter,
|
|
2043
2244
|
filterMode: state.filterMode,
|
|
2245
|
+
cliUpdate: state.cliUpdate,
|
|
2044
2246
|
});
|
|
2045
2247
|
for (const line of heroLines) out.push(line + ANSI.clearLine + "\n");
|
|
2046
2248
|
out.push(ANSI.clearLine + "\n");
|
|
@@ -2571,6 +2773,12 @@ async function browser(args = {}) {
|
|
|
2571
2773
|
resolve(cmdConnect([pick.slug]));
|
|
2572
2774
|
return;
|
|
2573
2775
|
}
|
|
2776
|
+
if (chunk === "U" && state.cliUpdate?.available) {
|
|
2777
|
+
const latest = state.cliUpdate.latest;
|
|
2778
|
+
cleanup();
|
|
2779
|
+
resolve(cmdCliUpdate(latest));
|
|
2780
|
+
return;
|
|
2781
|
+
}
|
|
2574
2782
|
if (chunk === "i") {
|
|
2575
2783
|
const pick = visible()[state.cursor]?.e;
|
|
2576
2784
|
if (!pick) return;
|
|
@@ -2657,6 +2865,11 @@ async function browser(args = {}) {
|
|
|
2657
2865
|
});
|
|
2658
2866
|
|
|
2659
2867
|
render();
|
|
2868
|
+
checkForCliUpdate().then((update) => {
|
|
2869
|
+
if (done || !update) return;
|
|
2870
|
+
state.cliUpdate = update;
|
|
2871
|
+
if (update.available) render();
|
|
2872
|
+
});
|
|
2660
2873
|
});
|
|
2661
2874
|
}
|
|
2662
2875
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# egregore-launcher-version: 0.4.
|
|
2
|
+
# egregore-launcher-version: 0.4.10
|
|
3
3
|
# egregore — launch an Egregore instance in Claude Code.
|
|
4
4
|
# Reads ~/.egregore/instances.json for the registry of installed egregores.
|
|
5
5
|
#
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
set -u
|
|
24
24
|
|
|
25
|
-
VERSION="0.4.
|
|
25
|
+
VERSION="0.4.10"
|
|
26
26
|
REGISTRY="${HOME}/.egregore/instances.json"
|
|
27
27
|
DEFAULT_FILE="${HOME}/.egregore/default"
|
|
28
28
|
|
|
@@ -93,7 +93,11 @@ launch() {
|
|
|
93
93
|
printf ' %s→%s launching %s%s%s ...\n\n' "$DIM" "$RESET" "$BOLD" "$target_slug" "$RESET"
|
|
94
94
|
fi
|
|
95
95
|
cd "$target_path" || die "Could not cd into $target_path"
|
|
96
|
-
|
|
96
|
+
local -a claude_args=("start")
|
|
97
|
+
if [ -d "$target_path/memory" ]; then
|
|
98
|
+
claude_args+=("--add-dir" "$target_path/memory")
|
|
99
|
+
fi
|
|
100
|
+
exec claude "${claude_args[@]}"
|
|
97
101
|
}
|
|
98
102
|
|
|
99
103
|
cmd_ls() {
|
package/bin/cli.js
CHANGED
|
@@ -223,6 +223,8 @@ function parseArgs(argv) {
|
|
|
223
223
|
// already served as the entry screen — a second alt-screen welcome
|
|
224
224
|
// would otherwise demand a redundant Enter and churn the alt buffer.
|
|
225
225
|
args.noWelcome = true;
|
|
226
|
+
} else if (argv[i] === "--refresh-launcher") {
|
|
227
|
+
args.refreshLauncher = true;
|
|
226
228
|
} else if (argv[i] === "--codex") {
|
|
227
229
|
args.runtime = "codex";
|
|
228
230
|
} else if (argv[i] === "--pi") {
|
|
@@ -395,6 +397,7 @@ function showHelp() {
|
|
|
395
397
|
ui.info(" --force Skip the in-repo detection check");
|
|
396
398
|
ui.info(" --no-autolaunch Don't launch the harness at the end (for CI)");
|
|
397
399
|
ui.info(" --no-screens Skip the full-screen welcome/ready bookends");
|
|
400
|
+
ui.info(" --refresh-launcher Refresh only the Egregore CLI, then exit");
|
|
398
401
|
ui.info(" -h, --help Show this help");
|
|
399
402
|
ui.info("");
|
|
400
403
|
}
|
|
@@ -443,6 +446,21 @@ async function main() {
|
|
|
443
446
|
}
|
|
444
447
|
}
|
|
445
448
|
|
|
449
|
+
if (args.refreshLauncher) {
|
|
450
|
+
const instances = readLocalInstances();
|
|
451
|
+
if (instances.length === 0) {
|
|
452
|
+
ui.error("No installed Egregore instance was found. Run create-egregore without --refresh-launcher.");
|
|
453
|
+
process.exit(1);
|
|
454
|
+
}
|
|
455
|
+
const alias = await installLauncherBinary(instances[0].path, ui);
|
|
456
|
+
if (!alias || !alias.binaryPath) {
|
|
457
|
+
ui.error("Could not refresh the Egregore CLI.");
|
|
458
|
+
process.exit(1);
|
|
459
|
+
}
|
|
460
|
+
ui.success(`Egregore CLI is current at ${alias.binaryPath}`);
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
|
|
446
464
|
// Local-first product surface: if the user already has egregores installed
|
|
447
465
|
// and didn't pass an explicit mode flag, refresh the launcher and open the
|
|
448
466
|
// same TUI as bare `egregore`. With no installed instances, make bare
|
package/lib/setup.js
CHANGED
|
@@ -426,6 +426,15 @@ async function install(data, ui, targetDir, opts = {}) {
|
|
|
426
426
|
ui.success("Linked");
|
|
427
427
|
}
|
|
428
428
|
|
|
429
|
+
// The connected config is required infrastructure, not an optional overlay.
|
|
430
|
+
// A setup race once left develop behind main, so a newly-created work branch
|
|
431
|
+
// could lose egregore.json even though the default branch contained it.
|
|
432
|
+
// Recover that exact file from origin/main when possible, then validate it
|
|
433
|
+
// before writing credentials or registering a launcher.
|
|
434
|
+
if (api_key || data.api_url) {
|
|
435
|
+
ensureConnectedConfig(egregoreDir, data, ui);
|
|
436
|
+
}
|
|
437
|
+
|
|
429
438
|
// 4. Write .env (secrets only — never committed to git)
|
|
430
439
|
ui.step(4, totalSteps, "Writing credentials...");
|
|
431
440
|
const envPath = path.join(egregoreDir, ".env");
|
|
@@ -450,19 +459,7 @@ async function install(data, ui, targetDir, opts = {}) {
|
|
|
450
459
|
fs.writeFileSync(statePath, JSON.stringify(state, null, 2) + "\n");
|
|
451
460
|
try { ensurePersonOnboardingWitness(memoryDir, state); } catch {}
|
|
452
461
|
|
|
453
|
-
// 4c.
|
|
454
|
-
const configPath = path.join(egregoreDir, "egregore.json");
|
|
455
|
-
if (fs.existsSync(configPath)) {
|
|
456
|
-
try {
|
|
457
|
-
const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
458
|
-
if (!config.mode) {
|
|
459
|
-
config.mode = "connected";
|
|
460
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
461
|
-
}
|
|
462
|
-
} catch {}
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
// 4d. Overlay the selected shell-runtime support. Claude Code reads
|
|
462
|
+
// 4c. Overlay the selected shell-runtime support. Claude Code reads
|
|
466
463
|
// CLAUDE.md, not AGENTS.md; overlaying the Codex AGENTS.md/.codex into a Claude
|
|
467
464
|
// workspace gives a wandering agent a competing startup protocol (the
|
|
468
465
|
// xenocracy onboarding regression). Claude joiners keep the cloned repo as-is —
|
|
@@ -584,6 +581,74 @@ async function install(data, ui, targetDir, opts = {}) {
|
|
|
584
581
|
await launchOrInstruct(path.join(base, forkDirName), alias, ui, opts);
|
|
585
582
|
}
|
|
586
583
|
|
|
584
|
+
/**
|
|
585
|
+
* Require and validate the connected instance configuration.
|
|
586
|
+
*
|
|
587
|
+
* If the current branch is missing egregore.json, restore it from origin/main
|
|
588
|
+
* without changing branches or disturbing existing work. A missing/invalid
|
|
589
|
+
* remote config aborts setup before credentials and launcher registration.
|
|
590
|
+
*/
|
|
591
|
+
function ensureConnectedConfig(repoDir, data, ui) {
|
|
592
|
+
const configPath = path.join(repoDir, "egregore.json");
|
|
593
|
+
|
|
594
|
+
if (!fs.existsSync(configPath)) {
|
|
595
|
+
try {
|
|
596
|
+
execFileSync(
|
|
597
|
+
"git",
|
|
598
|
+
["-C", repoDir, "fetch", "origin", "+main:refs/remotes/origin/main"],
|
|
599
|
+
{
|
|
600
|
+
stdio: "pipe", encoding: "utf-8", timeout: 30000,
|
|
601
|
+
},
|
|
602
|
+
);
|
|
603
|
+
const remoteConfig = execFileSync(
|
|
604
|
+
"git",
|
|
605
|
+
["-C", repoDir, "show", "origin/main:egregore.json"],
|
|
606
|
+
{ stdio: "pipe", encoding: "utf-8", timeout: 10000 },
|
|
607
|
+
);
|
|
608
|
+
JSON.parse(remoteConfig);
|
|
609
|
+
fs.writeFileSync(configPath, remoteConfig.endsWith("\n") ? remoteConfig : `${remoteConfig}\n`);
|
|
610
|
+
if (ui && typeof ui.warn === "function") {
|
|
611
|
+
ui.warn("Restored missing egregore.json from origin/main");
|
|
612
|
+
}
|
|
613
|
+
} catch {
|
|
614
|
+
throw new Error(
|
|
615
|
+
"Connected setup is incomplete: egregore.json is missing from this checkout and origin/main. Retry setup from egregore.xyz/setup."
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
let config;
|
|
621
|
+
try {
|
|
622
|
+
config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
623
|
+
} catch {
|
|
624
|
+
throw new Error("Connected setup is incomplete: egregore.json is not valid JSON.");
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
const required = ["org_name", "github_org", "memory_repo", "api_url", "slug"];
|
|
628
|
+
const missing = required.filter((key) => !config[key]);
|
|
629
|
+
if (missing.length) {
|
|
630
|
+
throw new Error(
|
|
631
|
+
`Connected setup is incomplete: egregore.json is missing ${missing.join(", ")}. Retry setup from egregore.xyz/setup.`
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
if (data.slug && config.slug !== data.slug) {
|
|
635
|
+
throw new Error(
|
|
636
|
+
`Connected setup mismatch: expected org ${data.slug}, but egregore.json belongs to ${config.slug}.`
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
if (data.github_org && config.github_org.toLowerCase() !== data.github_org.toLowerCase()) {
|
|
640
|
+
throw new Error(
|
|
641
|
+
`Connected setup mismatch: expected GitHub owner ${data.github_org}, but egregore.json uses ${config.github_org}.`
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
if (config.mode !== "connected") {
|
|
646
|
+
config.mode = "connected";
|
|
647
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
648
|
+
}
|
|
649
|
+
return config;
|
|
650
|
+
}
|
|
651
|
+
|
|
587
652
|
/**
|
|
588
653
|
* Ensure a cloned repo has a develop branch. Creates from default branch and pushes if missing.
|
|
589
654
|
* Idempotent — safe to call on repos that already have develop.
|
|
@@ -752,6 +817,20 @@ function piTrustArgs() {
|
|
|
752
817
|
}
|
|
753
818
|
}
|
|
754
819
|
|
|
820
|
+
function claudeLaunchArgs(egregoreDir) {
|
|
821
|
+
const args = ["start"];
|
|
822
|
+
const memoryLink = path.join(egregoreDir, "memory");
|
|
823
|
+
try {
|
|
824
|
+
const memoryDir = fs.realpathSync(memoryLink);
|
|
825
|
+
if (fs.statSync(memoryDir).isDirectory()) {
|
|
826
|
+
args.push("--add-dir", memoryDir);
|
|
827
|
+
}
|
|
828
|
+
} catch {
|
|
829
|
+
// Session startup owns the actionable missing-memory diagnostic.
|
|
830
|
+
}
|
|
831
|
+
return args;
|
|
832
|
+
}
|
|
833
|
+
|
|
755
834
|
function runtimeLaunchArgs(runtime, egregoreDir) {
|
|
756
835
|
if (normalizeRuntime(runtime) === "codex") {
|
|
757
836
|
return [
|
|
@@ -762,7 +841,7 @@ function runtimeLaunchArgs(runtime, egregoreDir) {
|
|
|
762
841
|
];
|
|
763
842
|
}
|
|
764
843
|
if (normalizeRuntime(runtime) === "pi") return piTrustArgs();
|
|
765
|
-
return
|
|
844
|
+
return claudeLaunchArgs(egregoreDir);
|
|
766
845
|
}
|
|
767
846
|
|
|
768
847
|
function startSpinner(label) {
|
|
@@ -1353,5 +1432,6 @@ module.exports = {
|
|
|
1353
1432
|
assistantInstallCommand,
|
|
1354
1433
|
initMemoryDirs,
|
|
1355
1434
|
ensureDevelop,
|
|
1435
|
+
ensureConnectedConfig,
|
|
1356
1436
|
run,
|
|
1357
1437
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-egregore",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.15",
|
|
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",
|
|
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-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
|
},
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
//
|
|
5
5
|
// Mirror of .claude/hooks/search-hint.sh: when the user's prompt is
|
|
6
6
|
// recall-shaped ("find the handoff about…", "did we decide…"), inject a
|
|
7
|
-
// routing hint toward `bin/
|
|
7
|
+
// routing hint toward `bash bin/search.sh query` — one ranked call over
|
|
8
8
|
// memory/ — instead of improvised ls/grep/graph exploration.
|
|
9
9
|
//
|
|
10
10
|
// Advisory only, never blocks, rate-limited once per session, exits 0.
|
|
@@ -43,6 +43,8 @@ const RECALL = new RegExp(
|
|
|
43
43
|
"\\bwho worked on\\b",
|
|
44
44
|
"\\bdidn.?t (someone|we) already\\b",
|
|
45
45
|
"\\bwas there (a|any) (handoff|decision|discussion|meeting)\\b",
|
|
46
|
+
"\\b(our|company|org|organization|team)\\b.*\\b(pricing|price|tiers?|plans?|paid|free|strategy|policy|positioning|ownership|decision)\\b",
|
|
47
|
+
"\\b(pricing|price|tiers?|plans?|paid|free)\\b.*\\b(our|company|org|organization|team)\\b",
|
|
46
48
|
].join("|"),
|
|
47
49
|
"i"
|
|
48
50
|
);
|
|
@@ -59,7 +61,7 @@ process.stdout.write(JSON.stringify({
|
|
|
59
61
|
hookSpecificOutput: {
|
|
60
62
|
hookEventName: "UserPromptSubmit",
|
|
61
63
|
additionalContext:
|
|
62
|
-
"Routing hint: this prompt asks for recall from org memory. FIRST action: `bin/
|
|
64
|
+
"Routing hint: this prompt asks for recall from org memory. FIRST action: `bash bin/search.sh query \"<the concept, not the literal sentence>\"` — one ranked call over all of memory/ (hybrid keyword+semantic; graph-state annotations attach automatically in connected mode). Read the top hits and cite paths. Do NOT improvise ls/grep/graph exploration first — hand-written graph queries against invented labels (e.g. :Handoff — handoffs are :Session nodes) return empty. Full spec: the search skill.",
|
|
63
65
|
},
|
|
64
66
|
}) + "\n");
|
|
65
67
|
process.exit(0);
|
|
@@ -11,12 +11,35 @@ search before answering from conversation context alone.
|
|
|
11
11
|
|
|
12
12
|
## Flow
|
|
13
13
|
|
|
14
|
-
1.
|
|
14
|
+
1. Organizational recall must begin with the Egregore search entry point in
|
|
15
|
+
step 2. Do not resolve `memory/` to its sibling Git repository, change into
|
|
16
|
+
that absolute path, or improvise raw grep/listing as the first retrieval
|
|
17
|
+
action. Use direct file reads or grep only on evidence paths returned by the
|
|
18
|
+
ranked search.
|
|
19
|
+
|
|
20
|
+
Before any shell, file-read, grep, or other retrieval tool call, send exactly
|
|
21
|
+
one standalone commentary line:
|
|
22
|
+
|
|
23
|
+
- local/filesystem-only:
|
|
24
|
+
`⌕ Egregore · searching your organization’s memory`
|
|
25
|
+
- connected mode with automatic graph enrichment:
|
|
26
|
+
`⌕ Egregore Connect · searching your organization’s memory and relationships`
|
|
27
|
+
|
|
28
|
+
Send the line verbatim. Do not replace it with generic narration such as
|
|
29
|
+
“I’ll search org memory.” It must be visible before the tool call; captured
|
|
30
|
+
command output does not satisfy this requirement. Emit it once even when the
|
|
31
|
+
retrieval needs multiple queries or source reads.
|
|
32
|
+
|
|
33
|
+
2. Run the search:
|
|
15
34
|
|
|
16
35
|
```bash
|
|
17
|
-
bin/
|
|
36
|
+
bash bin/search.sh query "your query" -n 6
|
|
18
37
|
```
|
|
19
38
|
|
|
39
|
+
- The command also prints the attribution as a direct-shell fallback. That
|
|
40
|
+
captured banner is diagnostic redundancy, not the user-visible product beat.
|
|
41
|
+
If the answer instead begins with a graph-only named read, follow the same
|
|
42
|
+
global Egregore Retrieval Beat before that query.
|
|
20
43
|
- Default is hybrid (BM25 + vector + rerank); it falls back to keyword
|
|
21
44
|
search automatically when local models aren't built, with a note.
|
|
22
45
|
- `--fast` — keyword only (BM25), instant. Use for exact terms: names,
|
|
@@ -27,15 +50,15 @@ bin/agent.sh search "your query" -n 6
|
|
|
27
50
|
state lines (status, recipient, artifact type, quest) with no flag.
|
|
28
51
|
`--no-enrich` skips it when speed matters; local mode never attempts it.
|
|
29
52
|
|
|
30
|
-
|
|
53
|
+
3. Search the *concept*, not the user's literal sentence. If the first query
|
|
31
54
|
returns weak scores, run a second with different wording — one
|
|
32
55
|
keyword-ish, one conceptual.
|
|
33
56
|
|
|
34
|
-
|
|
57
|
+
4. Results are snippets with `memory/...` file paths. Read the source file
|
|
35
58
|
for anything you will cite or act on — snippets truncate, and memory
|
|
36
59
|
files carry dates, authors, and caveats that change the answer.
|
|
37
60
|
|
|
38
|
-
|
|
61
|
+
5. Answer directly, citing file paths. If results are empty or weak, say so
|
|
39
62
|
plainly — "nothing in memory on X" is a real answer. Do not present
|
|
40
63
|
conversation-context guesses as memory.
|
|
41
64
|
|