@gh-symphony/cli 0.0.20 → 0.0.21

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.
@@ -2,22 +2,24 @@
2
2
  import {
3
3
  abortIfCancelled,
4
4
  generateProjectId,
5
+ warnIfProjectDiscoveryPartial,
5
6
  writeConfig
6
- } from "./chunk-RN2PACNV.js";
7
+ } from "./chunk-JN3TQVFV.js";
7
8
  import {
8
9
  start_default
9
- } from "./chunk-EKKT5USP.js";
10
+ } from "./chunk-KY6WKH66.js";
10
11
  import {
11
12
  GhAuthError,
12
13
  GitHubScopeError,
13
14
  checkRequiredScopes,
14
15
  createClient,
16
+ discoverUserProjects,
15
17
  getGhTokenWithSource,
16
18
  getProjectDetail,
17
19
  listUserProjects,
18
20
  resolveGitHubAuth,
19
21
  validateToken
20
- } from "./chunk-TILHWBP6.js";
22
+ } from "./chunk-C67H3OUL.js";
21
23
  import {
22
24
  status_default
23
25
  } from "./chunk-XN5ABWZ6.js";
@@ -47,6 +49,31 @@ import { readFile } from "fs/promises";
47
49
  import { join } from "path";
48
50
  var execFile = promisify(execFileCallback);
49
51
  var KNOWN_REQUIRED_SCOPES = ["repo", "read:org", "project"];
52
+ function formatProjectRepoSummary(selectedRepos, totalLinked) {
53
+ if (totalLinked === 0) {
54
+ return "none linked yet (0 linked)";
55
+ }
56
+ if (selectedRepos.length === totalLinked) {
57
+ return `${selectedRepos.map((repo) => `${repo.owner}/${repo.name}`).join(", ")} (all ${selectedRepos.length} linked)`;
58
+ }
59
+ if (selectedRepos.length === 0) {
60
+ return `none selected (0 of ${totalLinked} linked)`;
61
+ }
62
+ return `${selectedRepos.map((repo) => `${repo.owner}/${repo.name}`).join(", ")} (${selectedRepos.length} of ${totalLinked} linked)`;
63
+ }
64
+ function projectCreatedMessage(projectId, repositoryCount) {
65
+ const lines = [
66
+ `Project "${projectId}" created with ${repositoryCount} repositor${repositoryCount === 1 ? "y" : "ies"}.`,
67
+ "Run 'gh-symphony start' to begin orchestration."
68
+ ];
69
+ if (repositoryCount === 0) {
70
+ lines.push(
71
+ "Next step: run 'gh-symphony repo add <owner/name>' to register a repository.",
72
+ "Or add a repo-linked issue to the GitHub Project and re-run setup later."
73
+ );
74
+ }
75
+ return lines.join("\n");
76
+ }
50
77
  function displayScopeError(error, retryCommand) {
51
78
  const plural = error.requiredScopes.length === 1 ? "" : "s";
52
79
  p.log.error(
@@ -370,10 +397,9 @@ async function projectAddNonInteractive(flags, options) {
370
397
  JSON.stringify({ projectId, status: "created" }) + "\n"
371
398
  );
372
399
  } else {
373
- process.stdout.write(`Project created: ${projectId}
374
- `);
375
- process.stdout.write(`Run 'gh-symphony start' to begin orchestration.
376
- `);
400
+ process.stdout.write(
401
+ projectCreatedMessage(projectId, project.linkedRepositories.length) + "\n"
402
+ );
377
403
  }
378
404
  }
