@kolatts/pncli 1.14.1 → 1.16.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.
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  setRepoConfigValue,
9
9
  writeGlobalConfig,
10
10
  writeRepoConfig
11
- } from "./chunk-CFUUOW7B.js";
11
+ } from "./chunk-P5NWDQ2B.js";
12
12
  import {
13
13
  ExitCode,
14
14
  PncliError,
@@ -20,13 +20,13 @@ import {
20
20
  success,
21
21
  warn,
22
22
  writeRawOutput
23
- } from "./chunk-BNMFIGXF.js";
23
+ } from "./chunk-XPPEXJBP.js";
24
24
  import {
25
25
  AdoCoreClient
26
26
  } from "./chunk-VDL32PSG.js";
27
27
 
28
28
  // src/cli.ts
29
- import { Command as Command2 } from "commander";
29
+ import { Command as Command3 } from "commander";
30
30
  import { createRequire } from "module";
31
31
 
32
32
  // src/services/git/client.ts
@@ -306,7 +306,7 @@ function parseBranchReportOutput(raw, branchName, opts) {
306
306
  }
307
307
  function normalizeGitDate(dateStr) {
308
308
  if (/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) {
309
- return `${dateStr}T00:00:00`;
309
+ return `${dateStr}T00:00:00+00:00`;
310
310
  }
311
311
  return dateStr;
312
312
  }
@@ -328,7 +328,7 @@ function getBranchReport(root, opts) {
328
328
  const args = buildReportArgs(opts);
329
329
  let raw = "";
330
330
  try {
331
- raw = execFileSync("git", args, { encoding: "utf8", cwd: root }).trim();
331
+ raw = execFileSync("git", args, { encoding: "utf8", cwd: root, maxBuffer: 200 * 1024 * 1024 }).trim();
332
332
  } catch {
333
333
  }
334
334
  let branchName = opts.branch ?? "";
@@ -455,6 +455,42 @@ function parseAdoRemote(remoteUrl, adoBaseUrl) {
455
455
  const collection = decodeURIComponent(parts[parts.length - 2]);
456
456
  return { collection, project, repo };
457
457
  }
458
+ function parseGitHubRemote(remoteUrl, baseUrl) {
459
+ let targetHost;
460
+ if (baseUrl) {
461
+ try {
462
+ const normalizedBase = /^https?:\/\//i.test(baseUrl) ? baseUrl : `https://${baseUrl}`;
463
+ const hostname = new URL(normalizedBase).hostname.toLowerCase();
464
+ targetHost = hostname === "api.github.com" ? "github.com" : hostname;
465
+ } catch {
466
+ targetHost = baseUrl.replace(/\/$/, "").replace(/^https?:\/\//i, "").split(/[:/]/)[0].toLowerCase();
467
+ }
468
+ } else {
469
+ targetHost = "github.com";
470
+ }
471
+ const sshMatch = remoteUrl.match(/^git@([^:]+):([^/]+)\/([^/]+?)(?:\.git)?$/);
472
+ if (sshMatch) {
473
+ const [, host, owner, repo] = sshMatch;
474
+ if (host.toLowerCase() === targetHost) {
475
+ return { owner, repo };
476
+ }
477
+ }
478
+ const sshProtoMatch = remoteUrl.match(/^ssh:\/\/git@([^/:]+)(?::\d+)?\/([^/]+)\/([^/]+?)(?:\.git)?$/);
479
+ if (sshProtoMatch) {
480
+ const [, host, owner, repo] = sshProtoMatch;
481
+ if (host.toLowerCase() === targetHost) {
482
+ return { owner, repo };
483
+ }
484
+ }
485
+ const httpsMatch = remoteUrl.match(/^https?:\/\/([^/:]+)(?::\d+)?\/([^/]+)\/([^/]+?)(?:\.git)?$/);
486
+ if (httpsMatch) {
487
+ const [, host, owner, repo] = httpsMatch;
488
+ if (host.toLowerCase() === targetHost) {
489
+ return { owner, repo };
490
+ }
491
+ }
492
+ return null;
493
+ }
458
494
  function getRemoteUrls(repoRoot) {
459
495
  try {
460
496
  const output = execSync2("git remote -v", { encoding: "utf8", cwd: repoRoot });
@@ -486,7 +522,15 @@ function getGitContext(config) {
486
522
  break;
487
523
  }
488
524
  }
489
- return { root, branch, project, repo, ado: adoContext };
525
+ let githubContext = null;
526
+ for (const url of remoteUrls) {
527
+ const parsed = parseGitHubRemote(url, config.github.baseUrl);
528
+ if (parsed) {
529
+ githubContext = parsed;
530
+ break;
531
+ }
532
+ }
533
+ return { root, branch, project, repo, ado: adoContext, github: githubContext };
490
534
  }
491
535
 
492
536
  // src/services/bitbucket/client.ts
@@ -1675,6 +1719,466 @@ function registerBitbucketCommands(program2) {
1675
1719
  });
1676
1720
  }
1677
1721
 
