@gh-symphony/cli 0.4.6 → 0.4.9

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.
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  workflow_init_default
4
- } from "./chunk-DENDF6S6.js";
4
+ } from "./chunk-77INSSMM.js";
5
5
  import {
6
6
  fetchGithubProjectIssueByRepositoryAndNumber,
7
7
  inspectManagedProjectSelection,
8
8
  resolveTrackerAdapter
9
- } from "./chunk-RMNLHTIK.js";
9
+ } from "./chunk-D7HICFZ5.js";
10
10
  import {
11
11
  GitHubApiError,
12
12
  createClient,
@@ -14,12 +14,12 @@ import {
14
14
  getGhTokenWithSource,
15
15
  getProjectDetail,
16
16
  validateGitHubToken
17
- } from "./chunk-SMNIGNS3.js";
17
+ } from "./chunk-FFY5VKNV.js";
18
18
  import {
19
19
  buildPromptVariables,
20
20
  parseWorkflowMarkdown,
21
21
  renderPrompt
22
- } from "./chunk-RGCSM2KZ.js";
22
+ } from "./chunk-77H5ED5L.js";
23
23
  import {
24
24
  loadActiveProjectConfig
25
25
  } from "./chunk-YZP5N5XP.js";
@@ -5,18 +5,23 @@ import {
5
5
  parseIssueReference,
6
6
  readGitHubProjectBinding,
7
7
  renderIssueWorkflowPreview
8
- } from "./chunk-FBJRJWE5.js";
9
- import "./chunk-DENDF6S6.js";
8
+ } from "./chunk-RMXW2QQF.js";
9
+ import {
10
+ commandExistsOnPath
11
+ } from "./chunk-JNEP7OYK.js";
12
+ import "./chunk-77INSSMM.js";
10
13
  import {
11
14
  fetchGithubProjectIssueByRepositoryAndNumber,
12
15
  fetchGithubProjectIssues,
13
16
  inspectManagedProjectSelection
14
- } from "./chunk-RMNLHTIK.js";
15
- import "./chunk-XLDJTMW5.js";
17
+ } from "./chunk-D7HICFZ5.js";
18
+ import "./chunk-MAJOIZ5Q.js";
19
+ import "./chunk-LSH5HRQT.js";
16
20
  import {
17
21
  resolveRuntimeRoot
18
22
  } from "./chunk-3IRPSPAF.js";
