@latent-space-labs/open-auto-doc 0.5.2 → 0.5.3

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 (2) hide show
  1. package/dist/index.js +354 -153
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -10,6 +10,7 @@ import net from "net";
10
10
  import path10 from "path";
11
11
  import { fileURLToPath as fileURLToPath2 } from "url";
12
12
  import { spawn } from "child_process";
13
+ import { Octokit as Octokit4 } from "@octokit/rest";
13
14
 
14
15
  // src/auth/device-flow.ts
15
16
  import * as p from "@clack/prompts";
@@ -258,48 +259,58 @@ function getGitHubUsername(octokit) {
258
259
  return octokit.rest.users.getAuthenticated().then((res) => res.data.login);
259
260
  }
260
261
  async function createAndPushDocsRepo(params) {
261
- const { token, docsDir, config } = params;
262
+ const { token, docsDir, config, preCollected } = params;
262
263
  const octokit = new Octokit2({ auth: token });
263
264
  const username = await getGitHubUsername(octokit);
264
- let orgs = [];
265
- try {
266
- const { data } = await octokit.rest.orgs.listForAuthenticatedUser({ per_page: 100 });
267
- orgs = data;
268
- } catch {
269
- }
270
- const ownerOptions = [
271
- { value: username, label: username, hint: "Personal account" },
272
- ...orgs.map((org) => ({ value: org.login, label: org.login, hint: "Organization" }))
273
- ];
274
- let owner = username;
275
- if (ownerOptions.length > 1) {
276
- const selected = await p3.select({
277
- message: "Where should the docs repo be created?",
278
- options: ownerOptions
265
+ let owner;
266
+ let repoName;
267
+ let visibility;
268
+ if (preCollected) {
269
+ owner = preCollected.owner;
270
+ repoName = preCollected.repoName;
271
+ visibility = preCollected.visibility;
272
+ } else {
273
+ let orgs = [];
274
+ try {
275
+ const { data } = await octokit.rest.orgs.listForAuthenticatedUser({ per_page: 100 });
276
+ orgs = data;
277
+ } catch {
278
+ }
279
+ const ownerOptions = [
280
+ { value: username, label: username, hint: "Personal account" },
281
+ ...orgs.map((org) => ({ value: org.login, label: org.login, hint: "Organization" }))
282
+ ];
283
+ owner = username;
284
+ if (ownerOptions.length > 1) {
285
+ const selected = await p3.select({
286
+ message: "Where should the docs repo be created?",
287
+ options: ownerOptions
288
+ });
289
+ if (p3.isCancel(selected)) return null;
290
+ owner = selected;
291
+ }
292
+ const isOrg2 = owner !== username;
293
+ const slug = config?.projectName ? config.projectName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "") : config?.repos?.[0]?.name;
294
+ const defaultName = slug ? `${slug}-docs` : "my-project-docs";
295
+ repoName = await p3.text({
296
+ message: "Name for the docs GitHub repo:",
297
+ initialValue: defaultName,
298
+ validate: (v) => {
299
+ if (!v || v.length === 0) return "Repo name is required";
300
+ if (!/^[a-zA-Z0-9._-]+$/.test(v)) return "Invalid repo name";
301
+ }
302
+ });
303
+ if (p3.isCancel(repoName)) return null;
304
+ visibility = await p3.select({
305
+ message: "Repository visibility:",
306
+ options: [
307
+ { value: "public", label: "Public" },
308
+ { value: "private", label: "Private" }
309
+ ]
279
310
  });
280
- if (p3.isCancel(selected)) return null;
281
- owner = selected;
311
+ if (p3.isCancel(visibility)) return null;
282
312
  }
283
313
  const isOrg = owner !== username;
284
- const slug = config?.projectName ? config.projectName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "") : config?.repos?.[0]?.name;
285
- const defaultName = slug ? `${slug}-docs` : "my-project-docs";
286
- const repoName = await p3.text({
287
- message: "Name for the docs GitHub repo:",
288
- initialValue: defaultName,
289
- validate: (v) => {
290
- if (!v || v.length === 0) return "Repo name is required";
291
- if (!/^[a-zA-Z0-9._-]+$/.test(v)) return "Invalid repo name";
292
- }
293
- });
294
- if (p3.isCancel(repoName)) return null;
295
- const visibility = await p3.select({
296
- message: "Repository visibility:",
297
- options: [
298
- { value: "public", label: "Public" },
299
- { value: "private", label: "Private" }
300
- ]
301
- });
302
- if (p3.isCancel(visibility)) return null;
303
314
  const spinner9 = p3.spinner();
304
315
  spinner9.start(`Creating GitHub repo ${owner}/${repoName}...`);
305
316
  let repoUrl;
@@ -438,8 +449,18 @@ async function authenticateVercel() {
438
449
  }
439
450
  p4.log.warn("Saved Vercel token is invalid.");
440
451
  }
441
- p4.log.info(
442
- "Create a Vercel token at: https://vercel.com/account/tokens"
452
+ p4.note(
453
+ [
454
+ "To create a Vercel access token:",
455
+ "",
456
+ " 1. Go to https://vercel.com/account/settings/tokens",
457
+ " 2. Click 'Create Token'",
458
+ " 3. Enter a name (e.g., 'open-auto-doc')",
459
+ " 4. Select the scope (your personal account or a team)",
460
+ " 5. Set an expiration (or no expiration for convenience)",
461
+ " 6. Click 'Create Token' and copy the value"
462
+ ].join("\n"),
463
+ "Vercel Token Required"
443
464
  );
444
465
  const tokenInput = await p4.text({
445
466
  message: "Enter your Vercel token",
@@ -469,29 +490,32 @@ async function authenticateVercel() {
469
490
  }
470
491
  async function deployToVercel(params) {
471
492
  const { token, githubOwner, githubRepo, docsDir, config } = params;
472
- const { data: userData } = await vercelFetch(
473
- "/v2/user",
474
- token
475
- );
476
- const userId = userData.user?.id;
477
- const { data: teamsData } = await vercelFetch(
478
- "/v2/teams",
479
- token
480
- );
481
- const teams = teamsData.teams ?? [];
482
493
  let teamId;
483
- if (teams.length > 0) {
484
- const scopeOptions = [
485
- { value: "__personal__", label: userData.user?.username ?? "Personal", hint: "Personal account" },
486
- ...teams.map((t) => ({ value: t.id, label: t.name || t.slug, hint: "Team" }))
487
- ];
488
- const selectedScope = await p4.select({
489
- message: "Which Vercel scope should own this project?",
490
- options: scopeOptions
491
- });
492
- if (p4.isCancel(selectedScope)) return null;
493
- if (selectedScope !== "__personal__") {
494
- teamId = selectedScope;
494
+ if (params.scope) {
495
+ teamId = params.scope.teamId;
496
+ } else {
497
+ const { data: userData } = await vercelFetch(
498
+ "/v2/user",
499
+ token
500
+ );
501
+ const { data: teamsData } = await vercelFetch(
502
+ "/v2/teams",
503
+ token
504
+ );
505
+ const teams = teamsData.teams ?? [];
506
+ if (teams.length > 0) {
507
+ const scopeOptions = [
508
+ { value: "__personal__", label: userData.user?.username ?? "Personal", hint: "Personal account" },
509
+ ...teams.map((t) => ({ value: t.id, label: t.name || t.slug, hint: "Team" }))
510
+ ];
511
+ const selectedScope = await p4.select({
512
+ message: "Which Vercel scope should own this project?",
513
+ options: scopeOptions
514
+ });
515
+ if (p4.isCancel(selectedScope)) return null;
516
+ if (selectedScope !== "__personal__") {
517
+ teamId = selectedScope;
518
+ }
495
519
  }
496
520
  }
497
521
  const spinner9 = p4.spinner();
@@ -2802,18 +2826,24 @@ async function createCiWorkflow(params) {
2802
2826
  return createCiWorkflowsMultiRepo({
2803
2827
  token,
2804
2828
  config,
2805
- docsRepoUrl
2829
+ docsRepoUrl,
2830
+ branch: params.branch
2806
2831
  });
2807
2832
  }
2808
2833
  const relativeOutputDir = path7.relative(gitRoot, path7.resolve(outputDir));
2809
2834
  p6.log.info(`Docs repo: ${docsRepoUrl}`);
2810
2835
  p6.log.info(`Output directory: ${relativeOutputDir}`);
2811
- const branch = await p6.text({
2812
- message: "Which branch should trigger doc updates?",
2813
- initialValue: "main",
2814
- validate: (v) => v.length === 0 ? "Branch name is required" : void 0
2815
- });
2816
- if (p6.isCancel(branch)) return null;
2836
+ let branch;
2837
+ if (params.branch) {
2838
+ branch = params.branch;
2839
+ } else {
2840
+ branch = await p6.text({
2841
+ message: "Which branch should trigger doc updates?",
2842
+ initialValue: "main",
2843
+ validate: (v) => v.length === 0 ? "Branch name is required" : void 0
2844
+ });
2845
+ if (p6.isCancel(branch)) return null;
2846
+ }
2817
2847
  const workflowDir = path7.join(gitRoot, ".github", "workflows");
2818
2848
  const workflowPath = path7.join(workflowDir, "update-docs.yml");
2819
2849
  fs7.mkdirSync(workflowDir, { recursive: true });
@@ -2850,12 +2880,17 @@ async function createCiWorkflowsMultiRepo(params) {
2850
2880
  const octokit = new Octokit3({ auth: token });
2851
2881
  p6.log.info(`Setting up CI for ${config.repos.length} repositories`);
2852
2882
  p6.log.info(`Docs repo: ${docsRepoUrl}`);
2853
- const branch = await p6.text({
2854
- message: "Which branch should trigger doc updates?",
2855
- initialValue: "main",
2856
- validate: (v) => v.length === 0 ? "Branch name is required" : void 0
2857
- });
2858
- if (p6.isCancel(branch)) return null;
2883
+ let branch;
2884
+ if (params.branch) {
2885
+ branch = params.branch;
2886
+ } else {
2887
+ branch = await p6.text({
2888
+ message: "Which branch should trigger doc updates?",
2889
+ initialValue: "main",
2890
+ validate: (v) => v.length === 0 ? "Branch name is required" : void 0
2891
+ });
2892
+ if (p6.isCancel(branch)) return null;
2893
+ }
2859
2894
  const createdRepos = [];
2860
2895
  const workflowPath = ".github/workflows/update-docs.yml";
2861
2896
  for (const repo of config.repos) {
@@ -3011,19 +3046,18 @@ import * as p7 from "@clack/prompts";
3011
3046
  import fs8 from "fs";
3012
3047
  import path8 from "path";
3013
3048
  var MCP_SERVER_KEY = "project-docs";
3014
- function getMcpConfig() {
3049
+ function getMcpConfig(docsDir) {
3015
3050
  return {
3016
3051
  command: "npx",
3017
- args: ["-y", "@latent-space-labs/open-auto-doc-mcp", "--project-dir", "."]
3052
+ args: ["-y", "@latent-space-labs/open-auto-doc-mcp", "--project-dir", docsDir]
3018
3053
  };
3019
3054
  }
3020
3055
  async function setupMcpConfig(opts) {
3021
- const cacheDir = path8.join(opts.outputDir, ".autodoc-cache");
3022
- if (!fs8.existsSync(cacheDir)) {
3023
- p7.log.warn("No analysis cache found \u2014 skipping MCP setup. Run setup-mcp after generating docs.");
3024
- return;
3025
- }
3026
- writeMcpJson(process.cwd());
3056
+ const absOutputDir = path8.resolve(opts.outputDir);
3057
+ writeMcpJson(process.cwd(), absOutputDir);
3058
+ writeMcpJson(absOutputDir, absOutputDir);
3059
+ addMcpScript(absOutputDir);
3060
+ showMcpInstructions(absOutputDir);
3027
3061
  }
3028
3062
  async function setupMcpCommand() {
3029
3063
  p7.intro("open-auto-doc \u2014 MCP Server Setup");
@@ -3047,11 +3081,48 @@ Run \`open-auto-doc init\` or \`open-auto-doc generate\` first.`
3047
3081
  p7.log.error("Cache directory exists but contains no analysis files.");
3048
3082
  process.exit(1);
3049
3083
  }
3050
- writeMcpJson(process.cwd());
3084
+ const absOutputDir = path8.resolve(config.outputDir);
3085
+ writeMcpJson(process.cwd(), absOutputDir);
3086
+ writeMcpJson(absOutputDir, absOutputDir);
3087
+ addMcpScript(absOutputDir);
3088
+ showMcpInstructions(absOutputDir);
3089
+ p7.outro("Open Claude Code in this project to start using the tools.");
3090
+ }
3091
+ function writeMcpJson(projectRoot, docsDir) {
3092
+ const mcpPath = path8.join(projectRoot, ".mcp.json");
3093
+ const relDocsDir = path8.relative(projectRoot, docsDir);
3094
+ const isSubdir = !relDocsDir.startsWith("..") && !path8.isAbsolute(relDocsDir);
3095
+ const configDocsDir = isSubdir ? `./${relDocsDir}` : docsDir;
3096
+ let existing = {};
3097
+ if (fs8.existsSync(mcpPath)) {
3098
+ try {
3099
+ existing = JSON.parse(fs8.readFileSync(mcpPath, "utf-8"));
3100
+ } catch {
3101
+ }
3102
+ }
3103
+ const mcpServers = existing.mcpServers ?? {};
3104
+ mcpServers[MCP_SERVER_KEY] = getMcpConfig(configDocsDir);
3105
+ const merged = { ...existing, mcpServers };
3106
+ fs8.writeFileSync(mcpPath, JSON.stringify(merged, null, 2) + "\n");
3107
+ p7.log.step(`Wrote ${mcpPath}`);
3108
+ }
3109
+ function addMcpScript(docsDir) {
3110
+ const pkgPath = path8.join(docsDir, "package.json");
3111
+ if (!fs8.existsSync(pkgPath)) return;
3112
+ try {
3113
+ const pkg = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
3114
+ pkg.scripts = pkg.scripts ?? {};
3115
+ pkg.scripts.mcp = "npx -y @latent-space-labs/open-auto-doc-mcp --project-dir .";
3116
+ fs8.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
3117
+ p7.log.step(`Added 'mcp' script to ${pkgPath}`);
3118
+ } catch {
3119
+ }
3120
+ }
3121
+ function showMcpInstructions(docsDir) {
3051
3122
  p7.log.success("MCP server configured!");
3052
3123
  p7.note(
3053
3124
  [
3054
- "The following tools are now available in Claude Code:",
3125
+ "The following tools are now available:",
3055
3126
  "",
3056
3127
  " get_project_overview \u2014 Project summary and tech stack",
3057
3128
  " search_documentation \u2014 Full-text search across all docs",
@@ -3062,24 +3133,31 @@ Run \`open-auto-doc init\` or \`open-auto-doc generate\` first.`
3062
3133
  " get_diagram \u2014 Mermaid diagrams",
3063
3134
  " get_business_rules \u2014 Domain concepts and workflows"
3064
3135
  ].join("\n"),
3065
- "Available MCP tools"
3136
+ "Available MCP Tools"
3137
+ );
3138
+ p7.note(
3139
+ [
3140
+ "A .mcp.json file has been written to your project.",
3141
+ "Claude Code will automatically detect it.",
3142
+ "",
3143
+ "To use from another project, add this to its .mcp.json:",
3144
+ "",
3145
+ ` {`,
3146
+ ` "mcpServers": {`,
3147
+ ` "project-docs": {`,
3148
+ ` "command": "npx",`,
3149
+ ` "args": ["-y", "@latent-space-labs/open-auto-doc-mcp",`,
3150
+ ` "--project-dir", "${docsDir}"]`,
3151
+ ` }`,
3152
+ ` }`,
3153
+ ` }`,
3154
+ "",
3155
+ "Or run the MCP server directly from the docs directory:",
3156
+ "",
3157
+ ` cd ${docsDir} && npm run mcp`
3158
+ ].join("\n"),
3159
+ "How to Connect"
3066
3160
  );
3067
- p7.outro("Open Claude Code in this project to start using the tools.");
3068
- }
3069
- function writeMcpJson(projectRoot) {
3070
- const mcpPath = path8.join(projectRoot, ".mcp.json");
3071
- let existing = {};
3072
- if (fs8.existsSync(mcpPath)) {
3073
- try {
3074
- existing = JSON.parse(fs8.readFileSync(mcpPath, "utf-8"));
3075
- } catch {
3076
- }
3077
- }
3078
- const mcpServers = existing.mcpServers ?? {};
3079
- mcpServers[MCP_SERVER_KEY] = getMcpConfig();
3080
- const merged = { ...existing, mcpServers };
3081
- fs8.writeFileSync(mcpPath, JSON.stringify(merged, null, 2) + "\n");
3082
- p7.log.step(`Wrote ${path8.relative(projectRoot, mcpPath)}`);
3083
3161
  }
3084
3162
 
3085
3163
  // ../generator/dist/index.js
@@ -3961,6 +4039,146 @@ Try reinstalling: npm install -g @latent-space-labs/open-auto-doc`
3961
4039
  process.exit(0);
3962
4040
  }
3963
4041
  p8.log.info(`Using ${model}`);
4042
+ const shouldSetupMcp = await p8.confirm({
4043
+ message: "Set up MCP server so Claude Code can query your docs?"
4044
+ });
4045
+ const wantsMcp = !p8.isCancel(shouldSetupMcp) && shouldSetupMcp;
4046
+ const shouldDeploy = await p8.confirm({
4047
+ message: "Would you like to deploy your docs to GitHub?"
4048
+ });
4049
+ const wantsDeploy = !p8.isCancel(shouldDeploy) && shouldDeploy;
4050
+ let deployConfig;
4051
+ let vercelToken = null;
4052
+ let vercelScope;
4053
+ let wantsVercel = false;
4054
+ let wantsCi = false;
4055
+ let ciBranch;
4056
+ if (wantsDeploy) {
4057
+ const octokit = new Octokit4({ auth: token });
4058
+ let username;
4059
+ try {
4060
+ const { data } = await octokit.rest.users.getAuthenticated();
4061
+ username = data.login;
4062
+ } catch {
4063
+ p8.log.error("Failed to fetch GitHub user info.");
4064
+ process.exit(1);
4065
+ }
4066
+ let orgs = [];
4067
+ try {
4068
+ const { data } = await octokit.rest.orgs.listForAuthenticatedUser({ per_page: 100 });
4069
+ orgs = data;
4070
+ } catch {
4071
+ }
4072
+ const ownerOptions = [
4073
+ { value: username, label: username, hint: "Personal account" },
4074
+ ...orgs.map((org) => ({ value: org.login, label: org.login, hint: "Organization" }))
4075
+ ];
4076
+ let owner = username;
4077
+ if (ownerOptions.length > 1) {
4078
+ const selected = await p8.select({
4079
+ message: "Where should the docs repo be created?",
4080
+ options: ownerOptions
4081
+ });
4082
+ if (p8.isCancel(selected)) {
4083
+ p8.cancel("Operation cancelled");
4084
+ process.exit(0);
4085
+ }
4086
+ owner = selected;
4087
+ }
4088
+ const slug = projectName ? projectName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "") : repos[0]?.name;
4089
+ const defaultName = slug ? `${slug}-docs` : "my-project-docs";
4090
+ const repoNameInput = await p8.text({
4091
+ message: "Name for the docs GitHub repo:",
4092
+ initialValue: defaultName,
4093
+ validate: (v) => {
4094
+ if (!v || v.length === 0) return "Repo name is required";
4095
+ if (!/^[a-zA-Z0-9._-]+$/.test(v)) return "Invalid repo name";
4096
+ }
4097
+ });
4098
+ if (p8.isCancel(repoNameInput)) {
4099
+ p8.cancel("Operation cancelled");
4100
+ process.exit(0);
4101
+ }
4102
+ const visibilityInput = await p8.select({
4103
+ message: "Repository visibility:",
4104
+ options: [
4105
+ { value: "public", label: "Public" },
4106
+ { value: "private", label: "Private" }
4107
+ ]
4108
+ });
4109
+ if (p8.isCancel(visibilityInput)) {
4110
+ p8.cancel("Operation cancelled");
4111
+ process.exit(0);
4112
+ }
4113
+ deployConfig = {
4114
+ owner,
4115
+ repoName: repoNameInput,
4116
+ visibility: visibilityInput
4117
+ };
4118
+ const shouldDeployVercel = await p8.confirm({
4119
+ message: "Would you like to deploy to Vercel? (auto-deploys on every push)"
4120
+ });
4121
+ wantsVercel = !p8.isCancel(shouldDeployVercel) && shouldDeployVercel;
4122
+ if (wantsVercel) {
4123
+ vercelToken = await authenticateVercel();
4124
+ if (vercelToken) {
4125
+ try {
4126
+ const teamsRes = await fetch("https://api.vercel.com/v2/teams", {
4127
+ headers: { Authorization: `Bearer ${vercelToken}` }
4128
+ });
4129
+ const teamsData = await teamsRes.json();
4130
+ const teams = teamsData?.teams ?? [];
4131
+ if (teams.length > 0) {
4132
+ const userRes = await fetch("https://api.vercel.com/v2/user", {
4133
+ headers: { Authorization: `Bearer ${vercelToken}` }
4134
+ });
4135
+ const userData = await userRes.json();
4136
+ const vercelUsername = userData?.user?.username ?? "Personal";
4137
+ const scopeOptions = [
4138
+ { value: "__personal__", label: vercelUsername, hint: "Personal account" },
4139
+ ...teams.map((t) => ({ value: t.id, label: t.name || t.slug, hint: "Team" }))
4140
+ ];
4141
+ const selectedScope = await p8.select({
4142
+ message: "Which Vercel scope should own this project?",
4143
+ options: scopeOptions
4144
+ });
4145
+ if (p8.isCancel(selectedScope)) {
4146
+ p8.cancel("Operation cancelled");
4147
+ process.exit(0);
4148
+ }
4149
+ vercelScope = {
4150
+ teamId: selectedScope === "__personal__" ? void 0 : selectedScope
4151
+ };
4152
+ } else {
4153
+ vercelScope = { teamId: void 0 };
4154
+ }
4155
+ } catch {
4156
+ vercelScope = { teamId: void 0 };
4157
+ }
4158
+ } else {
4159
+ wantsVercel = false;
4160
+ }
4161
+ }
4162
+ const shouldSetupCi = await p8.confirm({
4163
+ message: "Would you like to set up CI to auto-update docs on every push?"
4164
+ });
4165
+ wantsCi = !p8.isCancel(shouldSetupCi) && shouldSetupCi;
4166
+ if (wantsCi) {
4167
+ const branchInput = await p8.text({
4168
+ message: "Which branch should trigger doc updates?",
4169
+ initialValue: "main",
4170
+ validate: (v) => v.length === 0 ? "Branch name is required" : void 0
4171
+ });
4172
+ if (p8.isCancel(branchInput)) {
4173
+ p8.cancel("Operation cancelled");
4174
+ process.exit(0);
4175
+ }
4176
+ ciBranch = branchInput;
4177
+ }
4178
+ }
4179
+ p8.log.step("All configured! Starting analysis and generation...");
4180
+ const outputDir = path10.resolve(options.output || "docs-site");
4181
+ const cacheDir = path10.join(outputDir, ".autodoc-cache");
3964
4182
  const cloneSpinner = p8.spinner();
3965
4183
  cloneSpinner.start(`Cloning ${repos.length} repositories...`);
3966
4184
  const clones = [];
@@ -3998,6 +4216,11 @@ Try reinstalling: npm install -g @latent-space-labs/open-auto-doc`
3998
4216
  progressTable.update(repoName, { activity: formatToolActivity(event) });
3999
4217
  }
4000
4218
  });
4219
+ try {
4220
+ const headSha = getHeadSha(cloned.localPath);
4221
+ saveCache(cacheDir, repoName, headSha, result);
4222
+ } catch {
4223
+ }
4001
4224
  progressTable.update(repoName, { status: "done", summary: buildRepoSummary(result) });
4002
4225
  return { repo: repoName, result };
4003
4226
  } catch (err) {
@@ -4033,7 +4256,6 @@ Try reinstalling: npm install -g @latent-space-labs/open-auto-doc`
4033
4256
  p8.log.warn(`Cross-repo error: ${err instanceof Error ? err.message : err}`);
4034
4257
  }
4035
4258
  }
4036
- const outputDir = path10.resolve(options.output || "docs-site");
4037
4259
  if (!projectName) {
4038
4260
  projectName = results.length === 1 ? results[0].repoName : "My Project";
4039
4261
  }
@@ -4081,10 +4303,7 @@ Try reinstalling: npm install -g @latent-space-labs/open-auto-doc`
4081
4303
  p8.log.warn(`Build check skipped: ${err instanceof Error ? err.message : err}`);
4082
4304
  }
4083
4305
  p8.log.success("Documentation generated successfully!");
4084
- const shouldSetupMcp = await p8.confirm({
4085
- message: "Set up MCP server so Claude Code can query your docs?"
4086
- });
4087
- if (!p8.isCancel(shouldSetupMcp) && shouldSetupMcp) {
4306
+ if (wantsMcp) {
4088
4307
  await setupMcpConfig({ outputDir });
4089
4308
  }
4090
4309
  let devServer;
@@ -4097,10 +4316,7 @@ Try reinstalling: npm install -g @latent-space-labs/open-auto-doc`
4097
4316
  p8.log.warn("Could not start preview server. You can run it manually:");
4098
4317
  p8.log.info(` cd ${path10.relative(process.cwd(), outputDir)} && npm run dev`);
4099
4318
  }
4100
- const shouldDeploy = await p8.confirm({
4101
- message: "Would you like to deploy your docs to GitHub?"
4102
- });
4103
- if (p8.isCancel(shouldDeploy) || !shouldDeploy) {
4319
+ if (!wantsDeploy || !deployConfig) {
4104
4320
  if (devServer) {
4105
4321
  killDevServer(devServer);
4106
4322
  }
@@ -4117,7 +4333,8 @@ Try reinstalling: npm install -g @latent-space-labs/open-auto-doc`
4117
4333
  const deployResult = await createAndPushDocsRepo({
4118
4334
  token,
4119
4335
  docsDir: outputDir,
4120
- config
4336
+ config,
4337
+ preCollected: deployConfig
4121
4338
  });
4122
4339
  if (!deployResult) {
4123
4340
  p8.note(
@@ -4128,51 +4345,35 @@ Try reinstalling: npm install -g @latent-space-labs/open-auto-doc`
4128
4345
  return;
4129
4346
  }
4130
4347
  let vercelDeployed = false;
4131
- const shouldDeployVercel = await p8.confirm({
4132
- message: "Would you like to deploy to Vercel? (auto-deploys on every push)"
4133
- });
4134
- if (!p8.isCancel(shouldDeployVercel) && shouldDeployVercel) {
4135
- const vercelToken = await authenticateVercel();
4136
- if (vercelToken) {
4137
- const vercelResult = await deployToVercel({
4138
- token: vercelToken,
4139
- githubOwner: deployResult.owner,
4140
- githubRepo: deployResult.repoName,
4141
- docsDir: outputDir,
4142
- config
4143
- });
4144
- if (vercelResult) {
4145
- p8.log.success(`Live at: ${vercelResult.deploymentUrl}`);
4146
- vercelDeployed = true;
4147
- }
4148
- }
4149
- }
4150
- const shouldSetupCi = await p8.confirm({
4151
- message: "Would you like to set up CI to auto-update docs on every push?"
4152
- });
4153
- if (p8.isCancel(shouldSetupCi) || !shouldSetupCi) {
4154
- if (!vercelDeployed) {
4155
- showVercelInstructions(deployResult.owner, deployResult.repoName);
4348
+ if (wantsVercel && vercelToken) {
4349
+ const vercelResult = await deployToVercel({
4350
+ token: vercelToken,
4351
+ githubOwner: deployResult.owner,
4352
+ githubRepo: deployResult.repoName,
4353
+ docsDir: outputDir,
4354
+ config,
4355
+ scope: vercelScope
4356
+ });
4357
+ if (vercelResult) {
4358
+ p8.log.success(`Live at: ${vercelResult.deploymentUrl}`);
4359
+ vercelDeployed = true;
4156
4360
  }
4157
- p8.outro(`Docs repo: https://github.com/${deployResult.owner}/${deployResult.repoName}`);
4158
- return;
4159
4361
  }
4160
- const gitRoot = getGitRoot();
4161
- if (!gitRoot) {
4162
- p8.log.warn("Not in a git repository \u2014 skipping CI setup. Run `open-auto-doc setup-ci` from your project root later.");
4163
- if (!vercelDeployed) {
4164
- showVercelInstructions(deployResult.owner, deployResult.repoName);
4362
+ if (wantsCi) {
4363
+ const gitRoot = getGitRoot();
4364
+ if (!gitRoot) {
4365
+ p8.log.warn("Not in a git repository \u2014 skipping CI setup. Run `open-auto-doc setup-ci` from your project root later.");
4366
+ } else {
4367
+ await createCiWorkflow({
4368
+ gitRoot,
4369
+ docsRepoUrl: deployResult.repoUrl,
4370
+ outputDir,
4371
+ token,
4372
+ config,
4373
+ branch: ciBranch
4374
+ });
4165
4375
  }
4166
- p8.outro(`Docs repo: https://github.com/${deployResult.owner}/${deployResult.repoName}`);
4167
- return;
4168
4376
  }
4169
- const ciResult = await createCiWorkflow({
4170
- gitRoot,
4171
- docsRepoUrl: deployResult.repoUrl,
4172
- outputDir,
4173
- token,
4174
- config
4175
- });
4176
4377
  if (!vercelDeployed) {
4177
4378
  showVercelInstructions(deployResult.owner, deployResult.repoName);
4178
4379
  }
@@ -4579,7 +4780,7 @@ async function logoutCommand() {
4579
4780
 
4580
4781
  // src/index.ts
4581
4782
  var program = new Command();
4582
- program.name("open-auto-doc").description("Auto-generate beautiful documentation websites from GitHub repositories using AI").version("0.5.2");
4783
+ program.name("open-auto-doc").description("Auto-generate beautiful documentation websites from GitHub repositories using AI").version("0.5.3");
4583
4784
  program.command("init", { isDefault: true }).description("Initialize and generate documentation for your repositories").option("-o, --output <dir>", "Output directory", "docs-site").action(initCommand);
4584
4785
  program.command("generate").description("Regenerate documentation using existing configuration").option("--incremental", "Only re-analyze changed files (uses cached results)").option("--force", "Force full regeneration (ignore cache)").option("--repo <name>", "Only analyze this repo (uses cache for others)").action(generateCommand);
4585
4786
  program.command("deploy").description("Create a GitHub repo for docs and push (connect to Vercel for auto-deploy)").option("-d, --dir <path>", "Docs site directory").action(deployCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@latent-space-labs/open-auto-doc",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
4
4
  "description": "Auto-generate beautiful documentation websites from GitHub repositories using AI",
5
5
  "type": "module",
6
6
  "bin": {