@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.
@@ -4,7 +4,7 @@
4
4
 
5
5
  ## What is pncli?
6
6
 
7
- pncli is a CLI tool that provides structured JSON access to Jira, Bitbucket, Confluence, SonarQube, SDElements, and local git state. Use it for all interactions with these services. It exists because MCP servers aren't available in this environment — pncli is your agent-friendly shim layer.
7
+ pncli is a CLI tool that provides structured JSON access to Jira, Bitbucket, Confluence, SonarQube, SDElements, Azure DevOps Server, Jenkins, JFrog Artifactory, IBM UrbanCode Deploy, Checkmarx, and local git state. Use it for all interactions with these services. It exists because MCP servers aren't available in this environment — pncli is your agent-friendly shim layer.
8
8
 
9
9
  ## Important
10
10
 
@@ -30,9 +30,7 @@ pncli ships with Claude Code skills — step-by-step workflow guides that agents
30
30
  pncli skills install
31
31
  ```
32
32
 
33
- This downloads the latest pncli skills from GitHub into `.claude/skills/` and also fetches `copilot-instructions.md` into the repo root. Only pncli-managed skills are replaced — any custom skills you've created in `.claude/skills/` are left untouched.
34
-
35
- Use `--skip-instructions` to skip downloading `copilot-instructions.md`.
33
+ This downloads the latest pncli skills from GitHub. Default installs to `.agents/skills/` (GitHub Copilot). For Claude Code, add `--agent claude-code`. For user-scope, add `--scope user`. Only pncli-managed skills are replaced — any custom skills you've added are left untouched.
36
34
 
37
35
  To see what's installed locally:
38
36
 
@@ -42,7 +40,7 @@ pncli skills list
42
40
 
43
41
  ## Common Workflows
44
42
 
45
- > These workflows are also available as Claude Code skills — run `pncli skills install` to download them into your repo, or copy `.claude/skills/` into `~/.claude/skills/` for global access.
43
+ > These workflows are also available as skills — run `pncli skills install` (Copilot default) or `pncli skills install --agent claude-code` (Claude Code) to download them into your repo. Add `--scope user` for global access.
46
44
 
47
45
  ### Review a Pull Request
48
46
 
@@ -374,7 +372,8 @@ pncli sonar issues
374
372
  --resolved <bool> Filter resolved issues: true or false
375
373
  --page <n> Page number (1-based) (default: "1")
376
374
  --page-size <n> Results per page (max 500) (default: "100")
377
- --all Fetch all pages (ignores --page/--page-size)
375
+ --all Fetch all pages (consider --output-file for large
376
+ results; ignores --page/--page-size)
378
377
 
379
378
  pncli sonar measures
380
379
  --project <key> SonarQube project key (or set defaults.sonar.project in
@@ -387,7 +386,7 @@ pncli sonar projects
387
386
  --query <text> Search query
388
387
  --page <n> Page number (1-based) (default: "1")
389
388
  --page-size <n> Results per page (default: "100")
390
- --all Fetch all pages
389
+ --all Fetch all pages (consider --output-file for large results)
391
390
 
392
391
  pncli sonar hotspots
393
392
  --project <key> SonarQube project key (or set defaults.sonar.project in
@@ -397,7 +396,8 @@ pncli sonar hotspots
397
396
  --branch <name> Branch name
398
397
  --page <n> Page number (1-based) (default: "1")
399
398
  --page-size <n> Results per page (default: "100")
400
- --all Fetch all pages
399
+ --all Fetch all pages (consider --output-file for large
400
+ results)
401
401
  ```
402
402
 
403
403
  ### Sde
@@ -414,7 +414,8 @@ pncli sde users
414
414
  --active <bool> Filter by active status: true or false
415
415
  --page <n> Page number (1-based) (default: "1")
416
416
  --page-size <n> Results per page (default: "100")
417
- --all Fetch all pages
417
+ --all Fetch all pages (consider --output-file for large
418
+ results)
418
419
 