19
23
  import {
24
+ DEFAULT_GITHUB_GRAPHQL_API_URL,
20
25
  GitHubApiError,
21
26
  REQUIRED_GH_SCOPES,
22
27
  checkGhAuthenticated,
@@ -31,16 +36,18 @@ import {
31
36
  runGhAuthLogin,
32
37
  runGhAuthRefresh,
33
38
  validateGitHubToken
34
- } from "./chunk-SMNIGNS3.js";
39
+ } from "./chunk-FFY5VKNV.js";
35
40
  import {
36
41
  isClaudeRuntimeCommand,
37
- parseWorkflowMarkdown,
38
- redactObservabilityDiagnosticsWithStats,
39
- redactObservabilityTextWithStats,
40
42
  resolveClaudeCommandBinary,
41
43
  resolveRuntimeCommandBinary,
42
44
  runClaudePreflight
43
- } from "./chunk-RGCSM2KZ.js";
45
+ } from "./chunk-PKUD2SVX.js";
46
+ import {
47
+ parseWorkflowMarkdown,
48
+ redactObservabilityDiagnosticsWithStats,
49
+ redactObservabilityTextWithStats
50
+ } from "./chunk-77H5ED5L.js";
44
51
  import {
45
52
  configFilePath,
46
53
  orchestratorLogPath,
@@ -51,7 +58,7 @@ import {
51
58
  import { constants as constants2 } from "fs";
52
59
  import { execFileSync, spawnSync } from "child_process";
53
60
  import { access as access2, mkdir as mkdir2, readFile as readFile2, stat as stat2 } from "fs/promises";
54
- import { delimiter, isAbsolute, join as join2, resolve as resolve2 } from "path";
61
+ import { isAbsolute, join as join2, resolve as resolve2 } from "path";
55
62
 
56
63
  // src/support/bundle.ts
57
64
  import { constants } from "fs";
@@ -537,6 +544,67 @@ function warnCheck(id, title, summary, remediation, details) {
537
544
  function formatAuthSource(source) {
538
545
  return source === "env" ? "GITHUB_GRAPHQL_TOKEN" : "gh CLI";
539
546
  }
547
+ function normalizeGitHubGraphqlEndpoint(value) {
548
+ const trimmed = value?.trim();
549
+ if (!trimmed) {
550
+ return null;
551
+ }
552
+ try {
553
+ const url = new URL(trimmed);
554
+ url.hostname = url.hostname.toLowerCase();
555
+ url.pathname = url.pathname.replace(/\/+$/, "") || "/";
556
+ url.search = "";
557
+ url.hash = "";
558
+ return url.toString().replace(/\/$/, "");
559
+ } catch {
560
+ return trimmed.replace(/\/+$/, "");
561
+ }
562
+ }
563
+ function deriveGhHostnameFromGraphqlEndpoint(endpoint) {
564
+ try {
565
+ const hostname = new URL(endpoint).hostname.toLowerCase();
566
+ return hostname === "api.github.com" ? "github.com" : hostname;
567
+ } catch {
568
+ return void 0;
569
+ }
570
+ }
571
+ function resolveGitHubGraphqlEndpoint(input) {
572
+ const trackerApiUrl = normalizeGitHubGraphqlEndpoint(input.trackerApiUrl);
573
+ const envApiUrl = normalizeGitHubGraphqlEndpoint(input.envApiUrl);
574
+ const resolvedEndpoint = trackerApiUrl ?? envApiUrl ?? DEFAULT_GITHUB_GRAPHQL_API_URL;
575
+ return {
576
+ resolvedEndpoint,
577
+ source: trackerApiUrl ? "tracker" : envApiUrl ? "env" : "default",
578
+ trackerApiUrl,
579
+ envApiUrl,
580
+ mismatch: trackerApiUrl !== null && envApiUrl !== null && trackerApiUrl !== envApiUrl,
581
+ ghHostname: deriveGhHostnameFromGraphqlEndpoint(resolvedEndpoint)
582
+ };
583
+ }
584
+ function buildGitHubGraphqlEndpointCheck(input) {
585
+ const details = {
586
+ resolvedEndpoint: input.resolvedEndpoint,
587
+ source: input.source,
588
+ trackerApiUrl: input.trackerApiUrl,
589
+ envApiUrl: input.envApiUrl,
590
+ ghHostname: input.ghHostname
591
+ };
592
+ if (input.mismatch) {
593
+ return warnCheck(
594
+ "github_graphql_endpoint",
595
+ "GitHub GraphQL endpoint",
596
+ `Resolved GitHub GraphQL endpoint is ${input.resolvedEndpoint}, but GITHUB_GRAPHQL_API_URL is ${input.envApiUrl}.`,
597
+ "Keep WORKFLOW.md tracker.endpoint/tracker.apiUrl and GITHUB_GRAPHQL_API_URL aligned, or unset GITHUB_GRAPHQL_API_URL so the workflow endpoint is authoritative.",
598
+ details
599
+ );
600
+ }
601
+ return passCheck(
602
+ "github_graphql_endpoint",
603
+ "GitHub GraphQL endpoint",
604
+ `Resolved GitHub GraphQL endpoint: ${input.resolvedEndpoint}.`,
605
+ details
606
+ );
607
+ }
540
608
  function remediationStep(id, checkId, title, status, summary, command, details) {
541
609
  return { id, checkId, title, status, summary, command, details };
542
610
  }
@@ -626,49 +694,6 @@ function formatGhSymphonyCommand(args, deps, options) {
626
694
  );
627
695
  return `gh-symphony ${commandArgs.join(" ")}`;
628
696
  }
629
- function getCommandCandidates(binary, deps) {
630
- if (deps.platform !== "win32") {
631
- return [binary];
632
- }
633
- const pathExts = (deps.pathExtEnv ?? ".COM;.EXE;.BAT;.CMD").split(";").map((ext) => ext.trim()).filter(Boolean);
634
- const normalizedBinary = binary.toLowerCase();
635
- if (pathExts.some((ext) => normalizedBinary.endsWith(ext.toLowerCase()))) {
636
- return [binary];
637
- }
638
- return [binary, ...pathExts.map((ext) => `${binary}${ext}`)];
639
- }
640
- async function commandExistsOnPath(binary, deps) {
641
- if (!binary) {
642
- return false;
643
- }
644
- const candidates = getCommandCandidates(binary, deps);
645
- if (isAbsolute(binary) || binary.includes("/") || binary.includes("\\")) {
646
- for (const candidate of candidates) {
647
- try {
648
- await deps.access(resolve2(candidate), constants2.X_OK);
649
- return true;
650
- } catch {
651
- continue;
652
- }
653
- }
654
- return false;
655
- }
656
- for (const segment of (deps.pathEnv ?? "").split(delimiter)) {
657
- if (!segment) {
658
- continue;
659
- }
660
- for (const command of candidates) {
661
- const candidate = join2(segment, command);
662
- try {
663
- await deps.access(candidate, constants2.X_OK);
664
- return true;
665
- } catch {
666
- continue;
667
- }
668
- }
669
- }
670
- return false;
671
- }
672
697
  function toDoctorClaudeCheck(check) {
673
698
  const id = check.id;
674
699
  if (check.status === "pass") {
@@ -768,7 +793,10 @@ function buildGithubTrackerConfig(input) {
768
793
  }
769
794
  async function checkLinearTrackerResolution(input) {
770
795
  const tracker = input.projectConfig.projectConfig.tracker;
771
- const projectSlug = readStringSetting(tracker.settings, "projectSlug")?.trim();
796
+ const projectSlug = readStringSetting(
797
+ tracker.settings,
798
+ "projectSlug"
799
+ )?.trim();
772
800
  const activeStates = readLinearActiveStates(tracker.settings);
773
801
  const pickupLabels = readLinearPickupLabels(tracker.settings);
774
802
  if (!projectSlug) {
@@ -915,7 +943,9 @@ async function fetchLinearProjectBySlug(input) {
915
943
  })
916
944
  });
917
945
  if (!response.ok) {
918
- throw new Error(`Linear GraphQL request failed with HTTP ${response.status}.`);
946
+ throw new Error(
947
+ `Linear GraphQL request failed with HTTP ${response.status}.`
948
+ );
919
949
  }
920
950
  const payload = await response.json();
921
951
  if (payload.errors?.length) {
@@ -1453,6 +1483,7 @@ ${DOCTOR_USAGE}`);
1453
1483
  let resolvedProjectConfig = null;
1454
1484
  let resolvedGithubProjectDetail = null;
1455
1485
  let resolvedGithubProjectBindingId = null;
1486
+ let githubGraphqlEndpoint = null;
1456
1487
  const envToken = deps.getEnvGitHubToken();
1457
1488
  const currentNodeVersion = deps.processVersion;
1458
1489
  const currentNodeMajor = parseMajorNodeVersion(currentNodeVersion);
@@ -1531,19 +1562,68 @@ ${DOCTOR_USAGE}`);
1531
1562
  )
1532
1563
  );
1533
1564
  }
