create-egregore 0.3.9 → 0.3.11

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 (3) hide show
  1. package/bin/cli.js +35 -9
  2. package/lib/setup.js +47 -8
  3. package/package.json +1 -1
package/bin/cli.js CHANGED
@@ -117,13 +117,22 @@ async function interactiveFlow(api) {
117
117
  // 3. Build choices
118
118
  const choices = [];
119
119
 
120
- // Orgs with egregore → join
120
+ // Orgs with egregore → show each instance to join + option to set up another
121
121
  for (const org of orgsData.orgs) {
122
122
  if (org.has_egregore) {
123
+ for (const inst of org.instances || []) {
124
+ choices.push({
125
+ label: `${org.name || org.login} — ${inst.org_name || inst.repo_name}`,
126
+ description: "Join existing",
127
+ action: "join",
128
+ login: org.login,
129
+ repo_name: inst.repo_name,
130
+ });
131
+ }
123
132
  choices.push({
124
- label: org.name || org.login,
125
- description: "Join existing",
126
- action: "join",
133
+ label: `${org.name || org.login} — new instance`,
134
+ description: "Set up another",
135
+ action: "setup",
127
136
  login: org.login,
128
137
  });
129
138
  }
@@ -141,13 +150,23 @@ async function interactiveFlow(api) {
141
150
  }
142
151
  }
143
152
 
144
- // Personal account
153
+ // Personal account — show join for each existing instance + setup for new
145
154
  if (orgsData.personal.has_egregore) {
155
+ for (const inst of orgsData.personal.instances || []) {
156
+ choices.push({
157
+ label: `${orgsData.user.login} (personal) — ${inst.org_name || inst.repo_name}`,
158
+ description: "Join existing",
159
+ action: "join",
160
+ login: orgsData.user.login,
161
+ repo_name: inst.repo_name,
162
+ });
163
+ }
146
164
  choices.push({
147
- label: `${orgsData.user.login} (personal)`,
148
- description: "Join existing",
149
- action: "join",
165
+ label: `${orgsData.user.login} (personal) — new instance`,
166
+ description: "Set up another",
167
+ action: "setup",
150
168
  login: orgsData.user.login,
169
+ is_personal: true,
151
170
  });
152
171
  } else {
153
172
  choices.push({
@@ -180,6 +199,10 @@ async function setupFlow(api, githubToken, choice) {
180
199
  const orgName = await ui.prompt(`Organization display name [${choice.login}]:`);
181
200
  const name = orgName || choice.login;
182
201
 
202
+ // Instance name — determines repo names (egregore-{instance}, {instance}-memory)
203
+ const instanceInput = await ui.prompt(`Instance name (e.g. "ops", "research") [leave blank for default]:`);
204
+ const instanceName = instanceInput || undefined;
205
+
183
206
  // Repo picker — show org repos for selection
184
207
  let selectedRepos = [];
185
208
  try {
@@ -201,6 +224,7 @@ async function setupFlow(api, githubToken, choice) {
201
224
  org_name: name,
202
225
  is_personal: choice.is_personal || false,
203
226
  repos: selectedRepos,
227
+ instance_name: instanceName,
204
228
  });
205
229
  s.stop("Setup complete on GitHub");
206
230
 
@@ -220,10 +244,12 @@ async function setupFlow(api, githubToken, choice) {
220
244
  }
221
245
 
222
246
  async function joinFlow(api, githubToken, choice) {
223
- const s = ui.spinner(`Joining ${ui.bold(choice.login)}...`);
247
+ const displayName = choice.repo_name ? `${choice.login} (${choice.repo_name})` : choice.login;
248
+ const s = ui.spinner(`Joining ${ui.bold(displayName)}...`);
224
249
  try {
225
250
  const result = await api.joinOrg(githubToken, {
226
251
  github_org: choice.login,
252
+ repo_name: choice.repo_name,
227
253
  });
228
254
  s.stop("Joined");
229
255
 
package/lib/setup.js CHANGED
@@ -20,11 +20,12 @@ function run(cmd, opts = {}) {
20
20
  * @param {string} targetDir - where to install (default: cwd)
21
21
  */
22
22
  async function install(data, ui, targetDir) {
23
- const { fork_url, memory_url, github_token, org_name, github_org, slug, api_key, repos = [], telegram_group_link } = data;
23
+ const { fork_url, memory_url, github_token, org_name, github_org, slug, api_key, repos = [], telegram_group_link, github_username, github_name } = data;
24
24
  const base = targetDir || process.cwd();
25
25
 
26
- const dirSlug = (github_org || slug || "egregore").toLowerCase();
27
- const egregoreDir = path.join(base, `egregore-${dirSlug}`);
26
+ // Directory name from fork URL (what git clone would use), e.g. "egregore-core"
27
+ const forkDirName = fork_url.split("/").pop().replace(/\.git$/, "");
28
+ const egregoreDir = path.join(base, forkDirName);
28
29
  const memoryDirName = memory_url
29
30
  .split("/")
30
31
  .pop()
@@ -36,23 +37,39 @@ async function install(data, ui, targetDir) {
36
37
  // Configure git credential helper for HTTPS cloning
37
38
  configureGitCredentials(github_token);
38
39
 
40
+ // Embed token in URLs as fallback for private repos (credential helper may not work)
41
+ const authedForkUrl = embedToken(fork_url, github_token);
42
+ const authedMemoryUrl = embedToken(memory_url, github_token);
43
+
39
44
  // 1. Clone fork
40
45
  ui.step(1, totalSteps, "Cloning egregore...");
41
46
  if (fs.existsSync(egregoreDir)) {
42
47
  ui.warn("egregore/ already exists — pulling latest");
43
48
  run("git pull", { cwd: egregoreDir });
44
49
  } else {
45
- execFileSync("git", ["clone", fork_url, egregoreDir], { stdio: "pipe", encoding: "utf-8", timeout: 60000 });
50
+ execFileSync("git", ["clone", authedForkUrl, egregoreDir], { stdio: "pipe", encoding: "utf-8", timeout: 60000 });
51
+ try { run(`git remote set-url origin ${fork_url}`, { cwd: egregoreDir }); } catch {}
46
52
  }
47
53
  ui.success("Cloned egregore");
48
54
 
55
+ // Set repo-local git identity from GitHub user (not machine-global config)
56
+ if (github_username) {
57
+ try {
58
+ run(`git config user.name "${github_name || github_username}"`, { cwd: egregoreDir });
59
+ run(`git config user.email "${github_username}@users.noreply.github.com"`, { cwd: egregoreDir });
60
+ } catch {
61
+ // Non-fatal
62
+ }
63
+ }
64
+
49
65
  // 2. Clone memory
50
66
  ui.step(2, totalSteps, "Cloning shared memory...");
51
67
  if (fs.existsSync(memoryDir)) {
52
68
  ui.warn(`${memoryDirName}/ already exists — pulling latest`);
53
69
  run("git pull", { cwd: memoryDir });
54
70
  } else {
55
- execFileSync("git", ["clone", memory_url, memoryDir], { stdio: "pipe", encoding: "utf-8", timeout: 60000 });
71
+ execFileSync("git", ["clone", authedMemoryUrl, memoryDir], { stdio: "pipe", encoding: "utf-8", timeout: 60000 });
72
+ try { run(`git remote set-url origin ${memory_url}`, { cwd: memoryDir }); } catch {}
56
73
  }
57
74
  ui.success("Cloned memory");
58
75
 
@@ -80,9 +97,22 @@ async function install(data, ui, targetDir) {
80
97
  fs.writeFileSync(envPath, envLines.join("\n") + "\n", { mode: 0o600 });
81
98
  ui.success("Credentials saved");
82
99
 
100
+ // 4b. Save identity to .egregore-state.json (so session-start.sh knows who this is)
101
+ const statePath = path.join(egregoreDir, ".egregore-state.json");
102
+ let state = {};
103
+ if (fs.existsSync(statePath)) {
104
+ try { state = JSON.parse(fs.readFileSync(statePath, "utf-8")); } catch {}
105
+ }
106
+ if (github_username) {
107
+ state.github_username = github_username;
108
+ state.github_name = github_name || github_username;
109
+ }
110
+ state.onboarding_complete = true;
111
+ fs.writeFileSync(statePath, JSON.stringify(state, null, 2) + "\n");
112
+
83
113
  // 5. Register instance + shell alias
84
114
  ui.step(5, totalSteps, "Registering instance...");
85
- registerInstance(dirSlug, org_name, egregoreDir);
115
+ registerInstance(forkDirName, org_name, egregoreDir);
86
116
  const alias = await installShellAlias(egregoreDir, ui);
87
117
 
88
118
  // 6+. Clone managed repos (if any)
@@ -96,7 +126,8 @@ async function install(data, ui, targetDir) {
96
126
  run("git pull", { cwd: repoDir });
97
127
  } else {
98
128
  const repoUrl = `https://github.com/${github_org}/${repoName}.git`;
99
- execFileSync("git", ["clone", repoUrl, repoDir], { stdio: "pipe", encoding: "utf-8", timeout: 60000 });
129
+ execFileSync("git", ["clone", embedToken(repoUrl, github_token), repoDir], { stdio: "pipe", encoding: "utf-8", timeout: 60000 });
130
+ try { run(`git remote set-url origin ${repoUrl}`, { cwd: repoDir }); } catch {}
100
131
  }
101
132
  clonedRepos.push(repoName);
102
133
  ui.success(`Cloned ${repoName}`);
@@ -107,7 +138,7 @@ async function install(data, ui, targetDir) {
107
138
  ui.success(`Egregore is ready for ${ui.bold(org_name)}`);
108
139
  console.log("");
109
140
  ui.info(`Your workspace:`);
110
- ui.info(` ${ui.cyan(`./egregore-${dirSlug}/`)} — Your Egregore instance`);
141
+ ui.info(` ${ui.cyan(`./${forkDirName}/`)} — Your Egregore instance`);
111
142
  ui.info(` ${ui.cyan(`./${memoryDirName}/`)} — Shared knowledge`);
112
143
  for (const repoName of clonedRepos) {
113
144
  ui.info(` ${ui.cyan(`./${repoName}/`)} — Managed repo`);
@@ -122,6 +153,14 @@ async function install(data, ui, targetDir) {
122
153
  console.log("");
123
154
  }
124
155
 
156
+ function embedToken(url, token) {
157
+ try {
158
+ return url.replace("https://github.com/", `https://x-access-token:${token}@github.com/`);
159
+ } catch {
160
+ return url;
161
+ }
162
+ }
163
+
125
164
  function configureGitCredentials(token) {
126
165
  try {
127
166
  run("git config credential.helper store");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-egregore",
3
- "version": "0.3.9",
3
+ "version": "0.3.11",
4
4
  "description": "Set up Egregore for your team in one command",
5
5
  "license": "MIT",
6
6
  "bin": {