create-egregore 0.19.8 → 0.19.12

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.
Files changed (92) hide show
  1. package/assets/egregore-launcher.js +266 -4
  2. package/lib/local.js +11 -1
  3. package/lib/setup.js +33 -0
  4. package/package.json +2 -2
  5. package/runtime/codex/.codex/hooks/branch-guard.js +242 -34
  6. package/runtime/codex/.codex/skills/activity/SKILL.md +18 -6
  7. package/runtime/codex/.codex/skills/deep-reflect/SKILL.md +90 -55
  8. package/runtime/codex/.codex/skills/deploy-bridge/SKILL.md +25 -0
  9. package/runtime/codex/.codex/skills/handoff/SKILL.md +6 -2
  10. package/runtime/codex/.codex/skills/save/SKILL.md +29 -5
  11. package/runtime/codex/.codex/skills/scroll/SKILL.md +54 -3
  12. package/runtime/codex/.codex/skills/task/SKILL.md +25 -0
  13. package/runtime/codex/.codex/skills/tui-design/SKILL.md +1 -1
  14. package/runtime/codex/.codex/skills/wrap/SKILL.md +4 -1
  15. package/runtime/codex/.codex/spec-manifest.json +19 -7
  16. package/runtime/codex/AGENTS.md +63 -19
  17. package/runtime/codex/bin/activity-data.sh +1 -1
  18. package/runtime/codex/bin/agent.sh +88 -25
  19. package/runtime/codex/bin/attendant.sh +525 -0
  20. package/runtime/codex/bin/capture-reconcile.sh +67 -0
  21. package/runtime/codex/bin/capture-run.sh +341 -0
  22. package/runtime/codex/bin/codex-render-spec.mjs +29 -14
  23. package/runtime/codex/bin/codex-session-start.sh +49 -4
  24. package/runtime/codex/bin/codex-skill-render.mjs +23 -4
  25. package/runtime/codex/bin/graph-op.sh +719 -49
  26. package/runtime/codex/bin/graph-wal.sh +3 -2
  27. package/runtime/codex/bin/handoff-run.sh +72 -0
  28. package/runtime/codex/bin/handoff-save-egregore.sh +51 -11
  29. package/runtime/codex/bin/index-handoff.sh +26 -17
  30. package/runtime/codex/bin/ingest-graph.sh +80 -0
  31. package/runtime/codex/bin/ingest-surface.html +429 -0
  32. package/runtime/codex/bin/ingest.py +614 -0
  33. package/runtime/codex/bin/ingest.sh +231 -0
  34. package/runtime/codex/bin/ingest_graph.py +776 -0
  35. package/runtime/codex/bin/ingest_surface.py +464 -0
  36. package/runtime/codex/bin/lib/git-sync.sh +2 -6
  37. package/runtime/codex/bin/lib/greeting.sh +157 -22
  38. package/runtime/codex/bin/lib/identity.sh +50 -29
  39. package/runtime/codex/bin/lib/noncode.sh +31 -0
  40. package/runtime/codex/bin/person.py +610 -0
  41. package/runtime/codex/bin/person.sh +448 -0
  42. package/runtime/codex/bin/session-autosave.sh +184 -0
  43. package/runtime/codex/bin/session-log.sh +199 -0
  44. package/runtime/codex/bin/settings.sh +1 -1
  45. package/runtime/codex/manifest.json +19 -1
  46. package/runtime/pi/.codex/hooks/branch-guard.js +242 -34
  47. package/runtime/pi/.codex/skills/activity/SKILL.md +18 -6
  48. package/runtime/pi/.codex/skills/deep-reflect/SKILL.md +90 -55
  49. package/runtime/pi/.codex/skills/deploy-bridge/SKILL.md +25 -0
  50. package/runtime/pi/.codex/skills/handoff/SKILL.md +6 -2
  51. package/runtime/pi/.codex/skills/save/SKILL.md +29 -5
  52. package/runtime/pi/.codex/skills/scroll/SKILL.md +54 -3
  53. package/runtime/pi/.codex/skills/task/SKILL.md +25 -0
  54. package/runtime/pi/.codex/skills/tui-design/SKILL.md +1 -1
  55. package/runtime/pi/.codex/skills/wrap/SKILL.md +4 -1
  56. package/runtime/pi/.codex/spec-manifest.json +19 -7
  57. package/runtime/pi/.pi/APPEND_SYSTEM.md +57 -14
  58. package/runtime/pi/.pi/extensions/egregore.ts +98 -8
  59. package/runtime/pi/.pi/spec-manifest.json +2 -2
  60. package/runtime/pi/AGENTS.md +63 -19
  61. package/runtime/pi/bin/activity-data.sh +1 -1
  62. package/runtime/pi/bin/agent.sh +88 -25
  63. package/runtime/pi/bin/attendant.sh +525 -0
  64. package/runtime/pi/bin/capture-reconcile.sh +67 -0
  65. package/runtime/pi/bin/capture-run.sh +341 -0
  66. package/runtime/pi/bin/codex-render-spec.mjs +29 -14
  67. package/runtime/pi/bin/codex-session-start.sh +49 -4
  68. package/runtime/pi/bin/codex-skill-render.mjs +23 -4
  69. package/runtime/pi/bin/graph-op.sh +719 -49
  70. package/runtime/pi/bin/graph-wal.sh +3 -2
  71. package/runtime/pi/bin/handoff-run.sh +72 -0
  72. package/runtime/pi/bin/handoff-save-egregore.sh +51 -11
  73. package/runtime/pi/bin/index-handoff.sh +26 -17
  74. package/runtime/pi/bin/ingest-graph.sh +80 -0
  75. package/runtime/pi/bin/ingest-surface.html +429 -0
  76. package/runtime/pi/bin/ingest.py +614 -0
  77. package/runtime/pi/bin/ingest.sh +231 -0
  78. package/runtime/pi/bin/ingest_graph.py +776 -0
  79. package/runtime/pi/bin/ingest_surface.py +464 -0
  80. package/runtime/pi/bin/lib/git-sync.sh +2 -6
  81. package/runtime/pi/bin/lib/greeting.sh +157 -22
  82. package/runtime/pi/bin/lib/identity.sh +50 -29
  83. package/runtime/pi/bin/lib/noncode.sh +31 -0
  84. package/runtime/pi/bin/person.py +610 -0
  85. package/runtime/pi/bin/person.sh +448 -0
  86. package/runtime/pi/bin/pi-branch-policy.mjs +164 -4
  87. package/runtime/pi/bin/pi-product-workflows.mjs +31 -2
  88. package/runtime/pi/bin/publish-artifact.sh +14 -0
  89. package/runtime/pi/bin/session-autosave.sh +184 -0
  90. package/runtime/pi/bin/session-log.sh +199 -0
  91. package/runtime/pi/bin/settings.sh +274 -0
  92. package/runtime/pi/manifest.json +20 -1
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // egregore-launcher-version: 0.7.3
2
+ // egregore-launcher-version: 0.8.0
3
3
  //