1534
- const ghAuth = ghInstalled ? deps.checkGhAuthenticated() : { authenticated: false };
1535
- const ghScopes = ghInstalled && ghAuth.authenticated ? deps.checkGhScopes() : { valid: false, missing: [...REQUIRED_GH_SCOPES], scopes: [] };
1565
+ resolvedProjectConfig = await deps.inspectManagedProjectSelection({
1566
+ configDir: options.configDir,
1567
+ requestedProjectId: parsedArgs.projectId
1568
+ });
1569
+ if (resolvedProjectConfig.kind === "resolved") {
1570
+ resolvedProjectId = resolvedProjectConfig.projectId;
1571
+ checks.push(
1572
+ passCheck(
1573
+ "managed_project",
1574
+ "Managed project selection",
1575
+ `Resolved managed project "${resolvedProjectConfig.projectId}".`,
1576
+ {
1577
+ projectId: resolvedProjectConfig.projectId,
1578
+ workspaceDir: resolvedProjectConfig.projectConfig.workspaceDir
1579
+ }
1580
+ )
1581
+ );
1582
+ if (resolvedProjectConfig.projectConfig.tracker.adapter !== "linear") {
1583
+ githubGraphqlEndpoint = resolveGitHubGraphqlEndpoint({
1584
+ trackerApiUrl: resolvedProjectConfig.projectConfig.tracker.apiUrl,
1585
+ envApiUrl: process.env.GITHUB_GRAPHQL_API_URL
1586
+ });
1587
+ checks.push(buildGitHubGraphqlEndpointCheck(githubGraphqlEndpoint));
1588
+ }
1589
+ } else {
1590
+ checks.push(
1591
+ failCheck(
1592
+ "managed_project",
1593
+ "Managed project selection",
1594
+ resolvedProjectConfig.message,
1595
+ "Run 'gh-symphony repo init' from the target repository.",
1596
+ {
1597
+ reason: resolvedProjectConfig.kind,
1598
+ ...resolvedProjectConfig.projectId ? { projectId: resolvedProjectConfig.projectId } : {}
1599
+ }
1600
+ )
1601
+ );
1602
+ }
1603
+ const ghHostname = githubGraphqlEndpoint?.ghHostname;
1604
+ const ghAuth = ghInstalled ? deps.checkGhAuthenticated({ hostname: ghHostname }) : { authenticated: false };
1605
+ const ghScopes = ghInstalled && ghAuth.authenticated ? deps.checkGhScopes({ hostname: ghHostname }) : { valid: false, missing: [...REQUIRED_GH_SCOPES], scopes: [] };
1606
+ const ghHostnameArg = ghHostname ? ` --hostname ${ghHostname}` : "";
1607
+ const ghLoginCommand = `gh auth login --scopes ${REQUIRED_GH_SCOPES.join(",")}${ghHostnameArg}`;
1608
+ const ghRefreshCommand = `gh auth refresh --scopes ${REQUIRED_GH_SCOPES.join(",")}${ghHostnameArg}`;
1536
1609
  if (envToken) {
1537
1610
  try {
1538
- auth = await deps.validateGitHubToken(envToken, "env");
1611
+ auth = await deps.validateGitHubToken(envToken, "env", {
1612
+ apiUrl: githubGraphqlEndpoint?.resolvedEndpoint
1613
+ });
1539
1614
  } catch (error) {
1540
1615
  envTokenError = error instanceof Error ? error.message : "Unknown token validation error.";
1541
1616
  }
1542
1617
  }
