create-egregore 0.21.0-runtime-mvp.19 → 0.21.0-runtime-mvp.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.
- package/assets/egregore-launcher.js +14 -4
- package/bin/cli.js +4 -0
- package/lib/setup.js +54 -7
- package/package.json +1 -1
- package/runtime/claude/.claude/hooks/memory-read-guard.sh +9 -5
- package/runtime/claude/.claude/hooks/retrieval-context.sh +3 -0
- package/runtime/claude/bin/search.sh +5 -0
- package/runtime/codex/bin/search.sh +5 -0
- package/runtime/pi/bin/search.sh +5 -0
- package/runtime/prime/bin/search.sh +5 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// egregore-launcher-version: 0.21.0-runtime-mvp.
|
|
2
|
+
// egregore-launcher-version: 0.21.0-runtime-mvp.21
|
|
3
3
|
//
|
|
4
4
|
// egregore — launch and browse Egregore instances from your terminal.
|
|
5
5
|
// Installed by create-egregore. Reads ~/.egregore/instances.json.
|
|
@@ -35,7 +35,7 @@ const crypto = require("node:crypto");
|
|
|
35
35
|
const { spawnSync, spawn } = require("node:child_process");
|
|
36
36
|
const readline = require("node:readline");
|
|
37
37
|
|
|
38
|
-
const VERSION = "0.21.0-runtime-mvp.
|
|
38
|
+
const VERSION = "0.21.0-runtime-mvp.21";
|
|
39
39
|
const REGISTRY = path.join(os.homedir(), ".egregore", "instances.json");
|
|
40
40
|
const DEFAULT_FILE = path.join(os.homedir(), ".egregore", "default");
|
|
41
41
|
const UPDATE_CACHE_FILE = path.join(os.homedir(), ".egregore", "update-check.json");
|
|
@@ -2341,8 +2341,18 @@ function connectedModeSuffix(instPath) {
|
|
|
2341
2341
|
function packagedUpgradeScript() {
|
|
2342
2342
|
const seeded = process.env.EGREGORE_RUNTIME_PACKAGED_BRIDGE;
|
|
2343
2343
|
if (seeded !== undefined) return seeded || null;
|
|
2344
|
-
|
|
2345
|
-
|
|
2344
|
+
// The launcher is copied into a PATH directory as a standalone executable,
|
|
2345
|
+
// so __dirname normally points at /usr/local/bin or ~/.local/bin rather than
|
|
2346
|
+
// the npm package that supplied it. installLauncherBinary keeps the matching
|
|
2347
|
+
// Runtime engine in a versioned per-user directory outside every Egregore
|
|
2348
|
+
// checkout. The package-adjacent fallback keeps direct source/package runs
|
|
2349
|
+
// working without a separate install step.
|
|
2350
|
+
const runtimeBase = process.env.EGREGORE_LAUNCHER_RUNTIME_DIR
|
|
2351
|
+
|| path.join(os.homedir(), ".egregore", "launcher-runtime");
|
|
2352
|
+
const installed = path.join(runtimeBase, VERSION, "claude", "bin", "runtime-upgrade.sh");
|
|
2353
|
+
if (fs.existsSync(installed)) return installed;
|
|
2354
|
+
const adjacent = path.join(__dirname, "..", "runtime", "claude", "bin", "runtime-upgrade.sh");
|
|
2355
|
+
return fs.existsSync(adjacent) ? adjacent : null;
|
|
2346
2356
|
}
|
|
2347
2357
|
|
|
2348
2358
|
function resolveUpgradeBridge(inst) {
|
package/bin/cli.js
CHANGED
|
@@ -471,6 +471,10 @@ async function main() {
|
|
|
471
471
|
ui.error("Could not refresh the Egregore CLI.");
|
|
472
472
|
process.exit(1);
|
|
473
473
|
}
|
|
474
|
+
if (!alias.runtimeBridgePath || !fs.existsSync(alias.runtimeBridgePath)) {
|
|
475
|
+
ui.error("Could not refresh the Runtime update engine.");
|
|
476
|
+
process.exit(1);
|
|
477
|
+
}
|
|
474
478
|
ui.success(`Egregore CLI is current at ${alias.binaryPath}`);
|
|
475
479
|
return;
|
|
476
480
|
}
|
package/lib/setup.js
CHANGED
|
@@ -1283,6 +1283,7 @@ async function launchOrInstruct(egregoreDir, alias, ui, ctx = {}) {
|
|
|
1283
1283
|
// browser, better rendering, and richer keybinds.
|
|
1284
1284
|
const LAUNCHER_TEMPLATE_JS = path.join(__dirname, "..", "assets", "egregore-launcher.js");
|
|
1285
1285
|
const LAUNCHER_TEMPLATE_SH = path.join(__dirname, "..", "assets", "egregore-launcher.sh");
|
|
1286
|
+
const LAUNCHER_RUNTIME_TEMPLATE = path.join(__dirname, "..", "runtime", "claude");
|
|
1286
1287
|
|
|
1287
1288
|
function readLauncherTemplate() {
|
|
1288
1289
|
// Prefer the Node launcher; fall back to bash only if the .js is missing.
|
|
@@ -1294,6 +1295,50 @@ function currentTemplatePath() {
|
|
|
1294
1295
|
return fs.existsSync(LAUNCHER_TEMPLATE_JS) ? LAUNCHER_TEMPLATE_JS : LAUNCHER_TEMPLATE_SH;
|
|
1295
1296
|
}
|
|
1296
1297
|
|
|
1298
|
+
function launcherRuntimeRoot(version) {
|
|
1299
|
+
const base = process.env.EGREGORE_LAUNCHER_RUNTIME_DIR
|
|
1300
|
+
|| path.join(os.homedir(), ".egregore", "launcher-runtime");
|
|
1301
|
+
return version ? path.join(base, version) : null;
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
function launcherRuntimeBridge(version) {
|
|
1305
|
+
const root = launcherRuntimeRoot(version);
|
|
1306
|
+
return root ? path.join(root, "claude", "bin", "runtime-upgrade.sh") : null;
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
// The PATH launcher is a copied standalone file, not an npm shim, so it cannot
|
|
1310
|
+
// reach runtime/ through __dirname after installation. Keep the matching
|
|
1311
|
+
// upgrade engine in a versioned per-user directory. This is deliberately
|
|
1312
|
+
// outside registered Egregore checkouts: refreshing the global CLI must never
|
|
1313
|
+
// spray framework files into an instance or dirty its Git working tree.
|
|
1314
|
+
function installLauncherRuntime(version, ui) {
|
|
1315
|
+
const destination = launcherRuntimeRoot(version);
|
|
1316
|
+
const bridge = launcherRuntimeBridge(version);
|
|
1317
|
+
if (!destination || !fs.existsSync(LAUNCHER_RUNTIME_TEMPLATE)) return null;
|
|
1318
|
+
if (bridge && fs.existsSync(bridge)) return bridge;
|
|
1319
|
+
|
|
1320
|
+
const parent = path.dirname(destination);
|
|
1321
|
+
const temporary = path.join(parent, `.${path.basename(destination)}.tmp-${process.pid}`);
|
|
1322
|
+
try {
|
|
1323
|
+
fs.mkdirSync(parent, { recursive: true, mode: 0o700 });
|
|
1324
|
+
fs.rmSync(temporary, { recursive: true, force: true });
|
|
1325
|
+
fs.mkdirSync(temporary, { recursive: true, mode: 0o700 });
|
|
1326
|
+
fs.cpSync(LAUNCHER_RUNTIME_TEMPLATE, path.join(temporary, "claude"), {
|
|
1327
|
+
recursive: true,
|
|
1328
|
+
dereference: false,
|
|
1329
|
+
});
|
|
1330
|
+
const stagedBridge = path.join(temporary, "claude", "bin", "runtime-upgrade.sh");
|
|
1331
|
+
if (!fs.existsSync(stagedBridge)) throw new Error("packaged runtime-upgrade.sh is missing");
|
|
1332
|
+
fs.rmSync(destination, { recursive: true, force: true });
|
|
1333
|
+
fs.renameSync(temporary, destination);
|
|
1334
|
+
return bridge;
|
|
1335
|
+
} catch (err) {
|
|
1336
|
+
try { fs.rmSync(temporary, { recursive: true, force: true }); } catch {}
|
|
1337
|
+
ui.warn(`Could not install the Runtime update engine: ${err.message}`);
|
|
1338
|
+
return null;
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1297
1342
|
// Parse a semver from a version-stamp comment. Accepts both styles so we can
|
|
1298
1343
|
// upgrade a bash launcher to the Node launcher across a major line:
|
|
1299
1344
|
// # egregore-launcher-version: X.Y.Z (bash)
|
|
@@ -1400,6 +1445,8 @@ function appendLocalBinToRc(ui) {
|
|
|
1400
1445
|
*/
|
|
1401
1446
|
async function installLauncherBinary(egregoreDir, ui) {
|
|
1402
1447
|
const template = readLauncherTemplate();
|
|
1448
|
+
const templateVer = launcherVersion(template);
|
|
1449
|
+
const packagedBridge = installLauncherRuntime(templateVer, ui);
|
|
1403
1450
|
// Tests, containers, and managed installations can pin the launcher to an
|
|
1404
1451
|
// isolated directory. When explicitly configured, never fall through to a
|
|
1405
1452
|
// machine-wide path: isolation is the reason the override exists.
|
|
@@ -1444,7 +1491,7 @@ async function installLauncherBinary(egregoreDir, ui) {
|
|
|
1444
1491
|
try { fs.chmodSync(binaryPath, 0o755); } catch {}
|
|
1445
1492
|
if (chosen.location === "user" && !dirOnPath(chosen.dir)) appendLocalBinToRc(ui);
|
|
1446
1493
|
removeShadowingEgregoreAliases(ui);
|
|
1447
|
-
return { aliasName: "egregore", binaryPath, location: chosen.location, replaced: false };
|
|
1494
|
+
return { aliasName: "egregore", binaryPath, location: chosen.location, replaced: false, runtimeBridgePath: packagedBridge };
|
|
1448
1495
|
}
|
|
1449
1496
|
|
|
1450
1497
|
// Different content — decide upgrade vs preserve via version stamps.
|
|
@@ -1458,18 +1505,18 @@ async function installLauncherBinary(egregoreDir, ui) {
|
|
|
1458
1505
|
ui.success(`Upgraded ${ui.dim("egregore")} at ${binaryPath} ${ui.dim(`(${existingVer} → ${templateVer})`)}`);
|
|
1459
1506
|
if (chosen.location === "user" && !dirOnPath(chosen.dir)) appendLocalBinToRc(ui);
|
|
1460
1507
|
removeShadowingEgregoreAliases(ui);
|
|
1461
|
-
return { aliasName: "egregore", binaryPath, location: chosen.location, replaced: true };
|
|
1508
|
+
return { aliasName: "egregore", binaryPath, location: chosen.location, replaced: true, runtimeBridgePath: packagedBridge };
|
|
1462
1509
|
}
|
|
1463
1510
|
if (compareVersions(templateVer, existingVer) < 0) {
|
|
1464
1511
|
// Template is older — don't downgrade. Warn lightly.
|
|
1465
1512
|
ui.warn(`Existing ${binaryPath} (v${existingVer}) is newer than this installer's template (v${templateVer}) — leaving it alone.`);
|
|
1466
1513
|
removeShadowingEgregoreAliases(ui);
|
|
1467
|
-
return { aliasName: "egregore", binaryPath, location: chosen.location, replaced: false };
|
|
1514
|
+
return { aliasName: "egregore", binaryPath, location: chosen.location, replaced: false, runtimeBridgePath: launcherRuntimeBridge(existingVer) };
|
|
1468
1515
|
}
|
|
1469
1516
|
// Equal stamps but different content → user edited the file. Don't clobber.
|
|
1470
1517
|
ui.warn(`Existing ${binaryPath} has been edited locally — not overwriting.`);
|
|
1471
1518
|
ui.info(` To reset: cp ${currentTemplatePath()} ${binaryPath}`);
|
|
1472
|
-
return { aliasName: "egregore", binaryPath, location: chosen.location, replaced: false };
|
|
1519
|
+
return { aliasName: "egregore", binaryPath, location: chosen.location, replaced: false, runtimeBridgePath: packagedBridge };
|
|
1473
1520
|
}
|
|
1474
1521
|
|
|
1475
1522
|
// No version stamp on existing. If it looks like one of OUR old
|
|
@@ -1485,11 +1532,11 @@ async function installLauncherBinary(egregoreDir, ui) {
|
|
|
1485
1532
|
ui.success(`Upgraded ${ui.dim("egregore")} at ${binaryPath} ${ui.dim(`(pre-0.4.1 → ${templateVer})`)}`);
|
|
1486
1533
|
if (chosen.location === "user" && !dirOnPath(chosen.dir)) appendLocalBinToRc(ui);
|
|
1487
1534
|
removeShadowingEgregoreAliases(ui);
|
|
1488
|
-
return { aliasName: "egregore", binaryPath, location: chosen.location, replaced: true };
|
|
1535
|
+
return { aliasName: "egregore", binaryPath, location: chosen.location, replaced: true, runtimeBridgePath: packagedBridge };
|
|
1489
1536
|
}
|
|
1490
1537
|
ui.warn(`Existing ${binaryPath} doesn't look like an egregore launcher — not overwriting.`);
|
|
1491
1538
|
ui.info(` If you meant to install: rm ${binaryPath} and re-run create-egregore.`);
|
|
1492
|
-
return { aliasName: "egregore", binaryPath, location: chosen.location, replaced: false };
|
|
1539
|
+
return { aliasName: "egregore", binaryPath, location: chosen.location, replaced: false, runtimeBridgePath: null };
|
|
1493
1540
|
} catch {
|
|
1494
1541
|
// Fall through to write
|
|
1495
1542
|
}
|
|
@@ -1509,7 +1556,7 @@ async function installLauncherBinary(egregoreDir, ui) {
|
|
|
1509
1556
|
|
|
1510
1557
|
removeShadowingEgregoreAliases(ui);
|
|
1511
1558
|
ui.success(`Installed ${ui.dim("egregore")} at ${binaryPath}`);
|
|
1512
|
-
return { aliasName: "egregore", binaryPath, location: chosen.location, replaced };
|
|
1559
|
+
return { aliasName: "egregore", binaryPath, location: chosen.location, replaced, runtimeBridgePath: packagedBridge };
|
|
1513
1560
|
}
|
|
1514
1561
|
|
|
1515
1562
|
// Strip legacy `alias egregore=...` lines from common shell rc files. These
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-egregore",
|
|
3
|
-
"version": "0.21.0-runtime-mvp.
|
|
3
|
+
"version": "0.21.0-runtime-mvp.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": {
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
# memory files right after a clean search), so this hook enforces it.
|
|
9
9
|
#
|
|
10
10
|
# Scope: a retrieval episode. The guard engages only while this session's
|
|
11
|
-
#
|
|
12
|
-
# minutes, default 15). Outside an episode — editing a decision record, a skill
|
|
11
|
+
# retrieval episode is fresh (a search or open within the last
|
|
12
|
+
# EGREGORE_RETRIEVAL_WINDOW_MIN minutes, default 15). Outside an episode — editing a decision record, a skill
|
|
13
13
|
# reading memory, ordinary work — memory reads are untouched. Edit and Write
|
|
14
14
|
# are never the guard's business. Searching memory with grep passes.
|
|
15
15
|
#
|
|
@@ -26,10 +26,14 @@ case "$TOOL" in Bash|Read) ;; *) exit 0 ;; esac
|
|
|
26
26
|
# --- Retrieval episode: this session's packet exists and is fresh ---------
|
|
27
27
|
SESSION=$(cat "$PROJECT_DIR/.egregore-session-id" 2>/dev/null | tr -cd 'A-Za-z0-9_.-')
|
|
28
28
|
[ -n "$SESSION" ] || exit 0
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
# The packet itself is consumed by the PostToolUse hook the moment it is
|
|
30
|
+
# attached, so the episode is recorded by a marker that bin/search.sh and the
|
|
31
|
+
# PostToolUse hook both touch. The packet is checked too, for the window
|
|
32
|
+
# between its write and its consumption.
|
|
33
|
+
CTX_DIR="${TMPDIR:-/tmp}/egregore-retrieval-context"
|
|
31
34
|
WINDOW_MIN="${EGREGORE_RETRIEVAL_WINDOW_MIN:-15}"
|
|
32
|
-
[ -n "$(find "$
|
|
35
|
+
_fresh() { [ -f "$1" ] && [ -n "$(find "$1" -mmin "-$WINDOW_MIN" 2>/dev/null)" ]; }
|
|
36
|
+
_fresh "$CTX_DIR/$SESSION.episode" || _fresh "$CTX_DIR/$SESSION.context" || exit 0
|
|
33
37
|
|
|
34
38
|
_block() {
|
|
35
39
|
printf 'Egregore: memory sources are opened through the private evidence packet, not printed into the transcript.\n' >&2
|
|
@@ -39,5 +39,8 @@ jq -Rs '{
|
|
|
39
39
|
)
|
|
40
40
|
}
|
|
41
41
|
}' "$PACKET"
|
|
42
|
+
# The packet is one-shot, but the retrieval episode is not: leave a marker so
|
|
43
|
+
# memory-read-guard.sh keeps routing raw memory reads through the opener.
|
|
44
|
+
touch "${PACKET%.context}.episode" 2>/dev/null || true
|
|
42
45
|
rm -f "$PACKET"
|
|
43
46
|
exit 0
|
|
@@ -283,6 +283,10 @@ cmd_query() {
|
|
|
283
283
|
}
|
|
284
284
|
umask 077
|
|
285
285
|
printf '%s\n' "$out" > "$packet"
|
|
286
|
+
# The PostToolUse hook consumes the packet the moment it attaches it; the
|
|
287
|
+
# episode marker outlives that so the memory-read guard knows a retrieval
|
|
288
|
+
# just happened.
|
|
289
|
+
touch "${packet%.context}.episode" 2>/dev/null || true
|
|
286
290
|
_banner_and_emit "$label" "$t0" "$emit" "$out" 1
|
|
287
291
|
else
|
|
288
292
|
_banner_and_emit "$label" "$t0" "$emit" "$out"
|
|
@@ -343,6 +347,7 @@ case "${1:-}" in
|
|
|
343
347
|
umask 077
|
|
344
348
|
if _runtime open "$OPEN_PATH" > "$packet" 2>"$packet.err"; then
|
|
345
349
|
rm -f "$packet.err"
|
|
350
|
+
touch "${packet%.context}.episode" 2>/dev/null || true
|
|
346
351
|
echo "↳ opened $OPEN_PATH · content attached privately"
|
|
347
352
|
else
|
|
348
353
|
cat "$packet.err" >&2 2>/dev/null
|
|
@@ -283,6 +283,10 @@ cmd_query() {
|
|
|
283
283
|
}
|
|
284
284
|
umask 077
|
|
285
285
|
printf '%s\n' "$out" > "$packet"
|
|
286
|
+
# The PostToolUse hook consumes the packet the moment it attaches it; the
|
|
287
|
+
# episode marker outlives that so the memory-read guard knows a retrieval
|
|
288
|
+
# just happened.
|
|
289
|
+
touch "${packet%.context}.episode" 2>/dev/null || true
|
|
286
290
|
_banner_and_emit "$label" "$t0" "$emit" "$out" 1
|
|
287
291
|
else
|
|
288
292
|
_banner_and_emit "$label" "$t0" "$emit" "$out"
|
|
@@ -343,6 +347,7 @@ case "${1:-}" in
|
|
|
343
347
|
umask 077
|
|
344
348
|
if _runtime open "$OPEN_PATH" > "$packet" 2>"$packet.err"; then
|
|
345
349
|
rm -f "$packet.err"
|
|
350
|
+
touch "${packet%.context}.episode" 2>/dev/null || true
|
|
346
351
|
echo "↳ opened $OPEN_PATH · content attached privately"
|
|
347
352
|
else
|
|
348
353
|
cat "$packet.err" >&2 2>/dev/null
|
package/runtime/pi/bin/search.sh
CHANGED
|
@@ -283,6 +283,10 @@ cmd_query() {
|
|
|
283
283
|
}
|
|
284
284
|
umask 077
|
|
285
285
|
printf '%s\n' "$out" > "$packet"
|
|
286
|
+
# The PostToolUse hook consumes the packet the moment it attaches it; the
|
|
287
|
+
# episode marker outlives that so the memory-read guard knows a retrieval
|
|
288
|
+
# just happened.
|
|
289
|
+
touch "${packet%.context}.episode" 2>/dev/null || true
|
|
286
290
|
_banner_and_emit "$label" "$t0" "$emit" "$out" 1
|
|
287
291
|
else
|
|
288
292
|
_banner_and_emit "$label" "$t0" "$emit" "$out"
|
|
@@ -343,6 +347,7 @@ case "${1:-}" in
|
|
|
343
347
|
umask 077
|
|
344
348
|
if _runtime open "$OPEN_PATH" > "$packet" 2>"$packet.err"; then
|
|
345
349
|
rm -f "$packet.err"
|
|
350
|
+
touch "${packet%.context}.episode" 2>/dev/null || true
|
|
346
351
|
echo "↳ opened $OPEN_PATH · content attached privately"
|
|
347
352
|
else
|
|
348
353
|
cat "$packet.err" >&2 2>/dev/null
|
|
@@ -283,6 +283,10 @@ cmd_query() {
|
|
|
283
283
|
}
|
|
284
284
|
umask 077
|
|
285
285
|
printf '%s\n' "$out" > "$packet"
|
|
286
|
+
# The PostToolUse hook consumes the packet the moment it attaches it; the
|
|
287
|
+
# episode marker outlives that so the memory-read guard knows a retrieval
|
|
288
|
+
# just happened.
|
|
289
|
+
touch "${packet%.context}.episode" 2>/dev/null || true
|
|
286
290
|
_banner_and_emit "$label" "$t0" "$emit" "$out" 1
|
|
287
291
|
else
|
|
288
292
|
_banner_and_emit "$label" "$t0" "$emit" "$out"
|
|
@@ -343,6 +347,7 @@ case "${1:-}" in
|
|
|
343
347
|
umask 077
|
|
344
348
|
if _runtime open "$OPEN_PATH" > "$packet" 2>"$packet.err"; then
|
|
345
349
|
rm -f "$packet.err"
|
|
350
|
+
touch "${packet%.context}.episode" 2>/dev/null || true
|
|
346
351
|
echo "↳ opened $OPEN_PATH · content attached privately"
|
|
347
352
|
else
|
|
348
353
|
cat "$packet.err" >&2 2>/dev/null
|