1722
+ // src/services/github/commands.ts
1723
+ import { Option } from "commander";
1724
+
1725
+ // src/services/github/client.ts
1726
+ var GitHubClient = class {
1727
+ constructor(http) {
1728
+ this.http = http;
1729
+ }
1730
+ http;
1731
+ async listPRs(opts) {
1732
+ return this.http.githubPaginate(
1733
+ (page, perPage) => this.http.github(
1734
+ `/repos/${opts.owner}/${opts.repo}/pulls`,
1735
+ {
1736
+ params: {
1737
+ state: opts.state ?? "open",
1738
+ ...opts.head ? { head: opts.head } : {},
1739
+ ...opts.base ? { base: opts.base } : {},
1740
+ per_page: perPage,
1741
+ page
1742
+ }
1743
+ }
1744
+ )
1745
+ );
1746
+ }
1747
+ async getPR(owner, repo, pullNumber) {
1748
+ return this.http.github(`/repos/${owner}/${repo}/pulls/${pullNumber}`);
1749
+ }
1750
+ async createPR(opts) {
1751
+ return this.http.github(
1752
+ `/repos/${opts.owner}/${opts.repo}/pulls`,
1753
+ {
1754
+ method: "POST",
1755
+ body: {
1756
+ title: opts.title,
1757
+ head: opts.head,
1758
+ base: opts.base,
1759
+ ...opts.body !== void 0 ? { body: opts.body } : {},
1760
+ ...opts.draft !== void 0 ? { draft: opts.draft } : {}
1761
+ }
1762
+ }
1763
+ );
1764
+ }
1765
+ async updatePR(opts) {
1766
+ const body = {};
1767
+ if (opts.title !== void 0) body.title = opts.title;
1768
+ if (opts.body !== void 0) body.body = opts.body;
1769
+ if (opts.state !== void 0) body.state = opts.state;
1770
+ if (opts.base !== void 0) body.base = opts.base;
1771
+ return this.http.github(
1772
+ `/repos/${opts.owner}/${opts.repo}/pulls/${opts.pullNumber}`,
1773
+ { method: "PATCH", body }
1774
+ );
1775
+ }
1776
+ async mergePR(opts) {
1777
+ const body = {};
1778
+ if (opts.commitTitle) body.commit_title = opts.commitTitle;
1779
+ if (opts.commitMessage) body.commit_message = opts.commitMessage;
1780
+ if (opts.mergeMethod) body.merge_method = opts.mergeMethod;
1781
+ return this.http.github(
1782
+ `/repos/${opts.owner}/${opts.repo}/pulls/${opts.pullNumber}/merge`,
1783
+ { method: "PUT", body }
1784
+ );
1785
+ }
1786
+ async closePR(owner, repo, pullNumber) {
1787
+ return this.updatePR({ owner, repo, pullNumber, state: "closed" });
1788
+ }
1789
+ async listComments(owner, repo, pullNumber) {
1790
+ return this.http.githubPaginate(
1791
+ (page, perPage) => this.http.github(
1792
+ `/repos/${owner}/${repo}/issues/${pullNumber}/comments`,
1793
+ { params: { per_page: perPage, page } }
1794
+ )
1795
+ );
1796
+ }
1797
+ async addComment(owner, repo, pullNumber, body) {
1798
+ return this.http.github(
1799
+ `/repos/${owner}/${repo}/issues/${pullNumber}/comments`,
1800
+ { method: "POST", body: { body } }
1801
+ );
1802
+ }
1803
+ async listReviewComments(owner, repo, pullNumber) {
1804
+ return this.http.githubPaginate(
1805
+ (page, perPage) => this.http.github(
1806
+ `/repos/${owner}/${repo}/pulls/${pullNumber}/comments`,
1807
+ { params: { per_page: perPage, page } }
1808
+ )
1809
+ );
1810
+ }
1811
+ async addInlineComment(opts) {
1812
+ return this.http.github(
1813
+ `/repos/${opts.owner}/${opts.repo}/pulls/${opts.pullNumber}/comments`,
1814
+ {
1815
+ method: "POST",
1816
+ body: {
1817
+ body: opts.body,
1818
+ commit_id: opts.commitId,
1819
+ path: opts.path,
1820
+ line: opts.line,
1821
+ side: opts.side ?? "RIGHT"
1822
+ }
1823
+ }
1824
+ );
1825
+ }
1826
+ async replyComment(owner, repo, pullNumber, commentId, body) {
1827
+ return this.http.github(
1828
+ `/repos/${owner}/${repo}/pulls/${pullNumber}/comments/${commentId}/replies`,
1829
+ { method: "POST", body: { body } }
1830
+ );
1831
+ }
1832
+ async deleteComment(owner, repo, commentId) {
1833
+ await this.http.github(
1834
+ `/repos/${owner}/${repo}/issues/comments/${commentId}`,
1835
+ { method: "DELETE" }
1836
+ );
1837
+ }
1838
+ async deleteReviewComment(owner, repo, commentId) {
1839
+ await this.http.github(
1840
+ `/repos/${owner}/${repo}/pulls/comments/${commentId}`,
1841
+ { method: "DELETE" }
1842
+ );
1843
+ }
1844
+ async getDiff(owner, repo, pullNumber) {
1845
+ return this.http.githubText(`/repos/${owner}/${repo}/pulls/${pullNumber}`);
1846
+ }
1847
+ async listFiles(owner, repo, pullNumber) {
1848
+ return this.http.githubPaginate(
1849
+ (page, perPage) => this.http.github(
1850
+ `/repos/${owner}/${repo}/pulls/${pullNumber}/files`,
1851
+ { params: { per_page: perPage, page } }
1852
+ )
1853
+ );
1854
+ }
1855
+ async listReviews(owner, repo, pullNumber) {
1856
+ return this.http.githubPaginate(
1857
+ (page, perPage) => this.http.github(
1858
+ `/repos/${owner}/${repo}/pulls/${pullNumber}/reviews`,
1859
+ { params: { per_page: perPage, page } }
1860
+ )
1861
+ );
1862
+ }
1863
+ async submitReview(opts) {
1864
+ return this.http.github(
1865
+ `/repos/${opts.owner}/${opts.repo}/pulls/${opts.pullNumber}/reviews`,
1866
+ {
1867
+ method: "POST",
1868
+ body: {
1869
+ event: opts.event,
1870
+ ...opts.body !== void 0 ? { body: opts.body } : {}
1871
+ }
1872
+ }
1873
+ );
1874
+ }
1875
+ async listRequestedReviewers(owner, repo, pullNumber) {
1876
+ return this.http.github(
1877
+ `/repos/${owner}/${repo}/pulls/${pullNumber}/requested_reviewers`
1878
+ );
1879
+ }
1880
+ async getCommitStatuses(owner, repo, ref) {
1881
+ return this.http.github(`/repos/${owner}/${repo}/commits/${ref}/status`);
1882
+ }
1883
+ async listCheckRuns(owner, repo, ref) {
1884
+ const results = [];
1885
+ let page = 1;
1886
+ const perPage = 100;
1887
+ while (true) {
1888
+ const result = await this.http.github(
1889
+ `/repos/${owner}/${repo}/commits/${ref}/check-runs`,
1890
+ { params: { per_page: perPage, page } }
1891
+ );
1892
+ const runs = result.check_runs ?? [];
1893
+ results.push(...runs);
1894
+ if (runs.length < perPage) break;
1895
+ page++;
1896
+ }
1897
+ return results;
1898
+ }
1899
+ };
1900
+
1901
+ // src/services/github/commands.ts
1902
+ function getClient3(program2, overrides) {
1903
+ const opts = program2.optsWithGlobals();
1904
+ const config = loadConfig({ configPath: opts.config });
1905
+ const http = createHttpClient(config, Boolean(opts.dryRun));
1906
+ const client = new GitHubClient(http);
1907
+ const ctx = getGitContext(config);
1908
+ const owner = overrides?.owner ?? opts.owner ?? ctx?.github?.owner ?? config.defaults.github?.owner ?? "";
1909
+ const repo = overrides?.repo ?? opts.repo ?? ctx?.github?.repo ?? config.defaults.github?.repo ?? "";
1910
+ if (!owner || !repo) {
1911
+ throw new PncliError(
1912
+ "Could not determine GitHub owner/repo. Pass --owner and --repo, or run pncli config init.",
1913
+ 1
1914
+ );
1915
+ }
1916
+ return { client, owner, repo, config };
1917
+ }
1918
+ function registerGitHubCommands(program2) {
1919
+ const gh = program2.command("github").description("GitHub repository operations").option("--owner <owner>", "GitHub owner (user or org)").option("--repo <repo>", "GitHub repository name");
1920
+ gh.command("list-prs").description("List pull requests").addOption(new Option("--state <state>", "PR state").choices(["open", "closed", "all"]).default("open")).option("--head <branch>", "Filter by head branch (user:branch)").option("--base <branch>", "Filter by base branch").action(async (opts) => {
1921
+ const start = Date.now();
1922
+ try {
1923
+ const { client, owner, repo } = getClient3(gh);
1924
+ const data = await client.listPRs({
1925
+ owner,
1926
+ repo,
1927
+ state: opts.state,
1928
+ head: opts.head,
1929
+ base: opts.base
1930
+ });
1931
+ success(data, "github", "list-prs", start);
1932
+ } catch (err) {
1933
+ fail(err, "github", "list-prs", start);
1934
+ }
1935
+ });
1936
+ gh.command("get-pr").description("Get a pull request by number").requiredOption("--number <n>", "Pull request number").action(async (opts) => {
1937
+ const start = Date.now();
1938
+ try {
1939
+ const { client, owner, repo } = getClient3(gh);
1940
+ const data = await client.getPR(owner, repo, parseInt(opts.number, 10));
1941
+ success(data, "github", "get-pr", start);
1942
+ } catch (err) {
1943
+ fail(err, "github", "get-pr", start);
1944
+ }
1945
+ });
1946
+ gh.command("create-pr").description("Create a pull request").requiredOption("--title <title>", "PR title").requiredOption("--head <branch>", "Source branch (or user:branch for forks)").option("--base <branch>", "Target branch (defaults to config or main)").option("--body <body>", "PR description").option("--draft", "Create as draft PR").option("--owner <owner>", "GitHub owner (overrides parent --owner)").option("--repo <repo>", "GitHub repository name (overrides parent --repo)").action(async (opts) => {
1947
+ const start = Date.now();
1948
+ try {
1949
+ const { client, owner, repo, config } = getClient3(gh, { owner: opts.owner, repo: opts.repo });
1950
+ const base = opts.base ?? config.defaults.github?.targetBranch ?? "main";
1951
+ const data = await client.createPR({
1952
+ owner,
1953
+ repo,
1954
+ title: opts.title,
1955
+ head: opts.head,
1956
+ base,
1957
+ body: opts.body,
1958
+ draft: opts.draft
1959
+ });
1960
+ success(data, "github", "create-pr", start);
1961
+ } catch (err) {
1962
+ fail(err, "github", "create-pr", start);
1963
+ }
1964
+ });
1965
+ gh.command("update-pr").description("Update a pull request").requiredOption("--number <n>", "Pull request number").option("--title <title>", "New title").option("--body <body>", "New description").option("--base <branch>", "New base branch").action(async (opts) => {
1966
+ const start = Date.now();
1967
+ try {
1968
+ const { client, owner, repo } = getClient3(gh);
1969
+ const data = await client.updatePR({
1970
+ owner,
1971
+ repo,
1972
+ pullNumber: parseInt(opts.number, 10),
1973
+ title: opts.title,
1974
+ body: opts.body,
1975
+ base: opts.base
1976
+ });
1977
+ success(data, "github", "update-pr", start);
1978
+ } catch (err) {
1979
+ fail(err, "github", "update-pr", start);
1980
+ }
1981
+ });
1982
+ gh.command("merge-pr").description("Merge a pull request").requiredOption("--number <n>", "Pull request number").addOption(new Option("--method <method>", "Merge method").choices(["merge", "squash", "rebase"]).default("merge")).option("--commit-title <title>", "Commit title for merge/squash").option("--commit-message <msg>", "Commit message for merge/squash").action(async (opts) => {
1983
+ const start = Date.now();
1984
+ try {
1985
+ const { client, owner, repo } = getClient3(gh);
1986
+ const data = await client.mergePR({
1987
+ owner,
1988
+ repo,
1989
+ pullNumber: parseInt(opts.number, 10),
1990
+ mergeMethod: opts.method,
1991
+ commitTitle: opts.commitTitle,
1992
+ commitMessage: opts.commitMessage
1993
+ });
1994
+ success(data, "github", "merge-pr", start);
1995
+ } catch (err) {
1996
+ fail(err, "github", "merge-pr", start);
1997
+ }
1998
+ });
1999
+ gh.command("close-pr").description("Close a pull request").requiredOption("--number <n>", "Pull request number").action(async (opts) => {
2000
+ const start = Date.now();
2001
+ try {
2002
+ const { client, owner, repo } = getClient3(gh);
2003
+ const data = await client.closePR(owner, repo, parseInt(opts.number, 10));
2004
+ success(data, "github", "close-pr", start);
2005
+ } catch (err) {
2006
+ fail(err, "github", "close-pr", start);
2007
+ }
2008
+ });
2009
+ gh.command("list-comments").description("List general comments on a pull request").requiredOption("--number <n>", "Pull request number").action(async (opts) => {
2010
+ const start = Date.now();
2011
+ try {
2012
+ const { client, owner, repo } = getClient3(gh);
2013
+ const data = await client.listComments(owner, repo, parseInt(opts.number, 10));
2014
+ success(data, "github", "list-comments", start);
2015
+ } catch (err) {
2016
+ fail(err, "github", "list-comments", start);
2017
+ }
2018
+ });
2019
+ gh.command("add-comment").description("Add a general comment to a pull request").requiredOption("--number <n>", "Pull request number").requiredOption("--body <text>", "Comment text").action(async (opts) => {
2020
+ const start = Date.now();
2021
+ try {
2022
+ const { client, owner, repo } = getClient3(gh);
2023
+ const data = await client.addComment(owner, repo, parseInt(opts.number, 10), opts.body);
2024
+ success(data, "github", "add-comment", start);
2025
+ } catch (err) {
2026
+ fail(err, "github", "add-comment", start);
2027
+ }
2028
+ });
2029
+ gh.command("list-review-comments").description("List inline review comments on a pull request").requiredOption("--number <n>", "Pull request number").action(async (opts) => {
2030
+ const start = Date.now();
2031
+ try {
2032
+ const { client, owner, repo } = getClient3(gh);
2033
+ const data = await client.listReviewComments(owner, repo, parseInt(opts.number, 10));
2034
+ success(data, "github", "list-review-comments", start);
2035
+ } catch (err) {
2036
+ fail(err, "github", "list-review-comments", start);
2037
+ }
2038
+ });
2039
+ gh.command("add-inline-comment").description("Add an inline review comment to a file in a pull request").requiredOption("--number <n>", "Pull request number").requiredOption("--commit <sha>", "Commit SHA the comment is on").requiredOption("--file <path>", "File path").requiredOption("--line <n>", "Line number").requiredOption("--body <text>", "Comment text").addOption(new Option("--side <side>", "Comment side").choices(["LEFT", "RIGHT"]).default("RIGHT")).action(async (opts) => {
2040
+ const start = Date.now();
2041
+ try {
2042
+ const { client, owner, repo } = getClient3(gh);
2043
+ const data = await client.addInlineComment({
2044
+ owner,
2045
+ repo,
2046
+ pullNumber: parseInt(opts.number, 10),
2047
+ body: opts.body,
2048
+ commitId: opts.commit,
2049
+ path: opts.file,
2050
+ line: parseInt(opts.line, 10),
2051
+ side: opts.side
2052
+ });
2053
+ success(data, "github", "add-inline-comment", start);
2054
+ } catch (err) {
2055
+ fail(err, "github", "add-inline-comment", start);
2056
+ }
2057
+ });
2058
+ gh.command("reply-comment").description("Reply to a review comment on a pull request").requiredOption("--number <n>", "Pull request number").requiredOption("--comment-id <id>", "Review comment ID to reply to").requiredOption("--body <text>", "Reply text").action(async (opts) => {
2059
+ const start = Date.now();
2060
+ try {
2061
+ const { client, owner, repo } = getClient3(gh);
2062
+ const data = await client.replyComment(owner, repo, parseInt(opts.number, 10), parseInt(opts.commentId, 10), opts.body);
2063
+ success(data, "github", "reply-comment", start);
2064
+ } catch (err) {
2065
+ fail(err, "github", "reply-comment", start);
2066
+ }
2067
+ });
2068
+ gh.command("delete-comment").description("Delete a general issue comment").requiredOption("--comment-id <id>", "Comment ID").action(async (opts) => {
2069
+ const start = Date.now();
2070
+ try {
2071
+ const { client, owner, repo } = getClient3(gh);
2072
+ await client.deleteComment(owner, repo, parseInt(opts.commentId, 10));
2073
+ success({ deleted: true }, "github", "delete-comment", start);
2074
+ } catch (err) {
2075
+ fail(err, "github", "delete-comment", start);
2076
+ }
2077
+ });
2078
+ gh.command("delete-review-comment").description("Delete an inline review comment").requiredOption("--comment-id <id>", "Review comment ID").action(async (opts) => {
2079
+ const start = Date.now();
2080
+ try {
2081
+ const { client, owner, repo } = getClient3(gh);
2082
+ await client.deleteReviewComment(owner, repo, parseInt(opts.commentId, 10));
2083
+ success({ deleted: true }, "github", "delete-review-comment", start);
2084
+ } catch (err) {
2085
+ fail(err, "github", "delete-review-comment", start);
2086
+ }
2087
+ });
2088
+ gh.command("diff").description("Get unified diff for a pull request").requiredOption("--number <n>", "Pull request number").action(async (opts) => {
2089
+ const start = Date.now();
2090
+ try {
2091
+ const { client, owner, repo } = getClient3(gh);
2092
+ const diff = await client.getDiff(owner, repo, parseInt(opts.number, 10));
2093
+ success({ diff }, "github", "diff", start);
2094
+ } catch (err) {
2095
+ fail(err, "github", "diff", start);
2096
+ }
2097
+ });
2098
+ gh.command("list-files").description("List files changed in a pull request").requiredOption("--number <n>", "Pull request number").action(async (opts) => {
2099
+ const start = Date.now();
2100
+ try {
2101
+ const { client, owner, repo } = getClient3(gh);
2102
+ const data = await client.listFiles(owner, repo, parseInt(opts.number, 10));
2103
+ success(data, "github", "list-files", start);
2104
+ } catch (err) {
2105
+ fail(err, "github", "list-files", start);
2106
+ }
2107
+ });
2108
+ gh.command("list-reviews").description("List reviews on a pull request").requiredOption("--number <n>", "Pull request number").action(async (opts) => {
2109
+ const start = Date.now();
2110
+ try {
2111
+ const { client, owner, repo } = getClient3(gh);
2112
+ const data = await client.listReviews(owner, repo, parseInt(opts.number, 10));
2113
+ success(data, "github", "list-reviews", start);
2114
+ } catch (err) {
2115
+ fail(err, "github", "list-reviews", start);
2116
+ }
2117
+ });
2118
+ gh.command("approve").description("Approve a pull request").requiredOption("--number <n>", "Pull request number").option("--body <text>", "Review body text").action(async (opts) => {
2119
+ const start = Date.now();
2120
+ try {
2121
+ const { client, owner, repo } = getClient3(gh);
2122
+ const data = await client.submitReview({
2123
+ owner,
2124
+ repo,
2125
+ pullNumber: parseInt(opts.number, 10),
2126
+ event: "APPROVE",
2127
+ body: opts.body
2128
+ });
2129
+ success(data, "github", "approve", start);
2130
+ } catch (err) {
2131
+ fail(err, "github", "approve", start);
2132
+ }
2133
+ });
2134
+ gh.command("request-changes").description("Request changes on a pull request").requiredOption("--number <n>", "Pull request number").requiredOption("--body <text>", "Review feedback text").action(async (opts) => {
2135
+ const start = Date.now();
2136
+ try {
2137
+ const { client, owner, repo } = getClient3(gh);
2138
+ const data = await client.submitReview({
2139
+ owner,
2140
+ repo,
2141
+ pullNumber: parseInt(opts.number, 10),
2142
+ event: "REQUEST_CHANGES",
2143
+ body: opts.body
2144
+ });
2145
+ success(data, "github", "request-changes", start);
2146
+ } catch (err) {
2147
+ fail(err, "github", "request-changes", start);
2148
+ }
2149
+ });
2150
+ gh.command("list-reviewers").description("List requested reviewers for a pull request").requiredOption("--number <n>", "Pull request number").action(async (opts) => {
2151
+ const start = Date.now();
2152
+ try {
2153
+ const { client, owner, repo } = getClient3(gh);
2154
+ const data = await client.listRequestedReviewers(owner, repo, parseInt(opts.number, 10));
2155
+ success(data, "github", "list-reviewers", start);
2156
+ } catch (err) {
2157
+ fail(err, "github", "list-reviewers", start);
2158
+ }
2159
+ });
2160
+ gh.command("get-status").description("Get combined commit status for a ref (branch, tag, or SHA)").requiredOption("--ref <ref>", "Git ref (branch, tag, or commit SHA)").action(async (opts) => {
2161
+ const start = Date.now();
2162
+ try {
2163
+ const { client, owner, repo } = getClient3(gh);
2164
+ const data = await client.getCommitStatuses(owner, repo, opts.ref);
2165
+ success(data, "github", "get-status", start);
2166
+ } catch (err) {
2167
+ fail(err, "github", "get-status", start);
2168
+ }
2169
+ });
2170
+ gh.command("list-checks").description("List check runs for a ref (branch, tag, or SHA)").requiredOption("--ref <ref>", "Git ref (branch, tag, or commit SHA)").action(async (opts) => {
2171
+ const start = Date.now();
2172
+ try {
2173
+ const { client, owner, repo } = getClient3(gh);
2174
+ const data = await client.listCheckRuns(owner, repo, opts.ref);
2175
+ success(data, "github", "list-checks", start);
2176
+ } catch (err) {
2177
+ fail(err, "github", "list-checks", start);
2178
+ }
2179
+ });
2180
+ }
2181
+
1678
2182
  // src/services/confluence/client.ts
