@kolatts/pncli 1.21.0 → 1.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/copilot-instructions.md +135 -12
- package/dist/{chunk-FXY262BR.js → chunk-KRDKHGYS.js} +11 -1
- package/dist/chunk-KRDKHGYS.js.map +1 -0
- package/dist/{chunk-PC4AHQCN.js → chunk-XYHZMXJU.js} +56 -1
- package/dist/chunk-XYHZMXJU.js.map +1 -0
- package/dist/cli.js +628 -165
- package/dist/cli.js.map +1 -1
- package/dist/{config-CPRXY5SN.js → config-QN5FFAZY.js} +2 -2
- package/dist/{http-IXJA4TMS.js → http-MCIU2IV6.js} +2 -2
- package/package.json +2 -1
- package/skills/pncli/SKILL.md +2 -1
- package/skills/pncli/confluence.md +1 -1
- package/skills/pncli/logscale.md +41 -0
- package/skills/pncli/marketplace.md +29 -0
- package/dist/chunk-FXY262BR.js.map +0 -1
- package/dist/chunk-PC4AHQCN.js.map +0 -1
- /package/dist/{config-CPRXY5SN.js.map → config-QN5FFAZY.js.map} +0 -0
- /package/dist/{http-IXJA4TMS.js.map → http-MCIU2IV6.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
setRepoConfigValue,
|
|
9
9
|
writeGlobalConfig,
|
|
10
10
|
writeRepoConfig
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-KRDKHGYS.js";
|
|
12
12
|
import {
|
|
13
13
|
ExitCode,
|
|
14
14
|
PncliError,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
success,
|
|
21
21
|
warn,
|
|
22
22
|
writeRawOutput
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-XYHZMXJU.js";
|
|
24
24
|
import {
|
|
25
25
|
AdoCoreClient
|
|
26
26
|
} from "./chunk-VDL32PSG.js";
|
|
@@ -421,34 +421,34 @@ function parseAdoRemote(remoteUrl, adoBaseUrl) {
|
|
|
421
421
|
} catch {
|
|
422
422
|
baseHostname = adoBaseUrl.replace(/\/$/, "").replace(/^https?:\/\//, "").split(/[:/]/)[0].toLowerCase();
|
|
423
423
|
}
|
|
424
|
-
let
|
|
424
|
+
let path9 = remoteUrl;
|
|
425
425
|
let remoteHostname = "";
|
|
426
|
-
const sshProtoMatch =
|
|
426
|
+
const sshProtoMatch = path9.match(/^ssh:\/\/[^@]*@?([^/:]+)(?::\d+)?(.*)/);
|
|
427
427
|
if (sshProtoMatch) {
|
|
428
428
|
remoteHostname = sshProtoMatch[1].toLowerCase();
|
|
429
|
-
|
|
429
|
+
path9 = sshProtoMatch[2];
|
|
430
430
|
} else {
|
|
431
|
-
const gitAtMatch =
|
|
431
|
+
const gitAtMatch = path9.match(/^git@([^:]+)(?::\d+)?:(.*)/);
|
|
432
432
|
if (gitAtMatch) {
|
|
433
433
|
remoteHostname = gitAtMatch[1].toLowerCase();
|
|
434
|
-
|
|
434
|
+
path9 = "/" + gitAtMatch[2];
|
|
435
435
|
} else {
|
|
436
|
-
const httpsMatch =
|
|
436
|
+
const httpsMatch = path9.match(/^https?:\/\/([^/:]+)(?::\d+)?(.*)/);
|
|
437
437
|
if (httpsMatch) {
|
|
438
438
|
remoteHostname = httpsMatch[1].toLowerCase();
|
|
439
|
-
|
|
439
|
+
path9 = httpsMatch[2];
|
|
440
440
|
}
|
|
441
441
|
}
|
|
442
442
|
}
|
|
443
443
|
if (!remoteHostname || remoteHostname !== baseHostname) return null;
|
|
444
|
-
|
|
445
|
-
const gitIdx =
|
|
446
|
-
const sshIdx =
|
|
444
|
+
path9 = path9.replace(/\.git$/, "");
|
|
445
|
+
const gitIdx = path9.indexOf("/_git/");
|
|
446
|
+
const sshIdx = path9.indexOf("/_ssh/");
|
|
447
447
|
const markerIdx = gitIdx !== -1 ? gitIdx : sshIdx;
|
|
448
448
|
if (markerIdx === -1) return null;
|
|
449
|
-
const repo = decodeURIComponent(
|
|
449
|
+
const repo = decodeURIComponent(path9.slice(markerIdx + 6));
|
|
450
450
|
if (!repo) return null;
|
|
451
|
-
const before =
|
|
451
|
+
const before = path9.slice(1, markerIdx);
|
|
452
452
|
const parts = before.split("/").filter(Boolean);
|
|
453
453
|
if (parts.length < 2) return null;
|
|
454
454
|
const project = decodeURIComponent(parts[parts.length - 1]);
|
|
@@ -1390,6 +1390,13 @@ function getDefaults(program2) {
|
|
|
1390
1390
|
priority: config.defaults.jira?.priority
|
|
1391
1391
|
};
|
|
1392
1392
|
}
|
|
1393
|
+
function resolveJqlInput(jql, jqlFile) {
|
|
1394
|
+
const raw = resolveTextInput(jql, jqlFile, "jql");
|
|
1395
|
+
if (raw === void 0) throw new PncliError("Must specify --jql or --jql-file", 1);
|
|
1396
|
+
const trimmed = raw.trim();
|
|
1397
|
+
if (!trimmed) throw new PncliError("JQL query is empty", 1);
|
|
1398
|
+
return trimmed;
|
|
1399
|
+
}
|
|
1393
1400
|
function registerJiraCommands(program2) {
|
|
1394
1401
|
const jira = program2.command("jira").description("Jira Data Cloud operations");
|
|
1395
1402
|
jira.command("get-issue").description("Get a Jira issue by key").requiredOption("--key <issue-key>", "Issue key (e.g. PROJ-123)").action(async (opts) => {
|
|
@@ -1539,12 +1546,13 @@ function registerJiraCommands(program2) {
|
|
|
1539
1546
|
fail(err, "jira", "list-comments", start);
|
|
1540
1547
|
}
|
|
1541
1548
|
});
|
|
1542
|
-
jira.command("search").description("Search Jira issues with JQL (consider --output-file for large results)").
|
|
1549
|
+
jira.command("search").description("Search Jira issues with JQL (consider --output-file for large results)").option("--jql <query>", "JQL query string").option("--jql-file <path>", "Path to a file containing the JQL query ('-' = stdin)").option("--max-results <n>", "Maximum number of results").action(async (opts) => {
|
|
1543
1550
|
const start = Date.now();
|
|
1544
1551
|
try {
|
|
1545
1552
|
const { client, fieldMap, customFields } = getClientAndFields(program2);
|
|
1553
|
+
const jql = resolveJqlInput(opts.jql, opts.jqlFile);
|
|
1546
1554
|
const maxResults = opts.maxResults ? parseInt(opts.maxResults, 10) : void 0;
|
|
1547
|
-
const translatedJql = translateJql(
|
|
1555
|
+
const translatedJql = translateJql(jql, fieldMap);
|
|
1548
1556
|
const data = await client.search(translatedJql, maxResults, customFields);
|
|
1549
1557
|
const translatedIssues = data.issues.map((issue) => ({
|
|
1550
1558
|
...issue,
|
|
@@ -2216,8 +2224,8 @@ var GitHubClient = class {
|
|
|
2216
2224
|
);
|
|
2217
2225
|
}
|
|
2218
2226
|
async createRepo(opts) {
|
|
2219
|
-
const
|
|
2220
|
-
return this.http.github(
|
|
2227
|
+
const path9 = opts.org ? `/orgs/${opts.org}/repos` : "/user/repos";
|
|
2228
|
+
return this.http.github(path9, {
|
|
2221
2229
|
method: "POST",
|
|
2222
2230
|
body: {
|
|
2223
2231
|
name: opts.name,
|
|
@@ -2556,7 +2564,8 @@ function registerGitHubCommands(program2) {
|
|
|
2556
2564
|
}
|
|
2557
2565
|
|
|
2558
2566
|
// src/services/confluence/commands.ts
|
|
2559
|
-
import { existsSync as existsSync2 } from "fs";
|
|
2567
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
2568
|
+
import path2 from "path";
|
|
2560
2569
|
|
|
2561
2570
|
// src/services/confluence/client.ts
|
|
2562
2571
|
import { readFileSync as readFileSync4 } from "fs";
|
|
@@ -2718,6 +2727,19 @@ var ConfluenceClient = class {
|
|
|
2718
2727
|
);
|
|
2719
2728
|
return result.results;
|
|
2720
2729
|
}
|
|
2730
|
+
async getAttachment(attachmentId) {
|
|
2731
|
+
return this.http.confluence(`${API3}/content/${attachmentId}`);
|
|
2732
|
+
}
|
|
2733
|
+
async downloadAttachment(attachment) {
|
|
2734
|
+
const downloadPath = attachment._links.download;
|
|
2735
|
+
if (!downloadPath) {
|
|
2736
|
+
throw new PncliError(
|
|
2737
|
+
`Content ${attachment.id} has no download link \u2014 is it an attachment? (title: ${attachment.title})`,
|
|
2738
|
+
1
|
|
2739
|
+
);
|
|
2740
|
+
}
|
|
2741
|
+
return this.http.confluenceBuffer(downloadPath);
|
|
2742
|
+
}
|
|
2721
2743
|
async deleteAttachment(attachmentId) {
|
|
2722
2744
|
return this.http.confluence(`${API3}/content/${attachmentId}`, { method: "DELETE" });
|
|
2723
2745
|
}
|
|
@@ -2737,6 +2759,11 @@ var ConfluenceClient = class {
|
|
|
2737
2759
|
};
|
|
2738
2760
|
|
|
2739
2761
|
// src/services/confluence/commands.ts
|
|
2762
|
+
function resolveAttachmentPath(outDir, title) {
|
|
2763
|
+
const segments = title.split(/[\\/]/).filter((s) => s !== "" && s !== "." && s !== "..");
|
|
2764
|
+
const name = segments.pop() ?? "attachment";
|
|
2765
|
+
return path2.join(outDir, name);
|
|
2766
|
+
}
|
|
2740
2767
|
function xmlParseHint(err, bodyContent, convertedFromMarkdown = false) {
|
|
2741
2768
|
if (!(err instanceof Error)) return null;
|
|
2742
2769
|
const m = err.message.match(/at \[row,col\]=\{(\d+),(\d+)\}/);
|
|
@@ -2986,6 +3013,21 @@ ${hint}`, status, url), "confluence", "update-page", start);
|
|
|
2986
3013
|
fail(err, "confluence", "upload-attachment", start);
|
|
2987
3014
|
}
|
|
2988
3015
|
});
|
|
3016
|
+
confluence.command("download-attachment").description("Download a Confluence attachment to .pncli/ (or --dir)").requiredOption("--id <attachment-id>", "Attachment content ID").option("--dir <path>", "Output directory (default: .pncli relative to cwd)").action(async (opts) => {
|
|
3017
|
+
const start = Date.now();
|
|
3018
|
+
try {
|
|
3019
|
+
const client = getClient4(program2);
|
|
3020
|
+
const attachment = await client.getAttachment(opts.id);
|
|
3021
|
+
const outDir = opts.dir ?? path2.join(process.cwd(), ".pncli");
|
|
3022
|
+
mkdirSync2(outDir, { recursive: true });
|
|
3023
|
+
const outPath = resolveAttachmentPath(outDir, attachment.title);
|
|
3024
|
+
const buffer = await client.downloadAttachment(attachment);
|
|
3025
|
+
writeFileSync2(outPath, buffer);
|
|
3026
|
+
success({ saved: outPath, filename: attachment.title, size: buffer.length }, "confluence", "download-attachment", start);
|
|
3027
|
+
} catch (err) {
|
|
3028
|
+
fail(err, "confluence", "download-attachment", start);
|
|
3029
|
+
}
|
|
3030
|
+
});
|
|
2989
3031
|
confluence.command("delete-attachment").description("Delete an attachment from Confluence by its content ID (moves it to the trash; does not permanently purge it)").requiredOption("--id <attachment-id>", "Attachment content ID").action(async (opts) => {
|
|
2990
3032
|
const start = Date.now();
|
|
2991
3033
|
try {
|
|
@@ -3549,23 +3591,23 @@ import { Option as Option2 } from "commander";
|
|
|
3549
3591
|
|
|
3550
3592
|
// src/services/deps/parsers/index.ts
|
|
3551
3593
|
import fs from "fs";
|
|
3552
|
-
import
|
|
3594
|
+
import path6 from "path";
|
|
3553
3595
|
import { execSync as execSync3, execFileSync as execFileSync2 } from "child_process";
|
|
3554
3596
|
|
|
3555
3597
|
// src/services/deps/parsers/npm.ts
|
|
3556
|
-
import
|
|
3598
|
+
import path3 from "path";
|
|
3557
3599
|
function findNpmManifests(files) {
|
|
3558
3600
|
const lockFiles = new Set(
|
|
3559
3601
|
files.filter((f) => {
|
|
3560
|
-
const base =
|
|
3602
|
+
const base = path3.basename(f);
|
|
3561
3603
|
return base === "package-lock.json" || base === "yarn.lock" || base === "pnpm-lock.yaml";
|
|
3562
3604
|
})
|
|
3563
3605
|
);
|
|
3564
3606
|
const manifests = [];
|
|
3565
3607
|
for (const file of files) {
|
|
3566
|
-
if (
|
|
3608
|
+
if (path3.basename(file) !== "package.json") continue;
|
|
3567
3609
|
if (file.split("/").includes("node_modules")) continue;
|
|
3568
|
-
const dir =
|
|
3610
|
+
const dir = path3.dirname(file);
|
|
3569
3611
|
let lockFile;
|
|
3570
3612
|
for (const lf of ["package-lock.json", "yarn.lock", "pnpm-lock.yaml"]) {
|
|
3571
3613
|
const candidate = dir === "." ? lf : `${dir}/${lf}`;
|
|
@@ -3580,7 +3622,7 @@ function findNpmManifests(files) {
|
|
|
3580
3622
|
}
|
|
3581
3623
|
function parseNpmPackages(content, manifest, opts, lockContent) {
|
|
3582
3624
|
if (lockContent && manifest.lockFile) {
|
|
3583
|
-
const base =
|
|
3625
|
+
const base = path3.basename(manifest.lockFile);
|
|
3584
3626
|
if (base === "package-lock.json") {
|
|
3585
3627
|
const pkgs = parsePackageLock(lockContent, manifest.lockFile, opts);
|
|
3586
3628
|
if (pkgs.length > 0) return pkgs;
|
|
@@ -3728,26 +3770,26 @@ function parsePnpmLock(content, lockFilePath, opts, packageJsonContent) {
|
|
|
3728
3770
|
}
|
|
3729
3771
|
|
|
3730
3772
|
// src/services/deps/parsers/nuget.ts
|
|
3731
|
-
import
|
|
3773
|
+
import path4 from "path";
|
|
3732
3774
|
var PROJ_EXTENSIONS = /* @__PURE__ */ new Set([".csproj", ".fsproj", ".vbproj"]);
|
|
3733
3775
|
function findNugetManifests(files) {
|
|
3734
3776
|
const lockFiles = /* @__PURE__ */ new Map();
|
|
3735
3777
|
for (const f of files) {
|
|
3736
|
-
if (
|
|
3737
|
-
lockFiles.set(
|
|
3778
|
+
if (path4.basename(f) === "packages.lock.json") {
|
|
3779
|
+
lockFiles.set(path4.dirname(f), f);
|
|
3738
3780
|
}
|
|
3739
3781
|
}
|
|
3740
3782
|
const manifests = [];
|
|
3741
3783
|
for (const file of files) {
|
|
3742
|
-
const base =
|
|
3743
|
-
const ext =
|
|
3784
|
+
const base = path4.basename(file);
|
|
3785
|
+
const ext = path4.extname(file);
|
|
3744
3786
|
if (PROJ_EXTENSIONS.has(ext) || base === "packages.config") {
|
|
3745
|
-
const dir =
|
|
3787
|
+
const dir = path4.dirname(file);
|
|
3746
3788
|
const lockFile = lockFiles.get(dir);
|
|
3747
3789
|
manifests.push({ file, ecosystem: "nuget", lockFile });
|
|
3748
3790
|
}
|
|
3749
3791
|
if (base === "Directory.Packages.props") {
|
|
3750
|
-
const dir =
|
|
3792
|
+
const dir = path4.dirname(file);
|
|
3751
3793
|
manifests.push({ file, ecosystem: "nuget", lockFile: lockFiles.get(dir) });
|
|
3752
3794
|
}
|
|
3753
3795
|
}
|
|
@@ -3766,7 +3808,7 @@ function findNugetManifests(files) {
|
|
|
3766
3808
|
return deduped;
|
|
3767
3809
|
}
|
|
3768
3810
|
function parseNugetPackages(content, manifest, opts, lockContent, propsContent) {
|
|
3769
|
-
const base =
|
|
3811
|
+
const base = path4.basename(manifest.file);
|
|
3770
3812
|
if (lockContent) {
|
|
3771
3813
|
return parsePackagesLock(lockContent, manifest.lockFile ?? manifest.file, opts);
|
|
3772
3814
|
}
|
|
@@ -3887,14 +3929,14 @@ function parsePackagesLock(content, filePath, opts) {
|
|
|
3887
3929
|
}
|
|
3888
3930
|
|
|
3889
3931
|
// src/services/deps/parsers/maven.ts
|
|
3890
|
-
import
|
|
3932
|
+
import path5 from "path";
|
|
3891
3933
|
function findMavenManifests(files) {
|
|
3892
|
-
const lockFiles = new Set(files.filter((f) =>
|
|
3934
|
+
const lockFiles = new Set(files.filter((f) => path5.basename(f) === "gradle.lockfile"));
|
|
3893
3935
|
const manifests = [];
|
|
3894
3936
|
for (const file of files) {
|
|
3895
|
-
const base =
|
|
3937
|
+
const base = path5.basename(file);
|
|
3896
3938
|
if (base === "pom.xml" || base === "build.gradle" || base === "build.gradle.kts") {
|
|
3897
|
-
const dir =
|
|
3939
|
+
const dir = path5.dirname(file);
|
|
3898
3940
|
const lockCandidate = dir === "." ? "gradle.lockfile" : `${dir}/gradle.lockfile`;
|
|
3899
3941
|
const lockFile = lockFiles.has(lockCandidate) ? lockCandidate : void 0;
|
|
3900
3942
|
manifests.push({ file, ecosystem: "maven", lockFile });
|
|
@@ -3903,7 +3945,7 @@ function findMavenManifests(files) {
|
|
|
3903
3945
|
return manifests;
|
|
3904
3946
|
}
|
|
3905
3947
|
function parseMavenPackages(content, manifest, opts, lockContent) {
|
|
3906
|
-
const base =
|
|
3948
|
+
const base = path5.basename(manifest.file);
|
|
3907
3949
|
if (lockContent && manifest.lockFile) {
|
|
3908
3950
|
return parseGradleLock(lockContent, manifest.lockFile, opts);
|
|
3909
3951
|
}
|
|
@@ -4064,7 +4106,7 @@ function getRepoFiles(repoRoot) {
|
|
|
4064
4106
|
}
|
|
4065
4107
|
function readFile(repoRoot, relPath) {
|
|
4066
4108
|
try {
|
|
4067
|
-
return fs.readFileSync(
|
|
4109
|
+
return fs.readFileSync(path6.join(repoRoot, relPath), "utf8");
|
|
4068
4110
|
} catch {
|
|
4069
4111
|
return null;
|
|
4070
4112
|
}
|
|
@@ -4082,13 +4124,13 @@ function readFileAtRef(repoRoot, ref, relPath) {
|
|
|
4082
4124
|
}
|
|
4083
4125
|
}
|
|
4084
4126
|
function findPropsContent(manifestFile, readFn) {
|
|
4085
|
-
let dir =
|
|
4127
|
+
let dir = path6.dirname(manifestFile);
|
|
4086
4128
|
for (let depth = 0; depth < 10; depth++) {
|
|
4087
4129
|
const candidate = dir === "." ? "Directory.Packages.props" : `${dir}/Directory.Packages.props`;
|
|
4088
4130
|
const content = readFn(candidate);
|
|
4089
4131
|
if (content) return content;
|
|
4090
4132
|
if (dir === ".") break;
|
|
4091
|
-
dir =
|
|
4133
|
+
dir = path6.dirname(dir);
|
|
4092
4134
|
}
|
|
4093
4135
|
return null;
|
|
4094
4136
|
}
|
|
@@ -5530,7 +5572,7 @@ import chalk from "chalk";
|
|
|
5530
5572
|
import fs2 from "fs";
|
|
5531
5573
|
function registerConfigCommands(program2) {
|
|
5532
5574
|
const config = program2.command("config").description("Manage pncli configuration");
|
|
5533
|
-
config.command("init").description("
|
|
5575
|
+
config.command("init").description("Setup wizard for service credentials (interactive)").option("--repo", "Write repo config (.pncli.json) instead of global config").action(async (opts) => {
|
|
5534
5576
|
const start = Date.now();
|
|
5535
5577
|
try {
|
|
5536
5578
|
if (opts.repo) {
|
|
@@ -5641,8 +5683,8 @@ function registerConfigCommands(program2) {
|
|
|
5641
5683
|
if (cfg.ado.baseUrl) {
|
|
5642
5684
|
try {
|
|
5643
5685
|
const collection = cfg.defaults.ado?.collection;
|
|
5644
|
-
const
|
|
5645
|
-
await http.ado(
|
|
5686
|
+
const path9 = collection ? `/${encodeURIComponent(collection)}/_apis/connectionData?api-version=7.1-preview.1` : "/_apis/projectCollections?api-version=7.1";
|
|
5687
|
+
await http.ado(path9);
|
|
5646
5688
|
results.ado = { ok: true, message: "connected" };
|
|
5647
5689
|
} catch (err) {
|
|
5648
5690
|
results.ado = { ok: false, message: err instanceof Error ? err.message : String(err) };
|
|
@@ -5762,6 +5804,16 @@ function registerConfigCommands(program2) {
|
|
|
5762
5804
|
} else {
|
|
5763
5805
|
results.dynatrace_platform = { ok: null, message: "not configured" };
|
|
5764
5806
|
}
|
|
5807
|
+
if (cfg.logscale.baseUrl && cfg.logscale.token) {
|
|
5808
|
+
try {
|
|
5809
|
+
await http.logscale("/api/v1/repositories");
|
|
5810
|
+
results.logscale = { ok: true, message: "connected" };
|
|
5811
|
+
} catch (err) {
|
|
5812
|
+
results.logscale = { ok: false, message: err instanceof Error ? err.message : String(err) };
|
|
5813
|
+
}
|
|
5814
|
+
} else {
|
|
5815
|
+
results.logscale = { ok: null, message: "not configured" };
|
|
5816
|
+
}
|
|
5765
5817
|
success(results, "config", "test", start);
|
|
5766
5818
|
} catch (err) {
|
|
5767
5819
|
fail(err, "config", "test", start);
|
|
@@ -5867,8 +5919,8 @@ function registerConfigCommands(program2) {
|
|
|
5867
5919
|
} else {
|
|
5868
5920
|
try {
|
|
5869
5921
|
const collection = cfg.defaults.ado?.collection;
|
|
5870
|
-
const
|
|
5871
|
-
await http.ado(
|
|
5922
|
+
const path9 = collection ? `/${encodeURIComponent(collection)}/_apis/connectionData?api-version=7.1-preview.1` : "/_apis/projectCollections?api-version=7.1";
|
|
5923
|
+
await http.ado(path9, { timeoutMs: 1e4 });
|
|
5872
5924
|
results.ado = { status: "valid", message: "ok" };
|
|
5873
5925
|
} catch (err) {
|
|
5874
5926
|
results.ado = categorize2(err);
|
|
@@ -6006,11 +6058,23 @@ function registerConfigCommands(program2) {
|
|
|
6006
6058
|
results.dynatrace_platform = categorize2(err);
|
|
6007
6059
|
}
|
|
6008
6060
|
}
|
|
6061
|
+
if (!cfg.logscale.token) {
|
|
6062
|
+
results.logscale = { status: "blank", message: "not configured" };
|
|
6063
|
+
} else if (!cfg.logscale.baseUrl) {
|
|
6064
|
+
results.logscale = { status: "error", message: "baseUrl not configured" };
|
|
6065
|
+
} else {
|
|
6066
|
+
try {
|
|
6067
|
+
await http.logscale("/api/v1/repositories", { timeoutMs: 1e4 });
|
|
6068
|
+
results.logscale = { status: "valid", message: "ok" };
|
|
6069
|
+
} catch (err) {
|
|
6070
|
+
results.logscale = categorize2(err);
|
|
6071
|
+
}
|
|
6072
|
+
}
|
|
6009
6073
|
const statuses = Object.values(results).map((r) => r.status);
|
|
6010
6074
|
if (statuses.includes("invalid")) process.exitCode = ExitCode.AUTH_ERROR;
|
|
6011
6075
|
else if (statuses.includes("error")) process.exitCode = ExitCode.NETWORK_ERROR;
|
|
6012
6076
|
if (cmdOpts.output === "table") {
|
|
6013
|
-
const services = ["jira", "bitbucket", "github", "confluence", "sonar", "sde", "ado", "jenkins", "udeploy", "artifactory", "checkmarx", "servicenow", "contrast", "sonatypeiq", "openshift", "dynatrace", "dynatrace_platform"];
|
|
6077
|
+
const services = ["jira", "bitbucket", "github", "confluence", "sonar", "sde", "ado", "jenkins", "udeploy", "artifactory", "checkmarx", "servicenow", "contrast", "sonatypeiq", "openshift", "dynatrace", "dynatrace_platform", "logscale"];
|
|
6014
6078
|
const labelWidth = 14;
|
|
6015
6079
|
const statusWidth = 9;
|
|
6016
6080
|
for (const svc of services) {
|
|
@@ -6024,7 +6088,7 @@ function registerConfigCommands(program2) {
|
|
|
6024
6088
|
process.stdout.write("\n");
|
|
6025
6089
|
} else {
|
|
6026
6090
|
if (opts.pretty) {
|
|
6027
|
-
const services = ["jira", "bitbucket", "github", "confluence", "sonar", "sde", "ado", "jenkins", "udeploy", "artifactory", "checkmarx", "servicenow", "contrast", "sonatypeiq", "openshift", "dynatrace", "dynatrace_platform"];
|
|
6091
|
+
const services = ["jira", "bitbucket", "github", "confluence", "sonar", "sde", "ado", "jenkins", "udeploy", "artifactory", "checkmarx", "servicenow", "contrast", "sonatypeiq", "openshift", "dynatrace", "dynatrace_platform", "logscale"];
|
|
6028
6092
|
const labelWidth = 14;
|
|
6029
6093
|
const statusWidth = 9;
|
|
6030
6094
|
for (const svc of services) {
|
|
@@ -6647,6 +6711,40 @@ async function initGlobalConfig(start) {
|
|
|
6647
6711
|
}
|
|
6648
6712
|
}
|
|
6649
6713
|
}
|
|
6714
|
+
process.stderr.write("\n\u2500\u2500 LogScale \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");
|
|
6715
|
+
const useLogscale = await confirm({
|
|
6716
|
+
message: "Configure LogScale (Falcon LogScale / Humio) for log queries?",
|
|
6717
|
+
default: false
|
|
6718
|
+
});
|
|
6719
|
+
let logscaleBaseUrl = "";
|
|
6720
|
+
let logscaleToken = "";
|
|
6721
|
+
if (useLogscale) {
|
|
6722
|
+
logscaleBaseUrl = await input({
|
|
6723
|
+
message: "LogScale base URL (e.g. https://logscale.imagile.dev):",
|
|
6724
|
+
default: ""
|
|
6725
|
+
});
|
|
6726
|
+
logscaleToken = await password({
|
|
6727
|
+
message: "LogScale personal access token:"
|
|
6728
|
+
});
|
|
6729
|
+
if (logscaleBaseUrl && logscaleToken) {
|
|
6730
|
+
process.stderr.write("\n Verifying connection...\n");
|
|
6731
|
+
try {
|
|
6732
|
+
const tempConfig = {
|
|
6733
|
+
...loadConfig(),
|
|
6734
|
+
logscale: {
|
|
6735
|
+
baseUrl: normalizeBaseUrl(logscaleBaseUrl),
|
|
6736
|
+
token: logscaleToken
|
|
6737
|
+
}
|
|
6738
|
+
};
|
|
6739
|
+
const tempHttp = createHttpClient(tempConfig);
|
|
6740
|
+
await tempHttp.logscale("/api/v1/repositories");
|
|
6741
|
+
process.stderr.write(" Connected.\n");
|
|
6742
|
+
} catch (err) {
|
|
6743
|
+
warn(`Could not connect to LogScale: ${err instanceof Error ? err.message : String(err)}`);
|
|
6744
|
+
warn("Config will be saved anyway. Check your URL and token and re-run pncli config init or pncli config test.");
|
|
6745
|
+
}
|
|
6746
|
+
}
|
|
6747
|
+
}
|
|
6650
6748
|
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");
|
|
6651
6749
|
const jiraProject = await input({
|
|
6652
6750
|
message: "Default Jira project key (optional):",
|
|
@@ -6786,6 +6884,12 @@ async function initGlobalConfig(start) {
|
|
|
6786
6884
|
platformToken: dynatracePlatformToken || void 0
|
|
6787
6885
|
}
|
|
6788
6886
|
} : {},
|
|
6887
|
+
...useLogscale && logscaleBaseUrl ? {
|
|
6888
|
+
logscale: {
|
|
6889
|
+
baseUrl: normalizeBaseUrl(logscaleBaseUrl),
|
|
6890
|
+
token: logscaleToken || void 0
|
|
6891
|
+
}
|
|
6892
|
+
} : {},
|
|
6789
6893
|
defaults: {
|
|
6790
6894
|
jira: {
|
|
6791
6895
|
project: jiraProject || void 0
|
|
@@ -7263,7 +7367,7 @@ function registerAdoProjectCommands(ado) {
|
|
|
7263
7367
|
|
|
7264
7368
|
// src/services/ado/commands/work.ts
|
|
7265
7369
|
import fs3 from "fs";
|
|
7266
|
-
import
|
|
7370
|
+
import path7 from "path";
|
|
7267
7371
|
|
|
7268
7372
|
// src/services/ado/input-schema.ts
|
|
7269
7373
|
var ADO_WORK_INPUT_FILE_SCHEMA = {
|
|
@@ -7504,9 +7608,9 @@ function registerAdoWorkCommands(ado) {
|
|
|
7504
7608
|
const attachments = await workClient.listAttachments(collection, parseInt(opts.id, 10));
|
|
7505
7609
|
const attachment = attachments.find((a) => a.id === opts.attachmentId);
|
|
7506
7610
|
if (!attachment) throw new PncliError(`Attachment not found: ${opts.attachmentId}`, 1);
|
|
7507
|
-
const outDir = opts.dir ??
|
|
7611
|
+
const outDir = opts.dir ?? path7.join(process.cwd(), ".pncli");
|
|
7508
7612
|
fs3.mkdirSync(outDir, { recursive: true });
|
|
7509
|
-
const outPath =
|
|
7613
|
+
const outPath = path7.join(outDir, path7.basename(attachment.name));
|
|
7510
7614
|
const buffer = await workClient.downloadAttachment(attachment.url);
|
|
7511
7615
|
fs3.writeFileSync(outPath, buffer);
|
|
7512
7616
|
success({ saved: outPath, name: attachment.name, size: buffer.length }, "ado", "work-download-attachment", start);
|
|
@@ -7666,7 +7770,7 @@ function registerAdoRepoCommands(ado) {
|
|
|
7666
7770
|
fail(err, "ado", "repo-get-pr", start);
|
|
7667
7771
|
}
|
|
7668
7772
|
});
|
|
7669
|
-
repo.command("create-pr").description("Create a pull request").requiredOption("--title <title>", "PR title").requiredOption("--source <branch>", "Source branch").option("--target <branch>", "Target branch (default: main)", "main").option("--description <text>", "PR description").option("--reviewers <ids>", "Comma-separated reviewer IDs or display names").action(async (opts) => {
|
|
7773
|
+
repo.command("create-pr").description("Create a pull request").requiredOption("--title <title>", "PR title").requiredOption("--source <branch>", "Source branch").option("--target <branch>", "Target branch (default: main)", "main").option("--description <text>", "PR description").option("--reviewers <ids>", "Comma-separated reviewer IDs or display names").option("--draft", "Create as a draft pull request").action(async (opts) => {
|
|
7670
7774
|
const start = Date.now();
|
|
7671
7775
|
try {
|
|
7672
7776
|
const { collection, project, repo: repo2, gitClient } = getAdoContext(ado, true);
|
|
@@ -7676,14 +7780,15 @@ function registerAdoRepoCommands(ado) {
|
|
|
7676
7780
|
description: opts.description,
|
|
7677
7781
|
sourceRefName: `refs/heads/${opts.source}`,
|
|
7678
7782
|
targetRefName: `refs/heads/${opts.target}`,
|
|
7679
|
-
reviewers
|
|
7783
|
+
reviewers,
|
|
7784
|
+
...opts.draft !== void 0 ? { isDraft: opts.draft } : {}
|
|
7680
7785
|
});
|
|
7681
7786
|
success(data, "ado", "repo-create-pr", start);
|
|
7682
7787
|
} catch (err) {
|
|
7683
7788
|
fail(err, "ado", "repo-create-pr", start);
|
|
7684
7789
|
}
|
|
7685
7790
|
});
|
|
7686
|
-
repo.command("update-pr").description("Update a pull request").requiredOption("--id <n>", "Pull request ID").option("--title <title>", "New title").option("--description <text>", "New description").option("--reviewers <ids>", "Comma-separated reviewer IDs").action(async (opts) => {
|
|
7791
|
+
repo.command("update-pr").description("Update a pull request").requiredOption("--id <n>", "Pull request ID").option("--title <title>", "New title").option("--description <text>", "New description").option("--reviewers <ids>", "Comma-separated reviewer IDs").option("--draft <bool>", "Set draft status: true|false").action(async (opts) => {
|
|
7687
7792
|
const start = Date.now();
|
|
7688
7793
|
try {
|
|
7689
7794
|
const { collection, project, repo: repo2, gitClient } = getAdoContext(ado, true);
|
|
@@ -7692,6 +7797,7 @@ function registerAdoRepoCommands(ado) {
|
|
|
7692
7797
|
if (opts.title) body.title = opts.title;
|
|
7693
7798
|
if (opts.description !== void 0) body.description = opts.description;
|
|
7694
7799
|
if (reviewers) body.reviewers = reviewers;
|
|
7800
|
+
if (opts.draft !== void 0) body.isDraft = opts.draft === "true";
|
|
7695
7801
|
const data = await gitClient.updatePR(collection, project, repo2, parseInt(opts.id, 10), body);
|
|
7696
7802
|
success(data, "ado", "repo-update-pr", start);
|
|
7697
7803
|
} catch (err) {
|
|
@@ -7873,11 +7979,11 @@ function registerAdoRepoCommands(ado) {
|
|
|
7873
7979
|
const start = Date.now();
|
|
7874
7980
|
try {
|
|
7875
7981
|
const globalOpts = ado.optsWithGlobals();
|
|
7876
|
-
const { loadConfig: lc } = await import("./config-
|
|
7982
|
+
const { loadConfig: lc } = await import("./config-QN5FFAZY.js");
|
|
7877
7983
|
const config = lc({ configPath: globalOpts.config });
|
|
7878
7984
|
const { collection, project, repo: repo2, gitClient } = getAdoContext(ado, true);
|
|
7879
7985
|
const { AdoCoreClient: AdoCoreClient2 } = await import("./core-SWYVXOQ4.js");
|
|
7880
|
-
const { createHttpClient: createHttpClient2 } = await import("./http-
|
|
7986
|
+
const { createHttpClient: createHttpClient2 } = await import("./http-MCIU2IV6.js");
|
|
7881
7987
|
const http = createHttpClient2(config, Boolean(globalOpts.dryRun));
|
|
7882
7988
|
const coreClient = new AdoCoreClient2(http);
|
|
7883
7989
|
const me = await coreClient.getConnectionData(collection);
|
|
@@ -8278,18 +8384,18 @@ var ArtifactoryClient = class {
|
|
|
8278
8384
|
}
|
|
8279
8385
|
});
|
|
8280
8386
|
}
|
|
8281
|
-
async getStorageInfo(repo,
|
|
8282
|
-
const cleanPath =
|
|
8387
|
+
async getStorageInfo(repo, path9) {
|
|
8388
|
+
const cleanPath = path9.startsWith("/") ? path9.slice(1) : path9;
|
|
8283
8389
|
return this.http.artifactory(`${API9}/storage/${repo}/${cleanPath}`);
|
|
8284
8390
|
}
|
|
8285
|
-
async getProperties(repo,
|
|
8286
|
-
const cleanPath =
|
|
8391
|
+
async getProperties(repo, path9) {
|
|
8392
|
+
const cleanPath = path9.startsWith("/") ? path9.slice(1) : path9;
|
|
8287
8393
|
return this.http.artifactory(`${API9}/storage/${repo}/${cleanPath}`, {
|
|
8288
8394
|
params: { properties: "" }
|
|
8289
8395
|
});
|
|
8290
8396
|
}
|
|
8291
|
-
async setProperties(repo,
|
|
8292
|
-
const cleanPath =
|
|
8397
|
+
async setProperties(repo, path9, properties, opts = {}) {
|
|
8398
|
+
const cleanPath = path9.startsWith("/") ? path9.slice(1) : path9;
|
|
8293
8399
|
const propString = Object.entries(properties).map(([k, v]) => `${k}=${v}`).join(";");
|
|
8294
8400
|
await this.http.artifactory(`${API9}/storage/${repo}/${cleanPath}`, {
|
|
8295
8401
|
method: "PUT",
|
|
@@ -8371,27 +8477,27 @@ function registerArtifactoryCommands(program2) {
|
|
|
8371
8477
|
fail(err, "artifactory", "repos", start);
|
|
8372
8478
|
}
|
|
8373
8479
|
});
|
|
8374
|
-
art.command("artifact-info <repo> <path>").description("Get metadata for an artifact or folder (checksums, size, timestamps)").action(async (repo,
|
|
8480
|
+
art.command("artifact-info <repo> <path>").description("Get metadata for an artifact or folder (checksums, size, timestamps)").action(async (repo, path9) => {
|
|
8375
8481
|
const start = Date.now();
|
|
8376
8482
|
try {
|
|
8377
8483
|
const client = getClient8(program2);
|
|
8378
|
-
const data = await client.getStorageInfo(repo,
|
|
8484
|
+
const data = await client.getStorageInfo(repo, path9);
|
|
8379
8485
|
success(data, "artifactory", "artifact-info", start);
|
|
8380
8486
|
} catch (err) {
|
|
8381
8487
|
fail(err, "artifactory", "artifact-info", start);
|
|
8382
8488
|
}
|
|
8383
8489
|
});
|
|
8384
|
-
art.command("properties-get <repo> <path>").description("Read custom properties on an artifact").action(async (repo,
|
|
8490
|
+
art.command("properties-get <repo> <path>").description("Read custom properties on an artifact").action(async (repo, path9) => {
|
|
8385
8491
|
const start = Date.now();
|
|
8386
8492
|
try {
|
|
8387
8493
|
const client = getClient8(program2);
|
|
8388
|
-
const data = await client.getProperties(repo,
|
|
8494
|
+
const data = await client.getProperties(repo, path9);
|
|
8389
8495
|
success(data, "artifactory", "properties-get", start);
|
|
8390
8496
|
} catch (err) {
|
|
8391
8497
|
fail(err, "artifactory", "properties-get", start);
|
|
8392
8498
|
}
|
|
8393
8499
|
});
|
|
8394
|
-
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,
|
|
8500
|
+
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, path9, keyvals, opts) => {
|
|
8395
8501
|
const start = Date.now();
|
|
8396
8502
|
try {
|
|
8397
8503
|
if (!keyvals.length) throw new PncliError("Provide at least one key=value pair");
|
|
@@ -8402,8 +8508,8 @@ function registerArtifactoryCommands(program2) {
|
|
|
8402
8508
|
properties[kv.slice(0, eq)] = kv.slice(eq + 1);
|
|
8403
8509
|
}
|
|
8404
8510
|
const client = getClient8(program2);
|
|
8405
|
-
await client.setProperties(repo,
|
|
8406
|
-
success({ repo, path:
|
|
8511
|
+
await client.setProperties(repo, path9, properties, { recursive: opts.recursive });
|
|
8512
|
+
success({ repo, path: path9, properties }, "artifactory", "properties-set", start);
|
|
8407
8513
|
} catch (err) {
|
|
8408
8514
|
fail(err, "artifactory", "properties-set", start);
|
|
8409
8515
|
}
|
|
@@ -9010,8 +9116,8 @@ function registerSonatypeIqCommands(program2) {
|
|
|
9010
9116
|
const startTime = Date.now();
|
|
9011
9117
|
try {
|
|
9012
9118
|
const http = getHttpClient(program2);
|
|
9013
|
-
const
|
|
9014
|
-
const data = await http.sonatypeiq(
|
|
9119
|
+
const path9 = opts.organizationId ? `/api/v2/policies/organization/${encodeURIComponent(opts.organizationId)}` : "/api/v2/policies";
|
|
9120
|
+
const data = await http.sonatypeiq(path9);
|
|
9015
9121
|
success(data, "sonatypeiq", "policies list", startTime);
|
|
9016
9122
|
} catch (err) {
|
|
9017
9123
|
fail(err, "sonatypeiq", "policies list", startTime);
|
|
@@ -9021,11 +9127,13 @@ function registerSonatypeIqCommands(program2) {
|
|
|
9021
9127
|
|
|
9022
9128
|
// src/services/skills/commands.ts
|
|
9023
9129
|
import fs4 from "fs";
|
|
9024
|
-
import
|
|
9130
|
+
import path8 from "path";
|
|
9025
9131
|
import os from "os";
|
|
9026
9132
|
import { fileURLToPath } from "url";
|
|
9027
9133
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
9028
9134
|
import select from "@inquirer/select";
|
|
9135
|
+
import checkbox, { Separator } from "@inquirer/checkbox";
|
|
9136
|
+
import input2 from "@inquirer/input";
|
|
9029
9137
|
var BACK = "__back__";
|
|
9030
9138
|
var ALL_MARKETPLACES = "__all_marketplaces__";
|
|
9031
9139
|
function injectTokenIntoUrl(url, token) {
|
|
@@ -9049,7 +9157,7 @@ function repoNameFromUrl(url) {
|
|
|
9049
9157
|
}
|
|
9050
9158
|
}
|
|
9051
9159
|
function defaultMarketplacePath(url) {
|
|
9052
|
-
return
|
|
9160
|
+
return path8.join(os.homedir(), ".agents", "marketplaces", repoNameFromUrl(url));
|
|
9053
9161
|
}
|
|
9054
9162
|
function marketplaceLabel(m) {
|
|
9055
9163
|
return m.name ?? repoNameFromUrl(m.repoUrl ?? "");
|
|
@@ -9063,8 +9171,8 @@ function assertInteractive(hint) {
|
|
|
9063
9171
|
}
|
|
9064
9172
|
}
|
|
9065
9173
|
var AGENT_PATHS = {
|
|
9066
|
-
"github-copilot": { project: ".agents/skills", user:
|
|
9067
|
-
"claude-code": { project: ".claude/skills", user:
|
|
9174
|
+
"github-copilot": { project: ".agents/skills", user: path8.join(os.homedir(), ".agents/skills") },
|
|
9175
|
+
"claude-code": { project: ".claude/skills", user: path8.join(os.homedir(), ".claude/skills") }
|
|
9068
9176
|
};
|
|
9069
9177
|
function resolveAgentName(opts) {
|
|
9070
9178
|
return opts.claude ? "claude-code" : opts.agent ?? "github-copilot";
|
|
@@ -9076,18 +9184,25 @@ function resolveAgentPaths(agentName) {
|
|
|
9076
9184
|
}
|
|
9077
9185
|
return agentConfig;
|
|
9078
9186
|
}
|
|
9187
|
+
function resolveTargetDir(opts) {
|
|
9188
|
+
if (opts.target) return path8.resolve(opts.target);
|
|
9189
|
+
const agentConfig = resolveAgentPaths(resolveAgentName(opts));
|
|
9190
|
+
const scopePath = (opts.scope ?? "user") === "project" ? agentConfig.project : agentConfig.user;
|
|
9191
|
+
return path8.resolve(scopePath);
|
|
9192
|
+
}
|
|
9079
9193
|
function getBundledSkillsDir() {
|
|
9080
9194
|
try {
|
|
9081
9195
|
const thisFile = fileURLToPath(import.meta.url);
|
|
9082
|
-
return
|
|
9196
|
+
return path8.resolve(path8.dirname(thisFile), "..", "skills");
|
|
9083
9197
|
} catch {
|
|
9084
9198
|
return "";
|
|
9085
9199
|
}
|
|
9086
9200
|
}
|
|
9087
9201
|
function getInstalledMetaPath(targetDir) {
|
|
9088
|
-
return
|
|
9202
|
+
return path8.join(targetDir, ".pncli-installed.json");
|
|
9089
9203
|
}
|
|
9090
9204
|
var SKILL_ORIGIN_FILENAME = "pncli-origin.json";
|
|
9205
|
+
var DISABLED_SUBDIR = ".pncli-disabled";
|
|
9091
9206
|
function readInstalledMeta(targetDir) {
|
|
9092
9207
|
const metaPath = getInstalledMetaPath(targetDir);
|
|
9093
9208
|
const raw = loadJsonFile(metaPath);
|
|
@@ -9095,7 +9210,7 @@ function readInstalledMeta(targetDir) {
|
|
|
9095
9210
|
return { version: 1, skills: {} };
|
|
9096
9211
|
}
|
|
9097
9212
|
function getSkillOriginPath(skillDir) {
|
|
9098
|
-
return
|
|
9213
|
+
return path8.join(skillDir, SKILL_ORIGIN_FILENAME);
|
|
9099
9214
|
}
|
|
9100
9215
|
function readSkillOrigin(skillDir) {
|
|
9101
9216
|
const originPath = getSkillOriginPath(skillDir);
|
|
@@ -9107,6 +9222,132 @@ function readSkillOrigin(skillDir) {
|
|
|
9107
9222
|
}
|
|
9108
9223
|
return null;
|
|
9109
9224
|
}
|
|
9225
|
+
function matchesMarketplaceFilter(record, filter) {
|
|
9226
|
+
return !filter || record.marketplace === filter || record.installedFrom === filter;
|
|
9227
|
+
}
|
|
9228
|
+
function resolveSkillRecord(meta, targetDir, skillName) {
|
|
9229
|
+
const fromMeta = meta.skills[skillName];
|
|
9230
|
+
if (fromMeta) return fromMeta;
|
|
9231
|
+
const perSkill = readSkillOrigin(path8.join(targetDir, skillName));
|
|
9232
|
+
if (!perSkill) return null;
|
|
9233
|
+
return {
|
|
9234
|
+
source: perSkill.source,
|
|
9235
|
+
marketplace: perSkill.marketplace,
|
|
9236
|
+
plugin: perSkill.plugin,
|
|
9237
|
+
installedFrom: perSkill.installedFrom,
|
|
9238
|
+
branch: perSkill.branch,
|
|
9239
|
+
installedAt: perSkill.installedAt
|
|
9240
|
+
};
|
|
9241
|
+
}
|
|
9242
|
+
function listActiveSkillDirs(targetDir) {
|
|
9243
|
+
return fs4.readdirSync(targetDir).filter((name) => {
|
|
9244
|
+
if (name.startsWith(".")) return false;
|
|
9245
|
+
const p = path8.join(targetDir, name);
|
|
9246
|
+
try {
|
|
9247
|
+
return fs4.statSync(p).isDirectory();
|
|
9248
|
+
} catch {
|
|
9249
|
+
return false;
|
|
9250
|
+
}
|
|
9251
|
+
});
|
|
9252
|
+
}
|
|
9253
|
+
function disablePluginSkills(targetDir, plugin, marketplaceFilter) {
|
|
9254
|
+
const resolvedTarget = path8.resolve(targetDir);
|
|
9255
|
+
const stashDir = path8.join(targetDir, DISABLED_SUBDIR);
|
|
9256
|
+
const meta = readInstalledMeta(targetDir);
|
|
9257
|
+
const disabled = [];
|
|
9258
|
+
const alreadyDisabled = [];
|
|
9259
|
+
const skipped = [];
|
|
9260
|
+
for (const skillName of listActiveSkillDirs(targetDir)) {
|
|
9261
|
+
const skillDir = path8.resolve(targetDir, skillName);
|
|
9262
|
+
if (!skillDir.startsWith(resolvedTarget + path8.sep)) continue;
|
|
9263
|
+
const record = resolveSkillRecord(meta, targetDir, skillName);
|
|
9264
|
+
if (!record || record.source !== "marketplace") {
|
|
9265
|
+
skipped.push(skillName);
|
|
9266
|
+
continue;
|
|
9267
|
+
}
|
|
9268
|
+
if (!matchesMarketplaceFilter(record, marketplaceFilter) || record.plugin !== plugin) {
|
|
9269
|
+
skipped.push(skillName);
|
|
9270
|
+
continue;
|
|
9271
|
+
}
|
|
9272
|
+
const stashDest = path8.join(stashDir, skillName);
|
|
9273
|
+
fs4.mkdirSync(stashDir, { recursive: true });
|
|
9274
|
+
if (fs4.existsSync(stashDest)) fs4.rmSync(stashDest, { recursive: true, force: true });
|
|
9275
|
+
fs4.renameSync(skillDir, stashDest);
|
|
9276
|
+
delete meta.skills[skillName];
|
|
9277
|
+
meta.disabled = meta.disabled ?? {};
|
|
9278
|
+
meta.disabled[skillName] = record;
|
|
9279
|
+
disabled.push(skillName);
|
|
9280
|
+
}
|
|
9281
|
+
for (const [skillName, record] of Object.entries(meta.disabled ?? {})) {
|
|
9282
|
+
if (record.plugin === plugin && matchesMarketplaceFilter(record, marketplaceFilter) && !disabled.includes(skillName)) {
|
|
9283
|
+
alreadyDisabled.push(skillName);
|
|
9284
|
+
}
|
|
9285
|
+
}
|
|
9286
|
+
if (disabled.length > 0) {
|
|
9287
|
+
fs4.writeFileSync(getInstalledMetaPath(targetDir), JSON.stringify(meta, null, 2), "utf8");
|
|
9288
|
+
}
|
|
9289
|
+
return { disabled, alreadyDisabled, skipped };
|
|
9290
|
+
}
|
|
9291
|
+
function enablePluginSkills(targetDir, plugin, marketplaceFilter) {
|
|
9292
|
+
const resolvedTarget = path8.resolve(targetDir);
|
|
9293
|
+
const stashDir = path8.join(targetDir, DISABLED_SUBDIR);
|
|
9294
|
+
const meta = readInstalledMeta(targetDir);
|
|
9295
|
+
const enabled = [];
|
|
9296
|
+
const stashMissing = [];
|
|
9297
|
+
const skipped = [];
|
|
9298
|
+
const disabledEntries = Object.entries(meta.disabled ?? {});
|
|
9299
|
+
for (const [skillName, record] of disabledEntries) {
|
|
9300
|
+
if (record.plugin !== plugin || !matchesMarketplaceFilter(record, marketplaceFilter)) {
|
|
9301
|
+
skipped.push(skillName);
|
|
9302
|
+
continue;
|
|
9303
|
+
}
|
|
9304
|
+
const dest = path8.resolve(targetDir, skillName);
|
|
9305
|
+
if (!dest.startsWith(resolvedTarget + path8.sep)) {
|
|
9306
|
+
skipped.push(skillName);
|
|
9307
|
+
continue;
|
|
9308
|
+
}
|
|
9309
|
+
const stashSrc = path8.join(stashDir, skillName);
|
|
9310
|
+
if (!fs4.existsSync(stashSrc)) {
|
|
9311
|
+
stashMissing.push(skillName);
|
|
9312
|
+
continue;
|
|
9313
|
+
}
|
|
9314
|
+
if (fs4.existsSync(dest)) fs4.rmSync(dest, { recursive: true, force: true });
|
|
9315
|
+
fs4.renameSync(stashSrc, dest);
|
|
9316
|
+
meta.skills[skillName] = record;
|
|
9317
|
+
delete meta.disabled[skillName];
|
|
9318
|
+
enabled.push(skillName);
|
|
9319
|
+
}
|
|
9320
|
+
if (fs4.existsSync(stashDir) && fs4.readdirSync(stashDir).length === 0) {
|
|
9321
|
+
fs4.rmdirSync(stashDir);
|
|
9322
|
+
}
|
|
9323
|
+
if (enabled.length > 0) {
|
|
9324
|
+
fs4.writeFileSync(getInstalledMetaPath(targetDir), JSON.stringify(meta, null, 2), "utf8");
|
|
9325
|
+
}
|
|
9326
|
+
return { enabled, stashMissing, skipped, hadDisabled: disabledEntries.length > 0 };
|
|
9327
|
+
}
|
|
9328
|
+
function listPluginStates(targetDir) {
|
|
9329
|
+
const meta = readInstalledMeta(targetDir);
|
|
9330
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
9331
|
+
const bucketFor = (record) => {
|
|
9332
|
+
if (record.source !== "marketplace" || !record.plugin) return null;
|
|
9333
|
+
const marketplaceName = record.marketplace ?? record.installedFrom;
|
|
9334
|
+
const key = `${marketplaceName ?? ""}\0${record.plugin}`;
|
|
9335
|
+
let state = byKey.get(key);
|
|
9336
|
+
if (!state) {
|
|
9337
|
+
state = { plugin: record.plugin, marketplace: marketplaceName, activeSkills: [], disabledSkills: [] };
|
|
9338
|
+
byKey.set(key, state);
|
|
9339
|
+
}
|
|
9340
|
+
return state;
|
|
9341
|
+
};
|
|
9342
|
+
for (const skillName of listActiveSkillDirs(targetDir)) {
|
|
9343
|
+
const record = resolveSkillRecord(meta, targetDir, skillName);
|
|
9344
|
+
if (record) bucketFor(record)?.activeSkills.push(skillName);
|
|
9345
|
+
}
|
|
9346
|
+
for (const [skillName, record] of Object.entries(meta.disabled ?? {})) {
|
|
9347
|
+
bucketFor(record)?.disabledSkills.push(skillName);
|
|
9348
|
+
}
|
|
9349
|
+
return [...byKey.values()].sort((a, b) => a.plugin.localeCompare(b.plugin));
|
|
9350
|
+
}
|
|
9110
9351
|
function detectRepoBranch(repoPath) {
|
|
9111
9352
|
try {
|
|
9112
9353
|
const result = execFileSync3("git", ["-C", repoPath, "rev-parse", "--abbrev-ref", "HEAD"], {
|
|
@@ -9125,7 +9366,7 @@ function recordInstalledSkills(targetDir, skillNames, record) {
|
|
|
9125
9366
|
for (const skillName of skillNames) {
|
|
9126
9367
|
const fullRecord = { ...record, installedAt: now };
|
|
9127
9368
|
meta.skills[skillName] = fullRecord;
|
|
9128
|
-
const skillDir =
|
|
9369
|
+
const skillDir = path8.join(targetDir, skillName);
|
|
9129
9370
|
if (fs4.existsSync(skillDir)) {
|
|
9130
9371
|
const origin = { version: 1, ...fullRecord };
|
|
9131
9372
|
try {
|
|
@@ -9195,7 +9436,7 @@ function upsertMarketplace(all, entry) {
|
|
|
9195
9436
|
}
|
|
9196
9437
|
}
|
|
9197
9438
|
function cloneOrReuseMarketplace(url, resolvedPath, opts) {
|
|
9198
|
-
const hasGit = fs4.existsSync(
|
|
9439
|
+
const hasGit = fs4.existsSync(path8.join(resolvedPath, ".git"));
|
|
9199
9440
|
if (fs4.existsSync(resolvedPath) && !hasGit && fs4.readdirSync(resolvedPath).length > 0) {
|
|
9200
9441
|
throw new Error(`Directory already exists and is not a git repo: ${resolvedPath}`);
|
|
9201
9442
|
}
|
|
@@ -9263,7 +9504,7 @@ function installPluginsForMarketplace(marketplacePath, marketplaceName, repoUrl,
|
|
|
9263
9504
|
results[pluginName] = { installed, failed };
|
|
9264
9505
|
totalInstalled += installed.length;
|
|
9265
9506
|
for (const skill of installed) {
|
|
9266
|
-
warn(` ${skill}: ${
|
|
9507
|
+
warn(` ${skill}: ${path8.join(skillsSrc, skill)} \u2192 ${path8.join(targetDir, skill)}`);
|
|
9267
9508
|
}
|
|
9268
9509
|
if (failed.length > 0) {
|
|
9269
9510
|
warn(`Skipped ${failed.length} skill(s) with invalid names in "${pluginName}": ${failed.join(", ")}`);
|
|
@@ -9315,34 +9556,55 @@ function syncMarketplacePlugins(m, targetDir, force, pluginFilter) {
|
|
|
9315
9556
|
return { marketplace: marketplaceName, skipped: true, error: message };
|
|
9316
9557
|
}
|
|
9317
9558
|
}
|
|
9559
|
+
function performMarketplaceAdd(url, localPath, opts) {
|
|
9560
|
+
const resolvedPath = path8.resolve(localPath ?? defaultMarketplacePath(url));
|
|
9561
|
+
const marketplaceName = opts.name ?? repoNameFromUrl(url);
|
|
9562
|
+
cloneOrReuseMarketplace(url, resolvedPath, opts);
|
|
9563
|
+
const configPath = getGlobalConfigPath();
|
|
9564
|
+
const { existing, all } = loadMarketplacesConfig(configPath);
|
|
9565
|
+
const entry = {
|
|
9566
|
+
name: marketplaceName,
|
|
9567
|
+
repoUrl: url,
|
|
9568
|
+
localPath: resolvedPath,
|
|
9569
|
+
...opts.token ? { token: opts.token } : {}
|
|
9570
|
+
};
|
|
9571
|
+
upsertMarketplace(all, entry);
|
|
9572
|
+
saveMarketplaces(configPath, existing, all);
|
|
9573
|
+
const targetDir = resolveAgentPaths(resolveAgentName(opts)).user;
|
|
9574
|
+
const { pluginResults, totalInstalled } = installAllPlugins(resolvedPath, marketplaceName, url, targetDir);
|
|
9575
|
+
return {
|
|
9576
|
+
name: marketplaceName,
|
|
9577
|
+
repoUrl: url,
|
|
9578
|
+
localPath: resolvedPath,
|
|
9579
|
+
branch: opts.branch ?? null,
|
|
9580
|
+
tokenConfigured: !!opts.token,
|
|
9581
|
+
plugins: pluginResults,
|
|
9582
|
+
total: totalInstalled,
|
|
9583
|
+
target: targetDir
|
|
9584
|
+
};
|
|
9585
|
+
}
|
|
9586
|
+
function performMarketplaceRemove(name) {
|
|
9587
|
+
const configPath = getGlobalConfigPath();
|
|
9588
|
+
const { existing, all } = loadMarketplacesConfig(configPath);
|
|
9589
|
+
const idx = all.findIndex((m) => m.name === name || m.repoUrl === name);
|
|
9590
|
+
if (idx === -1) {
|
|
9591
|
+
throw new Error(`Marketplace "${name}" not found. Run: pncli skills marketplace list`);
|
|
9592
|
+
}
|
|
9593
|
+
const removed = all.splice(idx, 1)[0];
|
|
9594
|
+
saveMarketplaces(configPath, existing, all);
|
|
9595
|
+
return {
|
|
9596
|
+
removed: {
|
|
9597
|
+
name: marketplaceLabel(removed),
|
|
9598
|
+
repoUrl: removed.repoUrl,
|
|
9599
|
+
localPath: removed.localPath
|
|
9600
|
+
},
|
|
9601
|
+
remaining: all.length
|
|
9602
|
+
};
|
|
9603
|
+
}
|
|
9318
9604
|
async function marketplaceAddAction(url, localPath, opts, commandName) {
|
|
9319
9605
|
const start = Date.now();
|
|
9320
9606
|
try {
|
|
9321
|
-
|
|
9322
|
-
const marketplaceName = opts.name ?? repoNameFromUrl(url);
|
|
9323
|
-
cloneOrReuseMarketplace(url, resolvedPath, opts);
|
|
9324
|
-
const configPath = getGlobalConfigPath();
|
|
9325
|
-
const { existing, all } = loadMarketplacesConfig(configPath);
|
|
9326
|
-
const entry = {
|
|
9327
|
-
name: marketplaceName,
|
|
9328
|
-
repoUrl: url,
|
|
9329
|
-
localPath: resolvedPath,
|
|
9330
|
-
...opts.token ? { token: opts.token } : {}
|
|
9331
|
-
};
|
|
9332
|
-
upsertMarketplace(all, entry);
|
|
9333
|
-
saveMarketplaces(configPath, existing, all);
|
|
9334
|
-
const targetDir = resolveAgentPaths(resolveAgentName(opts)).user;
|
|
9335
|
-
const { pluginResults, totalInstalled } = installAllPlugins(resolvedPath, marketplaceName, url, targetDir);
|
|
9336
|
-
success({
|
|
9337
|
-
name: marketplaceName,
|
|
9338
|
-
repoUrl: url,
|
|
9339
|
-
localPath: resolvedPath,
|
|
9340
|
-
branch: opts.branch ?? null,
|
|
9341
|
-
tokenConfigured: !!opts.token,
|
|
9342
|
-
plugins: pluginResults,
|
|
9343
|
-
total: totalInstalled,
|
|
9344
|
-
target: targetDir
|
|
9345
|
-
}, "skills", commandName, start);
|
|
9607
|
+
success(performMarketplaceAdd(url, localPath, opts), "skills", commandName, start);
|
|
9346
9608
|
} catch (err) {
|
|
9347
9609
|
fail(err, "skills", commandName, start);
|
|
9348
9610
|
}
|
|
@@ -9354,19 +9616,19 @@ function registerSkillsCommands(program2) {
|
|
|
9354
9616
|
try {
|
|
9355
9617
|
let targetDir;
|
|
9356
9618
|
if (opts.target) {
|
|
9357
|
-
targetDir =
|
|
9619
|
+
targetDir = path8.resolve(opts.target);
|
|
9358
9620
|
} else {
|
|
9359
9621
|
const agentConfig = resolveAgentPaths(opts.agent);
|
|
9360
9622
|
const scopePath = opts.scope === "user" ? agentConfig.user : agentConfig.project;
|
|
9361
|
-
targetDir =
|
|
9623
|
+
targetDir = path8.resolve(scopePath);
|
|
9362
9624
|
}
|
|
9363
|
-
const resolvedTarget =
|
|
9625
|
+
const resolvedTarget = path8.resolve(targetDir);
|
|
9364
9626
|
const bundledDir = getBundledSkillsDir();
|
|
9365
9627
|
let skillDirs = [];
|
|
9366
9628
|
if (bundledDir !== "") {
|
|
9367
9629
|
try {
|
|
9368
9630
|
skillDirs = fs4.readdirSync(bundledDir).filter(
|
|
9369
|
-
(name) => fs4.existsSync(
|
|
9631
|
+
(name) => fs4.existsSync(path8.join(bundledDir, name, "SKILL.md"))
|
|
9370
9632
|
);
|
|
9371
9633
|
} catch {
|
|
9372
9634
|
}
|
|
@@ -9377,25 +9639,25 @@ function registerSkillsCommands(program2) {
|
|
|
9377
9639
|
const installed = [];
|
|
9378
9640
|
const failed = [];
|
|
9379
9641
|
for (const skillName of skillDirs) {
|
|
9380
|
-
const existingDir =
|
|
9381
|
-
if (!existingDir.startsWith(resolvedTarget +
|
|
9642
|
+
const existingDir = path8.resolve(targetDir, skillName);
|
|
9643
|
+
if (!existingDir.startsWith(resolvedTarget + path8.sep)) continue;
|
|
9382
9644
|
if (fs4.existsSync(existingDir)) {
|
|
9383
9645
|
fs4.rmSync(existingDir, { recursive: true, force: true });
|
|
9384
9646
|
}
|
|
9385
9647
|
}
|
|
9386
9648
|
warn(`Installing ${skillDirs.length} bundled skill(s) to ${targetDir}...`);
|
|
9387
9649
|
for (const skillName of skillDirs) {
|
|
9388
|
-
const skillDir =
|
|
9389
|
-
if (!skillDir.startsWith(resolvedTarget +
|
|
9650
|
+
const skillDir = path8.resolve(targetDir, skillName);
|
|
9651
|
+
if (!skillDir.startsWith(resolvedTarget + path8.sep)) {
|
|
9390
9652
|
failed.push(skillName);
|
|
9391
9653
|
continue;
|
|
9392
9654
|
}
|
|
9393
9655
|
try {
|
|
9394
|
-
const mdFiles = fs4.readdirSync(
|
|
9656
|
+
const mdFiles = fs4.readdirSync(path8.join(bundledDir, skillName)).filter((f) => f.endsWith(".md"));
|
|
9395
9657
|
fs4.mkdirSync(skillDir, { recursive: true });
|
|
9396
9658
|
for (const mdFile of mdFiles) {
|
|
9397
|
-
const content = fs4.readFileSync(
|
|
9398
|
-
fs4.writeFileSync(
|
|
9659
|
+
const content = fs4.readFileSync(path8.join(bundledDir, skillName, mdFile), "utf8");
|
|
9660
|
+
fs4.writeFileSync(path8.join(skillDir, mdFile), content, "utf8");
|
|
9399
9661
|
}
|
|
9400
9662
|
installed.push(skillName);
|
|
9401
9663
|
} catch {
|
|
@@ -9425,11 +9687,11 @@ function registerSkillsCommands(program2) {
|
|
|
9425
9687
|
try {
|
|
9426
9688
|
let targetDir;
|
|
9427
9689
|
if (opts.target) {
|
|
9428
|
-
targetDir =
|
|
9690
|
+
targetDir = path8.resolve(opts.target);
|
|
9429
9691
|
} else {
|
|
9430
9692
|
const agentConfig = resolveAgentPaths(opts.agent);
|
|
9431
9693
|
const scopePath = opts.scope === "user" ? agentConfig.user : agentConfig.project;
|
|
9432
|
-
targetDir =
|
|
9694
|
+
targetDir = path8.resolve(scopePath);
|
|
9433
9695
|
}
|
|
9434
9696
|
if (!fs4.existsSync(targetDir)) {
|
|
9435
9697
|
success({ skills: [], message: `No skills directory found at ${targetDir}. Run: pncli skills install` }, "skills", "list", start);
|
|
@@ -9438,11 +9700,11 @@ function registerSkillsCommands(program2) {
|
|
|
9438
9700
|
const meta = readInstalledMeta(targetDir);
|
|
9439
9701
|
const skillDirs = fs4.readdirSync(targetDir).filter((name) => {
|
|
9440
9702
|
if (name.startsWith(".")) return false;
|
|
9441
|
-
const skillPath =
|
|
9703
|
+
const skillPath = path8.join(targetDir, name, "SKILL.md");
|
|
9442
9704
|
return fs4.existsSync(skillPath);
|
|
9443
9705
|
});
|
|
9444
9706
|
const skillsList = skillDirs.map((name) => {
|
|
9445
|
-
const content = fs4.readFileSync(
|
|
9707
|
+
const content = fs4.readFileSync(path8.join(targetDir, name, "SKILL.md"), "utf8");
|
|
9446
9708
|
const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
9447
9709
|
const data = {};
|
|
9448
9710
|
const metadata = {};
|
|
@@ -9486,15 +9748,15 @@ function registerSkillsCommands(program2) {
|
|
|
9486
9748
|
try {
|
|
9487
9749
|
let targetDir;
|
|
9488
9750
|
if (opts.target) {
|
|
9489
|
-
targetDir =
|
|
9751
|
+
targetDir = path8.resolve(opts.target);
|
|
9490
9752
|
} else {
|
|
9491
9753
|
const agentConfig = resolveAgentPaths(resolveAgentName(opts));
|
|
9492
9754
|
const scopePath = opts.scope === "project" ? agentConfig.project : agentConfig.user;
|
|
9493
|
-
targetDir =
|
|
9755
|
+
targetDir = path8.resolve(scopePath);
|
|
9494
9756
|
}
|
|
9495
|
-
const resolvedTarget =
|
|
9496
|
-
const skillDir =
|
|
9497
|
-
if (!skillDir.startsWith(resolvedTarget +
|
|
9757
|
+
const resolvedTarget = path8.resolve(targetDir);
|
|
9758
|
+
const skillDir = path8.resolve(targetDir, name);
|
|
9759
|
+
if (!skillDir.startsWith(resolvedTarget + path8.sep)) {
|
|
9498
9760
|
throw new Error(`Invalid skill name: "${name}"`);
|
|
9499
9761
|
}
|
|
9500
9762
|
if (!fs4.existsSync(skillDir)) {
|
|
@@ -9568,27 +9830,12 @@ function registerSkillsCommands(program2) {
|
|
|
9568
9830
|
marketplace.command("remove").description("Remove a registered marketplace from the config (does not delete the local clone)").argument("<name>", "Name of the marketplace to remove").action((name) => {
|
|
9569
9831
|
const start = Date.now();
|
|
9570
9832
|
try {
|
|
9571
|
-
|
|
9572
|
-
const { existing, all } = loadMarketplacesConfig(configPath);
|
|
9573
|
-
const idx = all.findIndex((m) => m.name === name || m.repoUrl === name);
|
|
9574
|
-
if (idx === -1) {
|
|
9575
|
-
throw new Error(`Marketplace "${name}" not found. Run: pncli skills marketplace list`);
|
|
9576
|
-
}
|
|
9577
|
-
const removed = all.splice(idx, 1)[0];
|
|
9578
|
-
saveMarketplaces(configPath, existing, all);
|
|
9579
|
-
success({
|
|
9580
|
-
removed: {
|
|
9581
|
-
name: marketplaceLabel(removed),
|
|
9582
|
-
repoUrl: removed.repoUrl,
|
|
9583
|
-
localPath: removed.localPath
|
|
9584
|
-
},
|
|
9585
|
-
remaining: all.length
|
|
9586
|
-
}, "skills", "marketplace-remove", start);
|
|
9833
|
+
success(performMarketplaceRemove(name), "skills", "marketplace-remove", start);
|
|
9587
9834
|
} catch (err) {
|
|
9588
9835
|
fail(err, "skills", "marketplace-remove", start);
|
|
9589
9836
|
}
|
|
9590
9837
|
});
|
|
9591
|
-
marketplace.command("sync").description("Pull latest marketplace content and install plugin skills").argument("[plugin]", 'Plugin name to install, or "all" to install every plugin (skips interactive selection)').option("--marketplace <name>", 'Marketplace name to sync, or "all" to sync every registered marketplace (skips interactive selection)').option("--agent <agent>", "Target agent host: github-copilot | claude-code (default: github-copilot)").option("--claude", "Shorthand for --agent claude-code").option("--force", 'Reinstall even if a marketplace has no new changes (applies to single-plugin and "all" installs alike)').action(async (plugin, opts) => {
|
|
9838
|
+
marketplace.command("sync").description("Pull latest marketplace content and install plugin skills (interactive picker when no plugin is given)").argument("[plugin]", 'Plugin name to install, or "all" to install every plugin (skips interactive selection)').option("--marketplace <name>", 'Marketplace name to sync, or "all" to sync every registered marketplace (skips interactive selection)').option("--agent <agent>", "Target agent host: github-copilot | claude-code (default: github-copilot)").option("--claude", "Shorthand for --agent claude-code").option("--force", 'Reinstall even if a marketplace has no new changes (applies to single-plugin and "all" installs alike)').action(async (plugin, opts) => {
|
|
9592
9839
|
const start = Date.now();
|
|
9593
9840
|
try {
|
|
9594
9841
|
const configPath = getGlobalConfigPath();
|
|
@@ -9701,13 +9948,13 @@ function registerSkillsCommands(program2) {
|
|
|
9701
9948
|
try {
|
|
9702
9949
|
let targetDir;
|
|
9703
9950
|
if (opts.target) {
|
|
9704
|
-
targetDir =
|
|
9951
|
+
targetDir = path8.resolve(opts.target);
|
|
9705
9952
|
} else {
|
|
9706
9953
|
const agentConfig = resolveAgentPaths(resolveAgentName(opts));
|
|
9707
9954
|
const scopePath = (opts.scope ?? "user") === "project" ? agentConfig.project : agentConfig.user;
|
|
9708
|
-
targetDir =
|
|
9955
|
+
targetDir = path8.resolve(scopePath);
|
|
9709
9956
|
}
|
|
9710
|
-
const resolvedTarget =
|
|
9957
|
+
const resolvedTarget = path8.resolve(targetDir);
|
|
9711
9958
|
if (!fs4.existsSync(targetDir)) {
|
|
9712
9959
|
success({ removed: [], total: 0, target: targetDir, message: "No skills directory found." }, "skills", "marketplace-purge-plugin", start);
|
|
9713
9960
|
return;
|
|
@@ -9717,7 +9964,7 @@ function registerSkillsCommands(program2) {
|
|
|
9717
9964
|
const skipped = [];
|
|
9718
9965
|
const skillDirs = fs4.readdirSync(targetDir).filter((name) => {
|
|
9719
9966
|
if (name.startsWith(".")) return false;
|
|
9720
|
-
const p =
|
|
9967
|
+
const p = path8.join(targetDir, name);
|
|
9721
9968
|
try {
|
|
9722
9969
|
return fs4.statSync(p).isDirectory();
|
|
9723
9970
|
} catch {
|
|
@@ -9725,8 +9972,8 @@ function registerSkillsCommands(program2) {
|
|
|
9725
9972
|
}
|
|
9726
9973
|
});
|
|
9727
9974
|
for (const skillName of skillDirs) {
|
|
9728
|
-
const skillDir =
|
|
9729
|
-
if (!skillDir.startsWith(resolvedTarget +
|
|
9975
|
+
const skillDir = path8.resolve(targetDir, skillName);
|
|
9976
|
+
if (!skillDir.startsWith(resolvedTarget + path8.sep)) continue;
|
|
9730
9977
|
let record = meta.skills[skillName] ?? null;
|
|
9731
9978
|
if (!record) {
|
|
9732
9979
|
const perSkill = readSkillOrigin(skillDir);
|
|
@@ -9770,6 +10017,161 @@ function registerSkillsCommands(program2) {
|
|
|
9770
10017
|
fail(err, "skills", "marketplace-purge-plugin", start);
|
|
9771
10018
|
}
|
|
9772
10019
|
});
|
|
10020
|
+
marketplace.command("disable").description("Temporarily deactivate a plugin's skills without deleting them (re-enable with `marketplace enable`)").argument("<plugin>", "Plugin name to disable").option("--marketplace <name>", "Restrict to skills from this marketplace (by name or repo URL)").option("--agent <agent>", "Target agent host: github-copilot | claude-code (default: github-copilot)").option("--claude", "Shorthand for --agent claude-code").option("--scope <scope>", "Installation scope: project | user (default: user)").option("--target <dir>", "Override skills directory").action((plugin, opts) => {
|
|
10021
|
+
const start = Date.now();
|
|
10022
|
+
try {
|
|
10023
|
+
const targetDir = resolveTargetDir(opts);
|
|
10024
|
+
if (!fs4.existsSync(targetDir)) {
|
|
10025
|
+
success({ disabled: [], total: 0, target: targetDir, message: "No skills directory found." }, "skills", "marketplace-disable", start);
|
|
10026
|
+
return;
|
|
10027
|
+
}
|
|
10028
|
+
const result = disablePluginSkills(targetDir, plugin, opts.marketplace);
|
|
10029
|
+
success({
|
|
10030
|
+
disabled: result.disabled,
|
|
10031
|
+
alreadyDisabled: result.alreadyDisabled,
|
|
10032
|
+
skipped: result.skipped.length,
|
|
10033
|
+
total: result.disabled.length,
|
|
10034
|
+
target: targetDir,
|
|
10035
|
+
plugin,
|
|
10036
|
+
stash: path8.join(targetDir, DISABLED_SUBDIR),
|
|
10037
|
+
...opts.marketplace ? { marketplace: opts.marketplace } : {}
|
|
10038
|
+
}, "skills", "marketplace-disable", start);
|
|
10039
|
+
} catch (err) {
|
|
10040
|
+
fail(err, "skills", "marketplace-disable", start);
|
|
10041
|
+
}
|
|
10042
|
+
});
|
|
10043
|
+
marketplace.command("enable").description("Re-activate a previously disabled plugin's skills").argument("<plugin>", "Plugin name to enable").option("--marketplace <name>", "Restrict to skills from this marketplace (by name or repo URL)").option("--agent <agent>", "Target agent host: github-copilot | claude-code (default: github-copilot)").option("--claude", "Shorthand for --agent claude-code").option("--scope <scope>", "Installation scope: project | user (default: user)").option("--target <dir>", "Override skills directory").action((plugin, opts) => {
|
|
10044
|
+
const start = Date.now();
|
|
10045
|
+
try {
|
|
10046
|
+
const targetDir = resolveTargetDir(opts);
|
|
10047
|
+
if (!fs4.existsSync(targetDir)) {
|
|
10048
|
+
success({ enabled: [], total: 0, target: targetDir, message: "No skills directory found." }, "skills", "marketplace-enable", start);
|
|
10049
|
+
return;
|
|
10050
|
+
}
|
|
10051
|
+
const result = enablePluginSkills(targetDir, plugin, opts.marketplace);
|
|
10052
|
+
if (!result.hadDisabled) {
|
|
10053
|
+
success({ enabled: [], total: 0, target: targetDir, plugin, message: `No disabled skills found for plugin "${plugin}".` }, "skills", "marketplace-enable", start);
|
|
10054
|
+
return;
|
|
10055
|
+
}
|
|
10056
|
+
success({
|
|
10057
|
+
enabled: result.enabled,
|
|
10058
|
+
skipped: result.skipped.length,
|
|
10059
|
+
total: result.enabled.length,
|
|
10060
|
+
target: targetDir,
|
|
10061
|
+
plugin,
|
|
10062
|
+
...result.stashMissing.length > 0 ? {
|
|
10063
|
+
stashMissing: result.stashMissing,
|
|
10064
|
+
warning: `Skills marked disabled but missing from the stash were left as-is. Re-install the plugin (pncli skills marketplace sync ${plugin}) to restore them.`
|
|
10065
|
+
} : {},
|
|
10066
|
+
...opts.marketplace ? { marketplace: opts.marketplace } : {}
|
|
10067
|
+
}, "skills", "marketplace-enable", start);
|
|
10068
|
+
} catch (err) {
|
|
10069
|
+
fail(err, "skills", "marketplace-enable", start);
|
|
10070
|
+
}
|
|
10071
|
+
});
|
|
10072
|
+
marketplace.command("manage").description("Manage marketplaces and plugins: toggle plugins on/off, add or remove marketplaces (interactive)").option("--agent <agent>", "Target agent host: github-copilot | claude-code (default: github-copilot)").option("--claude", "Shorthand for --agent claude-code").option("--scope <scope>", "Installation scope: project | user (default: user)").option("--target <dir>", "Override skills directory").action(async (opts) => {
|
|
10073
|
+
const start = Date.now();
|
|
10074
|
+
try {
|
|
10075
|
+
assertInteractive("Use `pncli skills marketplace enable|disable <plugin>`, `add <url>`, and `remove <name>` to manage non-interactively.");
|
|
10076
|
+
const targetDir = resolveTargetDir(opts);
|
|
10077
|
+
const enabledPlugins = [];
|
|
10078
|
+
const disabledPlugins = [];
|
|
10079
|
+
const stashMissing = [];
|
|
10080
|
+
const addedMarketplaces = [];
|
|
10081
|
+
const removedMarketplaces = [];
|
|
10082
|
+
for (; ; ) {
|
|
10083
|
+
const states = fs4.existsSync(targetDir) ? listPluginStates(targetDir) : [];
|
|
10084
|
+
const registered = loadMarketplaces(getGlobalConfigPath());
|
|
10085
|
+
const action = await select({
|
|
10086
|
+
message: `Manage skills marketplaces (target: ${targetDir}):`,
|
|
10087
|
+
choices: [
|
|
10088
|
+
...states.length > 0 ? [{ value: "toggle", name: `Toggle plugins on/off (${states.length} installed)` }] : [],
|
|
10089
|
+
{ value: "add", name: "Add a marketplace" },
|
|
10090
|
+
...registered.length > 0 ? [{ value: "remove", name: `Remove a marketplace (${registered.length} registered)` }] : [],
|
|
10091
|
+
{ value: "done", name: "Done" }
|
|
10092
|
+
]
|
|
10093
|
+
});
|
|
10094
|
+
if (action === "done") break;
|
|
10095
|
+
if (action === "toggle") {
|
|
10096
|
+
const sorted = [...states].sort((a, b) => (a.marketplace ?? "").localeCompare(b.marketplace ?? "") || a.plugin.localeCompare(b.plugin));
|
|
10097
|
+
const choices = [];
|
|
10098
|
+
let lastMarketplace = null;
|
|
10099
|
+
sorted.forEach((state, index) => {
|
|
10100
|
+
const marketplaceName = state.marketplace ?? "(unknown marketplace)";
|
|
10101
|
+
if (marketplaceName !== lastMarketplace) {
|
|
10102
|
+
choices.push(new Separator(`\u2500\u2500 ${marketplaceName} \u2500\u2500`));
|
|
10103
|
+
lastMarketplace = marketplaceName;
|
|
10104
|
+
}
|
|
10105
|
+
const active = state.activeSkills.length;
|
|
10106
|
+
const stashed = state.disabledSkills.length;
|
|
10107
|
+
const skillCount = stashed === 0 ? `${active} skill${active === 1 ? "" : "s"}` : active === 0 ? `${stashed} skill${stashed === 1 ? "" : "s"}, disabled` : `${active} active, ${stashed} disabled`;
|
|
10108
|
+
choices.push({ name: `${state.plugin} (${skillCount})`, value: index, checked: active > 0 });
|
|
10109
|
+
});
|
|
10110
|
+
const checkedIndexes = await checkbox({
|
|
10111
|
+
message: "Toggle plugins (space toggles, enter applies):",
|
|
10112
|
+
choices,
|
|
10113
|
+
pageSize: 15
|
|
10114
|
+
});
|
|
10115
|
+
const wanted = new Set(checkedIndexes);
|
|
10116
|
+
sorted.forEach((state, index) => {
|
|
10117
|
+
if (wanted.has(index)) {
|
|
10118
|
+
if (state.disabledSkills.length === 0) return;
|
|
10119
|
+
const result = enablePluginSkills(targetDir, state.plugin, state.marketplace);
|
|
10120
|
+
stashMissing.push(...result.stashMissing);
|
|
10121
|
+
if (result.enabled.length > 0) {
|
|
10122
|
+
enabledPlugins.push({ plugin: state.plugin, marketplace: state.marketplace, skills: result.enabled });
|
|
10123
|
+
}
|
|
10124
|
+
} else {
|
|
10125
|
+
if (state.activeSkills.length === 0) return;
|
|
10126
|
+
const result = disablePluginSkills(targetDir, state.plugin, state.marketplace);
|
|
10127
|
+
if (result.disabled.length > 0) {
|
|
10128
|
+
disabledPlugins.push({ plugin: state.plugin, marketplace: state.marketplace, skills: result.disabled });
|
|
10129
|
+
}
|
|
10130
|
+
}
|
|
10131
|
+
});
|
|
10132
|
+
} else if (action === "add") {
|
|
10133
|
+
const url = (await input2({
|
|
10134
|
+
message: "Git clone URL of the marketplace repository:",
|
|
10135
|
+
validate: (v) => v.trim().length > 0 || "URL is required"
|
|
10136
|
+
})).trim();
|
|
10137
|
+
const name = (await input2({
|
|
10138
|
+
message: "Marketplace name:",
|
|
10139
|
+
default: repoNameFromUrl(url)
|
|
10140
|
+
})).trim();
|
|
10141
|
+
addedMarketplaces.push(performMarketplaceAdd(url, void 0, {
|
|
10142
|
+
name: name || void 0,
|
|
10143
|
+
agent: opts.agent,
|
|
10144
|
+
claude: opts.claude
|
|
10145
|
+
}));
|
|
10146
|
+
} else if (action === "remove") {
|
|
10147
|
+
const chosen = await select({
|
|
10148
|
+
message: "Remove which marketplace? (the local clone is kept on disk)",
|
|
10149
|
+
choices: [
|
|
10150
|
+
// Index-based values: labels alone aren't guaranteed unique (two marketplaces can share a derived name).
|
|
10151
|
+
...registered.map((m, i) => ({ value: String(i), name: `${marketplaceLabel(m)} \u2014 ${m.repoUrl ?? ""}` })),
|
|
10152
|
+
{ value: BACK, name: "\u2190 Back" }
|
|
10153
|
+
]
|
|
10154
|
+
});
|
|
10155
|
+
if (chosen === BACK) continue;
|
|
10156
|
+
const target = registered[Number(chosen)];
|
|
10157
|
+
removedMarketplaces.push(performMarketplaceRemove(target.repoUrl ?? marketplaceLabel(target)));
|
|
10158
|
+
}
|
|
10159
|
+
}
|
|
10160
|
+
success({
|
|
10161
|
+
enabled: enabledPlugins,
|
|
10162
|
+
disabled: disabledPlugins,
|
|
10163
|
+
addedMarketplaces,
|
|
10164
|
+
removedMarketplaces,
|
|
10165
|
+
target: targetDir,
|
|
10166
|
+
...stashMissing.length > 0 ? {
|
|
10167
|
+
stashMissing,
|
|
10168
|
+
warning: "Skills marked disabled but missing from the stash were left as-is. Re-install their plugin (pncli skills marketplace sync <plugin>) to restore them."
|
|
10169
|
+
} : {}
|
|
10170
|
+
}, "skills", "marketplace-manage", start);
|
|
10171
|
+
} catch (err) {
|
|
10172
|
+
fail(err, "skills", "marketplace-manage", start);
|
|
10173
|
+
}
|
|
10174
|
+
});
|
|
9773
10175
|
skills.command("purge-user").description("Remove all skills from the user-level skills folder for the target agent").option("--agent <agent>", "Target agent host: github-copilot | claude-code (default: github-copilot)").option("--claude", "Shorthand for --agent claude-code").option("--force", "Skip confirmation \u2014 remove all skills without prompting").action((opts) => {
|
|
9774
10176
|
const start = Date.now();
|
|
9775
10177
|
try {
|
|
@@ -9780,7 +10182,7 @@ function registerSkillsCommands(program2) {
|
|
|
9780
10182
|
}
|
|
9781
10183
|
const skillDirs = fs4.readdirSync(userDir).filter((name) => {
|
|
9782
10184
|
if (name.startsWith(".")) return false;
|
|
9783
|
-
const p =
|
|
10185
|
+
const p = path8.join(userDir, name);
|
|
9784
10186
|
try {
|
|
9785
10187
|
return fs4.statSync(p).isDirectory();
|
|
9786
10188
|
} catch {
|
|
@@ -9798,8 +10200,8 @@ Re-run with --force to confirm, or use \`pncli skills marketplace purge-plugin\`
|
|
|
9798
10200
|
);
|
|
9799
10201
|
}
|
|
9800
10202
|
for (const name of skillDirs) {
|
|
9801
|
-
const skillDir =
|
|
9802
|
-
if (!skillDir.startsWith(
|
|
10203
|
+
const skillDir = path8.resolve(userDir, name);
|
|
10204
|
+
if (!skillDir.startsWith(path8.resolve(userDir) + path8.sep)) continue;
|
|
9803
10205
|
fs4.rmSync(skillDir, { recursive: true, force: true });
|
|
9804
10206
|
}
|
|
9805
10207
|
const metaPath = getInstalledMetaPath(userDir);
|
|
@@ -9813,7 +10215,7 @@ Re-run with --force to confirm, or use \`pncli skills marketplace purge-plugin\`
|
|
|
9813
10215
|
});
|
|
9814
10216
|
}
|
|
9815
10217
|
function resolvePluginChoices(marketplacePath) {
|
|
9816
|
-
const marketplaceJsonPath =
|
|
10218
|
+
const marketplaceJsonPath = path8.join(marketplacePath, ".claude-plugin", "marketplace.json");
|
|
9817
10219
|
if (fs4.existsSync(marketplaceJsonPath)) {
|
|
9818
10220
|
try {
|
|
9819
10221
|
const meta = JSON.parse(fs4.readFileSync(marketplaceJsonPath, "utf8"));
|
|
@@ -9823,36 +10225,36 @@ function resolvePluginChoices(marketplacePath) {
|
|
|
9823
10225
|
} catch {
|
|
9824
10226
|
}
|
|
9825
10227
|
}
|
|
9826
|
-
const pluginsDir =
|
|
10228
|
+
const pluginsDir = path8.join(marketplacePath, "plugins");
|
|
9827
10229
|
if (fs4.existsSync(pluginsDir)) {
|
|
9828
|
-
return fs4.readdirSync(pluginsDir).filter((name) => fs4.statSync(
|
|
10230
|
+
return fs4.readdirSync(pluginsDir).filter((name) => fs4.statSync(path8.join(pluginsDir, name)).isDirectory()).map((name) => ({ name, description: "" }));
|
|
9829
10231
|
}
|
|
9830
10232
|
return [];
|
|
9831
10233
|
}
|
|
9832
10234
|
function resolveSkillsSrc(marketplacePath, selectedPlugin) {
|
|
9833
|
-
const pluginsBase =
|
|
9834
|
-
const skillsSrc =
|
|
9835
|
-
if (!skillsSrc.startsWith(pluginsBase +
|
|
10235
|
+
const pluginsBase = path8.resolve(marketplacePath, "plugins");
|
|
10236
|
+
const skillsSrc = path8.resolve(pluginsBase, selectedPlugin, "skills");
|
|
10237
|
+
if (!skillsSrc.startsWith(pluginsBase + path8.sep)) {
|
|
9836
10238
|
throw new Error(`Invalid plugin name: "${selectedPlugin}"`);
|
|
9837
10239
|
}
|
|
9838
10240
|
return skillsSrc;
|
|
9839
10241
|
}
|
|
9840
10242
|
function copyPluginSkills(skillsSrc, targetDir, meta) {
|
|
9841
10243
|
fs4.mkdirSync(targetDir, { recursive: true });
|
|
9842
|
-
const resolvedTarget =
|
|
10244
|
+
const resolvedTarget = path8.resolve(targetDir);
|
|
9843
10245
|
const skillNames = fs4.readdirSync(skillsSrc).filter(
|
|
9844
|
-
(name) => fs4.statSync(
|
|
10246
|
+
(name) => fs4.statSync(path8.join(skillsSrc, name)).isDirectory()
|
|
9845
10247
|
);
|
|
9846
10248
|
const installed = [];
|
|
9847
10249
|
const failed = [];
|
|
9848
10250
|
for (const skillName of skillNames) {
|
|
9849
|
-
const dest =
|
|
9850
|
-
if (!dest.startsWith(resolvedTarget +
|
|
10251
|
+
const dest = path8.resolve(targetDir, skillName);
|
|
10252
|
+
if (!dest.startsWith(resolvedTarget + path8.sep)) {
|
|
9851
10253
|
failed.push(skillName);
|
|
9852
10254
|
continue;
|
|
9853
10255
|
}
|
|
9854
10256
|
fs4.rmSync(dest, { recursive: true, force: true });
|
|
9855
|
-
fs4.cpSync(
|
|
10257
|
+
fs4.cpSync(path8.join(skillsSrc, skillName), dest, { recursive: true });
|
|
9856
10258
|
installed.push(skillName);
|
|
9857
10259
|
}
|
|
9858
10260
|
if (meta) {
|
|
@@ -9868,8 +10270,8 @@ function copyPluginSkills(skillsSrc, targetDir, meta) {
|
|
|
9868
10270
|
}
|
|
9869
10271
|
|
|
9870
10272
|
// src/services/jwt/commands.ts
|
|
9871
|
-
function base64urlDecode(
|
|
9872
|
-
const base64 =
|
|
10273
|
+
function base64urlDecode(input3) {
|
|
10274
|
+
const base64 = input3.replace(/-/g, "+").replace(/_/g, "/");
|
|
9873
10275
|
const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
|
|
9874
10276
|
return Buffer.from(padded, "base64").toString("utf8");
|
|
9875
10277
|
}
|
|
@@ -10287,6 +10689,65 @@ function registerDynatraceCommands(program2) {
|
|
|
10287
10689
|
});
|
|
10288
10690
|
}
|
|
10289
10691
|
|
|
10692
|
+
// src/services/logscale/commands.ts
|
|
10693
|
+
function getHttp4(program2) {
|
|
10694
|
+
const opts = program2.optsWithGlobals();
|
|
10695
|
+
return createHttpClient(
|
|
10696
|
+
loadConfig({ configPath: opts.config }),
|
|
10697
|
+
Boolean(opts.dryRun)
|
|
10698
|
+
);
|
|
10699
|
+
}
|
|
10700
|
+
function registerLogscaleCommands(program2) {
|
|
10701
|
+
const logscale = program2.command("logscale").description("LogScale log query operations");
|
|
10702
|
+
const repositories = logscale.command("repositories").description("Repository operations");
|
|
10703
|
+
repositories.command("list").description("List available repositories").action(async () => {
|
|
10704
|
+
const start = Date.now();
|
|
10705
|
+
try {
|
|
10706
|
+
const data = await getHttp4(program2).logscale("/api/v1/repositories");
|
|
10707
|
+
success({ count: data.length, repositories: data }, "logscale", "repositories list", start);
|
|
10708
|
+
} catch (err) {
|
|
10709
|
+
fail(err, "logscale", "repositories list", start);
|
|
10710
|
+
}
|
|
10711
|
+
});
|
|
10712
|
+
logscale.command("query").description("Execute a LogScale Query Language (LQL) search against a repository").requiredOption("--repository <name>", "Repository name to query").requiredOption("--query <lql>", 'LogScale Query Language expression (e.g. "error" or "level=error | count()")').option("--start <time>", "Start time (ISO 8601 or relative such as 1h or 24h)", "1h").option("--end <time>", "End time (ISO 8601 or relative)", "now").option("--limit <n>", "Maximum number of events to return", "200").option("--timeout-ms <ms>", "Client-side HTTP timeout in milliseconds", "30000").action(async (opts) => {
|
|
10713
|
+
const ts = Date.now();
|
|
10714
|
+
try {
|
|
10715
|
+
const body = {
|
|
10716
|
+
queryString: opts.query,
|
|
10717
|
+
start: opts.start,
|
|
10718
|
+
end: opts.end,
|
|
10719
|
+
limit: parseInt(opts.limit, 10),
|
|
10720
|
+
showQueryEventDistribution: false,
|
|
10721
|
+
isLive: false
|
|
10722
|
+
};
|
|
10723
|
+
const data = await getHttp4(program2).logscale(
|
|
10724
|
+
`/api/v1/repositories/${encodeURIComponent(opts.repository)}/query`,
|
|
10725
|
+
{
|
|
10726
|
+
method: "POST",
|
|
10727
|
+
body,
|
|
10728
|
+
timeoutMs: parseInt(opts.timeoutMs, 10)
|
|
10729
|
+
}
|
|
10730
|
+
);
|
|
10731
|
+
success(
|
|
10732
|
+
{
|
|
10733
|
+
repository: opts.repository,
|
|
10734
|
+
done: data.done,
|
|
10735
|
+
cancelled: data.cancelled,
|
|
10736
|
+
eventCount: data.events.length,
|
|
10737
|
+
events: data.events,
|
|
10738
|
+
warnings: data.warnings ?? [],
|
|
10739
|
+
metaData: data.metaData
|
|
10740
|
+
},
|
|
10741
|
+
"logscale",
|
|
10742
|
+
"query",
|
|
10743
|
+
ts
|
|
10744
|
+
);
|
|
10745
|
+
} catch (err) {
|
|
10746
|
+
fail(err, "logscale", "query", ts);
|
|
10747
|
+
}
|
|
10748
|
+
});
|
|
10749
|
+
}
|
|
10750
|
+
|
|
10290
10751
|
// src/cli.ts
|
|
10291
10752
|
if (!process.env.PNCLI_VERIFY_TLS) {
|
|
10292
10753
|
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
|
@@ -10331,6 +10792,7 @@ registerSkillsCommands(program);
|
|
|
10331
10792
|
registerJwtCommands(program);
|
|
10332
10793
|
registerOpenShiftCommands(program);
|
|
10333
10794
|
registerDynatraceCommands(program);
|
|
10795
|
+
registerLogscaleCommands(program);
|
|
10334
10796
|
program.addHelpText("after", `
|
|
10335
10797
|
Services:
|
|
10336
10798
|
git Local git operations (status, diff, log, branch)
|
|
@@ -10351,6 +10813,7 @@ Services:
|
|
|
10351
10813
|
sonatypeiq Sonatype IQ Server (applications, organizations, policies)
|
|
10352
10814
|
openshift OpenShift / Kubernetes (pods, events, logs, metrics)
|
|
10353
10815
|
dynatrace Dynatrace (services, entities, problems, traces, Kubernetes workloads)
|
|
10816
|
+
logscale LogScale (log query, repositories)
|
|
10354
10817
|
config Manage pncli configuration
|
|
10355
10818
|
skills Download and manage Claude Code skills
|
|
10356
10819
|
jwt JWT token utilities (decode header and payload)
|