@kolatts/pncli 1.15.0 → 1.17.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/copilot-instructions.md +252 -19
- package/dist/{chunk-CFUUOW7B.js → chunk-FFVQ4PA7.js} +15 -3
- package/dist/chunk-FFVQ4PA7.js.map +1 -0
- package/dist/{chunk-YHRFL7VX.js → chunk-XPPEXJBP.js} +77 -1
- package/dist/chunk-XPPEXJBP.js.map +1 -0
- package/dist/cli.js +1196 -211
- package/dist/cli.js.map +1 -1
- package/dist/{config-TL5X47RY.js → config-S56LADVF.js} +2 -2
- package/dist/{http-OYI4NEKO.js → http-5WRBWEM6.js} +2 -2
- package/package.json +1 -1
- package/skills/pncli/SKILL.md +2 -1
- package/skills/pncli/github.md +38 -0
- package/skills/pncli/jenkins.md +22 -0
- package/skills/pncli/jira.md +15 -2
- package/skills/pncli/marketplace.md +47 -7
- package/dist/chunk-CFUUOW7B.js.map +0 -1
- package/dist/chunk-YHRFL7VX.js.map +0 -1
- /package/dist/{config-TL5X47RY.js.map → config-S56LADVF.js.map} +0 -0
- /package/dist/{http-OYI4NEKO.js.map → http-5WRBWEM6.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
setRepoConfigValue,
|
|
9
9
|
writeGlobalConfig,
|
|
10
10
|
writeRepoConfig
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-FFVQ4PA7.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-
|
|
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
|
|
29
|
+
import { Command as Command3 } from "commander";
|
|
30
30
|
import { createRequire } from "module";
|
|
31
31
|
|
|
32
32
|
// src/services/git/client.ts
|
|
@@ -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
|
-
|
|
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
|
|
@@ -832,6 +876,7 @@ function guessMimeType(filePath) {
|
|
|
832
876
|
|
|
833
877
|
// src/services/jira/client.ts
|
|
834
878
|
var API2 = "/rest/api/2";
|
|
879
|
+
var AGILE_API = "/rest/agile/1.0";
|
|
835
880
|
var JiraClient = class {
|
|
836
881
|
constructor(http) {
|
|
837
882
|
this.http = http;
|
|
@@ -1027,6 +1072,42 @@ var JiraClient = class {
|
|
|
1027
1072
|
}
|
|
1028
1073
|
});
|
|
1029
1074
|
}
|
|
1075
|
+
async listBoards(project) {
|
|
1076
|
+
return this.http.jiraPaginate(async (startAt, maxResults) => {
|
|
1077
|
+
return this.http.jira(
|
|
1078
|
+
`${AGILE_API}/board`,
|
|
1079
|
+
{ params: { projectKeyOrId: project, startAt, maxResults } }
|
|
1080
|
+
);
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
async listSprintsForBoard(boardId, states) {
|
|
1084
|
+
return this.http.jiraPaginate(async (startAt, maxResults) => {
|
|
1085
|
+
return this.http.jira(
|
|
1086
|
+
`${AGILE_API}/board/${boardId}/sprint`,
|
|
1087
|
+
{ params: { startAt, maxResults, ...states?.length ? { state: states.join(",") } : {} } }
|
|
1088
|
+
);
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
async listSprintsForProject(project, states) {
|
|
1092
|
+
const boards = await this.listBoards(project);
|
|
1093
|
+
const sprints = [];
|
|
1094
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1095
|
+
for (const board of boards) {
|
|
1096
|
+
if (board.type !== "scrum") continue;
|
|
1097
|
+
for (const sprint of await this.listSprintsForBoard(board.id, states)) {
|
|
1098
|
+
if (seen.has(sprint.id)) continue;
|
|
1099
|
+
seen.add(sprint.id);
|
|
1100
|
+
sprints.push(sprint);
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
return sprints;
|
|
1104
|
+
}
|
|
1105
|
+
async setSprint(sprintId, issueKeys) {
|
|
1106
|
+
await this.http.jira(`${AGILE_API}/sprint/${sprintId}/issue`, {
|
|
1107
|
+
method: "POST",
|
|
1108
|
+
body: { issues: issueKeys }
|
|
1109
|
+
});
|
|
1110
|
+
}
|
|
1030
1111
|
};
|
|
1031
1112
|
function schemaToPncliType(schema) {
|
|
1032
1113
|
if (!schema) return void 0;
|
|
@@ -1362,6 +1443,38 @@ function registerJiraCommands(program2) {
|
|
|
1362
1443
|
fail(err, "jira", "fields", start);
|
|
1363
1444
|
}
|
|
1364
1445
|
});
|
|
1446
|
+
jira.command("list-boards").description("List Agile boards for a project").requiredOption("--project <key>", "Project key").action(async (opts) => {
|
|
1447
|
+
const start = Date.now();
|
|
1448
|
+
try {
|
|
1449
|
+
const client = getClient(program2);
|
|
1450
|
+
const data = await client.listBoards(opts.project);
|
|
1451
|
+
success(data, "jira", "list-boards", start);
|
|
1452
|
+
} catch (err) {
|
|
1453
|
+
fail(err, "jira", "list-boards", start);
|
|
1454
|
+
}
|
|
1455
|
+
});
|
|
1456
|
+
jira.command("list-sprints").description("List sprints for a board or project, including start/end dates").option("--board <id>", "Board ID").option("--project <key>", "Project key \u2014 resolves to that project's board(s)").option("--state <states>", "Comma-separated states to filter (active,future,closed)").action(async (opts) => {
|
|
1457
|
+
const start = Date.now();
|
|
1458
|
+
try {
|
|
1459
|
+
if (!opts.board && !opts.project) throw new PncliError("Either --board or --project is required", 1);
|
|
1460
|
+
const client = getClient(program2);
|
|
1461
|
+
const states = opts.state ? opts.state.split(",").map((s) => s.trim()) : void 0;
|
|
1462
|
+
const data = opts.board ? await client.listSprintsForBoard(parseInt(opts.board, 10), states) : await client.listSprintsForProject(opts.project, states);
|
|
1463
|
+
success(data, "jira", "list-sprints", start);
|
|
1464
|
+
} catch (err) {
|
|
1465
|
+
fail(err, "jira", "list-sprints", start);
|
|
1466
|
+
}
|
|
1467
|
+
});
|
|
1468
|
+
jira.command("set-sprint").description("Move a Jira issue into a sprint").requiredOption("--key <issue-key>", "Issue key").requiredOption("--sprint <id>", "Sprint ID (from list-sprints)").action(async (opts) => {
|
|
1469
|
+
const start = Date.now();
|
|
1470
|
+
try {
|
|
1471
|
+
const client = getClient(program2);
|
|
1472
|
+
await client.setSprint(parseInt(opts.sprint, 10), [opts.key]);
|
|
1473
|
+
success({ updated: opts.key, sprint: opts.sprint }, "jira", "set-sprint", start);
|
|
1474
|
+
} catch (err) {
|
|
1475
|
+
fail(err, "jira", "set-sprint", start);
|
|
1476
|
+
}
|
|
1477
|
+
});
|
|
1365
1478
|
}
|
|
1366
1479
|
function parseFieldArgs(fieldArgs, fieldMap) {
|
|
1367
1480
|
const result = {};
|
|
@@ -1675,6 +1788,466 @@ function registerBitbucketCommands(program2) {
|
|
|
1675
1788
|
});
|
|
1676
1789
|
}
|
|
1677
1790
|
|
|
1791
|
+
// src/services/github/commands.ts
|
|
1792
|
+
import { Option } from "commander";
|
|
1793
|
+
|
|
1794
|
+
// src/services/github/client.ts
|
|
1795
|
+
var GitHubClient = class {
|
|
1796
|
+
constructor(http) {
|
|
1797
|
+
this.http = http;
|
|
1798
|
+
}
|
|
1799
|
+
http;
|
|
1800
|
+
async listPRs(opts) {
|
|
1801
|
+
return this.http.githubPaginate(
|
|
1802
|
+
(page, perPage) => this.http.github(
|
|
1803
|
+
`/repos/${opts.owner}/${opts.repo}/pulls`,
|
|
1804
|
+
{
|
|
1805
|
+
params: {
|
|
1806
|
+
state: opts.state ?? "open",
|
|
1807
|
+
...opts.head ? { head: opts.head } : {},
|
|
1808
|
+
...opts.base ? { base: opts.base } : {},
|
|
1809
|
+
per_page: perPage,
|
|
1810
|
+
page
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
)
|
|
1814
|
+
);
|
|
1815
|
+
}
|
|
1816
|
+
async getPR(owner, repo, pullNumber) {
|
|
1817
|
+
return this.http.github(`/repos/${owner}/${repo}/pulls/${pullNumber}`);
|
|
1818
|
+
}
|
|
1819
|
+
async createPR(opts) {
|
|
1820
|
+
return this.http.github(
|
|
1821
|
+
`/repos/${opts.owner}/${opts.repo}/pulls`,
|
|
1822
|
+
{
|
|
1823
|
+
method: "POST",
|
|
1824
|
+
body: {
|
|
1825
|
+
title: opts.title,
|
|
1826
|
+
head: opts.head,
|
|
1827
|
+
base: opts.base,
|
|
1828
|
+
...opts.body !== void 0 ? { body: opts.body } : {},
|
|
1829
|
+
...opts.draft !== void 0 ? { draft: opts.draft } : {}
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
);
|
|
1833
|
+
}
|
|
1834
|
+
async updatePR(opts) {
|
|
1835
|
+
const body = {};
|
|
1836
|
+
if (opts.title !== void 0) body.title = opts.title;
|
|
1837
|
+
if (opts.body !== void 0) body.body = opts.body;
|
|
1838
|
+
if (opts.state !== void 0) body.state = opts.state;
|
|
1839
|
+
if (opts.base !== void 0) body.base = opts.base;
|
|
1840
|
+
return this.http.github(
|
|
1841
|
+
`/repos/${opts.owner}/${opts.repo}/pulls/${opts.pullNumber}`,
|
|
1842
|
+
{ method: "PATCH", body }
|
|
1843
|
+
);
|
|
1844
|
+
}
|
|
1845
|
+
async mergePR(opts) {
|
|
1846
|
+
const body = {};
|
|
1847
|
+
if (opts.commitTitle) body.commit_title = opts.commitTitle;
|
|
1848
|
+
if (opts.commitMessage) body.commit_message = opts.commitMessage;
|
|
1849
|
+
if (opts.mergeMethod) body.merge_method = opts.mergeMethod;
|
|
1850
|
+
return this.http.github(
|
|
1851
|
+
`/repos/${opts.owner}/${opts.repo}/pulls/${opts.pullNumber}/merge`,
|
|
1852
|
+
{ method: "PUT", body }
|
|
1853
|
+
);
|
|
1854
|
+
}
|
|
1855
|
+
async closePR(owner, repo, pullNumber) {
|
|
1856
|
+
return this.updatePR({ owner, repo, pullNumber, state: "closed" });
|
|
1857
|
+
}
|
|
1858
|
+
async listComments(owner, repo, pullNumber) {
|
|
1859
|
+
return this.http.githubPaginate(
|
|
1860
|
+
(page, perPage) => this.http.github(
|
|
1861
|
+
`/repos/${owner}/${repo}/issues/${pullNumber}/comments`,
|
|
1862
|
+
{ params: { per_page: perPage, page } }
|
|
1863
|
+
)
|
|
1864
|
+
);
|
|
1865
|
+
}
|
|
1866
|
+
async addComment(owner, repo, pullNumber, body) {
|
|
1867
|
+
return this.http.github(
|
|
1868
|
+
`/repos/${owner}/${repo}/issues/${pullNumber}/comments`,
|
|
1869
|
+
{ method: "POST", body: { body } }
|
|
1870
|
+
);
|
|
1871
|
+
}
|
|
1872
|
+
async listReviewComments(owner, repo, pullNumber) {
|
|
1873
|
+
return this.http.githubPaginate(
|
|
1874
|
+
(page, perPage) => this.http.github(
|
|
1875
|
+
`/repos/${owner}/${repo}/pulls/${pullNumber}/comments`,
|
|
1876
|
+
{ params: { per_page: perPage, page } }
|
|
1877
|
+
)
|
|
1878
|
+
);
|
|
1879
|
+
}
|
|
1880
|
+
async addInlineComment(opts) {
|
|
1881
|
+
return this.http.github(
|
|
1882
|
+
`/repos/${opts.owner}/${opts.repo}/pulls/${opts.pullNumber}/comments`,
|
|
1883
|
+
{
|
|
1884
|
+
method: "POST",
|
|
1885
|
+
body: {
|
|
1886
|
+
body: opts.body,
|
|
1887
|
+
commit_id: opts.commitId,
|
|
1888
|
+
path: opts.path,
|
|
1889
|
+
line: opts.line,
|
|
1890
|
+
side: opts.side ?? "RIGHT"
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
);
|
|
1894
|
+
}
|
|
1895
|
+
async replyComment(owner, repo, pullNumber, commentId, body) {
|
|
1896
|
+
return this.http.github(
|
|
1897
|
+
`/repos/${owner}/${repo}/pulls/${pullNumber}/comments/${commentId}/replies`,
|
|
1898
|
+
{ method: "POST", body: { body } }
|
|
1899
|
+
);
|
|
1900
|
+
}
|
|
1901
|
+
async deleteComment(owner, repo, commentId) {
|
|
1902
|
+
await this.http.github(
|
|
1903
|
+
`/repos/${owner}/${repo}/issues/comments/${commentId}`,
|
|
1904
|
+
{ method: "DELETE" }
|
|
1905
|
+
);
|
|
1906
|
+
}
|
|
1907
|
+
async deleteReviewComment(owner, repo, commentId) {
|
|
1908
|
+
await this.http.github(
|
|
1909
|
+
`/repos/${owner}/${repo}/pulls/comments/${commentId}`,
|
|
1910
|
+
{ method: "DELETE" }
|
|
1911
|
+
);
|
|
1912
|
+
}
|
|
1913
|
+
async getDiff(owner, repo, pullNumber) {
|
|
1914
|
+
return this.http.githubText(`/repos/${owner}/${repo}/pulls/${pullNumber}`);
|
|
1915
|
+
}
|
|
1916
|
+
async listFiles(owner, repo, pullNumber) {
|
|
1917
|
+
return this.http.githubPaginate(
|
|
1918
|
+
(page, perPage) => this.http.github(
|
|
1919
|
+
`/repos/${owner}/${repo}/pulls/${pullNumber}/files`,
|
|
1920
|
+
{ params: { per_page: perPage, page } }
|
|
1921
|
+
)
|
|
1922
|
+
);
|
|
1923
|
+
}
|
|
1924
|
+
async listReviews(owner, repo, pullNumber) {
|
|
1925
|
+
return this.http.githubPaginate(
|
|
1926
|
+
(page, perPage) => this.http.github(
|
|
1927
|
+
`/repos/${owner}/${repo}/pulls/${pullNumber}/reviews`,
|
|
1928
|
+
{ params: { per_page: perPage, page } }
|
|
1929
|
+
)
|
|
1930
|
+
);
|
|
1931
|
+
}
|
|
1932
|
+
async submitReview(opts) {
|
|
1933
|
+
return this.http.github(
|
|
1934
|
+
`/repos/${opts.owner}/${opts.repo}/pulls/${opts.pullNumber}/reviews`,
|
|
1935
|
+
{
|
|
1936
|
+
method: "POST",
|
|
1937
|
+
body: {
|
|
1938
|
+
event: opts.event,
|
|
1939
|
+
...opts.body !== void 0 ? { body: opts.body } : {}
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
);
|
|
1943
|
+
}
|
|
1944
|
+
async listRequestedReviewers(owner, repo, pullNumber) {
|
|
1945
|
+
return this.http.github(
|
|
1946
|
+
`/repos/${owner}/${repo}/pulls/${pullNumber}/requested_reviewers`
|
|
1947
|
+
);
|
|
1948
|
+
}
|
|
1949
|
+
async getCommitStatuses(owner, repo, ref) {
|
|
1950
|
+
return this.http.github(`/repos/${owner}/${repo}/commits/${ref}/status`);
|
|
1951
|
+
}
|
|
1952
|
+
async listCheckRuns(owner, repo, ref) {
|
|
1953
|
+
const results = [];
|
|
1954
|
+
let page = 1;
|
|
1955
|
+
const perPage = 100;
|
|
1956
|
+
while (true) {
|
|
1957
|
+
const result = await this.http.github(
|
|
1958
|
+
`/repos/${owner}/${repo}/commits/${ref}/check-runs`,
|
|
1959
|
+
{ params: { per_page: perPage, page } }
|
|
1960
|
+
);
|
|
1961
|
+
const runs = result.check_runs ?? [];
|
|
1962
|
+
results.push(...runs);
|
|
1963
|
+
if (runs.length < perPage) break;
|
|
1964
|
+
page++;
|
|
1965
|
+
}
|
|
1966
|
+
return results;
|
|
1967
|
+
}
|
|
1968
|
+
};
|
|
1969
|
+
|
|
1970
|
+
// src/services/github/commands.ts
|
|
1971
|
+
function getClient3(program2, overrides) {
|
|
1972
|
+
const opts = program2.optsWithGlobals();
|
|
1973
|
+
const config = loadConfig({ configPath: opts.config });
|
|
1974
|
+
const http = createHttpClient(config, Boolean(opts.dryRun));
|
|
1975
|
+
const client = new GitHubClient(http);
|
|
1976
|
+
const ctx = getGitContext(config);
|
|
1977
|
+
const owner = overrides?.owner ?? opts.owner ?? ctx?.github?.owner ?? config.defaults.github?.owner ?? "";
|
|
1978
|
+
const repo = overrides?.repo ?? opts.repo ?? ctx?.github?.repo ?? config.defaults.github?.repo ?? "";
|
|
1979
|
+
if (!owner || !repo) {
|
|
1980
|
+
throw new PncliError(
|
|
1981
|
+
"Could not determine GitHub owner/repo. Pass --owner and --repo, or run pncli config init.",
|
|
1982
|
+
1
|
|
1983
|
+
);
|
|
1984
|
+
}
|
|
1985
|
+
return { client, owner, repo, config };
|
|
1986
|
+
}
|
|
1987
|
+
function registerGitHubCommands(program2) {
|
|
1988
|
+
const gh = program2.command("github").description("GitHub repository operations").option("--owner <owner>", "GitHub owner (user or org)").option("--repo <repo>", "GitHub repository name");
|
|
1989
|
+
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) => {
|
|
1990
|
+
const start = Date.now();
|
|
1991
|
+
try {
|
|
1992
|
+
const { client, owner, repo } = getClient3(gh);
|
|
1993
|
+
const data = await client.listPRs({
|
|
1994
|
+
owner,
|
|
1995
|
+
repo,
|
|
1996
|
+
state: opts.state,
|
|
1997
|
+
head: opts.head,
|
|
1998
|
+
base: opts.base
|
|
1999
|
+
});
|
|
2000
|
+
success(data, "github", "list-prs", start);
|
|
2001
|
+
} catch (err) {
|
|
2002
|
+
fail(err, "github", "list-prs", start);
|
|
2003
|
+
}
|
|
2004
|
+
});
|
|
2005
|
+
gh.command("get-pr").description("Get a pull request by number").requiredOption("--number <n>", "Pull request number").action(async (opts) => {
|
|
2006
|
+
const start = Date.now();
|
|
2007
|
+
try {
|
|
2008
|
+
const { client, owner, repo } = getClient3(gh);
|
|
2009
|
+
const data = await client.getPR(owner, repo, parseInt(opts.number, 10));
|
|
2010
|
+
success(data, "github", "get-pr", start);
|
|
2011
|
+
} catch (err) {
|
|
2012
|
+
fail(err, "github", "get-pr", start);
|
|
2013
|
+
}
|
|
2014
|
+
});
|
|
2015
|
+
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) => {
|
|
2016
|
+
const start = Date.now();
|
|
2017
|
+
try {
|
|
2018
|
+
const { client, owner, repo, config } = getClient3(gh, { owner: opts.owner, repo: opts.repo });
|
|
2019
|
+
const base = opts.base ?? config.defaults.github?.targetBranch ?? "main";
|
|
2020
|
+
const data = await client.createPR({
|
|
2021
|
+
owner,
|
|
2022
|
+
repo,
|
|
2023
|
+
title: opts.title,
|
|
2024
|
+
head: opts.head,
|
|
2025
|
+
base,
|
|
2026
|
+
body: opts.body,
|
|
2027
|
+
draft: opts.draft
|
|
2028
|
+
});
|
|
2029
|
+
success(data, "github", "create-pr", start);
|
|
2030
|
+
} catch (err) {
|
|
2031
|
+
fail(err, "github", "create-pr", start);
|
|
2032
|
+
}
|
|
2033
|
+
});
|
|
2034
|
+
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) => {
|
|
2035
|
+
const start = Date.now();
|
|
2036
|
+
try {
|
|
2037
|
+
const { client, owner, repo } = getClient3(gh);
|
|
2038
|
+
const data = await client.updatePR({
|
|
2039
|
+
owner,
|
|
2040
|
+
repo,
|
|
2041
|
+
pullNumber: parseInt(opts.number, 10),
|
|
2042
|
+
title: opts.title,
|
|
2043
|
+
body: opts.body,
|
|
2044
|
+
base: opts.base
|
|
2045
|
+
});
|
|
2046
|
+
success(data, "github", "update-pr", start);
|
|
2047
|
+
} catch (err) {
|
|
2048
|
+
fail(err, "github", "update-pr", start);
|
|
2049
|
+
}
|
|
2050
|
+
});
|
|
2051
|
+
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) => {
|
|
2052
|
+
const start = Date.now();
|
|
2053
|
+
try {
|
|
2054
|
+
const { client, owner, repo } = getClient3(gh);
|
|
2055
|
+
const data = await client.mergePR({
|
|
2056
|
+
owner,
|
|
2057
|
+
repo,
|
|
2058
|
+
pullNumber: parseInt(opts.number, 10),
|
|
2059
|
+
mergeMethod: opts.method,
|
|
2060
|
+
commitTitle: opts.commitTitle,
|
|
2061
|
+
commitMessage: opts.commitMessage
|
|
2062
|
+
});
|
|
2063
|
+
success(data, "github", "merge-pr", start);
|
|
2064
|
+
} catch (err) {
|
|
2065
|
+
fail(err, "github", "merge-pr", start);
|
|
2066
|
+
}
|
|
2067
|
+
});
|
|
2068
|
+
gh.command("close-pr").description("Close a pull request").requiredOption("--number <n>", "Pull request number").action(async (opts) => {
|
|
2069
|
+
const start = Date.now();
|
|
2070
|
+
try {
|
|
2071
|
+
const { client, owner, repo } = getClient3(gh);
|
|
2072
|
+
const data = await client.closePR(owner, repo, parseInt(opts.number, 10));
|
|
2073
|
+
success(data, "github", "close-pr", start);
|
|
2074
|
+
} catch (err) {
|
|
2075
|
+
fail(err, "github", "close-pr", start);
|
|
2076
|
+
}
|
|
2077
|
+
});
|
|
2078
|
+
gh.command("list-comments").description("List general comments on a pull request").requiredOption("--number <n>", "Pull request number").action(async (opts) => {
|
|
2079
|
+
const start = Date.now();
|
|
2080
|
+
try {
|
|
2081
|
+
const { client, owner, repo } = getClient3(gh);
|
|
2082
|
+
const data = await client.listComments(owner, repo, parseInt(opts.number, 10));
|
|
2083
|
+
success(data, "github", "list-comments", start);
|
|
2084
|
+
} catch (err) {
|
|
2085
|
+
fail(err, "github", "list-comments", start);
|
|
2086
|
+
}
|
|
2087
|
+
});
|
|
2088
|
+
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) => {
|
|
2089
|
+
const start = Date.now();
|
|
2090
|
+
try {
|
|
2091
|
+
const { client, owner, repo } = getClient3(gh);
|
|
2092
|
+
const data = await client.addComment(owner, repo, parseInt(opts.number, 10), opts.body);
|
|
2093
|
+
success(data, "github", "add-comment", start);
|
|
2094
|
+
} catch (err) {
|
|
2095
|
+
fail(err, "github", "add-comment", start);
|
|
2096
|
+
}
|
|
2097
|
+
});
|
|
2098
|
+
gh.command("list-review-comments").description("List inline review comments on 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.listReviewComments(owner, repo, parseInt(opts.number, 10));
|
|
2103
|
+
success(data, "github", "list-review-comments", start);
|
|
2104
|
+
} catch (err) {
|
|
2105
|
+
fail(err, "github", "list-review-comments", start);
|
|
2106
|
+
}
|
|
2107
|
+
});
|
|
2108
|
+
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) => {
|
|
2109
|
+
const start = Date.now();
|
|
2110
|
+
try {
|
|
2111
|
+
const { client, owner, repo } = getClient3(gh);
|
|
2112
|
+
const data = await client.addInlineComment({
|
|
2113
|
+
owner,
|
|
2114
|
+
repo,
|
|
2115
|
+
pullNumber: parseInt(opts.number, 10),
|
|
2116
|
+
body: opts.body,
|
|
2117
|
+
commitId: opts.commit,
|
|
2118
|
+
path: opts.file,
|
|
2119
|
+
line: parseInt(opts.line, 10),
|
|
2120
|
+
side: opts.side
|
|
2121
|
+
});
|
|
2122
|
+
success(data, "github", "add-inline-comment", start);
|
|
2123
|
+
} catch (err) {
|
|
2124
|
+
fail(err, "github", "add-inline-comment", start);
|
|
2125
|
+
}
|
|
2126
|
+
});
|
|
2127
|
+
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) => {
|
|
2128
|
+
const start = Date.now();
|
|
2129
|
+
try {
|
|
2130
|
+
const { client, owner, repo } = getClient3(gh);
|
|
2131
|
+
const data = await client.replyComment(owner, repo, parseInt(opts.number, 10), parseInt(opts.commentId, 10), opts.body);
|
|
2132
|
+
success(data, "github", "reply-comment", start);
|
|
2133
|
+
} catch (err) {
|
|
2134
|
+
fail(err, "github", "reply-comment", start);
|
|
2135
|
+
}
|
|
2136
|
+
});
|
|
2137
|
+
gh.command("delete-comment").description("Delete a general issue comment").requiredOption("--comment-id <id>", "Comment ID").action(async (opts) => {
|
|
2138
|
+
const start = Date.now();
|
|
2139
|
+
try {
|
|
2140
|
+
const { client, owner, repo } = getClient3(gh);
|
|
2141
|
+
await client.deleteComment(owner, repo, parseInt(opts.commentId, 10));
|
|
2142
|
+
success({ deleted: true }, "github", "delete-comment", start);
|
|
2143
|
+
} catch (err) {
|
|
2144
|
+
fail(err, "github", "delete-comment", start);
|
|
2145
|
+
}
|
|
2146
|
+
});
|
|
2147
|
+
gh.command("delete-review-comment").description("Delete an inline review comment").requiredOption("--comment-id <id>", "Review comment ID").action(async (opts) => {
|
|
2148
|
+
const start = Date.now();
|
|
2149
|
+
try {
|
|
2150
|
+
const { client, owner, repo } = getClient3(gh);
|
|
2151
|
+
await client.deleteReviewComment(owner, repo, parseInt(opts.commentId, 10));
|
|
2152
|
+
success({ deleted: true }, "github", "delete-review-comment", start);
|
|
2153
|
+
} catch (err) {
|
|
2154
|
+
fail(err, "github", "delete-review-comment", start);
|
|
2155
|
+
}
|
|
2156
|
+
});
|
|
2157
|
+
gh.command("diff").description("Get unified diff for a pull request").requiredOption("--number <n>", "Pull request number").action(async (opts) => {
|
|
2158
|
+
const start = Date.now();
|
|
2159
|
+
try {
|
|
2160
|
+
const { client, owner, repo } = getClient3(gh);
|
|
2161
|
+
const diff = await client.getDiff(owner, repo, parseInt(opts.number, 10));
|
|
2162
|
+
success({ diff }, "github", "diff", start);
|
|
2163
|
+
} catch (err) {
|
|
2164
|
+
fail(err, "github", "diff", start);
|
|
2165
|
+
}
|
|
2166
|
+
});
|
|
2167
|
+
gh.command("list-files").description("List files changed in a pull request").requiredOption("--number <n>", "Pull request number").action(async (opts) => {
|
|
2168
|
+
const start = Date.now();
|
|
2169
|
+
try {
|
|
2170
|
+
const { client, owner, repo } = getClient3(gh);
|
|
2171
|
+
const data = await client.listFiles(owner, repo, parseInt(opts.number, 10));
|
|
2172
|
+
success(data, "github", "list-files", start);
|
|
2173
|
+
} catch (err) {
|
|
2174
|
+
fail(err, "github", "list-files", start);
|
|
2175
|
+
}
|
|
2176
|
+
});
|
|
2177
|
+
gh.command("list-reviews").description("List reviews on a pull request").requiredOption("--number <n>", "Pull request number").action(async (opts) => {
|
|
2178
|
+
const start = Date.now();
|
|
2179
|
+
try {
|
|
2180
|
+
const { client, owner, repo } = getClient3(gh);
|
|
2181
|
+
const data = await client.listReviews(owner, repo, parseInt(opts.number, 10));
|
|
2182
|
+
success(data, "github", "list-reviews", start);
|
|
2183
|
+
} catch (err) {
|
|
2184
|
+
fail(err, "github", "list-reviews", start);
|
|
2185
|
+
}
|
|
2186
|
+
});
|
|
2187
|
+
gh.command("approve").description("Approve a pull request").requiredOption("--number <n>", "Pull request number").option("--body <text>", "Review body text").action(async (opts) => {
|
|
2188
|
+
const start = Date.now();
|
|
2189
|
+
try {
|
|
2190
|
+
const { client, owner, repo } = getClient3(gh);
|
|
2191
|
+
const data = await client.submitReview({
|
|
2192
|
+
owner,
|
|
2193
|
+
repo,
|
|
2194
|
+
pullNumber: parseInt(opts.number, 10),
|
|
2195
|
+
event: "APPROVE",
|
|
2196
|
+
body: opts.body
|
|
2197
|
+
});
|
|
2198
|
+
success(data, "github", "approve", start);
|
|
2199
|
+
} catch (err) {
|
|
2200
|
+
fail(err, "github", "approve", start);
|
|
2201
|
+
}
|
|
2202
|
+
});
|
|
2203
|
+
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) => {
|
|
2204
|
+
const start = Date.now();
|
|
2205
|
+
try {
|
|
2206
|
+
const { client, owner, repo } = getClient3(gh);
|
|
2207
|
+
const data = await client.submitReview({
|
|
2208
|
+
owner,
|
|
2209
|
+
repo,
|
|
2210
|
+
pullNumber: parseInt(opts.number, 10),
|
|
2211
|
+
event: "REQUEST_CHANGES",
|
|
2212
|
+
body: opts.body
|
|
2213
|
+
});
|
|
2214
|
+
success(data, "github", "request-changes", start);
|
|
2215
|
+
} catch (err) {
|
|
2216
|
+
fail(err, "github", "request-changes", start);
|
|
2217
|
+
}
|
|
2218
|
+
});
|
|
2219
|
+
gh.command("list-reviewers").description("List requested reviewers for a pull request").requiredOption("--number <n>", "Pull request number").action(async (opts) => {
|
|
2220
|
+
const start = Date.now();
|
|
2221
|
+
try {
|
|
2222
|
+
const { client, owner, repo } = getClient3(gh);
|
|
2223
|
+
const data = await client.listRequestedReviewers(owner, repo, parseInt(opts.number, 10));
|
|
2224
|
+
success(data, "github", "list-reviewers", start);
|
|
2225
|
+
} catch (err) {
|
|
2226
|
+
fail(err, "github", "list-reviewers", start);
|
|
2227
|
+
}
|
|
2228
|
+
});
|
|
2229
|
+
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) => {
|
|
2230
|
+
const start = Date.now();
|
|
2231
|
+
try {
|
|
2232
|
+
const { client, owner, repo } = getClient3(gh);
|
|
2233
|
+
const data = await client.getCommitStatuses(owner, repo, opts.ref);
|
|
2234
|
+
success(data, "github", "get-status", start);
|
|
2235
|
+
} catch (err) {
|
|
2236
|
+
fail(err, "github", "get-status", start);
|
|
2237
|
+
}
|
|
2238
|
+
});
|
|
2239
|
+
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) => {
|
|
2240
|
+
const start = Date.now();
|
|
2241
|
+
try {
|
|
2242
|
+
const { client, owner, repo } = getClient3(gh);
|
|
2243
|
+
const data = await client.listCheckRuns(owner, repo, opts.ref);
|
|
2244
|
+
success(data, "github", "list-checks", start);
|
|
2245
|
+
} catch (err) {
|
|
2246
|
+
fail(err, "github", "list-checks", start);
|
|
2247
|
+
}
|
|
2248
|
+
});
|
|
2249
|
+
}
|
|
2250
|
+
|
|
1678
2251
|
// src/services/confluence/client.ts
|
|
1679
2252
|
var API3 = "/rest/api";
|
|
1680
2253
|
var ConfluenceClient = class {
|
|
@@ -1823,7 +2396,7 @@ var ConfluenceClient = class {
|
|
|
1823
2396
|
};
|
|
1824
2397
|
|
|
1825
2398
|
// src/services/confluence/commands.ts
|
|
1826
|
-
function
|
|
2399
|
+
function getClient4(program2) {
|
|
1827
2400
|
const opts = program2.optsWithGlobals();
|
|
1828
2401
|
const config = loadConfig({ configPath: opts.config });
|
|
1829
2402
|
if (!config.confluence.baseUrl) throw new PncliError("Confluence not configured. Run: pncli config init");
|
|
@@ -1835,7 +2408,7 @@ function registerConfluenceCommands(program2) {
|
|
|
1835
2408
|
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
2409
|
const start = Date.now();
|
|
1837
2410
|
try {
|
|
1838
|
-
const client =
|
|
2411
|
+
const client = getClient4(program2);
|
|
1839
2412
|
const data = await client.getPage(opts.id, opts.expand);
|
|
1840
2413
|
success(data, "confluence", "get-page", start);
|
|
1841
2414
|
} catch (err) {
|
|
@@ -1845,7 +2418,7 @@ function registerConfluenceCommands(program2) {
|
|
|
1845
2418
|
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
2419
|
const start = Date.now();
|
|
1847
2420
|
try {
|
|
1848
|
-
const client =
|
|
2421
|
+
const client = getClient4(program2);
|
|
1849
2422
|
const data = await client.getPageByTitle(opts.space, opts.title);
|
|
1850
2423
|
if (!data) throw new PncliError(`Page not found: "${opts.title}" in space ${opts.space}`, 1);
|
|
1851
2424
|
success(data, "confluence", "get-page-by-title", start);
|
|
@@ -1856,7 +2429,7 @@ function registerConfluenceCommands(program2) {
|
|
|
1856
2429
|
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
2430
|
const start = Date.now();
|
|
1858
2431
|
try {
|
|
1859
|
-
const client =
|
|
2432
|
+
const client = getClient4(program2);
|
|
1860
2433
|
const data = await client.listPages(opts.space, {
|
|
1861
2434
|
limit: opts.limit ? parseInt(opts.limit, 10) : void 0,
|
|
1862
2435
|
start: opts.start ? parseInt(opts.start, 10) : void 0
|
|
@@ -1869,7 +2442,7 @@ function registerConfluenceCommands(program2) {
|
|
|
1869
2442
|
confluence.command("get-page-children").description("Get child pages of a Confluence page").requiredOption("--id <page-id>", "Parent page ID").action(async (opts) => {
|
|
1870
2443
|
const start = Date.now();
|
|
1871
2444
|
try {
|
|
1872
|
-
const client =
|
|
2445
|
+
const client = getClient4(program2);
|
|
1873
2446
|
const data = await client.getPageChildren(opts.id);
|
|
1874
2447
|
success(data, "confluence", "get-page-children", start);
|
|
1875
2448
|
} catch (err) {
|
|
@@ -1879,7 +2452,7 @@ function registerConfluenceCommands(program2) {
|
|
|
1879
2452
|
confluence.command("get-labels").description("Get labels on a Confluence page").requiredOption("--id <page-id>", "Page ID").action(async (opts) => {
|
|
1880
2453
|
const start = Date.now();
|
|
1881
2454
|
try {
|
|
1882
|
-
const client =
|
|
2455
|
+
const client = getClient4(program2);
|
|
1883
2456
|
const data = await client.getLabels(opts.id);
|
|
1884
2457
|
success(data, "confluence", "get-labels", start);
|
|
1885
2458
|
} catch (err) {
|
|
@@ -1889,7 +2462,7 @@ function registerConfluenceCommands(program2) {
|
|
|
1889
2462
|
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
2463
|
const start = Date.now();
|
|
1891
2464
|
try {
|
|
1892
|
-
const client =
|
|
2465
|
+
const client = getClient4(program2);
|
|
1893
2466
|
const data = await client.search(opts.cql, {
|
|
1894
2467
|
limit: parseInt(opts.limit, 10),
|
|
1895
2468
|
start: parseInt(opts.start, 10),
|
|
@@ -1903,7 +2476,7 @@ function registerConfluenceCommands(program2) {
|
|
|
1903
2476
|
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
2477
|
const start = Date.now();
|
|
1905
2478
|
try {
|
|
1906
|
-
const client =
|
|
2479
|
+
const client = getClient4(program2);
|
|
1907
2480
|
const data = await client.createPage({
|
|
1908
2481
|
spaceKey: opts.space,
|
|
1909
2482
|
title: opts.title,
|
|
@@ -1919,7 +2492,7 @@ function registerConfluenceCommands(program2) {
|
|
|
1919
2492
|
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
2493
|
const start = Date.now();
|
|
1921
2494
|
try {
|
|
1922
|
-
const client =
|
|
2495
|
+
const client = getClient4(program2);
|
|
1923
2496
|
const current = await client.getPage(opts.id, "version");
|
|
1924
2497
|
const nextVersion = current.version.number + 1;
|
|
1925
2498
|
const data = await client.updatePage(opts.id, {
|
|
@@ -1937,7 +2510,7 @@ function registerConfluenceCommands(program2) {
|
|
|
1937
2510
|
confluence.command("delete-page").description("Delete a Confluence page").requiredOption("--id <page-id>", "Page ID").action(async (opts) => {
|
|
1938
2511
|
const start = Date.now();
|
|
1939
2512
|
try {
|
|
1940
|
-
const client =
|
|
2513
|
+
const client = getClient4(program2);
|
|
1941
2514
|
await client.deletePage(opts.id);
|
|
1942
2515
|
success({ deleted: opts.id }, "confluence", "delete-page", start);
|
|
1943
2516
|
} catch (err) {
|
|
@@ -1947,7 +2520,7 @@ function registerConfluenceCommands(program2) {
|
|
|
1947
2520
|
confluence.command("list-comments").description("List comments on a Confluence page").requiredOption("--id <page-id>", "Page ID").action(async (opts) => {
|
|
1948
2521
|
const start = Date.now();
|
|
1949
2522
|
try {
|
|
1950
|
-
const client =
|
|
2523
|
+
const client = getClient4(program2);
|
|
1951
2524
|
const data = await client.listComments(opts.id);
|
|
1952
2525
|
success(data, "confluence", "list-comments", start);
|
|
1953
2526
|
} catch (err) {
|
|
@@ -1957,7 +2530,7 @@ function registerConfluenceCommands(program2) {
|
|
|
1957
2530
|
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
2531
|
const start = Date.now();
|
|
1959
2532
|
try {
|
|
1960
|
-
const client =
|
|
2533
|
+
const client = getClient4(program2);
|
|
1961
2534
|
const data = await client.addComment(opts.id, opts.body, opts.representation);
|
|
1962
2535
|
success(data, "confluence", "add-comment", start);
|
|
1963
2536
|
} catch (err) {
|
|
@@ -1967,7 +2540,7 @@ function registerConfluenceCommands(program2) {
|
|
|
1967
2540
|
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
2541
|
const start = Date.now();
|
|
1969
2542
|
try {
|
|
1970
|
-
const client =
|
|
2543
|
+
const client = getClient4(program2);
|
|
1971
2544
|
const labels = opts.labels.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1972
2545
|
const data = await client.addLabel(opts.id, labels);
|
|
1973
2546
|
success(data, "confluence", "add-label", start);
|
|
@@ -1978,7 +2551,7 @@ function registerConfluenceCommands(program2) {
|
|
|
1978
2551
|
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
2552
|
const start = Date.now();
|
|
1980
2553
|
try {
|
|
1981
|
-
const client =
|
|
2554
|
+
const client = getClient4(program2);
|
|
1982
2555
|
await client.removeLabel(opts.id, opts.label);
|
|
1983
2556
|
success({ removed: opts.label, from: opts.id }, "confluence", "remove-label", start);
|
|
1984
2557
|
} catch (err) {
|
|
@@ -1988,7 +2561,7 @@ function registerConfluenceCommands(program2) {
|
|
|
1988
2561
|
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
2562
|
const start = Date.now();
|
|
1990
2563
|
try {
|
|
1991
|
-
const client =
|
|
2564
|
+
const client = getClient4(program2);
|
|
1992
2565
|
const data = await client.listSpaces({
|
|
1993
2566
|
type: opts.type,
|
|
1994
2567
|
limit: opts.limit ? parseInt(opts.limit, 10) : void 0
|
|
@@ -2001,7 +2574,7 @@ function registerConfluenceCommands(program2) {
|
|
|
2001
2574
|
confluence.command("get-space").description("Get a Confluence space by key").requiredOption("--key <space-key>", "Space key").action(async (opts) => {
|
|
2002
2575
|
const start = Date.now();
|
|
2003
2576
|
try {
|
|
2004
|
-
const client =
|
|
2577
|
+
const client = getClient4(program2);
|
|
2005
2578
|
const data = await client.getSpace(opts.key);
|
|
2006
2579
|
success(data, "confluence", "get-space", start);
|
|
2007
2580
|
} catch (err) {
|
|
@@ -2011,7 +2584,7 @@ function registerConfluenceCommands(program2) {
|
|
|
2011
2584
|
confluence.command("list-attachments").description("List attachments on a Confluence page").requiredOption("--id <page-id>", "Page ID").action(async (opts) => {
|
|
2012
2585
|
const start = Date.now();
|
|
2013
2586
|
try {
|
|
2014
|
-
const client =
|
|
2587
|
+
const client = getClient4(program2);
|
|
2015
2588
|
const data = await client.listAttachments(opts.id);
|
|
2016
2589
|
success(data, "confluence", "list-attachments", start);
|
|
2017
2590
|
} catch (err) {
|
|
@@ -2123,7 +2696,7 @@ var SonarClient = class {
|
|
|
2123
2696
|
|
|
2124
2697
|
// src/services/sonar/commands.ts
|
|
2125
2698
|
var DEFAULT_METRICS = "coverage,duplicated_lines_density,bugs,vulnerabilities,code_smells,sqale_rating,reliability_rating,security_rating,ncloc";
|
|
2126
|
-
function
|
|
2699
|
+
function getClient5(program2) {
|
|
2127
2700
|
const opts = program2.optsWithGlobals();
|
|
2128
2701
|
const config = loadConfig({ configPath: opts.config });
|
|
2129
2702
|
if (!config.sonar.baseUrl) throw new PncliError("SonarQube not configured. Run: pncli config init");
|
|
@@ -2140,7 +2713,7 @@ function registerSonarCommands(program2) {
|
|
|
2140
2713
|
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
2714
|
const start = Date.now();
|
|
2142
2715
|
try {
|
|
2143
|
-
const { client, config } =
|
|
2716
|
+
const { client, config } = getClient5(program2);
|
|
2144
2717
|
const projectKey = resolveProject(config, opts.project);
|
|
2145
2718
|
const data = await client.getQualityGate(projectKey, opts.branch);
|
|
2146
2719
|
success(data, "sonar", "quality-gate", start);
|
|
@@ -2151,7 +2724,7 @@ function registerSonarCommands(program2) {
|
|
|
2151
2724
|
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
2725
|
const start = Date.now();
|
|
2153
2726
|
try {
|
|
2154
|
-
const { client, config } =
|
|
2727
|
+
const { client, config } = getClient5(program2);
|
|
2155
2728
|
const projectKey = resolveProject(config, opts.project);
|
|
2156
2729
|
const baseOpts = {
|
|
2157
2730
|
projectKey,
|
|
@@ -2179,7 +2752,7 @@ function registerSonarCommands(program2) {
|
|
|
2179
2752
|
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
2753
|
const start = Date.now();
|
|
2181
2754
|
try {
|
|
2182
|
-
const { client, config } =
|
|
2755
|
+
const { client, config } = getClient5(program2);
|
|
2183
2756
|
const projectKey = resolveProject(config, opts.project);
|
|
2184
2757
|
const data = await client.getMeasures({
|
|
2185
2758
|
projectKey,
|
|
@@ -2194,7 +2767,7 @@ function registerSonarCommands(program2) {
|
|
|
2194
2767
|
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
2768
|
const start = Date.now();
|
|
2196
2769
|
try {
|
|
2197
|
-
const { client } =
|
|
2770
|
+
const { client } = getClient5(program2);
|
|
2198
2771
|
if (opts.all) {
|
|
2199
2772
|
const data = await client.searchAllProjects(opts.query);
|
|
2200
2773
|
success(data, "sonar", "projects", start);
|
|
@@ -2213,7 +2786,7 @@ function registerSonarCommands(program2) {
|
|
|
2213
2786
|
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
2787
|
const start = Date.now();
|
|
2215
2788
|
try {
|
|
2216
|
-
const { client, config } =
|
|
2789
|
+
const { client, config } = getClient5(program2);
|
|
2217
2790
|
const projectKey = resolveProject(config, opts.project);
|
|
2218
2791
|
const baseOpts = {
|
|
2219
2792
|
projectKey,
|
|
@@ -2369,7 +2942,7 @@ var SdeClient = class {
|
|
|
2369
2942
|
};
|
|
2370
2943
|
|
|
2371
2944
|
// src/services/sde/commands.ts
|
|
2372
|
-
function
|
|
2945
|
+
function getClient6(program2) {
|
|
2373
2946
|
const opts = program2.optsWithGlobals();
|
|
2374
2947
|
const config = loadConfig({ configPath: opts.config });
|
|
2375
2948
|
if (!config.sde.baseUrl) throw new PncliError("SDElements not configured. Run: pncli config init");
|
|
@@ -2401,7 +2974,7 @@ function registerSdeCommands(program2) {
|
|
|
2401
2974
|
sde.command("server-info").description("Get server version and platform info (requires super-user)").action(async () => {
|
|
2402
2975
|
const start = Date.now();
|
|
2403
2976
|
try {
|
|
2404
|
-
const { client } =
|
|
2977
|
+
const { client } = getClient6(program2);
|
|
2405
2978
|
const data = await client.getServerInfo();
|
|
2406
2979
|
success(data, "sde", "server-info", start);
|
|
2407
2980
|
} catch (err) {
|
|
@@ -2411,7 +2984,7 @@ function registerSdeCommands(program2) {
|
|
|
2411
2984
|
sde.command("whoami").description("Get current authenticated user").action(async () => {
|
|
2412
2985
|
const start = Date.now();
|
|
2413
2986
|
try {
|
|
2414
|
-
const { client } =
|
|
2987
|
+
const { client } = getClient6(program2);
|
|
2415
2988
|
const data = await client.getMe();
|
|
2416
2989
|
success(data, "sde", "whoami", start);
|
|
2417
2990
|
} catch (err) {
|
|
@@ -2421,7 +2994,7 @@ function registerSdeCommands(program2) {
|
|
|
2421
2994
|
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
2995
|
const start = Date.now();
|
|
2423
2996
|
try {
|
|
2424
|
-
const { client } =
|
|
2997
|
+
const { client } = getClient6(program2);
|
|
2425
2998
|
const baseOpts = {
|
|
2426
2999
|
email: opts.email,
|
|
2427
3000
|
firstName: opts.firstName,
|
|
@@ -2446,7 +3019,7 @@ function registerSdeCommands(program2) {
|
|
|
2446
3019
|
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
3020
|
const start = Date.now();
|
|
2448
3021
|
try {
|
|
2449
|
-
const { client } =
|
|
3022
|
+
const { client } = getClient6(program2);
|
|
2450
3023
|
const baseOpts = {
|
|
2451
3024
|
name: opts.name,
|
|
2452
3025
|
search: opts.search,
|
|
@@ -2473,7 +3046,7 @@ function registerSdeCommands(program2) {
|
|
|
2473
3046
|
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
3047
|
const start = Date.now();
|
|
2475
3048
|
try {
|
|
2476
|
-
const { client, config } =
|
|
3049
|
+
const { client, config } = getClient6(program2);
|
|
2477
3050
|
const projectId = resolveProject2(config, opts.id);
|
|
2478
3051
|
const data = await client.getProject(projectId, opts.expand, opts.include);
|
|
2479
3052
|
success(data, "sde", "project", start);
|
|
@@ -2484,7 +3057,7 @@ function registerSdeCommands(program2) {
|
|
|
2484
3057
|
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
3058
|
const start = Date.now();
|
|
2486
3059
|
try {
|
|
2487
|
-
const { client, config } =
|
|
3060
|
+
const { client, config } = getClient6(program2);
|
|
2488
3061
|
const projectId = resolveProject2(config, opts.project);
|
|
2489
3062
|
const baseOpts = {
|
|
2490
3063
|
projectId,
|
|
@@ -2518,7 +3091,7 @@ function registerSdeCommands(program2) {
|
|
|
2518
3091
|
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
3092
|
const start = Date.now();
|
|
2520
3093
|
try {
|
|
2521
|
-
const { client, config } =
|
|
3094
|
+
const { client, config } = getClient6(program2);
|
|
2522
3095
|
const projectId = resolveProject2(config, opts.project);
|
|
2523
3096
|
const data = await client.getTask(projectId, opts.task, opts.expand, opts.include);
|
|
2524
3097
|
success(data, "sde", "task", start);
|
|
@@ -2529,7 +3102,7 @@ function registerSdeCommands(program2) {
|
|
|
2529
3102
|
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
3103
|
const start = Date.now();
|
|
2531
3104
|
try {
|
|
2532
|
-
const { client, config } =
|
|
3105
|
+
const { client, config } = getClient6(program2);
|
|
2533
3106
|
const projectId = resolveProject2(config, opts.project);
|
|
2534
3107
|
const baseOpts = {
|
|
2535
3108
|
projectId,
|
|
@@ -2557,7 +3130,7 @@ function registerSdeCommands(program2) {
|
|
|
2557
3130
|
}
|
|
2558
3131
|
|
|
2559
3132
|
// src/services/deps/commands.ts
|
|
2560
|
-
import { Option } from "commander";
|
|
3133
|
+
import { Option as Option2 } from "commander";
|
|
2561
3134
|
|
|
2562
3135
|
// src/services/deps/parsers/index.ts
|
|
2563
3136
|
import fs from "fs";
|
|
@@ -4241,7 +4814,7 @@ async function runLicenseCheck(config, opts) {
|
|
|
4241
4814
|
// src/services/deps/commands.ts
|
|
4242
4815
|
function registerDepsCommands(program2) {
|
|
4243
4816
|
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
|
|
4817
|
+
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
4818
|
const startTime = Date.now();
|
|
4246
4819
|
try {
|
|
4247
4820
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -4441,6 +5014,16 @@ var AdoWorkClient = class {
|
|
|
4441
5014
|
async downloadAttachment(absoluteUrl) {
|
|
4442
5015
|
return this.http.adoBuffer(absoluteUrl);
|
|
4443
5016
|
}
|
|
5017
|
+
async listAreas(collection, project, depth = 10) {
|
|
5018
|
+
return this.http.ado(
|
|
5019
|
+
`/${encodeURIComponent(collection)}/${encodeURIComponent(project)}/_apis/wit/classificationnodes/areas?$depth=${depth}&api-version=${API6}`
|
|
5020
|
+
);
|
|
5021
|
+
}
|
|
5022
|
+
async listIterations(collection, project, depth = 10) {
|
|
5023
|
+
return this.http.ado(
|
|
5024
|
+
`/${encodeURIComponent(collection)}/${encodeURIComponent(project)}/_apis/wit/classificationnodes/iterations?$depth=${depth}&api-version=${API6}`
|
|
5025
|
+
);
|
|
5026
|
+
}
|
|
4444
5027
|
async uploadAttachment(collection, workItemId, filePath, comment) {
|
|
4445
5028
|
const fileContent = readFileSync3(filePath);
|
|
4446
5029
|
const fileName = basename2(filePath);
|
|
@@ -4600,6 +5183,16 @@ function registerConfigCommands(program2) {
|
|
|
4600
5183
|
} else {
|
|
4601
5184
|
results.bitbucket = { ok: null, message: "not configured" };
|
|
4602
5185
|
}
|
|
5186
|
+
if (cfg.github.baseUrl) {
|
|
5187
|
+
try {
|
|
5188
|
+
await http.github("/user");
|
|
5189
|
+
results.github = { ok: true, message: "connected" };
|
|
5190
|
+
} catch (err) {
|
|
5191
|
+
results.github = { ok: false, message: err instanceof Error ? err.message : String(err) };
|
|
5192
|
+
}
|
|
5193
|
+
} else {
|
|
5194
|
+
results.github = { ok: null, message: "not configured" };
|
|
5195
|
+
}
|
|
4603
5196
|
if (cfg.confluence.baseUrl) {
|
|
4604
5197
|
try {
|
|
4605
5198
|
await http.confluence("/rest/api/space", { params: { limit: 1 } });
|
|
@@ -4770,6 +5363,18 @@ function registerConfigCommands(program2) {
|
|
|
4770
5363
|
results.bitbucket = categorize2(err);
|
|
4771
5364
|
}
|
|
4772
5365
|
}
|
|
5366
|
+
if (!cfg.github.token) {
|
|
5367
|
+
results.github = { status: "blank", message: "not configured" };
|
|
5368
|
+
} else if (!cfg.github.baseUrl) {
|
|
5369
|
+
results.github = { status: "error", message: "baseUrl not configured" };
|
|
5370
|
+
} else {
|
|
5371
|
+
try {
|
|
5372
|
+
await http.github("/user", { timeoutMs: 1e4 });
|
|
5373
|
+
results.github = { status: "valid", message: "ok" };
|
|
5374
|
+
} catch (err) {
|
|
5375
|
+
results.github = categorize2(err);
|
|
5376
|
+
}
|
|
5377
|
+
}
|
|
4773
5378
|
if (!cfg.confluence.apiTokenExplicit && !cfg.jira.apiToken) {
|
|
4774
5379
|
results.confluence = { status: "blank", message: "not configured" };
|
|
4775
5380
|
} else if (!cfg.confluence.apiToken) {
|
|
@@ -4922,7 +5527,7 @@ function registerConfigCommands(program2) {
|
|
|
4922
5527
|
if (statuses.includes("invalid")) process.exitCode = ExitCode.AUTH_ERROR;
|
|
4923
5528
|
else if (statuses.includes("error")) process.exitCode = ExitCode.NETWORK_ERROR;
|
|
4924
5529
|
if (cmdOpts.output === "table") {
|
|
4925
|
-
const services = ["jira", "bitbucket", "confluence", "sonar", "sde", "ado", "jenkins", "udeploy", "artifactory", "checkmarx", "servicenow", "contrast", "sonatypeiq", "openshift"];
|
|
5530
|
+
const services = ["jira", "bitbucket", "github", "confluence", "sonar", "sde", "ado", "jenkins", "udeploy", "artifactory", "checkmarx", "servicenow", "contrast", "sonatypeiq", "openshift"];
|
|
4926
5531
|
const labelWidth = 14;
|
|
4927
5532
|
const statusWidth = 9;
|
|
4928
5533
|
for (const svc of services) {
|
|
@@ -4936,7 +5541,7 @@ function registerConfigCommands(program2) {
|
|
|
4936
5541
|
process.stdout.write("\n");
|
|
4937
5542
|
} else {
|
|
4938
5543
|
if (opts.pretty) {
|
|
4939
|
-
const services = ["jira", "bitbucket", "confluence", "sonar", "sde", "ado", "jenkins", "udeploy", "artifactory", "checkmarx", "servicenow", "contrast", "sonatypeiq", "openshift"];
|
|
5544
|
+
const services = ["jira", "bitbucket", "github", "confluence", "sonar", "sde", "ado", "jenkins", "udeploy", "artifactory", "checkmarx", "servicenow", "contrast", "sonatypeiq", "openshift"];
|
|
4940
5545
|
const labelWidth = 14;
|
|
4941
5546
|
const statusWidth = 9;
|
|
4942
5547
|
for (const svc of services) {
|
|
@@ -4988,6 +5593,40 @@ async function initGlobalConfig(start) {
|
|
|
4988
5593
|
const bitbucketPat = await password({
|
|
4989
5594
|
message: "Bitbucket personal access token:"
|
|
4990
5595
|
});
|
|
5596
|
+
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");
|
|
5597
|
+
const useGitHub = await confirm({
|
|
5598
|
+
message: "Configure GitHub for PR operations?",
|
|
5599
|
+
default: false
|
|
5600
|
+
});
|
|
5601
|
+
let githubBaseUrl = "";
|
|
5602
|
+
let githubToken = "";
|
|
5603
|
+
if (useGitHub) {
|
|
5604
|
+
githubBaseUrl = await input({
|
|
5605
|
+
message: "GitHub API base URL\n GitHub.com: api.github.com\n GitHub Enterprise: <host>/api/v3\n URL: ",
|
|
5606
|
+
default: ""
|
|
5607
|
+
});
|
|
5608
|
+
githubToken = await password({
|
|
5609
|
+
message: "GitHub personal access token (classic or fine-grained):"
|
|
5610
|
+
});
|
|
5611
|
+
if (githubBaseUrl && githubToken) {
|
|
5612
|
+
process.stderr.write("\n Verifying connection...\n");
|
|
5613
|
+
try {
|
|
5614
|
+
const tempConfig = {
|
|
5615
|
+
...loadConfig(),
|
|
5616
|
+
github: {
|
|
5617
|
+
baseUrl: normalizeBaseUrl(githubBaseUrl),
|
|
5618
|
+
token: githubToken
|
|
5619
|
+
}
|
|
5620
|
+
};
|
|
5621
|
+
const tempHttp = createHttpClient(tempConfig);
|
|
5622
|
+
await tempHttp.github("/user");
|
|
5623
|
+
process.stderr.write(" Connected.\n");
|
|
5624
|
+
} catch (err) {
|
|
5625
|
+
warn(`Could not connect to GitHub: ${err instanceof Error ? err.message : String(err)}`);
|
|
5626
|
+
warn("Config will be saved anyway. Check your URL and token and re-run pncli config init or pncli config test.");
|
|
5627
|
+
}
|
|
5628
|
+
}
|
|
5629
|
+
}
|
|
4991
5630
|
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
5631
|
const confluenceBaseUrl = await input({
|
|
4993
5632
|
message: "Confluence base URL (e.g. confluence.your-company.com):",
|
|
@@ -5495,6 +6134,12 @@ async function initGlobalConfig(start) {
|
|
|
5495
6134
|
baseUrl: normalizeBaseUrl(bitbucketBaseUrl) || void 0,
|
|
5496
6135
|
pat: bitbucketPat || void 0
|
|
5497
6136
|
},
|
|
6137
|
+
...useGitHub && githubBaseUrl ? {
|
|
6138
|
+
github: {
|
|
6139
|
+
baseUrl: normalizeBaseUrl(githubBaseUrl),
|
|
6140
|
+
token: githubToken || void 0
|
|
6141
|
+
}
|
|
6142
|
+
} : {},
|
|
5498
6143
|
...confluenceBaseUrl || confluenceApiToken ? {
|
|
5499
6144
|
confluence: {
|
|
5500
6145
|
baseUrl: normalizeBaseUrl(confluenceBaseUrl) || void 0,
|
|
@@ -6233,6 +6878,26 @@ function registerAdoWorkCommands(ado) {
|
|
|
6233
6878
|
fail(err, "ado", "work-download-attachment", start);
|
|
6234
6879
|
}
|
|
6235
6880
|
});
|
|
6881
|
+
work.command("list-areas").description("List classification areas for the team project (tree)").option("--depth <n>", "Depth of the area tree to retrieve", "10").action(async (opts) => {
|
|
6882
|
+
const start = Date.now();
|
|
6883
|
+
try {
|
|
6884
|
+
const { collection, project, workClient } = getAdoContext(ado);
|
|
6885
|
+
const data = await workClient.listAreas(collection, project, parseInt(opts.depth, 10));
|
|
6886
|
+
success(data, "ado", "work-list-areas", start);
|
|
6887
|
+
} catch (err) {
|
|
6888
|
+
fail(err, "ado", "work-list-areas", start);
|
|
6889
|
+
}
|
|
6890
|
+
});
|
|
6891
|
+
work.command("list-iterations").description("List classification iterations for the team project (tree), including start/finish dates").option("--depth <n>", "Depth of the iteration tree to retrieve", "10").action(async (opts) => {
|
|
6892
|
+
const start = Date.now();
|
|
6893
|
+
try {
|
|
6894
|
+
const { collection, project, workClient } = getAdoContext(ado);
|
|
6895
|
+
const data = await workClient.listIterations(collection, project, parseInt(opts.depth, 10));
|
|
6896
|
+
success(data, "ado", "work-list-iterations", start);
|
|
6897
|
+
} catch (err) {
|
|
6898
|
+
fail(err, "ado", "work-list-iterations", start);
|
|
6899
|
+
}
|
|
6900
|
+
});
|
|
6236
6901
|
work.command("types").description("List work item types in the project").option("--discover", "Fetch from server (always true for this command)").option("--save", "Save discovered types to ~/.pncli/config.json").action(async (opts) => {
|
|
6237
6902
|
const start = Date.now();
|
|
6238
6903
|
try {
|
|
@@ -6554,11 +7219,11 @@ function registerAdoRepoCommands(ado) {
|
|
|
6554
7219
|
const start = Date.now();
|
|
6555
7220
|
try {
|
|
6556
7221
|
const globalOpts = ado.optsWithGlobals();
|
|
6557
|
-
const { loadConfig: lc } = await import("./config-
|
|
7222
|
+
const { loadConfig: lc } = await import("./config-S56LADVF.js");
|
|
6558
7223
|
const config = lc({ configPath: globalOpts.config });
|
|
6559
7224
|
const { collection, project, repo: repo2, gitClient } = getAdoContext(ado, true);
|
|
6560
7225
|
const { AdoCoreClient: AdoCoreClient2 } = await import("./core-SWYVXOQ4.js");
|
|
6561
|
-
const { createHttpClient: createHttpClient2 } = await import("./http-
|
|
7226
|
+
const { createHttpClient: createHttpClient2 } = await import("./http-5WRBWEM6.js");
|
|
6562
7227
|
const http = createHttpClient2(config, Boolean(globalOpts.dryRun));
|
|
6563
7228
|
const coreClient = new AdoCoreClient2(http);
|
|
6564
7229
|
const me = await coreClient.getConnectionData(collection);
|
|
@@ -6816,7 +7481,7 @@ var JenkinsClient = class {
|
|
|
6816
7481
|
};
|
|
6817
7482
|
|
|
6818
7483
|
// src/services/jenkins/commands.ts
|
|
6819
|
-
function
|
|
7484
|
+
function getClient7(program2) {
|
|
6820
7485
|
const opts = program2.optsWithGlobals();
|
|
6821
7486
|
const config = loadConfig({ configPath: opts.config });
|
|
6822
7487
|
if (!config.jenkins.baseUrl) throw new PncliError("Jenkins not configured. Run: pncli config init");
|
|
@@ -6850,7 +7515,7 @@ function registerJenkinsCommands(program2) {
|
|
|
6850
7515
|
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
7516
|
const start = Date.now();
|
|
6852
7517
|
try {
|
|
6853
|
-
const client =
|
|
7518
|
+
const client = getClient7(program2);
|
|
6854
7519
|
const data = await client.listJobs(opts.folder);
|
|
6855
7520
|
success(data, "jenkins", "pipeline-list", start);
|
|
6856
7521
|
} catch (err) {
|
|
@@ -6860,7 +7525,7 @@ function registerJenkinsCommands(program2) {
|
|
|
6860
7525
|
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
7526
|
const start = Date.now();
|
|
6862
7527
|
try {
|
|
6863
|
-
const client =
|
|
7528
|
+
const client = getClient7(program2);
|
|
6864
7529
|
const data = await client.getJob(opts.name);
|
|
6865
7530
|
success(data, "jenkins", "pipeline-get", start);
|
|
6866
7531
|
} catch (err) {
|
|
@@ -6873,7 +7538,7 @@ function registerJenkinsCommands(program2) {
|
|
|
6873
7538
|
}, []).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
7539
|
const start = Date.now();
|
|
6875
7540
|
try {
|
|
6876
|
-
const client =
|
|
7541
|
+
const client = getClient7(program2);
|
|
6877
7542
|
for (const p of opts.parameter) {
|
|
6878
7543
|
if (!p.includes("=")) throw new PncliError(`Invalid --parameter "${p}". Expected format: key=value`, 1);
|
|
6879
7544
|
}
|
|
@@ -6906,7 +7571,7 @@ function registerJenkinsCommands(program2) {
|
|
|
6906
7571
|
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
7572
|
const start = Date.now();
|
|
6908
7573
|
try {
|
|
6909
|
-
const client =
|
|
7574
|
+
const client = getClient7(program2);
|
|
6910
7575
|
const top = parseInt(opts.top, 10);
|
|
6911
7576
|
if (isNaN(top) || top <= 0) throw new PncliError(`Invalid --top "${opts.top}". Expected a positive integer.`, 1);
|
|
6912
7577
|
const data = await client.listBuilds(opts.name, top);
|
|
@@ -6918,7 +7583,7 @@ function registerJenkinsCommands(program2) {
|
|
|
6918
7583
|
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
7584
|
const start = Date.now();
|
|
6920
7585
|
try {
|
|
6921
|
-
const client =
|
|
7586
|
+
const client = getClient7(program2);
|
|
6922
7587
|
const buildNumber = parseInt(opts.number, 10);
|
|
6923
7588
|
if (isNaN(buildNumber)) throw new PncliError(`Invalid --number "${opts.number}". Expected an integer.`, 1);
|
|
6924
7589
|
const data = await client.getBuild(opts.name, buildNumber);
|
|
@@ -6930,7 +7595,7 @@ function registerJenkinsCommands(program2) {
|
|
|
6930
7595
|
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
7596
|
const start = Date.now();
|
|
6932
7597
|
try {
|
|
6933
|
-
const client =
|
|
7598
|
+
const client = getClient7(program2);
|
|
6934
7599
|
const buildNumber = parseInt(opts.number, 10);
|
|
6935
7600
|
if (isNaN(buildNumber)) throw new PncliError(`Invalid --number "${opts.number}". Expected an integer.`, 1);
|
|
6936
7601
|
const text = await client.getConsoleLog(opts.name, buildNumber);
|
|
@@ -7022,7 +7687,7 @@ var ArtifactoryClient = class {
|
|
|
7022
7687
|
};
|
|
7023
7688
|
|
|
7024
7689
|
// src/services/artifactory/commands.ts
|
|
7025
|
-
function
|
|
7690
|
+
function getClient8(program2) {
|
|
7026
7691
|
const opts = program2.optsWithGlobals();
|
|
7027
7692
|
const config = loadConfig({ configPath: opts.config });
|
|
7028
7693
|
if (!config.artifactory.baseUrl) throw new PncliError("Artifactory not configured. Run: pncli config init");
|
|
@@ -7035,7 +7700,7 @@ function registerArtifactoryCommands(program2) {
|
|
|
7035
7700
|
art.command("ping").description("Check Artifactory connectivity and authentication").action(async () => {
|
|
7036
7701
|
const start = Date.now();
|
|
7037
7702
|
try {
|
|
7038
|
-
const client =
|
|
7703
|
+
const client = getClient8(program2);
|
|
7039
7704
|
const data = await client.ping();
|
|
7040
7705
|
success(data, "artifactory", "ping", start);
|
|
7041
7706
|
} catch (err) {
|
|
@@ -7045,7 +7710,7 @@ function registerArtifactoryCommands(program2) {
|
|
|
7045
7710
|
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
7711
|
const start = Date.now();
|
|
7047
7712
|
try {
|
|
7048
|
-
const client =
|
|
7713
|
+
const client = getClient8(program2);
|
|
7049
7714
|
const data = await client.listRepos({ type: opts.type, packageType: opts.packageType });
|
|
7050
7715
|
success(data, "artifactory", "repos", start);
|
|
7051
7716
|
} catch (err) {
|
|
@@ -7055,7 +7720,7 @@ function registerArtifactoryCommands(program2) {
|
|
|
7055
7720
|
art.command("artifact-info <repo> <path>").description("Get metadata for an artifact or folder (checksums, size, timestamps)").action(async (repo, path8) => {
|
|
7056
7721
|
const start = Date.now();
|
|
7057
7722
|
try {
|
|
7058
|
-
const client =
|
|
7723
|
+
const client = getClient8(program2);
|
|
7059
7724
|
const data = await client.getStorageInfo(repo, path8);
|
|
7060
7725
|
success(data, "artifactory", "artifact-info", start);
|
|
7061
7726
|
} catch (err) {
|
|
@@ -7065,7 +7730,7 @@ function registerArtifactoryCommands(program2) {
|
|
|
7065
7730
|
art.command("properties-get <repo> <path>").description("Read custom properties on an artifact").action(async (repo, path8) => {
|
|
7066
7731
|
const start = Date.now();
|
|
7067
7732
|
try {
|
|
7068
|
-
const client =
|
|
7733
|
+
const client = getClient8(program2);
|
|
7069
7734
|
const data = await client.getProperties(repo, path8);
|
|
7070
7735
|
success(data, "artifactory", "properties-get", start);
|
|
7071
7736
|
} catch (err) {
|
|
@@ -7082,7 +7747,7 @@ function registerArtifactoryCommands(program2) {
|
|
|
7082
7747
|
if (eq < 1) throw new PncliError(`Invalid property format: "${kv}" \u2014 expected key=value`);
|
|
7083
7748
|
properties[kv.slice(0, eq)] = kv.slice(eq + 1);
|
|
7084
7749
|
}
|
|
7085
|
-
const client =
|
|
7750
|
+
const client = getClient8(program2);
|
|
7086
7751
|
await client.setProperties(repo, path8, properties, { recursive: opts.recursive });
|
|
7087
7752
|
success({ repo, path: path8, properties }, "artifactory", "properties-set", start);
|
|
7088
7753
|
} catch (err) {
|
|
@@ -7095,7 +7760,7 @@ function registerArtifactoryCommands(program2) {
|
|
|
7095
7760
|
if (!opts.repo && !opts.name && !opts.path) {
|
|
7096
7761
|
throw new PncliError("Provide at least one filter: --repo, --name, or --path");
|
|
7097
7762
|
}
|
|
7098
|
-
const client =
|
|
7763
|
+
const client = getClient8(program2);
|
|
7099
7764
|
const data = await client.searchAql({
|
|
7100
7765
|
repo: opts.repo,
|
|
7101
7766
|
name: opts.name,
|
|
@@ -7113,7 +7778,7 @@ function registerArtifactoryCommands(program2) {
|
|
|
7113
7778
|
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
7779
|
const start = Date.now();
|
|
7115
7780
|
try {
|
|
7116
|
-
const client =
|
|
7781
|
+
const client = getClient8(program2);
|
|
7117
7782
|
const timeoutSecs = parseInt(opts.timeout, 10);
|
|
7118
7783
|
if (isNaN(timeoutSecs) || timeoutSecs <= 0) throw new PncliError("--timeout must be a positive integer (seconds)", 1);
|
|
7119
7784
|
const timeoutMs = timeoutSecs * 1e3;
|
|
@@ -7126,7 +7791,7 @@ function registerArtifactoryCommands(program2) {
|
|
|
7126
7791
|
art.command("build-runs <name>").description("List all run numbers for a build").action(async (name) => {
|
|
7127
7792
|
const start = Date.now();
|
|
7128
7793
|
try {
|
|
7129
|
-
const client =
|
|
7794
|
+
const client = getClient8(program2);
|
|
7130
7795
|
const data = await client.listBuildRuns(name);
|
|
7131
7796
|
success(data, "artifactory", "build-runs", start);
|
|
7132
7797
|
} catch (err) {
|
|
@@ -7136,7 +7801,7 @@ function registerArtifactoryCommands(program2) {
|
|
|
7136
7801
|
art.command("build-info <name> <number>").description("Get build info (artifacts, dependencies, VCS revision) for a specific run").action(async (name, number) => {
|
|
7137
7802
|
const start = Date.now();
|
|
7138
7803
|
try {
|
|
7139
|
-
const client =
|
|
7804
|
+
const client = getClient8(program2);
|
|
7140
7805
|
const data = await client.getBuildInfo(name, number);
|
|
7141
7806
|
success(data, "artifactory", "build-info", start);
|
|
7142
7807
|
} catch (err) {
|
|
@@ -7208,7 +7873,7 @@ var UdeployClient = class {
|
|
|
7208
7873
|
// src/services/udeploy/commands.ts
|
|
7209
7874
|
var POLL_INTERVAL_MS2 = 5e3;
|
|
7210
7875
|
var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
7211
|
-
function
|
|
7876
|
+
function getClient9(program2) {
|
|
7212
7877
|
const opts = program2.optsWithGlobals();
|
|
7213
7878
|
const config = loadConfig({ configPath: opts.config });
|
|
7214
7879
|
if (!config.udeploy.baseUrl) {
|
|
@@ -7232,7 +7897,7 @@ function registerUdeployCommands(program2) {
|
|
|
7232
7897
|
udeploy.command("apps").description("List all applications").action(async () => {
|
|
7233
7898
|
const start = Date.now();
|
|
7234
7899
|
try {
|
|
7235
|
-
const { client } =
|
|
7900
|
+
const { client } = getClient9(program2);
|
|
7236
7901
|
const apps = await client.listApplications();
|
|
7237
7902
|
success(apps, "udeploy", "apps", start);
|
|
7238
7903
|
} catch (err) {
|
|
@@ -7242,7 +7907,7 @@ function registerUdeployCommands(program2) {
|
|
|
7242
7907
|
udeploy.command("environments").description("List environments for an application").action(async () => {
|
|
7243
7908
|
const start = Date.now();
|
|
7244
7909
|
try {
|
|
7245
|
-
const { client, config } =
|
|
7910
|
+
const { client, config } = getClient9(program2);
|
|
7246
7911
|
const parentOpts = udeploy.opts();
|
|
7247
7912
|
const application = resolveApplication(config, parentOpts.application);
|
|
7248
7913
|
const envs = await client.listEnvironments(application);
|
|
@@ -7254,7 +7919,7 @@ function registerUdeployCommands(program2) {
|
|
|
7254
7919
|
udeploy.command("components").description("List all components").action(async () => {
|
|
7255
7920
|
const start = Date.now();
|
|
7256
7921
|
try {
|
|
7257
|
-
const { client } =
|
|
7922
|
+
const { client } = getClient9(program2);
|
|
7258
7923
|
const components = await client.listComponents();
|
|
7259
7924
|
success(components, "udeploy", "components", start);
|
|
7260
7925
|
} catch (err) {
|
|
@@ -7264,7 +7929,7 @@ function registerUdeployCommands(program2) {
|
|
|
7264
7929
|
udeploy.command("versions").description("List versions for a component").requiredOption("--component <name>", "Component name or ID").action(async (cmdOpts) => {
|
|
7265
7930
|
const start = Date.now();
|
|
7266
7931
|
try {
|
|
7267
|
-
const { client } =
|
|
7932
|
+
const { client } = getClient9(program2);
|
|
7268
7933
|
const versions = await client.listVersions(cmdOpts.component);
|
|
7269
7934
|
success(versions, "udeploy", "versions", start);
|
|
7270
7935
|
} catch (err) {
|
|
@@ -7274,7 +7939,7 @@ function registerUdeployCommands(program2) {
|
|
|
7274
7939
|
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
7940
|
const start = Date.now();
|
|
7276
7941
|
try {
|
|
7277
|
-
const { client } =
|
|
7942
|
+
const { client } = getClient9(program2);
|
|
7278
7943
|
const created = await client.createVersion(cmdOpts.component, cmdOpts.name);
|
|
7279
7944
|
if (cmdOpts.finish) {
|
|
7280
7945
|
await client.finishImporting(cmdOpts.component, cmdOpts.name);
|
|
@@ -7287,7 +7952,7 @@ function registerUdeployCommands(program2) {
|
|
|
7287
7952
|
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
7953
|
const start = Date.now();
|
|
7289
7954
|
try {
|
|
7290
|
-
const { client, config } =
|
|
7955
|
+
const { client, config } = getClient9(program2);
|
|
7291
7956
|
const parentOpts = udeploy.opts();
|
|
7292
7957
|
const application = resolveApplication(config, parentOpts.application);
|
|
7293
7958
|
const environment = resolveEnvironment(config, parentOpts.environment);
|
|
@@ -7340,7 +8005,7 @@ function registerUdeployCommands(program2) {
|
|
|
7340
8005
|
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
8006
|
const start = Date.now();
|
|
7342
8007
|
try {
|
|
7343
|
-
const { client } =
|
|
8008
|
+
const { client } = getClient9(program2);
|
|
7344
8009
|
const status = await client.getRequestStatus(cmdOpts.requestId);
|
|
7345
8010
|
success(status, "udeploy", "request-status", start);
|
|
7346
8011
|
} catch (err) {
|
|
@@ -7350,7 +8015,7 @@ function registerUdeployCommands(program2) {
|
|
|
7350
8015
|
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
8016
|
const start = Date.now();
|
|
7352
8017
|
try {
|
|
7353
|
-
const { client } =
|
|
8018
|
+
const { client } = getClient9(program2);
|
|
7354
8019
|
const info = await client.getRequestInfo(cmdOpts.requestId);
|
|
7355
8020
|
success(info, "udeploy", "request-info", start);
|
|
7356
8021
|
} catch (err) {
|
|
@@ -7394,7 +8059,7 @@ var CheckmarxClient = class {
|
|
|
7394
8059
|
};
|
|
7395
8060
|
|
|
7396
8061
|
// src/services/checkmarx/commands.ts
|
|
7397
|
-
function
|
|
8062
|
+
function getClient10(program2) {
|
|
7398
8063
|
const opts = program2.optsWithGlobals();
|
|
7399
8064
|
const config = loadConfig({ configPath: opts.config });
|
|
7400
8065
|
if (!config.checkmarx.baseUrl) throw new PncliError("Checkmarx not configured. Run: pncli config init");
|
|
@@ -7408,7 +8073,7 @@ function registerCheckmarxCommands(program2) {
|
|
|
7408
8073
|
project.command("list").description("List all projects").action(async () => {
|
|
7409
8074
|
const start = Date.now();
|
|
7410
8075
|
try {
|
|
7411
|
-
const data = await
|
|
8076
|
+
const data = await getClient10(program2).listProjects();
|
|
7412
8077
|
success(data, "checkmarx", "project-list", start);
|
|
7413
8078
|
} catch (err) {
|
|
7414
8079
|
fail(err, "checkmarx", "project-list", start);
|
|
@@ -7419,7 +8084,7 @@ function registerCheckmarxCommands(program2) {
|
|
|
7419
8084
|
try {
|
|
7420
8085
|
const id = parseInt(opts.id, 10);
|
|
7421
8086
|
if (isNaN(id)) throw new PncliError(`Invalid --id "${opts.id}". Expected an integer.`, 1);
|
|
7422
|
-
const data = await
|
|
8087
|
+
const data = await getClient10(program2).getProject(id);
|
|
7423
8088
|
success(data, "checkmarx", "project-get", start);
|
|
7424
8089
|
} catch (err) {
|
|
7425
8090
|
fail(err, "checkmarx", "project-get", start);
|
|
@@ -7432,7 +8097,7 @@ function registerCheckmarxCommands(program2) {
|
|
|
7432
8097
|
const last = opts.last ? parseInt(opts.last, 10) : void 0;
|
|
7433
8098
|
if (opts.project && isNaN(projectId)) throw new PncliError(`Invalid --project "${opts.project}". Expected an integer.`, 1);
|
|
7434
8099
|
if (opts.last && isNaN(last)) throw new PncliError(`Invalid --last "${opts.last}". Expected an integer.`, 1);
|
|
7435
|
-
const data = await
|
|
8100
|
+
const data = await getClient10(program2).listScans({ projectId, last });
|
|
7436
8101
|
success(data, "checkmarx", "scan-list", start);
|
|
7437
8102
|
} catch (err) {
|
|
7438
8103
|
fail(err, "checkmarx", "scan-list", start);
|
|
@@ -7443,7 +8108,7 @@ function registerCheckmarxCommands(program2) {
|
|
|
7443
8108
|
try {
|
|
7444
8109
|
const id = parseInt(opts.id, 10);
|
|
7445
8110
|
if (isNaN(id)) throw new PncliError(`Invalid --id "${opts.id}". Expected an integer.`, 1);
|
|
7446
|
-
const data = await
|
|
8111
|
+
const data = await getClient10(program2).getScan(id);
|
|
7447
8112
|
success(data, "checkmarx", "scan-get", start);
|
|
7448
8113
|
} catch (err) {
|
|
7449
8114
|
fail(err, "checkmarx", "scan-get", start);
|
|
@@ -7454,7 +8119,7 @@ function registerCheckmarxCommands(program2) {
|
|
|
7454
8119
|
try {
|
|
7455
8120
|
const id = parseInt(opts.id, 10);
|
|
7456
8121
|
if (isNaN(id)) throw new PncliError(`Invalid --id "${opts.id}". Expected an integer.`, 1);
|
|
7457
|
-
const data = await
|
|
8122
|
+
const data = await getClient10(program2).getScanResultsStatistics(id);
|
|
7458
8123
|
success(data, "checkmarx", "scan-stats", start);
|
|
7459
8124
|
} catch (err) {
|
|
7460
8125
|
fail(err, "checkmarx", "scan-stats", start);
|
|
@@ -7704,6 +8369,8 @@ import os from "os";
|
|
|
7704
8369
|
import { fileURLToPath } from "url";
|
|
7705
8370
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
7706
8371
|
import select from "@inquirer/select";
|
|
8372
|
+
var BACK = "__back__";
|
|
8373
|
+
var ALL_MARKETPLACES = "__all_marketplaces__";
|
|
7707
8374
|
function injectTokenIntoUrl(url, token) {
|
|
7708
8375
|
let parsed;
|
|
7709
8376
|
try {
|
|
@@ -7727,10 +8394,31 @@ function repoNameFromUrl(url) {
|
|
|
7727
8394
|
function defaultMarketplacePath(url) {
|
|
7728
8395
|
return path7.join(os.homedir(), ".agents", "marketplaces", repoNameFromUrl(url));
|
|
7729
8396
|
}
|
|
8397
|
+
function marketplaceLabel(m) {
|
|
8398
|
+
return m.name ?? repoNameFromUrl(m.repoUrl ?? "");
|
|
8399
|
+
}
|
|
8400
|
+
function scrubToken(msg) {
|
|
8401
|
+
return msg.replace(/x-(?:token-auth|access-token):[^@]+@/g, "x-token-auth:***@");
|
|
8402
|
+
}
|
|
8403
|
+
function assertInteractive(hint) {
|
|
8404
|
+
if (!process.stdin.isTTY) {
|
|
8405
|
+
throw new Error(`This selection requires an interactive terminal. ${hint}`);
|
|
8406
|
+
}
|
|
8407
|
+
}
|
|
7730
8408
|
var AGENT_PATHS = {
|
|
7731
8409
|
"github-copilot": { project: ".agents/skills", user: path7.join(os.homedir(), ".copilot/skills") },
|
|
7732
8410
|
"claude-code": { project: ".claude/skills", user: path7.join(os.homedir(), ".claude/skills") }
|
|
7733
8411
|
};
|
|
8412
|
+
function resolveAgentName(opts) {
|
|
8413
|
+
return opts.claude ? "claude-code" : opts.agent ?? "github-copilot";
|
|
8414
|
+
}
|
|
8415
|
+
function resolveAgentPaths(agentName) {
|
|
8416
|
+
const agentConfig = AGENT_PATHS[agentName];
|
|
8417
|
+
if (!agentConfig) {
|
|
8418
|
+
throw new Error(`Unknown agent: "${agentName}". Use: ${Object.keys(AGENT_PATHS).join(" | ")}`);
|
|
8419
|
+
}
|
|
8420
|
+
return agentConfig;
|
|
8421
|
+
}
|
|
7734
8422
|
function getBundledSkillsDir() {
|
|
7735
8423
|
try {
|
|
7736
8424
|
const thisFile = fileURLToPath(import.meta.url);
|
|
@@ -7739,6 +8427,233 @@ function getBundledSkillsDir() {
|
|
|
7739
8427
|
return "";
|
|
7740
8428
|
}
|
|
7741
8429
|
}
|
|
8430
|
+
function getInstalledMetaPath(targetDir) {
|
|
8431
|
+
return path7.join(targetDir, ".pncli-installed.json");
|
|
8432
|
+
}
|
|
8433
|
+
function readInstalledMeta(targetDir) {
|
|
8434
|
+
const metaPath = getInstalledMetaPath(targetDir);
|
|
8435
|
+
const raw = loadJsonFile(metaPath);
|
|
8436
|
+
if (raw && raw.version === 1 && raw.skills) return raw;
|
|
8437
|
+
return { version: 1, skills: {} };
|
|
8438
|
+
}
|
|
8439
|
+
function recordInstalledSkills(targetDir, skillNames, record) {
|
|
8440
|
+
if (skillNames.length === 0) return;
|
|
8441
|
+
const meta = readInstalledMeta(targetDir);
|
|
8442
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8443
|
+
for (const skillName of skillNames) {
|
|
8444
|
+
meta.skills[skillName] = { ...record, installedAt: now };
|
|
8445
|
+
}
|
|
8446
|
+
fs4.writeFileSync(getInstalledMetaPath(targetDir), JSON.stringify(meta, null, 2), "utf8");
|
|
8447
|
+
}
|
|
8448
|
+
function getAllMarketplaces(globalConfig) {
|
|
8449
|
+
const result = [];
|
|
8450
|
+
if (Array.isArray(globalConfig.marketplaces)) {
|
|
8451
|
+
result.push(...globalConfig.marketplaces);
|
|
8452
|
+
}
|
|
8453
|
+
if (globalConfig.marketplace?.repoUrl) {
|
|
8454
|
+
const legacyUrl = globalConfig.marketplace.repoUrl;
|
|
8455
|
+
const alreadyPresent = result.some((m) => m.repoUrl === legacyUrl);
|
|
8456
|
+
if (!alreadyPresent) {
|
|
8457
|
+
const existingNames = new Set(result.map((m) => m.name).filter((n) => !!n));
|
|
8458
|
+
let legacyName = globalConfig.marketplace.name ?? repoNameFromUrl(legacyUrl);
|
|
8459
|
+
if (existingNames.has(legacyName)) {
|
|
8460
|
+
let suffix = 2;
|
|
8461
|
+
while (existingNames.has(`${legacyName}-${suffix}`)) suffix++;
|
|
8462
|
+
legacyName = `${legacyName}-${suffix}`;
|
|
8463
|
+
}
|
|
8464
|
+
result.push({
|
|
8465
|
+
name: legacyName,
|
|
8466
|
+
repoUrl: globalConfig.marketplace.repoUrl,
|
|
8467
|
+
localPath: globalConfig.marketplace.localPath,
|
|
8468
|
+
token: globalConfig.marketplace.token
|
|
8469
|
+
});
|
|
8470
|
+
}
|
|
8471
|
+
}
|
|
8472
|
+
return result;
|
|
8473
|
+
}
|
|
8474
|
+
function saveMarketplaces(configPath, existing, marketplaces) {
|
|
8475
|
+
const updated = { ...existing, marketplaces };
|
|
8476
|
+
delete updated.marketplace;
|
|
8477
|
+
writeGlobalConfig(updated, configPath);
|
|
8478
|
+
}
|
|
8479
|
+
function loadMarketplacesConfig(configPath) {
|
|
8480
|
+
const existing = loadJsonFile(configPath) ?? {};
|
|
8481
|
+
const all = getAllMarketplaces(existing);
|
|
8482
|
+
if (existing.marketplace?.repoUrl) {
|
|
8483
|
+
saveMarketplaces(configPath, existing, all);
|
|
8484
|
+
warn("Migrated legacy single-marketplace config to the multi-marketplace format.");
|
|
8485
|
+
delete existing.marketplace;
|
|
8486
|
+
}
|
|
8487
|
+
return { existing, all };
|
|
8488
|
+
}
|
|
8489
|
+
function loadMarketplaces(configPath) {
|
|
8490
|
+
return loadMarketplacesConfig(configPath).all;
|
|
8491
|
+
}
|
|
8492
|
+
function upsertMarketplace(all, entry) {
|
|
8493
|
+
const idxByUrl = all.findIndex((m) => m.repoUrl === entry.repoUrl);
|
|
8494
|
+
const idxByName = all.findIndex((m) => m.name === entry.name);
|
|
8495
|
+
if (idxByUrl !== -1) {
|
|
8496
|
+
if (idxByName !== -1 && idxByName !== idxByUrl) {
|
|
8497
|
+
throw new Error(`Marketplace name "${entry.name}" is already used by a different marketplace (${all[idxByName].repoUrl}). Choose a different --name.`);
|
|
8498
|
+
}
|
|
8499
|
+
all[idxByUrl] = { ...entry, token: entry.token ?? all[idxByUrl].token };
|
|
8500
|
+
} else if (idxByName !== -1) {
|
|
8501
|
+
throw new Error(`Marketplace name "${entry.name}" is already registered for a different repo (${all[idxByName].repoUrl}). Choose a different --name, or remove the existing marketplace first.`);
|
|
8502
|
+
} else {
|
|
8503
|
+
all.push(entry);
|
|
8504
|
+
}
|
|
8505
|
+
}
|
|
8506
|
+
function cloneOrReuseMarketplace(url, resolvedPath, opts) {
|
|
8507
|
+
const hasGit = fs4.existsSync(path7.join(resolvedPath, ".git"));
|
|
8508
|
+
if (fs4.existsSync(resolvedPath) && !hasGit && fs4.readdirSync(resolvedPath).length > 0) {
|
|
8509
|
+
throw new Error(`Directory already exists and is not a git repo: ${resolvedPath}`);
|
|
8510
|
+
}
|
|
8511
|
+
if (hasGit) {
|
|
8512
|
+
warn(`Directory already contains a git repo at ${resolvedPath} \u2014 skipping clone, updating config and re-installing plugins.`);
|
|
8513
|
+
return;
|
|
8514
|
+
}
|
|
8515
|
+
const branchLabel = opts.branch ?? "remote default";
|
|
8516
|
+
warn(`Cloning ${url} (branch: ${branchLabel}) \u2192 ${resolvedPath}...`);
|
|
8517
|
+
const cloneUrl = opts.token ? injectTokenIntoUrl(url, opts.token) : url;
|
|
8518
|
+
const cloneArgs = ["clone"];
|
|
8519
|
+
if (opts.branch) cloneArgs.push("--branch", opts.branch);
|
|
8520
|
+
cloneArgs.push(cloneUrl, resolvedPath);
|
|
8521
|
+
try {
|
|
8522
|
+
execFileSync3("git", cloneArgs, { stdio: ["inherit", "inherit", "pipe"] });
|
|
8523
|
+
} catch (e) {
|
|
8524
|
+
let cloneActuallySucceeded = false;
|
|
8525
|
+
try {
|
|
8526
|
+
execFileSync3("git", ["-C", resolvedPath, "rev-parse", "HEAD"], { stdio: "pipe" });
|
|
8527
|
+
cloneActuallySucceeded = true;
|
|
8528
|
+
} catch {
|
|
8529
|
+
}
|
|
8530
|
+
if (!cloneActuallySucceeded) {
|
|
8531
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
8532
|
+
throw new Error(scrubToken(msg));
|
|
8533
|
+
}
|
|
8534
|
+
}
|
|
8535
|
+
}
|
|
8536
|
+
function pullMarketplace(marketplacePath, repoUrl, token, marketplaceName) {
|
|
8537
|
+
warn(`Pulling latest content for "${marketplaceName}"...`);
|
|
8538
|
+
const gitArgs = ["-C", marketplacePath];
|
|
8539
|
+
if (repoUrl && token) {
|
|
8540
|
+
gitArgs.push("-c", `remote.origin.url=${injectTokenIntoUrl(repoUrl, token)}`);
|
|
8541
|
+
}
|
|
8542
|
+
gitArgs.push("pull");
|
|
8543
|
+
let pullOutput;
|
|
8544
|
+
try {
|
|
8545
|
+
pullOutput = execFileSync3("git", gitArgs, { encoding: "utf8", stdio: ["inherit", "pipe", "pipe"], env: { ...process.env, LANG: "C", LC_ALL: "C" } });
|
|
8546
|
+
} catch (e) {
|
|
8547
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
8548
|
+
throw new Error(scrubToken(msg));
|
|
8549
|
+
}
|
|
8550
|
+
const updated = !pullOutput.includes("Already up to date");
|
|
8551
|
+
if (pullOutput.trim() && updated) warn(pullOutput.trim());
|
|
8552
|
+
warn(updated ? `"${marketplaceName}" updated.` : `"${marketplaceName}" already up to date \u2014 no changes to sync.`);
|
|
8553
|
+
return { updated };
|
|
8554
|
+
}
|
|
8555
|
+
function installPluginsForMarketplace(marketplacePath, marketplaceName, repoUrl, pluginNames, targetDir) {
|
|
8556
|
+
const results = {};
|
|
8557
|
+
let totalInstalled = 0;
|
|
8558
|
+
for (const pluginName of pluginNames) {
|
|
8559
|
+
const skillsSrc = resolveSkillsSrc(marketplacePath, pluginName);
|
|
8560
|
+
if (!fs4.existsSync(skillsSrc)) {
|
|
8561
|
+
results[pluginName] = { installed: [], failed: [] };
|
|
8562
|
+
warn(`No skills directory found for plugin "${pluginName}" in "${marketplaceName}" \u2014 skipping.`);
|
|
8563
|
+
continue;
|
|
8564
|
+
}
|
|
8565
|
+
const { installed, failed } = copyPluginSkills(skillsSrc, targetDir, {
|
|
8566
|
+
marketplace: marketplaceName,
|
|
8567
|
+
plugin: pluginName,
|
|
8568
|
+
installedFrom: repoUrl ?? ""
|
|
8569
|
+
});
|
|
8570
|
+
results[pluginName] = { installed, failed };
|
|
8571
|
+
totalInstalled += installed.length;
|
|
8572
|
+
for (const skill of installed) {
|
|
8573
|
+
warn(` ${skill}: ${path7.join(skillsSrc, skill)} \u2192 ${path7.join(targetDir, skill)}`);
|
|
8574
|
+
}
|
|
8575
|
+
if (failed.length > 0) {
|
|
8576
|
+
warn(`Skipped ${failed.length} skill(s) with invalid names in "${pluginName}": ${failed.join(", ")}`);
|
|
8577
|
+
}
|
|
8578
|
+
}
|
|
8579
|
+
return { results, totalInstalled };
|
|
8580
|
+
}
|
|
8581
|
+
function installAllPlugins(resolvedPath, marketplaceName, url, targetDir) {
|
|
8582
|
+
const pluginChoices = resolvePluginChoices(resolvedPath);
|
|
8583
|
+
if (pluginChoices.length === 0) {
|
|
8584
|
+
warn("No plugins found in marketplace. Check the marketplace repository structure.");
|
|
8585
|
+
return { pluginResults: {}, totalInstalled: 0 };
|
|
8586
|
+
}
|
|
8587
|
+
warn(`Installing ${pluginChoices.length} plugin(s) to ${targetDir}...`);
|
|
8588
|
+
const { results, totalInstalled } = installPluginsForMarketplace(resolvedPath, marketplaceName, url, pluginChoices.map((p) => p.name), targetDir);
|
|
8589
|
+
return { pluginResults: results, totalInstalled };
|
|
8590
|
+
}
|
|
8591
|
+
function syncMarketplacePlugins(m, targetDir, force, pluginFilter) {
|
|
8592
|
+
const marketplaceName = marketplaceLabel(m);
|
|
8593
|
+
try {
|
|
8594
|
+
const marketplacePath = m.localPath;
|
|
8595
|
+
if (!marketplacePath || !fs4.existsSync(marketplacePath)) {
|
|
8596
|
+
warn(`Marketplace "${marketplaceName}" local path not found \u2014 skipping.`);
|
|
8597
|
+
return { marketplace: marketplaceName, skipped: true, message: "Local path not found." };
|
|
8598
|
+
}
|
|
8599
|
+
const { updated } = pullMarketplace(marketplacePath, m.repoUrl, m.token, marketplaceName);
|
|
8600
|
+
if (!updated && !force) {
|
|
8601
|
+
return { marketplace: marketplaceName, marketplaceUpdated: false, skipped: true, message: "No changes detected \u2014 skipping install. Use --force to reinstall anyway." };
|
|
8602
|
+
}
|
|
8603
|
+
const pluginChoices = resolvePluginChoices(marketplacePath);
|
|
8604
|
+
if (pluginChoices.length === 0) {
|
|
8605
|
+
warn(`No plugins found in marketplace "${marketplaceName}" \u2014 skipping.`);
|
|
8606
|
+
return { marketplace: marketplaceName, skipped: true, message: "No plugins found." };
|
|
8607
|
+
}
|
|
8608
|
+
let pluginNames;
|
|
8609
|
+
if (pluginFilter === "all") {
|
|
8610
|
+
pluginNames = pluginChoices.map((p) => p.name);
|
|
8611
|
+
} else if (pluginChoices.some((p) => p.name === pluginFilter)) {
|
|
8612
|
+
pluginNames = [pluginFilter];
|
|
8613
|
+
} else {
|
|
8614
|
+
warn(`Plugin "${pluginFilter}" not found in "${marketplaceName}" \u2014 skipping.`);
|
|
8615
|
+
return { marketplace: marketplaceName, skipped: true, message: `Plugin "${pluginFilter}" not found.` };
|
|
8616
|
+
}
|
|
8617
|
+
const { results, totalInstalled } = installPluginsForMarketplace(marketplacePath, marketplaceName, m.repoUrl, pluginNames, targetDir);
|
|
8618
|
+
return { marketplace: marketplaceName, plugins: results, total: totalInstalled, marketplaceUpdated: updated };
|
|
8619
|
+
} catch (err) {
|
|
8620
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
8621
|
+
warn(`Marketplace "${marketplaceName}" failed \u2014 skipping. ${message}`);
|
|
8622
|
+
return { marketplace: marketplaceName, skipped: true, error: message };
|
|
8623
|
+
}
|
|
8624
|
+
}
|
|
8625
|
+
async function marketplaceAddAction(url, localPath, opts, commandName) {
|
|
8626
|
+
const start = Date.now();
|
|
8627
|
+
try {
|
|
8628
|
+
const resolvedPath = path7.resolve(localPath ?? defaultMarketplacePath(url));
|
|
8629
|
+
const marketplaceName = opts.name ?? repoNameFromUrl(url);
|
|
8630
|
+
cloneOrReuseMarketplace(url, resolvedPath, opts);
|
|
8631
|
+
const configPath = getGlobalConfigPath();
|
|
8632
|
+
const { existing, all } = loadMarketplacesConfig(configPath);
|
|
8633
|
+
const entry = {
|
|
8634
|
+
name: marketplaceName,
|
|
8635
|
+
repoUrl: url,
|
|
8636
|
+
localPath: resolvedPath,
|
|
8637
|
+
...opts.token ? { token: opts.token } : {}
|
|
8638
|
+
};
|
|
8639
|
+
upsertMarketplace(all, entry);
|
|
8640
|
+
saveMarketplaces(configPath, existing, all);
|
|
8641
|
+
const targetDir = resolveAgentPaths(resolveAgentName(opts)).user;
|
|
8642
|
+
const { pluginResults, totalInstalled } = installAllPlugins(resolvedPath, marketplaceName, url, targetDir);
|
|
8643
|
+
success({
|
|
8644
|
+
name: marketplaceName,
|
|
8645
|
+
repoUrl: url,
|
|
8646
|
+
localPath: resolvedPath,
|
|
8647
|
+
branch: opts.branch ?? null,
|
|
8648
|
+
tokenConfigured: !!opts.token,
|
|
8649
|
+
plugins: pluginResults,
|
|
8650
|
+
total: totalInstalled,
|
|
8651
|
+
target: targetDir
|
|
8652
|
+
}, "skills", commandName, start);
|
|
8653
|
+
} catch (err) {
|
|
8654
|
+
fail(err, "skills", commandName, start);
|
|
8655
|
+
}
|
|
8656
|
+
}
|
|
7742
8657
|
function registerSkillsCommands(program2) {
|
|
7743
8658
|
const skills = program2.command("skills").description("Manage pncli Claude Code skills");
|
|
7744
8659
|
skills.command("install").description("Install pncli skills into the current repo").option("--agent <agent>", "Target agent host: github-copilot | claude-code", "github-copilot").option("--scope <scope>", "Installation scope: project | user", "project").option("--target <dir>", "Override install directory (ignores --agent and --scope)").action((opts) => {
|
|
@@ -7748,10 +8663,7 @@ function registerSkillsCommands(program2) {
|
|
|
7748
8663
|
if (opts.target) {
|
|
7749
8664
|
targetDir = path7.resolve(opts.target);
|
|
7750
8665
|
} else {
|
|
7751
|
-
const agentConfig =
|
|
7752
|
-
if (!agentConfig) {
|
|
7753
|
-
throw new Error(`Unknown agent: "${opts.agent}". Use: ${Object.keys(AGENT_PATHS).join(" | ")}`);
|
|
7754
|
-
}
|
|
8666
|
+
const agentConfig = resolveAgentPaths(opts.agent);
|
|
7755
8667
|
const scopePath = opts.scope === "user" ? agentConfig.user : agentConfig.project;
|
|
7756
8668
|
targetDir = path7.resolve(scopePath);
|
|
7757
8669
|
}
|
|
@@ -7801,6 +8713,7 @@ function registerSkillsCommands(program2) {
|
|
|
7801
8713
|
if (failed.length > 0) {
|
|
7802
8714
|
warn(`Failed to install: ${failed.join(", ")}`);
|
|
7803
8715
|
}
|
|
8716
|
+
recordInstalledSkills(targetDir, installed, { source: "bundled" });
|
|
7804
8717
|
success({
|
|
7805
8718
|
installed,
|
|
7806
8719
|
failed,
|
|
@@ -7821,10 +8734,7 @@ function registerSkillsCommands(program2) {
|
|
|
7821
8734
|
if (opts.target) {
|
|
7822
8735
|
targetDir = path7.resolve(opts.target);
|
|
7823
8736
|
} else {
|
|
7824
|
-
const agentConfig =
|
|
7825
|
-
if (!agentConfig) {
|
|
7826
|
-
throw new Error(`Unknown agent: "${opts.agent}". Use: ${Object.keys(AGENT_PATHS).join(" | ")}`);
|
|
7827
|
-
}
|
|
8737
|
+
const agentConfig = resolveAgentPaths(opts.agent);
|
|
7828
8738
|
const scopePath = opts.scope === "user" ? agentConfig.user : agentConfig.project;
|
|
7829
8739
|
targetDir = path7.resolve(scopePath);
|
|
7830
8740
|
}
|
|
@@ -7832,7 +8742,9 @@ function registerSkillsCommands(program2) {
|
|
|
7832
8742
|
success({ skills: [], message: `No skills directory found at ${targetDir}. Run: pncli skills install` }, "skills", "list", start);
|
|
7833
8743
|
return;
|
|
7834
8744
|
}
|
|
8745
|
+
const meta = readInstalledMeta(targetDir);
|
|
7835
8746
|
const skillDirs = fs4.readdirSync(targetDir).filter((name) => {
|
|
8747
|
+
if (name.startsWith(".")) return false;
|
|
7836
8748
|
const skillPath = path7.join(targetDir, name, "SKILL.md");
|
|
7837
8749
|
return fs4.existsSync(skillPath);
|
|
7838
8750
|
});
|
|
@@ -7867,7 +8779,8 @@ function registerSkillsCommands(program2) {
|
|
|
7867
8779
|
category: metadata.category || data.category || "other",
|
|
7868
8780
|
services: metadata.services || data.services || "",
|
|
7869
8781
|
providers: metadata.providers || data.providers || "none",
|
|
7870
|
-
userInvocable: data["user-invocable"] === "true"
|
|
8782
|
+
userInvocable: data["user-invocable"] === "true",
|
|
8783
|
+
installed: meta.skills[name] ?? null
|
|
7871
8784
|
};
|
|
7872
8785
|
});
|
|
7873
8786
|
success({ skills: skillsList, total: skillsList.length }, "skills", "list", start);
|
|
@@ -7875,155 +8788,217 @@ function registerSkillsCommands(program2) {
|
|
|
7875
8788
|
fail(err, "skills", "list", start);
|
|
7876
8789
|
}
|
|
7877
8790
|
});
|
|
7878
|
-
|
|
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) => {
|
|
8791
|
+
skills.command("uninstall").description("Uninstall a skill (defaults to user scope, matching the marketplace install target; pass --scope project for skills installed via `skills install`)").argument("<name>", "Skill name to uninstall (the directory name under your skills folder)").option("--agent <agent>", "Target agent host: github-copilot | claude-code", "github-copilot").option("--scope <scope>", "Installation scope to uninstall from: project | user", "user").option("--claude", "Shorthand for --agent claude-code").option("--target <dir>", "Override skills directory").action((name, opts) => {
|
|
7880
8792
|
const start = Date.now();
|
|
7881
8793
|
try {
|
|
7882
|
-
|
|
7883
|
-
|
|
7884
|
-
|
|
7885
|
-
throw new Error(`Directory already exists and is not a git repo: ${resolvedPath}`);
|
|
7886
|
-
}
|
|
7887
|
-
if (hasGit) {
|
|
7888
|
-
warn(`Directory already contains a git repo at ${resolvedPath} \u2014 skipping clone, updating config only.`);
|
|
8794
|
+
let targetDir;
|
|
8795
|
+
if (opts.target) {
|
|
8796
|
+
targetDir = path7.resolve(opts.target);
|
|
7889
8797
|
} else {
|
|
7890
|
-
const
|
|
7891
|
-
|
|
7892
|
-
|
|
7893
|
-
const cloneArgs = ["clone"];
|
|
7894
|
-
if (opts.branch) cloneArgs.push("--branch", opts.branch);
|
|
7895
|
-
cloneArgs.push(cloneUrl, resolvedPath);
|
|
7896
|
-
try {
|
|
7897
|
-
execFileSync3("git", cloneArgs, { stdio: ["inherit", "inherit", "pipe"] });
|
|
7898
|
-
} catch (e) {
|
|
7899
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
7900
|
-
throw new Error(msg.replace(/x-(?:token-auth|access-token):[^@]+@/g, "x-token-auth:***@"));
|
|
7901
|
-
}
|
|
8798
|
+
const agentConfig = resolveAgentPaths(resolveAgentName(opts));
|
|
8799
|
+
const scopePath = opts.scope === "project" ? agentConfig.project : agentConfig.user;
|
|
8800
|
+
targetDir = path7.resolve(scopePath);
|
|
7902
8801
|
}
|
|
8802
|
+
const resolvedTarget = path7.resolve(targetDir);
|
|
8803
|
+
const skillDir = path7.resolve(targetDir, name);
|
|
8804
|
+
if (!skillDir.startsWith(resolvedTarget + path7.sep)) {
|
|
8805
|
+
throw new Error(`Invalid skill name: "${name}"`);
|
|
8806
|
+
}
|
|
8807
|
+
if (!fs4.existsSync(skillDir)) {
|
|
8808
|
+
throw new Error(`Skill "${name}" not found at ${skillDir}`);
|
|
8809
|
+
}
|
|
8810
|
+
const meta = readInstalledMeta(targetDir);
|
|
8811
|
+
const record = meta.skills[name];
|
|
8812
|
+
fs4.rmSync(skillDir, { recursive: true, force: true });
|
|
8813
|
+
delete meta.skills[name];
|
|
8814
|
+
fs4.writeFileSync(getInstalledMetaPath(targetDir), JSON.stringify(meta, null, 2), "utf8");
|
|
8815
|
+
success({
|
|
8816
|
+
uninstalled: name,
|
|
8817
|
+
target: targetDir,
|
|
8818
|
+
wasTracked: !!record,
|
|
8819
|
+
...record ? { installedFrom: record } : {}
|
|
8820
|
+
}, "skills", "uninstall", start);
|
|
8821
|
+
} catch (err) {
|
|
8822
|
+
fail(err, "skills", "uninstall", start);
|
|
8823
|
+
}
|
|
8824
|
+
});
|
|
8825
|
+
const marketplace = skills.command("marketplace").description("Manage git-hosted skills marketplaces");
|
|
8826
|
+
function withMarketplaceAddOptions(cmd) {
|
|
8827
|
+
return cmd.argument("<url>", "Git clone URL of the marketplace repository").argument("[localPath]", "Local directory to clone into (default: ~/.agents/marketplaces/<repo-name>)").option("--name <name>", "Human-readable name for this marketplace (default: derived from URL)").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");
|
|
8828
|
+
}
|
|
8829
|
+
withMarketplaceAddOptions(
|
|
8830
|
+
marketplace.command("add").description("Register a new marketplace, clone it, and install all its plugins")
|
|
8831
|
+
).action((url, localPath, opts) => marketplaceAddAction(url, localPath, opts, "marketplace-add"));
|
|
8832
|
+
withMarketplaceAddOptions(
|
|
8833
|
+
marketplace.command("setup").description("Alias for `marketplace add` \u2014 clone a marketplace and install all its plugins")
|
|
8834
|
+
).action((url, localPath, opts) => marketplaceAddAction(url, localPath, opts, "marketplace-setup"));
|
|
8835
|
+
marketplace.command("list").description("List all registered marketplaces").action(() => {
|
|
8836
|
+
const start = Date.now();
|
|
8837
|
+
try {
|
|
7903
8838
|
const configPath = getGlobalConfigPath();
|
|
7904
|
-
const
|
|
7905
|
-
|
|
7906
|
-
|
|
7907
|
-
|
|
7908
|
-
|
|
8839
|
+
const all = loadMarketplaces(configPath);
|
|
8840
|
+
success({
|
|
8841
|
+
marketplaces: all.map((m) => ({
|
|
8842
|
+
name: marketplaceLabel(m),
|
|
8843
|
+
repoUrl: m.repoUrl,
|
|
8844
|
+
localPath: m.localPath,
|
|
8845
|
+
tokenConfigured: !!m.token
|
|
8846
|
+
})),
|
|
8847
|
+
total: all.length
|
|
8848
|
+
}, "skills", "marketplace-list", start);
|
|
7909
8849
|
} catch (err) {
|
|
7910
|
-
fail(err, "skills", "marketplace-
|
|
8850
|
+
fail(err, "skills", "marketplace-list", start);
|
|
7911
8851
|
}
|
|
7912
8852
|
});
|
|
7913
|
-
marketplace.command("
|
|
8853
|
+
marketplace.command("plugins").description("List the plugins available in a registered marketplace").argument("<name>", "Marketplace name (or repo URL) to inspect").action((name) => {
|
|
7914
8854
|
const start = Date.now();
|
|
7915
8855
|
try {
|
|
7916
8856
|
const configPath = getGlobalConfigPath();
|
|
7917
|
-
const
|
|
7918
|
-
const
|
|
7919
|
-
if (!
|
|
7920
|
-
throw new Error(
|
|
7921
|
-
}
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
const
|
|
7926
|
-
|
|
7927
|
-
|
|
7928
|
-
|
|
7929
|
-
|
|
7930
|
-
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
|
|
7938
|
-
|
|
7939
|
-
|
|
7940
|
-
|
|
7941
|
-
|
|
7942
|
-
|
|
7943
|
-
|
|
7944
|
-
|
|
7945
|
-
|
|
7946
|
-
|
|
7947
|
-
|
|
7948
|
-
|
|
7949
|
-
|
|
7950
|
-
|
|
7951
|
-
|
|
7952
|
-
|
|
7953
|
-
|
|
7954
|
-
|
|
7955
|
-
|
|
8857
|
+
const all = loadMarketplaces(configPath);
|
|
8858
|
+
const found = all.find((m) => m.name === name || m.repoUrl === name);
|
|
8859
|
+
if (!found) {
|
|
8860
|
+
throw new Error(`Marketplace "${name}" not found. Run: pncli skills marketplace list`);
|
|
8861
|
+
}
|
|
8862
|
+
if (!found.localPath || !fs4.existsSync(found.localPath)) {
|
|
8863
|
+
throw new Error(`Marketplace "${marketplaceLabel(found)}" local path not found at ${found.localPath ?? "(not set)"}. Run: pncli skills marketplace add <url>`);
|
|
8864
|
+
}
|
|
8865
|
+
const plugins = resolvePluginChoices(found.localPath);
|
|
8866
|
+
success({
|
|
8867
|
+
marketplace: marketplaceLabel(found),
|
|
8868
|
+
plugins,
|
|
8869
|
+
total: plugins.length
|
|
8870
|
+
}, "skills", "marketplace-plugins", start);
|
|
8871
|
+
} catch (err) {
|
|
8872
|
+
fail(err, "skills", "marketplace-plugins", start);
|
|
8873
|
+
}
|
|
8874
|
+
});
|
|
8875
|
+
marketplace.command("remove").description("Remove a registered marketplace from the config (does not delete the local clone)").argument("<name>", "Name of the marketplace to remove").action((name) => {
|
|
8876
|
+
const start = Date.now();
|
|
8877
|
+
try {
|
|
8878
|
+
const configPath = getGlobalConfigPath();
|
|
8879
|
+
const { existing, all } = loadMarketplacesConfig(configPath);
|
|
8880
|
+
const idx = all.findIndex((m) => m.name === name || m.repoUrl === name);
|
|
8881
|
+
if (idx === -1) {
|
|
8882
|
+
throw new Error(`Marketplace "${name}" not found. Run: pncli skills marketplace list`);
|
|
8883
|
+
}
|
|
8884
|
+
const removed = all.splice(idx, 1)[0];
|
|
8885
|
+
saveMarketplaces(configPath, existing, all);
|
|
8886
|
+
success({
|
|
8887
|
+
removed: {
|
|
8888
|
+
name: marketplaceLabel(removed),
|
|
8889
|
+
repoUrl: removed.repoUrl,
|
|
8890
|
+
localPath: removed.localPath
|
|
8891
|
+
},
|
|
8892
|
+
remaining: all.length
|
|
8893
|
+
}, "skills", "marketplace-remove", start);
|
|
8894
|
+
} catch (err) {
|
|
8895
|
+
fail(err, "skills", "marketplace-remove", start);
|
|
8896
|
+
}
|
|
8897
|
+
});
|
|
8898
|
+
marketplace.command("sync").description("Pull latest marketplace content and install plugin skills").argument("[plugin]", 'Plugin name to install, or "all" to install every plugin (skips interactive selection)').option("--marketplace <name>", 'Marketplace name to sync, or "all" to sync every registered marketplace (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", 'Reinstall even if a marketplace has no new changes (applies to single-plugin and "all" installs alike)').action(async (plugin, opts) => {
|
|
8899
|
+
const start = Date.now();
|
|
8900
|
+
try {
|
|
8901
|
+
const configPath = getGlobalConfigPath();
|
|
8902
|
+
const allMarketplaces = loadMarketplaces(configPath);
|
|
8903
|
+
if (allMarketplaces.length === 0) {
|
|
8904
|
+
throw new Error("No marketplaces configured. Run: pncli skills marketplace add <url>");
|
|
8905
|
+
}
|
|
8906
|
+
const targetDir = resolveAgentPaths(resolveAgentName(opts)).user;
|
|
8907
|
+
const force = opts.force ?? false;
|
|
8908
|
+
if (opts.marketplace === "all") {
|
|
8909
|
+
const results = allMarketplaces.map((m) => syncMarketplacePlugins(m, targetDir, force, plugin ?? "all"));
|
|
8910
|
+
success({ allMarketplaces: true, marketplaces: results, target: targetDir }, "skills", "marketplace-sync", start);
|
|
8911
|
+
return;
|
|
8912
|
+
}
|
|
8913
|
+
let selectedMarketplace;
|
|
8914
|
+
let selectedPlugin = plugin;
|
|
8915
|
+
const canGoBack = !opts.marketplace && allMarketplaces.length > 1;
|
|
8916
|
+
if (opts.marketplace) {
|
|
8917
|
+
const found = allMarketplaces.find((m) => m.name === opts.marketplace || m.repoUrl === opts.marketplace);
|
|
8918
|
+
if (!found) {
|
|
8919
|
+
throw new Error(`Marketplace "${opts.marketplace}" not found. Run: pncli skills marketplace list`);
|
|
7956
8920
|
}
|
|
7957
|
-
|
|
7958
|
-
} else {
|
|
7959
|
-
|
|
7960
|
-
|
|
7961
|
-
|
|
8921
|
+
selectedMarketplace = found;
|
|
8922
|
+
} else if (allMarketplaces.length === 1) {
|
|
8923
|
+
selectedMarketplace = allMarketplaces[0];
|
|
8924
|
+
}
|
|
8925
|
+
for (; ; ) {
|
|
8926
|
+
if (!selectedMarketplace) {
|
|
8927
|
+
assertInteractive(`Multiple marketplaces are registered (${allMarketplaces.map(marketplaceLabel).join(", ")}). Pass --marketplace <name> (or --marketplace all) to run this non-interactively.`);
|
|
8928
|
+
const chosen = await select({
|
|
8929
|
+
message: "Select a marketplace to sync:",
|
|
8930
|
+
choices: [
|
|
8931
|
+
{ value: ALL_MARKETPLACES, name: "All marketplaces \u2014 sync every plugin from every marketplace" },
|
|
8932
|
+
// Index-based values: labels alone aren't guaranteed unique (two marketplaces can share a derived name).
|
|
8933
|
+
...allMarketplaces.map((m, i) => ({ value: String(i), name: `${marketplaceLabel(m)} \u2014 ${m.repoUrl ?? ""}` }))
|
|
8934
|
+
]
|
|
8935
|
+
});
|
|
8936
|
+
if (chosen === ALL_MARKETPLACES) {
|
|
8937
|
+
const results2 = allMarketplaces.map((m) => syncMarketplacePlugins(m, targetDir, force, selectedPlugin ?? "all"));
|
|
8938
|
+
success({ allMarketplaces: true, marketplaces: results2, target: targetDir }, "skills", "marketplace-sync", start);
|
|
8939
|
+
return;
|
|
8940
|
+
}
|
|
8941
|
+
selectedMarketplace = allMarketplaces[Number(chosen)];
|
|
8942
|
+
if (!selectedMarketplace) {
|
|
8943
|
+
throw new Error(`Marketplace "${chosen}" not found.`);
|
|
8944
|
+
}
|
|
8945
|
+
}
|
|
8946
|
+
const marketplaceName = marketplaceLabel(selectedMarketplace);
|
|
8947
|
+
const marketplacePath = selectedMarketplace.localPath;
|
|
8948
|
+
if (!marketplacePath || !fs4.existsSync(marketplacePath)) {
|
|
8949
|
+
throw new Error(`Marketplace "${marketplaceName}" local path not found at ${marketplacePath ?? "(not set)"}. Run: pncli skills marketplace add <url>`);
|
|
8950
|
+
}
|
|
8951
|
+
const { updated } = pullMarketplace(marketplacePath, selectedMarketplace.repoUrl, selectedMarketplace.token, marketplaceName);
|
|
8952
|
+
const pluginChoices = resolvePluginChoices(marketplacePath);
|
|
8953
|
+
if (pluginChoices.length === 0) {
|
|
8954
|
+
throw new Error(`No plugins found in marketplace "${marketplaceName}". Check the marketplace repository structure.`);
|
|
8955
|
+
}
|
|
8956
|
+
if (!selectedPlugin) {
|
|
8957
|
+
assertInteractive(`Pass a plugin name (or "all") to run this non-interactively.`);
|
|
8958
|
+
const choices = [
|
|
7962
8959
|
{ value: "all", name: "All \u2014 install every plugin" },
|
|
7963
|
-
...pluginChoices.map((p) => ({
|
|
7964
|
-
|
|
7965
|
-
|
|
7966
|
-
})
|
|
7967
|
-
|
|
7968
|
-
|
|
7969
|
-
|
|
7970
|
-
|
|
7971
|
-
|
|
8960
|
+
...pluginChoices.map((p) => ({ value: p.name, name: p.description ? `${p.name} \u2014 ${p.description}` : p.name }))
|
|
8961
|
+
];
|
|
8962
|
+
if (canGoBack) {
|
|
8963
|
+
choices.push({ value: BACK, name: "\u2190 Back to marketplace selection" });
|
|
8964
|
+
}
|
|
8965
|
+
const chosen = await select({ message: `Select a plugin from "${marketplaceName}" to install:`, choices });
|
|
8966
|
+
if (chosen === BACK) {
|
|
8967
|
+
selectedMarketplace = void 0;
|
|
8968
|
+
continue;
|
|
8969
|
+
}
|
|
8970
|
+
selectedPlugin = chosen;
|
|
8971
|
+
} else if (selectedPlugin !== "all" && !pluginChoices.some((p) => p.name === selectedPlugin)) {
|
|
8972
|
+
throw new Error(`Plugin "${selectedPlugin}" not found in "${marketplaceName}". Available: ${pluginChoices.map((p) => p.name).join(", ")}`);
|
|
8973
|
+
}
|
|
8974
|
+
if (!updated && !force) {
|
|
7972
8975
|
success({
|
|
7973
|
-
marketplace:
|
|
8976
|
+
marketplace: marketplaceName,
|
|
7974
8977
|
marketplaceUpdated: false,
|
|
7975
8978
|
updated: false,
|
|
7976
8979
|
skipped: true,
|
|
7977
|
-
message:
|
|
8980
|
+
message: `No changes detected in "${marketplaceName}" \u2014 skipping install. Use --force to reinstall anyway.`
|
|
7978
8981
|
}, "skills", "marketplace-sync", start);
|
|
7979
8982
|
return;
|
|
7980
8983
|
}
|
|
7981
|
-
const
|
|
7982
|
-
|
|
7983
|
-
|
|
7984
|
-
|
|
7985
|
-
|
|
7986
|
-
|
|
7987
|
-
|
|
7988
|
-
|
|
7989
|
-
|
|
7990
|
-
|
|
7991
|
-
|
|
7992
|
-
|
|
7993
|
-
|
|
7994
|
-
|
|
7995
|
-
}
|
|
7996
|
-
if (failed2.length > 0) {
|
|
7997
|
-
warn(`Skipped ${failed2.length} skill(s) with invalid names in "${pluginChoice.name}": ${failed2.join(", ")}`);
|
|
7998
|
-
}
|
|
8984
|
+
const pluginNames = selectedPlugin === "all" ? pluginChoices.map((p) => p.name) : [selectedPlugin];
|
|
8985
|
+
const { results, totalInstalled } = installPluginsForMarketplace(marketplacePath, marketplaceName, selectedMarketplace.repoUrl, pluginNames, targetDir);
|
|
8986
|
+
if (selectedPlugin === "all") {
|
|
8987
|
+
success({ marketplace: marketplaceName, plugins: results, total: totalInstalled, target: targetDir, marketplaceUpdated: updated }, "skills", "marketplace-sync", start);
|
|
8988
|
+
} else {
|
|
8989
|
+
const single = results[selectedPlugin] ?? { installed: [], failed: [] };
|
|
8990
|
+
success({
|
|
8991
|
+
marketplace: marketplaceName,
|
|
8992
|
+
plugin: selectedPlugin,
|
|
8993
|
+
installed: single.installed,
|
|
8994
|
+
failed: single.failed,
|
|
8995
|
+
total: single.installed.length,
|
|
8996
|
+
target: targetDir,
|
|
8997
|
+
marketplaceUpdated: updated
|
|
8998
|
+
}, "skills", "marketplace-sync", start);
|
|
7999
8999
|
}
|
|
8000
|
-
success({
|
|
8001
|
-
plugins: results,
|
|
8002
|
-
total: totalInstalled,
|
|
8003
|
-
target: targetDir,
|
|
8004
|
-
marketplaceUpdated
|
|
8005
|
-
}, "skills", "marketplace-sync", start);
|
|
8006
9000
|
return;
|
|
8007
9001
|
}
|
|
8008
|
-
const skillsSrc = resolveSkillsSrc(marketplacePath, selectedPlugin);
|
|
8009
|
-
if (!fs4.existsSync(skillsSrc)) {
|
|
8010
|
-
throw new Error(`No skills directory found at ${skillsSrc}`);
|
|
8011
|
-
}
|
|
8012
|
-
const { installed, failed } = copyPluginSkills(skillsSrc, targetDir);
|
|
8013
|
-
for (const skill of installed) {
|
|
8014
|
-
warn(` ${skill}: ${path7.join(skillsSrc, skill)} \u2192 ${path7.join(targetDir, skill)}`);
|
|
8015
|
-
}
|
|
8016
|
-
if (failed.length > 0) {
|
|
8017
|
-
warn(`Skipped ${failed.length} skill(s) with invalid names: ${failed.join(", ")}`);
|
|
8018
|
-
}
|
|
8019
|
-
success({
|
|
8020
|
-
plugin: selectedPlugin,
|
|
8021
|
-
installed,
|
|
8022
|
-
failed,
|
|
8023
|
-
total: installed.length,
|
|
8024
|
-
target: targetDir,
|
|
8025
|
-
marketplaceUpdated
|
|
8026
|
-
}, "skills", "marketplace-sync", start);
|
|
8027
9002
|
} catch (err) {
|
|
8028
9003
|
fail(err, "skills", "marketplace-sync", start);
|
|
8029
9004
|
}
|
|
@@ -8054,7 +9029,7 @@ function resolveSkillsSrc(marketplacePath, selectedPlugin) {
|
|
|
8054
9029
|
}
|
|
8055
9030
|
return skillsSrc;
|
|
8056
9031
|
}
|
|
8057
|
-
function copyPluginSkills(skillsSrc, targetDir) {
|
|
9032
|
+
function copyPluginSkills(skillsSrc, targetDir, meta) {
|
|
8058
9033
|
fs4.mkdirSync(targetDir, { recursive: true });
|
|
8059
9034
|
const resolvedTarget = path7.resolve(targetDir);
|
|
8060
9035
|
const skillNames = fs4.readdirSync(skillsSrc).filter(
|
|
@@ -8072,6 +9047,14 @@ function copyPluginSkills(skillsSrc, targetDir) {
|
|
|
8072
9047
|
fs4.cpSync(path7.join(skillsSrc, skillName), dest, { recursive: true });
|
|
8073
9048
|
installed.push(skillName);
|
|
8074
9049
|
}
|
|
9050
|
+
if (meta) {
|
|
9051
|
+
recordInstalledSkills(targetDir, installed, {
|
|
9052
|
+
source: "marketplace",
|
|
9053
|
+
marketplace: meta.marketplace,
|
|
9054
|
+
plugin: meta.plugin,
|
|
9055
|
+
installedFrom: meta.installedFrom
|
|
9056
|
+
});
|
|
9057
|
+
}
|
|
8075
9058
|
return { installed, failed };
|
|
8076
9059
|
}
|
|
8077
9060
|
|
|
@@ -8338,7 +9321,7 @@ if (!process.env.PNCLI_VERIFY_TLS) {
|
|
|
8338
9321
|
var require2 = createRequire(import.meta.url);
|
|
8339
9322
|
var pkg = require2("../package.json");
|
|
8340
9323
|
var TAGLINE = "One command does what three meetings couldn't.";
|
|
8341
|
-
var program = new
|
|
9324
|
+
var program = new Command3();
|
|
8342
9325
|
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");
|
|
8343
9326
|
program.hook("preAction", (thisCommand) => {
|
|
8344
9327
|
const opts = thisCommand.optsWithGlobals();
|
|
@@ -8357,6 +9340,7 @@ program.hook("preAction", (thisCommand) => {
|
|
|
8357
9340
|
registerGitCommands(program);
|
|
8358
9341
|
registerJiraCommands(program);
|
|
8359
9342
|
registerBitbucketCommands(program);
|
|
9343
|
+
registerGitHubCommands(program);
|
|
8360
9344
|
registerConfluenceCommands(program);
|
|
8361
9345
|
registerSonarCommands(program);
|
|
8362
9346
|
registerSdeCommands(program);
|
|
@@ -8379,6 +9363,7 @@ Services:
|
|
|
8379
9363
|
deps Dependency scanning, CVE detection, license auditing
|
|
8380
9364
|
jira Jira Data Cloud
|
|
8381
9365
|
bitbucket Bitbucket Server
|
|
9366
|
+
github GitHub (PRs, reviews, comments, checks)
|
|
8382
9367
|
confluence Confluence
|
|
8383
9368
|
sonar SonarQube Server (quality gates, issues, metrics, hotspots)
|
|
8384
9369
|
sde SDElements (threat modeling, countermeasures, compliance)
|