create-hq 10.9.1 → 10.11.0

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 (59) hide show
  1. package/commands/{team-sync.md → sync-team.md} +167 -6
  2. package/dist/__tests__/auth.test.js +176 -186
  3. package/dist/__tests__/auth.test.js.map +1 -1
  4. package/dist/__tests__/scaffold.test.js +5 -3
  5. package/dist/__tests__/scaffold.test.js.map +1 -1
  6. package/dist/auth.d.ts +52 -131
  7. package/dist/auth.d.ts.map +1 -1
  8. package/dist/auth.js +73 -369
  9. package/dist/auth.js.map +1 -1
  10. package/dist/fetch-template.d.ts +7 -2
  11. package/dist/fetch-template.d.ts.map +1 -1
  12. package/dist/fetch-template.js +27 -15
  13. package/dist/fetch-template.js.map +1 -1
  14. package/dist/index.js +7 -17
  15. package/dist/index.js.map +1 -1
  16. package/dist/packages.d.ts.map +1 -1
  17. package/dist/packages.js +1 -1
  18. package/dist/packages.js.map +1 -1
  19. package/dist/recommended-packages.d.ts +93 -0
  20. package/dist/recommended-packages.d.ts.map +1 -0
  21. package/dist/recommended-packages.js +221 -0
  22. package/dist/recommended-packages.js.map +1 -0
  23. package/dist/scaffold.d.ts +4 -2
  24. package/dist/scaffold.d.ts.map +1 -1
  25. package/dist/scaffold.js +139 -98
  26. package/dist/scaffold.js.map +1 -1
  27. package/dist/ui.d.ts +0 -17
  28. package/dist/ui.d.ts.map +1 -1
  29. package/dist/ui.js +0 -45
  30. package/dist/ui.js.map +1 -1
  31. package/package.json +2 -1
  32. package/dist/admin-onboarding.d.ts +0 -44
  33. package/dist/admin-onboarding.d.ts.map +0 -1
  34. package/dist/admin-onboarding.js +0 -530
  35. package/dist/admin-onboarding.js.map +0 -1
  36. package/dist/company-template.d.ts +0 -34
  37. package/dist/company-template.d.ts.map +0 -1
  38. package/dist/company-template.js +0 -142
  39. package/dist/company-template.js.map +0 -1
  40. package/dist/invite-command.d.ts +0 -10
  41. package/dist/invite-command.d.ts.map +0 -1
  42. package/dist/invite-command.js +0 -110
  43. package/dist/invite-command.js.map +0 -1
  44. package/dist/invite.d.ts +0 -91
  45. package/dist/invite.d.ts.map +0 -1
  46. package/dist/invite.js +0 -230
  47. package/dist/invite.js.map +0 -1
  48. package/dist/join-flow.d.ts +0 -32
  49. package/dist/join-flow.d.ts.map +0 -1
  50. package/dist/join-flow.js +0 -205
  51. package/dist/join-flow.js.map +0 -1
  52. package/dist/team-setup.d.ts +0 -83
  53. package/dist/team-setup.d.ts.map +0 -1
  54. package/dist/team-setup.js +0 -353
  55. package/dist/team-setup.js.map +0 -1
  56. package/dist/teams-flow.d.ts +0 -41
  57. package/dist/teams-flow.d.ts.map +0 -1
  58. package/dist/teams-flow.js +0 -173
  59. package/dist/teams-flow.js.map +0 -1