4
4
  // egregore — launch and browse Egregore instances from your terminal.
5
5
  // Installed by create-egregore. Reads ~/.egregore/instances.json.
@@ -13,6 +13,7 @@
13
13
  // egregore <slug|name> pick a harness for the matching instance
14
14
  // egregore ls | list list instances (non-interactive table)
15
15
  // egregore use <slug> set the default for bare `egregore`
16
+ // egregore connect [slug] activate Connected for an existing instance
16
17
  // egregore /invite <user> invite a GitHub user to an instance
17
18
  // egregore settings [slug] hosting on/off, managed repos, admins (s in browser)
18
19
  // egregore prune [--dry-run] remove entries whose path is gone
@@ -28,13 +29,18 @@ const path = require("node:path");
28
29
  const os = require("node:os");
29
30
  const http = require("node:http");
30
31
  const https = require("node:https");
32
+ const crypto = require("node:crypto");
31
33
  const { spawnSync, spawn } = require("node:child_process");
32
34
  const readline = require("node:readline");
33
35
 
34
- const VERSION = "0.7.3";
36
+ const VERSION = "0.8.0";
35
37
  const REGISTRY = path.join(os.homedir(), ".egregore", "instances.json");
36
38
  const DEFAULT_FILE = path.join(os.homedir(), ".egregore", "default");