419
420
  pncli sde projects
420
421
  --name <name> Filter by project name
@@ -428,7 +429,7 @@ pncli sde projects
428
429
  task_counts,permissions
429
430
  --page <n> Page number (1-based) (default: "1")
430
431
  --page-size <n> Results per page (default: "100")
431
- --all Fetch all pages
432
+ --all Fetch all pages (consider --output-file for large results)
432
433
 
433
434
  pncli sde project
434
435
  --id <id> Project ID (or set defaults.sde.project in config)
@@ -455,7 +456,8 @@ pncli sde tasks
455
456
  how_tos,last_note,references,regulation_sections
456
457
  --page <n> Page number (1-based) (default: "1")
457
458
  --page-size <n> Results per page (default: "100")
458
- --all Fetch all pages
459
+ --all Fetch all pages (consider --output-file for large
460
+ results)
459
461
 
460
462
  pncli sde task
461
463
  --project <id> Project ID (or set defaults.sde.project in config)
@@ -475,7 +477,8 @@ pncli sde threats
475
477
  --component-id <id> Filter by component ID
476
478
  --page <n> Page number (1-based) (default: "1")
477
479
  --page-size <n> Results per page (default: "100")
478
- --all Fetch all pages
480
+ --all Fetch all pages (consider --output-file for large
481
+ results)
479
482
  ```
480
483
 
481
484
  ### Deps
@@ -530,6 +533,7 @@ pncli config set
530
533
  pncli config test
531
534
 
532
535
  pncli config check
536
+ --output <format> Output format: json or table (default: "json")
533
537
  ```
534
538
 
535
539
  ### Ado
@@ -548,16 +552,113 @@ pncli ado repo
548
552
  pncli ado pipeline
549
553
  ```
550
554
 
555
+ ### Jenkins
556
+
557
+ ```
558
+ pncli jenkins pipeline
559
+ ```
560
+
561
+ ### Artifactory
562
+
563
+ ```
564
+ pncli artifactory ping
565
+
566
+ pncli artifactory repos
567
+ --type <type> Filter by type: local, virtual, remote, federated
568
+ --package-type <type> Filter by package type: npm, maven, nuget, docker,
569
+ pypi, etc.
570
+
571
+ pncli artifactory artifact-info
572
+
573
+ pncli artifactory properties-get
574
+
575
+ pncli artifactory properties-set
576
+ --recursive Apply recursively to folder contents (default: false)
577
+
578
+ pncli artifactory search
579
+ --repo <name> Filter by repository key
580
+ --name <pattern> Filter by artifact name (supports * and ? wildcards)
581
+ --path <pattern> Filter by artifact path (supports * and ? wildcards)
582
+ --after <date> Created after this date (ISO 8601, e.g. 2024-01-01)
583
+ --before <date> Created before this date (ISO 8601)
584
+ --limit <n> Maximum results to return (default: 100) (default: "100")
585
+ --properties Include artifact properties in results (default: false)
586
+
587
+ pncli artifactory builds
588
+
589
+ pncli artifactory build-runs
590
+
591
+ pncli artifactory build-info
592
+ ```
593
+
594
+ ### Udeploy
595
+
596
+ ```
597
+ pncli udeploy apps
598
+
599
+ pncli udeploy environments
600
+
601
+ pncli udeploy components
602
+
603
+ pncli udeploy versions
604
+ --component <name> Component name or ID
605
+
606
+ pncli udeploy import-version
607
+ --component <name> Component name or ID
608
+ --name <name> Version name
609
+ --no-finish Skip marking the version as finished importing
610
+
611
+ pncli udeploy run
612
+ --process <name> Application process name or ID
613
+ --component <name> Component name (repeatable) (default: [])
614
+ --component-version <name> Component version (repeatable; positionally paired
615
+ with --component) (default: [])
616
+ --snapshot <name> Snapshot name or ID (alternative to specifying
617
+ versions)
618
+ --only-changed Deploy only changed components (default: false)
619
+ --wait Poll until the process completes (default: false)
620
+ --timeout <ms> Max wait time in milliseconds (default: "600000")
621
+
622
+ pncli udeploy request-status
623
+ --request-id <id> Request ID returned by udeploy run
624
+
625
+ pncli udeploy request-info
626
+ --request-id <id> Request ID returned by udeploy run
627
+ ```
628
+
629
+ ### Checkmarx
630
+
631
+ ```
632
+ pncli checkmarx project list
633
+
634
+ pncli checkmarx project get
635
+ --id <id> Project ID (required)
636
+
637
+ pncli checkmarx scan list
638
+ --project <id> Filter by project ID
639
+ --last <n> Return only the last N scans per project
640
+
641
+ pncli checkmarx scan get
642
+ --id <id> Scan ID (required)
643
+
644
+ pncli checkmarx scan stats
645
+ --id <id> Scan ID (required)
646
+ ```
647
+
551
648
  ### Skills
552
649
 
553
650
  ```