379
405
  async function projectAddInteractive(flags, options) {
@@ -417,10 +443,12 @@ async function projectAddInteractive(flags, options) {
417
443
  s2.start("Loading GitHub Project boards...");
418
444
  let projects;
419
445
  try {
420
- projects = await listUserProjects(client);
446
+ const discovery = await discoverUserProjects(client);
447
+ projects = discovery.projects;
421
448
  s2.stop(
422
449
  `Found ${projects.length} project${projects.length === 1 ? "" : "s"}`
423
450
  );
451
+ warnIfProjectDiscoveryPartial(discovery);
424
452
  } catch (error) {
425
453
  s2.stop("Failed to load projects.");
426
454
  if (error instanceof GitHubScopeError) {
@@ -463,10 +491,8 @@ async function projectAddInteractive(flags, options) {
463
491
  }
464
492
  if (projectDetail.linkedRepositories.length === 0) {
465
493
  p.log.warn(
466
- "No linked repositories found in this project. Add issues from repositories to the project first."
494
+ "No linked repositories found in this project. Add issues from repositories to the project, or run 'gh-symphony repo add owner/name' to validate and save a repository before your first orchestration run."
467
495
  );
468
- process.exitCode = 1;
469
- return;
470
496
  }
471
497
  const {
472
498
  assignedOnly: promptAssignedOnly,
@@ -479,7 +505,10 @@ async function projectAddInteractive(flags, options) {
479
505
  assignedOnlyInitialValue: flags.assignedOnly
480
506
  });
481
507
  const assignedOnly = flags.assignedOnly || promptAssignedOnly;
482
- const repoSummary = selectedRepos.length === projectDetail.linkedRepositories.length ? `${selectedRepos.map((repo) => `${repo.owner}/${repo.name}`).join(", ")} (all ${selectedRepos.length} linked)` : `${selectedRepos.map((repo) => `${repo.owner}/${repo.name}`).join(", ")} (${selectedRepos.length} of ${projectDetail.linkedRepositories.length} linked)`;
508
+ const repoSummary = formatProjectRepoSummary(
509
+ selectedRepos,
510
+ projectDetail.linkedRepositories.length
511
+ );
483
512
  p.note(
484
513
  renderProjectRegistrationSummary({
485
514
  login,
@@ -516,10 +545,7 @@ async function projectAddInteractive(flags, options) {
516
545
  process.exitCode = 1;
517
546
  return;
518
547
  }
519
- p.outro(
520
- `Project "${projectId}" created.
521
- Run 'gh-symphony start' to begin orchestration.`
522
- );
548
+ p.outro(projectCreatedMessage(projectId, selectedRepos.length));
523
549
  }
524
550
  async function promptProjectRegistrationOptions(input) {
525
551
  const assignedOnly = await abortIfCancelled(
@@ -537,23 +563,25 @@ async function promptProjectRegistrationOptions(input) {
537
563
  let selectedRepos = input.projectDetail.linkedRepositories;
538
564
  let workspaceDir = input.defaultWorkspaceDir;
539
565
  if (customizeAdvancedOptions) {
540
- const filterRepositories = await abortIfCancelled(
541
- p.confirm({
542
- message: "Filter specific repositories? (default: No)",
543
- initialValue: false
544
- })
545
- );
546
- if (filterRepositories) {
547
- selectedRepos = await abortIfCancelled(
548
- p.multiselect({
549
- message: "Select repositories to orchestrate:",
550
- options: input.projectDetail.linkedRepositories.map((repo) => ({
551
- value: repo,
552
- label: `${repo.owner}/${repo.name}`
553
- })),
554
- required: true
566
+ if (input.projectDetail.linkedRepositories.length > 0) {
567
+ const filterRepositories = await abortIfCancelled(
568
+ p.confirm({
569
+ message: "Filter specific repositories? (default: No)",
570
+ initialValue: false
555
571
  })
556
572
  );
573
+ if (filterRepositories) {
574
+ selectedRepos = await abortIfCancelled(
575
+ p.multiselect({
576
+ message: "Select repositories to orchestrate:",
577
+ options: input.projectDetail.linkedRepositories.map((repo) => ({
578
+ value: repo,
579
+ label: `${repo.owner}/${repo.name}`
580
+ })),
581
+ required: true
582
+ })
583
+ );
584
+ }
557
585
  }
558
586
  workspaceDir = await abortIfCancelled(
559
587
  p.text({