1543
1618
  if (!auth && ghInstalled && ghAuth.authenticated && ghScopes.valid) {
1544
1619
  try {
1545
- const ghToken = deps.getGhToken({ allowEnv: false });
1546
- auth = await deps.validateGitHubToken(ghToken, "gh");
1620
+ const ghToken = deps.getGhToken({
1621
+ allowEnv: false,
1622
+ hostname: ghHostname
1623
+ });
1624
+ auth = await deps.validateGitHubToken(ghToken, "gh", {
1625
+ apiUrl: githubGraphqlEndpoint?.resolvedEndpoint
1626
+ });
1547
1627
  } catch (error) {
1548
1628
  tokenError = error instanceof Error ? error.message : "Unknown token retrieval error.";
1549
1629
  }
@@ -1575,7 +1655,7 @@ ${DOCTOR_USAGE}`);
1575
1655
  "gh_authentication",
1576
1656
  "GitHub authentication",
1577
1657
  "gh auth status failed or no GitHub login is configured.",
1578
- `Run 'gh auth login --scopes ${REQUIRED_GH_SCOPES.join(",")}' and re-run the doctor command.`
1658
+ `Run '${ghLoginCommand}' and re-run the doctor command.`
1579
1659
  )
1580
1660
  );
1581
1661
  }
@@ -1605,42 +1685,11 @@ ${DOCTOR_USAGE}`);
1605
1685
  "gh_scopes",
1606
1686
  "GitHub token scopes",
1607
1687
  `Missing required scopes: ${missingScopes.join(", ")}.`,
1608
- `Run 'gh auth refresh --scopes ${REQUIRED_GH_SCOPES.join(",")}' and confirm 'gh auth status' shows the updated scopes.`,
1688
+ `Run '${ghRefreshCommand}' and confirm 'gh auth status${ghHostnameArg}' shows the updated scopes.`,
1609
1689
  { missing: missingScopes, scopes: ghScopes.scopes }