554
651
  pncli skills install
555
- --target <dir> Target directory for skills (default: ".claude/skills")
556
- --skip-instructions Skip downloading copilot-instructions.md (always written
557
- to repo root)
652
+ --agent <agent> Target agent host: github-copilot | claude-code (default:
653
+ "github-copilot")
654
+ --scope <scope> Installation scope: project | user (default: "project")
655
+ --target <dir> Override install directory (ignores --agent and --scope)
558
656
 
559
657
  pncli skills list
560
- --target <dir> Skills directory to scan (default: ".claude/skills")
658
+ --agent <agent> Target agent host: github-copilot | claude-code (default:
659
+ "github-copilot")
660
+ --scope <scope> Installation scope: project | user (default: "project")
661
+ --target <dir> Override skills directory to scan
561
662
  ```
562
663
 
563
664
  <!-- COMMAND-REFERENCE:END -->
@@ -575,5 +676,6 @@ Always check `ok` before accessing `data`.
575
676
  - Use `--dry-run` to preview API calls without executing
576
677
  - Use `--verbose` to see full response metadata for debugging
577
678
  - Use `--pretty` when running manually for readable output
679
+ - Use `--output-file <path>` to write JSON output to a file instead of stdout — recommended for commands that produce large payloads (search, logs, `--all` pagination) to avoid flooding agent context
578
680
  - Pipe output through `jq` for ad-hoc filtering (if available)
579
681
  - Defaults from `.pncli.json` are applied automatically — you rarely need to pass `--project`, `--type`, or `--priority` flags for Jira
@@ -38,8 +38,10 @@ import fs from "fs";
38
38
  import chalk from "chalk";
39
39
  var globalOptions = { pretty: false, verbose: false };
40
40
  var globalUser = { email: void 0, userId: void 0 };
41
+ var outputFile;
41
42
  function setGlobalOptions(opts) {
42
- globalOptions = opts;
43
+ globalOptions = { pretty: opts.pretty, verbose: opts.verbose };
44
+ outputFile = opts.outputFile;
43
45
  }
44
46
  function setGlobalUser(user) {
45
47
  globalUser = user;
@@ -53,15 +55,29 @@ function buildMeta(service, action, startTime) {
53
55
  user: globalUser.email ?? globalUser.userId ? globalUser : void 0
54
56
  };
55
57
  }
58
+ function writeToFile(filePath, content) {
59
+ try {
60
+ fs.writeFileSync(filePath, content, "utf8");
61
+ } catch (writeErr) {
62
+ process.stderr.write(
63
+ `Warning: could not write to output file "${filePath}": ${writeErr.message}
64
+ `
65
+ );
66
+ process.exitCode = ExitCode.GENERAL_ERROR;
67
+ }
68
+ }
56
69
  function success(data, service, action, startTime) {
57
70
  const envelope = {
58
71
  ok: true,
59
72
  data,
60
73
  meta: buildMeta(service, action, startTime)
61
74
  };
62
- process.stdout.write(
63
- (globalOptions.pretty ? JSON.stringify(envelope, null, 2) : JSON.stringify(envelope)) + "\n"
64
- );
75
+ const out = (globalOptions.pretty ? JSON.stringify(envelope, null, 2) : JSON.stringify(envelope)) + "\n";
76
+ if (outputFile) {
77
+ writeToFile(outputFile, out);
78
+ } else {
79
+ process.stdout.write(out);
80
+ }
65
81
  }
66
82
  function fail(err, service, action, startTime) {
67
83
  const errorDetail = {
@@ -78,10 +94,14 @@ function fail(err, service, action, startTime) {
78
94
  if (msg) process.stderr.write(msg + "\n");
79
95
  const output = (globalOptions.pretty ? JSON.stringify(envelope, null, 2) : JSON.stringify(envelope)) + "\n";
80
96
  const exitCode = err instanceof PncliError ? exitCodeFromStatus(err.status) : ExitCode.GENERAL_ERROR;
81
- try {
82
- fs.writeSync(process.stdout.fd, output);
83
- } catch (writeErr) {
84
- if (writeErr.code !== "EPIPE") throw writeErr;
97
+ if (outputFile) {
98
+ writeToFile(outputFile, output);
99
+ } else {
100
+ try {
101
+ fs.writeSync(process.stdout.fd, output);
102
+ } catch (writeErr) {
103
+ if (writeErr.code !== "EPIPE") throw writeErr;
104
+ }
85
105
  }
86
106
  process.exitCode = exitCode;
87
107
  throw new PncliError(errorDetail.message, errorDetail.status);
@@ -124,6 +144,66 @@ async function buildAdoFetcher(config) {
124
144
  });
125
145
  }
126
146
 
147
+ // src/lib/checkmarxFetch.ts
148
+ var CX_CLIENT_ID = "resource_owner_client";
149
+ var CX_CLIENT_SECRET = "014DF517-39D1-4453-B7B3-9930C563627C";
150
+ var CX_SCOPE = "sast_api offline_access";
151
+ function buildCheckmarxFetcher(config) {
152
+ const { baseUrl, username, password } = config.checkmarx;
153
+ if (!baseUrl) throw new PncliError("Checkmarx baseUrl not configured. Run: pncli config init", 1);
154
+ if (!username) throw new PncliError("Checkmarx username not configured. Run: pncli config init", 1);
155
+ if (!password) throw new PncliError("Checkmarx password not configured. Run: pncli config init", 1);
156
+ const resolvedUsername = username;
157
+ const resolvedPassword = password;
158
+ const tokenUrl = `${baseUrl.replace(/\/$/, "")}/cxrestapi/auth/identity/connect/token`;
159
+ let cache = null;
160
+ async function getToken() {
161
+ const now = Date.now();
162
+ if (cache && cache.expiresAt - now > 6e4) {
163
+ return cache.value;
164
+ }
165
+ const body = new URLSearchParams({
166
+ grant_type: "password",
167
+ client_id: CX_CLIENT_ID,
168
+ client_secret: CX_CLIENT_SECRET,
169
+ scope: CX_SCOPE,
170
+ username: resolvedUsername,
171
+ password: resolvedPassword
172
+ });
173
+ const response = await fetch(tokenUrl, {
174
+ method: "POST",
175
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
176
+ body: body.toString()
177
+ });
178
+ if (!response.ok) {
179
+ const text = await response.text().catch(() => "");
180
+ throw new PncliError(
181
+ `Checkmarx token exchange failed (${response.status}): ${text || response.statusText}`,
182
+ response.status
183
+ );
184
+ }
185
+ const data = await response.json();
186
+ if (!data.access_token) {
187
+ throw new PncliError("Checkmarx token endpoint returned no access_token", response.status);
188
+ }
189
+ cache = {
190
+ value: data.access_token,
191
+ expiresAt: now + data.expires_in * 1e3
192
+ };
193
+ return cache.value;
194
+ }
195
+ return async (url, init) => {
196
+ const token = await getToken();
197
+ return fetch(url, {
198
+ ...init,
199
+ headers: {
200
+ ...init?.headers,
201
+ "Authorization": `Bearer ${token}`
202
+ }
203
+ });
204
+ };
205
+ }
206
+
127
207
  // src/lib/http.ts
128
208
  function buildUrl(base, path, params) {
129
209
  const url = new URL(path, base.endsWith("/") ? base : base + "/");
@@ -207,6 +287,7 @@ var HttpClient = class {
207
287
  config;
208
288
  dryRun;
209
289
  adoFetcher = null;
290
+ checkmarxFetcher = null;
210
291
  constructor(config, dryRun = false) {
211
292
  this.config = config;
212
293
  this.dryRun = dryRun;
@@ -457,6 +538,172 @@ Headers: ${JSON.stringify(safeHeaders, null, 2)}
457
538
  }
458
539
  return results;
459
540
  }
541
+ jenkinsHeaders() {
542
+ const { username, apiToken } = this.config.jenkins;
543
+ if (!username || !apiToken) throw new PncliError("Jenkins credentials not configured. Run: pncli config init");
544
+ const creds = Buffer.from(`${username}:${apiToken}`).toString("base64");
545
+ return {
546
+ "Authorization": `Basic ${creds}`,
547
+ "Content-Type": "application/json",
548
+ "Accept": "application/json",
549
+ "Connection": "close"
550
+ };
551
+ }
552
+ artifactoryHeaders() {
553
+ const { token } = this.config.artifactory;
554
+ if (!token) throw new PncliError("Artifactory credentials not configured. Run: pncli config init");
555
+ return {
556
+ "Authorization": `Bearer ${token}`,
557
+ "Content-Type": "application/json",
558
+ "Accept": "application/json",
559
+ "Connection": "close"
560
+ };
561
+ }
562
+ udeployHeaders() {
563
+ const { pat, username, password } = this.config.udeploy;
564
+ let encoded;
565
+ if (username && password) {
566
+ encoded = Buffer.from(`${username}:${password}`).toString("base64");
567
+ } else if (username && pat) {
568
+ encoded = Buffer.from(`${username}:${pat}`).toString("base64");
569
+ } else if (pat) {
570
+ encoded = Buffer.from(`PasswordIsAuthToken:${JSON.stringify({ token: pat })}`).toString("base64");
571
+ } else {
572
+ throw new PncliError("UDeploy credentials not configured. Run: pncli config init");
573
+ }
574
+ return {
575
+ "Authorization": `Basic ${encoded}`,
576
+ "Content-Type": "application/json",
577
+ "Accept": "application/json",
578
+ "Connection": "close"
579
+ };
580
+ }
581
+ async jenkins(path, opts = {}) {
582
+ const baseUrl = this.config.jenkins.baseUrl;
583
+ if (!baseUrl) throw new PncliError("Jenkins baseUrl not configured. Run: pncli config init");
584
+ const url = buildUrl(baseUrl, path, opts.params);
585
+ const headers = this.jenkinsHeaders();
586
+ const init = {
587
+ method: opts.method ?? "GET",
588
+ headers,
589
+ body: opts.body !== void 0 ? JSON.stringify(opts.body) : void 0
590
+ };
591
+ if (this.dryRun) {
592
+ const safeHeaders = { ...headers, Authorization: "[REDACTED]" };
593
+ const msg = `DRY RUN: ${init.method} ${url}
594
+ Headers: ${JSON.stringify(safeHeaders, null, 2)}
595
+ ` + (opts.body ? `Body: ${JSON.stringify(opts.body, null, 2)}
596
+ ` : "");
597
+ fs2.writeSync(process.stderr.fd, msg);
598
+ process.exitCode = ExitCode.SUCCESS;
599
+ throw new PncliError("dry-run", 0);
600
+ }
601
+ return request(url, init, opts.timeoutMs ?? 3e4);
602
+ }
603
+ async jenkinsRaw(path, opts = {}) {
604
+ const baseUrl = this.config.jenkins.baseUrl;
605
+ if (!baseUrl) throw new PncliError("Jenkins baseUrl not configured. Run: pncli config init");
606
+ const url = buildUrl(baseUrl, path, opts.params);
607
+ const headers = this.jenkinsHeaders();
608
+ if (this.dryRun) {
609
+ const safeHeaders = { ...headers, Authorization: "[REDACTED]" };
610
+ const msg = `DRY RUN: ${opts.method ?? "GET"} ${url}
611
+ Headers: ${JSON.stringify(safeHeaders, null, 2)}
612
+ ` + (opts.body ? `Body: ${JSON.stringify(opts.body, null, 2)}
613
+ ` : "");
614
+ fs2.writeSync(process.stderr.fd, msg);
615
+ process.exitCode = ExitCode.SUCCESS;
616
+ throw new PncliError("dry-run", 0);
617
+ }
618
+ const response = await fetchWithTimeout(url, {
619
+ method: opts.method ?? "GET",
620
+ headers,
621
+ body: opts.body !== void 0 ? JSON.stringify(opts.body) : void 0
622
+ }, opts.timeoutMs ?? 3e4);
623
+ if (!response.ok) {
624
+ let message = `HTTP ${response.status} ${response.statusText}`;
625
+ try {
626
+ const parsed = JSON.parse(await response.text());
627
+ if (parsed.message) message = String(parsed.message);
628
+ } catch {
629
+ }
630
+ throw new PncliError(message, response.status, url);
631
+ }
632
+ return { status: response.status, headers: response.headers, text: await response.text() };
633
+ }
634
+ async artifactory(path, opts = {}) {
635
+ const baseUrl = this.config.artifactory.baseUrl;
636
+ if (!baseUrl) throw new PncliError("Artifactory baseUrl not configured. Run: pncli config init");
637
+ const url = buildUrl(baseUrl, path, opts.params);
638
+ const headers = { ...this.artifactoryHeaders(), ...opts.headers };
639
+ const body = opts.body !== void 0 ? typeof opts.body === "string" ? opts.body : JSON.stringify(opts.body) : void 0;
640
+ const init = {
641
+ method: opts.method ?? "GET",
642
+ headers,
643
+ body
644
+ };
645
+ if (this.dryRun) {
646
+ const safeHeaders = { ...headers, Authorization: "[REDACTED]" };
647
+ const msg = `DRY RUN: ${init.method} ${url}
648
+ Headers: ${JSON.stringify(safeHeaders, null, 2)}
649
+ ` + (opts.body ? `Body: ${typeof opts.body === "string" ? opts.body : JSON.stringify(opts.body, null, 2)}
650
+ ` : "");
651
+ fs2.writeSync(process.stderr.fd, msg);
652
+ process.exitCode = ExitCode.SUCCESS;
653
+ throw new PncliError("dry-run", 0);
654
+ }
655
+ return request(url, init, opts.timeoutMs ?? 3e4);
656
+ }
657
+ async udeploy(path, opts = {}) {
658
+ const baseUrl = this.config.udeploy.baseUrl;
659
+ if (!baseUrl) throw new PncliError("UDeploy baseUrl not configured. Run: pncli config init");
660
+ const url = buildUrl(baseUrl, path, opts.params);
661
+ const headers = this.udeployHeaders();
662
+ const init = {
663
+ method: opts.method ?? "GET",
664
+ headers,
665
+ body: opts.body !== void 0 ? JSON.stringify(opts.body) : void 0
666
+ };
667
+ if (this.dryRun) {
668
+ const safeHeaders = { ...headers, Authorization: "[REDACTED]" };
669
+ const msg = `DRY RUN: ${init.method} ${url}
670
+ Headers: ${JSON.stringify(safeHeaders, null, 2)}
671
+ ` + (opts.body ? `Body: ${JSON.stringify(opts.body, null, 2)}
672
+ ` : "");
673
+ fs2.writeSync(process.stderr.fd, msg);
674
+ process.exitCode = ExitCode.SUCCESS;
675
+ throw new PncliError("dry-run", 0);
676
+ }
677
+ return request(url, init, opts.timeoutMs ?? 3e4);
678
+ }
679
+ async artifactoryText(path, opts = {}) {
680
+ const baseUrl = this.config.artifactory.baseUrl;
681
+ if (!baseUrl) throw new PncliError("Artifactory baseUrl not configured. Run: pncli config init");
682
+ const url = buildUrl(baseUrl, path, opts.params);
683
+ const headers = { ...this.artifactoryHeaders(), Accept: "text/plain", ...opts.headers };
684
+ if (this.dryRun) {
685
+ const safeHeaders = { ...headers, Authorization: "[REDACTED]" };
686
+ fs2.writeSync(process.stderr.fd, `DRY RUN: ${opts.method ?? "GET"} ${url}
687
+ Headers: ${JSON.stringify(safeHeaders, null, 2)}
688
+ `);
689
+ process.exitCode = ExitCode.SUCCESS;
690
+ throw new PncliError("dry-run", 0);
691
+ }
692
+ const response = await fetchWithTimeout(url, {
693
+ method: opts.method ?? "GET",
694
+ headers
695
+ }, opts.timeoutMs ?? 3e4);
696
+ if (!response.ok) {
697
+ let message = `HTTP ${response.status} ${response.statusText}`;
698
+ try {
699
+ const parsed = JSON.parse(await response.text());
700
+ if (parsed.message) message = String(parsed.message);
701
+ } catch {
702
+ }
703
+ throw new PncliError(message, response.status, url);
704
+ }
705
+ return response.text();
706
+ }
460
707
  sonarHeaders() {
461
708
  const { token } = this.config.sonar;
462
709
  if (!token) throw new PncliError("SonarQube credentials not configured. Run: pncli config init");
@@ -538,6 +785,32 @@ Headers: ${JSON.stringify(safeHeaders, null, 2)}
538
785
  }
539
786
  return results;
540
787
  }
788
+ getCheckmarxFetcher() {
789
+ if (!this.checkmarxFetcher) {
790
+ this.checkmarxFetcher = buildCheckmarxFetcher(this.config);
791
+ }
792
+ return this.checkmarxFetcher;
793
+ }
794
+ async checkmarx(path, opts = {}) {
795
+ const baseUrl = this.config.checkmarx.baseUrl;
796
+ if (!baseUrl) throw new PncliError("Checkmarx baseUrl not configured. Run: pncli config init");
797
+ const url = buildUrl(baseUrl, path, opts.params);
798
+ if (this.dryRun) {
799
+ const msg = `DRY RUN: ${opts.method ?? "GET"} ${url}
800
+ ` + (opts.body ? `Body: ${JSON.stringify(opts.body, null, 2)}
801
+ ` : "");
802
+ fs2.writeSync(process.stderr.fd, msg);
803
+ process.exitCode = ExitCode.SUCCESS;
804
+ throw new PncliError("dry-run", 0);
805
+ }
806
+ const fetcher = this.getCheckmarxFetcher();
807
+ const init = {
808
+ method: opts.method ?? "GET",
809
+ headers: { "Accept": "application/json", "Content-Type": "application/json", ...opts.headers },
810
+ body: opts.body !== void 0 ? JSON.stringify(opts.body) : void 0
811
+ };
812
+ return request(url, init, opts.timeoutMs ?? 3e4, fetcher);
813
+ }
541
814
  };
542
815
  function createHttpClient(config, dryRun = false) {
543
816
  return new HttpClient(config, dryRun);
@@ -555,4 +828,4 @@ export {
555
828
  HttpClient,
556
829
  createHttpClient
557
830
  };
558
- //# sourceMappingURL=chunk-HWXXNMGE.js.map
831
+ //# sourceMappingURL=chunk-QQ7UKWTU.js.map