1679
2183
  var API3 = "/rest/api";
1680
2184
  var ConfluenceClient = class {
@@ -1823,7 +2327,7 @@ var ConfluenceClient = class {
1823
2327
  };
1824
2328
 
1825
2329
  // src/services/confluence/commands.ts
1826
- function getClient3(program2) {
2330
+ function getClient4(program2) {
1827
2331
  const opts = program2.optsWithGlobals();
1828
2332
  const config = loadConfig({ configPath: opts.config });
1829
2333
  if (!config.confluence.baseUrl) throw new PncliError("Confluence not configured. Run: pncli config init");
@@ -1835,7 +2339,7 @@ function registerConfluenceCommands(program2) {
1835
2339
  confluence.command("get-page").description("Get a Confluence page by ID").requiredOption("--id <page-id>", "Page ID").option("--expand <fields>", "Comma-separated fields to expand", "body.storage,version,space,ancestors").action(async (opts) => {
1836
2340
  const start = Date.now();
1837
2341
  try {
1838
- const client = getClient3(program2);
2342
+ const client = getClient4(program2);
1839
2343
  const data = await client.getPage(opts.id, opts.expand);
1840
2344
  success(data, "confluence", "get-page", start);
1841
2345
  } catch (err) {
@@ -1845,7 +2349,7 @@ function registerConfluenceCommands(program2) {
1845
2349
  confluence.command("get-page-by-title").description("Find a Confluence page by space key and title").requiredOption("--space <key>", "Space key").requiredOption("--title <title>", "Page title").action(async (opts) => {
1846
2350
  const start = Date.now();
1847
2351
  try {
1848
- const client = getClient3(program2);
2352
+ const client = getClient4(program2);
1849
2353
  const data = await client.getPageByTitle(opts.space, opts.title);
1850
2354
  if (!data) throw new PncliError(`Page not found: "${opts.title}" in space ${opts.space}`, 1);
1851
2355
  success(data, "confluence", "get-page-by-title", start);
@@ -1856,7 +2360,7 @@ function registerConfluenceCommands(program2) {
1856
2360
  confluence.command("list-pages").description("List pages in a Confluence space (consider --output-file for large results)").requiredOption("--space <key>", "Space key").option("--limit <n>", "Max total pages to return (default: all)").option("--start <n>", "Offset for first result").action(async (opts) => {
1857
2361
  const start = Date.now();
1858
2362
  try {
1859
- const client = getClient3(program2);
2363
+ const client = getClient4(program2);
1860
2364
  const data = await client.listPages(opts.space, {
1861
2365
  limit: opts.limit ? parseInt(opts.limit, 10) : void 0,
1862
2366
  start: opts.start ? parseInt(opts.start, 10) : void 0
@@ -1869,7 +2373,7 @@ function registerConfluenceCommands(program2) {
1869
2373
  confluence.command("get-page-children").description("Get child pages of a Confluence page").requiredOption("--id <page-id>", "Parent page ID").action(async (opts) => {
1870
2374
  const start = Date.now();
1871
2375
  try {
1872
- const client = getClient3(program2);
2376
+ const client = getClient4(program2);
1873
2377
  const data = await client.getPageChildren(opts.id);
1874
2378
  success(data, "confluence", "get-page-children", start);
1875
2379
  } catch (err) {
@@ -1879,7 +2383,7 @@ function registerConfluenceCommands(program2) {
1879
2383
  confluence.command("get-labels").description("Get labels on a Confluence page").requiredOption("--id <page-id>", "Page ID").action(async (opts) => {
1880
2384
  const start = Date.now();
1881
2385
  try {
1882
- const client = getClient3(program2);
2386
+ const client = getClient4(program2);
1883
2387
  const data = await client.getLabels(opts.id);
1884
2388
  success(data, "confluence", "get-labels", start);
1885
2389
  } catch (err) {
@@ -1889,7 +2393,7 @@ function registerConfluenceCommands(program2) {
1889
2393
  confluence.command("search").description("Search Confluence with CQL").requiredOption("--cql <query>", 'CQL query string (e.g. "space=PROJ AND type=page")').option("--limit <n>", "Maximum number of results", "25").option("--start <n>", "Offset for first result", "0").option("--expand <fields>", "Comma-separated fields to expand").action(async (opts) => {
1890
2394
  const start = Date.now();
1891
2395
  try {
1892
- const client = getClient3(program2);
2396
+ const client = getClient4(program2);
1893
2397
  const data = await client.search(opts.cql, {
1894
2398
  limit: parseInt(opts.limit, 10),
1895
2399
  start: parseInt(opts.start, 10),
@@ -1903,7 +2407,7 @@ function registerConfluenceCommands(program2) {
1903
2407
  confluence.command("create-page").description("Create a new Confluence page").requiredOption("--space <key>", "Space key").requiredOption("--title <title>", "Page title").requiredOption("--body <html>", "Page body (storage format HTML)").option("--parent-id <id>", "Parent page ID (to nest under a page)").option("--representation <format>", "Body format: storage (default) or wiki", "storage").action(async (opts) => {
1904
2408
  const start = Date.now();
1905
2409
  try {
1906
- const client = getClient3(program2);
2410
+ const client = getClient4(program2);
1907
2411
  const data = await client.createPage({
1908
2412
  spaceKey: opts.space,
1909
2413
  title: opts.title,
@@ -1919,7 +2423,7 @@ function registerConfluenceCommands(program2) {
1919
2423
  confluence.command("update-page").description("Update a Confluence page (fetches current version automatically)").requiredOption("--id <page-id>", "Page ID").option("--title <title>", "New page title").option("--body <html>", "New page body (storage format HTML)").option("--status <status>", "Page status: current (default) or draft", "current").option("--representation <format>", "Body format: storage (default) or wiki", "storage").action(async (opts) => {
1920
2424
  const start = Date.now();
1921
2425
  try {
1922
- const client = getClient3(program2);
2426
+ const client = getClient4(program2);
1923
2427
  const current = await client.getPage(opts.id, "version");
1924
2428
  const nextVersion = current.version.number + 1;
1925
2429
  const data = await client.updatePage(opts.id, {
@@ -1937,7 +2441,7 @@ function registerConfluenceCommands(program2) {
1937
2441
  confluence.command("delete-page").description("Delete a Confluence page").requiredOption("--id <page-id>", "Page ID").action(async (opts) => {
1938
2442
  const start = Date.now();
1939
2443
  try {
1940
- const client = getClient3(program2);
2444
+ const client = getClient4(program2);
1941
2445
  await client.deletePage(opts.id);
1942
2446
  success({ deleted: opts.id }, "confluence", "delete-page", start);
1943
2447
  } catch (err) {
@@ -1947,7 +2451,7 @@ function registerConfluenceCommands(program2) {
1947
2451
  confluence.command("list-comments").description("List comments on a Confluence page").requiredOption("--id <page-id>", "Page ID").action(async (opts) => {
1948
2452
  const start = Date.now();
1949
2453
  try {
1950
- const client = getClient3(program2);
2454
+ const client = getClient4(program2);
1951
2455
  const data = await client.listComments(opts.id);
1952
2456
  success(data, "confluence", "list-comments", start);
1953
2457
  } catch (err) {
@@ -1957,7 +2461,7 @@ function registerConfluenceCommands(program2) {
1957
2461
  confluence.command("add-comment").description("Add a comment to a Confluence page").requiredOption("--id <page-id>", "Page ID").requiredOption("--body <text>", "Comment body (storage format HTML)").option("--representation <format>", "Body format: storage (default) or wiki", "storage").action(async (opts) => {
1958
2462
  const start = Date.now();
1959
2463
  try {
1960
- const client = getClient3(program2);
2464
+ const client = getClient4(program2);
1961
2465
  const data = await client.addComment(opts.id, opts.body, opts.representation);
1962
2466
  success(data, "confluence", "add-comment", start);
1963
2467
  } catch (err) {
@@ -1967,7 +2471,7 @@ function registerConfluenceCommands(program2) {
1967
2471
  confluence.command("add-label").description("Add labels to a Confluence page").requiredOption("--id <page-id>", "Page ID").requiredOption("--labels <names>", "Comma-separated label names").action(async (opts) => {
1968
2472
  const start = Date.now();
1969
2473
  try {
1970
- const client = getClient3(program2);
2474
+ const client = getClient4(program2);
1971
2475
  const labels = opts.labels.split(",").map((s) => s.trim()).filter(Boolean);
1972
2476
  const data = await client.addLabel(opts.id, labels);
1973
2477
  success(data, "confluence", "add-label", start);
@@ -1978,7 +2482,7 @@ function registerConfluenceCommands(program2) {
1978
2482
  confluence.command("remove-label").description("Remove a label from a Confluence page").requiredOption("--id <page-id>", "Page ID").requiredOption("--label <name>", "Label name to remove").action(async (opts) => {
1979
2483
  const start = Date.now();
1980
2484
  try {
1981
- const client = getClient3(program2);
2485
+ const client = getClient4(program2);
1982
2486
  await client.removeLabel(opts.id, opts.label);
1983
2487
  success({ removed: opts.label, from: opts.id }, "confluence", "remove-label", start);
1984
2488
  } catch (err) {
@@ -1988,7 +2492,7 @@ function registerConfluenceCommands(program2) {
1988
2492
  confluence.command("list-spaces").description("List Confluence spaces").option("--type <type>", "Space type: global or personal").option("--limit <n>", "Max total spaces to return (default: all)").action(async (opts) => {
1989
2493
  const start = Date.now();
1990
2494
  try {
1991
- const client = getClient3(program2);
2495
+ const client = getClient4(program2);
1992
2496
  const data = await client.listSpaces({
1993
2497
  type: opts.type,
1994
2498
  limit: opts.limit ? parseInt(opts.limit, 10) : void 0
@@ -2001,7 +2505,7 @@ function registerConfluenceCommands(program2) {
2001
2505
  confluence.command("get-space").description("Get a Confluence space by key").requiredOption("--key <space-key>", "Space key").action(async (opts) => {
2002
2506
  const start = Date.now();
2003
2507
  try {
2004
- const client = getClient3(program2);
2508
+ const client = getClient4(program2);
2005
2509
  const data = await client.getSpace(opts.key);
2006
2510
  success(data, "confluence", "get-space", start);
2007
2511
  } catch (err) {
@@ -2011,7 +2515,7 @@ function registerConfluenceCommands(program2) {
2011
2515
  confluence.command("list-attachments").description("List attachments on a Confluence page").requiredOption("--id <page-id>", "Page ID").action(async (opts) => {
2012
2516
  const start = Date.now();
2013
2517
  try {
2014
- const client = getClient3(program2);
2518
+ const client = getClient4(program2);
2015
2519
  const data = await client.listAttachments(opts.id);
2016
2520
  success(data, "confluence", "list-attachments", start);
2017
2521
  } catch (err) {
@@ -2123,7 +2627,7 @@ var SonarClient = class {
2123
2627
 
2124
2628
  // src/services/sonar/commands.ts
2125
2629
  var DEFAULT_METRICS = "coverage,duplicated_lines_density,bugs,vulnerabilities,code_smells,sqale_rating,reliability_rating,security_rating,ncloc";
2126
- function getClient4(program2) {
2630
+ function getClient5(program2) {
2127
2631
  const opts = program2.optsWithGlobals();
2128
2632
  const config = loadConfig({ configPath: opts.config });
2129
2633
  if (!config.sonar.baseUrl) throw new PncliError("SonarQube not configured. Run: pncli config init");
@@ -2140,7 +2644,7 @@ function registerSonarCommands(program2) {
2140
2644
  sonar.command("quality-gate").description("Get quality gate status for a project").option("--project <key>", "SonarQube project key (or set defaults.sonar.project in config)").option("--branch <name>", "Branch name").action(async (opts) => {
2141
2645
  const start = Date.now();
2142
2646
  try {
2143
- const { client, config } = getClient4(program2);
2647
+ const { client, config } = getClient5(program2);
2144
2648
  const projectKey = resolveProject(config, opts.project);
2145
2649
  const data = await client.getQualityGate(projectKey, opts.branch);
2146
2650
  success(data, "sonar", "quality-gate", start);
@@ -2151,7 +2655,7 @@ function registerSonarCommands(program2) {
2151
2655
  sonar.command("issues").description("Search/list issues for a project").option("--project <key>", "SonarQube project key (or set defaults.sonar.project in config)").option("--severities <list>", "Filter by severity: BLOCKER,CRITICAL,MAJOR,MINOR,INFO (comma-separated)").option("--types <list>", "Filter by type: BUG,VULNERABILITY,CODE_SMELL (comma-separated)").option("--statuses <list>", "Filter by status: OPEN,CONFIRMED,REOPENED,RESOLVED,CLOSED (comma-separated)").option("--branch <name>", "Branch name").option("--resolved <bool>", "Filter resolved issues: true or false").option("--page <n>", "Page number (1-based)", "1").option("--page-size <n>", "Results per page (max 500)", "100").option("--all", "Fetch all pages (consider --output-file for large results; ignores --page/--page-size)").action(async (opts) => {
2152
2656
  const start = Date.now();
2153
2657
  try {
2154
- const { client, config } = getClient4(program2);
2658
+ const { client, config } = getClient5(program2);
2155
2659
  const projectKey = resolveProject(config, opts.project);
2156
2660
  const baseOpts = {
2157
2661
  projectKey,
@@ -2179,7 +2683,7 @@ function registerSonarCommands(program2) {
2179
2683
  sonar.command("measures").description("Get key metrics for a project").option("--project <key>", "SonarQube project key (or set defaults.sonar.project in config)").option("--metrics <list>", "Comma-separated metric keys", DEFAULT_METRICS).option("--branch <name>", "Branch name").action(async (opts) => {
2180
2684
  const start = Date.now();
2181
2685
  try {
2182
- const { client, config } = getClient4(program2);
2686
+ const { client, config } = getClient5(program2);
2183
2687
  const projectKey = resolveProject(config, opts.project);
2184
2688
  const data = await client.getMeasures({
2185
2689
  projectKey,
@@ -2194,7 +2698,7 @@ function registerSonarCommands(program2) {
2194
2698
  sonar.command("projects").description("Search/list projects").option("--query <text>", "Search query").option("--page <n>", "Page number (1-based)", "1").option("--page-size <n>", "Results per page", "100").option("--all", "Fetch all pages (consider --output-file for large results)").action(async (opts) => {
2195
2699
  const start = Date.now();
2196
2700
  try {
2197
- const { client } = getClient4(program2);
2701
+ const { client } = getClient5(program2);
2198
2702
  if (opts.all) {
2199
2703
  const data = await client.searchAllProjects(opts.query);
2200
2704
  success(data, "sonar", "projects", start);
@@ -2213,7 +2717,7 @@ function registerSonarCommands(program2) {
2213
2717
  sonar.command("hotspots").description("List security hotspots for a project").option("--project <key>", "SonarQube project key (or set defaults.sonar.project in config)").option("--status <status>", "Filter: TO_REVIEW or REVIEWED").option("--resolution <list>", "Filter: FIXED,SAFE,ACKNOWLEDGED (comma-separated)").option("--branch <name>", "Branch name").option("--page <n>", "Page number (1-based)", "1").option("--page-size <n>", "Results per page", "100").option("--all", "Fetch all pages (consider --output-file for large results)").action(async (opts) => {
2214
2718
  const start = Date.now();
2215
2719
  try {
2216
- const { client, config } = getClient4(program2);
2720
+ const { client, config } = getClient5(program2);
2217
2721
  const projectKey = resolveProject(config, opts.project);
2218
2722
  const baseOpts = {
2219
2723
  projectKey,
@@ -2369,7 +2873,7 @@ var SdeClient = class {
2369
2873
  };
2370
2874
 
2371
2875
  // src/services/sde/commands.ts
2372
- function getClient5(program2) {
2876
+ function getClient6(program2) {
2373
2877
  const opts = program2.optsWithGlobals();
2374
2878
  const config = loadConfig({ configPath: opts.config });
2375
2879
  if (!config.sde.baseUrl) throw new PncliError("SDElements not configured. Run: pncli config init");
@@ -2401,7 +2905,7 @@ function registerSdeCommands(program2) {
2401
2905
  sde.command("server-info").description("Get server version and platform info (requires super-user)").action(async () => {
2402
2906
  const start = Date.now();
2403
2907
  try {
2404
- const { client } = getClient5(program2);
2908
+ const { client } = getClient6(program2);
2405
2909
  const data = await client.getServerInfo();
2406
2910
  success(data, "sde", "server-info", start);
2407
2911
  } catch (err) {
@@ -2411,7 +2915,7 @@ function registerSdeCommands(program2) {
2411
2915
  sde.command("whoami").description("Get current authenticated user").action(async () => {
2412
2916
  const start = Date.now();
2413
2917
  try {
2414
- const { client } = getClient5(program2);
2918
+ const { client } = getClient6(program2);
2415
2919
  const data = await client.getMe();
2416
2920
  success(data, "sde", "whoami", start);
2417
2921
  } catch (err) {
@@ -2421,7 +2925,7 @@ function registerSdeCommands(program2) {
2421
2925
  sde.command("users").description("List users").option("--email <email>", "Filter by email address").option("--first-name <name>", "Filter by first name").option("--last-name <name>", "Filter by last name").option("--active <bool>", "Filter by active status: true or false").option("--page <n>", "Page number (1-based)", "1").option("--page-size <n>", "Results per page", "100").option("--all", "Fetch all pages (consider --output-file for large results)").action(async (opts) => {
2422
2926
  const start = Date.now();
2423
2927
  try {
2424
- const { client } = getClient5(program2);
2928
+ const { client } = getClient6(program2);
2425
2929
  const baseOpts = {
2426
2930
  email: opts.email,
2427
2931
  firstName: opts.firstName,
@@ -2446,7 +2950,7 @@ function registerSdeCommands(program2) {
2446
2950
  sde.command("projects").description("List projects").option("--name <name>", "Filter by project name").option("--search <text>", "Text search on name and profile").option("--active <val>", "Filter by active status: true, false, or all").option("--ordering <field>", "Sort by: name, created, updated (prefix with - for descending)").option("--expand <fields>", "Expand nested fields (comma-separated): application,business_unit,creator").option("--include <fields>", "Include extra fields (comma-separated): task_counts,permissions").option("--page <n>", "Page number (1-based)", "1").option("--page-size <n>", "Results per page", "100").option("--all", "Fetch all pages (consider --output-file for large results)").action(async (opts) => {
2447
2951
  const start = Date.now();
2448
2952
  try {
2449
- const { client } = getClient5(program2);
2953
+ const { client } = getClient6(program2);
2450
2954
  const baseOpts = {
2451
2955
  name: opts.name,
2452
2956
  search: opts.search,
@@ -2473,7 +2977,7 @@ function registerSdeCommands(program2) {
2473
2977
  sde.command("project").description("Get a single project by ID").option("--id <id>", "Project ID (or set defaults.sde.project in config)").option("--expand <fields>", "Expand nested fields (comma-separated): application,business_unit,creator").option("--include <fields>", "Include extra fields (comma-separated): task_counts,permissions").action(async (opts) => {
2474
2978
  const start = Date.now();
2475
2979
  try {
2476
- const { client, config } = getClient5(program2);
2980
+ const { client, config } = getClient6(program2);
2477
2981
  const projectId = resolveProject2(config, opts.id);
2478
2982
  const data = await client.getProject(projectId, opts.expand, opts.include);
2479
2983
  success(data, "sde", "project", start);
@@ -2484,7 +2988,7 @@ function registerSdeCommands(program2) {
2484
2988
  sde.command("tasks").description("List countermeasures for a project").option("--project <id>", "Project ID (or set defaults.sde.project in config)").option("--phase <slug>", "Filter by phase slug (e.g. development, architecture-design)").option("--priority <n>", "Filter by priority (1-10)").option("--status <id>", "Filter by status ID (e.g. TS1, TS2)").option("--assigned-to <email>", "Filter by assignee email").option("--source <val>", "Filter by source: default, custom, manual, project").option("--verification <val>", "Filter by verification: pass, fail, partial, none").option("--tag <name>", "Filter by tag name").option("--accepted <bool>", "Filter by accepted status: true or false").option("--relevant <bool>", "Filter by relevant status: true or false").option("--expand <fields>", "Expand nested fields (comma-separated): status,phase,problem,text").option("--include <fields>", "Include extra fields (comma-separated): how_tos,last_note,references,regulation_sections").option("--page <n>", "Page number (1-based)", "1").option("--page-size <n>", "Results per page", "100").option("--all", "Fetch all pages (consider --output-file for large results)").action(async (opts) => {
2485
2989
  const start = Date.now();
2486
2990
  try {
2487
- const { client, config } = getClient5(program2);
2991
+ const { client, config } = getClient6(program2);
2488
2992
  const projectId = resolveProject2(config, opts.project);
2489
2993
  const baseOpts = {
2490
2994
  projectId,
@@ -2518,7 +3022,7 @@ function registerSdeCommands(program2) {
2518
3022
  sde.command("task").description("Get a single countermeasure").option("--project <id>", "Project ID (or set defaults.sde.project in config)").requiredOption("--task <id>", "Task ID (e.g. T21)").option("--expand <fields>", "Expand nested fields (comma-separated): status,phase,problem,text").option("--include <fields>", "Include extra fields (comma-separated): how_tos,last_note,references").action(async (opts) => {
2519
3023
  const start = Date.now();
2520
3024
  try {
2521
- const { client, config } = getClient5(program2);
3025
+ const { client, config } = getClient6(program2);
2522
3026
  const projectId = resolveProject2(config, opts.project);
2523
3027
  const data = await client.getTask(projectId, opts.task, opts.expand, opts.include);
2524
3028
  success(data, "sde", "task", start);
@@ -2529,7 +3033,7 @@ function registerSdeCommands(program2) {
2529
3033
  sde.command("threats").description("List threats for a project").option("--project <id>", "Project ID (or set defaults.sde.project in config)").option("--severity <n>", "Filter by severity (1-10)").option("--search <text>", "Full-text search on title and threat ID").option("--ordering <field>", "Sort by: threat__severity, threat_id, status (prefix - for descending)").option("--capec-id <id>", "Filter by CAPEC attack pattern ID").option("--component-id <id>", "Filter by component ID").option("--page <n>", "Page number (1-based)", "1").option("--page-size <n>", "Results per page", "100").option("--all", "Fetch all pages (consider --output-file for large results)").action(async (opts) => {
2530
3034
  const start = Date.now();
2531
3035
  try {
2532
- const { client, config } = getClient5(program2);
3036
+ const { client, config } = getClient6(program2);
2533
3037
  const projectId = resolveProject2(config, opts.project);
2534
3038
  const baseOpts = {
2535
3039
  projectId,
@@ -2557,7 +3061,7 @@ function registerSdeCommands(program2) {
2557
3061
  }
2558
3062
 
2559
3063
  // src/services/deps/commands.ts
2560
- import { Option } from "commander";
3064
+ import { Option as Option2 } from "commander";
2561
3065
 
2562
3066
  // src/services/deps/parsers/index.ts
2563
3067
  import fs from "fs";
@@ -4241,7 +4745,7 @@ async function runLicenseCheck(config, opts) {
4241
4745
  // src/services/deps/commands.ts
4242
4746
  function registerDepsCommands(program2) {
4243
4747
  const deps = program2.command("deps").description("Dependency scanning, CVE detection, and license auditing");
4244
- deps.command("frisk").description("Scan all dependencies for CVEs and output remediation paths (requires OSV.dev, Sonatype OSS Index, or Sonatype IQ Server)").option("--ecosystem <ecosystem>", "Filter to one ecosystem: npm, nuget, maven, all", "all").option("--direct-only", "Only scan direct dependencies (default: include transitive)", false).option("--include-dev", "Include dev/test dependencies", false).addOption(new Option("--source <source>", "Vulnerability source").choices(["osv", "sonatype", "sonatypeiq", "all"]).default("osv")).option("--application-id <id>", "Sonatype IQ Server application ID (required when --source sonatypeiq)").action(async (opts, cmd) => {
4748
+ deps.command("frisk").description("Scan all dependencies for CVEs and output remediation paths (requires OSV.dev, Sonatype OSS Index, or Sonatype IQ Server)").option("--ecosystem <ecosystem>", "Filter to one ecosystem: npm, nuget, maven, all", "all").option("--direct-only", "Only scan direct dependencies (default: include transitive)", false).option("--include-dev", "Include dev/test dependencies", false).addOption(new Option2("--source <source>", "Vulnerability source").choices(["osv", "sonatype", "sonatypeiq", "all"]).default("osv")).option("--application-id <id>", "Sonatype IQ Server application ID (required when --source sonatypeiq)").action(async (opts, cmd) => {
4245
4749
  const startTime = Date.now();
4246
4750
  try {
4247
4751
  const globalOpts = cmd.optsWithGlobals();
@@ -4600,6 +5104,16 @@ function registerConfigCommands(program2) {
4600
5104
  } else {
4601
5105
  results.bitbucket = { ok: null, message: "not configured" };
4602
5106
  }
5107
+ if (cfg.github.baseUrl) {
5108
+ try {
5109
+ await http.github("/user");
5110
+ results.github = { ok: true, message: "connected" };
5111
+ } catch (err) {
5112
+ results.github = { ok: false, message: err instanceof Error ? err.message : String(err) };
5113
+ }
5114
+ } else {
5115
+ results.github = { ok: null, message: "not configured" };
5116
+ }
4603
5117
  if (cfg.confluence.baseUrl) {
4604
5118
  try {
4605
5119
  await http.confluence("/rest/api/space", { params: { limit: 1 } });
@@ -4770,6 +5284,18 @@ function registerConfigCommands(program2) {
4770
5284
  results.bitbucket = categorize2(err);
4771
5285
  }
4772
5286
  }
5287
+ if (!cfg.github.token) {
5288
+ results.github = { status: "blank", message: "not configured" };
5289
+ } else if (!cfg.github.baseUrl) {
5290
+ results.github = { status: "error", message: "baseUrl not configured" };
5291
+ } else {
5292
+ try {
5293
+ await http.github("/user", { timeoutMs: 1e4 });
5294
+ results.github = { status: "valid", message: "ok" };
5295
+ } catch (err) {
5296
+ results.github = categorize2(err);
5297
+ }
5298
+ }
4773
5299
  if (!cfg.confluence.apiTokenExplicit && !cfg.jira.apiToken) {
4774
5300
  results.confluence = { status: "blank", message: "not configured" };
4775
5301
  } else if (!cfg.confluence.apiToken) {
@@ -4922,7 +5448,7 @@ function registerConfigCommands(program2) {
4922
5448
  if (statuses.includes("invalid")) process.exitCode = ExitCode.AUTH_ERROR;
4923
5449
  else if (statuses.includes("error")) process.exitCode = ExitCode.NETWORK_ERROR;
4924
5450
  if (cmdOpts.output === "table") {
4925
- const services = ["jira", "bitbucket", "confluence", "sonar", "sde", "ado", "jenkins", "udeploy", "artifactory", "checkmarx", "servicenow", "contrast", "sonatypeiq", "openshift"];
5451
+ const services = ["jira", "bitbucket", "github", "confluence", "sonar", "sde", "ado", "jenkins", "udeploy", "artifactory", "checkmarx", "servicenow", "contrast", "sonatypeiq", "openshift"];
4926
5452
  const labelWidth = 14;
4927
5453
  const statusWidth = 9;
4928
5454
  for (const svc of services) {
@@ -4936,7 +5462,7 @@ function registerConfigCommands(program2) {
4936
5462
  process.stdout.write("\n");
4937
5463
  } else {
4938
5464
  if (opts.pretty) {
4939
- const services = ["jira", "bitbucket", "confluence", "sonar", "sde", "ado", "jenkins", "udeploy", "artifactory", "checkmarx", "servicenow", "contrast", "sonatypeiq", "openshift"];
5465
+ const services = ["jira", "bitbucket", "github", "confluence", "sonar", "sde", "ado", "jenkins", "udeploy", "artifactory", "checkmarx", "servicenow", "contrast", "sonatypeiq", "openshift"];
4940
5466
  const labelWidth = 14;
4941
5467
  const statusWidth = 9;
4942
5468
  for (const svc of services) {
@@ -4988,6 +5514,40 @@ async function initGlobalConfig(start) {
4988
5514
  const bitbucketPat = await password({
4989
5515
  message: "Bitbucket personal access token:"
4990
5516
  });
5517
+ process.stderr.write("\n\u2500\u2500 GitHub \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
5518
+ const useGitHub = await confirm({
5519
+ message: "Configure GitHub for PR operations?",
5520
+ default: false
5521
+ });
5522
+ let githubBaseUrl = "";
5523
+ let githubToken = "";
5524
+ if (useGitHub) {
5525
+ githubBaseUrl = await input({
5526
+ message: "GitHub API base URL\n GitHub.com: api.github.com\n GitHub Enterprise: <host>/api/v3\n URL: ",
5527
+ default: ""
5528
+ });
5529
+ githubToken = await password({
5530
+ message: "GitHub personal access token (classic or fine-grained):"
5531
+ });
5532
+ if (githubBaseUrl && githubToken) {
5533
+ process.stderr.write("\n Verifying connection...\n");
5534
+ try {
5535
+ const tempConfig = {
5536
+ ...loadConfig(),
5537
+ github: {
5538
+ baseUrl: normalizeBaseUrl(githubBaseUrl),
5539
+ token: githubToken
5540
+ }
5541
+ };
5542
+ const tempHttp = createHttpClient(tempConfig);
5543
+ await tempHttp.github("/user");
5544
+ process.stderr.write(" Connected.\n");
5545
+ } catch (err) {
5546
+ warn(`Could not connect to GitHub: ${err instanceof Error ? err.message : String(err)}`);
5547
+ warn("Config will be saved anyway. Check your URL and token and re-run pncli config init or pncli config test.");
5548
+ }
5549
+ }
5550
+ }
4991
5551
  process.stderr.write("\n\u2500\u2500 Confluence \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
4992
5552
  const confluenceBaseUrl = await input({
4993
5553
  message: "Confluence base URL (e.g. confluence.your-company.com):",
@@ -5495,6 +6055,12 @@ async function initGlobalConfig(start) {
5495
6055
  baseUrl: normalizeBaseUrl(bitbucketBaseUrl) || void 0,
5496
6056
  pat: bitbucketPat || void 0
5497
6057
  },
6058
+ ...useGitHub && githubBaseUrl ? {
6059
+ github: {
6060
+ baseUrl: normalizeBaseUrl(githubBaseUrl),
6061
+ token: githubToken || void 0
6062
+ }
6063
+ } : {},
5498
6064
  ...confluenceBaseUrl || confluenceApiToken ? {
5499
6065
  confluence: {
5500
6066
  baseUrl: normalizeBaseUrl(confluenceBaseUrl) || void 0,
@@ -6554,11 +7120,11 @@ function registerAdoRepoCommands(ado) {
6554
7120
  const start = Date.now();
6555
7121
  try {
6556
7122
  const globalOpts = ado.optsWithGlobals();
6557
- const { loadConfig: lc } = await import("./config-TL5X47RY.js");
7123
+ const { loadConfig: lc } = await import("./config-3QD5Y2O5.js");
6558
7124
  const config = lc({ configPath: globalOpts.config });
6559
7125
  const { collection, project, repo: repo2, gitClient } = getAdoContext(ado, true);
6560
7126
  const { AdoCoreClient: AdoCoreClient2 } = await import("./core-SWYVXOQ4.js");
6561
- const { createHttpClient: createHttpClient2 } = await import("./http-UN6N4WIB.js");
7127
+ const { createHttpClient: createHttpClient2 } = await import("./http-5WRBWEM6.js");
6562
7128
  const http = createHttpClient2(config, Boolean(globalOpts.dryRun));
6563
7129
  const coreClient = new AdoCoreClient2(http);
6564
7130
  const me = await coreClient.getConnectionData(collection);
@@ -6816,7 +7382,7 @@ var JenkinsClient = class {
6816
7382
  };
6817
7383
 
6818
7384
  // src/services/jenkins/commands.ts
6819
- function getClient6(program2) {
7385
+ function getClient7(program2) {
6820
7386
  const opts = program2.optsWithGlobals();
6821
7387
  const config = loadConfig({ configPath: opts.config });
6822
7388
  if (!config.jenkins.baseUrl) throw new PncliError("Jenkins not configured. Run: pncli config init");
@@ -6850,7 +7416,7 @@ function registerJenkinsCommands(program2) {
6850
7416
  pipeline.command("list").description("List all jobs on the Jenkins instance (or within a folder)").option("--folder <name>", "Folder name to enumerate (supports nested folders: parentFolder/childFolder)").action(async (opts) => {
6851
7417
  const start = Date.now();
6852
7418
  try {
6853
- const client = getClient6(program2);
7419
+ const client = getClient7(program2);
6854
7420
  const data = await client.listJobs(opts.folder);
6855
7421
  success(data, "jenkins", "pipeline-list", start);
6856
7422
  } catch (err) {
@@ -6860,7 +7426,7 @@ function registerJenkinsCommands(program2) {
6860
7426
  pipeline.command("get").description("Get details for a specific job").requiredOption("--name <job>", "Job name (use folder/job for folder-scoped jobs)").action(async (opts) => {
6861
7427
  const start = Date.now();
6862
7428
  try {
6863
- const client = getClient6(program2);
7429
+ const client = getClient7(program2);
6864
7430
  const data = await client.getJob(opts.name);
6865
7431
  success(data, "jenkins", "pipeline-get", start);
6866
7432
  } catch (err) {
@@ -6873,7 +7439,7 @@ function registerJenkinsCommands(program2) {
6873
7439
  }, []).option("--wait", "Wait for the build to complete before returning").option("--timeout <s>", "Max wait time in seconds per phase (queue-wait and build-wait each get this budget; default 600)", "600").option("--poll <s>", "Poll interval in seconds (default 10)", "10").action(async (opts) => {
6874
7440
  const start = Date.now();
6875
7441
  try {
6876
- const client = getClient6(program2);
7442
+ const client = getClient7(program2);
6877
7443
  for (const p of opts.parameter) {
6878
7444
  if (!p.includes("=")) throw new PncliError(`Invalid --parameter "${p}". Expected format: key=value`, 1);
6879
7445
  }
@@ -6906,7 +7472,7 @@ function registerJenkinsCommands(program2) {
6906
7472
  pipeline.command("list-runs").description("List recent builds for a job").requiredOption("--name <job>", "Job name (use folder/job for folder-scoped jobs)").option("--top <n>", "Maximum number of builds to return (default 25)", "25").action(async (opts) => {
6907
7473
  const start = Date.now();
6908
7474
  try {
6909
- const client = getClient6(program2);
7475
+ const client = getClient7(program2);
6910
7476
  const top = parseInt(opts.top, 10);
6911
7477
  if (isNaN(top) || top <= 0) throw new PncliError(`Invalid --top "${opts.top}". Expected a positive integer.`, 1);
6912
7478
  const data = await client.listBuilds(opts.name, top);
@@ -6918,7 +7484,7 @@ function registerJenkinsCommands(program2) {
6918
7484
  pipeline.command("get-run").description("Get details for a specific build").requiredOption("--name <job>", "Job name (use folder/job for folder-scoped jobs)").requiredOption("--number <n>", "Build number").action(async (opts) => {
6919
7485
  const start = Date.now();
6920
7486
  try {
6921
- const client = getClient6(program2);
7487
+ const client = getClient7(program2);
6922
7488
  const buildNumber = parseInt(opts.number, 10);
6923
7489
  if (isNaN(buildNumber)) throw new PncliError(`Invalid --number "${opts.number}". Expected an integer.`, 1);
6924
7490
  const data = await client.getBuild(opts.name, buildNumber);
@@ -6930,7 +7496,7 @@ function registerJenkinsCommands(program2) {
6930
7496
  pipeline.command("logs").description("Fetch console log for a build (consider --output-file for large logs)").requiredOption("--name <job>", "Job name (use folder/job for folder-scoped jobs)").requiredOption("--number <n>", "Build number").action(async (opts) => {
6931
7497
  const start = Date.now();
6932
7498
  try {
6933
- const client = getClient6(program2);
7499
+ const client = getClient7(program2);
6934
7500
  const buildNumber = parseInt(opts.number, 10);
6935
7501
  if (isNaN(buildNumber)) throw new PncliError(`Invalid --number "${opts.number}". Expected an integer.`, 1);
6936
7502
  const text = await client.getConsoleLog(opts.name, buildNumber);
@@ -7022,7 +7588,7 @@ var ArtifactoryClient = class {
7022
7588
  };
7023
7589
 
7024
7590
  // src/services/artifactory/commands.ts
7025
- function getClient7(program2) {
7591
+ function getClient8(program2) {
7026
7592
  const opts = program2.optsWithGlobals();
7027
7593
  const config = loadConfig({ configPath: opts.config });
7028
7594
  if (!config.artifactory.baseUrl) throw new PncliError("Artifactory not configured. Run: pncli config init");
@@ -7035,7 +7601,7 @@ function registerArtifactoryCommands(program2) {
7035
7601
  art.command("ping").description("Check Artifactory connectivity and authentication").action(async () => {
7036
7602
  const start = Date.now();
7037
7603
  try {
7038
- const client = getClient7(program2);
7604
+ const client = getClient8(program2);
7039
7605
  const data = await client.ping();
7040
7606
  success(data, "artifactory", "ping", start);
7041
7607
  } catch (err) {
@@ -7045,7 +7611,7 @@ function registerArtifactoryCommands(program2) {
7045
7611
  art.command("repos").description("List repositories").option("--type <type>", "Filter by type: local, virtual, remote, federated").option("--package-type <type>", "Filter by package type: npm, maven, nuget, docker, pypi, etc.").action(async (opts) => {
7046
7612
  const start = Date.now();
7047
7613
  try {
7048
- const client = getClient7(program2);
7614
+ const client = getClient8(program2);
7049
7615
  const data = await client.listRepos({ type: opts.type, packageType: opts.packageType });
7050
7616
  success(data, "artifactory", "repos", start);
7051
7617
  } catch (err) {
@@ -7055,7 +7621,7 @@ function registerArtifactoryCommands(program2) {
7055
7621
  art.command("artifact-info <repo> <path>").description("Get metadata for an artifact or folder (checksums, size, timestamps)").action(async (repo, path8) => {
7056
7622
  const start = Date.now();
7057
7623
  try {
7058
- const client = getClient7(program2);
7624
+ const client = getClient8(program2);
7059
7625
  const data = await client.getStorageInfo(repo, path8);
7060
7626
  success(data, "artifactory", "artifact-info", start);
7061
7627
  } catch (err) {
@@ -7065,7 +7631,7 @@ function registerArtifactoryCommands(program2) {
7065
7631
  art.command("properties-get <repo> <path>").description("Read custom properties on an artifact").action(async (repo, path8) => {
7066
7632
  const start = Date.now();
7067
7633
  try {
7068
- const client = getClient7(program2);
7634
+ const client = getClient8(program2);
7069
7635
  const data = await client.getProperties(repo, path8);
7070
7636
  success(data, "artifactory", "properties-get", start);
7071
7637
  } catch (err) {
@@ -7082,7 +7648,7 @@ function registerArtifactoryCommands(program2) {
7082
7648
  if (eq < 1) throw new PncliError(`Invalid property format: "${kv}" \u2014 expected key=value`);
7083
7649
  properties[kv.slice(0, eq)] = kv.slice(eq + 1);
7084
7650
  }
7085
- const client = getClient7(program2);
7651
+ const client = getClient8(program2);
7086
7652
  await client.setProperties(repo, path8, properties, { recursive: opts.recursive });
7087
7653
  success({ repo, path: path8, properties }, "artifactory", "properties-set", start);
7088
7654
  } catch (err) {
@@ -7095,7 +7661,7 @@ function registerArtifactoryCommands(program2) {
7095
7661
  if (!opts.repo && !opts.name && !opts.path) {
7096
7662
  throw new PncliError("Provide at least one filter: --repo, --name, or --path");
7097
7663
  }
7098
- const client = getClient7(program2);
7664
+ const client = getClient8(program2);
7099
7665
  const data = await client.searchAql({
7100
7666
  repo: opts.repo,
7101
7667
  name: opts.name,
@@ -7113,7 +7679,7 @@ function registerArtifactoryCommands(program2) {
7113
7679
  art.command("builds").description('List builds registered in Artifactory Build Integration (names may differ from ADO pipeline or uDeploy component names; use "search --repo" when empty)').option("--timeout <s>", "Request timeout in seconds (default 60; increase for large Artifactory instances)", "60").action(async (opts) => {
7114
7680
  const start = Date.now();
7115
7681
  try {
7116
- const client = getClient7(program2);
7682
+ const client = getClient8(program2);
7117
7683
  const timeoutSecs = parseInt(opts.timeout, 10);
7118
7684
  if (isNaN(timeoutSecs) || timeoutSecs <= 0) throw new PncliError("--timeout must be a positive integer (seconds)", 1);
7119
7685
  const timeoutMs = timeoutSecs * 1e3;
@@ -7126,7 +7692,7 @@ function registerArtifactoryCommands(program2) {
7126
7692
  art.command("build-runs <name>").description("List all run numbers for a build").action(async (name) => {
7127
7693
  const start = Date.now();
7128
7694
  try {
7129
- const client = getClient7(program2);
7695
+ const client = getClient8(program2);
7130
7696
  const data = await client.listBuildRuns(name);
7131
7697
  success(data, "artifactory", "build-runs", start);
7132
7698
  } catch (err) {
@@ -7136,7 +7702,7 @@ function registerArtifactoryCommands(program2) {
7136
7702
  art.command("build-info <name> <number>").description("Get build info (artifacts, dependencies, VCS revision) for a specific run").action(async (name, number) => {
7137
7703
  const start = Date.now();
7138
7704
  try {
7139
- const client = getClient7(program2);
7705
+ const client = getClient8(program2);
7140
7706
  const data = await client.getBuildInfo(name, number);
7141
7707
  success(data, "artifactory", "build-info", start);
7142
7708
  } catch (err) {
@@ -7208,7 +7774,7 @@ var UdeployClient = class {
7208
7774
  // src/services/udeploy/commands.ts
7209
7775
  var POLL_INTERVAL_MS2 = 5e3;
7210
7776
  var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
7211
- function getClient8(program2) {
7777
+ function getClient9(program2) {
7212
7778
  const opts = program2.optsWithGlobals();
7213
7779
  const config = loadConfig({ configPath: opts.config });
7214
7780
  if (!config.udeploy.baseUrl) {
@@ -7232,7 +7798,7 @@ function registerUdeployCommands(program2) {
7232
7798
  udeploy.command("apps").description("List all applications").action(async () => {
7233
7799
  const start = Date.now();
7234
7800
  try {
7235
- const { client } = getClient8(program2);
7801
+ const { client } = getClient9(program2);
7236
7802
  const apps = await client.listApplications();
7237
7803
  success(apps, "udeploy", "apps", start);
7238
7804
  } catch (err) {
@@ -7242,7 +7808,7 @@ function registerUdeployCommands(program2) {
7242
7808
  udeploy.command("environments").description("List environments for an application").action(async () => {
7243
7809
  const start = Date.now();
7244
7810
  try {
7245
- const { client, config } = getClient8(program2);
7811
+ const { client, config } = getClient9(program2);
7246
7812
  const parentOpts = udeploy.opts();
7247
7813
  const application = resolveApplication(config, parentOpts.application);
7248
7814
  const envs = await client.listEnvironments(application);
@@ -7254,7 +7820,7 @@ function registerUdeployCommands(program2) {
7254
7820
  udeploy.command("components").description("List all components").action(async () => {
7255
7821
  const start = Date.now();
7256
7822
  try {
7257
- const { client } = getClient8(program2);
7823
+ const { client } = getClient9(program2);
7258
7824
  const components = await client.listComponents();
7259
7825
  success(components, "udeploy", "components", start);
7260
7826
  } catch (err) {
@@ -7264,7 +7830,7 @@ function registerUdeployCommands(program2) {
7264
7830
  udeploy.command("versions").description("List versions for a component").requiredOption("--component <name>", "Component name or ID").action(async (cmdOpts) => {
7265
7831
  const start = Date.now();
7266
7832
  try {
7267
- const { client } = getClient8(program2);
7833
+ const { client } = getClient9(program2);
7268
7834
  const versions = await client.listVersions(cmdOpts.component);
7269
7835
  success(versions, "udeploy", "versions", start);
7270
7836
  } catch (err) {
@@ -7274,7 +7840,7 @@ function registerUdeployCommands(program2) {
7274
7840
  udeploy.command("import-version").description("Create a component version and mark it ready for deployment").requiredOption("--component <name>", "Component name or ID").requiredOption("--name <name>", "Version name").option("--no-finish", "Skip marking the version as finished importing").action(async (cmdOpts) => {
7275
7841
  const start = Date.now();
7276
7842
  try {
7277
- const { client } = getClient8(program2);
7843
+ const { client } = getClient9(program2);
7278
7844
  const created = await client.createVersion(cmdOpts.component, cmdOpts.name);
7279
7845
  if (cmdOpts.finish) {
7280
7846
  await client.finishImporting(cmdOpts.component, cmdOpts.name);
@@ -7287,7 +7853,7 @@ function registerUdeployCommands(program2) {
7287
7853
  udeploy.command("run").description("Run an application deployment process").requiredOption("--process <name>", "Application process name or ID").option("--component <name>", "Component name (repeatable)", collect, []).option("--component-version <name>", "Component version (repeatable; positionally paired with --component)", collect, []).option("--snapshot <name>", "Snapshot name or ID (alternative to specifying versions)").option("--only-changed", "Deploy only changed components", false).option("--wait", "Poll until the process completes", false).option("--timeout <ms>", "Max wait time in milliseconds", String(DEFAULT_TIMEOUT_MS)).action(async (cmdOpts) => {
7288
7854
  const start = Date.now();
7289
7855
  try {
7290
- const { client, config } = getClient8(program2);
7856
+ const { client, config } = getClient9(program2);
7291
7857
  const parentOpts = udeploy.opts();
7292
7858
  const application = resolveApplication(config, parentOpts.application);
7293
7859
  const environment = resolveEnvironment(config, parentOpts.environment);
@@ -7340,7 +7906,7 @@ function registerUdeployCommands(program2) {
7340
7906
  udeploy.command("request-status").description("Get the current status of a deployment process request").requiredOption("--request-id <id>", "Request ID returned by udeploy run").action(async (cmdOpts) => {
7341
7907
  const start = Date.now();
7342
7908
  try {
7343
- const { client } = getClient8(program2);
7909
+ const { client } = getClient9(program2);
7344
7910
  const status = await client.getRequestStatus(cmdOpts.requestId);
7345
7911
  success(status, "udeploy", "request-status", start);
7346
7912
  } catch (err) {
@@ -7350,7 +7916,7 @@ function registerUdeployCommands(program2) {
7350
7916
  udeploy.command("request-info").description("Get full details of a deployment process request").requiredOption("--request-id <id>", "Request ID returned by udeploy run").action(async (cmdOpts) => {
7351
7917
  const start = Date.now();
7352
7918
  try {
7353
- const { client } = getClient8(program2);
7919
+ const { client } = getClient9(program2);
7354
7920
  const info = await client.getRequestInfo(cmdOpts.requestId);
7355
7921
  success(info, "udeploy", "request-info", start);
7356
7922
  } catch (err) {
@@ -7394,7 +7960,7 @@ var CheckmarxClient = class {
7394
7960
  };
7395
7961
 
7396
7962
  // src/services/checkmarx/commands.ts
7397
- function getClient9(program2) {
7963
+ function getClient10(program2) {
7398
7964
  const opts = program2.optsWithGlobals();
7399
7965
  const config = loadConfig({ configPath: opts.config });
7400
7966
  if (!config.checkmarx.baseUrl) throw new PncliError("Checkmarx not configured. Run: pncli config init");
@@ -7408,7 +7974,7 @@ function registerCheckmarxCommands(program2) {
7408
7974
  project.command("list").description("List all projects").action(async () => {
7409
7975
  const start = Date.now();
7410
7976
  try {
7411
- const data = await getClient9(program2).listProjects();
7977
+ const data = await getClient10(program2).listProjects();
7412
7978
  success(data, "checkmarx", "project-list", start);
7413
7979
  } catch (err) {
7414
7980
  fail(err, "checkmarx", "project-list", start);
@@ -7419,7 +7985,7 @@ function registerCheckmarxCommands(program2) {
7419
7985
  try {
7420
7986
  const id = parseInt(opts.id, 10);
7421
7987
  if (isNaN(id)) throw new PncliError(`Invalid --id "${opts.id}". Expected an integer.`, 1);
7422
- const data = await getClient9(program2).getProject(id);
7988
+ const data = await getClient10(program2).getProject(id);
7423
7989
  success(data, "checkmarx", "project-get", start);
7424
7990
  } catch (err) {
7425
7991
  fail(err, "checkmarx", "project-get", start);
@@ -7432,7 +7998,7 @@ function registerCheckmarxCommands(program2) {
7432
7998
  const last = opts.last ? parseInt(opts.last, 10) : void 0;
7433
7999
  if (opts.project && isNaN(projectId)) throw new PncliError(`Invalid --project "${opts.project}". Expected an integer.`, 1);
7434
8000
  if (opts.last && isNaN(last)) throw new PncliError(`Invalid --last "${opts.last}". Expected an integer.`, 1);
7435
- const data = await getClient9(program2).listScans({ projectId, last });
8001
+ const data = await getClient10(program2).listScans({ projectId, last });
7436
8002
  success(data, "checkmarx", "scan-list", start);
7437
8003
  } catch (err) {
7438
8004
  fail(err, "checkmarx", "scan-list", start);
@@ -7443,7 +8009,7 @@ function registerCheckmarxCommands(program2) {
7443
8009
  try {
7444
8010
  const id = parseInt(opts.id, 10);
7445
8011
  if (isNaN(id)) throw new PncliError(`Invalid --id "${opts.id}". Expected an integer.`, 1);
7446
- const data = await getClient9(program2).getScan(id);
8012
+ const data = await getClient10(program2).getScan(id);
7447
8013
  success(data, "checkmarx", "scan-get", start);
7448
8014
  } catch (err) {
7449
8015
  fail(err, "checkmarx", "scan-get", start);
@@ -7454,7 +8020,7 @@ function registerCheckmarxCommands(program2) {
7454
8020
  try {
7455
8021
  const id = parseInt(opts.id, 10);
7456
8022
  if (isNaN(id)) throw new PncliError(`Invalid --id "${opts.id}". Expected an integer.`, 1);
7457
- const data = await getClient9(program2).getScanResultsStatistics(id);
8023
+ const data = await getClient10(program2).getScanResultsStatistics(id);
7458
8024
  success(data, "checkmarx", "scan-stats", start);
7459
8025
  } catch (err) {
7460
8026
  fail(err, "checkmarx", "scan-stats", start);
@@ -7876,7 +8442,7 @@ function registerSkillsCommands(program2) {
7876
8442
  }
7877
8443
  });
7878
8444
  const marketplace = skills.command("marketplace").description("Manage a git-hosted skills marketplace");
7879
- marketplace.command("setup").description("Clone a skills marketplace repo and register it in global config").argument("<url>", "Git clone URL of the marketplace repository").argument("[localPath]", "Local directory to clone into (default: ~/.agents/marketplaces/<repo-name>)").option("--branch <branch>", "Branch to clone (default: remote HEAD)").option("--token <token>", "HTTP access token for authenticated clone and pull (GitHub PAT or Bitbucket token)").action(async (url, localPath, opts) => {
8445
+ marketplace.command("setup").description("Clone a skills marketplace repo, register it in global config, and install all plugins").argument("<url>", "Git clone URL of the marketplace repository").argument("[localPath]", "Local directory to clone into (default: ~/.agents/marketplaces/<repo-name>)").option("--branch <branch>", "Branch to clone (default: remote HEAD)").option("--token <token>", "HTTP access token for authenticated clone and pull (GitHub PAT or Bitbucket token)").option("--agent <agent>", "Target agent host for plugin install: github-copilot | claude-code (default: github-copilot)").option("--claude", "Shorthand for --agent claude-code").action(async (url, localPath, opts) => {
7880
8446
  const start = Date.now();
7881
8447
  try {
7882
8448
  const resolvedPath = path7.resolve(localPath ?? defaultMarketplacePath(url));
@@ -7885,7 +8451,7 @@ function registerSkillsCommands(program2) {
7885
8451
  throw new Error(`Directory already exists and is not a git repo: ${resolvedPath}`);
7886
8452
  }
7887
8453
  if (hasGit) {
7888
- warn(`Directory already contains a git repo at ${resolvedPath} \u2014 skipping clone, updating config only.`);
8454
+ warn(`Directory already contains a git repo at ${resolvedPath} \u2014 skipping clone, updating config and re-installing plugins.`);
7889
8455
  } else {
7890
8456
  const branchLabel = opts.branch ?? "remote default";
7891
8457
  warn(`Cloning ${url} (branch: ${branchLabel}) \u2192 ${resolvedPath}...`);
@@ -7904,12 +8470,51 @@ function registerSkillsCommands(program2) {
7904
8470
  const existing = loadJsonFile(configPath) ?? {};
7905
8471
  existing.marketplace = { repoUrl: url, localPath: resolvedPath, ...opts.token ? { token: opts.token } : {} };
7906
8472
  writeGlobalConfig(existing, configPath);
7907
- success({ repoUrl: url, localPath: resolvedPath, branch: opts.branch ?? null, tokenConfigured: !!opts.token }, "skills", "marketplace-setup", start);
8473
+ const agentName = opts.claude ? "claude-code" : opts.agent ?? "github-copilot";
8474
+ const agentConfig = AGENT_PATHS[agentName];
8475
+ if (!agentConfig) {
8476
+ throw new Error(`Unknown agent: "${agentName}". Use: ${Object.keys(AGENT_PATHS).join(" | ")}`);
8477
+ }
8478
+ const targetDir = agentConfig.user;
8479
+ const pluginChoices = resolvePluginChoices(resolvedPath);
8480
+ const pluginResults = {};
8481
+ let totalInstalled = 0;
8482
+ if (pluginChoices.length === 0) {
8483
+ warn("No plugins found in marketplace. Check the marketplace repository structure.");
8484
+ } else {
8485
+ warn(`Installing ${pluginChoices.length} plugin(s) to ${targetDir}...`);
8486
+ for (const pluginChoice of pluginChoices) {
8487
+ const skillsSrc = resolveSkillsSrc(resolvedPath, pluginChoice.name);
8488
+ if (!fs4.existsSync(skillsSrc)) {
8489
+ pluginResults[pluginChoice.name] = { installed: [], failed: [] };
8490
+ warn(`No skills directory found for plugin "${pluginChoice.name}" \u2014 skipping.`);
8491
+ continue;
8492
+ }
8493
+ const { installed, failed } = copyPluginSkills(skillsSrc, targetDir);
8494
+ pluginResults[pluginChoice.name] = { installed, failed };
8495
+ totalInstalled += installed.length;
8496
+ for (const skill of installed) {
8497
+ warn(` ${skill}: ${path7.join(skillsSrc, skill)} \u2192 ${path7.join(targetDir, skill)}`);
8498
+ }
8499
+ if (failed.length > 0) {
8500
+ warn(`Skipped ${failed.length} skill(s) with invalid names in "${pluginChoice.name}": ${failed.join(", ")}`);
8501
+ }
8502
+ }
8503
+ }
8504
+ success({
8505
+ repoUrl: url,
8506
+ localPath: resolvedPath,
8507
+ branch: opts.branch ?? null,
8508
+ tokenConfigured: !!opts.token,
8509
+ plugins: pluginResults,
8510
+ total: totalInstalled,
8511
+ target: targetDir
8512
+ }, "skills", "marketplace-setup", start);
7908
8513
  } catch (err) {
7909
8514
  fail(err, "skills", "marketplace-setup", start);
7910
8515
  }
7911
8516
  });
7912
- marketplace.command("sync").description("Pull latest marketplace content and install a plugin's skills").argument("[plugin]", 'Plugin name to install, or "all" to install every plugin (skips interactive selection)').option("--claude", "Install to ~/.claude/skills instead of ~/.agents/skills").option("--force", "Force reinstall even if the marketplace repo has no new changes").action(async (plugin, opts) => {
8517
+ marketplace.command("sync").description("Pull latest marketplace content and install a plugin's skills").argument("[plugin]", 'Plugin name to install, or "all" to install every plugin (skips interactive selection)').option("--agent <agent>", "Target agent host: github-copilot | claude-code (default: github-copilot)").option("--claude", "Shorthand for --agent claude-code").option("--force", "Force reinstall even if the marketplace repo has no new changes").action(async (plugin, opts) => {
7913
8518
  const start = Date.now();
7914
8519
  try {
7915
8520
  const configPath = getGlobalConfigPath();
@@ -7938,22 +8543,45 @@ function registerSkillsCommands(program2) {
7938
8543
  warn(pullOutput.trim());
7939
8544
  }
7940
8545
  warn(marketplaceUpdated ? "Marketplace updated." : "Marketplace already up to date \u2014 no changes to sync.");
7941
- if (!marketplaceUpdated && !plugin && !opts.force) {
7942
- success({
7943
- marketplace: marketplacePath,
7944
- marketplaceUpdated: false,
7945
- updated: false,
7946
- skipped: true,
7947
- message: "No marketplace changes detected \u2014 skipping plugin selection and install."
7948
- }, "skills", "marketplace-sync", start);
7949
- return;
7950
- }
7951
8546
  const pluginChoices = resolvePluginChoices(marketplacePath);
7952
8547
  if (pluginChoices.length === 0) {
7953
8548
  throw new Error("No plugins found in marketplace. Check the marketplace repository structure.");
7954
8549
  }
7955
- const targetDir = opts.claude ? path7.join(os.homedir(), ".claude", "skills") : path7.join(os.homedir(), ".agents", "skills");
7956
- if (plugin === "all") {
8550
+ const agentName = opts.claude ? "claude-code" : opts.agent ?? "github-copilot";
8551
+ const agentConfig = AGENT_PATHS[agentName];
8552
+ if (!agentConfig) {
8553
+ throw new Error(`Unknown agent: "${agentName}". Use: ${Object.keys(AGENT_PATHS).join(" | ")}`);
8554
+ }
8555
+ const targetDir = agentConfig.user;
8556
+ let selectedPlugin;
8557
+ if (plugin) {
8558
+ if (plugin !== "all" && !pluginChoices.some((p) => p.name === plugin)) {
8559
+ throw new Error(`Plugin "${plugin}" not found. Available: ${pluginChoices.map((p) => p.name).join(", ")}`);
8560
+ }
8561
+ selectedPlugin = plugin;
8562
+ } else {
8563
+ selectedPlugin = await select({
8564
+ message: "Select a plugin to install:",
8565
+ choices: [
8566
+ { value: "all", name: "All \u2014 install every plugin" },
8567
+ ...pluginChoices.map((p) => ({
8568
+ value: p.name,
8569
+ name: p.description ? `${p.name} \u2014 ${p.description}` : p.name
8570
+ }))
8571
+ ]
8572
+ });
8573
+ }
8574
+ if (selectedPlugin === "all") {
8575
+ if (!marketplaceUpdated && !opts.force) {
8576
+ success({
8577
+ marketplace: marketplacePath,
8578
+ marketplaceUpdated: false,
8579
+ updated: false,
8580
+ skipped: true,
8581
+ message: "No marketplace changes detected \u2014 skipping install."
8582
+ }, "skills", "marketplace-sync", start);
8583
+ return;
8584
+ }
7957
8585
  const results = {};
7958
8586
  let totalInstalled = 0;
7959
8587
  for (const pluginChoice of pluginChoices) {
@@ -7981,21 +8609,6 @@ function registerSkillsCommands(program2) {
7981
8609
  }, "skills", "marketplace-sync", start);
7982
8610
  return;
7983
8611
  }
7984
- let selectedPlugin;
7985
- if (plugin) {
7986
- if (!pluginChoices.some((p) => p.name === plugin)) {
7987
- throw new Error(`Plugin "${plugin}" not found. Available: ${pluginChoices.map((p) => p.name).join(", ")}`);
7988
- }
7989
- selectedPlugin = plugin;
7990
- } else {
7991
- selectedPlugin = await select({
7992
- message: "Select a plugin to install:",
7993
- choices: pluginChoices.map((p) => ({
7994
- value: p.name,
7995
- name: p.description ? `${p.name} \u2014 ${p.description}` : p.name
7996
- }))
7997
- });
7998
- }
7999
8612
  const skillsSrc = resolveSkillsSrc(marketplacePath, selectedPlugin);
8000
8613
  if (!fs4.existsSync(skillsSrc)) {
8001
8614
  throw new Error(`No skills directory found at ${skillsSrc}`);
@@ -8329,13 +8942,14 @@ if (!process.env.PNCLI_VERIFY_TLS) {
8329
8942
  var require2 = createRequire(import.meta.url);
8330
8943
  var pkg = require2("../package.json");
8331
8944
  var TAGLINE = "One command does what three meetings couldn't.";
8332
- var program = new Command2();
8333
- program.name("pncli").description(`The Paperwork Nightmare CLI \u2014 ${TAGLINE}`).version(`${pkg.version} \u2014 ${TAGLINE}`, "-v, --version").option("--pretty", "Human-readable formatted output", false).option("--verbose", "Include full response metadata", false).option("--dry-run", "Print API requests without executing", false).option("--config <path>", "Override global config file location").option("--output-file <path>", "Write JSON output to file instead of stdout");
8945
+ var program = new Command3();
8946
+ program.name("pncli").description(`The Paperwork Nightmare CLI \u2014 ${TAGLINE}`).version(`${pkg.version} \u2014 ${TAGLINE}`, "-v, --version").option("--pretty", "Human-readable formatted output", false).option("--verbose", "Include full response metadata", false).option("--debug", "Print diagnostic traces (method, URL, status) for all API calls \u2014 does not log credentials", false).option("--dry-run", "Print API requests without executing", false).option("--config <path>", "Override global config file location").option("--output-file <path>", "Write JSON output to file instead of stdout");
8334
8947
  program.hook("preAction", (thisCommand) => {
8335
8948
  const opts = thisCommand.optsWithGlobals();
8336
8949
  setGlobalOptions({
8337
8950
  pretty: Boolean(opts.pretty),
8338
8951
  verbose: Boolean(opts.verbose),
8952
+ debug: Boolean(opts.debug),
8339
8953
  outputFile: opts.outputFile
8340
8954
  });
8341
8955
  try {
@@ -8347,6 +8961,7 @@ program.hook("preAction", (thisCommand) => {
8347
8961
  registerGitCommands(program);
8348
8962
  registerJiraCommands(program);
8349
8963
  registerBitbucketCommands(program);
8964
+ registerGitHubCommands(program);
8350
8965
  registerConfluenceCommands(program);
8351
8966
  registerSonarCommands(program);
8352
8967
  registerSdeCommands(program);
@@ -8369,6 +8984,7 @@ Services:
8369
8984
  deps Dependency scanning, CVE detection, license auditing
8370
8985
  jira Jira Data Cloud
8371
8986
  bitbucket Bitbucket Server
8987
+ github GitHub (PRs, reviews, comments, checks)
8372
8988
  confluence Confluence
8373
8989
  sonar SonarQube Server (quality gates, issues, metrics, hotspots)
8374
8990
  sde SDElements (threat modeling, countermeasures, compliance)