create-egregore 0.6.2 → 0.7.1

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/lib/auth.js CHANGED
@@ -91,12 +91,15 @@ async function deviceFlow(ui) {
91
91
  // 2. Show code + open browser
92
92
  const copied = copyToClipboard(user_code);
93
93
  ui.info("");
94
+ ui.info("A GitHub page will open. Just click Authorize — you'll choose");
95
+ ui.info("where to set up Egregore in the next step.");
96
+ ui.info("");
94
97
  if (copied) {
95
98
  ui.info(`Code copied to clipboard: ${ui.bold(user_code)}`);
96
99
  } else {
97
100
  ui.info(`Your code: ${ui.bold(user_code)}`);
98
101
  }
99
- ui.info("Opening browser — paste the code and authorize.");
102
+ ui.info("Opening browser...");
100
103
  ui.info("");
101
104
  openBrowser(verifyUrl);
102
105
 
package/lib/local.js CHANGED
@@ -104,8 +104,9 @@ async function putFileContent(token, owner, repo, filePath, content, message) {
104
104
  return ghApi("PUT", `/repos/${owner}/${repo}/contents/${filePath}`, token, body);
105
105
  }
106
106
 
107
- async function listOrgRepos(token, org) {
108
- const { status, data } = await ghApi("GET", `/orgs/${org}/repos?per_page=100&sort=updated`, token);
107
+ async function listOrgRepos(token, owner, isOrg) {
108
+ const apiPath = isOrg ? `/orgs/${owner}/repos?per_page=100&sort=updated` : `/users/${owner}/repos?per_page=100&sort=updated&type=owner`;
109
+ const { status, data } = await ghApi("GET", apiPath, token);
109
110
  if (status !== 200 || !Array.isArray(data)) return [];
110
111
  return data
111
112
  .filter((r) => !r.archived && r.name !== "egregore" && r.name !== "egregore-core" && !r.name.endsWith("-memory"))
@@ -180,14 +181,7 @@ async function localFounderFlow(ui) {
180
181
  // 2. Get user info
181
182
  const user = await getUser(githubToken);
182
183
 
183
- // 3. "What do you want to do?"
184
- const action = await ui.choose("What do you want to do?", [
185
- { label: "Start a new project", description: "Create repos and shared memory" },
186
- { label: "Connect existing repos", description: "Set up Egregore for an org you manage" },
187
- ]);
188
- const isNewProject = action.label.startsWith("Start");
189
-
190
- // 4. Detect GitHub org
184
+ // 3. Where — always show picker (personal first)
191
185
  console.log("");
192
186
  const s = ui.spinner("Checking your GitHub accounts...");
193
187
  const orgs = await listOrgs(githubToken);
@@ -195,29 +189,31 @@ async function localFounderFlow(ui) {
195
189
 
196
190
  let githubOrg, isOrg;
197
191
  if (orgs.length === 0) {
198
- // No orgs — use personal account
199
192
  githubOrg = user.login;
200
193
  isOrg = false;
201
194
  ui.info(`Using personal account: ${ui.bold(githubOrg)}`);
202
- } else if (orgs.length === 1) {
203
- // One org — use it automatically
204
- githubOrg = orgs[0].login;
205
- isOrg = true;
206
- ui.info(`Using organization: ${ui.bold(githubOrg)}`);
207
195
  } else {
208
- // Multiple orgs — picker
209
- const orgChoices = orgs.map((o) => ({ label: o.login, description: "Organization" }));
210
- orgChoices.push({ label: user.login, description: "Personal account" });
211
- const orgChoice = await ui.choose("Which GitHub account?", orgChoices);
196
+ const orgChoices = [{ label: user.login, description: "Personal account" }];
197
+ for (const o of orgs) {
198
+ orgChoices.push({ label: o.login, description: "Organization" });
199
+ }
200
+ const orgChoice = await ui.choose("Where do you want to set up Egregore?", orgChoices);
212
201
  githubOrg = orgChoice.label;
213
202
  isOrg = githubOrg !== user.login;
214
203
  }
215
204
 
205
+ // 4. What — new or existing?
206
+ const action = await ui.choose("What are you working on?", [
207
+ { label: "Starting something new", description: "Create a project from scratch" },
208
+ { label: "Existing project", description: "Connect repos I already have" },
209
+ ]);
210
+ const isNewProject = action.label.startsWith("Starting");
211
+
216
212
  // 5. Collect project info
217
213
  let orgName, description, selectedRepos = [], newProjectRepo = null;
218
214
 
219
215
  if (isNewProject) {
220
- const nameInput = await ui.prompt("What's your team called?");
216
+ const nameInput = await ui.prompt("What's your team or project called?");
221
217
  orgName = nameInput || githubOrg;
222
218
 
223
219
  const descInput = await ui.prompt("What are you building? (one line)");
@@ -225,44 +221,32 @@ async function localFounderFlow(ui) {
225
221
 
226
222
  // Ask about project repo
227
223
  console.log("");
228
- const projectAction = await ui.choose("Do you have a project repo?", [
229
- { label: "Create a new repo", description: "Start fresh — we'll create it on GitHub" },
230
- { label: "Connect an existing repo", description: "I already have code" },
231
- { label: "Skip for now", description: "I'll add projects later" },
232
- ]);
233
-
234
- if (projectAction.label.startsWith("Create")) {
235
- const projectName = await ui.prompt("Repo name:");
236
- if (projectName) {
237
- newProjectRepo = projectName.trim().toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
238
- }
239
- } else if (projectAction.label.startsWith("Connect")) {
240
- const s0 = ui.spinner("Loading repos...");
241
- try {
242
- const repos = await listOrgRepos(githubToken, githubOrg);
243
- s0.stop("Loaded repos");
244
- if (repos.length > 0) {
245
- selectedRepos = await ui.multiSelect("Which repos should Egregore manage?", repos);
246
- } else {
247
- ui.info("No repos found in this org yet.");
248
- }
249
- } catch {
250
- s0.stop("Could not load repos");
251
- }
224
+ ui.info("Egregore creates its own repo for shared memory and config.");
225
+ ui.info("Want to also create a repo for your actual project code?");
226
+ console.log("");
227
+ const projectName = await ui.prompt("Project repo name (Enter to skip):");
228
+ if (projectName) {
229
+ newProjectRepo = projectName.trim().toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
252
230
  }
253
231
  } else {
254
- // "Connect existing repos"
255
- const nameInput = await ui.prompt(`Display name [${githubOrg}]:`);
232
+ // "Existing project"
233
+ const nameInput = await ui.prompt(`What should we call this Egregore? [${githubOrg}]:`);
256
234
  orgName = nameInput || githubOrg;
257
235
  description = "";
258
236
 
259
237
  // Multi-select repos to manage
238
+ const s0 = ui.spinner("Loading repos...");
260
239
  try {
261
- const repos = await listOrgRepos(githubToken, githubOrg);
240
+ const repos = await listOrgRepos(githubToken, githubOrg, isOrg);
241
+ s0.stop("Loaded repos");
262
242
  if (repos.length > 0) {
263
243
  selectedRepos = await ui.multiSelect("Which repos should Egregore manage?", repos);
244
+ } else {
245
+ ui.info("No repos found yet.");
264
246
  }
265
- } catch {}
247
+ } catch {
248
+ s0.stop("Could not load repos");
249
+ }
266
250
  }
267
251
 
268
252
  // 6. Derive repo names from team name
@@ -507,46 +491,43 @@ async function localFounderFlow(ui) {
507
491
 
508
492
  // 19. Optional invite
509
493
  console.log("");
510
- const inviteAnswer = await ui.prompt("Know someone who'd find this useful? [y/N]:");
511
- if (inviteAnswer && inviteAnswer.toLowerCase() === "y") {
512
- const invUsername = await ui.prompt("GitHub username:");
513
- if (invUsername) {
514
- const invSpin = ui.spinner(`Inviting ${invUsername}...`);
515
- try {
516
- // Add as collaborator to all repos
517
- await addCollaborator(githubToken, githubOrg, repoName, invUsername);
518
- await addCollaborator(githubToken, githubOrg, memoryRepoName, invUsername);
519
- for (const mr of selectedRepos) {
520
- await addCollaborator(githubToken, githubOrg, mr, invUsername).catch(() => {});
521
- }
522
-
523
- // Create person file
524
- const invPersonContent = [
525
- "---",
526
- `name: ${invUsername}`,
527
- `github: ${invUsername}`,
528
- `invited_by: ${user.login}`,
529
- `joined: ${today}`,
530
- "---",
531
- "",
532
- ].join("\n");
533
- const invPersonFile = path.join(memoryDir, "people", `${invUsername}.md`);
534
- if (!fs.existsSync(invPersonFile)) {
535
- fs.writeFileSync(invPersonFile, invPersonContent);
536
- try {
537
- run("git add -A", { cwd: memoryDir });
538
- run(`git commit -m "Invite ${invUsername}"`, { cwd: memoryDir });
539
- run("git push", { cwd: memoryDir });
540
- } catch {}
541
- }
494
+ const invUsername = await ui.prompt("Invite a teammate? Enter their GitHub username (or press Enter to skip):");
495
+ if (invUsername) {
496
+ const invSpin = ui.spinner(`Inviting ${invUsername}...`);
497
+ try {
498
+ // Add as collaborator to all repos
499
+ await addCollaborator(githubToken, githubOrg, repoName, invUsername);
500
+ await addCollaborator(githubToken, githubOrg, memoryRepoName, invUsername);
501
+ for (const mr of selectedRepos) {
502
+ await addCollaborator(githubToken, githubOrg, mr, invUsername).catch(() => {});
503
+ }
542
504
 
543
- invSpin.stop(`Invited ${invUsername}`);
544
- console.log("");
545
- ui.info(`Tell them to run:`);
546
- ui.info(` ${ui.bold(`npx create-egregore join ${githubOrg}/${repoName}`)}`);
547
- } catch (err) {
548
- invSpin.fail(`Could not invite ${invUsername}: ${err.message}`);
505
+ // Create person file
506
+ const invPersonContent = [
507
+ "---",
508
+ `name: ${invUsername}`,
509
+ `github: ${invUsername}`,
510
+ `invited_by: ${user.login}`,
511
+ `joined: ${today}`,
512
+ "---",
513
+ "",
514
+ ].join("\n");
515
+ const invPersonFile = path.join(memoryDir, "people", `${invUsername}.md`);
516
+ if (!fs.existsSync(invPersonFile)) {
517
+ fs.writeFileSync(invPersonFile, invPersonContent);
518
+ try {
519
+ run("git add -A", { cwd: memoryDir });
520
+ run(`git commit -m "Invite ${invUsername}"`, { cwd: memoryDir });
521
+ run("git push", { cwd: memoryDir });
522
+ } catch {}
549
523
  }
524
+
525
+ invSpin.stop(`Invited ${invUsername}`);
526
+ console.log("");
527
+ ui.info(`Tell them to run:`);
528
+ ui.info(` ${ui.bold(`npx create-egregore@latest join ${githubOrg}/${repoName}`)}`);
529
+ } catch (err) {
530
+ invSpin.fail(`Could not invite ${invUsername}: ${err.message}`);
550
531
  }
551
532
  }
552
533
 
package/lib/setup.js CHANGED
@@ -357,7 +357,7 @@ async function installShellAlias(egregoreDir, ui) {
357
357
  if (fs.existsSync(configPath)) {
358
358
  try { slug = JSON.parse(fs.readFileSync(configPath, "utf-8")).slug || ""; } catch {}
359
359
  }
360
- defaultName = slug ? `egregore-${slug}` : "egregore-2";
360
+ defaultName = slug || "egregore-2";
361
361
  }
362
362
 
363
363
  // Ask user (skip prompt in non-interactive mode)
package/lib/ui.js CHANGED
@@ -69,11 +69,11 @@ function spinner(msg) {
69
69
  return {
70
70
  stop(result) {
71
71
  clearInterval(interval);
72
- process.stdout.write(`\r ${GREEN}✓${RESET} ${result || msg}\n`);
72
+ process.stdout.write(`\r\x1b[2K ${GREEN}✓${RESET} ${result || msg}\n`);
73
73
  },
74
74
  fail(result) {
75
75
  clearInterval(interval);
76
- process.stdout.write(`\r ${RED}✗${RESET} ${result || msg}\n`);
76
+ process.stdout.write(`\r\x1b[2K ${RED}✗${RESET} ${result || msg}\n`);
77
77
  },
78
78
  };
79
79
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-egregore",
3
- "version": "0.6.2",
3
+ "version": "0.7.1",
4
4
  "description": "Set up Egregore for your team in one command",
5
5
  "license": "MIT",
6
6
  "bin": {