package/dist/join-flow.js DELETED
@@ -1,205 +0,0 @@
1
- /**
2
- * Join-via-invite flow for HQ Teams members.
3
- *
4
- * When a member has an invite token (from an admin), this flow:
5
- * 1. Decodes the token to get team coordinates
6
- * 2. Authenticates via GitHub device flow
7
- * 3. Checks org membership / repo access (waits if invite is pending)
8
- * 4. Clones the team repo into companies/{slug}/
9
- *
10
- * This is the primary path for non-technical users who received an invite.
11
- */
12
- import * as fs from "fs";
13
- import * as path from "path";
14
- import * as os from "os";
15
- import { execSync } from "child_process";
16
- import { createInterface } from "readline";
17
- import chalk from "chalk";
18
- import { ensureCompanyStructure } from "./company-template.js";
19
- import { stepStatus, success, warn, info, step } from "./ui.js";
20
- import { decodeInviteToken, checkRepoAccess, } from "./invite.js";
21
- import { linkTeamCommands, installTeamCommands } from "./team-setup.js";
22
- // ─── Prompt helpers ─────────────────────────────────────────────────────────
23
- function prompt(question, defaultVal) {
24
- const rl = createInterface({ input: process.stdin, output: process.stdout });
25
- const suffix = defaultVal ? ` (${defaultVal})` : "";
26
- return new Promise((resolve) => {
27
- rl.question(` ? ${question}${suffix} `, (answer) => {
28
- rl.close();
29
- resolve(answer.trim() || defaultVal || "");
30
- });
31
- });
32
- }
33
- function pause(message) {
34
- const rl = createInterface({ input: process.stdin, output: process.stdout });
35
- return new Promise((resolve) => {
36
- rl.question(` ${message}`, () => {
37
- rl.close();
38
- resolve();
39
- });
40
- });
41
- }
42
- // ─── Git helpers (same as team-setup.ts / admin-onboarding.ts) ──────────────
43
- function runGitWithToken(args, cwd, auth) {
44
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "create-hq-git-"));
45
- const isWindows = process.platform === "win32";
46
- const askpassPath = path.join(tmpDir, isWindows ? "askpass.cmd" : "askpass.sh");
47
- try {
48
- if (isWindows) {
49
- fs.writeFileSync(askpassPath, `@echo off\nif "%~1"=="" (echo %GIT_TOKEN%) else (echo %GIT_TOKEN%)\n`, "utf-8");
50
- }
51
- else {
52
- fs.writeFileSync(askpassPath, `#!/bin/sh\necho "$GIT_TOKEN"\n`, "utf-8");
53
- fs.chmodSync(askpassPath, 0o700);
54
- }
55
- execSync(`git -c credential.helper= ${args.join(" ")}`, {
56
- cwd,
57
- stdio: "pipe",
58
- env: {
59
- ...process.env,
60
- GIT_TOKEN: auth.access_token,
61
- GIT_ASKPASS: askpassPath,
62
- GIT_TERMINAL_PROMPT: "0",
63
- GCM_INTERACTIVE: "never",
64
- },
65
- });
66
- }
67
- finally {
68
- try {
69
- fs.rmSync(tmpDir, { recursive: true, force: true });
70
- }
71
- catch {
72
- // ignore
73
- }
74
- }
75
- }
76
- function tokenAuthUrl(cloneUrl) {
77
- const u = new URL(cloneUrl);
78
- u.username = "x-access-token";
79
- return u.toString();
80
- }
81
- // ─── Main flow ──────────────────────────────────────────────────────────────
82
- /**
83
- * Run the join-via-invite flow.
84
- *
85
- * @param auth - Authenticated GitHub user (already signed in)
86
- * @param hqRoot - Local HQ root directory (where companies/ lives)
87
- * @param token - The invite token string (hq_...) or raw base64
88
- * @returns - Result on success, null on failure/abort
89
- */
90
- export async function runJoinByInvite(auth, hqRoot, token) {
91
- // 1. Decode token
92
- const payload = decodeInviteToken(token);
93
- if (!payload) {
94
- warn("Invalid invite code. Check that you copied it correctly and try again.");
95
- return null;
96
- }
97
- console.log();
98
- console.log(chalk.bold(" Joining a team"));
99
- console.log();
100
- console.log(` ${chalk.dim("Team:")} ${chalk.cyan(payload.teamName)}`);
101
- console.log(` ${chalk.dim("Org:")} ${payload.org}`);
102
- console.log(` ${chalk.dim("Invited by:")} @${payload.invitedBy}`);
103
- console.log();
104
- // 2. Check repo access (proves org membership + repo visibility)
105
- const accessLabel = `Checking access to ${payload.org}/${payload.repo}`;
106
- stepStatus(accessLabel, "running");
107
- let hasAccess = await checkRepoAccess(auth, payload.org, payload.repo);
108
- if (!hasAccess) {
109
- stepStatus(accessLabel, "failed");
110
- console.log();
111
- // Guide the user to accept the org invite
112
- info("You don't have access to this team's repository yet.");
113
- console.log();
114
- step("Check your email for a GitHub organization invite from " + chalk.cyan(payload.org));
115
- step("Accept the invite, then come back here.");
116
- console.log();
117
- // Give them up to 3 chances to retry
118
- for (let attempt = 1; attempt <= 3; attempt++) {
119
- await pause(`Press Enter after accepting the invite (attempt ${attempt}/3)... `);
120
- stepStatus(`Re-checking access (attempt ${attempt})`, "running");
121
- hasAccess = await checkRepoAccess(auth, payload.org, payload.repo);
122
- if (hasAccess) {
123
- stepStatus(`Re-checking access (attempt ${attempt})`, "done");
124
- break;
125
- }
126
- else {
127
- stepStatus(`Re-checking access (attempt ${attempt})`, "failed");
128
- if (attempt < 3) {
129
- console.log();
130
- info("Still no access. Make sure you:");
131
- step(`Accepted the org invite from ${payload.org} (check email from GitHub)`);
132
- step("Are signed in to GitHub with the same account you accepted the invite on");
133
- console.log();
134
- }
135
- }
136
- }
137
- if (!hasAccess) {
138
- console.log();
139
- warn("Could not access the team repository after 3 attempts.");
140
- info(`Your GitHub username is @${auth.login}`);
141
- info(`Ask @${payload.invitedBy} to add @${auth.login} to the ${payload.org} organization.`);
142
- info("Then run create-hq again with the same invite code.");
143
- return null;
144
- }
145
- }
146
- else {
147
- stepStatus(accessLabel, "done");
148
- }
149
- // 3. Clone the repo into companies/{slug}/
150
- const companiesDir = path.join(hqRoot, "companies");
151
- if (!fs.existsSync(companiesDir)) {
152
- fs.mkdirSync(companiesDir, { recursive: true });
153
- }
154
- const companyDir = path.join(companiesDir, payload.slug);
155
- if (fs.existsSync(companyDir) && fs.readdirSync(companyDir).length > 0) {
156
- info(`companies/${payload.slug}/ already exists — skipping clone.`);
157
- }
158
- else {
159
- const cloneLabel = `Cloning ${payload.teamName} into companies/${payload.slug}`;
160
- stepStatus(cloneLabel, "running");
161
- try {
162
- const remoteUrl = tokenAuthUrl(payload.cloneUrl);
163
- runGitWithToken(["clone", `"${remoteUrl}"`, `"${companyDir}"`], companiesDir, auth);
164
- // Strip the token from the stored remote URL
165
- execSync(`git remote set-url origin "${payload.cloneUrl}"`, {
166
- cwd: companyDir,
167
- stdio: "pipe",
168
- });
169
- // Ensure standard company subdirectories exist
170
- ensureCompanyStructure(companyDir);
171
- stepStatus(cloneLabel, "done");
172
- }
173
- catch (err) {
174
- stepStatus(cloneLabel, "failed");
175
- const message = err instanceof Error ? err.message : String(err);
176
- warn(`Could not clone team repo: ${message}`);
177
- // Provide actionable guidance
178
- console.log();
179
- info("This usually means the repository doesn't exist or your access isn't set up yet.");
180
- info(`Ask @${payload.invitedBy} to verify the repo exists at: https://github.com/${payload.org}/${payload.repo}`);
181
- return null;
182
- }
183
- }
184
- // Install bundled team commands (invite, sync, promote)
185
- const installed = installTeamCommands(hqRoot);
186
- if (installed.length > 0) {
187
- info(`Installed ${installed.length} team command${installed.length === 1 ? "" : "s"}: ${installed.join(", ")}`);
188
- }
189
- // Link team-distributed commands as slash commands
190
- const symlinks = linkTeamCommands(hqRoot, payload.slug);
191
- if (symlinks.linked.length > 0) {
192
- info(`Linked ${symlinks.linked.length} team command${symlinks.linked.length === 1 ? "" : "s"}`);
193
- }
194
- const repoUrl = `https://github.com/${payload.org}/${payload.repo}`;
195
- console.log();
196
- success(`Joined ${payload.teamName}!`);
197
- info(`Team workspace: companies/${payload.slug}/`);
198
- return {
199
- slug: payload.slug,
200
- teamName: payload.teamName,
201
- companyDir,
202
- repoUrl,
203
- };
204
- }
205
- //# sourceMappingURL=join-flow.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"join-flow.js","sourceRoot":"","sources":["../src/join-flow.ts"],"names":[],"mappings":"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"}
@@ -1,83 +0,0 @@
1
- /**
2
- * Member team discovery + clone flow.
3
- *
4
- * For users who already have access to one or more HQ team repos via the
5
- * hq-team-sync GitHub App, enumerate their installations, find the {org}/hq
6
- * repo for each, present a checklist, and clone the selected repos into
7
- * companies/{slug}/.
8
- *
9
- * No backend involved — all data comes from api.github.com using the user's
10
- * GitHub App user token.
11
- */
12
- import { type GitHubAuth } from "./auth.js";
13
- export interface DiscoveredTeam {
14
- /** Org login (used as companies/{slug}/ directory name) */
15
- slug: string;
16
- /** Display name (defaults to org login). */
17
- name: string;
18
- /** GitHub HTML URL for the team repo. */
19
- repoHtmlUrl: string;
20
- /** HTTPS clone URL. */
21
- cloneUrl: string;
22
- /** GitHub installation ID for the App on this org. */
23
- installationId: number;
24
- }
25
- export interface MemberJoinResult {
26
- joined: DiscoveredTeam[];
27
- skipped: DiscoveredTeam[];
28
- failed: {
29
- team: DiscoveredTeam;
30
- error: string;
31
- }[];
32
- }
33
- /**
34
- * Install the bundled team management commands into .claude/commands/.
35
- * These ship with the create-hq npm package (in commands/) and are only
36
- * installed when a user joins or creates a team.
37
- *
38
- * Returns the list of files installed (skips files that already exist).
39
- */
40
- export declare function installTeamCommands(hqRoot: string): string[];
41
- export interface SymlinkResult {
42
- linked: string[];
43
- alreadyLinked: string[];
44
- skipped: string[];
45
- unlinked: string[];
46
- }
47
- /**
48
- * Link team-distributed commands into .claude/commands/ so they're
49
- * discoverable as slash commands. Mirrors the logic in /sync (Section 5).
50
- *
51
- * Naming convention: {slug}--{command}.md (double-dash separates team from command).
52
- * Symlinks use relative paths so they work regardless of HQ's absolute location.
53
- *
54
- * @param hqRoot - The HQ root directory
55
- * @param slug - Team slug (companies/{slug}/)
56
- */
57
- export declare function linkTeamCommands(hqRoot: string, slug: string): SymlinkResult;
58
- /**
59
- * Enumerate the user's HQ team memberships by walking their App installations.
60
- * Returns one DiscoveredTeam per hq-* repo found across all installed orgs.
61
- */
62
- export declare function discoverTeams(auth: GitHubAuth): Promise<DiscoveredTeam[]>;
63
- /**
64
- * Present discovered teams as a numbered list with all pre-selected, and
65
- * let the user deselect any they want to skip.
66
- *
67
- * Input format:
68
- * - empty / "all" → keep all
69
- * - "none" → skip all
70
- * - "1,3" → deselect entries 1 and 3
71
- */
72
- export declare function selectTeams(teams: DiscoveredTeam[]): Promise<{
73
- selected: DiscoveredTeam[];
74
- skipped: DiscoveredTeam[];
75
- }>;
76
- /**
77
- * Discover, select, and clone team repos for an authenticated member.
78
- *
79
- * Returns a result with joined / skipped / failed lists. Returns null if
80
- * the user has no HQ teams at all (caller can route to admin onboarding).
81
- */
82
- export declare function runMemberJoin(auth: GitHubAuth, hqRoot: string): Promise<MemberJoinResult | null>;
83
- //# sourceMappingURL=team-setup.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"team-setup.d.ts","sourceRoot":"","sources":["../src/team-setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAYH,OAAO,EACL,KAAK,UAAU,EAGhB,MAAM,WAAW,CAAC;AA6BnB,MAAM,WAAW,cAAc;IAC7B,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,uBAAuB;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,MAAM,EAAE;QAAE,IAAI,EAAE,cAAc,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACnD;AAOD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAyB5D;AAID,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,aAAa,CAiE5E;AA4FD;;;GAGG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAwB/E;AAID;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,cAAc,EAAE,GACtB,OAAO,CAAC;IAAE,QAAQ,EAAE,cAAc,EAAE,CAAC;IAAC,OAAO,EAAE,cAAc,EAAE,CAAA;CAAE,CAAC,CA8CpE;AAoCD;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CA4DlC"}
@@ -1,353 +0,0 @@
1
- /**
2
- * Member team discovery + clone flow.
3
- *
4
- * For users who already have access to one or more HQ team repos via the
5
- * hq-team-sync GitHub App, enumerate their installations, find the {org}/hq
6
- * repo for each, present a checklist, and clone the selected repos into
7
- * companies/{slug}/.
8
- *
9
- * No backend involved — all data comes from api.github.com using the user's
10
- * GitHub App user token.
11
- */
12
- import * as fs from "fs";
13
- import * as path from "path";
14
- import * as os from "os";
15
- import { fileURLToPath } from "url";
16
- import { execSync } from "child_process";
17
- import { createInterface } from "readline";
18
- import chalk from "chalk";
19
- const __filename = fileURLToPath(import.meta.url);
20
- const __dirname = path.dirname(__filename);
21
- import { HQ_GITHUB_APP_SLUG, githubApi, } from "./auth.js";
22
- import { ensureCompanyStructure } from "./company-template.js";
23
- import { stepStatus, success, warn, info } from "./ui.js";
24
- // ─── Bundled Team Commands ──────────────────────────────────────────────────
25
- /** Team-specific commands bundled with create-hq (not part of core HQ template). */
26
- const TEAM_COMMANDS = ["invite.md", "team-sync.md", "promote.md"];
27
- /**
28
- * Install the bundled team management commands into .claude/commands/.
29
- * These ship with the create-hq npm package (in commands/) and are only
30
- * installed when a user joins or creates a team.
31
- *
32
- * Returns the list of files installed (skips files that already exist).
33
- */
34
- export function installTeamCommands(hqRoot) {
35
- const hqCommandsDir = path.join(hqRoot, ".claude", "commands");
36
- if (!fs.existsSync(hqCommandsDir)) {
37
- fs.mkdirSync(hqCommandsDir, { recursive: true });
38
- }
39
- // Resolve the bundled commands directory relative to this module.
40
- // In the built package: dist/team-setup.js → ../commands/
41
- const bundledDir = path.resolve(__dirname, "..", "commands");
42
- if (!fs.existsSync(bundledDir)) {
43
- // Fallback: running from source (ts-node / vitest)
44
- // src/team-setup.ts → ../commands/
45
- return [];
46
- }
47
- const installed = [];
48
- for (const file of TEAM_COMMANDS) {
49
- const src = path.join(bundledDir, file);
50
- const dest = path.join(hqCommandsDir, file);
51
- if (!fs.existsSync(src))
52
- continue;
53
- if (fs.existsSync(dest))
54
- continue; // don't overwrite — user may have customized
55
- fs.copyFileSync(src, dest);
56
- installed.push(file);
57
- }
58
- return installed;
59
- }
60
- /**
61
- * Link team-distributed commands into .claude/commands/ so they're
62
- * discoverable as slash commands. Mirrors the logic in /sync (Section 5).
63
- *
64
- * Naming convention: {slug}--{command}.md (double-dash separates team from command).
65
- * Symlinks use relative paths so they work regardless of HQ's absolute location.
66
- *
67
- * @param hqRoot - The HQ root directory
68
- * @param slug - Team slug (companies/{slug}/)
69
- */
70
- export function linkTeamCommands(hqRoot, slug) {
71
- const result = { linked: [], alreadyLinked: [], skipped: [], unlinked: [] };
72
- const teamCommandsDir = path.join(hqRoot, "companies", slug, ".claude", "commands");
73
- const hqCommandsDir = path.join(hqRoot, ".claude", "commands");
74
- // Ensure .claude/commands/ exists
75
- if (!fs.existsSync(hqCommandsDir)) {
76
- fs.mkdirSync(hqCommandsDir, { recursive: true });
77
- }
78
- // Scan for team commands
79
- if (!fs.existsSync(teamCommandsDir)) {
80
- return result;
81
- }
82
- const teamFiles = fs.readdirSync(teamCommandsDir).filter((f) => f.endsWith(".md"));
83
- if (teamFiles.length === 0) {
84
- return result;
85
- }
86
- // Create symlinks for new commands
87
- for (const file of teamFiles) {
88
- const symlinkName = `${slug}--${file}`;
89
- const symlinkPath = path.join(hqCommandsDir, symlinkName);
90
- // Relative path from .claude/commands/ to companies/{slug}/.claude/commands/
91
- const relativeSrc = path.join("..", "..", "companies", slug, ".claude", "commands", file);
92
- if (fs.existsSync(symlinkPath)) {
93
- const stats = fs.lstatSync(symlinkPath);
94
- if (stats.isSymbolicLink()) {
95
- const target = fs.readlinkSync(symlinkPath);
96
- if (target === relativeSrc) {
97
- result.alreadyLinked.push(symlinkName);
98
- continue;
99
- }
100
- }
101
- // Exists but is not the right symlink — skip to avoid collision
102
- result.skipped.push(symlinkName);
103
- continue;
104
- }
105
- try {
106
- fs.symlinkSync(relativeSrc, symlinkPath);
107
- result.linked.push(symlinkName);
108
- }
109
- catch {
110
- result.skipped.push(symlinkName);
111
- }
112
- }
113
- // Remove stale symlinks for this team
114
- const teamPattern = `${slug}--`;
115
- const existing = fs.readdirSync(hqCommandsDir).filter((f) => f.startsWith(teamPattern));
116
- const expectedNames = new Set(teamFiles.map((f) => `${slug}--${f}`));
117
- for (const link of existing) {
118
- const linkPath = path.join(hqCommandsDir, link);
119
- const stats = fs.lstatSync(linkPath);
120
- if (stats.isSymbolicLink() && !expectedNames.has(link)) {
121
- fs.unlinkSync(linkPath);
122
- result.unlinked.push(link);
123
- }
124
- }
125
- return result;
126
- }
127
- // ─── Prompt helper ──────────────────────────────────────────────────────────
128
- function prompt(question, defaultVal) {
129
- const rl = createInterface({ input: process.stdin, output: process.stdout });
130
- const suffix = defaultVal ? ` (${defaultVal})` : "";
131
- return new Promise((resolve) => {
132
- rl.question(` ? ${question}${suffix} `, (answer) => {
133
- rl.close();
134
- resolve(answer.trim() || defaultVal || "");
135
- });
136
- });
137
- }
138
- // ─── Git with embedded token (mirrors admin-onboarding.ts) ──────────────────
139
- function runGitWithToken(args, cwd, auth) {
140
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "create-hq-git-"));
141
- const isWindows = process.platform === "win32";
142
- const askpassPath = path.join(tmpDir, isWindows ? "askpass.cmd" : "askpass.sh");
143
- try {
144
- if (isWindows) {
145
- fs.writeFileSync(askpassPath, `@echo off\nif "%~1"=="" (echo %GIT_TOKEN%) else (echo %GIT_TOKEN%)\n`, "utf-8");
146
- }
147
- else {
148
- fs.writeFileSync(askpassPath, `#!/bin/sh\necho "$GIT_TOKEN"\n`, "utf-8");
149
- fs.chmodSync(askpassPath, 0o700);
150
- }
151
- execSync(`git -c credential.helper= ${args.join(" ")}`, {
152
- cwd,
153
- stdio: "pipe",
154
- env: {
155
- ...process.env,
156
- GIT_TOKEN: auth.access_token,
157
- GIT_ASKPASS: askpassPath,
158
- GIT_TERMINAL_PROMPT: "0",
159
- GCM_INTERACTIVE: "never",
160
- },
161
- });
162
- }
163
- finally {
164
- try {
165
- fs.rmSync(tmpDir, { recursive: true, force: true });
166
- }
167
- catch {
168
- // ignore
169
- }
170
- }
171
- }
172
- function tokenAuthUrl(cloneUrl) {
173
- const u = new URL(cloneUrl);
174
- u.username = "x-access-token";
175
- return u.toString();
176
- }
177
- // ─── Discovery ──────────────────────────────────────────────────────────────
178
- async function fetchHqInstallations(auth) {
179
- const data = await githubApi("/user/installations?per_page=100", auth);
180
- return (data.installations ?? []).filter((i) => i.app_slug === HQ_GITHUB_APP_SLUG);
181
- }
182
- async function findHqReposInInstallation(auth, installation) {
183
- // GitHub App user token can list repos accessible through this installation
184
- const data = await githubApi(`/user/installations/${installation.id}/repositories?per_page=100`, auth);
185
- const repos = data.repositories ?? [];
186
- // Match repos named hq-* (e.g. hq-indigo, hq-frogbear)
187
- return repos.filter((r) => /^hq-.+$/i.test(r.name));
188
- }
189
- /**
190
- * Derive a team slug from the repo name. Repo naming convention: hq-{teamSlug}.
191
- * e.g. "hq-indigo" → "indigo", "hq-frogbear" → "frogbear"
192
- */
193
- function teamSlugFromRepoName(repoName) {
194
- return repoName.replace(/^hq-/i, "").toLowerCase();
195
- }
196
- /**
197
- * Enumerate the user's HQ team memberships by walking their App installations.
198
- * Returns one DiscoveredTeam per hq-* repo found across all installed orgs.
199
- */
200
- export async function discoverTeams(auth) {
201
- const installations = await fetchHqInstallations(auth);
202
- const teams = [];
203
- for (const inst of installations) {
204
- try {
205
- const repos = await findHqReposInInstallation(auth, inst);
206
- for (const repo of repos) {
207
- const slug = teamSlugFromRepoName(repo.name);
208
- teams.push({
209
- slug,
210
- name: `${inst.account.login}/${slug}`,
211
- repoHtmlUrl: repo.html_url,
212
- cloneUrl: repo.clone_url,
213
- installationId: inst.id,
214
- });
215
- }
216
- }
217
- catch {
218
- // Per-installation errors should not abort the whole discovery
219
- continue;
220
- }
221
- }
222
- return teams;
223
- }
224
- // ─── Selection UI ───────────────────────────────────────────────────────────
225
- /**
226
- * Present discovered teams as a numbered list with all pre-selected, and
227
- * let the user deselect any they want to skip.
228
- *
229
- * Input format:
230
- * - empty / "all" → keep all
231
- * - "none" → skip all
232
- * - "1,3" → deselect entries 1 and 3
233
- */
234
- export async function selectTeams(teams) {
235
- if (teams.length === 0) {
236
- return { selected: [], skipped: [] };
237
- }
238
- if (teams.length === 1) {
239
- console.log();
240
- console.log(chalk.bold(" Found 1 HQ team:"));
241
- console.log(chalk.green(" [✓] ") + chalk.white(teams[0].name));
242
- console.log();
243
- const answer = await prompt(`Set up ${chalk.cyan(teams[0].name)}? (Y/n)`, "y");
244
- if (answer.toLowerCase().startsWith("y")) {
245
- return { selected: [teams[0]], skipped: [] };
246
- }
247
- return { selected: [], skipped: [teams[0]] };
248
- }
249
- console.log();
250
- console.log(chalk.bold(` Found ${teams.length} HQ teams (all pre-selected):`));
251
- for (let i = 0; i < teams.length; i++) {
252
- console.log(chalk.green(" [✓] ") + chalk.cyan(`${i + 1}. `) + chalk.white(teams[i].name));
253
- }
254
- console.log();
255
- console.log(chalk.dim(" Press Enter to set up all, or type numbers to skip (e.g. 2,3)"));
256
- const answer = await prompt("Skip");
257
- if (!answer || answer.toLowerCase() === "all") {
258
- return { selected: [...teams], skipped: [] };
259
- }
260
- if (answer.toLowerCase() === "none") {
261
- return { selected: [], skipped: [...teams] };
262
- }
263
- const skipIdx = new Set(answer
264
- .split(",")
265
- .map((s) => parseInt(s.trim(), 10) - 1)
266
- .filter((n) => Number.isInteger(n) && n >= 0 && n < teams.length));
267
- const selected = teams.filter((_, i) => !skipIdx.has(i));
268
- const skipped = teams.filter((_, i) => skipIdx.has(i));
269
- return { selected, skipped };
270
- }
271
- // ─── Clone ──────────────────────────────────────────────────────────────────
272
- function cloneTeam(team, hqRoot, auth) {
273
- const companiesDir = path.join(hqRoot, "companies");
274
- if (!fs.existsSync(companiesDir)) {
275
- fs.mkdirSync(companiesDir, { recursive: true });
276
- }
277
- const companyDir = path.join(companiesDir, team.slug);
278
- if (fs.existsSync(companyDir) && fs.readdirSync(companyDir).length > 0) {
279
- throw new Error(`companies/${team.slug}/ already exists and is not empty`);
280
- }
281
- const remoteUrl = tokenAuthUrl(team.cloneUrl);
282
- runGitWithToken(["clone", `"${remoteUrl}"`, `"${companyDir}"`], companiesDir, auth);
283
- // Strip token from stored remote URL
284
- execSync(`git remote set-url origin "${team.cloneUrl}"`, {
285
- cwd: companyDir,
286
- stdio: "pipe",
287
- });
288
- // Make sure all standard subdirs exist
289
- ensureCompanyStructure(companyDir);
290
- return companyDir;
291
- }
292
- // ─── Main flow ──────────────────────────────────────────────────────────────
293
- /**
294
- * Discover, select, and clone team repos for an authenticated member.
295
- *
296
- * Returns a result with joined / skipped / failed lists. Returns null if
297
- * the user has no HQ teams at all (caller can route to admin onboarding).
298
- */
299
- export async function runMemberJoin(auth, hqRoot) {
300
- const discoveryLabel = "Looking up your HQ teams";
301
- stepStatus(discoveryLabel, "running");
302
- let teams;
303
- try {
304
- teams = await discoverTeams(auth);
305
- stepStatus(discoveryLabel, "done");
306
- }
307
- catch (err) {
308
- stepStatus(discoveryLabel, "failed");
309
- const message = err instanceof Error ? err.message : String(err);
310
- warn(`Could not look up your teams: ${message}`);
311
- return null;
312
- }
313
- if (teams.length === 0) {
314
- return null;
315
- }
316
- const { selected, skipped } = await selectTeams(teams);
317
- if (selected.length === 0) {
318
- info("No teams selected — continuing.");
319
- return { joined: [], skipped, failed: [] };
320
- }
321
- const joined = [];
322
- const failed = [];
323
- for (const team of selected) {
324
- const label = `Cloning ${team.name} → companies/${team.slug}`;
325
- stepStatus(label, "running");
326
- try {
327
- cloneTeam(team, hqRoot, auth);
328
- stepStatus(label, "done");
329
- // Install bundled team commands (invite, sync, promote)
330
- const installed = installTeamCommands(hqRoot);
331
- if (installed.length > 0) {
332
- info(`Installed ${installed.length} team command${installed.length === 1 ? "" : "s"}: ${installed.join(", ")}`);
333
- }
334
- // Link team-distributed commands as slash commands
335
- const symlinks = linkTeamCommands(hqRoot, team.slug);
336
- if (symlinks.linked.length > 0) {
337
- info(`Linked ${symlinks.linked.length} team command${symlinks.linked.length === 1 ? "" : "s"} from ${team.name}`);
338
- }
339
- joined.push(team);
340
- }
341
- catch (err) {
342
- stepStatus(label, "failed");
343
- const message = err instanceof Error ? err.message : String(err);
344
- warn(`Could not set up ${team.name}: ${message}`);
345
- failed.push({ team, error: message });
346
- }
347
- }
348
- if (joined.length > 0) {
349
- success(`${joined.length} team${joined.length === 1 ? "" : "s"} ready`);
350
- }
351
- return { joined, skipped, failed };
352
- }
353
- //# sourceMappingURL=team-setup.js.map