@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/dist/cli.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  import {
15
15
  createHttpClient,
16
16
  validateAlationAccessToken
17
- } from "./chunk-FAMJPKO4.js";
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 path10 = remoteUrl;
450
+ let path11 = remoteUrl;
451
451
  let remoteHostname = "";
452
- const sshProtoMatch = path10.match(/^ssh:\/\/[^@]*@?([^/:]+)(?::\d+)?(.*)/);
452
+ const sshProtoMatch = path11.match(/^ssh:\/\/[^@]*@?([^/:]+)(?::\d+)?(.*)/);
453
453
  if (sshProtoMatch) {
454
454
  remoteHostname = sshProtoMatch[1].toLowerCase();
455
- path10 = sshProtoMatch[2];
455
+ path11 = sshProtoMatch[2];
456
456
  } else {
457
- const gitAtMatch = path10.match(/^git@([^:]+)(?::\d+)?:(.*)/);
457
+ const gitAtMatch = path11.match(/^git@([^:]+)(?::\d+)?:(.*)/);
458
458
  if (gitAtMatch) {
459
459
  remoteHostname = gitAtMatch[1].toLowerCase();
460
- path10 = "/" + gitAtMatch[2];
460
+ path11 = "/" + gitAtMatch[2];
461
461
  } else {
462
- const httpsMatch = path10.match(/^https?:\/\/([^/:]+)(?::\d+)?(.*)/);
462
+ const httpsMatch = path11.match(/^https?:\/\/([^/:]+)(?::\d+)?(.*)/);
463
463
  if (httpsMatch) {
464
464
  remoteHostname = httpsMatch[1].toLowerCase();
465
- path10 = httpsMatch[2];
465
+ path11 = httpsMatch[2];
466
466
  }
467
467
  }
468
468
  }
469
469
  if (!remoteHostname || remoteHostname !== baseHostname) return null;
470
- path10 = path10.replace(/\.git$/, "");
471
- const gitIdx = path10.indexOf("/_git/");
472
- const sshIdx = path10.indexOf("/_ssh/");
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(path10.slice(markerIdx + 6));
475
+ const repo = decodeURIComponent(path11.slice(markerIdx + 6));
476
476
  if (!repo) return null;
477
- const before = path10.slice(1, markerIdx);
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.version ?? "0", 10));
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.version ?? "0", 10));
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 path10 = opts.org ? `/orgs/${opts.org}/repos` : "/user/repos";
2310
- return this.http.github(path10, {
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 path10 = collection ? `/${encodeURIComponent(collection)}/_apis/connectionData?api-version=7.1-preview.1` : "/_apis/projectCollections?api-version=7.1";
6276
- await http.ado(path10, { timeoutMs: 1e4 });
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 fs2 from "fs";
6515
- import path7 from "path";
6516
- import os from "os";
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 path7.join(os.homedir(), ".agents", "marketplaces", repoNameFromUrl(url));
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: path7.join(os.homedir(), ".agents/skills") },
6623
- "github-copilot": { project: ".github/skills", user: path7.join(os.homedir(), ".copilot/skills") },
6624
- "claude-code": { project: ".claude/skills", user: path7.join(os.homedir(), ".claude/skills") }
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 ? path7.resolve(root) : null;
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 ? path7.resolve(root, relativePath) : path7.resolve(relativePath);
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) : path7.resolve(agentPaths.user);
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 (fs2.existsSync(path7.join(resolveScopedPath(agentPaths, scope), "pncli", "SKILL.md"))) {
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 path7.resolve(opts.target);
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 path7.resolve(path7.dirname(thisFile), "..", "skills");
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 path7.join(targetDir, ".pncli-installed.json");
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 path7.join(skillDir, SKILL_ORIGIN_FILENAME);
6875
+ return path8.join(skillDir, SKILL_ORIGIN_FILENAME);
6702
6876
  }
