@kolatts/pncli 5.1.0 → 5.2.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/README.md +20 -3
- package/dist/{chunk-FAMJPKO4.js → chunk-2J2HAFKO.js} +2 -2
- package/dist/{chunk-FAMJPKO4.js.map → chunk-2J2HAFKO.js.map} +1 -1
- package/dist/cli.js +656 -310
- package/dist/cli.js.map +1 -1
- package/dist/{http-TAINU2LF.js → http-V7PZD7LE.js} +2 -2
- package/package.json +2 -2
- package/skills/pncli/SKILL.md +2 -2
- package/skills/pncli/marketplace.md +55 -4
- /package/dist/{http-TAINU2LF.js.map → http-V7PZD7LE.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
import {
|
|
15
15
|
createHttpClient,
|
|
16
16
|
validateAlationAccessToken
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-2J2HAFKO.js";
|
|
18
18
|
import {
|
|
19
19
|
ExitCode,
|
|
20
20
|
fail,
|
|
@@ -447,34 +447,34 @@ function parseAdoRemote(remoteUrl, adoBaseUrl) {
|
|
|
447
447
|
} catch {
|
|
448
448
|
baseHostname = adoBaseUrl.replace(/\/$/, "").replace(/^https?:\/\//, "").split(/[:/]/)[0].toLowerCase();
|
|
449
449
|
}
|
|
450
|
-
let
|
|
450
|
+
let path11 = remoteUrl;
|
|
451
451
|
let remoteHostname = "";
|
|
452
|
-
const sshProtoMatch =
|
|
452
|
+
const sshProtoMatch = path11.match(/^ssh:\/\/[^@]*@?([^/:]+)(?::\d+)?(.*)/);
|
|
453
453
|
if (sshProtoMatch) {
|
|
454
454
|
remoteHostname = sshProtoMatch[1].toLowerCase();
|
|
455
|
-
|
|
455
|
+
path11 = sshProtoMatch[2];
|
|
456
456
|
} else {
|
|
457
|
-
const gitAtMatch =
|
|
457
|
+
const gitAtMatch = path11.match(/^git@([^:]+)(?::\d+)?:(.*)/);
|
|
458
458
|
if (gitAtMatch) {
|
|
459
459
|
remoteHostname = gitAtMatch[1].toLowerCase();
|
|
460
|
-
|
|
460
|
+
path11 = "/" + gitAtMatch[2];
|
|
461
461
|
} else {
|
|
462
|
-
const httpsMatch =
|
|
462
|
+
const httpsMatch = path11.match(/^https?:\/\/([^/:]+)(?::\d+)?(.*)/);
|
|
463
463
|
if (httpsMatch) {
|
|
464
464
|
remoteHostname = httpsMatch[1].toLowerCase();
|
|
465
|
-
|
|
465
|
+
path11 = httpsMatch[2];
|
|
466
466
|
}
|
|
467
467
|
}
|
|
468
468
|
}
|
|
469
469
|
if (!remoteHostname || remoteHostname !== baseHostname) return null;
|
|
470
|
-
|
|
471
|
-
const gitIdx =
|
|
472
|
-
const sshIdx =
|
|
470
|
+
path11 = path11.replace(/\.git$/, "");
|
|
471
|
+
const gitIdx = path11.indexOf("/_git/");
|
|
472
|
+
const sshIdx = path11.indexOf("/_ssh/");
|
|
473
473
|
const markerIdx = gitIdx !== -1 ? gitIdx : sshIdx;
|
|
474
474
|
if (markerIdx === -1) return null;
|
|
475
|
-
const repo = decodeURIComponent(
|
|
475
|
+
const repo = decodeURIComponent(path11.slice(markerIdx + 6));
|
|
476
476
|
if (!repo) return null;
|
|
477
|
-
const before =
|
|
477
|
+
const before = path11.slice(1, markerIdx);
|
|
478
478
|
const parts = before.split("/").filter(Boolean);
|
|
479
479
|
if (parts.length < 2) return null;
|
|
480
480
|
const project = decodeURIComponent(parts[parts.length - 1]);
|
|
@@ -2001,21 +2001,21 @@ function registerBitbucketCommands(program2) {
|
|
|
2001
2001
|
fail(err, "bitbucket", "reply-comment", start);
|
|
2002
2002
|
}
|
|
2003
2003
|
});
|
|
2004
|
-
bb.command("resolve-comment").description("Resolve a comment on a pull request").requiredOption("--pr <pr-id>", "Pull request ID").requiredOption("--comment-id <id>", "Comment ID").option("--version <n>", "Comment version", "0").action(async (opts) => {
|
|
2004
|
+
bb.command("resolve-comment").description("Resolve a comment on a pull request").requiredOption("--pr <pr-id>", "Pull request ID").requiredOption("--comment-id <id>", "Comment ID").option("--comment-version <n>", "Comment version, as reported by list-comments", "0").action(async (opts) => {
|
|
2005
2005
|
const start = Date.now();
|
|
2006
2006
|
try {
|
|
2007
2007
|
const { client, project, repo } = getClient2(bb);
|
|
2008
|
-
await client.resolveComment(project, repo, parseInt(opts.pr, 10), parseInt(opts.commentId, 10), parseInt(opts.
|
|
2008
|
+
await client.resolveComment(project, repo, parseInt(opts.pr, 10), parseInt(opts.commentId, 10), parseInt(opts.commentVersion ?? "0", 10));
|
|
2009
2009
|
success({ resolved: true }, "bitbucket", "resolve-comment", start);
|
|
2010
2010
|
} catch (err) {
|
|
2011
2011
|
fail(err, "bitbucket", "resolve-comment", start);
|
|
2012
2012
|
}
|
|
2013
2013
|
});
|
|
2014
|
-
bb.command("delete-comment").description("Delete a comment on a pull request").requiredOption("--pr <pr-id>", "Pull request ID").requiredOption("--comment-id <id>", "Comment ID").option("--version <n>", "Comment version", "0").action(async (opts) => {
|
|
2014
|
+
bb.command("delete-comment").description("Delete a comment on a pull request").requiredOption("--pr <pr-id>", "Pull request ID").requiredOption("--comment-id <id>", "Comment ID").option("--comment-version <n>", "Comment version, as reported by list-comments", "0").action(async (opts) => {
|
|
2015
2015
|
const start = Date.now();
|
|
2016
2016
|
try {
|
|
2017
2017
|
const { client, project, repo } = getClient2(bb);
|
|
2018
|
-
await client.deleteComment(project, repo, parseInt(opts.pr, 10), parseInt(opts.commentId, 10), parseInt(opts.
|
|
2018
|
+
await client.deleteComment(project, repo, parseInt(opts.pr, 10), parseInt(opts.commentId, 10), parseInt(opts.commentVersion ?? "0", 10));
|
|
2019
2019
|
success({ deleted: true }, "bitbucket", "delete-comment", start);
|
|
2020
2020
|
} catch (err) {
|
|
2021
2021
|
fail(err, "bitbucket", "delete-comment", start);
|
|
@@ -2306,8 +2306,8 @@ var GitHubClient = class {
|
|
|
2306
2306
|
);
|
|
2307
2307
|
}
|
|
2308
2308
|
async createRepo(opts) {
|
|
2309
|
-
const
|
|
2310
|
-
return this.http.github(
|
|
2309
|
+
const path11 = opts.org ? `/orgs/${opts.org}/repos` : "/user/repos";
|
|
2310
|
+
return this.http.github(path11, {
|
|
2311
2311
|
method: "POST",
|
|
2312
2312
|
body: {
|
|
2313
2313
|
name: opts.name,
|
|
@@ -6272,8 +6272,8 @@ async function runCredentialChecks(cfg, http) {
|
|
|
6272
6272
|
} else {
|
|
6273
6273
|
try {
|
|
6274
6274
|
const collection = cfg.defaults.ado?.collection;
|
|
6275
|
-
const
|
|
6276
|
-
await http.ado(
|
|
6275
|
+
const path11 = collection ? `/${encodeURIComponent(collection)}/_apis/connectionData?api-version=7.1-preview.1` : "/_apis/projectCollections?api-version=7.1";
|
|
6276
|
+
await http.ado(path11, { timeoutMs: 1e4 });
|
|
6277
6277
|
results.ado = { status: "valid", message: "ok" };
|
|
6278
6278
|
} catch (err) {
|
|
6279
6279
|
results.ado = categorize(err);
|
|
@@ -6511,9 +6511,9 @@ async function runCredentialChecks(cfg, http) {
|
|
|
6511
6511
|
}
|
|
6512
6512
|
|
|
6513
6513
|
// src/services/skills/commands.ts
|
|
6514
|
-
import
|
|
6515
|
-
import
|
|
6516
|
-
import
|
|
6514
|
+
import fs3 from "fs";
|
|
6515
|
+
import path8 from "path";
|
|
6516
|
+
import os2 from "os";
|
|
6517
6517
|
import { fileURLToPath } from "url";
|
|
6518
6518
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
6519
6519
|
import select from "@inquirer/select";
|
|
@@ -6540,6 +6540,170 @@ function getPncliVersion() {
|
|
|
6540
6540
|
return cached;
|
|
6541
6541
|
}
|
|
6542
6542
|
|
|
6543
|
+
// src/services/skills/instructions.ts
|
|
6544
|
+
import fs2 from "fs";
|
|
6545
|
+
import path7 from "path";
|
|
6546
|
+
import os from "os";
|
|
6547
|
+
var INSTRUCTIONS_SUBDIR = "instructions";
|
|
6548
|
+
var INSTRUCTION_SOURCE_FILES = ["AGENTS.md", "CLAUDE.md"];
|
|
6549
|
+
var AGENT_INSTRUCTION_SPECS = {
|
|
6550
|
+
"codex": {
|
|
6551
|
+
file: () => path7.join(process.env.CODEX_HOME || path7.join(os.homedir(), ".codex"), "AGENTS.md"),
|
|
6552
|
+
sources: ["AGENTS.md", "CLAUDE.md"]
|
|
6553
|
+
},
|
|
6554
|
+
"github-copilot": {
|
|
6555
|
+
file: () => path7.join(process.env.COPILOT_HOME || path7.join(os.homedir(), ".copilot"), "copilot-instructions.md"),
|
|
6556
|
+
sources: ["AGENTS.md", "CLAUDE.md"]
|
|
6557
|
+
},
|
|
6558
|
+
"claude-code": {
|
|
6559
|
+
file: () => path7.join(process.env.CLAUDE_CONFIG_DIR || path7.join(os.homedir(), ".claude"), "CLAUDE.md"),
|
|
6560
|
+
sources: ["CLAUDE.md", "AGENTS.md"]
|
|
6561
|
+
}
|
|
6562
|
+
};
|
|
6563
|
+
var INSTRUCTION_AGENTS = Object.keys(AGENT_INSTRUCTION_SPECS);
|
|
6564
|
+
function agentInstructionsFile(agent) {
|
|
6565
|
+
const spec = AGENT_INSTRUCTION_SPECS[agent];
|
|
6566
|
+
if (!spec) throw new Error(`Unknown agent: "${agent}". Use: ${INSTRUCTION_AGENTS.join(" | ")}`);
|
|
6567
|
+
return path7.resolve(spec.file());
|
|
6568
|
+
}
|
|
6569
|
+
function findMarketplaceInstructions(marketplacePath) {
|
|
6570
|
+
const dir = path7.join(marketplacePath, INSTRUCTIONS_SUBDIR);
|
|
6571
|
+
return INSTRUCTION_SOURCE_FILES.map((file) => ({ file, path: path7.join(dir, file) })).filter((entry) => {
|
|
6572
|
+
try {
|
|
6573
|
+
return fs2.statSync(entry.path).isFile();
|
|
6574
|
+
} catch {
|
|
6575
|
+
return false;
|
|
6576
|
+
}
|
|
6577
|
+
});
|
|
6578
|
+
}
|
|
6579
|
+
function pickInstructionSource(agent, shipped) {
|
|
6580
|
+
const spec = AGENT_INSTRUCTION_SPECS[agent];
|
|
6581
|
+
if (!spec) throw new Error(`Unknown agent: "${agent}". Use: ${INSTRUCTION_AGENTS.join(" | ")}`);
|
|
6582
|
+
for (const wanted of spec.sources) {
|
|
6583
|
+
const match = shipped.find((s) => s.file === wanted);
|
|
6584
|
+
if (match) return match;
|
|
6585
|
+
}
|
|
6586
|
+
return null;
|
|
6587
|
+
}
|
|
6588
|
+
var MARKER_PREFIX = "pncli:instructions";
|
|
6589
|
+
function escapeRegExp(s) {
|
|
6590
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6591
|
+
}
|
|
6592
|
+
function assertSafeName(name) {
|
|
6593
|
+
if (!name.trim() || name.includes("-->") || name.includes('"') || /[\r\n]/.test(name)) {
|
|
6594
|
+
throw new Error(`Marketplace name "${name}" cannot be used as an instructions block marker.`);
|
|
6595
|
+
}
|
|
6596
|
+
}
|
|
6597
|
+
function beginMarker(marketplaceName) {
|
|
6598
|
+
assertSafeName(marketplaceName);
|
|
6599
|
+
return `<!-- ${MARKER_PREFIX} marketplace="${marketplaceName}" begin -->`;
|
|
6600
|
+
}
|
|
6601
|
+
function endMarker(marketplaceName) {
|
|
6602
|
+
assertSafeName(marketplaceName);
|
|
6603
|
+
return `<!-- ${MARKER_PREFIX} marketplace="${marketplaceName}" end -->`;
|
|
6604
|
+
}
|
|
6605
|
+
function blockPattern(marketplaceName) {
|
|
6606
|
+
const begin = escapeRegExp(beginMarker(marketplaceName));
|
|
6607
|
+
const end = escapeRegExp(endMarker(marketplaceName));
|
|
6608
|
+
return new RegExp(`${begin}\\r?\\n[\\s\\S]*?${end}`);
|
|
6609
|
+
}
|
|
6610
|
+
function renderManagedBlock(marketplaceName, body, sourceFile) {
|
|
6611
|
+
const trimmed = body.replace(/\r\n/g, "\n").trim();
|
|
6612
|
+
return [
|
|
6613
|
+
beginMarker(marketplaceName),
|
|
6614
|
+
`<!-- Managed by pncli from ${sourceFile} in marketplace "${marketplaceName}". Edits here are overwritten on the next sync; remove with: pncli skills marketplace instructions remove ${marketplaceName} -->`,
|
|
6615
|
+
trimmed,
|
|
6616
|
+
endMarker(marketplaceName)
|
|
6617
|
+
].join("\n");
|
|
6618
|
+
}
|
|
6619
|
+
function detectEol(content) {
|
|
6620
|
+
return /\r\n/.test(content) ? "\r\n" : "\n";
|
|
6621
|
+
}
|
|
6622
|
+
function upsertManagedBlock(existing, marketplaceName, block) {
|
|
6623
|
+
const eol = detectEol(existing);
|
|
6624
|
+
const rendered = block.replace(/\n/g, eol);
|
|
6625
|
+
const pattern = blockPattern(marketplaceName);
|
|
6626
|
+
const match = pattern.exec(existing);
|
|
6627
|
+
if (match) {
|
|
6628
|
+
if (match[0] === rendered) return { content: existing, action: "unchanged" };
|
|
6629
|
+
return { content: existing.slice(0, match.index) + rendered + existing.slice(match.index + match[0].length), action: "updated" };
|
|
6630
|
+
}
|
|
6631
|
+
const separator = existing.length === 0 ? "" : eol;
|
|
6632
|
+
return { content: `${existing}${separator}${rendered}${eol}`, action: "added" };
|
|
6633
|
+
}
|
|
6634
|
+
function removeManagedBlock(existing, marketplaceName) {
|
|
6635
|
+
const match = blockPattern(marketplaceName).exec(existing);
|
|
6636
|
+
if (!match) return { content: existing, removed: false };
|
|
6637
|
+
const eol = detectEol(existing);
|
|
6638
|
+
let start = match.index;
|
|
6639
|
+
let end = start + match[0].length;
|
|
6640
|
+
if (existing.startsWith(eol, end)) end += eol.length;
|
|
6641
|
+
if (start > 0 && existing.slice(0, start).endsWith(eol)) start -= eol.length;
|
|
6642
|
+
else if (start === 0 && existing.startsWith(eol, end)) end += eol.length;
|
|
6643
|
+
return { content: existing.slice(0, start) + existing.slice(end), removed: true };
|
|
6644
|
+
}
|
|
6645
|
+
function listManagedBlocks(content) {
|
|
6646
|
+
const re = new RegExp(`<!-- ${escapeRegExp(MARKER_PREFIX)} marketplace="([^"]*)" begin -->`, "g");
|
|
6647
|
+
return Array.from(content.matchAll(re)).map((m) => m[1]);
|
|
6648
|
+
}
|
|
6649
|
+
function readFileOrEmpty(file) {
|
|
6650
|
+
try {
|
|
6651
|
+
return fs2.readFileSync(file, "utf8");
|
|
6652
|
+
} catch {
|
|
6653
|
+
return "";
|
|
6654
|
+
}
|
|
6655
|
+
}
|
|
6656
|
+
function applyMarketplaceInstructions(marketplacePath, marketplaceName, agents) {
|
|
6657
|
+
const shipped = findMarketplaceInstructions(marketplacePath);
|
|
6658
|
+
return agents.map((agent) => {
|
|
6659
|
+
const file = agentInstructionsFile(agent);
|
|
6660
|
+
const source = pickInstructionSource(agent, shipped);
|
|
6661
|
+
if (!source) return { agent, file, source: null, action: "skipped" };
|
|
6662
|
+
const body = fs2.readFileSync(source.path, "utf8");
|
|
6663
|
+
const block = renderManagedBlock(marketplaceName, body, `${INSTRUCTIONS_SUBDIR}/${source.file}`);
|
|
6664
|
+
const { content, action } = upsertManagedBlock(readFileOrEmpty(file), marketplaceName, block);
|
|
6665
|
+
if (action !== "unchanged") {
|
|
6666
|
+
fs2.mkdirSync(path7.dirname(file), { recursive: true });
|
|
6667
|
+
fs2.writeFileSync(file, content, "utf8");
|
|
6668
|
+
}
|
|
6669
|
+
return { agent, file, source: `${INSTRUCTIONS_SUBDIR}/${source.file}`, action };
|
|
6670
|
+
});
|
|
6671
|
+
}
|
|
6672
|
+
function removeMarketplaceInstructions(marketplaceName, agents) {
|
|
6673
|
+
return agents.map((agent) => {
|
|
6674
|
+
const file = agentInstructionsFile(agent);
|
|
6675
|
+
const existing = readFileOrEmpty(file);
|
|
6676
|
+
const { content, removed } = removeManagedBlock(existing, marketplaceName);
|
|
6677
|
+
if (removed) fs2.writeFileSync(file, content, "utf8");
|
|
6678
|
+
return { agent, file, removed };
|
|
6679
|
+
});
|
|
6680
|
+
}
|
|
6681
|
+
function marketplaceInstructionsStatus(marketplacePath, marketplaceName, agents) {
|
|
6682
|
+
const shipped = marketplacePath && fs2.existsSync(marketplacePath) ? findMarketplaceInstructions(marketplacePath) : [];
|
|
6683
|
+
return agents.map((agent) => {
|
|
6684
|
+
const file = agentInstructionsFile(agent);
|
|
6685
|
+
const fileExists = fs2.existsSync(file);
|
|
6686
|
+
const existing = fileExists ? readFileOrEmpty(file) : "";
|
|
6687
|
+
const installed = listManagedBlocks(existing).includes(marketplaceName);
|
|
6688
|
+
const source = pickInstructionSource(agent, shipped);
|
|
6689
|
+
let upToDate = null;
|
|
6690
|
+
if (installed && source) {
|
|
6691
|
+
const block = renderManagedBlock(marketplaceName, fs2.readFileSync(source.path, "utf8"), `${INSTRUCTIONS_SUBDIR}/${source.file}`);
|
|
6692
|
+
upToDate = upsertManagedBlock(existing, marketplaceName, block).action === "unchanged";
|
|
6693
|
+
} else if (installed) {
|
|
6694
|
+
upToDate = false;
|
|
6695
|
+
}
|
|
6696
|
+
return {
|
|
6697
|
+
agent,
|
|
6698
|
+
file,
|
|
6699
|
+
fileExists,
|
|
6700
|
+
source: source ? `${INSTRUCTIONS_SUBDIR}/${source.file}` : null,
|
|
6701
|
+
installed,
|
|
6702
|
+
upToDate
|
|
6703
|
+
};
|
|
6704
|
+
});
|
|
6705
|
+
}
|
|
6706
|
+
|
|
6543
6707
|
// src/services/skills/commands.ts
|
|
6544
6708
|
var BACK = "__back__";
|
|
6545
6709
|
var ALL_MARKETPLACES = "__all_marketplaces__";
|
|
@@ -6564,7 +6728,7 @@ function repoNameFromUrl(url) {
|
|
|
6564
6728
|
}
|
|
6565
6729
|
}
|
|
6566
6730
|
function defaultMarketplacePath(url) {
|
|
6567
|
-
return
|
|
6731
|
+
return path8.join(os2.homedir(), ".agents", "marketplaces", repoNameFromUrl(url));
|
|
6568
6732
|
}
|
|
6569
6733
|
function marketplaceLabel(m) {
|
|
6570
6734
|
return m.name ?? repoNameFromUrl(m.repoUrl ?? "");
|
|
@@ -6619,9 +6783,9 @@ function assertInteractive(hint) {
|
|
|
6619
6783
|
}
|
|
6620
6784
|
}
|
|
6621
6785
|
var AGENT_PATHS = {
|
|
6622
|
-
"codex": { project: ".agents/skills", user:
|
|
6623
|
-
"github-copilot": { project: ".github/skills", user:
|
|
6624
|
-
"claude-code": { project: ".claude/skills", user:
|
|
6786
|
+
"codex": { project: ".agents/skills", user: path8.join(os2.homedir(), ".agents/skills") },
|
|
6787
|
+
"github-copilot": { project: ".github/skills", user: path8.join(os2.homedir(), ".copilot/skills") },
|
|
6788
|
+
"claude-code": { project: ".claude/skills", user: path8.join(os2.homedir(), ".claude/skills") }
|
|
6625
6789
|
};
|
|
6626
6790
|
var DEFAULT_AGENT = "codex";
|
|
6627
6791
|
var AGENT_CHOICES = Object.keys(AGENT_PATHS).join(" | ");
|
|
@@ -6648,23 +6812,23 @@ function findGitRoot(from = process.cwd()) {
|
|
|
6648
6812
|
encoding: "utf8",
|
|
6649
6813
|
stdio: ["pipe", "pipe", "pipe"]
|
|
6650
6814
|
}).trim();
|
|
6651
|
-
return root ?
|
|
6815
|
+
return root ? path8.resolve(root) : null;
|
|
6652
6816
|
} catch {
|
|
6653
6817
|
return null;
|
|
6654
6818
|
}
|
|
6655
6819
|
}
|
|
6656
6820
|
function resolveProjectPath(relativePath) {
|
|
6657
6821
|
const root = findGitRoot();
|
|
6658
|
-
return root ?
|
|
6822
|
+
return root ? path8.resolve(root, relativePath) : path8.resolve(relativePath);
|
|
6659
6823
|
}
|
|
6660
6824
|
function resolveScopedPath(agentPaths, scope) {
|
|
6661
|
-
return scope === "project" ? resolveProjectPath(agentPaths.project) :
|
|
6825
|
+
return scope === "project" ? resolveProjectPath(agentPaths.project) : path8.resolve(agentPaths.user);
|
|
6662
6826
|
}
|
|
6663
6827
|
function hasInstalledPncliSkill() {
|
|
6664
6828
|
try {
|
|
6665
6829
|
for (const agentPaths of Object.values(AGENT_PATHS)) {
|
|
6666
6830
|
for (const scope of ["project", "user"]) {
|
|
6667
|
-
if (
|
|
6831
|
+
if (fs3.existsSync(path8.join(resolveScopedPath(agentPaths, scope), "pncli", "SKILL.md"))) {
|
|
6668
6832
|
return true;
|
|
6669
6833
|
}
|
|
6670
6834
|
}
|
|
@@ -6674,20 +6838,30 @@ function hasInstalledPncliSkill() {
|
|
|
6674
6838
|
return false;
|
|
6675
6839
|
}
|
|
6676
6840
|
function resolveTargetDir(opts) {
|
|
6677
|
-
if (opts.target) return
|
|
6841
|
+
if (opts.target) return path8.resolve(opts.target);
|
|
6678
6842
|
const agentConfig = resolveAgentPaths(resolveAgentName(opts));
|
|
6679
6843
|
return resolveScopedPath(agentConfig, opts.scope ?? "user");
|
|
6680
6844
|
}
|
|
6845
|
+
function resolveInstallTargets(opts, scope = "user") {
|
|
6846
|
+
if (opts.allAgents) {
|
|
6847
|
+
if (opts.claude || opts.agent) {
|
|
6848
|
+
throw new Error("--all-agents cannot be combined with --claude or --agent");
|
|
6849
|
+
}
|
|
6850
|
+
return Object.entries(AGENT_PATHS).map(([agent2, paths]) => ({ agent: agent2, target: resolveScopedPath(paths, scope) }));
|
|
6851
|
+
}
|
|
6852
|
+
const agent = resolveAgentName(opts);
|
|
6853
|
+
return [{ agent, target: resolveScopedPath(resolveAgentPaths(agent), scope) }];
|
|
6854
|
+
}
|
|
6681
6855
|
function getBundledSkillsDir() {
|
|
6682
6856
|
try {
|
|
6683
6857
|
const thisFile = fileURLToPath(import.meta.url);
|
|
6684
|
-
return
|
|
6858
|
+
return path8.resolve(path8.dirname(thisFile), "..", "skills");
|
|
6685
6859
|
} catch {
|
|
6686
6860
|
return "";
|
|
6687
6861
|
}
|
|
6688
6862
|
}
|
|
6689
6863
|
function getInstalledMetaPath(targetDir) {
|
|
6690
|
-
return
|
|
6864
|
+
return path8.join(targetDir, ".pncli-installed.json");
|
|
6691
6865
|
}
|
|
6692
6866
|
var SKILL_ORIGIN_FILENAME = "pncli-origin.json";
|
|
6693
6867
|
var DISABLED_SUBDIR = ".pncli-disabled";
|
|
@@ -6698,13 +6872,13 @@ function readInstalledMeta(targetDir) {
|
|
|
6698
6872
|
return { version: 1, skills: {} };
|
|
6699
6873
|
}
|
|
6700
6874
|
function getSkillOriginPath(skillDir) {
|
|
6701
|
-
return
|
|
6875
|
+
return path8.join(skillDir, SKILL_ORIGIN_FILENAME);
|
|
6702
6876
|
}
|
|
6703
6877
|
function readSkillOrigin(skillDir) {
|
|
6704
6878
|
const originPath = getSkillOriginPath(skillDir);
|
|
6705
|
-
if (!
|
|
6879
|
+
if (!fs3.existsSync(originPath)) return null;
|
|
6706
6880
|
try {
|
|
6707
|
-
const raw = JSON.parse(
|
|
6881
|
+
const raw = JSON.parse(fs3.readFileSync(originPath, "utf8"));
|
|
6708
6882
|
if (raw && raw.version === 1 && raw.source) return raw;
|
|
6709
6883
|
} catch {
|
|
6710
6884
|
}
|
|
@@ -6716,7 +6890,7 @@ function matchesMarketplaceFilter(record, filter) {
|
|
|
6716
6890
|
function resolveSkillRecord(meta, targetDir, skillName) {
|
|
6717
6891
|
const fromMeta = meta.skills[skillName];
|
|
6718
6892
|
if (fromMeta) return fromMeta;
|
|
6719
|
-
const perSkill = readSkillOrigin(
|
|
6893
|
+
const perSkill = readSkillOrigin(path8.join(targetDir, skillName));
|
|
6720
6894
|
if (!perSkill) return null;
|
|
6721
6895
|
return {
|
|
6722
6896
|
source: perSkill.source,
|
|
@@ -6729,28 +6903,28 @@ function resolveSkillRecord(meta, targetDir, skillName) {
|
|
|
6729
6903
|
};
|
|
6730
6904
|
}
|
|
6731
6905
|
function listActiveSkillDirs(targetDir) {
|
|
6732
|
-
if (!
|
|
6733
|
-
return
|
|
6906
|
+
if (!fs3.existsSync(targetDir)) return [];
|
|
6907
|
+
return fs3.readdirSync(targetDir).filter((name) => {
|
|
6734
6908
|
if (name.startsWith(".")) return false;
|
|
6735
|
-
const dir =
|
|
6909
|
+
const dir = path8.join(targetDir, name);
|
|
6736
6910
|
try {
|
|
6737
|
-
if (!
|
|
6911
|
+
if (!fs3.statSync(dir).isDirectory()) return false;
|
|
6738
6912
|
} catch {
|
|
6739
6913
|
return false;
|
|
6740
6914
|
}
|
|
6741
|
-
return
|
|
6915
|
+
return fs3.existsSync(path8.join(dir, "SKILL.md"));
|
|
6742
6916
|
});
|
|
6743
6917
|
}
|
|
6744
6918
|
function disablePluginSkills(targetDir, plugin, marketplaceFilter) {
|
|
6745
|
-
const resolvedTarget =
|
|
6746
|
-
const stashDir =
|
|
6919
|
+
const resolvedTarget = path8.resolve(targetDir);
|
|
6920
|
+
const stashDir = path8.join(targetDir, DISABLED_SUBDIR);
|
|
6747
6921
|
const meta = readInstalledMeta(targetDir);
|
|
6748
6922
|
const disabled = [];
|
|
6749
6923
|
const alreadyDisabled = [];
|
|
6750
6924
|
const skipped = [];
|
|
6751
6925
|
for (const skillName of listActiveSkillDirs(targetDir)) {
|
|
6752
|
-
const skillDir =
|
|
6753
|
-
if (!skillDir.startsWith(resolvedTarget +
|
|
6926
|
+
const skillDir = path8.resolve(targetDir, skillName);
|
|
6927
|
+
if (!skillDir.startsWith(resolvedTarget + path8.sep)) continue;
|
|
6754
6928
|
const record = resolveSkillRecord(meta, targetDir, skillName);
|
|
6755
6929
|
if (!record || record.source !== "marketplace") {
|
|
6756
6930
|
skipped.push(skillName);
|
|
@@ -6760,10 +6934,10 @@ function disablePluginSkills(targetDir, plugin, marketplaceFilter) {
|
|
|
6760
6934
|
skipped.push(skillName);
|
|
6761
6935
|
continue;
|
|
6762
6936
|
}
|
|
6763
|
-
const stashDest =
|
|
6764
|
-
|
|
6765
|
-
if (
|
|
6766
|
-
|
|
6937
|
+
const stashDest = path8.join(stashDir, skillName);
|
|
6938
|
+
fs3.mkdirSync(stashDir, { recursive: true });
|
|
6939
|
+
if (fs3.existsSync(stashDest)) fs3.rmSync(stashDest, { recursive: true, force: true });
|
|
6940
|
+
fs3.renameSync(skillDir, stashDest);
|
|
6767
6941
|
delete meta.skills[skillName];
|
|
6768
6942
|
meta.disabled = meta.disabled ?? {};
|
|
6769
6943
|
meta.disabled[skillName] = record;
|
|
@@ -6775,13 +6949,13 @@ function disablePluginSkills(targetDir, plugin, marketplaceFilter) {
|
|
|
6775
6949
|
}
|
|
6776
6950
|
}
|
|
6777
6951
|
if (disabled.length > 0) {
|
|
6778
|
-
|
|
6952
|
+
fs3.writeFileSync(getInstalledMetaPath(targetDir), JSON.stringify(meta, null, 2), "utf8");
|
|
6779
6953
|
}
|
|
6780
6954
|
return { disabled, alreadyDisabled, skipped };
|
|
6781
6955
|
}
|
|
6782
6956
|
function enablePluginSkills(targetDir, plugin, marketplaceFilter) {
|
|
6783
|
-
const resolvedTarget =
|
|
6784
|
-
const stashDir =
|
|
6957
|
+
const resolvedTarget = path8.resolve(targetDir);
|
|
6958
|
+
const stashDir = path8.join(targetDir, DISABLED_SUBDIR);
|
|
6785
6959
|
const meta = readInstalledMeta(targetDir);
|
|
6786
6960
|
const enabled = [];
|
|
6787
6961
|
const stashMissing = [];
|
|
@@ -6792,27 +6966,27 @@ function enablePluginSkills(targetDir, plugin, marketplaceFilter) {
|
|
|
6792
6966
|
skipped.push(skillName);
|
|
6793
6967
|
continue;
|
|
6794
6968
|
}
|
|
6795
|
-
const dest =
|
|
6796
|
-
if (!dest.startsWith(resolvedTarget +
|
|
6969
|
+
const dest = path8.resolve(targetDir, skillName);
|
|
6970
|
+
if (!dest.startsWith(resolvedTarget + path8.sep)) {
|
|
6797
6971
|
skipped.push(skillName);
|
|
6798
6972
|
continue;
|
|
6799
6973
|
}
|
|
6800
|
-
const stashSrc =
|
|
6801
|
-
if (!
|
|
6974
|
+
const stashSrc = path8.join(stashDir, skillName);
|
|
6975
|
+
if (!fs3.existsSync(stashSrc)) {
|
|
6802
6976
|
stashMissing.push(skillName);
|
|
6803
6977
|
continue;
|
|
6804
6978
|
}
|
|
6805
|
-
if (
|
|
6806
|
-
|
|
6979
|
+
if (fs3.existsSync(dest)) fs3.rmSync(dest, { recursive: true, force: true });
|
|
6980
|
+
fs3.renameSync(stashSrc, dest);
|
|
6807
6981
|
meta.skills[skillName] = record;
|
|
6808
6982
|
delete meta.disabled[skillName];
|
|
6809
6983
|
enabled.push(skillName);
|
|
6810
6984
|
}
|
|
6811
|
-
if (
|
|
6812
|
-
|
|
6985
|
+
if (fs3.existsSync(stashDir) && fs3.readdirSync(stashDir).length === 0) {
|
|
6986
|
+
fs3.rmdirSync(stashDir);
|
|
6813
6987
|
}
|
|
6814
6988
|
if (enabled.length > 0) {
|
|
6815
|
-
|
|
6989
|
+
fs3.writeFileSync(getInstalledMetaPath(targetDir), JSON.stringify(meta, null, 2), "utf8");
|
|
6816
6990
|
}
|
|
6817
6991
|
return { enabled, stashMissing, skipped, hadDisabled: disabledEntries.length > 0 };
|
|
6818
6992
|
}
|
|
@@ -6878,11 +7052,11 @@ function getInstalledPluginsForMarketplace(targetDir, marketplaceName, repoUrl)
|
|
|
6878
7052
|
function readCustomTargets(globalConfig) {
|
|
6879
7053
|
const raw = globalConfig.skillsTargets;
|
|
6880
7054
|
if (!Array.isArray(raw)) return [];
|
|
6881
|
-
return [...new Set(raw.filter((t) => typeof t === "string" && t.length > 0).map((t) =>
|
|
7055
|
+
return [...new Set(raw.filter((t) => typeof t === "string" && t.length > 0).map((t) => path8.resolve(t)))];
|
|
6882
7056
|
}
|
|
6883
7057
|
function rememberCustomTarget(configPath, targetDir) {
|
|
6884
|
-
const resolved =
|
|
6885
|
-
const builtIn = Object.values(AGENT_PATHS).flatMap((a) => [
|
|
7058
|
+
const resolved = path8.resolve(targetDir);
|
|
7059
|
+
const builtIn = Object.values(AGENT_PATHS).flatMap((a) => [path8.resolve(a.user), resolveProjectPath(a.project)]);
|
|
6886
7060
|
if (builtIn.includes(resolved)) return false;
|
|
6887
7061
|
const existing = loadJsonFile(configPath) ?? {};
|
|
6888
7062
|
const targets = readCustomTargets(existing);
|
|
@@ -6891,7 +7065,7 @@ function rememberCustomTarget(configPath, targetDir) {
|
|
|
6891
7065
|
return true;
|
|
6892
7066
|
}
|
|
6893
7067
|
function forgetCustomTarget(configPath, targetDir) {
|
|
6894
|
-
const resolved =
|
|
7068
|
+
const resolved = path8.resolve(targetDir);
|
|
6895
7069
|
const existing = loadJsonFile(configPath) ?? {};
|
|
6896
7070
|
const targets = readCustomTargets(existing);
|
|
6897
7071
|
if (!targets.includes(resolved)) return false;
|
|
@@ -6899,12 +7073,12 @@ function forgetCustomTarget(configPath, targetDir) {
|
|
|
6899
7073
|
return true;
|
|
6900
7074
|
}
|
|
6901
7075
|
function summarizeLocation(agent, scope, targetDir) {
|
|
6902
|
-
const resolved =
|
|
7076
|
+
const resolved = path8.resolve(targetDir);
|
|
6903
7077
|
const base = {
|
|
6904
7078
|
agent,
|
|
6905
7079
|
scope,
|
|
6906
7080
|
path: resolved,
|
|
6907
|
-
exists:
|
|
7081
|
+
exists: fs3.existsSync(resolved),
|
|
6908
7082
|
totalSkills: 0,
|
|
6909
7083
|
marketplaceSkills: 0,
|
|
6910
7084
|
bundledSkills: 0,
|
|
@@ -6923,8 +7097,8 @@ function summarizeLocation(agent, scope, targetDir) {
|
|
|
6923
7097
|
}
|
|
6924
7098
|
const disabledNames = Object.keys(meta.disabled ?? {});
|
|
6925
7099
|
base.disabledSkills = disabledNames.length;
|
|
6926
|
-
const stashDir =
|
|
6927
|
-
base.disabledStashMissing = disabledNames.filter((name) => !
|
|
7100
|
+
const stashDir = path8.join(resolved, DISABLED_SUBDIR);
|
|
7101
|
+
base.disabledStashMissing = disabledNames.filter((name) => !fs3.existsSync(path8.join(stashDir, name)));
|
|
6928
7102
|
return base;
|
|
6929
7103
|
}
|
|
6930
7104
|
function listKnownLocations(globalConfig) {
|
|
@@ -6949,7 +7123,7 @@ function collectSkillStatus(globalConfig, locations) {
|
|
|
6949
7123
|
const { byName, byUrl } = buildMarketplaceIndex(getAllMarketplaces(globalConfig));
|
|
6950
7124
|
const remoteCache = /* @__PURE__ */ new Map();
|
|
6951
7125
|
const remoteFor = (localPath) => {
|
|
6952
|
-
if (!localPath || !
|
|
7126
|
+
if (!localPath || !fs3.existsSync(localPath)) return null;
|
|
6953
7127
|
if (!remoteCache.has(localPath)) remoteCache.set(localPath, getRepoRemoteUrl(localPath));
|
|
6954
7128
|
return remoteCache.get(localPath) ?? null;
|
|
6955
7129
|
};
|
|
@@ -6962,7 +7136,7 @@ function collectSkillStatus(globalConfig, locations) {
|
|
|
6962
7136
|
const registered = (recordedName ? byName.get(recordedName) : void 0) ?? (record?.installedFrom ? byUrl.get(record.installedFrom) : void 0) ?? null;
|
|
6963
7137
|
records.push({
|
|
6964
7138
|
skill: skillName,
|
|
6965
|
-
path: enabled ?
|
|
7139
|
+
path: enabled ? path8.join(location.path, skillName) : path8.join(location.path, DISABLED_SUBDIR, skillName),
|
|
6966
7140
|
agent: location.agent,
|
|
6967
7141
|
scope: location.scope,
|
|
6968
7142
|
location: location.path,
|
|
@@ -6995,16 +7169,16 @@ function recordInstalledSkills(targetDir, skillNames, record) {
|
|
|
6995
7169
|
for (const skillName of skillNames) {
|
|
6996
7170
|
const fullRecord = { pncliVersion: getPncliVersion(), ...record, installedAt: now };
|
|
6997
7171
|
meta.skills[skillName] = fullRecord;
|
|
6998
|
-
const skillDir =
|
|
6999
|
-
if (
|
|
7172
|
+
const skillDir = path8.join(targetDir, skillName);
|
|
7173
|
+
if (fs3.existsSync(skillDir)) {
|
|
7000
7174
|
const origin = { version: 1, ...fullRecord };
|
|
7001
7175
|
try {
|
|
7002
|
-
|
|
7176
|
+
fs3.writeFileSync(getSkillOriginPath(skillDir), JSON.stringify(origin, null, 2), "utf8");
|
|
7003
7177
|
} catch {
|
|
7004
7178
|
}
|
|
7005
7179
|
}
|
|
7006
7180
|
}
|
|
7007
|
-
|
|
7181
|
+
fs3.writeFileSync(getInstalledMetaPath(targetDir), JSON.stringify(meta, null, 2), "utf8");
|
|
7008
7182
|
}
|
|
7009
7183
|
function findStaleBundledSkills(targetDir) {
|
|
7010
7184
|
const currentVersion = getPncliVersion();
|
|
@@ -7080,8 +7254,8 @@ function upsertMarketplace(all, entry) {
|
|
|
7080
7254
|
}
|
|
7081
7255
|
}
|
|
7082
7256
|
function cloneOrReuseMarketplace(url, resolvedPath, opts, marketplaceName) {
|
|
7083
|
-
const hasGit =
|
|
7084
|
-
if (
|
|
7257
|
+
const hasGit = fs3.existsSync(path8.join(resolvedPath, ".git"));
|
|
7258
|
+
if (fs3.existsSync(resolvedPath) && !hasGit && fs3.readdirSync(resolvedPath).length > 0) {
|
|
7085
7259
|
throw new Error(`Directory already exists and is not a git repo: ${resolvedPath}`);
|
|
7086
7260
|
}
|
|
7087
7261
|
if (hasGit) {
|
|
@@ -7138,7 +7312,7 @@ function installPluginsForMarketplace(marketplacePath, marketplaceName, repoUrl,
|
|
|
7138
7312
|
const branch = detectRepoBranch(marketplacePath);
|
|
7139
7313
|
for (const pluginName of pluginNames) {
|
|
7140
7314
|
const skillsSrc = resolveSkillsSrc(marketplacePath, pluginName);
|
|
7141
|
-
if (!
|
|
7315
|
+
if (!fs3.existsSync(skillsSrc)) {
|
|
7142
7316
|
results[pluginName] = { installed: [], failed: [] };
|
|
7143
7317
|
warn(`No skills directory found for plugin "${pluginName}" in "${marketplaceName}" \u2014 skipping.`);
|
|
7144
7318
|
continue;
|
|
@@ -7152,61 +7326,105 @@ function installPluginsForMarketplace(marketplacePath, marketplaceName, repoUrl,
|
|
|
7152
7326
|
results[pluginName] = { installed, failed };
|
|
7153
7327
|
totalInstalled += installed.length;
|
|
7154
7328
|
for (const skill of installed) {
|
|
7155
|
-
|
|
7329
|
+
log(` ${skill}: ${path8.join(skillsSrc, skill)} \u2192 ${path8.join(targetDir, skill)}`);
|
|
7156
7330
|
}
|
|
7157
7331
|
if (failed.length > 0) {
|
|
7158
7332
|
warn(`Skipped ${failed.length} skill(s) with invalid names in "${pluginName}": ${failed.join(", ")}`);
|
|
7159
7333
|
}
|
|
7160
7334
|
}
|
|
7335
|
+
warn(`Installed ${totalInstalled} skill(s) from ${pluginNames.length} plugin(s) in "${marketplaceName}" to ${targetDir}`);
|
|
7161
7336
|
return { results, totalInstalled };
|
|
7162
7337
|
}
|
|
7163
|
-
function
|
|
7338
|
+
function installMarketplaceToTarget(m, marketplacePath, pluginChoices, pluginFilter, target, opts) {
|
|
7339
|
+
const marketplaceName = marketplaceLabel(m);
|
|
7340
|
+
const base = { agent: target.agent, target: target.target };
|
|
7341
|
+
const installedHere = getInstalledPluginsForMarketplace(target.target, marketplaceName, m.repoUrl).filter((name) => pluginChoices.some((p) => p.name === name));
|
|
7342
|
+
let requested;
|
|
7343
|
+
if (pluginFilter === "all") {
|
|
7344
|
+
if (opts.installedOnly) {
|
|
7345
|
+
if (installedHere.length === 0) {
|
|
7346
|
+
warn(`No installed plugins from "${marketplaceName}" in ${target.target} \u2014 skipping. Run marketplace sync without --installed-only to install plugins.`);
|
|
7347
|
+
return { ...base, plugins: {}, total: 0, skipped: true, installedOnly: true, message: "No installed plugins found \u2014 nothing to sync." };
|
|
7348
|
+
}
|
|
7349
|
+
requested = installedHere;
|
|
7350
|
+
} else {
|
|
7351
|
+
requested = pluginChoices.map((p) => p.name);
|
|
7352
|
+
}
|
|
7353
|
+
} else {
|
|
7354
|
+
requested = [pluginFilter];
|
|
7355
|
+
}
|
|
7356
|
+
const missing = requested.filter((name) => !installedHere.includes(name));
|
|
7357
|
+
if (!opts.updated && !opts.force) {
|
|
7358
|
+
if (missing.length === 0) {
|
|
7359
|
+
return { ...base, plugins: {}, total: 0, skipped: true, message: "No changes detected \u2014 skipping install. Use --force to reinstall anyway." };
|
|
7360
|
+
}
|
|
7361
|
+
requested = missing;
|
|
7362
|
+
}
|
|
7363
|
+
const { results, totalInstalled } = installPluginsForMarketplace(marketplacePath, marketplaceName, m.repoUrl, requested, target.target);
|
|
7364
|
+
return { ...base, plugins: results, total: totalInstalled };
|
|
7365
|
+
}
|
|
7366
|
+
function applyInstructionsForTargets(m, marketplacePath, targets, enabled) {
|
|
7367
|
+
if (!enabled || findMarketplaceInstructions(marketplacePath).length === 0) return void 0;
|
|
7368
|
+
const results = applyMarketplaceInstructions(marketplacePath, marketplaceLabel(m), targets.map((t) => t.agent));
|
|
7369
|
+
for (const r of results) {
|
|
7370
|
+
if (r.action === "added" || r.action === "updated") warn(`Instructions ${r.action} in ${r.file} (from ${r.source})`);
|
|
7371
|
+
}
|
|
7372
|
+
return results;
|
|
7373
|
+
}
|
|
7374
|
+
function shapeSyncResult(marketplaceName, updated, pluginFilter, targetResults, extras) {
|
|
7375
|
+
const tail = {
|
|
7376
|
+
marketplaceUpdated: updated,
|
|
7377
|
+
...extras.installedOnly ? { installedOnly: true } : {},
|
|
7378
|
+
...extras.instructions ? { instructions: extras.instructions } : {}
|
|
7379
|
+
};
|
|
7380
|
+
if (targetResults.length === 1) {
|
|
7381
|
+
const r = targetResults[0];
|
|
7382
|
+
if (r.skipped) {
|
|
7383
|
+
return { marketplace: marketplaceName, updated: false, skipped: true, target: r.target, message: r.message, ...tail };
|
|
7384
|
+
}
|
|
7385
|
+
if (pluginFilter === "all") {
|
|
7386
|
+
return { marketplace: marketplaceName, plugins: r.plugins, total: r.total, target: r.target, ...tail };
|
|
7387
|
+
}
|
|
7388
|
+
const single = r.plugins[pluginFilter] ?? { installed: [], failed: [] };
|
|
7389
|
+
return { marketplace: marketplaceName, plugin: pluginFilter, installed: single.installed, failed: single.failed, total: single.installed.length, target: r.target, ...tail };
|
|
7390
|
+
}
|
|
7391
|
+
return {
|
|
7392
|
+
marketplace: marketplaceName,
|
|
7393
|
+
...pluginFilter === "all" ? {} : { plugin: pluginFilter },
|
|
7394
|
+
targets: targetResults,
|
|
7395
|
+
total: targetResults.reduce((sum, r) => sum + r.total, 0),
|
|
7396
|
+
...tail
|
|
7397
|
+
};
|
|
7398
|
+
}
|
|
7399
|
+
function installAllPlugins(m, resolvedPath, targets) {
|
|
7164
7400
|
const pluginChoices = resolvePluginChoices(resolvedPath);
|
|
7165
7401
|
if (pluginChoices.length === 0) {
|
|
7166
7402
|
warn("No plugins found in marketplace. Check the marketplace repository structure.");
|
|
7167
|
-
return {
|
|
7403
|
+
return targets.map((t) => ({ ...t, plugins: {}, total: 0 }));
|
|
7168
7404
|
}
|
|
7169
|
-
|
|
7170
|
-
const { results, totalInstalled } = installPluginsForMarketplace(resolvedPath, marketplaceName, url, pluginChoices.map((p) => p.name), targetDir);
|
|
7171
|
-
return { pluginResults: results, totalInstalled };
|
|
7405
|
+
return targets.map((t) => installMarketplaceToTarget(m, resolvedPath, pluginChoices, "all", t, { updated: true, force: true, installedOnly: false }));
|
|
7172
7406
|
}
|
|
7173
|
-
function syncMarketplacePlugins(m,
|
|
7407
|
+
function syncMarketplacePlugins(m, targets, pluginFilter, opts) {
|
|
7174
7408
|
const marketplaceName = marketplaceLabel(m);
|
|
7175
7409
|
try {
|
|
7176
7410
|
const marketplacePath = m.localPath;
|
|
7177
|
-
if (!marketplacePath || !
|
|
7411
|
+
if (!marketplacePath || !fs3.existsSync(marketplacePath)) {
|
|
7178
7412
|
warn(`Marketplace "${marketplaceName}" local path not found \u2014 skipping.`);
|
|
7179
7413
|
return { marketplace: marketplaceName, skipped: true, message: "Local path not found." };
|
|
7180
7414
|
}
|
|
7181
7415
|
const { updated } = pullMarketplace(marketplacePath, m.repoUrl, m.token, marketplaceName);
|
|
7182
|
-
if (!updated && !force) {
|
|
7183
|
-
return { marketplace: marketplaceName, marketplaceUpdated: false, skipped: true, message: "No changes detected \u2014 skipping install. Use --force to reinstall anyway." };
|
|
7184
|
-
}
|
|
7185
7416
|
const pluginChoices = resolvePluginChoices(marketplacePath);
|
|
7186
7417
|
if (pluginChoices.length === 0) {
|
|
7187
7418
|
warn(`No plugins found in marketplace "${marketplaceName}" \u2014 skipping.`);
|
|
7188
7419
|
return { marketplace: marketplaceName, skipped: true, message: "No plugins found." };
|
|
7189
7420
|
}
|
|
7190
|
-
|
|
7191
|
-
if (pluginFilter === "all") {
|
|
7192
|
-
if (installedOnly) {
|
|
7193
|
-
const installed = getInstalledPluginsForMarketplace(targetDir, marketplaceName, m.repoUrl).filter((name) => pluginChoices.some((p) => p.name === name));
|
|
7194
|
-
if (installed.length === 0) {
|
|
7195
|
-
warn(`No installed plugins found for "${marketplaceName}" \u2014 skipping. Run marketplace sync without --installed-only to install plugins.`);
|
|
7196
|
-
return { marketplace: marketplaceName, skipped: true, installedOnly: true, message: "No installed plugins found \u2014 nothing to sync." };
|
|
7197
|
-
}
|
|
7198
|
-
pluginNames = installed;
|
|
7199
|
-
} else {
|
|
7200
|
-
pluginNames = pluginChoices.map((p) => p.name);
|
|
7201
|
-
}
|
|
7202
|
-
} else if (pluginChoices.some((p) => p.name === pluginFilter)) {
|
|
7203
|
-
pluginNames = [pluginFilter];
|
|
7204
|
-
} else {
|
|
7421
|
+
if (pluginFilter !== "all" && !pluginChoices.some((p) => p.name === pluginFilter)) {
|
|
7205
7422
|
warn(`Plugin "${pluginFilter}" not found in "${marketplaceName}" \u2014 skipping.`);
|
|
7206
7423
|
return { marketplace: marketplaceName, skipped: true, message: `Plugin "${pluginFilter}" not found.` };
|
|
7207
7424
|
}
|
|
7208
|
-
const
|
|
7209
|
-
|
|
7425
|
+
const targetResults = targets.map((t) => installMarketplaceToTarget(m, marketplacePath, pluginChoices, pluginFilter, t, { updated, ...opts }));
|
|
7426
|
+
const instructions = applyInstructionsForTargets(m, marketplacePath, targets, opts.instructions);
|
|
7427
|
+
return shapeSyncResult(marketplaceName, updated, pluginFilter, targetResults, { installedOnly: opts.installedOnly, instructions });
|
|
7210
7428
|
} catch (err) {
|
|
7211
7429
|
const message = err instanceof Error ? err.message : String(err);
|
|
7212
7430
|
warn(`Marketplace "${marketplaceName}" failed \u2014 skipping. ${message}`);
|
|
@@ -7214,7 +7432,7 @@ function syncMarketplacePlugins(m, targetDir, force, pluginFilter, installedOnly
|
|
|
7214
7432
|
}
|
|
7215
7433
|
}
|
|
7216
7434
|
function performMarketplaceAdd(url, localPath, opts) {
|
|
7217
|
-
const resolvedPath =
|
|
7435
|
+
const resolvedPath = path8.resolve(localPath ?? defaultMarketplacePath(url));
|
|
7218
7436
|
const marketplaceName = opts.name ?? repoNameFromUrl(url);
|
|
7219
7437
|
cloneOrReuseMarketplace(url, resolvedPath, opts, marketplaceName);
|
|
7220
7438
|
const configPath = getGlobalConfigPath();
|
|
@@ -7227,17 +7445,18 @@ function performMarketplaceAdd(url, localPath, opts) {
|
|
|
7227
7445
|
};
|
|
7228
7446
|
upsertMarketplace(all, entry);
|
|
7229
7447
|
saveMarketplaces(configPath, existing, all);
|
|
7230
|
-
const
|
|
7231
|
-
const
|
|
7448
|
+
const targets = resolveInstallTargets(opts);
|
|
7449
|
+
const targetResults = installAllPlugins(entry, resolvedPath, targets);
|
|
7450
|
+
const instructions = applyInstructionsForTargets(entry, resolvedPath, targets, opts.instructions !== false);
|
|
7232
7451
|
return {
|
|
7233
7452
|
name: marketplaceName,
|
|
7234
7453
|
repoUrl: url,
|
|
7235
7454
|
localPath: resolvedPath,
|
|
7236
7455
|
branch: opts.branch ?? null,
|
|
7237
7456
|
tokenConfigured: !!opts.token,
|
|
7238
|
-
|
|
7239
|
-
total:
|
|
7240
|
-
|
|
7457
|
+
// One target keeps the flat shape earlier versions emitted; several nest under `targets`.
|
|
7458
|
+
...targetResults.length === 1 ? { plugins: targetResults[0].plugins, total: targetResults[0].total, target: targetResults[0].target } : { targets: targetResults, total: targetResults.reduce((sum, r) => sum + r.total, 0) },
|
|
7459
|
+
...instructions ? { instructions } : {}
|
|
7241
7460
|
};
|
|
7242
7461
|
}
|
|
7243
7462
|
function performMarketplaceRemove(name) {
|
|
@@ -7271,8 +7490,8 @@ function listBundledSkillDirs() {
|
|
|
7271
7490
|
let skillDirs = [];
|
|
7272
7491
|
if (bundledDir !== "") {
|
|
7273
7492
|
try {
|
|
7274
|
-
skillDirs =
|
|
7275
|
-
(name) =>
|
|
7493
|
+
skillDirs = fs3.readdirSync(bundledDir).filter(
|
|
7494
|
+
(name) => fs3.existsSync(path8.join(bundledDir, name, "SKILL.md"))
|
|
7276
7495
|
);
|
|
7277
7496
|
} catch {
|
|
7278
7497
|
}
|
|
@@ -7283,37 +7502,37 @@ function listBundledSkillDirs() {
|
|
|
7283
7502
|
return skillDirs;
|
|
7284
7503
|
}
|
|
7285
7504
|
function installBundledSkillsTo(targetDir, skillDirs) {
|
|
7286
|
-
const resolvedTarget =
|
|
7505
|
+
const resolvedTarget = path8.resolve(targetDir);
|
|
7287
7506
|
const bundledDir = getBundledSkillsDir();
|
|
7288
7507
|
const installed = [];
|
|
7289
7508
|
const failed = [];
|
|
7290
7509
|
for (const skillName of skillDirs) {
|
|
7291
|
-
const existingDir =
|
|
7292
|
-
if (!existingDir.startsWith(resolvedTarget +
|
|
7293
|
-
if (
|
|
7294
|
-
|
|
7510
|
+
const existingDir = path8.resolve(targetDir, skillName);
|
|
7511
|
+
if (!existingDir.startsWith(resolvedTarget + path8.sep)) continue;
|
|
7512
|
+
if (fs3.existsSync(existingDir)) {
|
|
7513
|
+
fs3.rmSync(existingDir, { recursive: true, force: true });
|
|
7295
7514
|
}
|
|
7296
7515
|
}
|
|
7297
|
-
|
|
7516
|
+
log(`Installing ${skillDirs.length} bundled skill(s) to ${targetDir}...`);
|
|
7298
7517
|
for (const skillName of skillDirs) {
|
|
7299
|
-
const skillDir =
|
|
7300
|
-
if (!skillDir.startsWith(resolvedTarget +
|
|
7518
|
+
const skillDir = path8.resolve(targetDir, skillName);
|
|
7519
|
+
if (!skillDir.startsWith(resolvedTarget + path8.sep)) {
|
|
7301
7520
|
failed.push(skillName);
|
|
7302
7521
|
continue;
|
|
7303
7522
|
}
|
|
7304
7523
|
try {
|
|
7305
|
-
const mdFiles =
|
|
7306
|
-
|
|
7524
|
+
const mdFiles = fs3.readdirSync(path8.join(bundledDir, skillName)).filter((f) => f.endsWith(".md"));
|
|
7525
|
+
fs3.mkdirSync(skillDir, { recursive: true });
|
|
7307
7526
|
for (const mdFile of mdFiles) {
|
|
7308
|
-
const content =
|
|
7309
|
-
|
|
7527
|
+
const content = fs3.readFileSync(path8.join(bundledDir, skillName, mdFile), "utf8");
|
|
7528
|
+
fs3.writeFileSync(path8.join(skillDir, mdFile), content, "utf8");
|
|
7310
7529
|
}
|
|
7311
7530
|
installed.push(skillName);
|
|
7312
7531
|
} catch {
|
|
7313
7532
|
failed.push(skillName);
|
|
7314
7533
|
}
|
|
7315
7534
|
}
|
|
7316
|
-
warn(`Installed ${installed.length} skill(s) to ${targetDir}`);
|
|
7535
|
+
warn(`Installed ${installed.length} bundled skill(s) to ${targetDir}`);
|
|
7317
7536
|
if (failed.length > 0) {
|
|
7318
7537
|
warn(`Failed to install: ${failed.join(", ")}`);
|
|
7319
7538
|
}
|
|
@@ -7321,8 +7540,28 @@ function installBundledSkillsTo(targetDir, skillDirs) {
|
|
|
7321
7540
|
return { installed, failed };
|
|
7322
7541
|
}
|
|
7323
7542
|
function registerSkillsCommands(program2) {
|
|
7324
|
-
const skills = program2.command("skills").description(`
|
|
7325
|
-
skills.
|
|
7543
|
+
const skills = program2.command("skills").description(`Install bundled pncli skills and org plugins from git marketplaces (${AGENT_CHOICES})`);
|
|
7544
|
+
skills.addHelpText("after", `
|
|
7545
|
+
Where skills go:
|
|
7546
|
+
--agent codex (default) \u2192 .agents/skills (project) or ~/.agents/skills (user); read by Codex and GitHub Copilot
|
|
7547
|
+
--agent github-copilot \u2192 .github/skills or ~/.copilot/skills
|
|
7548
|
+
--agent claude-code \u2192 .claude/skills or ~/.claude/skills (--claude is a shorthand)
|
|
7549
|
+
--all-agents \u2192 every host above in one run
|
|
7550
|
+
|
|
7551
|
+
Bundled skills (ship with pncli, refresh after upgrading):
|
|
7552
|
+
pncli skills install --all-agents # into this repo, every agent host
|
|
7553
|
+
pncli skills install --all-agents --scope user # for every repo on this machine
|
|
7554
|
+
|
|
7555
|
+
Org plugins (skills and AGENTS.md/CLAUDE.md from a git-hosted marketplace):
|
|
7556
|
+
pncli skills marketplace add <git-url> --all-agents # register, clone, install everything
|
|
7557
|
+
pncli skills marketplace sync --marketplace all --all-agents
|
|
7558
|
+
pncli skills marketplace manage # interactive: toggle plugins, add/remove marketplaces
|
|
7559
|
+
pncli skills marketplace --help # the full plugin workflow
|
|
7560
|
+
|
|
7561
|
+
Check what is installed:
|
|
7562
|
+
pncli skills status | pncli skills locations | pncli doctor
|
|
7563
|
+
`);
|
|
7564
|
+
skills.command("install").description("Install the bundled pncli skills (project scope by default; --scope user for every repo)").option("--agent <agent>", `Target agent host: ${AGENT_CHOICES}`, DEFAULT_AGENT).option("--scope <scope>", "Installation scope: project | user", "project").option("--claude", "Shorthand for --agent claude-code").option("--all-agents", "Install to every supported agent host in one run").option("--target <dir>", "Override install directory (ignores --agent and --scope)").action((opts, cmd) => {
|
|
7326
7565
|
const start = Date.now();
|
|
7327
7566
|
try {
|
|
7328
7567
|
const agentExplicit = cmd.getOptionValueSource("agent") === "cli";
|
|
@@ -7332,10 +7571,9 @@ function registerSkillsCommands(program2) {
|
|
|
7332
7571
|
const scope = opts.scope === "user" ? "user" : "project";
|
|
7333
7572
|
const skillDirs = listBundledSkillDirs();
|
|
7334
7573
|
if (opts.allAgents) {
|
|
7335
|
-
const targets =
|
|
7336
|
-
const
|
|
7337
|
-
|
|
7338
|
-
return { agent, target: targetDir2, installed: installed2, failed: failed2, total: installed2.length };
|
|
7574
|
+
const targets = resolveInstallTargets({ allAgents: true }, scope).map(({ agent, target }) => {
|
|
7575
|
+
const { installed: installed2, failed: failed2 } = installBundledSkillsTo(target, skillDirs);
|
|
7576
|
+
return { agent, target, installed: installed2, failed: failed2, total: installed2.length };
|
|
7339
7577
|
});
|
|
7340
7578
|
success({
|
|
7341
7579
|
targets,
|
|
@@ -7346,7 +7584,7 @@ function registerSkillsCommands(program2) {
|
|
|
7346
7584
|
return;
|
|
7347
7585
|
}
|
|
7348
7586
|
const agentName = resolveAgentName(opts);
|
|
7349
|
-
const targetDir = opts.target ?
|
|
7587
|
+
const targetDir = opts.target ? path8.resolve(opts.target) : resolveScopedPath(resolveAgentPaths(agentName), scope);
|
|
7350
7588
|
const { installed, failed } = installBundledSkillsTo(targetDir, skillDirs);
|
|
7351
7589
|
let tracked = false;
|
|
7352
7590
|
if (opts.target && installed.length > 0) {
|
|
@@ -7375,12 +7613,12 @@ function registerSkillsCommands(program2) {
|
|
|
7375
7613
|
try {
|
|
7376
7614
|
let targetDir;
|
|
7377
7615
|
if (opts.target) {
|
|
7378
|
-
targetDir =
|
|
7616
|
+
targetDir = path8.resolve(opts.target);
|
|
7379
7617
|
} else {
|
|
7380
7618
|
const agentConfig = resolveAgentPaths(resolveAgentName(opts));
|
|
7381
7619
|
targetDir = resolveScopedPath(agentConfig, opts.scope === "user" ? "user" : "project");
|
|
7382
7620
|
}
|
|
7383
|
-
if (!
|
|
7621
|
+
if (!fs3.existsSync(targetDir)) {
|
|
7384
7622
|
success({
|
|
7385
7623
|
skills: [],
|
|
7386
7624
|
total: 0,
|
|
@@ -7391,13 +7629,13 @@ function registerSkillsCommands(program2) {
|
|
|
7391
7629
|
return;
|
|
7392
7630
|
}
|
|
7393
7631
|
const meta = readInstalledMeta(targetDir);
|
|
7394
|
-
const skillDirs =
|
|
7632
|
+
const skillDirs = fs3.readdirSync(targetDir).filter((name) => {
|
|
7395
7633
|
if (name.startsWith(".")) return false;
|
|
7396
|
-
const skillPath =
|
|
7397
|
-
return
|
|
7634
|
+
const skillPath = path8.join(targetDir, name, "SKILL.md");
|
|
7635
|
+
return fs3.existsSync(skillPath);
|
|
7398
7636
|
});
|
|
7399
7637
|
const skillsList = skillDirs.map((name) => {
|
|
7400
|
-
const content =
|
|
7638
|
+
const content = fs3.readFileSync(path8.join(targetDir, name, "SKILL.md"), "utf8");
|
|
7401
7639
|
const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
7402
7640
|
const data = {};
|
|
7403
7641
|
const metadata = {};
|
|
@@ -7450,24 +7688,24 @@ function registerSkillsCommands(program2) {
|
|
|
7450
7688
|
try {
|
|
7451
7689
|
let targetDir;
|
|
7452
7690
|
if (opts.target) {
|
|
7453
|
-
targetDir =
|
|
7691
|
+
targetDir = path8.resolve(opts.target);
|
|
7454
7692
|
} else {
|
|
7455
7693
|
const agentConfig = resolveAgentPaths(resolveAgentName(opts));
|
|
7456
7694
|
targetDir = resolveScopedPath(agentConfig, opts.scope === "project" ? "project" : "user");
|
|
7457
7695
|
}
|
|
7458
|
-
const resolvedTarget =
|
|
7459
|
-
const skillDir =
|
|
7460
|
-
if (!skillDir.startsWith(resolvedTarget +
|
|
7696
|
+
const resolvedTarget = path8.resolve(targetDir);
|
|
7697
|
+
const skillDir = path8.resolve(targetDir, name);
|
|
7698
|
+
if (!skillDir.startsWith(resolvedTarget + path8.sep)) {
|
|
7461
7699
|
throw new Error(`Invalid skill name: "${name}"`);
|
|
7462
7700
|
}
|
|
7463
|
-
if (!
|
|
7701
|
+
if (!fs3.existsSync(skillDir)) {
|
|
7464
7702
|
throw new Error(`Skill "${name}" not found at ${skillDir}`);
|
|
7465
7703
|
}
|
|
7466
7704
|
const meta = readInstalledMeta(targetDir);
|
|
7467
7705
|
const record = meta.skills[name];
|
|
7468
|
-
|
|
7706
|
+
fs3.rmSync(skillDir, { recursive: true, force: true });
|
|
7469
7707
|
delete meta.skills[name];
|
|
7470
|
-
|
|
7708
|
+
fs3.writeFileSync(getInstalledMetaPath(targetDir), JSON.stringify(meta, null, 2), "utf8");
|
|
7471
7709
|
success({
|
|
7472
7710
|
uninstalled: name,
|
|
7473
7711
|
target: targetDir,
|
|
@@ -7538,7 +7776,7 @@ function registerSkillsCommands(program2) {
|
|
|
7538
7776
|
const start = Date.now();
|
|
7539
7777
|
try {
|
|
7540
7778
|
const configPath = getGlobalConfigPath();
|
|
7541
|
-
const resolved =
|
|
7779
|
+
const resolved = path8.resolve(dir);
|
|
7542
7780
|
const forgotten = forgetCustomTarget(configPath, resolved);
|
|
7543
7781
|
if (!forgotten) {
|
|
7544
7782
|
warn(`${resolved} is not a tracked custom target \u2014 nothing to forget.`);
|
|
@@ -7552,16 +7790,28 @@ function registerSkillsCommands(program2) {
|
|
|
7552
7790
|
fail(err, "skills", "forget-target", start);
|
|
7553
7791
|
}
|
|
7554
7792
|
});
|
|
7555
|
-
const marketplace = skills.command("marketplace").description("
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
|
|
7793
|
+
const marketplace = skills.command("marketplace").description("Org plugins from git-hosted marketplaces: add, sync, enable/disable, and shipped AGENTS.md/CLAUDE.md");
|
|
7794
|
+
marketplace.addHelpText("after", `
|
|
7795
|
+
Workflow:
|
|
7796
|
+
pncli skills marketplace add <git-url> --all-agents # register + clone + install every plugin
|
|
7797
|
+
pncli skills marketplace sync --marketplace all --all-agents # pull and refresh everything installed
|
|
7798
|
+
pncli skills marketplace plugins <name> # browse a marketplace without installing
|
|
7799
|
+
pncli skills marketplace manage # interactive hub (toggle, add, remove)
|
|
7800
|
+
pncli skills marketplace disable <plugin> / enable <plugin> # switch a plugin off without deleting it
|
|
7801
|
+
|
|
7802
|
+
Shipped instructions:
|
|
7803
|
+
A marketplace may ship instructions/AGENTS.md and/or instructions/CLAUDE.md. add and sync merge
|
|
7804
|
+
them into each agent's user-level file (~/.codex/AGENTS.md, ~/.copilot/copilot-instructions.md,
|
|
7805
|
+
~/.claude/CLAUDE.md) as a marked block that never touches your own content.
|
|
7806
|
+
pncli skills marketplace instructions list | install | remove <name>
|
|
7807
|
+
|
|
7808
|
+
Plugin skills always install at user scope. --agent picks the host (default: ${DEFAULT_AGENT}); --all-agents covers all of them.
|
|
7809
|
+
`);
|
|
7810
|
+
marketplace.command("add").alias("setup").description("Register a new marketplace, clone it, install all its plugins, and apply its shipped AGENTS.md / CLAUDE.md (`setup` is an alias)").argument("<url>", "Git clone URL of the marketplace repository").argument("[localPath]", "Local directory to clone into (default: ~/.agents/marketplaces/<repo-name>)").option("--name <name>", "Human-readable name for this marketplace (default: derived from URL)").option("--branch <branch>", "Branch to clone (default: remote HEAD)").option("--token <token>", "HTTP access token for authenticated clone and pull (GitHub PAT or Bitbucket token)").option("--agent <agent>", `Target agent host for plugin install: ${AGENT_CHOICES} (default: ${DEFAULT_AGENT})`).option("--claude", "Shorthand for --agent claude-code").option("--all-agents", "Install to every supported agent host in one run").option("--no-instructions", "Do not apply the shipped AGENTS.md / CLAUDE.md to user-level instructions files").action((url, localPath, opts) => {
|
|
7811
|
+
const marketplaceIdx = process.argv.indexOf("marketplace");
|
|
7812
|
+
const invokedAs = marketplaceIdx !== -1 && process.argv[marketplaceIdx + 1] === "setup" ? "marketplace-setup" : "marketplace-add";
|
|
7813
|
+
return marketplaceAddAction(url, localPath, opts, invokedAs);
|
|
7814
|
+
});
|
|
7565
7815
|
marketplace.command("list").description("List all registered marketplaces").action(() => {
|
|
7566
7816
|
const start = Date.now();
|
|
7567
7817
|
try {
|
|
@@ -7573,7 +7823,7 @@ function registerSkillsCommands(program2) {
|
|
|
7573
7823
|
repoUrl: m.repoUrl,
|
|
7574
7824
|
localPath: m.localPath,
|
|
7575
7825
|
tokenConfigured: !!m.token,
|
|
7576
|
-
upstreamRemote: m.localPath &&
|
|
7826
|
+
upstreamRemote: m.localPath && fs3.existsSync(m.localPath) ? getRepoRemoteUrl(m.localPath) : null
|
|
7577
7827
|
})),
|
|
7578
7828
|
total: all.length
|
|
7579
7829
|
}, "skills", "marketplace-list", start);
|
|
@@ -7590,7 +7840,7 @@ function registerSkillsCommands(program2) {
|
|
|
7590
7840
|
if (!found) {
|
|
7591
7841
|
throw new Error(`Marketplace "${name}" not found. Run: pncli skills marketplace list`);
|
|
7592
7842
|
}
|
|
7593
|
-
if (!found.localPath || !
|
|
7843
|
+
if (!found.localPath || !fs3.existsSync(found.localPath)) {
|
|
7594
7844
|
throw new Error(`Marketplace "${marketplaceLabel(found)}" local path not found at ${found.localPath ?? "(not set)"}. Run: pncli skills marketplace add <url>`);
|
|
7595
7845
|
}
|
|
7596
7846
|
const plugins = resolvePluginChoices(found.localPath);
|
|
@@ -7611,7 +7861,7 @@ function registerSkillsCommands(program2) {
|
|
|
7611
7861
|
fail(err, "skills", "marketplace-remove", start);
|
|
7612
7862
|
}
|
|
7613
7863
|
});
|
|
7614
|
-
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: ${AGENT_CHOICES} (default: ${DEFAULT_AGENT})`).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)').option("--installed-only", "Only sync plugins that are already installed \u2014 skip plugins newly added to the marketplace").action(async (plugin, opts) => {
|
|
7864
|
+
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: ${AGENT_CHOICES} (default: ${DEFAULT_AGENT})`).option("--claude", "Shorthand for --agent claude-code").option("--all-agents", "Install to every supported agent host in one run").option("--force", 'Reinstall even if a marketplace has no new changes (applies to single-plugin and "all" installs alike)').option("--installed-only", "Only sync plugins that are already installed \u2014 skip plugins newly added to the marketplace").option("--no-instructions", "Do not apply the shipped AGENTS.md / CLAUDE.md to user-level instructions files").action(async (plugin, opts) => {
|
|
7615
7865
|
const start = Date.now();
|
|
7616
7866
|
try {
|
|
7617
7867
|
const configPath = getGlobalConfigPath();
|
|
@@ -7619,12 +7869,12 @@ function registerSkillsCommands(program2) {
|
|
|
7619
7869
|
if (allMarketplaces.length === 0) {
|
|
7620
7870
|
throw new Error("No marketplaces configured. Run: pncli skills marketplace add <url>");
|
|
7621
7871
|
}
|
|
7622
|
-
const
|
|
7623
|
-
const
|
|
7624
|
-
const
|
|
7872
|
+
const targets = resolveInstallTargets(opts);
|
|
7873
|
+
const syncOpts = { force: opts.force ?? false, installedOnly: opts.installedOnly ?? false, instructions: opts.instructions !== false };
|
|
7874
|
+
const targetSummary = targets.length === 1 ? { target: targets[0].target } : { targets: targets.map((t) => t.target) };
|
|
7625
7875
|
if (opts.marketplace === "all") {
|
|
7626
|
-
const results = allMarketplaces.map((m) => syncMarketplacePlugins(m,
|
|
7627
|
-
success({ allMarketplaces: true, marketplaces: results,
|
|
7876
|
+
const results = allMarketplaces.map((m) => syncMarketplacePlugins(m, targets, plugin ?? "all", syncOpts));
|
|
7877
|
+
success({ allMarketplaces: true, marketplaces: results, ...targetSummary }, "skills", "marketplace-sync", start);
|
|
7628
7878
|
return;
|
|
7629
7879
|
}
|
|
7630
7880
|
let selectedMarketplace;
|
|
@@ -7647,12 +7897,12 @@ function registerSkillsCommands(program2) {
|
|
|
7647
7897
|
choices: [
|
|
7648
7898
|
{ value: ALL_MARKETPLACES, name: "All marketplaces \u2014 sync every plugin from every marketplace" },
|
|
7649
7899
|
// Index-based values: labels alone aren't guaranteed unique (two marketplaces can share a derived name).
|
|
7650
|
-
...allMarketplaces.map((
|
|
7900
|
+
...allMarketplaces.map((m2, i) => ({ value: String(i), name: `${marketplaceLabel(m2)} \u2014 ${m2.repoUrl ?? ""}` }))
|
|
7651
7901
|
]
|
|
7652
7902
|
});
|
|
7653
7903
|
if (chosen === ALL_MARKETPLACES) {
|
|
7654
|
-
const
|
|
7655
|
-
success({ allMarketplaces: true, marketplaces:
|
|
7904
|
+
const results = allMarketplaces.map((m2) => syncMarketplacePlugins(m2, targets, selectedPlugin ?? "all", syncOpts));
|
|
7905
|
+
success({ allMarketplaces: true, marketplaces: results, ...targetSummary }, "skills", "marketplace-sync", start);
|
|
7656
7906
|
return;
|
|
7657
7907
|
}
|
|
7658
7908
|
selectedMarketplace = allMarketplaces[Number(chosen)];
|
|
@@ -7662,7 +7912,7 @@ function registerSkillsCommands(program2) {
|
|
|
7662
7912
|
}
|
|
7663
7913
|
const marketplaceName = marketplaceLabel(selectedMarketplace);
|
|
7664
7914
|
const marketplacePath = selectedMarketplace.localPath;
|
|
7665
|
-
if (!marketplacePath || !
|
|
7915
|
+
if (!marketplacePath || !fs3.existsSync(marketplacePath)) {
|
|
7666
7916
|
throw new Error(`Marketplace "${marketplaceName}" local path not found at ${marketplacePath ?? "(not set)"}. Run: pncli skills marketplace add <url>`);
|
|
7667
7917
|
}
|
|
7668
7918
|
const { updated } = pullMarketplace(marketplacePath, selectedMarketplace.repoUrl, selectedMarketplace.token, marketplaceName);
|
|
@@ -7688,55 +7938,94 @@ function registerSkillsCommands(program2) {
|
|
|
7688
7938
|
} else if (selectedPlugin !== "all" && !pluginChoices.some((p) => p.name === selectedPlugin)) {
|
|
7689
7939
|
throw new Error(`Plugin "${selectedPlugin}" not found in "${marketplaceName}". Available: ${pluginChoices.map((p) => p.name).join(", ")}`);
|
|
7690
7940
|
}
|
|
7691
|
-
|
|
7692
|
-
|
|
7693
|
-
|
|
7694
|
-
|
|
7695
|
-
|
|
7696
|
-
|
|
7697
|
-
|
|
7698
|
-
|
|
7699
|
-
|
|
7941
|
+
const m = selectedMarketplace;
|
|
7942
|
+
const targetResults = targets.map((t) => installMarketplaceToTarget(m, marketplacePath, pluginChoices, selectedPlugin, t, { updated, force: syncOpts.force, installedOnly: syncOpts.installedOnly }));
|
|
7943
|
+
const instructions2 = applyInstructionsForTargets(m, marketplacePath, targets, syncOpts.instructions);
|
|
7944
|
+
success(
|
|
7945
|
+
shapeSyncResult(marketplaceName, updated, selectedPlugin, targetResults, { installedOnly: syncOpts.installedOnly, instructions: instructions2 }),
|
|
7946
|
+
"skills",
|
|
7947
|
+
"marketplace-sync",
|
|
7948
|
+
start
|
|
7949
|
+
);
|
|
7950
|
+
return;
|
|
7951
|
+
}
|
|
7952
|
+
} catch (err) {
|
|
7953
|
+
fail(err, "skills", "marketplace-sync", start);
|
|
7954
|
+
}
|
|
7955
|
+
});
|
|
7956
|
+
const instructions = marketplace.command("instructions").description("Manage the AGENTS.md / CLAUDE.md a marketplace ships (merged as a marked block into each agent's user-level instructions file)");
|
|
7957
|
+
function resolveInstructionAgents(opts) {
|
|
7958
|
+
return resolveInstallTargets(opts).map((t) => t.agent);
|
|
7959
|
+
}
|
|
7960
|
+
function findMarketplaceOrThrow(all, name) {
|
|
7961
|
+
const found = all.find((m) => m.name === name || m.repoUrl === name);
|
|
7962
|
+
if (!found) throw new Error(`Marketplace "${name}" not found. Run: pncli skills marketplace list`);
|
|
7963
|
+
return found;
|
|
7964
|
+
}
|
|
7965
|
+
instructions.command("list").description("Show which marketplaces ship instructions and whether each agent's user-level file has them installed and current").option("--marketplace <name>", "Only report this marketplace (name or clone URL)").action((opts) => {
|
|
7966
|
+
const start = Date.now();
|
|
7967
|
+
try {
|
|
7968
|
+
const all = loadMarketplaces(getGlobalConfigPath());
|
|
7969
|
+
const selected = opts.marketplace ? [findMarketplaceOrThrow(all, opts.marketplace)] : all;
|
|
7970
|
+
const agents = Object.keys(AGENT_PATHS);
|
|
7971
|
+
const marketplaces = selected.map((m) => {
|
|
7972
|
+
const shipped = m.localPath && fs3.existsSync(m.localPath) ? findMarketplaceInstructions(m.localPath).map((s) => s.file) : [];
|
|
7973
|
+
return {
|
|
7974
|
+
marketplace: marketplaceLabel(m),
|
|
7975
|
+
localPath: m.localPath ?? null,
|
|
7976
|
+
ships: shipped,
|
|
7977
|
+
agents: marketplaceInstructionsStatus(m.localPath, marketplaceLabel(m), agents)
|
|
7978
|
+
};
|
|
7979
|
+
});
|
|
7980
|
+
success({
|
|
7981
|
+
files: Object.fromEntries(agents.map((a) => [a, agentInstructionsFile(a)])),
|
|
7982
|
+
marketplaces,
|
|
7983
|
+
hint: marketplaces.some((m) => m.ships.length > 0) ? "Apply with: pncli skills marketplace instructions install --all-agents" : "No registered marketplace ships an instructions/AGENTS.md or instructions/CLAUDE.md."
|
|
7984
|
+
}, "skills", "marketplace-instructions-list", start);
|
|
7985
|
+
} catch (err) {
|
|
7986
|
+
fail(err, "skills", "marketplace-instructions-list", start);
|
|
7987
|
+
}
|
|
7988
|
+
});
|
|
7989
|
+
instructions.command("install").description("Apply (or refresh) the shipped instructions of one or every marketplace into the agent's user-level file").option("--marketplace <name>", "Marketplace to apply (name or clone URL); default: every registered marketplace that ships instructions").option("--agent <agent>", `Target agent host: ${AGENT_CHOICES} (default: ${DEFAULT_AGENT})`).option("--claude", "Shorthand for --agent claude-code").option("--all-agents", "Apply to every supported agent host in one run").action((opts) => {
|
|
7990
|
+
const start = Date.now();
|
|
7991
|
+
try {
|
|
7992
|
+
const all = loadMarketplaces(getGlobalConfigPath());
|
|
7993
|
+
if (all.length === 0) throw new Error("No marketplaces configured. Run: pncli skills marketplace add <url>");
|
|
7994
|
+
const agents = resolveInstructionAgents(opts);
|
|
7995
|
+
const selected = opts.marketplace ? [findMarketplaceOrThrow(all, opts.marketplace)] : all;
|
|
7996
|
+
const results = selected.map((m) => {
|
|
7997
|
+
const name = marketplaceLabel(m);
|
|
7998
|
+
if (!m.localPath || !fs3.existsSync(m.localPath)) {
|
|
7999
|
+
return { marketplace: name, skipped: true, message: "Local path not found. Run: pncli skills marketplace add <url>" };
|
|
7700
8000
|
}
|
|
7701
|
-
|
|
7702
|
-
|
|
7703
|
-
if (installedOnly) {
|
|
7704
|
-
const installed = getInstalledPluginsForMarketplace(targetDir, marketplaceName, selectedMarketplace.repoUrl).filter((name) => pluginChoices.some((p) => p.name === name));
|
|
7705
|
-
if (installed.length === 0) {
|
|
7706
|
-
success({
|
|
7707
|
-
marketplace: marketplaceName,
|
|
7708
|
-
skipped: true,
|
|
7709
|
-
installedOnly: true,
|
|
7710
|
-
message: `No installed plugins found for "${marketplaceName}" \u2014 run marketplace sync without --installed-only to install plugins.`
|
|
7711
|
-
}, "skills", "marketplace-sync", start);
|
|
7712
|
-
return;
|
|
7713
|
-
}
|
|
7714
|
-
pluginNames = installed;
|
|
7715
|
-
} else {
|
|
7716
|
-
pluginNames = pluginChoices.map((p) => p.name);
|
|
7717
|
-
}
|
|
7718
|
-
} else {
|
|
7719
|
-
pluginNames = [selectedPlugin];
|
|
8001
|
+
if (findMarketplaceInstructions(m.localPath).length === 0) {
|
|
8002
|
+
return { marketplace: name, skipped: true, message: `Ships no instructions/AGENTS.md or instructions/CLAUDE.md.` };
|
|
7720
8003
|
}
|
|
7721
|
-
const
|
|
7722
|
-
|
|
7723
|
-
|
|
7724
|
-
} else {
|
|
7725
|
-
const single = results[selectedPlugin] ?? { installed: [], failed: [] };
|
|
7726
|
-
success({
|
|
7727
|
-
marketplace: marketplaceName,
|
|
7728
|
-
plugin: selectedPlugin,
|
|
7729
|
-
installed: single.installed,
|
|
7730
|
-
failed: single.failed,
|
|
7731
|
-
total: single.installed.length,
|
|
7732
|
-
target: targetDir,
|
|
7733
|
-
marketplaceUpdated: updated
|
|
7734
|
-
}, "skills", "marketplace-sync", start);
|
|
8004
|
+
const applied = applyMarketplaceInstructions(m.localPath, name, agents);
|
|
8005
|
+
for (const r of applied) {
|
|
8006
|
+
if (r.action === "added" || r.action === "updated") warn(`Instructions ${r.action} in ${r.file} (from ${r.source})`);
|
|
7735
8007
|
}
|
|
7736
|
-
return;
|
|
8008
|
+
return { marketplace: name, agents: applied };
|
|
8009
|
+
});
|
|
8010
|
+
if (opts.marketplace && "skipped" in results[0]) throw new Error(`Marketplace "${results[0].marketplace}": ${results[0].message}`);
|
|
8011
|
+
success({ marketplaces: results, agents }, "skills", "marketplace-instructions-install", start);
|
|
8012
|
+
} catch (err) {
|
|
8013
|
+
fail(err, "skills", "marketplace-instructions-install", start);
|
|
8014
|
+
}
|
|
8015
|
+
});
|
|
8016
|
+
instructions.command("remove").description("Strip a marketplace's instructions block from the agent's user-level file (everything else in the file is kept)").argument("<marketplace>", "Marketplace name (or clone URL). Works even after the marketplace has been removed from config.").option("--agent <agent>", `Target agent host: ${AGENT_CHOICES} (default: ${DEFAULT_AGENT})`).option("--claude", "Shorthand for --agent claude-code").option("--all-agents", "Remove from every supported agent host in one run").action((marketplaceName, opts) => {
|
|
8017
|
+
const start = Date.now();
|
|
8018
|
+
try {
|
|
8019
|
+
const agents = resolveInstructionAgents(opts);
|
|
8020
|
+
const registered = loadMarketplaces(getGlobalConfigPath()).find((m) => m.name === marketplaceName || m.repoUrl === marketplaceName);
|
|
8021
|
+
const name = registered ? marketplaceLabel(registered) : marketplaceName;
|
|
8022
|
+
const results = removeMarketplaceInstructions(name, agents);
|
|
8023
|
+
for (const r of results) {
|
|
8024
|
+
if (r.removed) warn(`Instructions block for "${name}" removed from ${r.file}`);
|
|
7737
8025
|
}
|
|
8026
|
+
success({ marketplace: name, agents: results, removed: results.filter((r) => r.removed).length }, "skills", "marketplace-instructions-remove", start);
|
|
7738
8027
|
} catch (err) {
|
|
7739
|
-
fail(err, "skills", "marketplace-
|
|
8028
|
+
fail(err, "skills", "marketplace-instructions-remove", start);
|
|
7740
8029
|
}
|
|
7741
8030
|
});
|
|
7742
8031
|
marketplace.command("purge-plugin").description("Remove all skills installed from a specific plugin (or all plugins in a marketplace)").argument("<plugin>", 'Plugin name to purge, or "all" to remove every skill from the marketplace').option("--marketplace <name>", "Restrict purge to skills from this marketplace (by name or repo URL)").option("--agent <agent>", `Target agent host: ${AGENT_CHOICES} (default: ${DEFAULT_AGENT})`).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) => {
|
|
@@ -7744,31 +8033,31 @@ function registerSkillsCommands(program2) {
|
|
|
7744
8033
|
try {
|
|
7745
8034
|
let targetDir;
|
|
7746
8035
|
if (opts.target) {
|
|
7747
|
-
targetDir =
|
|
8036
|
+
targetDir = path8.resolve(opts.target);
|
|
7748
8037
|
} else {
|
|
7749
8038
|
const agentConfig = resolveAgentPaths(resolveAgentName(opts));
|
|
7750
8039
|
targetDir = resolveScopedPath(agentConfig, opts.scope ?? "user");
|
|
7751
8040
|
}
|
|
7752
|
-
const resolvedTarget =
|
|
7753
|
-
if (!
|
|
8041
|
+
const resolvedTarget = path8.resolve(targetDir);
|
|
8042
|
+
if (!fs3.existsSync(targetDir)) {
|
|
7754
8043
|
success({ removed: [], total: 0, target: targetDir, message: "No skills directory found." }, "skills", "marketplace-purge-plugin", start);
|
|
7755
8044
|
return;
|
|
7756
8045
|
}
|
|
7757
8046
|
const meta = readInstalledMeta(targetDir);
|
|
7758
8047
|
const removed = [];
|
|
7759
8048
|
const skipped = [];
|
|
7760
|
-
const skillDirs =
|
|
8049
|
+
const skillDirs = fs3.readdirSync(targetDir).filter((name) => {
|
|
7761
8050
|
if (name.startsWith(".")) return false;
|
|
7762
|
-
const p =
|
|
8051
|
+
const p = path8.join(targetDir, name);
|
|
7763
8052
|
try {
|
|
7764
|
-
return
|
|
8053
|
+
return fs3.statSync(p).isDirectory();
|
|
7765
8054
|
} catch {
|
|
7766
8055
|
return false;
|
|
7767
8056
|
}
|
|
7768
8057
|
});
|
|
7769
8058
|
for (const skillName of skillDirs) {
|
|
7770
|
-
const skillDir =
|
|
7771
|
-
if (!skillDir.startsWith(resolvedTarget +
|
|
8059
|
+
const skillDir = path8.resolve(targetDir, skillName);
|
|
8060
|
+
if (!skillDir.startsWith(resolvedTarget + path8.sep)) continue;
|
|
7772
8061
|
let record = meta.skills[skillName] ?? null;
|
|
7773
8062
|
if (!record) {
|
|
7774
8063
|
const perSkill = readSkillOrigin(skillDir);
|
|
@@ -7793,12 +8082,12 @@ function registerSkillsCommands(program2) {
|
|
|
7793
8082
|
skipped.push(skillName);
|
|
7794
8083
|
continue;
|
|
7795
8084
|
}
|
|
7796
|
-
|
|
8085
|
+
fs3.rmSync(skillDir, { recursive: true, force: true });
|
|
7797
8086
|
delete meta.skills[skillName];
|
|
7798
8087
|
removed.push(skillName);
|
|
7799
8088
|
}
|
|
7800
8089
|
if (removed.length > 0) {
|
|
7801
|
-
|
|
8090
|
+
fs3.writeFileSync(getInstalledMetaPath(targetDir), JSON.stringify(meta, null, 2), "utf8");
|
|
7802
8091
|
}
|
|
7803
8092
|
success({
|
|
7804
8093
|
removed,
|
|
@@ -7816,7 +8105,7 @@ function registerSkillsCommands(program2) {
|
|
|
7816
8105
|
const start = Date.now();
|
|
7817
8106
|
try {
|
|
7818
8107
|
const targetDir = resolveTargetDir(opts);
|
|
7819
|
-
if (!
|
|
8108
|
+
if (!fs3.existsSync(targetDir)) {
|
|
7820
8109
|
success({ disabled: [], total: 0, target: targetDir, message: "No skills directory found." }, "skills", "marketplace-disable", start);
|
|
7821
8110
|
return;
|
|
7822
8111
|
}
|
|
@@ -7828,7 +8117,7 @@ function registerSkillsCommands(program2) {
|
|
|
7828
8117
|
total: result.disabled.length,
|
|
7829
8118
|
target: targetDir,
|
|
7830
8119
|
plugin,
|
|
7831
|
-
stash:
|
|
8120
|
+
stash: path8.join(targetDir, DISABLED_SUBDIR),
|
|
7832
8121
|
...opts.marketplace ? { marketplace: opts.marketplace } : {}
|
|
7833
8122
|
}, "skills", "marketplace-disable", start);
|
|
7834
8123
|
} catch (err) {
|
|
@@ -7839,7 +8128,7 @@ function registerSkillsCommands(program2) {
|
|
|
7839
8128
|
const start = Date.now();
|
|
7840
8129
|
try {
|
|
7841
8130
|
const targetDir = resolveTargetDir(opts);
|
|
7842
|
-
if (!
|
|
8131
|
+
if (!fs3.existsSync(targetDir)) {
|
|
7843
8132
|
success({ enabled: [], total: 0, target: targetDir, message: "No skills directory found." }, "skills", "marketplace-enable", start);
|
|
7844
8133
|
return;
|
|
7845
8134
|
}
|
|
@@ -7864,29 +8153,48 @@ function registerSkillsCommands(program2) {
|
|
|
7864
8153
|
fail(err, "skills", "marketplace-enable", start);
|
|
7865
8154
|
}
|
|
7866
8155
|
});
|
|
7867
|
-
marketplace.command("manage").description("Manage marketplaces and plugins: toggle plugins on/off, add or remove marketplaces (interactive)").option("--agent <agent>", `Target agent host: ${AGENT_CHOICES} (default: ${DEFAULT_AGENT})`).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) => {
|
|
8156
|
+
marketplace.command("manage").description("Manage marketplaces and plugins: toggle plugins on/off, sync every marketplace, apply shipped AGENTS.md / CLAUDE.md, add or remove marketplaces (interactive)").option("--agent <agent>", `Target agent host: ${AGENT_CHOICES} (default: ${DEFAULT_AGENT})`).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) => {
|
|
7868
8157
|
const start = Date.now();
|
|
7869
8158
|
try {
|
|
7870
|
-
assertInteractive("Use `pncli skills marketplace enable|disable <plugin>`, `add <url>`, and `remove <name>` to manage non-interactively.");
|
|
8159
|
+
assertInteractive("Use `pncli skills marketplace enable|disable <plugin>`, `sync --marketplace all`, `instructions install`, `add <url>`, and `remove <name>` to manage non-interactively.");
|
|
7871
8160
|
const targetDir = resolveTargetDir(opts);
|
|
7872
8161
|
const enabledPlugins = [];
|
|
7873
8162
|
const disabledPlugins = [];
|
|
7874
8163
|
const stashMissing = [];
|
|
7875
8164
|
const addedMarketplaces = [];
|
|
7876
8165
|
const removedMarketplaces = [];
|
|
8166
|
+
const syncedMarketplaces = /* @__PURE__ */ new Map();
|
|
8167
|
+
const appliedInstructions = /* @__PURE__ */ new Map();
|
|
8168
|
+
const instructionAgents = opts.target ? [] : [resolveAgentName(opts)];
|
|
7877
8169
|
for (; ; ) {
|
|
7878
|
-
const states =
|
|
8170
|
+
const states = fs3.existsSync(targetDir) ? listPluginStates(targetDir) : [];
|
|
7879
8171
|
const registered = loadMarketplaces(getGlobalConfigPath());
|
|
8172
|
+
const shipping = registered.filter((m) => m.localPath && fs3.existsSync(m.localPath) && findMarketplaceInstructions(m.localPath).length > 0);
|
|
7880
8173
|
const action = await select({
|
|
7881
8174
|
message: `Manage skills marketplaces (target: ${targetDir}):`,
|
|
7882
8175
|
choices: [
|
|
7883
8176
|
...states.length > 0 ? [{ value: "toggle", name: `Toggle plugins on/off (${states.length} installed)` }] : [],
|
|
8177
|
+
...registered.length > 0 ? [{ value: "sync", name: `Sync every marketplace (${registered.length} registered)` }] : [],
|
|
8178
|
+
...shipping.length > 0 && instructionAgents.length > 0 ? [{ value: "instructions", name: `Apply shipped AGENTS.md / CLAUDE.md (${shipping.length} marketplace(s) ship them)` }] : [],
|
|
7884
8179
|
{ value: "add", name: "Add a marketplace" },
|
|
7885
8180
|
...registered.length > 0 ? [{ value: "remove", name: `Remove a marketplace (${registered.length} registered)` }] : [],
|
|
7886
8181
|
{ value: "done", name: "Done" }
|
|
7887
8182
|
]
|
|
7888
8183
|
});
|
|
7889
8184
|
if (action === "done") break;
|
|
8185
|
+
if (action === "sync") {
|
|
8186
|
+
const syncTargets = [{ agent: opts.target ? "custom" : resolveAgentName(opts), target: targetDir }];
|
|
8187
|
+
for (const m of registered) {
|
|
8188
|
+
syncedMarketplaces.set(marketplaceLabel(m), syncMarketplacePlugins(m, syncTargets, "all", { force: false, installedOnly: false, instructions: instructionAgents.length > 0 }));
|
|
8189
|
+
}
|
|
8190
|
+
continue;
|
|
8191
|
+
}
|
|
8192
|
+
if (action === "instructions") {
|
|
8193
|
+
for (const m of shipping) {
|
|
8194
|
+
appliedInstructions.set(marketplaceLabel(m), { marketplace: marketplaceLabel(m), agents: applyMarketplaceInstructions(m.localPath, marketplaceLabel(m), instructionAgents) });
|
|
8195
|
+
}
|
|
8196
|
+
continue;
|
|
8197
|
+
}
|
|
7890
8198
|
if (action === "toggle") {
|
|
7891
8199
|
const sorted = [...states].sort((a, b) => (a.marketplace ?? "").localeCompare(b.marketplace ?? "") || a.plugin.localeCompare(b.plugin));
|
|
7892
8200
|
const choices = [];
|
|
@@ -7936,7 +8244,9 @@ function registerSkillsCommands(program2) {
|
|
|
7936
8244
|
addedMarketplaces.push(performMarketplaceAdd(url, void 0, {
|
|
7937
8245
|
name: name || void 0,
|
|
7938
8246
|
agent: opts.agent,
|
|
7939
|
-
claude: opts.claude
|
|
8247
|
+
claude: opts.claude,
|
|
8248
|
+
// A custom --target has no agent host to map instructions onto.
|
|
8249
|
+
instructions: instructionAgents.length > 0
|
|
7940
8250
|
}));
|
|
7941
8251
|
} else if (action === "remove") {
|
|
7942
8252
|
const chosen = await select({
|
|
@@ -7957,6 +8267,8 @@ function registerSkillsCommands(program2) {
|
|
|
7957
8267
|
disabled: disabledPlugins,
|
|
7958
8268
|
addedMarketplaces,
|
|
7959
8269
|
removedMarketplaces,
|
|
8270
|
+
syncedMarketplaces: [...syncedMarketplaces.values()],
|
|
8271
|
+
appliedInstructions: [...appliedInstructions.values()],
|
|
7960
8272
|
target: targetDir,
|
|
7961
8273
|
...stashMissing.length > 0 ? {
|
|
7962
8274
|
stashMissing,
|
|
@@ -7971,15 +8283,15 @@ function registerSkillsCommands(program2) {
|
|
|
7971
8283
|
const start = Date.now();
|
|
7972
8284
|
try {
|
|
7973
8285
|
const userDir = resolveAgentPaths(resolveAgentName(opts)).user;
|
|
7974
|
-
if (!
|
|
8286
|
+
if (!fs3.existsSync(userDir)) {
|
|
7975
8287
|
success({ removed: 0, target: userDir, message: "No user-level skills directory found \u2014 nothing to purge." }, "skills", "purge-user", start);
|
|
7976
8288
|
return;
|
|
7977
8289
|
}
|
|
7978
|
-
const skillDirs =
|
|
8290
|
+
const skillDirs = fs3.readdirSync(userDir).filter((name) => {
|
|
7979
8291
|
if (name.startsWith(".")) return false;
|
|
7980
|
-
const p =
|
|
8292
|
+
const p = path8.join(userDir, name);
|
|
7981
8293
|
try {
|
|
7982
|
-
return
|
|
8294
|
+
return fs3.statSync(p).isDirectory();
|
|
7983
8295
|
} catch {
|
|
7984
8296
|
return false;
|
|
7985
8297
|
}
|
|
@@ -7995,13 +8307,13 @@ Re-run with --force to confirm, or use \`pncli skills marketplace purge-plugin\`
|
|
|
7995
8307
|
);
|
|
7996
8308
|
}
|
|
7997
8309
|
for (const name of skillDirs) {
|
|
7998
|
-
const skillDir =
|
|
7999
|
-
if (!skillDir.startsWith(
|
|
8000
|
-
|
|
8310
|
+
const skillDir = path8.resolve(userDir, name);
|
|
8311
|
+
if (!skillDir.startsWith(path8.resolve(userDir) + path8.sep)) continue;
|
|
8312
|
+
fs3.rmSync(skillDir, { recursive: true, force: true });
|
|
8001
8313
|
}
|
|
8002
8314
|
const metaPath = getInstalledMetaPath(userDir);
|
|
8003
|
-
if (
|
|
8004
|
-
|
|
8315
|
+
if (fs3.existsSync(metaPath)) {
|
|
8316
|
+
fs3.writeFileSync(metaPath, JSON.stringify({ version: 1, skills: {} }, null, 2), "utf8");
|
|
8005
8317
|
}
|
|
8006
8318
|
success({ removed: skillDirs.length, skills: skillDirs, target: userDir }, "skills", "purge-user", start);
|
|
8007
8319
|
} catch (err) {
|
|
@@ -8010,46 +8322,46 @@ Re-run with --force to confirm, or use \`pncli skills marketplace purge-plugin\`
|
|
|
8010
8322
|
});
|
|
8011
8323
|
}
|
|
8012
8324
|
function resolvePluginChoices(marketplacePath) {
|
|
8013
|
-
const marketplaceJsonPath =
|
|
8014
|
-
if (
|
|
8325
|
+
const marketplaceJsonPath = path8.join(marketplacePath, ".claude-plugin", "marketplace.json");
|
|
8326
|
+
if (fs3.existsSync(marketplaceJsonPath)) {
|
|
8015
8327
|
try {
|
|
8016
|
-
const meta = JSON.parse(
|
|
8328
|
+
const meta = JSON.parse(fs3.readFileSync(marketplaceJsonPath, "utf8"));
|
|
8017
8329
|
if (Array.isArray(meta.plugins) && meta.plugins.length > 0) {
|
|
8018
8330
|
return meta.plugins.map((p) => ({ name: p.name, description: p.description ?? "" }));
|
|
8019
8331
|
}
|
|
8020
8332
|
} catch {
|
|
8021
8333
|
}
|
|
8022
8334
|
}
|
|
8023
|
-
const pluginsDir =
|
|
8024
|
-
if (
|
|
8025
|
-
return
|
|
8335
|
+
const pluginsDir = path8.join(marketplacePath, "plugins");
|
|
8336
|
+
if (fs3.existsSync(pluginsDir)) {
|
|
8337
|
+
return fs3.readdirSync(pluginsDir).filter((name) => fs3.statSync(path8.join(pluginsDir, name)).isDirectory()).map((name) => ({ name, description: "" }));
|
|
8026
8338
|
}
|
|
8027
8339
|
return [];
|
|
8028
8340
|
}
|
|
8029
8341
|
function resolveSkillsSrc(marketplacePath, selectedPlugin) {
|
|
8030
|
-
const pluginsBase =
|
|
8031
|
-
const skillsSrc =
|
|
8032
|
-
if (!skillsSrc.startsWith(pluginsBase +
|
|
8342
|
+
const pluginsBase = path8.resolve(marketplacePath, "plugins");
|
|
8343
|
+
const skillsSrc = path8.resolve(pluginsBase, selectedPlugin, "skills");
|
|
8344
|
+
if (!skillsSrc.startsWith(pluginsBase + path8.sep)) {
|
|
8033
8345
|
throw new Error(`Invalid plugin name: "${selectedPlugin}"`);
|
|
8034
8346
|
}
|
|
8035
8347
|
return skillsSrc;
|
|
8036
8348
|
}
|
|
8037
8349
|
function copyPluginSkills(skillsSrc, targetDir, meta) {
|
|
8038
|
-
|
|
8039
|
-
const resolvedTarget =
|
|
8040
|
-
const skillNames =
|
|
8041
|
-
(name) =>
|
|
8350
|
+
fs3.mkdirSync(targetDir, { recursive: true });
|
|
8351
|
+
const resolvedTarget = path8.resolve(targetDir);
|
|
8352
|
+
const skillNames = fs3.readdirSync(skillsSrc).filter(
|
|
8353
|
+
(name) => fs3.statSync(path8.join(skillsSrc, name)).isDirectory()
|
|
8042
8354
|
);
|
|
8043
8355
|
const installed = [];
|
|
8044
8356
|
const failed = [];
|
|
8045
8357
|
for (const skillName of skillNames) {
|
|
8046
|
-
const dest =
|
|
8047
|
-
if (!dest.startsWith(resolvedTarget +
|
|
8358
|
+
const dest = path8.resolve(targetDir, skillName);
|
|
8359
|
+
if (!dest.startsWith(resolvedTarget + path8.sep)) {
|
|
8048
8360
|
failed.push(skillName);
|
|
8049
8361
|
continue;
|
|
8050
8362
|
}
|
|
8051
|
-
|
|
8052
|
-
|
|
8363
|
+
fs3.rmSync(dest, { recursive: true, force: true });
|
|
8364
|
+
fs3.cpSync(path8.join(skillsSrc, skillName), dest, { recursive: true });
|
|
8053
8365
|
installed.push(skillName);
|
|
8054
8366
|
}
|
|
8055
8367
|
if (meta) {
|
|
@@ -8066,7 +8378,7 @@ function copyPluginSkills(skillsSrc, targetDir, meta) {
|
|
|
8066
8378
|
|
|
8067
8379
|
// src/services/config/commands.ts
|
|
8068
8380
|
import chalk from "chalk";
|
|
8069
|
-
import
|
|
8381
|
+
import fs4 from "fs";
|
|
8070
8382
|
function registerConfigCommands(program2) {
|
|
8071
8383
|
const config = program2.command("config").description("Manage pncli configuration");
|
|
8072
8384
|
config.command("init").description("Setup wizard for service credentials (interactive)").option("--repo", "Write repo config (.pncli.json) instead of global config").action(async (opts) => {
|
|
@@ -8180,8 +8492,8 @@ function registerConfigCommands(program2) {
|
|
|
8180
8492
|
if (cfg.ado.baseUrl) {
|
|
8181
8493
|
try {
|
|
8182
8494
|
const collection = cfg.defaults.ado?.collection;
|
|
8183
|
-
const
|
|
8184
|
-
await http.ado(
|
|
8495
|
+
const path11 = collection ? `/${encodeURIComponent(collection)}/_apis/connectionData?api-version=7.1-preview.1` : "/_apis/projectCollections?api-version=7.1";
|
|
8496
|
+
await http.ado(path11);
|
|
8185
8497
|
results.ado = { ok: true, message: "connected" };
|
|
8186
8498
|
} catch (err) {
|
|
8187
8499
|
results.ado = { ok: false, message: err instanceof Error ? err.message : String(err) };
|
|
@@ -9059,7 +9371,7 @@ async function initGlobalConfig(start) {
|
|
|
9059
9371
|
});
|
|
9060
9372
|
alationUserId = await input2({
|
|
9061
9373
|
message: "Alation user ID (numeric):",
|
|
9062
|
-
validate: (v) =>
|
|
9374
|
+
validate: (v) => /^[1-9]\d*$/.test(v.trim()) || "Must be a positive integer"
|
|
9063
9375
|
});
|
|
9064
9376
|
if (alationBaseUrl && alationRefreshToken && alationUserId) {
|
|
9065
9377
|
process.stderr.write("\n Verifying connection...\n");
|
|
@@ -9312,7 +9624,7 @@ async function initRepoConfig(start) {
|
|
|
9312
9624
|
process.exitCode = ExitCode.SUCCESS;
|
|
9313
9625
|
return;
|
|
9314
9626
|
}
|
|
9315
|
-
if (
|
|
9627
|
+
if (fs4.existsSync(".pncli.json")) {
|
|
9316
9628
|
const overwrite = await confirm({
|
|
9317
9629
|
message: ".pncli.json already exists. Overwrite?",
|
|
9318
9630
|
default: false
|
|
@@ -9346,14 +9658,14 @@ async function initRepoConfig(start) {
|
|
|
9346
9658
|
}
|
|
9347
9659
|
|
|
9348
9660
|
// src/services/doctor/commands.ts
|
|
9349
|
-
import
|
|
9350
|
-
import
|
|
9661
|
+
import fs5 from "fs";
|
|
9662
|
+
import path9 from "path";
|
|
9351
9663
|
function checkConfigFile(filePath) {
|
|
9352
|
-
if (!
|
|
9664
|
+
if (!fs5.existsSync(filePath)) {
|
|
9353
9665
|
return { path: filePath, exists: false, valid: false, message: "not present" };
|
|
9354
9666
|
}
|
|
9355
9667
|
try {
|
|
9356
|
-
JSON.parse(
|
|
9668
|
+
JSON.parse(fs5.readFileSync(filePath, "utf8"));
|
|
9357
9669
|
return { path: filePath, exists: true, valid: true, message: "ok" };
|
|
9358
9670
|
} catch (err) {
|
|
9359
9671
|
return {
|
|
@@ -9364,7 +9676,7 @@ function checkConfigFile(filePath) {
|
|
|
9364
9676
|
};
|
|
9365
9677
|
}
|
|
9366
9678
|
}
|
|
9367
|
-
function buildProblems(globalFile, repoFile, credentials, skillLocations) {
|
|
9679
|
+
function buildProblems(globalFile, repoFile, credentials, skillLocations, marketplaces = []) {
|
|
9368
9680
|
const problems = [];
|
|
9369
9681
|
for (const file of [globalFile, repoFile]) {
|
|
9370
9682
|
if (file.exists && !file.valid) {
|
|
@@ -9415,16 +9727,32 @@ function buildProblems(globalFile, repoFile, credentials, skillLocations) {
|
|
|
9415
9727
|
fix: "Refresh them with: pncli skills install"
|
|
9416
9728
|
});
|
|
9417
9729
|
}
|
|
9730
|
+
for (const m of marketplaces.filter((m2) => !m2.cloneExists)) {
|
|
9731
|
+
problems.push({
|
|
9732
|
+
area: "marketplaces",
|
|
9733
|
+
message: `Marketplace "${m.name}" is registered but its clone is missing (${m.localPath ?? "no local path"})`,
|
|
9734
|
+
fix: `Re-clone it with: pncli skills marketplace add ${m.repoUrl ?? "<url>"}`
|
|
9735
|
+
});
|
|
9736
|
+
}
|
|
9737
|
+
const marketplaceSkillTotal = skillLocations.reduce((sum, l) => sum + (l.marketplaceSkills ?? 0), 0);
|
|
9738
|
+
const liveMarketplaces = marketplaces.filter((m) => m.cloneExists).length;
|
|
9739
|
+
if (liveMarketplaces > 0 && marketplaceSkillTotal === 0) {
|
|
9740
|
+
problems.push({
|
|
9741
|
+
area: "marketplaces",
|
|
9742
|
+
message: `${liveMarketplaces} marketplace(s) registered but no plugin skills are installed from any of them`,
|
|
9743
|
+
fix: "Run: pncli skills marketplace sync --marketplace all --all-agents"
|
|
9744
|
+
});
|
|
9745
|
+
}
|
|
9418
9746
|
return problems;
|
|
9419
9747
|
}
|
|
9420
9748
|
function registerDoctorCommands(program2) {
|
|
9421
|
-
program2.command("doctor").description("Diagnose pncli setup: config files, credentials, and
|
|
9749
|
+
program2.command("doctor").description("Diagnose pncli setup: config files, credentials, agent skills, and skills marketplaces").option("--offline", "Skip credential checks (no network calls)").action(async (cmdOpts) => {
|
|
9422
9750
|
const start = Date.now();
|
|
9423
9751
|
try {
|
|
9424
9752
|
const opts = program2.optsWithGlobals();
|
|
9425
9753
|
const globalFile = checkConfigFile(opts.config ?? getGlobalConfigPath());
|
|
9426
9754
|
const repoRoot = findGitRoot() ?? process.cwd();
|
|
9427
|
-
const repoFile = checkConfigFile(
|
|
9755
|
+
const repoFile = checkConfigFile(path9.join(repoRoot, ".pncli.json"));
|
|
9428
9756
|
let credentials = null;
|
|
9429
9757
|
let credentialsSkipped = cmdOpts.offline ? "--offline" : null;
|
|
9430
9758
|
if (!cmdOpts.offline) {
|
|
@@ -9442,9 +9770,16 @@ function registerDoctorCommands(program2) {
|
|
|
9442
9770
|
path: l.path,
|
|
9443
9771
|
exists: l.exists,
|
|
9444
9772
|
totalSkills: l.totalSkills,
|
|
9773
|
+
marketplaceSkills: l.marketplaceSkills,
|
|
9445
9774
|
staleSkills: l.exists ? findStaleBundledSkills(l.path) : []
|
|
9446
9775
|
}));
|
|
9447
|
-
const
|
|
9776
|
+
const marketplaces = getAllMarketplaces(globalConfig).map((m) => ({
|
|
9777
|
+
name: m.name ?? m.repoUrl ?? "(unnamed)",
|
|
9778
|
+
repoUrl: m.repoUrl ?? null,
|
|
9779
|
+
localPath: m.localPath ?? null,
|
|
9780
|
+
cloneExists: !!m.localPath && fs5.existsSync(m.localPath)
|
|
9781
|
+
}));
|
|
9782
|
+
const problems = buildProblems(globalFile, repoFile, credentials, skillLocations, marketplaces);
|
|
9448
9783
|
if (credentials) {
|
|
9449
9784
|
const statuses = Object.values(credentials).map((r) => r.status);
|
|
9450
9785
|
if (statuses.includes("invalid")) process.exitCode = ExitCode.AUTH_ERROR;
|
|
@@ -9463,6 +9798,10 @@ function registerDoctorCommands(program2) {
|
|
|
9463
9798
|
locations: skillLocations,
|
|
9464
9799
|
staleTotal: skillLocations.reduce((sum, l) => sum + l.staleSkills.length, 0)
|
|
9465
9800
|
},
|
|
9801
|
+
marketplaces: {
|
|
9802
|
+
registered: marketplaces,
|
|
9803
|
+
hint: marketplaces.length === 0 ? "No org marketplaces registered. Add one with: pncli skills marketplace add <git-url> --all-agents" : "Refresh with: pncli skills marketplace sync --marketplace all --all-agents"
|
|
9804
|
+
},
|
|
9466
9805
|
problems
|
|
9467
9806
|
}, "doctor", "check", start);
|
|
9468
9807
|
} catch (err) {
|
|
@@ -9852,8 +10191,8 @@ function registerAdoProjectCommands(ado) {
|
|
|
9852
10191
|
}
|
|
9853
10192
|
|
|
9854
10193
|
// src/services/ado/commands/work.ts
|
|
9855
|
-
import
|
|
9856
|
-
import
|
|
10194
|
+
import fs6 from "fs";
|
|
10195
|
+
import path10 from "path";
|
|
9857
10196
|
|
|
9858
10197
|
// src/services/ado/input-schema.ts
|
|
9859
10198
|
var ADO_WORK_INPUT_FILE_SCHEMA = {
|
|
@@ -10094,11 +10433,11 @@ function registerAdoWorkCommands(ado) {
|
|
|
10094
10433
|
const attachments = await workClient.listAttachments(collection, parseInt(opts.id, 10));
|
|
10095
10434
|
const attachment = attachments.find((a) => a.id === opts.attachmentId);
|
|
10096
10435
|
if (!attachment) throw new PncliError(`Attachment not found: ${opts.attachmentId}`, 1);
|
|
10097
|
-
const outDir = opts.dir ??
|
|
10098
|
-
|
|
10099
|
-
const outPath =
|
|
10436
|
+
const outDir = opts.dir ?? path10.join(process.cwd(), ".pncli");
|
|
10437
|
+
fs6.mkdirSync(outDir, { recursive: true });
|
|
10438
|
+
const outPath = path10.join(outDir, path10.basename(attachment.name));
|
|
10100
10439
|
const buffer = await workClient.downloadAttachment(attachment.url);
|
|
10101
|
-
|
|
10440
|
+
fs6.writeFileSync(outPath, buffer);
|
|
10102
10441
|
success({ saved: outPath, name: attachment.name, size: buffer.length }, "ado", "work-download-attachment", start);
|
|
10103
10442
|
} catch (err) {
|
|
10104
10443
|
fail(err, "ado", "work-download-attachment", start);
|
|
@@ -10132,10 +10471,10 @@ function registerAdoWorkCommands(ado) {
|
|
|
10132
10471
|
const types = await discoverTypes(workClient, collection, project);
|
|
10133
10472
|
if (opts.save) {
|
|
10134
10473
|
const globalConfigPath = getGlobalConfigPath(globalOpts.config);
|
|
10135
|
-
const
|
|
10136
|
-
const existing = JSON.parse(
|
|
10474
|
+
const fs9 = await import("fs");
|
|
10475
|
+
const existing = JSON.parse(fs9.readFileSync(globalConfigPath, "utf8") || "{}");
|
|
10137
10476
|
existing.ado = { ...existing.ado ?? {}, discoveredTypes: types };
|
|
10138
|
-
|
|
10477
|
+
fs9.writeFileSync(globalConfigPath, JSON.stringify(existing, null, 2) + "\n", "utf8");
|
|
10139
10478
|
warn(`Saved ${types.length} types to ${globalConfigPath}`);
|
|
10140
10479
|
}
|
|
10141
10480
|
success(types, "ado", "work-types", start);
|
|
@@ -10164,11 +10503,11 @@ function registerAdoWorkCommands(ado) {
|
|
|
10164
10503
|
) : allFields;
|
|
10165
10504
|
if (opts.save) {
|
|
10166
10505
|
const globalConfigPath = getGlobalConfigPath(globalOpts.config);
|
|
10167
|
-
const
|
|
10168
|
-
const existing = JSON.parse(
|
|
10506
|
+
const fs9 = await import("fs");
|
|
10507
|
+
const existing = JSON.parse(fs9.readFileSync(globalConfigPath, "utf8") || "{}");
|
|
10169
10508
|
const aliases = buildDefaultAliases(allFields);
|
|
10170
10509
|
existing.ado = { ...existing.ado ?? {}, discoveredFields: allFields, fieldAliases: aliases };
|
|
10171
|
-
|
|
10510
|
+
fs9.writeFileSync(globalConfigPath, JSON.stringify(existing, null, 2) + "\n", "utf8");
|
|
10172
10511
|
warn(`Saved ${allFields.length} fields and ${Object.keys(aliases).length} aliases to ${globalConfigPath}`);
|
|
10173
10512
|
}
|
|
10174
10513
|
success(fields, "ado", "work-fields", start);
|
|
@@ -10469,7 +10808,7 @@ function registerAdoRepoCommands(ado) {
|
|
|
10469
10808
|
const config = lc({ configPath: globalOpts.config });
|
|
10470
10809
|
const { collection, project, repo: repo2, gitClient } = getAdoContext(ado, true);
|
|
10471
10810
|
const { AdoCoreClient: AdoCoreClient2 } = await import("./core-SWYVXOQ4.js");
|
|
10472
|
-
const { createHttpClient: createHttpClient2 } = await import("./http-
|
|
10811
|
+
const { createHttpClient: createHttpClient2 } = await import("./http-V7PZD7LE.js");
|
|
10473
10812
|
const http = createHttpClient2(config, Boolean(globalOpts.dryRun));
|
|
10474
10813
|
const coreClient = new AdoCoreClient2(http);
|
|
10475
10814
|
const me = await coreClient.getConnectionData(collection);
|
|
@@ -10939,18 +11278,18 @@ var ArtifactoryClient = class {
|
|
|
10939
11278
|
}
|
|
10940
11279
|
});
|
|
10941
11280
|
}
|
|
10942
|
-
async getStorageInfo(repo,
|
|
10943
|
-
const cleanPath =
|
|
11281
|
+
async getStorageInfo(repo, path11) {
|
|
11282
|
+
const cleanPath = path11.startsWith("/") ? path11.slice(1) : path11;
|
|
10944
11283
|
return this.http.artifactory(`${API9}/storage/${repo}/${cleanPath}`);
|
|
10945
11284
|
}
|
|
10946
|
-
async getProperties(repo,
|
|
10947
|
-
const cleanPath =
|
|
11285
|
+
async getProperties(repo, path11) {
|
|
11286
|
+
const cleanPath = path11.startsWith("/") ? path11.slice(1) : path11;
|
|
10948
11287
|
return this.http.artifactory(`${API9}/storage/${repo}/${cleanPath}`, {
|
|
10949
11288
|
params: { properties: "" }
|
|
10950
11289
|
});
|
|
10951
11290
|
}
|
|
10952
|
-
async setProperties(repo,
|
|
10953
|
-
const cleanPath =
|
|
11291
|
+
async setProperties(repo, path11, properties, opts = {}) {
|
|
11292
|
+
const cleanPath = path11.startsWith("/") ? path11.slice(1) : path11;
|
|
10954
11293
|
const propString = Object.entries(properties).map(([k, v]) => `${k}=${v}`).join(";");
|
|
10955
11294
|
await this.http.artifactory(`${API9}/storage/${repo}/${cleanPath}`, {
|
|
10956
11295
|
method: "PUT",
|
|
@@ -11032,27 +11371,27 @@ function registerArtifactoryCommands(program2) {
|
|
|
11032
11371
|
fail(err, "artifactory", "repos", start);
|
|
11033
11372
|
}
|
|
11034
11373
|
});
|
|
11035
|
-
art.command("artifact-info <repo> <path>").description("Get metadata for an artifact or folder (checksums, size, timestamps)").action(async (repo,
|
|
11374
|
+
art.command("artifact-info <repo> <path>").description("Get metadata for an artifact or folder (checksums, size, timestamps)").action(async (repo, path11) => {
|
|
11036
11375
|
const start = Date.now();
|
|
11037
11376
|
try {
|
|
11038
11377
|
const client = getClient8(program2);
|
|
11039
|
-
const data = await client.getStorageInfo(repo,
|
|
11378
|
+
const data = await client.getStorageInfo(repo, path11);
|
|
11040
11379
|
success(data, "artifactory", "artifact-info", start);
|
|
11041
11380
|
} catch (err) {
|
|
11042
11381
|
fail(err, "artifactory", "artifact-info", start);
|
|
11043
11382
|
}
|
|
11044
11383
|
});
|
|
11045
|
-
art.command("properties-get <repo> <path>").description("Read custom properties on an artifact").action(async (repo,
|
|
11384
|
+
art.command("properties-get <repo> <path>").description("Read custom properties on an artifact").action(async (repo, path11) => {
|
|
11046
11385
|
const start = Date.now();
|
|
11047
11386
|
try {
|
|
11048
11387
|
const client = getClient8(program2);
|
|
11049
|
-
const data = await client.getProperties(repo,
|
|
11388
|
+
const data = await client.getProperties(repo, path11);
|
|
11050
11389
|
success(data, "artifactory", "properties-get", start);
|
|
11051
11390
|
} catch (err) {
|
|
11052
11391
|
fail(err, "artifactory", "properties-get", start);
|
|
11053
11392
|
}
|
|
11054
11393
|
});
|
|
11055
|
-
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,
|
|
11394
|
+
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, path11, keyvals, opts) => {
|
|
11056
11395
|
const start = Date.now();
|
|
11057
11396
|
try {
|
|
11058
11397
|
if (!keyvals.length) throw new PncliError("Provide at least one key=value pair");
|
|
@@ -11063,8 +11402,8 @@ function registerArtifactoryCommands(program2) {
|
|
|
11063
11402
|
properties[kv.slice(0, eq)] = kv.slice(eq + 1);
|
|
11064
11403
|
}
|
|
11065
11404
|
const client = getClient8(program2);
|
|
11066
|
-
await client.setProperties(repo,
|
|
11067
|
-
success({ repo, path:
|
|
11405
|
+
await client.setProperties(repo, path11, properties, { recursive: opts.recursive });
|
|
11406
|
+
success({ repo, path: path11, properties }, "artifactory", "properties-set", start);
|
|
11068
11407
|
} catch (err) {
|
|
11069
11408
|
fail(err, "artifactory", "properties-set", start);
|
|
11070
11409
|
}
|
|
@@ -11382,8 +11721,8 @@ function registerSonatypeIqCommands(program2) {
|
|
|
11382
11721
|
const startTime = Date.now();
|
|
11383
11722
|
try {
|
|
11384
11723
|
const http = getHttpClient(program2);
|
|
11385
|
-
const
|
|
11386
|
-
const data = await http.sonatypeiq(
|
|
11724
|
+
const path11 = opts.organizationId ? `/api/v2/policies/organization/${encodeURIComponent(opts.organizationId)}` : "/api/v2/policies";
|
|
11725
|
+
const data = await http.sonatypeiq(path11);
|
|
11387
11726
|
success(data, "sonatypeiq", "policies list", startTime);
|
|
11388
11727
|
} catch (err) {
|
|
11389
11728
|
fail(err, "sonatypeiq", "policies list", startTime);
|
|
@@ -12078,7 +12417,7 @@ function registerLogscaleCommands(program2) {
|
|
|
12078
12417
|
}
|
|
12079
12418
|
|
|
12080
12419
|
// src/services/splitio/commands.ts
|
|
12081
|
-
import
|
|
12420
|
+
import fs7 from "fs";
|
|
12082
12421
|
import confirm2 from "@inquirer/confirm";
|
|
12083
12422
|
function getHttp4(program2) {
|
|
12084
12423
|
const opts = program2.optsWithGlobals();
|
|
@@ -12090,9 +12429,9 @@ function getHttp4(program2) {
|
|
|
12090
12429
|
function loadInputFile(filePath) {
|
|
12091
12430
|
let raw;
|
|
12092
12431
|
if (filePath === "-") {
|
|
12093
|
-
raw =
|
|
12432
|
+
raw = fs7.readFileSync(process.stdin.fd, "utf8");
|
|
12094
12433
|
} else {
|
|
12095
|
-
raw =
|
|
12434
|
+
raw = fs7.readFileSync(filePath, "utf8");
|
|
12096
12435
|
}
|
|
12097
12436
|
try {
|
|
12098
12437
|
return JSON.parse(raw);
|
|
@@ -12627,7 +12966,7 @@ function registerFigmaCommands(program2) {
|
|
|
12627
12966
|
}
|
|
12628
12967
|
|
|
12629
12968
|
// src/services/alation/commands.ts
|
|
12630
|
-
import
|
|
12969
|
+
import fs8 from "fs";
|
|
12631
12970
|
var MAX_PAGE = 1e3;
|
|
12632
12971
|
function parseIntOption(value, flag) {
|
|
12633
12972
|
if (value === void 0) return void 0;
|
|
@@ -12740,8 +13079,8 @@ function getHttp6(program2) {
|
|
|
12740
13079
|
Boolean(opts.dryRun)
|
|
12741
13080
|
);
|
|
12742
13081
|
}
|
|
12743
|
-
async function getOne(http,
|
|
12744
|
-
const rows = await http.alation(
|
|
13082
|
+
async function getOne(http, path11, id, what) {
|
|
13083
|
+
const rows = await http.alation(path11, { params: { id, limit: 1 } });
|
|
12745
13084
|
const row = Array.isArray(rows) ? rows.find((r) => r.id === id) ?? rows[0] : void 0;
|
|
12746
13085
|
if (!row) throw new PncliError(`${what} ${id} not found`, 404);
|
|
12747
13086
|
return row;
|
|
@@ -12791,7 +13130,7 @@ function registerAlationCommands(program2) {
|
|
|
12791
13130
|
const opts = program2.optsWithGlobals();
|
|
12792
13131
|
const config = loadConfig({ configPath: opts.config });
|
|
12793
13132
|
if (opts.dryRun) {
|
|
12794
|
-
|
|
13133
|
+
fs8.writeSync(process.stderr.fd, `DRY RUN: POST ${config.alation.baseUrl ?? "<alation.baseUrl>"}/integration/v1/createAPIAccessToken/ (token exchange skipped)
|
|
12795
13134
|
`);
|
|
12796
13135
|
process.exitCode = ExitCode.SUCCESS;
|
|
12797
13136
|
throw new PncliError("dry-run", 0);
|
|
@@ -13035,7 +13374,7 @@ if (!process.env.PNCLI_VERIFY_TLS) {
|
|
|
13035
13374
|
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
|
13036
13375
|
}
|
|
13037
13376
|
await configureProxy();
|
|
13038
|
-
var TAGLINE = "
|
|
13377
|
+
var TAGLINE = "Connectivity without MCP.";
|
|
13039
13378
|
var program = new Command3();
|
|
13040
13379
|
program.name("pncli").description(`The Paperwork Nightmare CLI \u2014 ${TAGLINE}`).version(`${getPncliVersion()} \u2014 ${TAGLINE}`, "-v, --version").option("--pretty", "Human-readable formatted output", false).option("--verbose", "Include full response metadata", false).option("--debug", "Print diagnostic traces (method, URL, status) for all API calls \u2014 does not log credentials", false).option("--dry-run", "Print API requests without executing", false).option("--config <path>", "Override global config file location").option("--output-file <path>", "Write JSON output to file instead of stdout");
|
|
13041
13380
|
program.hook("preAction", (thisCommand) => {
|
|
@@ -13099,9 +13438,16 @@ Services:
|
|
|
13099
13438
|
figma Figma (files, comments, version history)
|
|
13100
13439
|
alation Alation (data sources, schemas, tables, columns, search, Document Hubs)
|
|
13101
13440
|
config Manage pncli configuration
|
|
13102
|
-
doctor Diagnose pncli setup (config files, credentials, agent skills)
|
|
13441
|
+
doctor Diagnose pncli setup (config files, credentials, agent skills, marketplaces)
|
|
13103
13442
|
skills Install and manage agent skills (Codex, GitHub Copilot, Claude Code)
|
|
13104
13443
|
jwt JWT token utilities (decode header and payload)
|
|
13444
|
+
|
|
13445
|
+
Agent skills and org plugins:
|
|
13446
|
+
pncli skills install --all-agents Bundled pncli skills into this repo, every agent host
|
|
13447
|
+
pncli skills marketplace add <git-url> --all-agents Org plugins (skills + AGENTS.md/CLAUDE.md) from a git marketplace
|
|
13448
|
+
pncli skills marketplace sync --marketplace all Pull and refresh everything installed from marketplaces
|
|
13449
|
+
pncli skills marketplace manage Interactive: toggle plugins, add/remove marketplaces
|
|
13450
|
+
pncli skills --help Every install location and command
|
|
13105
13451
|
`);
|
|
13106
13452
|
program.parseAsync(process.argv).catch((err) => {
|
|
13107
13453
|
if (process.exitCode !== void 0) return;
|