@kolatts/pncli 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  setRepoConfigValue,
8
8
  writeGlobalConfig,
9
9
  writeRepoConfig
10
- } from "./chunk-VEHCNGUP.js";
10
+ } from "./chunk-S4SJLY4M.js";
11
11
  import {
12
12
  ExitCode,
13
13
  PncliError,
@@ -18,10 +18,10 @@ import {
18
18
  setGlobalUser,
19
19
  success,
20
20
  warn
21
- } from "./chunk-HWXXNMGE.js";
21
+ } from "./chunk-QQ7UKWTU.js";
22
22
  import {
23
23
  AdoCoreClient
24
- } from "./chunk-KT3XINLJ.js";
24
+ } from "./chunk-VDL32PSG.js";
25
25
 
26
26
  // src/cli.ts
27
27
  import { Command } from "commander";
@@ -847,7 +847,7 @@ function registerJiraCommands(program2) {
847
847
  fail(err, "jira", "list-comments", start);
848
848
  }
849
849
  });
850
- jira.command("search").description("Search Jira issues with JQL").requiredOption("--jql <query>", "JQL query string").option("--max-results <n>", "Maximum number of results").action(async (opts) => {
850
+ jira.command("search").description("Search Jira issues with JQL (consider --output-file for large results)").requiredOption("--jql <query>", "JQL query string").option("--max-results <n>", "Maximum number of results").action(async (opts) => {
851
851
  const start = Date.now();
852
852
  try {
853
853
  const { client, fieldMap, customFields } = getClientAndFields(program2);
@@ -883,7 +883,7 @@ function registerJiraCommands(program2) {
883
883
  fail(err, "jira", "link-issue", start);
884
884
  }
885
885
  });
886
- jira.command("fields").description("List custom fields (configured or discovered from Jira API)").option("--discover", "Fetch field metadata from Jira API").option("--custom-only", "Show only custom fields (requires --discover)").action(async (opts) => {
886
+ jira.command("fields").description("List custom fields (configured or discovered from Jira API) (consider --output-file for large results)").option("--discover", "Fetch field metadata from Jira API").option("--custom-only", "Show only custom fields (requires --discover)").action(async (opts) => {
887
887
  const start = Date.now();
888
888
  try {
889
889
  if (opts.discover) {
@@ -936,7 +936,7 @@ function registerBitbucketCommands(program2) {
936
936
  bb.command("list-prs").description("List pull requests").option("--state <state>", "PR state: OPEN|MERGED|DECLINED|ALL", "OPEN").option("--author <username>", "Filter by author username").option("--reviewer <username>", "Filter by reviewer username").action(async (opts) => {
937
937
  const start = Date.now();
938
938
  try {
939
- const { client, project, repo } = getClient2(program2);
939
+ const { client, project, repo } = getClient2(bb);
940
940
  const data = await client.listPRs({ project, repo, state: opts.state, author: opts.author, reviewer: opts.reviewer });
941
941
  success(data, "bitbucket", "list-prs", start);
942
942
  } catch (err) {
@@ -946,7 +946,7 @@ function registerBitbucketCommands(program2) {
946
946
  bb.command("get-pr").description("Get a pull request by ID").requiredOption("--id <pr-id>", "Pull request ID").action(async (opts) => {
947
947
  const start = Date.now();
948
948
  try {
949
- const { client, project, repo } = getClient2(program2);
949
+ const { client, project, repo } = getClient2(bb);
950
950
  const data = await client.getPR(project, repo, parseInt(opts.id, 10));
951
951
  success(data, "bitbucket", "get-pr", start);
952
952
  } catch (err) {
@@ -956,8 +956,8 @@ function registerBitbucketCommands(program2) {
956
956
  bb.command("create-pr").description("Create a pull request").requiredOption("--title <title>", "PR title").requiredOption("--source <branch>", "Source branch").option("--target <branch>", "Target branch (defaults to config)").option("--description <desc>", "PR description").option("--reviewers <users>", "Comma-separated reviewer usernames").action(async (opts) => {
957
957
  const start = Date.now();
958
958
  try {
959
- const { client, project, repo } = getClient2(program2);
960
- const config = loadConfig({ configPath: program2.optsWithGlobals().config });
959
+ const { client, project, repo } = getClient2(bb);
960
+ const config = loadConfig({ configPath: bb.optsWithGlobals().config });
961
961
  const target = opts.target ?? config.defaults.bitbucket?.targetBranch ?? "main";
962
962
  const reviewers = opts.reviewers ? opts.reviewers.split(",").map((s) => s.trim()) : [];
963
963
  const data = await client.createPR({ project, repo, title: opts.title, source: opts.source, target, description: opts.description, reviewers });
@@ -969,7 +969,7 @@ function registerBitbucketCommands(program2) {
969
969
  bb.command("update-pr").description("Update a pull request").requiredOption("--id <pr-id>", "Pull request ID").option("--title <title>", "New title").option("--description <desc>", "New description").option("--reviewers <users>", "Comma-separated reviewer usernames").action(async (opts) => {
970
970
  const start = Date.now();
971
971
  try {
972
- const { client, project, repo } = getClient2(program2);
972
+ const { client, project, repo } = getClient2(bb);
973
973
  const prId = parseInt(opts.id, 10);
974
974
  const pr = await client.getPR(project, repo, prId);
975
975
  const reviewers = opts.reviewers ? opts.reviewers.split(",").map((s) => s.trim()) : void 0;
@@ -982,7 +982,7 @@ function registerBitbucketCommands(program2) {
982
982
  bb.command("merge-pr").description("Merge a pull request").requiredOption("--id <pr-id>", "Pull request ID").option("--strategy <strategy>", "Merge strategy: merge|squash|ff").option("--delete-branch", "Delete source branch after merge").action(async (opts) => {
983
983
  const start = Date.now();
984
984
  try {
985
- const { client, project, repo } = getClient2(program2);
985
+ const { client, project, repo } = getClient2(bb);
986
986
  const prId = parseInt(opts.id, 10);
987
987
  const pr = await client.getPR(project, repo, prId);
988
988
  const data = await client.mergePR({ project, repo, id: prId, version: pr.version, strategy: opts.strategy, deleteBranch: opts.deleteBranch });
@@ -994,7 +994,7 @@ function registerBitbucketCommands(program2) {
994
994
  bb.command("decline-pr").description("Decline a pull request").requiredOption("--id <pr-id>", "Pull request ID").action(async (opts) => {
995
995
  const start = Date.now();
996
996
  try {
997
- const { client, project, repo } = getClient2(program2);
997
+ const { client, project, repo } = getClient2(bb);
998
998
  const prId = parseInt(opts.id, 10);
999
999
  const pr = await client.getPR(project, repo, prId);
1000
1000
  const data = await client.declinePR(project, repo, prId, pr.version);
@@ -1006,7 +1006,7 @@ function registerBitbucketCommands(program2) {
1006
1006
  bb.command("list-comments").description("List comments on a pull request (includes threaded replies by default)").requiredOption("--pr <pr-id>", "Pull request ID").option("--no-with-replies", "Exclude replies; return top-level comments only").option("--inline-only", "Return only inline file comments").option("--general-only", "Return only general (non-inline) PR comments").action(async (opts) => {
1007
1007
  const start = Date.now();
1008
1008
  try {
1009
- const { client, project, repo } = getClient2(program2);
1009
+ const { client, project, repo } = getClient2(bb);
1010
1010
  const data = await client.listComments(project, repo, parseInt(opts.pr, 10), {
1011
1011
  withReplies: opts.withReplies,
1012
1012
  inlineOnly: opts.inlineOnly,
@@ -1020,7 +1020,7 @@ function registerBitbucketCommands(program2) {
1020
1020
  bb.command("add-comment").description("Add a comment to a pull request").requiredOption("--pr <pr-id>", "Pull request ID").requiredOption("--body <text>", "Comment text").action(async (opts) => {
1021
1021
  const start = Date.now();
1022
1022
  try {
1023
- const { client, project, repo } = getClient2(program2);
1023
+ const { client, project, repo } = getClient2(bb);
1024
1024
  const data = await client.addComment(project, repo, parseInt(opts.pr, 10), opts.body);
1025
1025
  success(data, "bitbucket", "add-comment", start);
1026
1026
  } catch (err) {
@@ -1030,7 +1030,7 @@ function registerBitbucketCommands(program2) {
1030
1030
  bb.command("add-inline-comment").description("Add an inline comment to a file in a pull request").requiredOption("--pr <pr-id>", "Pull request ID").requiredOption("--file <path>", "File path").requiredOption("--line <n>", "Line number").requiredOption("--body <text>", "Comment text").option("--line-type <type>", "Line type: ADDED|REMOVED|CONTEXT", "ADDED").action(async (opts) => {
1031
1031
  const start = Date.now();
1032
1032
  try {
1033
- const { client, project, repo } = getClient2(program2);
1033
+ const { client, project, repo } = getClient2(bb);
1034
1034
  const data = await client.addInlineComment({
1035
1035
  project,
1036
1036
  repo,
@@ -1048,7 +1048,7 @@ function registerBitbucketCommands(program2) {
1048
1048
  bb.command("reply-comment").description("Reply to a comment on a pull request").requiredOption("--pr <pr-id>", "Pull request ID").requiredOption("--comment-id <id>", "Comment ID to reply to").requiredOption("--body <text>", "Reply text").action(async (opts) => {
1049
1049
  const start = Date.now();
1050
1050
  try {
1051
- const { client, project, repo } = getClient2(program2);
1051
+ const { client, project, repo } = getClient2(bb);
1052
1052
  const data = await client.replyComment(project, repo, parseInt(opts.pr, 10), parseInt(opts.commentId, 10), opts.body);
1053
1053
  success(data, "bitbucket", "reply-comment", start);
1054
1054
  } catch (err) {
@@ -1058,7 +1058,7 @@ function registerBitbucketCommands(program2) {
1058
1058
  bb.command("resolve-comment").description("Resolve a comment on a pull request").requiredOption("--pr <pr-id>", "Pull request ID").requiredOption("--comment-id <id>", "Comment ID").option("--version <n>", "Comment version", "0").action(async (opts) => {
1059
1059
  const start = Date.now();
1060
1060
  try {
1061
- const { client, project, repo } = getClient2(program2);
1061
+ const { client, project, repo } = getClient2(bb);
1062
1062
  await client.resolveComment(project, repo, parseInt(opts.pr, 10), parseInt(opts.commentId, 10), parseInt(opts.version ?? "0", 10));
1063
1063
  success({ resolved: true }, "bitbucket", "resolve-comment", start);
1064
1064
  } catch (err) {
@@ -1068,7 +1068,7 @@ function registerBitbucketCommands(program2) {
1068
1068
  bb.command("delete-comment").description("Delete a comment on a pull request").requiredOption("--pr <pr-id>", "Pull request ID").requiredOption("--comment-id <id>", "Comment ID").option("--version <n>", "Comment version", "0").action(async (opts) => {
1069
1069
  const start = Date.now();
1070
1070
  try {
1071
- const { client, project, repo } = getClient2(program2);
1071
+ const { client, project, repo } = getClient2(bb);
1072
1072
  await client.deleteComment(project, repo, parseInt(opts.pr, 10), parseInt(opts.commentId, 10), parseInt(opts.version ?? "0", 10));
1073
1073
  success({ deleted: true }, "bitbucket", "delete-comment", start);
1074
1074
  } catch (err) {
@@ -1078,7 +1078,7 @@ function registerBitbucketCommands(program2) {
1078
1078
  bb.command("diff").description("Get unified diff for a pull request").requiredOption("--pr <pr-id>", "Pull request ID").option("--file <path>", "Limit diff to a specific file").option("--context-lines <n>", "Lines of context around changes").action(async (opts) => {
1079
1079
  const start = Date.now();
1080
1080
  try {
1081
- const { client, project, repo } = getClient2(program2);
1081
+ const { client, project, repo } = getClient2(bb);
1082
1082
  const contextLines = opts.contextLines ? parseInt(opts.contextLines, 10) : void 0;
1083
1083
  const diff = await client.getDiff(project, repo, parseInt(opts.pr, 10), opts.file, contextLines);
1084
1084
  success({ diff }, "bitbucket", "diff", start);
@@ -1089,7 +1089,7 @@ function registerBitbucketCommands(program2) {
1089
1089
  bb.command("list-files").description("List files changed in a pull request").requiredOption("--pr <pr-id>", "Pull request ID").action(async (opts) => {
1090
1090
  const start = Date.now();
1091
1091
  try {
1092
- const { client, project, repo } = getClient2(program2);
1092
+ const { client, project, repo } = getClient2(bb);
1093
1093
  const data = await client.listFiles(project, repo, parseInt(opts.pr, 10));
1094
1094
  success(data, "bitbucket", "list-files", start);
1095
1095
  } catch (err) {
@@ -1099,7 +1099,7 @@ function registerBitbucketCommands(program2) {
1099
1099
  bb.command("approve").description("Approve a pull request").requiredOption("--pr <pr-id>", "Pull request ID").action(async (opts) => {
1100
1100
  const start = Date.now();
1101
1101
  try {
1102
- const { client, project, repo } = getClient2(program2);
1102
+ const { client, project, repo } = getClient2(bb);
1103
1103
  const data = await client.approvePR(project, repo, parseInt(opts.pr, 10));
1104
1104
  success(data, "bitbucket", "approve", start);
1105
1105
  } catch (err) {
@@ -1109,7 +1109,7 @@ function registerBitbucketCommands(program2) {
1109
1109
  bb.command("unapprove").description("Remove approval from a pull request").requiredOption("--pr <pr-id>", "Pull request ID").action(async (opts) => {
1110
1110
  const start = Date.now();
1111
1111
  try {
1112
- const { client, project, repo } = getClient2(program2);
1112
+ const { client, project, repo } = getClient2(bb);
1113
1113
  const data = await client.unapprovePR(project, repo, parseInt(opts.pr, 10));
1114
1114
  success(data, "bitbucket", "unapprove", start);
1115
1115
  } catch (err) {
@@ -1119,7 +1119,7 @@ function registerBitbucketCommands(program2) {
1119
1119
  bb.command("needs-work").description("Mark a pull request as needs-work").requiredOption("--pr <pr-id>", "Pull request ID").action(async (opts) => {
1120
1120
  const start = Date.now();
1121
1121
  try {
1122
- const { client, project, repo } = getClient2(program2);
1122
+ const { client, project, repo } = getClient2(bb);
1123
1123
  const data = await client.needsWorkPR(project, repo, parseInt(opts.pr, 10));
1124
1124
  success(data, "bitbucket", "needs-work", start);
1125
1125
  } catch (err) {
@@ -1129,7 +1129,7 @@ function registerBitbucketCommands(program2) {
1129
1129
  bb.command("list-reviewers").description("List reviewers of a pull request").requiredOption("--pr <pr-id>", "Pull request ID").action(async (opts) => {
1130
1130
  const start = Date.now();
1131
1131
  try {
1132
- const { client, project, repo } = getClient2(program2);
1132
+ const { client, project, repo } = getClient2(bb);
1133
1133
  const data = await client.listReviewers(project, repo, parseInt(opts.pr, 10));
1134
1134
  success(data, "bitbucket", "list-reviewers", start);
1135
1135
  } catch (err) {
@@ -1139,7 +1139,7 @@ function registerBitbucketCommands(program2) {
1139
1139
  bb.command("list-builds").description("List build statuses for a pull request").requiredOption("--pr <pr-id>", "Pull request ID").action(async (opts) => {
1140
1140
  const start = Date.now();
1141
1141
  try {
1142
- const { client, project, repo } = getClient2(program2);
1142
+ const { client, project, repo } = getClient2(bb);
1143
1143
  const data = await client.listBuilds(project, repo, parseInt(opts.pr, 10));
1144
1144
  success(data, "bitbucket", "list-builds", start);
1145
1145
  } catch (err) {
@@ -1149,7 +1149,7 @@ function registerBitbucketCommands(program2) {
1149
1149
  bb.command("get-build-status").description("Get build status for a commit SHA").requiredOption("--commit <sha>", "Commit SHA").action(async (opts) => {
1150
1150
  const start = Date.now();
1151
1151
  try {
1152
- const { client } = getClient2(program2);
1152
+ const { client } = getClient2(bb);
1153
1153
  const data = await client.getBuildStatus(opts.commit);
1154
1154
  success(data, "bitbucket", "get-build-status", start);
1155
1155
  } catch (err) {
@@ -1336,7 +1336,7 @@ function registerConfluenceCommands(program2) {
1336
1336
  fail(err, "confluence", "get-page-by-title", start);
1337
1337
  }
1338
1338
  });
1339
- confluence.command("list-pages").description("List pages in a Confluence space").requiredOption("--space <key>", "Space key").option("--limit <n>", "Max results per page (default: all)").option("--start <n>", "Offset for first result").action(async (opts) => {
1339
+ 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 results per page (default: all)").option("--start <n>", "Offset for first result").action(async (opts) => {
1340
1340
  const start = Date.now();
1341
1341
  try {
1342
1342
  const client = getClient3(program2);
@@ -1631,7 +1631,7 @@ function registerSonarCommands(program2) {
1631
1631
  fail(err, "sonar", "quality-gate", start);
1632
1632
  }
1633
1633
  });
1634
- 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 (ignores --page/--page-size)").action(async (opts) => {
1634
+ 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) => {
1635
1635
  const start = Date.now();
1636
1636
  try {
1637
1637
  const { client, config } = getClient4(program2);
@@ -1674,7 +1674,7 @@ function registerSonarCommands(program2) {
1674
1674
  fail(err, "sonar", "measures", start);
1675
1675
  }
1676
1676
  });
1677
- 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").action(async (opts) => {
1677
+ 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) => {
1678
1678
  const start = Date.now();
1679
1679
  try {
1680
1680
  const { client } = getClient4(program2);
@@ -1693,7 +1693,7 @@ function registerSonarCommands(program2) {
1693
1693
  fail(err, "sonar", "projects", start);
1694
1694
  }
1695
1695
  });
1696
- 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").action(async (opts) => {
1696
+ 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) => {
1697
1697
  const start = Date.now();
1698
1698
  try {
1699
1699
  const { client, config } = getClient4(program2);
@@ -1901,7 +1901,7 @@ function registerSdeCommands(program2) {
1901
1901
  fail(err, "sde", "whoami", start);
1902
1902
  }
1903
1903
  });
1904
- 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").action(async (opts) => {
1904
+ 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) => {
1905
1905
  const start = Date.now();
1906
1906
  try {
1907
1907
  const { client } = getClient5(program2);
@@ -1926,7 +1926,7 @@ function registerSdeCommands(program2) {
1926
1926
  fail(err, "sde", "users", start);
1927
1927
  }
1928
1928
  });
1929
- 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").action(async (opts) => {
1929
+ 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) => {
1930
1930
  const start = Date.now();
1931
1931
  try {
1932
1932
  const { client } = getClient5(program2);
@@ -1964,7 +1964,7 @@ function registerSdeCommands(program2) {
1964
1964
  fail(err, "sde", "project", start);
1965
1965
  }
1966
1966
  });
1967
- 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").action(async (opts) => {
1967
+ 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) => {
1968
1968
  const start = Date.now();
1969
1969
  try {
1970
1970
  const { client, config } = getClient5(program2);
@@ -2009,7 +2009,7 @@ function registerSdeCommands(program2) {
2009
2009
  fail(err, "sde", "task", start);
2010
2010
  }
2011
2011
  });
2012
- 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").action(async (opts) => {
2012
+ 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) => {
2013
2013
  const start = Date.now();
2014
2014
  try {
2015
2015
  const { client, config } = getClient5(program2);
@@ -3603,7 +3603,7 @@ function registerConfigCommands(program2) {
3603
3603
  if (cfg.ado.baseUrl) {
3604
3604
  try {
3605
3605
  const collection = cfg.defaults.ado?.collection;
3606
- const path6 = collection ? `/${encodeURIComponent(collection)}/_apis/connectionData?api-version=7.1` : "/_apis/projectCollections?api-version=7.1";
3606
+ const path6 = collection ? `/${encodeURIComponent(collection)}/_apis/connectionData?api-version=7.1-preview.1` : "/_apis/projectCollections?api-version=7.1";
3607
3607
  await http.ado(path6);
3608
3608
  results.ado = { ok: true, message: "connected" };
3609
3609
  } catch (err) {
@@ -3612,6 +3612,46 @@ function registerConfigCommands(program2) {
3612
3612
  } else {
3613
3613
  results.ado = { ok: null, message: "not configured" };
3614
3614
  }
3615
+ if (cfg.jenkins.baseUrl && cfg.jenkins.username && cfg.jenkins.apiToken) {
3616
+ try {
3617
+ await http.jenkins("/api/json", { params: { tree: "nodeName" } });
3618
+ results.jenkins = { ok: true, message: "connected" };
3619
+ } catch (err) {
3620
+ results.jenkins = { ok: false, message: err instanceof Error ? err.message : String(err) };
3621
+ }
3622
+ } else {
3623
+ results.jenkins = { ok: null, message: "not configured" };
3624
+ }
3625
+ if (cfg.artifactory.baseUrl) {
3626
+ try {
3627
+ await http.artifactoryText("/api/system/ping");
3628
+ results.artifactory = { ok: true, message: "connected" };
3629
+ } catch (err) {
3630
+ results.artifactory = { ok: false, message: err instanceof Error ? err.message : String(err) };
3631
+ }
3632
+ } else {
3633
+ results.artifactory = { ok: null, message: "not configured" };
3634
+ }
3635
+ if (cfg.udeploy.baseUrl && (cfg.udeploy.pat || cfg.udeploy.username && cfg.udeploy.password)) {
3636
+ try {
3637
+ await http.udeploy("/cli/application");
3638
+ results.udeploy = { ok: true, message: "connected" };
3639
+ } catch (err) {
3640
+ results.udeploy = { ok: false, message: err instanceof Error ? err.message : String(err) };
3641
+ }
3642
+ } else {
3643
+ results.udeploy = { ok: null, message: "not configured" };
3644
+ }
3645
+ if (cfg.checkmarx.baseUrl && cfg.checkmarx.username && cfg.checkmarx.password) {
3646
+ try {
3647
+ await http.checkmarx("/cxrestapi/projects");
3648
+ results.checkmarx = { ok: true, message: "connected" };
3649
+ } catch (err) {
3650
+ results.checkmarx = { ok: false, message: err instanceof Error ? err.message : String(err) };
3651
+ }
3652
+ } else {
3653
+ results.checkmarx = { ok: null, message: "not configured" };
3654
+ }
3615
3655
  success(results, "config", "test", start);
3616
3656
  } catch (err) {
3617
3657
  fail(err, "config", "test", start);
@@ -3705,13 +3745,37 @@ function registerConfigCommands(program2) {
3705
3745
  } else {
3706
3746
  try {
3707
3747
  const collection = cfg.defaults.ado?.collection;
3708
- const path6 = collection ? `/${encodeURIComponent(collection)}/_apis/connectionData?api-version=7.1` : "/_apis/projectCollections?api-version=7.1";
3748
+ const path6 = collection ? `/${encodeURIComponent(collection)}/_apis/connectionData?api-version=7.1-preview.1` : "/_apis/projectCollections?api-version=7.1";
3709
3749
  await http.ado(path6, { timeoutMs: 1e4 });
3710
3750
  results.ado = { status: "valid", message: "ok" };
3711
3751
  } catch (err) {
3712
3752
  results.ado = categorize2(err);
3713
3753
  }
3714
3754
  }
3755
+ if (!cfg.jenkins.apiToken || !cfg.jenkins.username) {
3756
+ results.jenkins = { status: "blank", message: "not configured" };
3757
+ } else if (!cfg.jenkins.baseUrl) {
3758
+ results.jenkins = { status: "error", message: "baseUrl not configured" };
3759
+ } else {
3760
+ try {
3761
+ await http.jenkins("/api/json", { params: { tree: "nodeName" }, timeoutMs: 1e4 });
3762
+ results.jenkins = { status: "valid", message: "ok" };
3763
+ } catch (err) {
3764
+ results.jenkins = categorize2(err);
3765
+ }
3766
+ }
3767
+ if (!cfg.udeploy.pat && !(cfg.udeploy.username && cfg.udeploy.password)) {
3768
+ results.udeploy = { status: "blank", message: "not configured" };
3769
+ } else if (!cfg.udeploy.baseUrl) {
3770
+ results.udeploy = { status: "error", message: "baseUrl not configured" };
3771
+ } else {
3772
+ try {
3773
+ await http.udeploy("/cli/application", { timeoutMs: 1e4 });
3774
+ results.udeploy = { status: "valid", message: "ok" };
3775
+ } catch (err) {
3776
+ results.udeploy = categorize2(err);
3777
+ }
3778
+ }
3715
3779
  const artResult = await checkArtifactoryConnectivity(cfg.artifactory);
3716
3780
  if (!artResult.configured) {
3717
3781
  results.artifactory = { status: "blank", message: artResult.error ?? "not configured" };
@@ -3722,11 +3786,23 @@ function registerConfigCommands(program2) {
3722
3786
  } else {
3723
3787
  results.artifactory = { status: "valid", message: "ok" };
3724
3788
  }
3789
+ if (!cfg.checkmarx.username || !cfg.checkmarx.password) {
3790
+ results.checkmarx = { status: "blank", message: "not configured" };
3791
+ } else if (!cfg.checkmarx.baseUrl) {
3792
+ results.checkmarx = { status: "error", message: "baseUrl not configured" };
3793
+ } else {
3794
+ try {
3795
+ await http.checkmarx("/cxrestapi/projects", { timeoutMs: 1e4 });
3796
+ results.checkmarx = { status: "valid", message: "ok" };
3797
+ } catch (err) {
3798
+ results.checkmarx = categorize2(err);
3799
+ }
3800
+ }
3725
3801
  const statuses = Object.values(results).map((r) => r.status);
3726
3802
  if (statuses.includes("invalid")) process.exitCode = ExitCode.AUTH_ERROR;
3727
3803
  else if (statuses.includes("error")) process.exitCode = ExitCode.NETWORK_ERROR;
3728
3804
  if (cmdOpts.output === "table") {
3729
- const services = ["jira", "bitbucket", "confluence", "sonar", "sde", "ado", "artifactory"];
3805
+ const services = ["jira", "bitbucket", "confluence", "sonar", "sde", "ado", "jenkins", "udeploy", "artifactory", "checkmarx"];
3730
3806
  const labelWidth = 14;
3731
3807
  const statusWidth = 9;
3732
3808
  for (const svc of services) {
@@ -3740,7 +3816,7 @@ function registerConfigCommands(program2) {
3740
3816
  process.stdout.write("\n");
3741
3817
  } else {
3742
3818
  if (opts.pretty) {
3743
- const services = ["jira", "bitbucket", "confluence", "sonar", "sde", "ado", "artifactory"];
3819
+ const services = ["jira", "bitbucket", "confluence", "sonar", "sde", "ado", "jenkins", "udeploy", "artifactory", "checkmarx"];
3744
3820
  const labelWidth = 14;
3745
3821
  const statusWidth = 9;
3746
3822
  for (const svc of services) {
@@ -3951,6 +4027,128 @@ async function initGlobalConfig(start) {
3951
4027
  }
3952
4028
  }
3953
4029
  }
4030
+ process.stderr.write("\n\u2500\u2500 Jenkins \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");
4031
+ const useJenkins = await confirm({
4032
+ message: "Configure Jenkins Data Center for pipeline operations?",
4033
+ default: false
4034
+ });
4035
+ let jenkinsBaseUrl = "";
4036
+ let jenkinsUsername = "";
4037
+ let jenkinsApiToken = "";
4038
+ if (useJenkins) {
4039
+ jenkinsBaseUrl = await input({
4040
+ message: "Jenkins base URL (e.g. https://jenkins.your-company.com):",
4041
+ default: ""
4042
+ });
4043
+ jenkinsUsername = await input({
4044
+ message: "Jenkins username:",
4045
+ default: ""
4046
+ });
4047
+ jenkinsApiToken = await password({
4048
+ message: "Jenkins API token:"
4049
+ });
4050
+ }
4051
+ process.stderr.write("\n\u2500\u2500 IBM UrbanCode Deploy \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");
4052
+ const useUdeploy = await confirm({
4053
+ message: "Configure IBM UrbanCode Deploy for component imports and deployment processes?",
4054
+ default: false
4055
+ });
4056
+ let udeployBaseUrl = "";
4057
+ let udeployPat = "";
4058
+ let udeployUsername = "";
4059
+ let udeployPassword = "";
4060
+ let udeployDefaultApp = "";
4061
+ let udeployDefaultEnv = "";
4062
+ if (useUdeploy) {
4063
+ udeployBaseUrl = await input({
4064
+ message: "UDeploy base URL (e.g. https://ucd.company.com:8443):",
4065
+ default: ""
4066
+ });
4067
+ udeployUsername = await input({
4068
+ message: "UDeploy username (leave blank to use a PAT instead):",
4069
+ default: ""
4070
+ });
4071
+ if (udeployUsername) {
4072
+ udeployPassword = await password({
4073
+ message: "UDeploy password:",
4074
+ validate: (v) => v.length > 0 || "Password cannot be blank"
4075
+ });
4076
+ } else {
4077
+ udeployPat = await password({
4078
+ message: "UDeploy personal access token:"
4079
+ });
4080
+ }
4081
+ udeployDefaultApp = await input({
4082
+ message: "Default application name (optional):",
4083
+ default: ""
4084
+ });
4085
+ udeployDefaultEnv = await input({
4086
+ message: "Default environment name (optional):",
4087
+ default: ""
4088
+ });
4089
+ const hasCredentials = udeployBaseUrl && (udeployPat || udeployUsername && udeployPassword);
4090
+ if (hasCredentials) {
4091
+ process.stderr.write("\n Verifying connection...\n");
4092
+ try {
4093
+ const tempConfig = {
4094
+ ...loadConfig(),
4095
+ udeploy: {
4096
+ baseUrl: udeployBaseUrl,
4097
+ pat: udeployPat || void 0,
4098
+ username: udeployUsername || void 0,
4099
+ password: udeployPassword || void 0
4100
+ }
4101
+ };
4102
+ const tempHttp = createHttpClient(tempConfig);
4103
+ await tempHttp.udeploy("/cli/application");
4104
+ process.stderr.write(" Connected.\n");
4105
+ } catch (err) {
4106
+ warn(`Could not connect to UDeploy: ${err instanceof Error ? err.message : String(err)}`);
4107
+ warn("Config will be saved anyway. Check your URL and credentials and re-run pncli config init or pncli config test.");
4108
+ }
4109
+ }
4110
+ }
4111
+ process.stderr.write("\n\u2500\u2500 Checkmarx \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");
4112
+ const useCheckmarx = await confirm({
4113
+ message: "Configure Checkmarx CxSAST for vulnerability scanning?",
4114
+ default: false
4115
+ });
4116
+ let checkmarxBaseUrl = "";
4117
+ let checkmarxUsername = "";
4118
+ let checkmarxPassword = "";
4119
+ if (useCheckmarx) {
4120
+ checkmarxBaseUrl = await input({
4121
+ message: "Checkmarx base URL (e.g. https://cx.company.com):",
4122
+ default: ""
4123
+ });
4124
+ checkmarxUsername = await input({
4125
+ message: "Checkmarx username:",
4126
+ default: ""
4127
+ });
4128
+ checkmarxPassword = await password({
4129
+ message: "Checkmarx password:",
4130
+ validate: (v) => v.length > 0 || "Password cannot be blank"
4131
+ });
4132
+ if (checkmarxBaseUrl && checkmarxUsername && checkmarxPassword) {
4133
+ process.stderr.write("\n Verifying connection...\n");
4134
+ try {
4135
+ const tempConfig = {
4136
+ ...loadConfig(),
4137
+ checkmarx: {
4138
+ baseUrl: checkmarxBaseUrl,
4139
+ username: checkmarxUsername,
4140
+ password: checkmarxPassword
4141
+ }
4142
+ };
4143
+ const tempHttp = createHttpClient(tempConfig);
4144
+ await tempHttp.checkmarx("/cxrestapi/projects");
4145
+ process.stderr.write(" Connected.\n");
4146
+ } catch (err) {
4147
+ warn(`Could not connect to Checkmarx: ${err instanceof Error ? err.message : String(err)}`);
4148
+ warn("Config will be saved anyway. Check your URL and credentials and re-run pncli config init or pncli config test.");
4149
+ }
4150
+ }
4151
+ }
3954
4152
  process.stderr.write("\n\u2500\u2500 Defaults \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");
3955
4153
  const jiraProject = await input({
3956
4154
  message: "Default Jira project key (optional):",
@@ -4022,6 +4220,28 @@ async function initGlobalConfig(start) {
4022
4220
  ...adoDiscoveredTypes.length ? { discoveredTypes: adoDiscoveredTypes } : {}
4023
4221
  }
4024
4222
  } : {},
4223
+ ...useJenkins && jenkinsBaseUrl ? {
4224
+ jenkins: {
4225
+ baseUrl: jenkinsBaseUrl,
4226
+ ...jenkinsUsername ? { username: jenkinsUsername } : {},
4227
+ ...jenkinsApiToken ? { apiToken: jenkinsApiToken } : {}
4228
+ }
4229
+ } : {},
4230
+ ...useUdeploy && udeployBaseUrl ? {
4231
+ udeploy: {
4232
+ baseUrl: udeployBaseUrl,
4233
+ ...udeployPat ? { pat: udeployPat } : {},
4234
+ ...udeployUsername ? { username: udeployUsername } : {},
4235
+ ...udeployPassword ? { password: udeployPassword } : {}
4236
+ }
4237
+ } : {},
4238
+ ...useCheckmarx && checkmarxBaseUrl ? {
4239
+ checkmarx: {
4240
+ baseUrl: checkmarxBaseUrl,
4241
+ username: checkmarxUsername || void 0,
4242
+ password: checkmarxPassword || void 0
4243
+ }
4244
+ } : {},
4025
4245
  defaults: {
4026
4246
  jira: {
4027
4247
  project: jiraProject || void 0
@@ -4041,6 +4261,12 @@ async function initGlobalConfig(start) {
4041
4261
  collection: adoCollection || void 0,
4042
4262
  project: adoProject || void 0
4043
4263
  }
4264
+ } : {},
4265
+ ...useUdeploy && (udeployDefaultApp || udeployDefaultEnv) ? {
4266
+ udeploy: {
4267
+ application: udeployDefaultApp || void 0,
4268
+ environment: udeployDefaultEnv || void 0
4269
+ }
4044
4270
  } : {}
4045
4271
  }
4046
4272
  });
@@ -4558,7 +4784,7 @@ function registerAdoWorkCommands(ado) {
4558
4784
  fail(err, "ado", "work-link", start);
4559
4785
  }
4560
4786
  });
4561
- work.command("search").description("Run a WIQL query").requiredOption("--wiql <query>", "WIQL query string").action(async (opts) => {
4787
+ work.command("search").description("Run a WIQL query (consider --output-file for large results)").requiredOption("--wiql <query>", "WIQL query string").action(async (opts) => {
4562
4788
  const start = Date.now();
4563
4789
  try {
4564
4790
  const { collection, project, workClient } = getAdoContext(ado);
@@ -4617,7 +4843,7 @@ function registerAdoWorkCommands(ado) {
4617
4843
  fail(err, "ado", "work-list-states", start);
4618
4844
  }
4619
4845
  });
4620
- work.command("fields").description("List work item fields available in the collection").option("--type <type>", "Scope to fields for a specific work item type (e.g. Bug)").option("--custom-only", "Exclude System.* and Microsoft.VSTS.* fields").option("--discover", "Fetch from server (always true for this command)").option("--save", "Save discovered fields and aliases to ~/.pncli/config.json").action(async (opts) => {
4846
+ work.command("fields").description("List work item fields available in the collection (consider --output-file for large results)").option("--type <type>", "Scope to fields for a specific work item type (e.g. Bug)").option("--custom-only", "Exclude System.* and Microsoft.VSTS.* fields").option("--discover", "Fetch from server (always true for this command)").option("--save", "Save discovered fields and aliases to ~/.pncli/config.json").action(async (opts) => {
4621
4847
  const start = Date.now();
4622
4848
  try {
4623
4849
  const { collection, project, workClient } = getAdoContext(ado);
@@ -4868,11 +5094,17 @@ function registerAdoRepoCommands(ado) {
4868
5094
  fail(err, "ado", "repo-list-files", start);
4869
5095
  }
4870
5096
  });
4871
- repo.command("diff").description("Show files changed in a pull request with change types and commit metadata").requiredOption("--pr <n>", "Pull request ID").action(async (opts) => {
5097
+ repo.command("diff").description("Show files changed in a pull request with change types and commit metadata").requiredOption("--pr <n>", "Pull request ID").option("--path <p>", "Filter diff to a specific file path").action(async (opts) => {
4872
5098
  const start = Date.now();
4873
5099
  try {
4874
5100
  const { collection, project, repo: repo2, gitClient } = getAdoContext(ado, true);
4875
5101
  const data = await gitClient.getPRDiff(collection, project, repo2, parseInt(opts.pr, 10));
5102
+ if (opts.path) {
5103
+ if (opts.path) {
5104
+ const needle = opts.path.startsWith("/") ? opts.path : `/${opts.path}`;
5105
+ data.changes = (data.changes ?? []).filter((c) => c.item.path === needle);
5106
+ }
5107
+ }
4876
5108
  success(data, "ado", "repo-diff", start);
4877
5109
  } catch (err) {
4878
5110
  fail(err, "ado", "repo-diff", start);
@@ -4903,11 +5135,11 @@ function registerAdoRepoCommands(ado) {
4903
5135
  const start = Date.now();
4904
5136
  try {
4905
5137
  const globalOpts = ado.optsWithGlobals();
4906
- const { loadConfig: lc } = await import("./config-IJ57IIQA.js");
5138
+ const { loadConfig: lc } = await import("./config-PXL632ZX.js");
4907
5139
  const config = lc({ configPath: globalOpts.config });
4908
5140
  const { collection, project, repo: repo2, gitClient } = getAdoContext(ado, true);
4909
- const { AdoCoreClient: AdoCoreClient2 } = await import("./core-AJYPJ2Z7.js");
4910
- const { createHttpClient: createHttpClient2 } = await import("./http-FC24JSK7.js");
5141
+ const { AdoCoreClient: AdoCoreClient2 } = await import("./core-SWYVXOQ4.js");
5142
+ const { createHttpClient: createHttpClient2 } = await import("./http-TID3W235.js");
4911
5143
  const http = createHttpClient2(config, Boolean(globalOpts.dryRun));
4912
5144
  const coreClient = new AdoCoreClient2(http);
4913
5145
  const me = await coreClient.getConnectionData(collection);
@@ -5035,7 +5267,7 @@ function registerAdoPipelineCommands(ado) {
5035
5267
  fail(err, "ado", "pipeline-cancel-run", start);
5036
5268
  }
5037
5269
  });
5038
- pipeline.command("logs").description("List or retrieve build logs").requiredOption("--id <n>", "Build ID").option("--log-id <n>", "Specific log ID (omit to list all logs)").action(async (opts) => {
5270
+ pipeline.command("logs").description("List or retrieve build logs (consider --output-file for large logs)").requiredOption("--id <n>", "Build ID").option("--log-id <n>", "Specific log ID (omit to list all logs)").action(async (opts) => {
5039
5271
  const start = Date.now();
5040
5272
  try {
5041
5273
  const { collection, project, buildClient } = getAdoContext(ado);
@@ -5082,18 +5314,725 @@ function registerAdoCommands(program2) {
5082
5314
  registerAdoPipelineCommands(ado);
5083
5315
  }
5084
5316
 
5317
+ // src/services/jenkins/client.ts
5318
+ function jobPath(name) {
5319
+ return name.split("/").map(encodeURIComponent).join("/job/");
5320
+ }
5321
+ var JenkinsClient = class {
5322
+ constructor(http) {
5323
+ this.http = http;
5324
+ }
5325
+ http;
5326
+ async listJobs() {
5327
+ const data = await this.http.jenkins(
5328
+ "/api/json",
5329
+ { params: { tree: "jobs[name,url,color,description,buildable,lastBuild[number,url,result]]" } }
5330
+ );
5331
+ return data.jobs;
5332
+ }
5333
+ async getJob(name) {
5334
+ return this.http.jenkins(
5335
+ `/job/${jobPath(name)}/api/json`,
5336
+ { params: { tree: "name,url,color,description,buildable,nextBuildNumber,inQueue,lastBuild[number,url,result],builds[number,url,result,duration,timestamp,building,displayName]{0,10}" } }
5337
+ );
5338
+ }
5339
+ async triggerBuild(name, params) {
5340
+ const hasParams = params && Object.keys(params).length > 0;
5341
+ const endpoint = hasParams ? `/job/${jobPath(name)}/buildWithParameters` : `/job/${jobPath(name)}/build`;
5342
+ const res = await this.http.jenkinsRaw(endpoint, {
5343
+ method: "POST",
5344
+ params: hasParams ? params : void 0
5345
+ });
5346
+ const location = res.headers.get("Location") ?? "";
5347
+ const match = location.match(/\/queue\/item\/(\d+)\/?$/);
5348
+ if (!match) throw new PncliError("Build queued but Jenkins did not return a Location header \u2014 cannot track queue item");
5349
+ const queueItemId = parseInt(match[1], 10);
5350
+ return { queueItemId };
5351
+ }
5352
+ async getQueueItem(queueItemId) {
5353
+ return this.http.jenkins(`/queue/item/${queueItemId}/api/json`);
5354
+ }
5355
+ async listBuilds(name, top = 25) {
5356
+ const data = await this.http.jenkins(
5357
+ `/job/${jobPath(name)}/api/json`,
5358
+ { params: { tree: `builds[number,url,result,duration,timestamp,building,displayName,fullDisplayName,description,id,queueId]{0,${top}}` } }
5359
+ );
5360
+ return data.builds ?? [];
5361
+ }
5362
+ async getBuild(name, buildNumber) {
5363
+ return this.http.jenkins(
5364
+ `/job/${jobPath(name)}/${buildNumber}/api/json`
5365
+ );
5366
+ }
5367
+ async getConsoleLog(name, buildNumber) {
5368
+ const res = await this.http.jenkinsRaw(
5369
+ `/job/${jobPath(name)}/${buildNumber}/consoleText`,
5370
+ { method: "GET" }
5371
+ );
5372
+ return res.text;
5373
+ }
5374
+ };
5375
+
5376
+ // src/services/jenkins/commands.ts
5377
+ function getClient6(program2) {
5378
+ const opts = program2.optsWithGlobals();
5379
+ const config = loadConfig({ configPath: opts.config });
5380
+ if (!config.jenkins.baseUrl) throw new PncliError("Jenkins not configured. Run: pncli config init");
5381
+ const http = createHttpClient(config, Boolean(opts.dryRun));
5382
+ return new JenkinsClient(http);
5383
+ }
5384
+ async function pollQueueItem(client, queueItemId, pollMs, timeoutMs) {
5385
+ const deadline = Date.now() + timeoutMs;
5386
+ while (Date.now() < deadline) {
5387
+ const item = await client.getQueueItem(queueItemId);
5388
+ if (item.executable?.number !== void 0) return item.executable.number;
5389
+ if (item.why === null && !item.blocked && !item.buildable) {
5390
+ throw new PncliError("Build was cancelled while in queue", 1);
5391
+ }
5392
+ await new Promise((r) => setTimeout(r, pollMs));
5393
+ }
5394
+ throw new PncliError(`Timed out waiting for build to leave queue after ${timeoutMs / 1e3}s`, 1);
5395
+ }
5396
+ async function pollBuildComplete(client, jobName, buildNumber, pollMs, timeoutMs) {
5397
+ const deadline = Date.now() + timeoutMs;
5398
+ while (Date.now() < deadline) {
5399
+ const build = await client.getBuild(jobName, buildNumber);
5400
+ if (!build.building) return build;
5401
+ await new Promise((r) => setTimeout(r, pollMs));
5402
+ }
5403
+ throw new PncliError(`Timed out waiting for build #${buildNumber} to complete after ${timeoutMs / 1e3}s`, 1);
5404
+ }
5405
+ function registerJenkinsCommands(program2) {
5406
+ const jenkins = program2.command("jenkins").description("Jenkins Data Center operations");
5407
+ const pipeline = jenkins.command("pipeline").description("Pipeline (job/build) operations");
5408
+ pipeline.command("list").description("List all jobs on the Jenkins instance").action(async () => {
5409
+ const start = Date.now();
5410
+ try {
5411
+ const client = getClient6(program2);
5412
+ const data = await client.listJobs();
5413
+ success(data, "jenkins", "pipeline-list", start);
5414
+ } catch (err) {
5415
+ fail(err, "jenkins", "pipeline-list", start);
5416
+ }
5417
+ });
5418
+ pipeline.command("get").description("Get details for a specific job").requiredOption("--name <job>", "Job name").action(async (opts) => {
5419
+ const start = Date.now();
5420
+ try {
5421
+ const client = getClient6(program2);
5422
+ const data = await client.getJob(opts.name);
5423
+ success(data, "jenkins", "pipeline-get", start);
5424
+ } catch (err) {
5425
+ fail(err, "jenkins", "pipeline-get", start);
5426
+ }
5427
+ });
5428
+ pipeline.command("run").description("Trigger a build").requiredOption("--name <job>", "Job name").option("--parameter <k=v>", "Build parameter (repeatable)", (v, acc) => {
5429
+ acc.push(v);
5430
+ return acc;
5431
+ }, []).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) => {
5432
+ const start = Date.now();
5433
+ try {
5434
+ const client = getClient6(program2);
5435
+ for (const p of opts.parameter) {
5436
+ if (!p.includes("=")) throw new PncliError(`Invalid --parameter "${p}". Expected format: key=value`, 1);
5437
+ }
5438
+ const params = opts.parameter.length > 0 ? Object.fromEntries(opts.parameter.map((p) => {
5439
+ const eq = p.indexOf("=");
5440
+ return [p.slice(0, eq), p.slice(eq + 1)];
5441
+ })) : void 0;
5442
+ const { queueItemId } = await client.triggerBuild(opts.name, params);
5443
+ log(`Queued build (queue item #${queueItemId})`);
5444
+ if (!opts.wait) {
5445
+ success({ queueItemId }, "jenkins", "pipeline-run", start);
5446
+ return;
5447
+ }
5448
+ const timeoutSec = parseInt(opts.timeout, 10);
5449
+ const pollSec = parseInt(opts.poll, 10);
5450
+ if (isNaN(timeoutSec) || timeoutSec <= 0) throw new PncliError(`Invalid --timeout "${opts.timeout}". Expected a positive number of seconds.`, 1);
5451
+ if (isNaN(pollSec) || pollSec <= 0) throw new PncliError(`Invalid --poll "${opts.poll}". Expected a positive number of seconds.`, 1);
5452
+ log(`Waiting for build to start (timeout ${timeoutSec}s)...`);
5453
+ const buildNumber = await pollQueueItem(client, queueItemId, pollSec * 1e3, timeoutSec * 1e3);
5454
+ log(`Build #${buildNumber} started \u2014 waiting for completion...`);
5455
+ const build = await pollBuildComplete(client, opts.name, buildNumber, pollSec * 1e3, timeoutSec * 1e3);
5456
+ if (build.result !== null && build.result !== "SUCCESS") {
5457
+ process.exitCode = ExitCode.GENERAL_ERROR;
5458
+ }
5459
+ success(build, "jenkins", "pipeline-run", start);
5460
+ } catch (err) {
5461
+ fail(err, "jenkins", "pipeline-run", start);
5462
+ }
5463
+ });
5464
+ pipeline.command("list-runs").description("List recent builds for a job").requiredOption("--name <job>", "Job name").option("--top <n>", "Maximum number of builds to return (default 25)", "25").action(async (opts) => {
5465
+ const start = Date.now();
5466
+ try {
5467
+ const client = getClient6(program2);
5468
+ const top = parseInt(opts.top, 10);
5469
+ if (isNaN(top) || top <= 0) throw new PncliError(`Invalid --top "${opts.top}". Expected a positive integer.`, 1);
5470
+ const data = await client.listBuilds(opts.name, top);
5471
+ success(data, "jenkins", "pipeline-list-runs", start);
5472
+ } catch (err) {
5473
+ fail(err, "jenkins", "pipeline-list-runs", start);
5474
+ }
5475
+ });
5476
+ pipeline.command("get-run").description("Get details for a specific build").requiredOption("--name <job>", "Job name").requiredOption("--number <n>", "Build number").action(async (opts) => {
5477
+ const start = Date.now();
5478
+ try {
5479
+ const client = getClient6(program2);
5480
+ const buildNumber = parseInt(opts.number, 10);
5481
+ if (isNaN(buildNumber)) throw new PncliError(`Invalid --number "${opts.number}". Expected an integer.`, 1);
5482
+ const data = await client.getBuild(opts.name, buildNumber);
5483
+ success(data, "jenkins", "pipeline-get-run", start);
5484
+ } catch (err) {
5485
+ fail(err, "jenkins", "pipeline-get-run", start);
5486
+ }
5487
+ });
5488
+ pipeline.command("logs").description("Fetch console log for a build (consider --output-file for large logs)").requiredOption("--name <job>", "Job name").requiredOption("--number <n>", "Build number").action(async (opts) => {
5489
+ const start = Date.now();
5490
+ try {
5491
+ const client = getClient6(program2);
5492
+ const buildNumber = parseInt(opts.number, 10);
5493
+ if (isNaN(buildNumber)) throw new PncliError(`Invalid --number "${opts.number}". Expected an integer.`, 1);
5494
+ const text = await client.getConsoleLog(opts.name, buildNumber);
5495
+ success({ log: text }, "jenkins", "pipeline-logs", start);
5496
+ } catch (err) {
5497
+ fail(err, "jenkins", "pipeline-logs", start);
5498
+ }
5499
+ });
5500
+ }
5501
+
5502
+ // src/services/artifactory/client.ts
5503
+ var API9 = "api";
5504
+ var ArtifactoryClient = class {
5505
+ constructor(http) {
5506
+ this.http = http;
5507
+ }
5508
+ http;
5509
+ async ping() {
5510
+ return this.http.artifactoryText(`${API9}/system/ping`);
5511
+ }
5512
+ async listRepos(opts = {}) {
5513
+ return this.http.artifactory(`${API9}/repositories`, {
5514
+ params: {
5515
+ type: opts.type,
5516
+ packageType: opts.packageType
5517
+ }
5518
+ });
5519
+ }
5520
+ async getStorageInfo(repo, path6) {
5521
+ const cleanPath = path6.startsWith("/") ? path6.slice(1) : path6;
5522
+ return this.http.artifactory(`${API9}/storage/${repo}/${cleanPath}`);
5523
+ }
5524
+ async getProperties(repo, path6) {
5525
+ const cleanPath = path6.startsWith("/") ? path6.slice(1) : path6;
5526
+ return this.http.artifactory(`${API9}/storage/${repo}/${cleanPath}`, {
5527
+ params: { properties: "" }
5528
+ });
5529
+ }
5530
+ async setProperties(repo, path6, properties, opts = {}) {
5531
+ const cleanPath = path6.startsWith("/") ? path6.slice(1) : path6;
5532
+ const propString = Object.entries(properties).map(([k, v]) => `${k}=${v}`).join(";");
5533
+ await this.http.artifactory(`${API9}/storage/${repo}/${cleanPath}`, {
5534
+ method: "PUT",
5535
+ params: {
5536
+ properties: propString,
5537
+ ...opts.recursive ? { recursive: 1 } : {}
5538
+ }
5539
+ });
5540
+ }
5541
+ async searchAql(opts = {}) {
5542
+ const criteria = [];
5543
+ if (opts.repo) criteria.push(`"repo": {"$eq": "${opts.repo}"}`);
5544
+ if (opts.name) criteria.push(`"name": {"$match": "${opts.name}"}`);
5545
+ if (opts.path) criteria.push(`"path": {"$match": "${opts.path}"}`);
5546
+ if (opts.after) criteria.push(`"created": {"$gt": "${opts.after}"}`);
5547
+ if (opts.before) criteria.push(`"created": {"$lt": "${opts.before}"}`);
5548
+ const findClause = criteria.length > 0 ? `{${criteria.join(",")}}` : "{}";
5549
+ const includeFields = opts.includeProperties ? '.include("repo","path","name","type","size","created","modified","sha256","properties")' : '.include("repo","path","name","type","size","created","modified","sha256")';
5550
+ const sortClause = '.sort({"$desc": ["created"]})';
5551
+ const limitClause = opts.limit ? `.limit(${opts.limit})` : ".limit(100)";
5552
+ const aql = `items.find(${findClause})${includeFields}${sortClause}${limitClause}`;
5553
+ return this.http.artifactory(`${API9}/search/aql`, {
5554
+ method: "POST",
5555
+ body: aql,
5556
+ headers: { "Content-Type": "text/plain" }
5557
+ });
5558
+ }
5559
+ async listBuilds() {
5560
+ return this.http.artifactory(`${API9}/build`);
5561
+ }
5562
+ async listBuildRuns(buildName) {
5563
+ return this.http.artifactory(`${API9}/build/${encodeURIComponent(buildName)}`);
5564
+ }
5565
+ async getBuildInfo(buildName, buildNumber) {
5566
+ return this.http.artifactory(
5567
+ `${API9}/build/${encodeURIComponent(buildName)}/${encodeURIComponent(buildNumber)}`
5568
+ );
5569
+ }
5570
+ };
5571
+
5572
+ // src/services/artifactory/commands.ts
5573
+ function getClient7(program2) {
5574
+ const opts = program2.optsWithGlobals();
5575
+ const config = loadConfig({ configPath: opts.config });
5576
+ if (!config.artifactory.baseUrl) throw new PncliError("Artifactory not configured. Run: pncli config init");
5577
+ if (!config.artifactory.token) throw new PncliError("Artifactory token not configured. Run: pncli config init");
5578
+ const http = createHttpClient(config, Boolean(opts.dryRun));
5579
+ return new ArtifactoryClient(http);
5580
+ }
5581
+ function registerArtifactoryCommands(program2) {
5582
+ const art = program2.command("artifactory").description("Artifactory operations");
5583
+ art.command("ping").description("Check Artifactory connectivity and authentication").action(async () => {
5584
+ const start = Date.now();
5585
+ try {
5586
+ const client = getClient7(program2);
5587
+ const data = await client.ping();
5588
+ success(data, "artifactory", "ping", start);
5589
+ } catch (err) {
5590
+ fail(err, "artifactory", "ping", start);
5591
+ }
5592
+ });
5593
+ 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) => {
5594
+ const start = Date.now();
5595
+ try {
5596
+ const client = getClient7(program2);
5597
+ const data = await client.listRepos({ type: opts.type, packageType: opts.packageType });
5598
+ success(data, "artifactory", "repos", start);
5599
+ } catch (err) {
5600
+ fail(err, "artifactory", "repos", start);
5601
+ }
5602
+ });
5603
+ art.command("artifact-info <repo> <path>").description("Get metadata for an artifact or folder (checksums, size, timestamps)").action(async (repo, path6) => {
5604
+ const start = Date.now();
5605
+ try {
5606
+ const client = getClient7(program2);
5607
+ const data = await client.getStorageInfo(repo, path6);
5608
+ success(data, "artifactory", "artifact-info", start);
5609
+ } catch (err) {
5610
+ fail(err, "artifactory", "artifact-info", start);
5611
+ }
5612
+ });
5613
+ art.command("properties-get <repo> <path>").description("Read custom properties on an artifact").action(async (repo, path6) => {
5614
+ const start = Date.now();
5615
+ try {
5616
+ const client = getClient7(program2);
5617
+ const data = await client.getProperties(repo, path6);
5618
+ success(data, "artifactory", "properties-get", start);
5619
+ } catch (err) {
5620
+ fail(err, "artifactory", "properties-get", start);
5621
+ }
5622
+ });
5623
+ art.command("properties-set <repo> <path> [keyvals...]").description("Set custom properties on an artifact (format: key=value key=value...)").option("--recursive", "Apply recursively to folder contents", false).action(async (repo, path6, keyvals, opts) => {
5624
+ const start = Date.now();
5625
+ try {
5626
+ if (!keyvals.length) throw new PncliError("Provide at least one key=value pair");
5627
+ const properties = {};
5628
+ for (const kv of keyvals) {
5629
+ const eq = kv.indexOf("=");
5630
+ if (eq < 1) throw new PncliError(`Invalid property format: "${kv}" \u2014 expected key=value`);
5631
+ properties[kv.slice(0, eq)] = kv.slice(eq + 1);
5632
+ }
5633
+ const client = getClient7(program2);
5634
+ await client.setProperties(repo, path6, properties, { recursive: opts.recursive });
5635
+ success({ repo, path: path6, properties }, "artifactory", "properties-set", start);
5636
+ } catch (err) {
5637
+ fail(err, "artifactory", "properties-set", start);
5638
+ }
5639
+ });
5640
+ art.command("search").description("Search artifacts using AQL (Artifactory Query Language) (consider --output-file for large results)").option("--repo <name>", "Filter by repository key").option("--name <pattern>", "Filter by artifact name (supports * and ? wildcards)").option("--path <pattern>", "Filter by artifact path (supports * and ? wildcards)").option("--after <date>", "Created after this date (ISO 8601, e.g. 2024-01-01)").option("--before <date>", "Created before this date (ISO 8601)").option("--limit <n>", "Maximum results to return (default: 100)", "100").option("--properties", "Include artifact properties in results", false).action(async (opts) => {
5641
+ const start = Date.now();
5642
+ try {
5643
+ if (!opts.repo && !opts.name && !opts.path) {
5644
+ throw new PncliError("Provide at least one filter: --repo, --name, or --path");
5645
+ }
5646
+ const client = getClient7(program2);
5647
+ const data = await client.searchAql({
5648
+ repo: opts.repo,
5649
+ name: opts.name,
5650
+ path: opts.path,
5651
+ after: opts.after,
5652
+ before: opts.before,
5653
+ limit: Number.isNaN(parseInt(opts.limit, 10)) ? 100 : parseInt(opts.limit, 10),
5654
+ includeProperties: opts.properties
5655
+ });
5656
+ success(data, "artifactory", "search", start);
5657
+ } catch (err) {
5658
+ fail(err, "artifactory", "search", start);
5659
+ }
5660
+ });
5661
+ art.command("builds").description("List all build names in Artifactory").action(async () => {
5662
+ const start = Date.now();
5663
+ try {
5664
+ const client = getClient7(program2);
5665
+ const data = await client.listBuilds();
5666
+ success(data, "artifactory", "builds", start);
5667
+ } catch (err) {
5668
+ fail(err, "artifactory", "builds", start);
5669
+ }
5670
+ });
5671
+ art.command("build-runs <name>").description("List all run numbers for a build").action(async (name) => {
5672
+ const start = Date.now();
5673
+ try {
5674
+ const client = getClient7(program2);
5675
+ const data = await client.listBuildRuns(name);
5676
+ success(data, "artifactory", "build-runs", start);
5677
+ } catch (err) {
5678
+ fail(err, "artifactory", "build-runs", start);
5679
+ }
5680
+ });
5681
+ art.command("build-info <name> <number>").description("Get build info (artifacts, dependencies, VCS revision) for a specific run").action(async (name, number) => {
5682
+ const start = Date.now();
5683
+ try {
5684
+ const client = getClient7(program2);
5685
+ const data = await client.getBuildInfo(name, number);
5686
+ success(data, "artifactory", "build-info", start);
5687
+ } catch (err) {
5688
+ fail(err, "artifactory", "build-info", start);
5689
+ }
5690
+ });
5691
+ }
5692
+
5693
+ // src/services/udeploy/client.ts
5694
+ var UdeployClient = class {
5695
+ constructor(http) {
5696
+ this.http = http;
5697
+ }
5698
+ http;
5699
+ listApplications() {
5700
+ return this.http.udeploy("/cli/application");
5701
+ }
5702
+ listEnvironments(application) {
5703
+ return this.http.udeploy("/cli/application/environmentsInApplication", {
5704
+ params: { application }
5705
+ });
5706
+ }
5707
+ listComponents() {
5708
+ return this.http.udeploy("/cli/component");
5709
+ }
5710
+ listVersions(component) {
5711
+ return this.http.udeploy("/cli/component/versions", {
5712
+ params: { component }
5713
+ });
5714
+ }
5715
+ createVersion(component, name) {
5716
+ return this.http.udeploy("/cli/version/createVersion", {
5717
+ method: "POST",
5718
+ params: { component, name }
5719
+ });
5720
+ }
5721
+ finishImporting(component, version) {
5722
+ return this.http.udeploy("/cli/version/finishedImporting", {
5723
+ method: "POST",
5724
+ params: { component, version }
5725
+ });
5726
+ }
5727
+ runProcess(opts) {
5728
+ return this.http.udeploy("/cli/applicationProcessRequest/request", {
5729
+ method: "PUT",
5730
+ body: {
5731
+ application: opts.application,
5732
+ applicationProcess: opts.applicationProcess,
5733
+ environment: opts.environment,
5734
+ onlyChanged: opts.onlyChanged ?? false,
5735
+ ...opts.versions ? { versions: opts.versions } : {},
5736
+ ...opts.snapshot ? { snapshot: opts.snapshot } : {},
5737
+ ...opts.properties ? { properties: opts.properties } : {}
5738
+ }
5739
+ });
5740
+ }
5741
+ getRequestStatus(requestId) {
5742
+ return this.http.udeploy("/cli/applicationProcessRequest/requestStatus", {
5743
+ params: { requestId }
5744
+ });
5745
+ }
5746
+ getRequestInfo(requestId) {
5747
+ return this.http.udeploy("/cli/applicationProcessRequest/requestInfo", {
5748
+ params: { requestId }
5749
+ });
5750
+ }
5751
+ };
5752
+
5753
+ // src/services/udeploy/commands.ts
5754
+ var POLL_INTERVAL_MS = 5e3;
5755
+ var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
5756
+ function getClient8(program2) {
5757
+ const opts = program2.optsWithGlobals();
5758
+ const config = loadConfig({ configPath: opts.config });
5759
+ if (!config.udeploy.baseUrl) {
5760
+ throw new PncliError("UDeploy baseUrl not configured. Run: pncli config init", 78);
5761
+ }
5762
+ const http = createHttpClient(config, opts.dryRun);
5763
+ return { client: new UdeployClient(http), config };
5764
+ }
5765
+ function resolveApplication(config, cliValue) {
5766
+ const app = cliValue ?? config.defaults.udeploy.application;
5767
+ if (!app) throw new PncliError("--application is required (or set defaults.udeploy.application in config)", 2);
5768
+ return app;
5769
+ }
5770
+ function resolveEnvironment(config, cliValue) {
5771
+ const env = cliValue ?? config.defaults.udeploy.environment;
5772
+ if (!env) throw new PncliError("--environment is required (or set defaults.udeploy.environment in config)", 2);
5773
+ return env;
5774
+ }
5775
+ function registerUdeployCommands(program2) {
5776
+ const udeploy = program2.command("udeploy").description("IBM UrbanCode Deploy \u2014 component versions and deployment processes").option("--application <name>", "Application name or ID").option("--environment <name>", "Environment name or ID");
5777
+ udeploy.command("apps").description("List all applications").action(async () => {
5778
+ const start = Date.now();
5779
+ try {
5780
+ const { client } = getClient8(program2);
5781
+ const apps = await client.listApplications();
5782
+ success(apps, "udeploy", "apps", start);
5783
+ } catch (err) {
5784
+ fail(err, "udeploy", "apps", start);
5785
+ }
5786
+ });
5787
+ udeploy.command("environments").description("List environments for an application").action(async () => {
5788
+ const start = Date.now();
5789
+ try {
5790
+ const { client, config } = getClient8(program2);
5791
+ const parentOpts = udeploy.opts();
5792
+ const application = resolveApplication(config, parentOpts.application);
5793
+ const envs = await client.listEnvironments(application);
5794
+ success(envs, "udeploy", "environments", start);
5795
+ } catch (err) {
5796
+ fail(err, "udeploy", "environments", start);
5797
+ }
5798
+ });
5799
+ udeploy.command("components").description("List all components").action(async () => {
5800
+ const start = Date.now();
5801
+ try {
5802
+ const { client } = getClient8(program2);
5803
+ const components = await client.listComponents();
5804
+ success(components, "udeploy", "components", start);
5805
+ } catch (err) {
5806
+ fail(err, "udeploy", "components", start);
5807
+ }
5808
+ });
5809
+ udeploy.command("versions").description("List versions for a component").requiredOption("--component <name>", "Component name or ID").action(async (cmdOpts) => {
5810
+ const start = Date.now();
5811
+ try {
5812
+ const { client } = getClient8(program2);
5813
+ const versions = await client.listVersions(cmdOpts.component);
5814
+ success(versions, "udeploy", "versions", start);
5815
+ } catch (err) {
5816
+ fail(err, "udeploy", "versions", start);
5817
+ }
5818
+ });
5819
+ 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) => {
5820
+ const start = Date.now();
5821
+ try {
5822
+ const { client } = getClient8(program2);
5823
+ const created = await client.createVersion(cmdOpts.component, cmdOpts.name);
5824
+ if (cmdOpts.finish) {
5825
+ await client.finishImporting(cmdOpts.component, cmdOpts.name);
5826
+ }
5827
+ success({ ...created, finishedImporting: cmdOpts.finish }, "udeploy", "import-version", start);
5828
+ } catch (err) {
5829
+ fail(err, "udeploy", "import-version", start);
5830
+ }
5831
+ });
5832
+ 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) => {
5833
+ const start = Date.now();
5834
+ try {
5835
+ const { client, config } = getClient8(program2);
5836
+ const parentOpts = udeploy.opts();
5837
+ const application = resolveApplication(config, parentOpts.application);
5838
+ const environment = resolveEnvironment(config, parentOpts.environment);
5839
+ const components = cmdOpts.component;
5840
+ const versions = cmdOpts.componentVersion;
5841
+ const versionEntries = components.map((c, i) => ({
5842
+ component: c,
5843
+ version: versions[i] ?? "latest"
5844
+ }));
5845
+ const result = await client.runProcess({
5846
+ application,
5847
+ applicationProcess: cmdOpts.process,
5848
+ environment,
5849
+ onlyChanged: cmdOpts.onlyChanged,
5850
+ ...versionEntries.length > 0 ? { versions: versionEntries } : {},
5851
+ ...cmdOpts.snapshot ? { snapshot: cmdOpts.snapshot } : {}
5852
+ });
5853
+ if (!cmdOpts.wait) {
5854
+ success(result, "udeploy", "run", start);
5855
+ return;
5856
+ }
5857
+ const timeoutMs = parseInt(cmdOpts.timeout, 10);
5858
+ const requestId = result.requestId;
5859
+ log(`Process started. Request ID: ${requestId}. Polling for completion...`);
5860
+ while (true) {
5861
+ const elapsed = Date.now() - start;
5862
+ if (elapsed >= timeoutMs) {
5863
+ throw new PncliError(`Timed out after ${timeoutMs}ms waiting for request ${requestId}`, 1);
5864
+ }
5865
+ await sleep(POLL_INTERVAL_MS);
5866
+ const status = await client.getRequestStatus(requestId);
5867
+ if (status.status === "CLOSED") {
5868
+ const outcome = { requestId, status: status.status, result: status.result, elapsed_ms: Date.now() - start };
5869
+ if (status.result === "SUCCEEDED") {
5870
+ success(outcome, "udeploy", "run", start);
5871
+ } else {
5872
+ throw new PncliError(
5873
+ `Deployment ${status.result ?? "failed"} (requestId: ${requestId})`,
5874
+ 1
5875
+ );
5876
+ }
5877
+ return;
5878
+ }
5879
+ log(`Status: ${status.status} (${Math.round((Date.now() - start) / 1e3)}s elapsed)`);
5880
+ }
5881
+ } catch (err) {
5882
+ fail(err, "udeploy", "run", start);
5883
+ }
5884
+ });
5885
+ 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) => {
5886
+ const start = Date.now();
5887
+ try {
5888
+ const { client } = getClient8(program2);
5889
+ const status = await client.getRequestStatus(cmdOpts.requestId);
5890
+ success(status, "udeploy", "request-status", start);
5891
+ } catch (err) {
5892
+ fail(err, "udeploy", "request-status", start);
5893
+ }
5894
+ });
5895
+ 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) => {
5896
+ const start = Date.now();
5897
+ try {
5898
+ const { client } = getClient8(program2);
5899
+ const info = await client.getRequestInfo(cmdOpts.requestId);
5900
+ success(info, "udeploy", "request-info", start);
5901
+ } catch (err) {
5902
+ fail(err, "udeploy", "request-info", start);
5903
+ }
5904
+ });
5905
+ }
5906
+ function collect(val, prev) {
5907
+ return [...prev, val];
5908
+ }
5909
+ function sleep(ms) {
5910
+ return new Promise((resolve) => setTimeout(resolve, ms));
5911
+ }
5912
+
5913
+ // src/services/checkmarx/client.ts
5914
+ var CheckmarxClient = class {
5915
+ constructor(http) {
5916
+ this.http = http;
5917
+ }
5918
+ http;
5919
+ async listProjects() {
5920
+ return this.http.checkmarx("/cxrestapi/projects");
5921
+ }
5922
+ async getProject(id) {
5923
+ return this.http.checkmarx(`/cxrestapi/projects/${id}`);
5924
+ }
5925
+ async listScans(opts = {}) {
5926
+ return this.http.checkmarx("/cxrestapi/sast/scans", {
5927
+ params: {
5928
+ projectId: opts.projectId,
5929
+ last: opts.last
5930
+ }
5931
+ });
5932
+ }
5933
+ async getScan(id) {
5934
+ return this.http.checkmarx(`/cxrestapi/sast/scans/${id}`);
5935
+ }
5936
+ async getScanResultsStatistics(scanId) {
5937
+ return this.http.checkmarx(`/cxrestapi/sast/scans/${scanId}/resultsStatistics`);
5938
+ }
5939
+ };
5940
+
5941
+ // src/services/checkmarx/commands.ts
5942
+ function getClient9(program2) {
5943
+ const opts = program2.optsWithGlobals();
5944
+ const config = loadConfig({ configPath: opts.config });
5945
+ if (!config.checkmarx.baseUrl) throw new PncliError("Checkmarx not configured. Run: pncli config init");
5946
+ const http = createHttpClient(config, Boolean(opts.dryRun));
5947
+ return new CheckmarxClient(http);
5948
+ }
5949
+ function registerCheckmarxCommands(program2) {
5950
+ const cx = program2.command("checkmarx").description("Checkmarx CxSAST operations");
5951
+ const project = cx.command("project").description("Project operations");
5952
+ const scan = cx.command("scan").description("Scan operations");
5953
+ project.command("list").description("List all projects").action(async () => {
5954
+ const start = Date.now();
5955
+ try {
5956
+ const data = await getClient9(program2).listProjects();
5957
+ success(data, "checkmarx", "project-list", start);
5958
+ } catch (err) {
5959
+ fail(err, "checkmarx", "project-list", start);
5960
+ }
5961
+ });
5962
+ project.command("get").description("Get a project by ID").requiredOption("--id <id>", "Project ID").action(async (opts) => {
5963
+ const start = Date.now();
5964
+ try {
5965
+ const id = parseInt(opts.id, 10);
5966
+ if (isNaN(id)) throw new PncliError(`Invalid --id "${opts.id}". Expected an integer.`, 1);
5967
+ const data = await getClient9(program2).getProject(id);
5968
+ success(data, "checkmarx", "project-get", start);
5969
+ } catch (err) {
5970
+ fail(err, "checkmarx", "project-get", start);
5971
+ }
5972
+ });
5973
+ scan.command("list").description("List scans").option("--project <id>", "Filter by project ID").option("--last <n>", "Return only the last N scans per project").action(async (opts) => {
5974
+ const start = Date.now();
5975
+ try {
5976
+ const projectId = opts.project ? parseInt(opts.project, 10) : void 0;
5977
+ const last = opts.last ? parseInt(opts.last, 10) : void 0;
5978
+ if (opts.project && isNaN(projectId)) throw new PncliError(`Invalid --project "${opts.project}". Expected an integer.`, 1);
5979
+ if (opts.last && isNaN(last)) throw new PncliError(`Invalid --last "${opts.last}". Expected an integer.`, 1);
5980
+ const data = await getClient9(program2).listScans({ projectId, last });
5981
+ success(data, "checkmarx", "scan-list", start);
5982
+ } catch (err) {
5983
+ fail(err, "checkmarx", "scan-list", start);
5984
+ }
5985
+ });
5986
+ scan.command("get").description("Get a scan by ID").requiredOption("--id <id>", "Scan ID").action(async (opts) => {
5987
+ const start = Date.now();
5988
+ try {
5989
+ const id = parseInt(opts.id, 10);
5990
+ if (isNaN(id)) throw new PncliError(`Invalid --id "${opts.id}". Expected an integer.`, 1);
5991
+ const data = await getClient9(program2).getScan(id);
5992
+ success(data, "checkmarx", "scan-get", start);
5993
+ } catch (err) {
5994
+ fail(err, "checkmarx", "scan-get", start);
5995
+ }
5996
+ });
5997
+ scan.command("stats").description("Get results statistics for a scan").requiredOption("--id <id>", "Scan ID").action(async (opts) => {
5998
+ const start = Date.now();
5999
+ try {
6000
+ const id = parseInt(opts.id, 10);
6001
+ if (isNaN(id)) throw new PncliError(`Invalid --id "${opts.id}". Expected an integer.`, 1);
6002
+ const data = await getClient9(program2).getScanResultsStatistics(id);
6003
+ success(data, "checkmarx", "scan-stats", start);
6004
+ } catch (err) {
6005
+ fail(err, "checkmarx", "scan-stats", start);
6006
+ }
6007
+ });
6008
+ }
6009
+
5085
6010
  // src/services/skills/commands.ts
5086
6011
  import fs3 from "fs";
5087
6012
  import path5 from "path";
5088
- var GITHUB_API = "https://api.github.com/repos/kolatts/pncli/contents/.claude/skills";
5089
- var RAW_BASE = "https://raw.githubusercontent.com/kolatts/pncli/main/.claude/skills";
5090
- var RAW_COPILOT_INSTRUCTIONS = "https://raw.githubusercontent.com/kolatts/pncli/main/copilot-instructions.md";
6013
+ import os from "os";
6014
+ var GITHUB_API = "https://api.github.com/repos/kolatts/pncli/contents/skills";
6015
+ var RAW_BASE = "https://raw.githubusercontent.com/kolatts/pncli/main/skills";
6016
+ var AGENT_PATHS = {
6017
+ "github-copilot": { project: ".agents/skills", user: path5.join(os.homedir(), ".copilot/skills") },
6018
+ "claude-code": { project: ".claude/skills", user: path5.join(os.homedir(), ".claude/skills") }
6019
+ };
5091
6020
  function registerSkillsCommands(program2) {
5092
6021
  const skills = program2.command("skills").description("Manage pncli Claude Code skills");
5093
- skills.command("install").description("Download latest pncli skills and copilot-instructions.md into the current repo").option("--target <dir>", "Target directory for skills", ".claude/skills").option("--skip-instructions", "Skip downloading copilot-instructions.md (always written to repo root)").action(async (opts) => {
6022
+ skills.command("install").description("Download latest 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(async (opts) => {
5094
6023
  const start = Date.now();
5095
6024
  try {
5096
- const targetDir = path5.resolve(opts.target);
6025
+ let targetDir;
6026
+ if (opts.target) {
6027
+ targetDir = path5.resolve(opts.target);
6028
+ } else {
6029
+ const agentConfig = AGENT_PATHS[opts.agent];
6030
+ if (!agentConfig) {
6031
+ throw new Error(`Unknown agent: "${opts.agent}". Use: ${Object.keys(AGENT_PATHS).join(" | ")}`);
6032
+ }
6033
+ const scopePath = opts.scope === "user" ? agentConfig.user : agentConfig.project;
6034
+ targetDir = path5.resolve(scopePath);
6035
+ }
5097
6036
  warn("Fetching skill list from GitHub...");
5098
6037
  const listRes = await fetch(GITHUB_API, {
5099
6038
  headers: { "Accept": "application/vnd.github.v3+json", "User-Agent": "pncli" }
@@ -5114,7 +6053,7 @@ function registerSkillsCommands(program2) {
5114
6053
  fs3.rmSync(existingDir, { recursive: true, force: true });
5115
6054
  }
5116
6055
  }
5117
- warn(`Found ${skillDirs.length} skills. Downloading...`);
6056
+ warn(`Found ${skillDirs.length} skills. Downloading to ${targetDir}...`);
5118
6057
  const installed = [];
5119
6058
  const failed = [];
5120
6059
  for (const skillName of skillDirs) {
@@ -5144,54 +6083,60 @@ function registerSkillsCommands(program2) {
5144
6083
  if (failed.length > 0) {
5145
6084
  warn(`Failed to download: ${failed.join(", ")}`);
5146
6085
  }
5147
- let instructionsWritten = false;
5148
- if (!opts.skipInstructions) {
5149
- warn("Downloading copilot-instructions.md...");
5150
- try {
5151
- const res = await fetch(RAW_COPILOT_INSTRUCTIONS, {
5152
- headers: { "User-Agent": "pncli" }
5153
- });
5154
- if (res.ok) {
5155
- const content = await res.text();
5156
- fs3.writeFileSync("copilot-instructions.md", content, "utf8");
5157
- instructionsWritten = true;
5158
- warn("Wrote copilot-instructions.md");
5159
- } else {
5160
- warn(`Failed to download copilot-instructions.md (${res.status})`);
5161
- }
5162
- } catch {
5163
- warn("Failed to download copilot-instructions.md");
5164
- }
5165
- }
5166
6086
  success({
5167
6087
  installed,
5168
6088
  failed,
5169
6089
  target: targetDir,
5170
6090
  total: installed.length,
5171
- copilotInstructions: instructionsWritten
6091
+ agent: opts.target ? "custom" : opts.agent,
6092
+ scope: opts.target ? "custom" : opts.scope
5172
6093
  }, "skills", "install", start);
5173
6094
  } catch (err) {
5174
6095
  fail(err, "skills", "install", start);
5175
6096
  }
5176
6097
  });
5177
- skills.command("list").description("List locally installed skills").option("--target <dir>", "Skills directory to scan", ".claude/skills").action((opts) => {
6098
+ skills.command("list").description("List locally installed skills").option("--agent <agent>", "Target agent host: github-copilot | claude-code", "github-copilot").option("--scope <scope>", "Installation scope: project | user", "project").option("--target <dir>", "Override skills directory to scan").action((opts) => {
5178
6099
  const start = Date.now();
5179
6100
  try {
5180
- const targetDir = path5.resolve(opts.target);
6101
+ let targetDir;
6102
+ if (opts.target) {
6103
+ targetDir = path5.resolve(opts.target);
6104
+ } else {
6105
+ const agentConfig = AGENT_PATHS[opts.agent];
6106
+ if (!agentConfig) {
6107
+ throw new Error(`Unknown agent: "${opts.agent}". Use: ${Object.keys(AGENT_PATHS).join(" | ")}`);
6108
+ }
6109
+ const scopePath = opts.scope === "user" ? agentConfig.user : agentConfig.project;
6110
+ targetDir = path5.resolve(scopePath);
6111
+ }
5181
6112
  if (!fs3.existsSync(targetDir)) {
5182
- success({ skills: [], message: "No skills directory found. Run: pncli skills install" }, "skills", "list", start);
6113
+ success({ skills: [], message: `No skills directory found at ${targetDir}. Run: pncli skills install` }, "skills", "list", start);
5183
6114
  return;
5184
6115
  }
5185
6116
  const skillDirs = fs3.readdirSync(targetDir).filter((name) => {
5186
6117
  const skillPath = path5.join(targetDir, name, "SKILL.md");
5187
6118
  return fs3.existsSync(skillPath);
5188
6119
  });
5189
- const skills2 = skillDirs.map((name) => {
6120
+ const skillsList = skillDirs.map((name) => {
5190
6121
  const content = fs3.readFileSync(path5.join(targetDir, name, "SKILL.md"), "utf8");
5191
6122
  const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
5192
6123
  const data = {};
6124
+ const metadata = {};
5193
6125
  if (frontmatter) {
6126
+ let inMetadata = false;
5194
6127
  for (const line of frontmatter[1].split("\n")) {
6128
+ if (line.trimEnd() === "metadata:") {
6129
+ inMetadata = true;
6130
+ continue;
6131
+ }
6132
+ if (inMetadata && line.startsWith(" ")) {
6133
+ const colonIdx2 = line.indexOf(":");
6134
+ if (colonIdx2 !== -1) {
6135
+ metadata[line.slice(0, colonIdx2).trim()] = line.slice(colonIdx2 + 1).trim();
6136
+ }
6137
+ continue;
6138
+ }
6139
+ inMetadata = false;
5195
6140
  const colonIdx = line.indexOf(":");
5196
6141
  if (colonIdx === -1) continue;
5197
6142
  data[line.slice(0, colonIdx).trim()] = line.slice(colonIdx + 1).trim();
@@ -5200,12 +6145,13 @@ function registerSkillsCommands(program2) {
5200
6145
  return {
5201
6146
  name: data.name || name,
5202
6147
  slug: name,
5203
- category: data.category || "other",
5204
- services: data.services || "",
5205
- providers: data.providers || "none"
6148
+ category: metadata.category || data.category || "other",
6149
+ services: metadata.services || data.services || "",
6150
+ providers: metadata.providers || data.providers || "none",
6151
+ userInvocable: data["user-invocable"] === "true"
5206
6152
  };
5207
6153
  });
5208
- success({ skills: skills2, total: skills2.length }, "skills", "list", start);
6154
+ success({ skills: skillsList, total: skillsList.length }, "skills", "list", start);
5209
6155
  } catch (err) {
5210
6156
  fail(err, "skills", "list", start);
5211
6157
  }
@@ -5220,12 +6166,13 @@ var require2 = createRequire(import.meta.url);
5220
6166
  var pkg = require2("../package.json");
5221
6167
  var TAGLINE = "One command does what three meetings couldn't.";
5222
6168
  var program = new Command();
5223
- program.name("pncli").description(`The Paperwork Nightmare CLI \u2014 ${TAGLINE}`).version(`${pkg.version} \u2014 ${TAGLINE}`, "-v, --version").option("--pretty", "Human-readable formatted output", false).option("--verbose", "Include full response metadata", false).option("--dry-run", "Print API requests without executing", false).option("--config <path>", "Override global config file location");
6169
+ program.name("pncli").description(`The Paperwork Nightmare CLI \u2014 ${TAGLINE}`).version(`${pkg.version} \u2014 ${TAGLINE}`, "-v, --version").option("--pretty", "Human-readable formatted output", false).option("--verbose", "Include full response metadata", false).option("--dry-run", "Print API requests without executing", false).option("--config <path>", "Override global config file location").option("--output-file <path>", "Write JSON output to file instead of stdout");
5224
6170
  program.hook("preAction", (thisCommand) => {
5225
6171
  const opts = thisCommand.optsWithGlobals();
5226
6172
  setGlobalOptions({
5227
6173
  pretty: Boolean(opts.pretty),
5228
- verbose: Boolean(opts.verbose)
6174
+ verbose: Boolean(opts.verbose),
6175
+ outputFile: opts.outputFile
5229
6176
  });
5230
6177
  try {
5231
6178
  const config = loadConfig({ configPath: opts.config });
@@ -5242,6 +6189,10 @@ registerSdeCommands(program);
5242
6189
  registerDepsCommands(program);
5243
6190
  registerConfigCommands(program);
5244
6191
  registerAdoCommands(program);
6192
+ registerJenkinsCommands(program);
6193
+ registerArtifactoryCommands(program);
6194
+ registerUdeployCommands(program);
6195
+ registerCheckmarxCommands(program);
5245
6196
  registerSkillsCommands(program);
5246
6197
  program.addHelpText("after", `
5247
6198
  Services:
@@ -5253,6 +6204,10 @@ Services:
5253
6204
  sonar SonarQube Server (quality gates, issues, metrics, hotspots)
5254
6205
  sde SDElements (threat modeling, countermeasures, compliance)
5255
6206
  ado Azure DevOps Server (work items, repos, PRs, pipelines)
6207
+ jenkins Jenkins Data Center (jobs, builds, logs)
6208
+ artifactory Artifactory (repos, artifact search, build info, properties)
6209
+ udeploy IBM UrbanCode Deploy (component versions, deployment processes)
6210
+ checkmarx Checkmarx CxSAST (projects, scans, scan statistics)
5256
6211
  config Manage pncli configuration
5257
6212
  skills Download and manage Claude Code skills
5258
6213
  `);