6703
6877
  function readSkillOrigin(skillDir) {
6704
6878
  const originPath = getSkillOriginPath(skillDir);
6705
- if (!fs2.existsSync(originPath)) return null;
6879
+ if (!fs3.existsSync(originPath)) return null;
6706
6880
  try {
6707
- const raw = JSON.parse(fs2.readFileSync(originPath, "utf8"));
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(path7.join(targetDir, skillName));
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 (!fs2.existsSync(targetDir)) return [];
6733
- return fs2.readdirSync(targetDir).filter((name) => {
6906
+ if (!fs3.existsSync(targetDir)) return [];
6907
+ return fs3.readdirSync(targetDir).filter((name) => {
6734
6908
  if (name.startsWith(".")) return false;
6735
- const dir = path7.join(targetDir, name);
6909
+ const dir = path8.join(targetDir, name);
6736
6910
  try {
6737
- if (!fs2.statSync(dir).isDirectory()) return false;
6911
+ if (!fs3.statSync(dir).isDirectory()) return false;
6738
6912
  } catch {
6739
6913
  return false;
6740
6914
  }
6741
- return fs2.existsSync(path7.join(dir, "SKILL.md"));
6915
+ return fs3.existsSync(path8.join(dir, "SKILL.md"));
6742
6916
  });
6743
6917
  }
6744
6918
  function disablePluginSkills(targetDir, plugin, marketplaceFilter) {
6745
- const resolvedTarget = path7.resolve(targetDir);
6746
- const stashDir = path7.join(targetDir, DISABLED_SUBDIR);
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 = path7.resolve(targetDir, skillName);
6753
- if (!skillDir.startsWith(resolvedTarget + path7.sep)) continue;
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 = path7.join(stashDir, skillName);
6764
- fs2.mkdirSync(stashDir, { recursive: true });
6765
- if (fs2.existsSync(stashDest)) fs2.rmSync(stashDest, { recursive: true, force: true });
6766
- fs2.renameSync(skillDir, stashDest);
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
- fs2.writeFileSync(getInstalledMetaPath(targetDir), JSON.stringify(meta, null, 2), "utf8");
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 = path7.resolve(targetDir);
6784
- const stashDir = path7.join(targetDir, DISABLED_SUBDIR);
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 = path7.resolve(targetDir, skillName);
6796
- if (!dest.startsWith(resolvedTarget + path7.sep)) {
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 = path7.join(stashDir, skillName);
6801
- if (!fs2.existsSync(stashSrc)) {
6974
+ const stashSrc = path8.join(stashDir, skillName);
6975
+ if (!fs3.existsSync(stashSrc)) {
6802
6976
  stashMissing.push(skillName);
6803
6977
  continue;
6804
6978
  }
6805
- if (fs2.existsSync(dest)) fs2.rmSync(dest, { recursive: true, force: true });
6806
- fs2.renameSync(stashSrc, dest);
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 (fs2.existsSync(stashDir) && fs2.readdirSync(stashDir).length === 0) {
6812
- fs2.rmdirSync(stashDir);
6985
+ if (fs3.existsSync(stashDir) && fs3.readdirSync(stashDir).length === 0) {
6986
+ fs3.rmdirSync(stashDir);
6813
6987
  }
6814
6988
  if (enabled.length > 0) {
6815
- fs2.writeFileSync(getInstalledMetaPath(targetDir), JSON.stringify(meta, null, 2), "utf8");
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) => path7.resolve(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 = path7.resolve(targetDir);
6885
- const builtIn = Object.values(AGENT_PATHS).flatMap((a) => [path7.resolve(a.user), resolveProjectPath(a.project)]);
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 = path7.resolve(targetDir);
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 = path7.resolve(targetDir);
7076
+ const resolved = path8.resolve(targetDir);
6903
7077
  const base = {
6904
7078
  agent,
6905
7079
  scope,
6906
7080
  path: resolved,
6907
- exists: fs2.existsSync(resolved),
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 = path7.join(resolved, DISABLED_SUBDIR);
6927
- base.disabledStashMissing = disabledNames.filter((name) => !fs2.existsSync(path7.join(stashDir, 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 || !fs2.existsSync(localPath)) return null;
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 ? path7.join(location.path, skillName) : path7.join(location.path, DISABLED_SUBDIR, skillName),
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 = path7.join(targetDir, skillName);
6999
- if (fs2.existsSync(skillDir)) {
7172
+ const skillDir = path8.join(targetDir, skillName);
7173
+ if (fs3.existsSync(skillDir)) {
7000
7174
  const origin = { version: 1, ...fullRecord };
7001
7175
  try {
7002
- fs2.writeFileSync(getSkillOriginPath(skillDir), JSON.stringify(origin, null, 2), "utf8");
7176
+ fs3.writeFileSync(getSkillOriginPath(skillDir), JSON.stringify(origin, null, 2), "utf8");
7003
7177
  } catch {
7004
7178
  }
7005
7179
  }
7006
7180
  }
7007
- fs2.writeFileSync(getInstalledMetaPath(targetDir), JSON.stringify(meta, null, 2), "utf8");
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 = fs2.existsSync(path7.join(resolvedPath, ".git"));
7084
- if (fs2.existsSync(resolvedPath) && !hasGit && fs2.readdirSync(resolvedPath).length > 0) {
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 (!fs2.existsSync(skillsSrc)) {
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
- warn(` ${skill}: ${path7.join(skillsSrc, skill)} \u2192 ${path7.join(targetDir, skill)}`);
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 installAllPlugins(resolvedPath, marketplaceName, url, targetDir) {
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 { pluginResults: {}, totalInstalled: 0 };
7403
+ return targets.map((t) => ({ ...t, plugins: {}, total: 0 }));
7168
7404
  }
7169
- warn(`Installing ${pluginChoices.length} plugin(s) to ${targetDir}...`);
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, targetDir, force, pluginFilter, installedOnly = false) {
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 || !fs2.existsSync(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
- let pluginNames;
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 { results, totalInstalled } = installPluginsForMarketplace(marketplacePath, marketplaceName, m.repoUrl, pluginNames, targetDir);
7209
- return { marketplace: marketplaceName, plugins: results, total: totalInstalled, marketplaceUpdated: updated };
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 = path7.resolve(localPath ?? defaultMarketplacePath(url));
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 targetDir = resolveAgentPaths(resolveAgentName(opts)).user;
7231
- const { pluginResults, totalInstalled } = installAllPlugins(resolvedPath, marketplaceName, url, targetDir);
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
- plugins: pluginResults,
7239
- total: totalInstalled,
7240
- target: targetDir
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 = fs2.readdirSync(bundledDir).filter(
7275
- (name) => fs2.existsSync(path7.join(bundledDir, name, "SKILL.md"))
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 = path7.resolve(targetDir);
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 = path7.resolve(targetDir, skillName);
7292
- if (!existingDir.startsWith(resolvedTarget + path7.sep)) continue;
7293
- if (fs2.existsSync(existingDir)) {
7294
- fs2.rmSync(existingDir, { recursive: true, force: true });
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
- warn(`Installing ${skillDirs.length} bundled skill(s) to ${targetDir}...`);
7516
+ log(`Installing ${skillDirs.length} bundled skill(s) to ${targetDir}...`);
7298
7517
  for (const skillName of skillDirs) {
7299
- const skillDir = path7.resolve(targetDir, skillName);
7300
- if (!skillDir.startsWith(resolvedTarget + path7.sep)) {
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 = fs2.readdirSync(path7.join(bundledDir, skillName)).filter((f) => f.endsWith(".md"));
7306
- fs2.mkdirSync(skillDir, { recursive: true });
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 = fs2.readFileSync(path7.join(bundledDir, skillName, mdFile), "utf8");
7309
- fs2.writeFileSync(path7.join(skillDir, mdFile), content, "utf8");
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(`Manage agent skills (${AGENT_CHOICES})`);
7325
- skills.command("install").description("Install pncli skills into the current 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) => {
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 = Object.entries(AGENT_PATHS).map(([agent, agentPaths]) => {
7336
- const targetDir2 = resolveScopedPath(agentPaths, scope);
7337
- const { installed: installed2, failed: failed2 } = installBundledSkillsTo(targetDir2, skillDirs);
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 ? path7.resolve(opts.target) : resolveScopedPath(resolveAgentPaths(agentName), scope);
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 = path7.resolve(opts.target);
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 (!fs2.existsSync(targetDir)) {
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 = fs2.readdirSync(targetDir).filter((name) => {
7632
+ const skillDirs = fs3.readdirSync(targetDir).filter((name) => {
7395
7633
  if (name.startsWith(".")) return false;
7396
- const skillPath = path7.join(targetDir, name, "SKILL.md");
7397
- return fs2.existsSync(skillPath);
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 = fs2.readFileSync(path7.join(targetDir, name, "SKILL.md"), "utf8");
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 = path7.resolve(opts.target);
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 = path7.resolve(targetDir);
7459
- const skillDir = path7.resolve(targetDir, name);
7460
- if (!skillDir.startsWith(resolvedTarget + path7.sep)) {
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 (!fs2.existsSync(skillDir)) {
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
- fs2.rmSync(skillDir, { recursive: true, force: true });
7706
+ fs3.rmSync(skillDir, { recursive: true, force: true });
7469
7707
  delete meta.skills[name];
7470
- fs2.writeFileSync(getInstalledMetaPath(targetDir), JSON.stringify(meta, null, 2), "utf8");
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 = path7.resolve(dir);
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("Manage git-hosted skills marketplaces");
7556
- function withMarketplaceAddOptions(cmd) {
7557
- return cmd.argument("<url>", "Git clone URL of the marketplace repository").argument("[localPath]", "Local directory to clone into (default: ~/.agents/marketplaces/<repo-name>)").option("--name <name>", "Human-readable name for this marketplace (default: derived from URL)").option("--branch <branch>", "Branch to clone (default: remote HEAD)").option("--token <token>", "HTTP access token for authenticated clone and pull (GitHub PAT or Bitbucket token)").option("--agent <agent>", `Target agent host for plugin install: ${AGENT_CHOICES} (default: ${DEFAULT_AGENT})`).option("--claude", "Shorthand for --agent claude-code");
7558
- }
7559
- withMarketplaceAddOptions(
7560
- marketplace.command("add").description("Register a new marketplace, clone it, and install all its plugins")
7561
- ).action((url, localPath, opts) => marketplaceAddAction(url, localPath, opts, "marketplace-add"));
7562
- withMarketplaceAddOptions(
7563
- marketplace.command("setup").description("Alias for `marketplace add` \u2014 clone a marketplace and install all its plugins")
7564
- ).action((url, localPath, opts) => marketplaceAddAction(url, localPath, opts, "marketplace-setup"));
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 && fs2.existsSync(m.localPath) ? getRepoRemoteUrl(m.localPath) : null
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 || !fs2.existsSync(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 targetDir = resolveAgentPaths(resolveAgentName(opts)).user;
7623
- const force = opts.force ?? false;
7624
- const installedOnly = opts.installedOnly ?? false;
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, targetDir, force, plugin ?? "all", installedOnly));
7627
- success({ allMarketplaces: true, marketplaces: results, target: targetDir }, "skills", "marketplace-sync", start);
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((m, i) => ({ value: String(i), name: `${marketplaceLabel(m)} \u2014 ${m.repoUrl ?? ""}` }))
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 results2 = allMarketplaces.map((m) => syncMarketplacePlugins(m, targetDir, force, selectedPlugin ?? "all", installedOnly));
7655
- success({ allMarketplaces: true, marketplaces: results2, target: targetDir }, "skills", "marketplace-sync", start);
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 || !fs2.existsSync(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
- if (!updated && !force) {
7692
- success({
7693
- marketplace: marketplaceName,
7694
- marketplaceUpdated: false,
7695
- updated: false,
7696
- skipped: true,
7697
- message: `No changes detected in "${marketplaceName}" \u2014 skipping install. Use --force to reinstall anyway.`
7698
- }, "skills", "marketplace-sync", start);
7699
- return;
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
- let pluginNames;
7702
- if (selectedPlugin === "all") {
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 { results, totalInstalled } = installPluginsForMarketplace(marketplacePath, marketplaceName, selectedMarketplace.repoUrl, pluginNames, targetDir);
7722
- if (selectedPlugin === "all") {
7723
- success({ marketplace: marketplaceName, plugins: results, total: totalInstalled, target: targetDir, marketplaceUpdated: updated, ...installedOnly ? { installedOnly: true } : {} }, "skills", "marketplace-sync", start);
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-sync", start);
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 = path7.resolve(opts.target);
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 = path7.resolve(targetDir);
7753
- if (!fs2.existsSync(targetDir)) {
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 = fs2.readdirSync(targetDir).filter((name) => {
8049
+ const skillDirs = fs3.readdirSync(targetDir).filter((name) => {
7761
8050
  if (name.startsWith(".")) return false;
7762
- const p = path7.join(targetDir, name);
8051
+ const p = path8.join(targetDir, name);
7763
8052
  try {
7764
- return fs2.statSync(p).isDirectory();
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 = path7.resolve(targetDir, skillName);
7771
- if (!skillDir.startsWith(resolvedTarget + path7.sep)) continue;
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
- fs2.rmSync(skillDir, { recursive: true, force: true });
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
- fs2.writeFileSync(getInstalledMetaPath(targetDir), JSON.stringify(meta, null, 2), "utf8");
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 (!fs2.existsSync(targetDir)) {
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: path7.join(targetDir, DISABLED_SUBDIR),
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 (!fs2.existsSync(targetDir)) {
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 = fs2.existsSync(targetDir) ? listPluginStates(targetDir) : [];
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 (!fs2.existsSync(userDir)) {
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 = fs2.readdirSync(userDir).filter((name) => {
8290
+ const skillDirs = fs3.readdirSync(userDir).filter((name) => {
7979
8291
  if (name.startsWith(".")) return false;
7980
- const p = path7.join(userDir, name);
8292
+ const p = path8.join(userDir, name);
7981
8293
  try {
7982
- return fs2.statSync(p).isDirectory();
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 = path7.resolve(userDir, name);
7999
- if (!skillDir.startsWith(path7.resolve(userDir) + path7.sep)) continue;
8000
- fs2.rmSync(skillDir, { recursive: true, force: true });
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 (fs2.existsSync(metaPath)) {
8004
- fs2.writeFileSync(metaPath, JSON.stringify({ version: 1, skills: {} }, null, 2), "utf8");
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 = path7.join(marketplacePath, ".claude-plugin", "marketplace.json");
8014
- if (fs2.existsSync(marketplaceJsonPath)) {
8325
+ const marketplaceJsonPath = path8.join(marketplacePath, ".claude-plugin", "marketplace.json");
8326
+ if (fs3.existsSync(marketplaceJsonPath)) {
8015
8327
  try {
8016
- const meta = JSON.parse(fs2.readFileSync(marketplaceJsonPath, "utf8"));
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 = path7.join(marketplacePath, "plugins");
8024
- if (fs2.existsSync(pluginsDir)) {
8025
- return fs2.readdirSync(pluginsDir).filter((name) => fs2.statSync(path7.join(pluginsDir, name)).isDirectory()).map((name) => ({ name, description: "" }));
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 = path7.resolve(marketplacePath, "plugins");
8031
- const skillsSrc = path7.resolve(pluginsBase, selectedPlugin, "skills");
8032
- if (!skillsSrc.startsWith(pluginsBase + path7.sep)) {
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
- fs2.mkdirSync(targetDir, { recursive: true });
8039
- const resolvedTarget = path7.resolve(targetDir);
8040
- const skillNames = fs2.readdirSync(skillsSrc).filter(
8041
- (name) => fs2.statSync(path7.join(skillsSrc, name)).isDirectory()
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 = path7.resolve(targetDir, skillName);
8047
- if (!dest.startsWith(resolvedTarget + path7.sep)) {
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
- fs2.rmSync(dest, { recursive: true, force: true });
8052
- fs2.cpSync(path7.join(skillsSrc, skillName), dest, { recursive: true });
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 fs3 from "fs";
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 path10 = collection ? `/${encodeURIComponent(collection)}/_apis/connectionData?api-version=7.1-preview.1` : "/_apis/projectCollections?api-version=7.1";
8184
- await http.ado(path10);
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) => /^\d+$/.test(v.trim()) || "Must be a positive integer"
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 (fs3.existsSync(".pncli.json")) {
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 fs4 from "fs";
9350
- import path8 from "path";
9661
+ import fs5 from "fs";
9662
+ import path9 from "path";
9351
9663
  function checkConfigFile(filePath) {
9352
- if (!fs4.existsSync(filePath)) {
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(fs4.readFileSync(filePath, "utf8"));
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 agent skills").option("--offline", "Skip credential checks (no network calls)").action(async (cmdOpts) => {
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(path8.join(repoRoot, ".pncli.json"));
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 problems = buildProblems(globalFile, repoFile, credentials, skillLocations);
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 fs5 from "fs";
9856
- import path9 from "path";
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 ?? path9.join(process.cwd(), ".pncli");
10098
- fs5.mkdirSync(outDir, { recursive: true });
10099
- const outPath = path9.join(outDir, path9.basename(attachment.name));
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
- fs5.writeFileSync(outPath, buffer);
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 fs8 = await import("fs");
10136
- const existing = JSON.parse(fs8.readFileSync(globalConfigPath, "utf8") || "{}");
10474
+ const fs9 = await import("fs");
10475
+ const existing = JSON.parse(fs9.readFileSync(globalConfigPath, "utf8") || "{}");
10137
10476
  existing.ado = { ...existing.ado ?? {}, discoveredTypes: types };
10138
- fs8.writeFileSync(globalConfigPath, JSON.stringify(existing, null, 2) + "\n", "utf8");
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 fs8 = await import("fs");
10168
- const existing = JSON.parse(fs8.readFileSync(globalConfigPath, "utf8") || "{}");
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
- fs8.writeFileSync(globalConfigPath, JSON.stringify(existing, null, 2) + "\n", "utf8");
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-TAINU2LF.js");
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, path10) {
10943
- const cleanPath = path10.startsWith("/") ? path10.slice(1) : path10;
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, path10) {
10947
- const cleanPath = path10.startsWith("/") ? path10.slice(1) : path10;
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, path10, properties, opts = {}) {
10953
- const cleanPath = path10.startsWith("/") ? path10.slice(1) : path10;
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, path10) => {
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, path10);
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, path10) => {
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, path10);
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, path10, keyvals, opts) => {
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, path10, properties, { recursive: opts.recursive });
11067
- success({ repo, path: path10, properties }, "artifactory", "properties-set", start);
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 path10 = opts.organizationId ? `/api/v2/policies/organization/${encodeURIComponent(opts.organizationId)}` : "/api/v2/policies";
11386
- const data = await http.sonatypeiq(path10);
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 fs6 from "fs";
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 = fs6.readFileSync(process.stdin.fd, "utf8");
12432
+ raw = fs7.readFileSync(process.stdin.fd, "utf8");
12094
12433
  } else {
12095
- raw = fs6.readFileSync(filePath, "utf8");
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 fs7 from "fs";
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, path10, id, what) {
12744
- const rows = await http.alation(path10, { params: { id, limit: 1 } });
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
- fs7.writeSync(process.stderr.fd, `DRY RUN: POST ${config.alation.baseUrl ?? "<alation.baseUrl>"}/integration/v1/createAPIAccessToken/ (token exchange skipped)
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 = "One command does what three meetings couldn't.";
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;