1610
1690
  )
1611
1691
  );
1612
1692
  }
1613
- resolvedProjectConfig = await deps.inspectManagedProjectSelection({
1614
- configDir: options.configDir,
1615
- requestedProjectId: parsedArgs.projectId
1616
- });
1617
- if (resolvedProjectConfig.kind === "resolved") {
1618
- resolvedProjectId = resolvedProjectConfig.projectId;
1619
- checks.push(
1620
- passCheck(
1621
- "managed_project",
1622
- "Managed project selection",
1623
- `Resolved managed project "${resolvedProjectConfig.projectId}".`,
1624
- {
1625
- projectId: resolvedProjectConfig.projectId,
1626
- workspaceDir: resolvedProjectConfig.projectConfig.workspaceDir
1627
- }
1628
- )
1629
- );
1630
- } else {
1631
- checks.push(
1632
- failCheck(
1633
- "managed_project",
1634
- "Managed project selection",
1635
- resolvedProjectConfig.message,
1636
- "Run 'gh-symphony repo init' from the target repository.",
1637
- {
1638
- reason: resolvedProjectConfig.kind,
1639
- ...resolvedProjectConfig.projectId ? { projectId: resolvedProjectConfig.projectId } : {}
1640
- }
1641
- )
1642
- );
1643
- }
1644
1693
  if (resolvedProjectConfig.kind === "resolved" && resolvedProjectConfig.projectConfig.tracker.adapter === "linear") {
1645
1694
  checks.push(
1646
1695
  await checkLinearTrackerResolution({
@@ -1683,7 +1732,9 @@ ${DOCTOR_USAGE}`);
1683
1732
  throw new Error("Managed project is not bound to a GitHub Project.");
1684
1733
  }
1685
1734
  resolvedGithubProjectBindingId = bindingId;
1686
- const client = deps.createClient(auth.token);
1735
+ const client = deps.createClient(auth.token, {
1736
+ apiUrl: githubGraphqlEndpoint?.resolvedEndpoint
1737
+ });
1687
1738
  const detail = await deps.getProjectDetail(client, bindingId);
1688
1739
  resolvedGithubProjectDetail = detail;
1689
1740
  checks.push(
package/dist/index.d.ts CHANGED
@@ -1,10 +1,49 @@
1
1
  type GlobalOptions = {
2
2
  configDir: string;
3
+ configDirOverride?: boolean;
4
+ configDirSource?: "cli" | "env" | "default";
3
5
  verbose: boolean;
4
6
  json: boolean;
5
7
  noColor: boolean;
6
8
  };
7
9
  type CommandHandler = (args: string[], options: GlobalOptions) => Promise<void>;
10
+ type CliOptionValues = Partial<GlobalOptions & {
11
+ assignedOnly?: boolean;
12
+ config?: string;
13
+ daemon?: boolean;
14
+ dryRun?: boolean;
15
+ file?: string;
16
+ fix?: boolean;
17
+ follow?: boolean;
18
+ force?: boolean;
19
+ http?: string | boolean;
20
+ issue?: string;
21
+ level?: string;
22
+ logLevel?: string;
23
+ linearProjectSlug?: string;
24
+ nonInteractive?: boolean;
25
+ once?: boolean;
26
+ output?: string;
27
+ project?: string;
28
+ projectId?: string;
29
+ prune?: boolean;
30
+ run?: string;
31
+ runtime?: string;
32
+ skipContext?: boolean;
33
+ skipSkills?: boolean;
34
+ version?: boolean;
35
+ web?: string | boolean;
36
+ repoDir?: string;
37
+ workflowFile?: string;
38
+ workflow?: string;
39
+ watch?: boolean;
40
+ sample?: string;
41
+ smoke?: boolean;
42
+ bundle?: string | boolean;
43
+ attempt?: string;
44
+ tracker?: string;
45
+ }>;
46
+ declare function resolveGlobalOptions(values: CliOptionValues): GlobalOptions;
8
47
  declare function runCli(argv: string[]): Promise<void>;
9
48
 
10
- export { type CommandHandler, type GlobalOptions, runCli };
49
+ export { type CommandHandler, type GlobalOptions, resolveGlobalOptions, runCli };