37
39
  const PUBLIC_NPX_PACKAGE = "create-egregore";
40
+ const CONNECT_API_URL = (
41
+ process.env.EGREGORE_CONNECT_API_URL ||
42
+ "https://egregore-production-55f2.up.railway.app"
43
+ ).replace(/\/+$/, "");
38
44
 
39
45
  const NATIVE_CODEX_SKILLS = [
40
46
  "activity",
@@ -864,6 +870,7 @@ function cmdHelp() {
864
870
  ` ${CYAN}egregore browse${RESET} force the full-screen browser`,
865
871
  ` ${CYAN}egregore ls${RESET} ${DIM}| list${RESET} list installed instances`,
866
872
  ` ${CYAN}egregore use${RESET} ${DIM}<slug>${RESET} set the default for bare ${BOLD}egregore${RESET}`,
873
+ ` ${CYAN}egregore connect${RESET} ${DIM}[slug]${RESET} activate Connected for an existing instance`,
867
874
  ` ${CYAN}egregore /invite${RESET} ${DIM}<user> [slug]${RESET} invite a GitHub user`,
868
875
  ` ${CYAN}egregore settings${RESET} ${DIM}[slug]${RESET} hosting on/off, repos, admins`,
869
876
  ` ${CYAN}egregore prune${RESET} ${DIM}[--dry-run]${RESET} remove entries with missing paths`,
@@ -874,6 +881,7 @@ function cmdHelp() {
874
881
  ` ${DIM}enter${RESET} choose selected instance`,
875
882
  ` ${DIM}/${RESET} filter (type to narrow; esc to clear)`,
876
883
  ` ${DIM}n / +${RESET} create a new Egregore`,
884
+ ` ${DIM}c${RESET} connect selected instance`,
877
885
  ` ${DIM}u${RESET} set selected as default`,
878
886
  ` ${DIM}i${RESET} invite a GitHub user to selected instance`,
879
887
  ` ${DIM}s${RESET} settings (hosting on/off, repos, admins)`,
@@ -994,6 +1002,252 @@ function apiRequest(apiUrl, apiKey, apiPath, body) {
994
1002
  }, body);
995
1003
  }
996
1004
 
1005
+ function connectedApiRequest(githubToken, method, apiPath, body, headers = {}) {
1006
+ const url = new URL(apiPath, CONNECT_API_URL + "/");
1007
+ return requestJson({
1008
+ hostname: url.hostname,
1009
+ port: url.port || undefined,
1010
+ path: `${url.pathname}${url.search}`,
1011
+ method,
1012
+ protocol: url.protocol,
1013
+ headers: {
1014
+ Authorization: `Bearer ${githubToken}`,
1015
+ ...headers,
1016
+ },
1017
+ }, body);
1018
+ }
1019
+
1020
+ function openExternalUrl(url) {
1021
+ try {
1022
+ if (process.platform === "darwin") {
1023
+ spawn("open", [url], { detached: true, stdio: "ignore" }).unref();
1024
+ } else if (process.platform === "win32") {
1025
+ spawn("cmd", ["/c", "start", "", url], { detached: true, stdio: "ignore" }).unref();
1026
+ } else {
1027
+ spawn("xdg-open", [url], { detached: true, stdio: "ignore" }).unref();
1028
+ }
1029
+ return true;
1030
+ } catch {
1031
+ return false;
1032
+ }
1033
+ }
1034
+
1035
+ function atomicWrite(file, content, mode) {
1036
+ const temp = path.join(
1037
+ path.dirname(file),
1038
+ `.${path.basename(file)}.${process.pid}.${crypto.randomBytes(4).toString("hex")}.tmp`,
1039
+ );
1040
+ fs.writeFileSync(temp, content, { mode });
1041
+ fs.renameSync(temp, file);
1042
+ }
1043
+
1044
+ function writeJsonFileAtomic(file, value) {
1045
+ atomicWrite(file, JSON.stringify(value, null, 2) + "\n", 0o644);
1046
+ }
1047
+
1048
+ function writeEnvValueAtomic(file, key, value) {
1049
+ const lines = fs.existsSync(file)
1050
+ ? fs.readFileSync(file, "utf-8").split(/\r?\n/)
1051
+ : [];
1052
+ let replaced = false;
1053
+ const next = lines
1054
+ .filter((line, index) => !(index === lines.length - 1 && line === ""))
1055
+ .map((line) => {
1056
+ if (!line.startsWith(`${key}=`)) return line;
1057
+ replaced = true;
1058
+ return `${key}=${value}`;
1059
+ });
1060
+ if (!replaced) next.push(`${key}=${value}`);
1061
+ atomicWrite(file, next.join("\n") + "\n", 0o600);
1062
+ }
1063
+
1064
+ function persistConnectState(inst, fields) {
1065
+ const file = path.join(inst.path, ".egregore-state.json");
1066
+ const state = readJsonFile(file);
1067
+ state.connected_activation = {
1068
+ ...(state.connected_activation || {}),
1069
+ ...fields,
1070
+ updated_at: new Date().toISOString(),
1071
+ };
1072
+ writeJsonFileAtomic(file, state);
1073
+ }
1074
+
1075
+ function connectError(result, fallback) {
1076
+ if (!result) return fallback;
1077
+ const data = result.data || {};
1078
+ return data.detail || data.message || (typeof data === "string" ? data : fallback);
1079
+ }
1080
+
1081
+ function sleep(ms) {
1082
+ return new Promise((resolve) => setTimeout(resolve, ms));
1083
+ }
1084
+
1085
+ async function cmdConnect(args) {
1086
+ const registry = readRegistry();
1087
+ if (registry.length === 0) die("no egregores installed yet.\n run: npx -y create-egregore@latest");
1088
+ const inst = resolveInviteInstance(registry, args[0] || "");
1089
+ if (!inst) {
1090
+ die("multiple egregores installed; choose one:\n egregore connect <slug>\n run: egregore ls");
1091
+ }
1092
+ if (!fs.existsSync(inst.path)) die(`instance path is missing: ${inst.path}`);
1093
+
1094
+ const { cfg, state, env } = configForInstance(inst);
1095
+ const githubToken = env.GITHUB_TOKEN || "";
1096
+ if (!githubToken) {
1097
+ die(`GITHUB_TOKEN is missing for ${inst.slug}.\n Add it to ${path.join(inst.path, ".env")} and retry.`);
1098
+ }
1099
+ const activation = state.connected_activation || {};
1100
+ if (cfg.mode === "connected" && activation.status === "connected") {
1101
+ console.log(`\n ${GREEN}✓${RESET} ${BOLD}${inst.slug}${RESET} is already connected.\n`);
1102
+ return;
1103
+ }
1104
+
1105
+ console.log("");
1106
+ console.log(` ${BOLD}CONNECT${RESET} ${DIM}${inst.slug}${RESET}`);
1107
+ console.log(` ${DIM}€500/month · Stripe checkout · existing Markdown and Git stay in place${RESET}`);
1108
+ console.log("");
1109
+
1110
+ let intentId = activation.intent_id || "";
1111
+ let connectUrl = activation.connect_url || "";
1112
+ if (!intentId || !["checkout_pending", "entitled", "backfilling"].includes(activation.status)) {
1113
+ console.log(` [1/5] ${DIM}Creating connection intent…${RESET}`);
1114
+ const checkout = await connectedApiRequest(
1115
+ githubToken,
1116
+ "POST",
1117
+ "/api/billing/checkout",
1118
+ {
1119
+ org_slug: cfg.slug || inst.slug,
1120
+ plan: "connect",
1121
+ instance_metadata: {
1122
+ launcher_version: VERSION,
1123
+ mode: cfg.mode || "local",
1124
+ repo_name: cfg.repo_name || "",
1125
+ managed_repo_count: Array.isArray(cfg.repos) ? cfg.repos.length : 0,
1126
+ },
1127
+ },
1128
+ { "X-Idempotency-Key": `cli:${inst.slug}:${crypto.randomUUID()}` },
1129
+ );
1130
+ if (checkout.status !== 200) {
1131
+ die(`could not start Connected checkout: ${connectError(checkout, `HTTP ${checkout.status}`)}`);
1132
+ }
1133
+ intentId = checkout.data.connection_intent_id;
1134
+ connectUrl = checkout.data.connect_url;
1135
+ persistConnectState(inst, {
1136
+ intent_id: intentId,
1137
+ checkout_session_id: checkout.data.checkout_session_id,
1138
+ connect_url: connectUrl,
1139
+ status: "checkout_pending",
1140
+ });
1141
+ console.log(` [1/5] ${GREEN}✓${RESET} Connection intent`);
1142
+ } else {
1143
+ console.log(` [1/5] ${GREEN}✓${RESET} Resuming connection intent`);
1144
+ }
1145
+
1146
+ if (activation.status !== "backfilling") {
1147
+ if (connectUrl) {
1148
+ const opened = openExternalUrl(connectUrl);
1149
+ const mark = opened ? `${GREEN}✓${RESET}` : `${YELLOW}!${RESET}`;
1150
+ console.log(` [2/5] ${mark} ${opened ? "Opened" : "Open"} Egregore Connect`);
1151
+ console.log(` ${CYAN}${connectUrl}${RESET}`);
1152
+ }
1153
+ console.log(` ${DIM}Complete checkout in the browser. Promotion codes are entered in Stripe.${RESET}`);
1154
+ } else {
1155
+ console.log(` [2/5] ${GREEN}✓${RESET} Subscription already confirmed`);
1156
+ }
1157
+
1158
+ const pollMs = Math.max(100, Number(process.env.EGREGORE_CONNECT_POLL_INTERVAL_MS || 2000));
1159
+ const timeoutMs = Math.max(pollMs, Number(process.env.EGREGORE_CONNECT_TIMEOUT_MS || 30 * 60 * 1000));
1160
+ const deadline = Date.now() + timeoutMs;
1161
+ let status;
1162
+ while (Date.now() < deadline) {
1163
+ const result = await connectedApiRequest(
1164
+ githubToken,
1165
+ "GET",
1166
+ `/api/billing/connect/${encodeURIComponent(intentId)}`,
1167
+ );
1168
+ if (result.status === 200) {
1169
+ status = result.data;
1170
+ if (
1171
+ status.entitled ||
1172
+ ["entitled", "provisioning", "backfilling", "connected"].includes(status.status)
1173
+ ) break;
1174
+ if (["canceled", "failed"].includes(status.status)) {
1175
+ persistConnectState(inst, { status: status.status });
1176
+ die(`Connected checkout ${status.status}. Your local Egregore was not changed.`);
1177
+ }
1178
+ } else if (result.status !== 0) {
1179
+ die(`could not read Connected status: ${connectError(result, `HTTP ${result.status}`)}`);
1180
+ }
1181
+ await sleep(pollMs);
1182
+ }
1183
+ if (!status || !(status.entitled || ["entitled", "provisioning", "backfilling", "connected"].includes(status.status))) {
1184
+ die("timed out waiting for checkout. Run the same command to resume.");
1185
+ }
1186
+ console.log(` [3/5] ${GREEN}✓${RESET} Subscription confirmed`);
1187
+ persistConnectState(inst, { status: "entitled" });
1188
+
1189
+ const claim = await connectedApiRequest(
1190
+ githubToken,
1191
+ "POST",
1192
+ `/api/billing/connect/${encodeURIComponent(intentId)}/claim`,
1193
+ {},
1194
+ );
1195
+ if (claim.status !== 200) {
1196
+ die(`could not activate Connected infrastructure: ${connectError(claim, `HTTP ${claim.status}`)}`);
1197
+ }
1198
+
1199
+ const configFile = path.join(inst.path, "egregore.json");
1200
+ const envFile = path.join(inst.path, ".env");
1201
+ const currentConfig = readJsonFile(configFile);
1202
+ writeJsonFileAtomic(configFile, {
1203
+ ...currentConfig,
1204
+ mode: "connected",
1205
+ api_url: claim.data.api_url,
1206
+ });
1207
+ writeEnvValueAtomic(envFile, "EGREGORE_API_KEY", claim.data.api_key);
1208
+ persistConnectState(inst, {
1209
+ status: "backfilling",
1210
+ activation_protocol: claim.data.activation_protocol,
1211
+ });
1212
+ console.log(` [4/5] ${GREEN}✓${RESET} Connected credential and graph node`);
1213
+
1214
+ const syncScript = path.join(inst.path, "bin", "sync-graph.sh");
1215
+ if (!fs.existsSync(syncScript)) {
1216
+ die("bin/sync-graph.sh is missing. Update the installed Egregore version, then run egregore connect again.");
1217
+ }
1218
+ const sync = spawnSync("bash", [syncScript], {
1219
+ cwd: inst.path,
1220
+ encoding: "utf-8",
1221
+ stdio: "pipe",
1222
+ timeout: 10 * 60 * 1000,
1223
+ });
1224
+ if (sync.status !== 0) {
1225
+ const detail = (sync.stderr || sync.stdout || "graph replay failed").trim();
1226
+ die(`Connected is configured, but graph replay did not finish.\n ${detail}\n Run egregore connect ${inst.slug} to retry; existing data is unchanged.`);
1227
+ }
1228
+ let backfillSummary = { replayed: true };
1229
+ try { backfillSummary = JSON.parse((sync.stdout || "").trim()); } catch {}
1230
+
1231
+ const complete = await connectedApiRequest(
1232
+ githubToken,
1233
+ "POST",
1234
+ `/api/billing/connect/${encodeURIComponent(intentId)}/complete`,
1235
+ { backfill_summary: backfillSummary },
1236
+ );
1237
+ if (complete.status !== 200) {
1238
+ die(`graph replay finished, but activation could not be finalized: ${connectError(complete, `HTTP ${complete.status}`)}`);
1239
+ }
1240
+ persistConnectState(inst, {
1241
+ status: "connected",
1242
+ connected_at: new Date().toISOString(),
1243
+ backfill_summary: backfillSummary,
1244
+ });
1245
+ console.log(` [5/5] ${GREEN}✓${RESET} Existing memory projected into the graph`);
1246
+ console.log("");
1247
+ console.log(` ${GREEN}✓${RESET} ${BOLD}${inst.slug}${RESET} is connected. Local files remain the source of truth.`);
1248
+ console.log("");
1249
+ }
1250
+
997
1251
  function accessLabel(status) {
998
1252
  if (status === 201) return `${GREEN}sent${RESET}`;
999
1253
  if (status === 204) return `${GREEN}already added${RESET}`;
@@ -1952,8 +2206,8 @@ async function browser(args = {}) {
1952
2206
  footer = band(`● ${stripAnsi(state.message)}`, bg);
1953
2207
  } else {
1954
2208
  footer = blockWidth < 96
1955
- ? band(`${key("↑↓", "nav")} ${key("enter", "select")} ${key("n/+", "new")} ${key("i", "invite")} ${key("s", "settings")} ${key("d", "delete")} ${key("?", "help")} ${key("q", "quit")}`, 46)
1956
- : band(`${key("↑↓", "nav")} ${key("enter", "select")} ${key("n/+", "new")} ${key("/", "filter")} ${key("i", "invite")} ${key("s", "settings")} ${key("u", "default")} ${key("d", "delete")} ${key("?", "help")} ${key("q", "quit")}`, 46);
2209
+ ? band(`${key("↑↓", "nav")} ${key("enter", "select")} ${key("n/+", "new")} ${key("c", "connect")} ${key("i", "invite")} ${key("d", "delete")} ${key("?", "help")} ${key("q", "quit")}`, 46)
2210
+ : band(`${key("↑↓", "nav")} ${key("enter", "select")} ${key("n/+", "new")} ${key("/", "filter")} ${key("c", "connect")} ${key("i", "invite")} ${key("s", "settings")} ${key("u", "default")} ${key("d", "delete")} ${key("?", "help")} ${key("q", "quit")}`, 46);
1957
2211
  }
1958
2212
  out.push(M + footer + "\n");
1959
2213
  if (state.showHelp) {
@@ -2310,6 +2564,13 @@ async function browser(args = {}) {
2310
2564
  resolve(setupNewEgregore(args.runtimeOverride));
2311
2565
  return;
2312
2566
  }
2567
+ if (chunk === "c") {
2568
+ const pick = visible()[state.cursor]?.e;
2569
+ if (!pick) return;
2570
+ cleanup();
2571
+ resolve(cmdConnect([pick.slug]));
2572
+ return;
2573
+ }
2313
2574
  if (chunk === "i") {
2314
2575
  const pick = visible()[state.cursor]?.e;
2315
2576
  if (!pick) return;
@@ -2451,6 +2712,7 @@ function main() {
2451
2712
  // Non-interactive subcommands
2452
2713
  if (cmd === "ls" || cmd === "list") return cmdLs();
2453
2714
  if (cmd === "use") return cmdUse(args[1]);
2715
+ if (cmd === "connect") return cmdConnect(args.slice(1));
2454
2716
  if (cmd === "/invite" || cmd === "invite") return cmdInvite(args.slice(1));
2455
2717
  if (cmd === "settings" || cmd === "config") return cmdSettings(args.slice(1));
2456
2718
  if (cmd === "prune") return cmdPruneNonInteractive(args[1] === "--dry-run" || args[1] === "-n");
package/lib/local.js CHANGED
@@ -47,7 +47,12 @@ const TEMPLATE_REPO = "egregore";
47
47
  async function getUser(token) {
48
48
  const { status, data } = await ghApi("GET", "/user", token);
49
49
  if (status !== 200) throw new Error("GitHub auth failed — could not fetch user");
50
- return { login: data.login, name: data.name || data.login, email: data.email || null };
50
+ return {
51
+ login: data.login,
52
+ id: data.id || null,
53
+ name: data.name || data.login,
54
+ email: data.email || null,
55
+ };
51
56
  }
52
57
 
53
58
  async function listOrgs(token) {
@@ -1286,11 +1291,14 @@ async function localFounderFlow(ui, ctx = {}) {
1286
1291
  const slugifiedName = (displayName || user.login).toLowerCase().replace(/\s+/g, "-");
1287
1292
  const state = {
1288
1293
  github_username: user.login,
1294
+ github_id: user.id,
1295
+ person_id: user.id ? `github:${user.id}` : `github-login:${user.login.toLowerCase()}`,
1289
1296
  github_name: user.name,
1290
1297
  display_name: displayName || user.login,
1291
1298
  name: slugifiedName,
1292
1299
  member_role: memberRole,
1293
1300
  email: user.email,
1301
+ emails: user.email ? [user.email.toLowerCase()] : [],
1294
1302
  onboarding_complete: false,
1295
1303
  usage_type: "founder_group",
1296
1304
  org_setup: true,
@@ -1430,6 +1438,7 @@ async function localFounderFlow(ui, ctx = {}) {
1430
1438
  const invPersonContent = [
1431
1439
  "---",
1432
1440
  `name: ${invUsername}`,
1441
+ `person_id: github-login:${invUsername.toLowerCase()}`,
1433
1442
  `github: ${invUsername}`,
1434
1443
  `invited_by: ${user.login}`,
1435
1444
  `joined: ${today}`,
@@ -1755,6 +1764,7 @@ async function localJoinFlow(orgArg, ui, ctx = {}) {
1755
1764
  org_name: orgName,
1756
1765
  github_org: orgLogin,
1757
1766
  github_username: user.login,
1767
+ github_id: user.id,
1758
1768
  github_name: user.name,
1759
1769
  github_email: user.email,
1760
1770
  slug: config.slug || orgLogin,
package/lib/setup.js CHANGED
@@ -174,12 +174,24 @@ function mergeProfileFields(existing, additions = []) {
174
174
  function personProfileContent(state = {}, opts = {}) {
175
175
  const now = opts.onboardedAt || new Date().toISOString();
176
176
  const username = state.github_username || opts.githubUsername || "unknown";
177
+ const githubId = state.github_id || opts.githubId || "";
178
+ const personId = state.person_id || (githubId ? `github:${githubId}` : `github-login:${username.toLowerCase()}`);
177
179
  const displayName = state.display_name || state.github_name || username;
178
180
  const role = state.member_role || state.onboarding?.member_role || opts.memberRole || "Member";
181
+ const emails = [...new Set(
182
+ [state.email, ...(Array.isArray(state.emails) ? state.emails : [])]
183
+ .filter(Boolean)
184
+ .map((value) => String(value).trim().toLowerCase()),
185
+ )];
179
186
  return [
180
187
  `# ${displayName}`,
181
188
  "",
189
+ `Person-ID: ${personId}`,
182
190
  `GitHub: ${username}`,
191
+ `GitHub-ID: ${githubId}`,
192
+ "GitHub-Aliases: ",
193
+ `Email: ${emails[0] || ""}`,
194
+ `Emails: ${emails.join(", ")}`,
183
195
  `Role: ${role}`,
184
196
  `Joined: ${now.split("T")[0]}`,
185
197
  `Onboarded: ${now}`,
@@ -234,6 +246,7 @@ function buildOnboardingState(existing = {}, data = {}, opts = {}) {
234
246
  const state = { ...existing };
235
247
  const preferredRuntime = normalizeRuntime(opts.runtime);
236
248
  const githubUsername = data.github_username || state.github_username || "";
249
+ const githubId = data.github_id || state.github_id || null;
237
250
  const githubName = data.github_name || state.github_name || githubUsername;
238
251
  const displayName = data.display_name || state.display_name || githubName || githubUsername;
239
252
  const usageType = normalizeUsageType(data);
@@ -248,6 +261,12 @@ function buildOnboardingState(existing = {}, data = {}, opts = {}) {
248
261
  const shouldComplete = alreadyComplete || (requestedComplete && !!memberRole);
249
262
 
250
263
  if (githubUsername) state.github_username = githubUsername;
264
+ if (githubId) {
265
+ state.github_id = githubId;
266
+ state.person_id = `github:${githubId}`;
267
+ } else if (githubUsername && !state.person_id) {
268
+ state.person_id = `github-login:${githubUsername.toLowerCase()}`;
269
+ }
251
270
  if (githubName) state.github_name = githubName;
252
271
  if (displayName) {
253
272
  state.display_name = displayName;
@@ -457,6 +476,20 @@ async function install(data, ui, targetDir, opts = {}) {
457
476
  reportPiRuntimeSummary(piRuntime, ui);
458
477
  }
459
478
 
479
+ // Reconcile the installer-captured identity through the same portable writer
480
+ // used by /me and onboarding. Failure is recoverable: the state + person
481
+ // profile already exist and the next session retries `bin/person.sh sync`.
482
+ const personScript = path.join(egregoreDir, "bin", "person.sh");
483
+ if (fs.existsSync(personScript)) {
484
+ try {
485
+ spawnSync("bash", [personScript, "onboard"], {
486
+ cwd: egregoreDir,
487
+ stdio: "ignore",
488
+ timeout: 15000,
489
+ });
490
+ } catch {}
491
+ }
492
+
460
493
  // 5. Register instance + install launcher binary
461
494
  ui.step(5, totalSteps, "Registering instance...");
462
495
  registerInstance(forkDirName, org_name, egregoreDir, preferredRuntime);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-egregore",
3
- "version": "0.19.8",
3
+ "version": "0.19.12",
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",
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",
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
  },