skillops-cli 1.3.5 → 1.3.7

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
@@ -10,7 +10,7 @@ import YAML6 from "yaml";
10
10
  import fs from "fs";
11
11
  import YAML from "yaml";
12
12
  import { z } from "zod";
13
- var SKILLOPS_VERSION = "1.3.5";
13
+ var SKILLOPS_VERSION = "1.3.7";
14
14
  var SKILL_NAME_REGEX = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
15
15
  var SEMVER_REGEX = /^\d+\.\d+\.\d+$/;
16
16
  var FRONTMATTER_REGEX = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
@@ -4925,8 +4925,8 @@ var COMMAND_HELP_SPECS = [
4925
4925
  name: "remove",
4926
4926
  aliases: ["uninstall", "rm"],
4927
4927
  pillar: "library",
4928
- summary: "Remove a Skill/Subagent from harnesses",
4929
- usage: "npx skillops-cli remove <name...> [options]",
4928
+ summary: "Remove imported Skills/Subagents from harnesses",
4929
+ usage: "npx skillops-cli remove [name...] [options]",
4930
4930
  options: [
4931
4931
  {
4932
4932
  flags: "-p, --project <dir>",
@@ -4936,12 +4936,20 @@ var COMMAND_HELP_SPECS = [
4936
4936
  flags: "-t, --harness <list>",
4937
4937
  description: "Restrict removal to specific harnesses"
4938
4938
  },
4939
+ {
4940
+ flags: "--all",
4941
+ description: "Remove all imported Skills & Agents from project"
4942
+ },
4939
4943
  {
4940
4944
  flags: "--force",
4941
4945
  description: "Remove even if SHA-256 drift is detected"
4942
4946
  }
4943
4947
  ],
4944
4948
  examples: [
4949
+ {
4950
+ command: "npx skillops-cli remove",
4951
+ description: "Interactive selector of installed Skills & Agents"
4952
+ },
4945
4953
  {
4946
4954
  command: "npx skillops-cli uninstall mcp-architect",
4947
4955
  description: "Remove runtime files & update lockfile"
@@ -5331,24 +5339,20 @@ function renderGlobalHelp(version, colorEnabled) {
5331
5339
  desc: "Import Skills or Agents (interactive if no name given)"
5332
5340
  },
5333
5341
  {
5334
- cmd: "search <query>",
5335
- desc: "Search Skills & Agents by keyword, stack, or domain"
5342
+ cmd: "remove [name...]",
5343
+ desc: "Remove Skills or Agents (interactive if no name given)"
5336
5344
  },
5337
5345
  {
5338
5346
  cmd: "list [query]",
5339
5347
  desc: "List or filter available Skills & Agents in catalog"
5340
5348
  },
5341
5349
  {
5342
- cmd: "info <name>",
5343
- desc: "View details and how to use a Skill or Agent"
5344
- },
5345
- {
5346
- cmd: "remove <name>",
5347
- desc: "Remove an imported Skill or Agent from your project"
5350
+ cmd: "search <query>",
5351
+ desc: "Search Skills & Agents by keyword, stack, or domain"
5348
5352
  },
5349
5353
  {
5350
- cmd: "init",
5351
- desc: "Auto-detect stack & IDE and import recommended bundle"
5354
+ cmd: "info <name>",
5355
+ desc: "View details and how to use a Skill or Agent"
5352
5356
  }
5353
5357
  ];
5354
5358
  const cmdRows = coreCmds.map(
@@ -5360,12 +5364,12 @@ function renderGlobalHelp(version, colorEnabled) {
5360
5364
  renderSectionHeader("Commands", c),
5361
5365
  cmdRows,
5362
5366
  renderSectionHeader("Options", c),
5363
- `${bar} ${padVisible(style.cyan("-t, --target <ide>", c), 22, "left")}AI IDE (cursor, claude, copilot, windsurf, auto)`,
5367
+ `${bar} ${padVisible(style.cyan("-t, --target <ide>", c), 22, "left")}AI IDE (cursor, claude, copilot, antigravity...)`,
5364
5368
  `${bar} ${padVisible(style.cyan("-p, --project <dir>", c), 22, "left")}Project folder (default: current directory)`,
5365
- `${bar} ${padVisible(style.cyan("--all", c), 22, "left")}Import all 10 Skills & 9 Agents`,
5369
+ `${bar} ${padVisible(style.cyan("--all", c), 22, "left")}Import or remove all Skills & Agents`,
5366
5370
  renderSectionHeader("Examples", c),
5367
- `${bar} ${style.dim("$", c)} ${style.green("skillops add", c)} ${style.dim("\u2014 Interactive selector", c)}`,
5368
- `${bar} ${style.dim("$", c)} ${style.green("skillops add bug-reproducer-tdd red-team-breaker", c)} ${style.dim("\u2014 Direct import", c)}`,
5371
+ `${bar} ${style.dim("$", c)} ${style.green("skillops add", c)} ${style.dim("\u2014 Interactive import", c)}`,
5372
+ `${bar} ${style.dim("$", c)} ${style.green("skillops remove", c)} ${style.dim("\u2014 Interactive removal", c)}`,
5369
5373
  `${bar} ${style.dim("$", c)} ${style.green("skillops list", c)} ${style.dim("\u2014 Browse catalog", c)}`,
5370
5374
  bar,
5371
5375
  `${style.dim("\u2570\u2500", c)} ${style.dim("Run", c)} ${style.cyan("skillops <cmd> -h", c)} ${style.dim("for command options.", c)}`
@@ -5408,6 +5412,9 @@ function optionMatchesQuery(opt, rawQuery) {
5408
5412
  if (!q) {
5409
5413
  return true;
5410
5414
  }
5415
+ if (opt.isSelectAll) {
5416
+ return false;
5417
+ }
5411
5418
  return opt.label.toLowerCase().includes(q) || opt.value.toLowerCase().includes(q) || (opt.hint ?? "").toLowerCase().includes(q);
5412
5419
  }
5413
5420
  function getFilteredTabOptions(state, tab = state.activeTab) {
@@ -5415,16 +5422,47 @@ function getFilteredTabOptions(state, tab = state.activeTab) {
5415
5422
  (opt) => optionMatchesTab(opt, tab) && optionMatchesQuery(opt, state.searchQuery)
5416
5423
  );
5417
5424
  }
5418
- function createMultiSelectState(options) {
5425
+ function createMultiSelectState(options, stateOptions = {}) {
5426
+ let finalOptions = options;
5427
+ if (stateOptions.showSelectAll && !options.some((opt) => opt.isSelectAll === true)) {
5428
+ const skillItems = options.filter((opt) => optionMatchesTab(opt, "skill"));
5429
+ const agentItems = options.filter((opt) => optionMatchesTab(opt, "agent"));
5430
+ const withAll = [];
5431
+ if (skillItems.length > 0) {
5432
+ withAll.push({
5433
+ value: "__all_skills__",
5434
+ label: "[ALL] Select all Skills",
5435
+ kind: "skill",
5436
+ tab: "skill",
5437
+ isSelectAll: true,
5438
+ hint: `Select or deselect all ${skillItems.length} Skills in this tab`
5439
+ });
5440
+ withAll.push(...skillItems);
5441
+ }
5442
+ if (agentItems.length > 0) {
5443
+ withAll.push({
5444
+ value: "__all_agents__",
5445
+ label: "[ALL] Select all Agents",
5446
+ kind: "agent",
5447
+ tab: "agent",
5448
+ isSelectAll: true,
5449
+ hint: `Select or deselect all ${agentItems.length} Agents in this tab`
5450
+ });
5451
+ withAll.push(...agentItems);
5452
+ }
5453
+ if (withAll.length > 0) {
5454
+ finalOptions = withAll;
5455
+ }
5456
+ }
5419
5457
  const initial = /* @__PURE__ */ new Set();
5420
- for (const opt of options) {
5421
- if (opt.initialSelected) {
5458
+ for (const opt of finalOptions) {
5459
+ if (opt.initialSelected && !opt.isSelectAll) {
5422
5460
  initial.add(opt.value);
5423
5461
  }
5424
5462
  }
5425
- const hasSkills = options.some((opt) => optionMatchesTab(opt, "skill"));
5463
+ const hasSkills = finalOptions.some((opt) => optionMatchesTab(opt, "skill"));
5426
5464
  return {
5427
- options,
5465
+ options: finalOptions,
5428
5466
  activeTab: hasSkills ? "skill" : "agent",
5429
5467
  cursor: 0,
5430
5468
  selected: initial,
@@ -5514,6 +5552,29 @@ function applyMultiSelectKey(state, key, options = {}) {
5514
5552
  if (!currentOpt) {
5515
5553
  return state;
5516
5554
  }
5555
+ if (currentOpt.isSelectAll) {
5556
+ const realVisible = visible.filter((opt) => !opt.isSelectAll);
5557
+ if (realVisible.length === 0) {
5558
+ return state;
5559
+ }
5560
+ const allTabSelected = realVisible.every(
5561
+ (opt) => state.selected.has(opt.value)
5562
+ );
5563
+ const nextSelected2 = new Set(state.selected);
5564
+ if (allTabSelected) {
5565
+ for (const opt of realVisible) {
5566
+ nextSelected2.delete(opt.value);
5567
+ }
5568
+ } else {
5569
+ for (const opt of realVisible) {
5570
+ nextSelected2.add(opt.value);
5571
+ }
5572
+ }
5573
+ return {
5574
+ ...state,
5575
+ selected: nextSelected2
5576
+ };
5577
+ }
5517
5578
  const nextSelected = new Set(state.selected);
5518
5579
  if (nextSelected.has(currentOpt.value)) {
5519
5580
  nextSelected.delete(currentOpt.value);
@@ -5577,19 +5638,20 @@ function applyMultiSelectKey(state, key, options = {}) {
5577
5638
  };
5578
5639
  }
5579
5640
  if (key === "a" || key === "A") {
5580
- if (visible.length === 0) {
5641
+ const realVisible = visible.filter((opt) => !opt.isSelectAll);
5642
+ if (realVisible.length === 0) {
5581
5643
  return state;
5582
5644
  }
5583
- const allTabSelected = visible.every(
5645
+ const allTabSelected = realVisible.every(
5584
5646
  (opt) => state.selected.has(opt.value)
5585
5647
  );
5586
5648
  const nextSelected = new Set(state.selected);
5587
5649
  if (allTabSelected) {
5588
- for (const opt of visible) {
5650
+ for (const opt of realVisible) {
5589
5651
  nextSelected.delete(opt.value);
5590
5652
  }
5591
5653
  } else {
5592
- for (const opt of visible) {
5654
+ for (const opt of realVisible) {
5593
5655
  nextSelected.add(opt.value);
5594
5656
  }
5595
5657
  }
@@ -5604,20 +5666,24 @@ function renderMultiSelectFrame(title, state, options = {}) {
5604
5666
  const c = options.colorEnabled;
5605
5667
  const bar = style.dim("\u2502", c);
5606
5668
  const maxVisible = options.maxVisible ?? 10;
5607
- const totalSkills = state.options.filter(
5669
+ const realOptions = state.options.filter((o) => !o.isSelectAll);
5670
+ const totalSkills = realOptions.filter(
5608
5671
  (o) => optionMatchesTab(o, "skill")
5609
5672
  ).length;
5610
- const totalAgents = state.options.filter(
5673
+ const totalAgents = realOptions.filter(
5611
5674
  (o) => optionMatchesTab(o, "agent")
5612
5675
  ).length;
5613
5676
  const filteredSkills = getFilteredTabOptions(state, "skill");
5614
5677
  const filteredAgents = getFilteredTabOptions(state, "agent");
5678
+ const realFilteredSkills = filteredSkills.filter((o) => !o.isSelectAll);
5679
+ const realFilteredAgents = filteredAgents.filter((o) => !o.isSelectAll);
5615
5680
  const visible = state.activeTab === "skill" ? filteredSkills : filteredAgents;
5681
+ const realVisible = state.activeTab === "skill" ? realFilteredSkills : realFilteredAgents;
5616
5682
  const total = visible.length;
5617
5683
  const skillTabTitle = options.tabLabels?.skill ?? "Skills";
5618
5684
  const agentTabTitle = options.tabLabels?.agent ?? "Agents";
5619
5685
  const submitVerb = options.submitActionLabel ?? "import";
5620
- const isSinglePage = totalAgents === 0;
5686
+ const isSinglePage = totalAgents === 0 || totalSkills === 0;
5621
5687
  const header = `${style.bold(style.cyan(`\u25C6 ${title}`, c), c)}`;
5622
5688
  const tabHint = isSinglePage ? "" : " \xB7 \u2190/\u2192 tab";
5623
5689
  const instructions = state.searchMode ? `${bar} ${style.dim(`\u2191/\u2193 move${tabHint} \xB7 Space select \xB7 Esc clear \xB7 Enter ${submitVerb}`, c)}` : `${bar} ${style.dim(`\u2191/\u2193 move${tabHint} \xB7 Space select \xB7 / search \xB7 a all \xB7 Enter ${submitVerb}`, c)}`;
@@ -5627,8 +5693,8 @@ function renderMultiSelectFrame(title, state, options = {}) {
5627
5693
  }
5628
5694
  lines.push(instructions, bar);
5629
5695
  const hasQuery = state.searchQuery.length > 0;
5630
- const skillCountStr = hasQuery ? `${filteredSkills.length}/${totalSkills}` : `${totalSkills}`;
5631
- const agentCountStr = hasQuery ? `${filteredAgents.length}/${totalAgents}` : `${totalAgents}`;
5696
+ const skillCountStr = hasQuery ? `${realFilteredSkills.length}/${totalSkills}` : `${totalSkills}`;
5697
+ const agentCountStr = hasQuery ? `${realFilteredAgents.length}/${totalAgents}` : `${totalAgents}`;
5632
5698
  const skillTabLabel = state.activeTab === "skill" ? style.bold(
5633
5699
  style.cyan(`[ \u25C6 ${skillTabTitle} (${skillCountStr}) ]`, c),
5634
5700
  c
@@ -5639,8 +5705,9 @@ function renderMultiSelectFrame(title, state, options = {}) {
5639
5705
  ) : style.dim(`\u25CB ${agentTabTitle} (${agentCountStr})`, c);
5640
5706
  const rawQuerySlice = state.searchQuery.length > 14 ? `\u2026${state.searchQuery.slice(-13)}` : state.searchQuery;
5641
5707
  const searchSuffix = state.searchMode || hasQuery ? ` ${style.yellow(`/ Filter: ${rawQuerySlice}${state.searchMode ? "\u2588" : ""}`, c)}` : ` ${style.dim("(/ search)", c)}`;
5708
+ const singleTabLabel = totalSkills > 0 ? skillTabLabel : agentTabLabel;
5642
5709
  lines.push(
5643
- isSinglePage ? `${style.dim("\u251C\u2500", c)} ${skillTabLabel}${searchSuffix}` : `${style.dim("\u251C\u2500", c)} ${skillTabLabel} ${agentTabLabel}${searchSuffix}`,
5710
+ isSinglePage ? `${style.dim("\u251C\u2500", c)} ${singleTabLabel}${searchSuffix}` : `${style.dim("\u251C\u2500", c)} ${skillTabLabel} ${agentTabLabel}${searchSuffix}`,
5644
5711
  bar
5645
5712
  );
5646
5713
  if (total === 0) {
@@ -5666,9 +5733,11 @@ function renderMultiSelectFrame(title, state, options = {}) {
5666
5733
  const opt = visible[i];
5667
5734
  if (!opt) continue;
5668
5735
  const isCurrent = i === state.cursor;
5669
- const isChecked = state.selected.has(opt.value);
5736
+ const allRealSelected = realVisible.length > 0 && realVisible.every((o) => state.selected.has(o.value));
5737
+ const someRealSelected = realVisible.some((o) => state.selected.has(o.value)) && !allRealSelected;
5738
+ const isChecked = opt.isSelectAll ? allRealSelected : state.selected.has(opt.value);
5670
5739
  const pointer = isCurrent ? style.cyan("\u276F", c) : " ";
5671
- const checkbox = isChecked ? style.green("\u25C9", c) : style.dim("\u25EF", c);
5740
+ const checkbox = isChecked ? style.green("\u25C9", c) : opt.isSelectAll && someRealSelected ? style.cyan("\u25D0", c) : style.dim("\u25EF", c);
5672
5741
  const labelRaw = opt.label.length > maxLabelCols ? `${opt.label.slice(0, maxLabelCols - 1)}\u2026` : opt.label;
5673
5742
  const labelPadded = padVisible(labelRaw, maxLabelCols + 1, "left");
5674
5743
  const styledLabel = isCurrent ? style.bold(labelPadded, c) : isChecked ? style.green(labelPadded, c) : labelPadded;
@@ -5686,10 +5755,10 @@ function renderMultiSelectFrame(title, state, options = {}) {
5686
5755
  }
5687
5756
  }
5688
5757
  lines.push(bar);
5689
- const selectedSkillsCount = state.options.filter(
5758
+ const selectedSkillsCount = realOptions.filter(
5690
5759
  (o) => optionMatchesTab(o, "skill") && state.selected.has(o.value)
5691
5760
  ).length;
5692
- const selectedAgentsCount = state.options.filter(
5761
+ const selectedAgentsCount = realOptions.filter(
5693
5762
  (o) => optionMatchesTab(o, "agent") && state.selected.has(o.value)
5694
5763
  ).length;
5695
5764
  const currentOptLabel = visible[state.cursor]?.label ?? "";
@@ -5706,7 +5775,9 @@ async function runMultiSelectPrompt(title, options, renderOpts = {}) {
5706
5775
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
5707
5776
  return null;
5708
5777
  }
5709
- let state = createMultiSelectState(options);
5778
+ let state = createMultiSelectState(options, {
5779
+ showSelectAll: renderOpts.showSelectAll
5780
+ });
5710
5781
  let renderedLineCount = 0;
5711
5782
  const render = () => {
5712
5783
  const frame = renderMultiSelectFrame(title, state, renderOpts);
@@ -6652,6 +6723,66 @@ function buildInteractiveCatalogOptions(catalogDir) {
6652
6723
  }
6653
6724
  return options;
6654
6725
  }
6726
+ function buildInteractiveInstalledOptions(repoPath, catalogDir, targetFilter) {
6727
+ const lockfilePath = path10.join(path10.resolve(repoPath), ".skillops-lock.json");
6728
+ if (!fs10.existsSync(lockfilePath)) {
6729
+ return [];
6730
+ }
6731
+ let parsedLock;
6732
+ try {
6733
+ const raw = JSON.parse(fs10.readFileSync(lockfilePath, "utf-8"));
6734
+ parsedLock = SyncLockfileSchema.safeParse(raw);
6735
+ } catch {
6736
+ return [];
6737
+ }
6738
+ if (!parsedLock.success) {
6739
+ return [];
6740
+ }
6741
+ const lock = parsedLock.data;
6742
+ const targetSet = targetFilter && targetFilter.length > 0 ? new Set(targetFilter) : null;
6743
+ const targetsByItem = /* @__PURE__ */ new Map();
6744
+ for (const entry of Object.values(lock.artifacts)) {
6745
+ if (entry.skill_name === "codex-agents-index") {
6746
+ continue;
6747
+ }
6748
+ if (targetSet && !targetSet.has(entry.target)) {
6749
+ continue;
6750
+ }
6751
+ let itemTargets = targetsByItem.get(entry.skill_name);
6752
+ if (!itemTargets) {
6753
+ itemTargets = /* @__PURE__ */ new Set();
6754
+ targetsByItem.set(entry.skill_name, itemTargets);
6755
+ }
6756
+ itemTargets.add(entry.target);
6757
+ }
6758
+ if (targetsByItem.size === 0) {
6759
+ return [];
6760
+ }
6761
+ const catalogOptions = buildInteractiveCatalogOptions(catalogDir);
6762
+ const catalogMap = new Map(catalogOptions.map((o) => [o.value, o]));
6763
+ const subagentSet = new Set(lock.selected_subagents);
6764
+ const skills = [];
6765
+ const agents = [];
6766
+ for (const [itemName, itemTargets] of targetsByItem.entries()) {
6767
+ const catalogOpt = catalogMap.get(itemName);
6768
+ const isAgent = catalogOpt ? catalogOpt.kind === "agent" : subagentSet.has(itemName);
6769
+ const targetsLabel = [...itemTargets].join(", ");
6770
+ const baseHint = catalogOpt?.hint ?? "Installed in project";
6771
+ const option = {
6772
+ value: itemName,
6773
+ label: itemName,
6774
+ kind: isAgent ? "agent" : "skill",
6775
+ tab: isAgent ? "agent" : "skill",
6776
+ hint: `[${targetsLabel}] \xB7 ${baseHint}`
6777
+ };
6778
+ if (isAgent) {
6779
+ agents.push(option);
6780
+ } else {
6781
+ skills.push(option);
6782
+ }
6783
+ }
6784
+ return [...skills, ...agents];
6785
+ }
6655
6786
  function runCli(argv) {
6656
6787
  const args = argv.slice(2);
6657
6788
  if (args.includes("--version") || args.includes("-V") || args[0] === "version") {
@@ -6919,6 +7050,10 @@ function runCli(argv) {
6919
7050
  (a) => ` [SUBAGENT] ${a.subagentName} (prompt=${a.systemPromptTokens}t, max_out<=${a.maxOutputTokens}t, tools=${a.tools.length})`
6920
7051
  ),
6921
7052
  renderNextSteps([
7053
+ {
7054
+ command: "npx skillops-cli add",
7055
+ description: "Interactive import into your AI IDE"
7056
+ },
6922
7057
  {
6923
7058
  command: `npx skillops-cli info ${firstSkill}`,
6924
7059
  description: "Inspect triggers & usage"
@@ -6926,10 +7061,6 @@ function runCli(argv) {
6926
7061
  {
6927
7062
  command: "npx skillops-cli search <keyword>",
6928
7063
  description: "Search Skills & Subagents"
6929
- },
6930
- {
6931
- command: "npx skillops-cli quickstart",
6932
- description: "Auto-detect & install"
6933
7064
  }
6934
7065
  ])
6935
7066
  ];
@@ -7170,12 +7301,7 @@ function runCli(argv) {
7170
7301
  }
7171
7302
  if (command === "remove" || command === "uninstall" || command === "rm") {
7172
7303
  const positionals = getPositionalArgs(args);
7173
- if (positionals.length === 0) {
7174
- process.stderr.write(
7175
- "Usage: npx skillops-cli remove <skill-or-subagent-name> [options]\n"
7176
- );
7177
- return ExitCode.ERROR;
7178
- }
7304
+ const removeAll = args.includes("--all");
7179
7305
  const repoPath = getFlagValue(args, "--repo", "--project", "-p") ?? process.cwd();
7180
7306
  const cfgRes = loadSkillOpsConfig(repoPath);
7181
7307
  if (cfgRes.error) {
@@ -7193,19 +7319,44 @@ function runCli(argv) {
7193
7319
  const format = rawFormat === "json" || rawFormat === "markdown" ? rawFormat : "table";
7194
7320
  try {
7195
7321
  const targets = rawTargetsArg ? resolveRuntimeTargets(rawTargetsArg, repoPath) : void 0;
7196
- let allPassed = true;
7197
- for (const itemName of positionals) {
7198
- const report = removeSkillOrSubagent(repoPath, catalogDir, itemName, {
7199
- targets,
7322
+ if (removeAll && positionals.length === 0 && !targets) {
7323
+ const report = cleanSyncedArtifacts(repoPath, catalogDir, {
7200
7324
  dryRun,
7201
7325
  force
7202
7326
  });
7203
7327
  process.stdout.write(formatSyncReport(report, format));
7204
- if (!report.passed) {
7205
- allPassed = false;
7206
- }
7328
+ return report.passed ? ExitCode.SUCCESS : ExitCode.ERROR;
7329
+ }
7330
+ const itemsToRemove = removeAll && positionals.length === 0 ? buildInteractiveInstalledOptions(repoPath, catalogDir, targets).map(
7331
+ (o) => o.value
7332
+ ) : positionals;
7333
+ if (itemsToRemove.length === 0) {
7334
+ process.stderr.write(
7335
+ "Usage: npx skillops-cli remove <skill-or-subagent-name> [options]\n"
7336
+ );
7337
+ return ExitCode.ERROR;
7207
7338
  }
7208
- return allPassed ? ExitCode.SUCCESS : ExitCode.ERROR;
7339
+ const reports = itemsToRemove.map(
7340
+ (itemName) => removeSkillOrSubagent(repoPath, catalogDir, itemName, {
7341
+ targets,
7342
+ dryRun,
7343
+ force
7344
+ })
7345
+ );
7346
+ const firstReport = reports[0];
7347
+ if (!firstReport) {
7348
+ return ExitCode.ERROR;
7349
+ }
7350
+ const finalReport = reports.length === 1 ? firstReport : {
7351
+ ...firstReport,
7352
+ artifacts: reports.flatMap((r) => r.artifacts),
7353
+ drift_conflicts: [
7354
+ ...new Set(reports.flatMap((r) => r.drift_conflicts))
7355
+ ],
7356
+ passed: reports.every((r) => r.passed)
7357
+ };
7358
+ process.stdout.write(formatSyncReport(finalReport, format));
7359
+ return finalReport.passed ? ExitCode.SUCCESS : ExitCode.ERROR;
7209
7360
  } catch (err) {
7210
7361
  const msg = err instanceof Error ? err.message : String(err);
7211
7362
  process.stderr.write(`Error: ${msg}
@@ -7393,7 +7544,8 @@ async function runCliInteractive(argv) {
7393
7544
  const options = buildInteractiveCatalogOptions(catalogDir);
7394
7545
  const step2Title = !explicitTargetsArg ? "SkillOps \u2014 Step 2/2: Select Skills & Agents to Import" : "SkillOps \u2014 Select Skills & Agents to Import";
7395
7546
  const selected = await runMultiSelectPrompt(step2Title, options, {
7396
- targetHint: targets.join(", ")
7547
+ targetHint: targets.join(", "),
7548
+ showSelectAll: true
7397
7549
  });
7398
7550
  if (!selected || selected.length === 0) {
7399
7551
  process.stdout.write("No Skills or Agents selected.\n");
@@ -7403,6 +7555,58 @@ async function runCliInteractive(argv) {
7403
7555
  }
7404
7556
  return runCli([...argv, ...extraArgs]);
7405
7557
  }
7558
+ if ((command === "remove" || command === "uninstall" || command === "rm") && !args.includes("--help") && !args.includes("-h") && !args.includes("--all") && process.stdin.isTTY && process.stdout.isTTY) {
7559
+ const positionals = getPositionalArgs(args);
7560
+ if (positionals.length === 0) {
7561
+ const repoPath = getFlagValue(args, "--repo", "--project", "-p") ?? process.cwd();
7562
+ const cfgRes = loadSkillOpsConfig(repoPath);
7563
+ if (cfgRes.error) {
7564
+ process.stderr.write(
7565
+ `Invalid .skillops.yml at ${cfgRes.configPath}: ${cfgRes.error}
7566
+ `
7567
+ );
7568
+ return ExitCode.BUDGET_OR_CONTRACT_ERROR;
7569
+ }
7570
+ const catalogDir = resolveCatalogDir(args, repoPath, cfgRes.config);
7571
+ const explicitTargetsArg = resolveTargetsArg(args);
7572
+ let targets;
7573
+ if (explicitTargetsArg) {
7574
+ try {
7575
+ targets = resolveRuntimeTargets(explicitTargetsArg, repoPath);
7576
+ } catch (err) {
7577
+ const msg = err instanceof Error ? err.message : String(err);
7578
+ process.stderr.write(`Invalid --targets: ${msg}
7579
+ `);
7580
+ return ExitCode.BUDGET_OR_CONTRACT_ERROR;
7581
+ }
7582
+ }
7583
+ const installedOptions = buildInteractiveInstalledOptions(
7584
+ repoPath,
7585
+ catalogDir,
7586
+ targets
7587
+ );
7588
+ if (installedOptions.length === 0) {
7589
+ process.stdout.write(
7590
+ "No imported Skills or Agents found in this project.\n"
7591
+ );
7592
+ return ExitCode.SUCCESS;
7593
+ }
7594
+ const selected = await runMultiSelectPrompt(
7595
+ "SkillOps \u2014 Select Installed Skills & Agents to Remove",
7596
+ installedOptions,
7597
+ {
7598
+ submitActionLabel: "remove",
7599
+ targetHint: targets ? targets.join(", ") : void 0,
7600
+ showSelectAll: true
7601
+ }
7602
+ );
7603
+ if (!selected || selected.length === 0) {
7604
+ process.stdout.write("No Skills or Agents selected for removal.\n");
7605
+ return ExitCode.SUCCESS;
7606
+ }
7607
+ return runCli([...argv, ...selected]);
7608
+ }
7609
+ }
7406
7610
  return runCli(argv);
7407
7611
  }
7408
7612
  function isDirectCliInvocation() {
@@ -7427,6 +7631,7 @@ if (isDirectCliInvocation()) {
7427
7631
  }
7428
7632
  export {
7429
7633
  buildInteractiveCatalogOptions,
7634
+ buildInteractiveInstalledOptions,
7430
7635
  buildInteractiveTargetOptions,
7431
7636
  runCli,
7432
7637
  runCliInteractive
package/dist/index.d.ts CHANGED
@@ -8,7 +8,7 @@ import { z } from 'zod';
8
8
  * telemetry and Multi-Runtime Transpilation lockfiles.
9
9
  */
10
10
 
11
- declare const SKILLOPS_VERSION = "1.3.5";
11
+ declare const SKILLOPS_VERSION = "1.3.7";
12
12
  /**
13
13
  * Count whitespace-delimited words in text.
14
14
  */
@@ -2190,6 +2190,10 @@ interface MultiSelectOption {
2190
2190
  readonly tab?: MultiSelectTab;
2191
2191
  readonly hint?: string;
2192
2192
  readonly initialSelected?: boolean;
2193
+ readonly isSelectAll?: boolean;
2194
+ }
2195
+ interface CreateMultiSelectStateOptions {
2196
+ readonly showSelectAll?: boolean;
2193
2197
  }
2194
2198
  interface MultiSelectState {
2195
2199
  readonly options: readonly MultiSelectOption[];
@@ -2212,6 +2216,7 @@ interface RenderMultiSelectOptions {
2212
2216
  };
2213
2217
  readonly submitActionLabel?: string;
2214
2218
  readonly selectCursorOnEmptySubmit?: boolean;
2219
+ readonly showSelectAll?: boolean;
2215
2220
  readonly maxVisible?: number;
2216
2221
  readonly colorEnabled?: boolean;
2217
2222
  }
@@ -2222,7 +2227,7 @@ declare function getFilteredTabOptions(state: MultiSelectState, tab?: MultiSelec
2222
2227
  /**
2223
2228
  * Initialize a deterministic multi-select state from a list of options.
2224
2229
  */
2225
- declare function createMultiSelectState(options: readonly MultiSelectOption[]): MultiSelectState;
2230
+ declare function createMultiSelectState(options: readonly MultiSelectOption[], stateOptions?: CreateMultiSelectStateOptions): MultiSelectState;
2226
2231
  /**
2227
2232
  * Pure state reducer for keyboard input in the tabbed + searchable multi-select TUI.
2228
2233
  */
@@ -2356,4 +2361,4 @@ declare const DEFAULT_VALID_COMMANDS: readonly string[];
2356
2361
  */
2357
2362
  declare function suggestCommand(unknownCommand: string, validCommands?: readonly string[]): string | null;
2358
2363
 
2359
- export { ALL_RUNTIME_TARGETS, type AddOptions, type AnalyzeTokenEconomyOptions, type ApplyMultiSelectKeyOptions, type AuditCatalogOptions, type BadgeType, type BenchmarkArm, BenchmarkArmSchema, type BenchmarkDelta, BenchmarkDeltaSchema, type BoxOptions, COMMAND_HELP_SPECS, CORE_RUNTIME_TARGETS, type CatalogEvalSummary, CatalogEvalSummarySchema, type CatalogItemInfo, type CatalogSearchMatch, type CatalogSearchReport, type CatalogTokenBudgetSummary, type CatalogTrack, CatalogTrackSchema, type CleanOptions, type CollisionPair, CollisionPairSchema, type CollisionReport, CollisionReportSchema, type CollisionSeverity, CollisionSeveritySchema, type ColorOverrideOptions, type CommandHelpSpec, type CommandOptionSpec, DEFAULT_VALID_COMMANDS, type DiagnoseRepositoryOptions, type DoctorCheckItem, type DoctorReport, type EvalAssertion, EvalAssertionSchema, type EvalCase, EvalCaseSchema, ExitCode, type ExitCodeValue, type HarnessCategory, type HarnessInspectionEntry, type HarnessInspectionReport, type HarnessSkillFormat, type JsonRpcRequest, type JsonRpcResponse, type LoadedSkillOpsConfigResult, type LockfileArtifactDiagnostic, type McpConfigAction, type McpServerContext, type McpSetupOptions, type McpSetupReport, type MultiSelectOption, type MultiSelectState, type MultiSelectTab, type NextStepHint, type ParseSkillOptions, type ParsedSkillDirectory, type ParsedSubagentFile, type QuickstartOptions, type QuickstartReport, RUNTIME_HARNESS_SPECS, type RemoveOptions, type RenderMultiSelectOptions, RepoFingerprinter, type RepoProfile, RepoProfileSchema, type RouteOptions, type RoutedBundle, RoutedBundleSchema, type RoutingSignals, RoutingSignalsSchema, type RuntimeHarnessSpec, type RuntimeTarget, RuntimeTargetSchema, SKILLOPS_VERSION, type ScaffoldOptions, type ScaffoldResult, type ScoredSkill, ScoredSkillSchema, type ScriptEntrypoint, ScriptEntrypointSchema, type SkillBenchmark, SkillBenchmarkSchema, type SkillEntrypoints, SkillEntrypointsSchema, type SkillEvalSuite, SkillEvalSuiteSchema, type SkillManifest, type SkillManifestInput, SkillManifestSchema, type SkillOpsConfig, SkillOpsConfigSchema, type SkillOpsDetectedStackConfig, SkillOpsDetectedStackConfigSchema, type SkillOpsTokenEconomyConfig, SkillOpsTokenEconomyConfigSchema, type SkillTokenBudgetAudit, type SkippedSkill, SkippedSkillSchema, type SubagentManifest, SubagentManifestSchema, type SubagentTokenBudgetAudit, type SyncLockEntry, SyncLockEntrySchema, type SyncLockfile, SyncLockfileSchema, type SyncOptions, type SyncReport, SyncReportSchema, type SyncedArtifact, SyncedArtifactSchema, type TableOptions, type TokenBarOptions, type TokenEconomyReport, TokenEconomyReportSchema, type TokenFileCategory, TokenFileCategorySchema, type TokenFileEntry, TokenFileEntrySchema, type TokenWasteHotspot, TokenWasteHotspotSchema, addSkillOrSubagent, analyzeRepoTokenEconomy, applyMultiSelectKey, auditCatalog, auditCatalogTokenBudgets, auditSkillTokenBudget, auditSubagentTokenBudget, classifyCollisionSeverity, cleanSyncedArtifacts, compileSkillBundle, computeCollisionMatrix, computeFeatureCosine, computeIdfWeights, computeSha256, countWords, createCollisionPair, createCollisionReport, createMultiSelectState, createSkillManifest, createSubagentManifest, detectInstalledHarnesses, diagnoseRepository, discoverSkillDirectories, discoverSubagentFiles, estimateTokenCount, evaluateCatalog, evaluateSkillDirectory, extractSkillFeatures, findCommandHelpSpec, fingerprintRepository, formatBadge, formatCatalogItemInfo, formatCatalogSearchReport, formatDoctorReport, formatEvalSummary, formatHarnessInspectionReport, formatMcpSetupReport, formatQuickstartReport, formatRoutedBundle, formatScaffoldReport, formatSyncReport, formatTokenEconomyReport, getFilteredTabOptions, handleMcpJsonRpcRequest, inspectCatalogItem, inspectHarnesses, isColorEnabled, levenshteinDistance, loadSkillOpsConfig, normalizePackageName, padVisible, parseSkillDirectory, parseSkillMdFile, parseSubagentFile, parseSubagentMdFile, removeSkillOrSubagent, renderAiUsageGuide, renderBox, renderCodexAgentsIndex, renderCommandHelp, renderGlobalHelp, renderHeaderBanner, renderMultiSelectFrame, renderNextSteps, renderProgressBar, renderSectionHeader, renderStep, renderTable, renderTokenBar, resolveRuntimeTargets, routeRepository, runMultiSelectPrompt, runQuickstart, scaffoldFrameworkItem, scoreSkillAgainstProfile, searchCatalog, setupMcpForHarnesses, splitFrontmatter, startMcpStdioServer, stripAnsi, stripCodexAgentsBlock, stripNegativeTriggerClauses, style, suggestCommand, syncRepository, tokenizeRoutingText, visibleLength };
2364
+ export { ALL_RUNTIME_TARGETS, type AddOptions, type AnalyzeTokenEconomyOptions, type ApplyMultiSelectKeyOptions, type AuditCatalogOptions, type BadgeType, type BenchmarkArm, BenchmarkArmSchema, type BenchmarkDelta, BenchmarkDeltaSchema, type BoxOptions, COMMAND_HELP_SPECS, CORE_RUNTIME_TARGETS, type CatalogEvalSummary, CatalogEvalSummarySchema, type CatalogItemInfo, type CatalogSearchMatch, type CatalogSearchReport, type CatalogTokenBudgetSummary, type CatalogTrack, CatalogTrackSchema, type CleanOptions, type CollisionPair, CollisionPairSchema, type CollisionReport, CollisionReportSchema, type CollisionSeverity, CollisionSeveritySchema, type ColorOverrideOptions, type CommandHelpSpec, type CommandOptionSpec, type CreateMultiSelectStateOptions, DEFAULT_VALID_COMMANDS, type DiagnoseRepositoryOptions, type DoctorCheckItem, type DoctorReport, type EvalAssertion, EvalAssertionSchema, type EvalCase, EvalCaseSchema, ExitCode, type ExitCodeValue, type HarnessCategory, type HarnessInspectionEntry, type HarnessInspectionReport, type HarnessSkillFormat, type JsonRpcRequest, type JsonRpcResponse, type LoadedSkillOpsConfigResult, type LockfileArtifactDiagnostic, type McpConfigAction, type McpServerContext, type McpSetupOptions, type McpSetupReport, type MultiSelectOption, type MultiSelectState, type MultiSelectTab, type NextStepHint, type ParseSkillOptions, type ParsedSkillDirectory, type ParsedSubagentFile, type QuickstartOptions, type QuickstartReport, RUNTIME_HARNESS_SPECS, type RemoveOptions, type RenderMultiSelectOptions, RepoFingerprinter, type RepoProfile, RepoProfileSchema, type RouteOptions, type RoutedBundle, RoutedBundleSchema, type RoutingSignals, RoutingSignalsSchema, type RuntimeHarnessSpec, type RuntimeTarget, RuntimeTargetSchema, SKILLOPS_VERSION, type ScaffoldOptions, type ScaffoldResult, type ScoredSkill, ScoredSkillSchema, type ScriptEntrypoint, ScriptEntrypointSchema, type SkillBenchmark, SkillBenchmarkSchema, type SkillEntrypoints, SkillEntrypointsSchema, type SkillEvalSuite, SkillEvalSuiteSchema, type SkillManifest, type SkillManifestInput, SkillManifestSchema, type SkillOpsConfig, SkillOpsConfigSchema, type SkillOpsDetectedStackConfig, SkillOpsDetectedStackConfigSchema, type SkillOpsTokenEconomyConfig, SkillOpsTokenEconomyConfigSchema, type SkillTokenBudgetAudit, type SkippedSkill, SkippedSkillSchema, type SubagentManifest, SubagentManifestSchema, type SubagentTokenBudgetAudit, type SyncLockEntry, SyncLockEntrySchema, type SyncLockfile, SyncLockfileSchema, type SyncOptions, type SyncReport, SyncReportSchema, type SyncedArtifact, SyncedArtifactSchema, type TableOptions, type TokenBarOptions, type TokenEconomyReport, TokenEconomyReportSchema, type TokenFileCategory, TokenFileCategorySchema, type TokenFileEntry, TokenFileEntrySchema, type TokenWasteHotspot, TokenWasteHotspotSchema, addSkillOrSubagent, analyzeRepoTokenEconomy, applyMultiSelectKey, auditCatalog, auditCatalogTokenBudgets, auditSkillTokenBudget, auditSubagentTokenBudget, classifyCollisionSeverity, cleanSyncedArtifacts, compileSkillBundle, computeCollisionMatrix, computeFeatureCosine, computeIdfWeights, computeSha256, countWords, createCollisionPair, createCollisionReport, createMultiSelectState, createSkillManifest, createSubagentManifest, detectInstalledHarnesses, diagnoseRepository, discoverSkillDirectories, discoverSubagentFiles, estimateTokenCount, evaluateCatalog, evaluateSkillDirectory, extractSkillFeatures, findCommandHelpSpec, fingerprintRepository, formatBadge, formatCatalogItemInfo, formatCatalogSearchReport, formatDoctorReport, formatEvalSummary, formatHarnessInspectionReport, formatMcpSetupReport, formatQuickstartReport, formatRoutedBundle, formatScaffoldReport, formatSyncReport, formatTokenEconomyReport, getFilteredTabOptions, handleMcpJsonRpcRequest, inspectCatalogItem, inspectHarnesses, isColorEnabled, levenshteinDistance, loadSkillOpsConfig, normalizePackageName, padVisible, parseSkillDirectory, parseSkillMdFile, parseSubagentFile, parseSubagentMdFile, removeSkillOrSubagent, renderAiUsageGuide, renderBox, renderCodexAgentsIndex, renderCommandHelp, renderGlobalHelp, renderHeaderBanner, renderMultiSelectFrame, renderNextSteps, renderProgressBar, renderSectionHeader, renderStep, renderTable, renderTokenBar, resolveRuntimeTargets, routeRepository, runMultiSelectPrompt, runQuickstart, scaffoldFrameworkItem, scoreSkillAgainstProfile, searchCatalog, setupMcpForHarnesses, splitFrontmatter, startMcpStdioServer, stripAnsi, stripCodexAgentsBlock, stripNegativeTriggerClauses, style, suggestCommand, syncRepository, tokenizeRoutingText, visibleLength };
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import fs from "fs";
3
3
  import YAML from "yaml";
4
4
  import { z } from "zod";
5
- var SKILLOPS_VERSION = "1.3.5";
5
+ var SKILLOPS_VERSION = "1.3.7";
6
6
  var SKILL_NAME_REGEX = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
7
7
  var SEMVER_REGEX = /^\d+\.\d+\.\d+$/;
8
8
  var FRONTMATTER_REGEX = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
@@ -4943,8 +4943,8 @@ var COMMAND_HELP_SPECS = [
4943
4943
  name: "remove",
4944
4944
  aliases: ["uninstall", "rm"],
4945
4945
  pillar: "library",
4946
- summary: "Remove a Skill/Subagent from harnesses",
4947
- usage: "npx skillops-cli remove <name...> [options]",
4946
+ summary: "Remove imported Skills/Subagents from harnesses",
4947
+ usage: "npx skillops-cli remove [name...] [options]",
4948
4948
  options: [
4949
4949
  {
4950
4950
  flags: "-p, --project <dir>",
@@ -4954,12 +4954,20 @@ var COMMAND_HELP_SPECS = [
4954
4954
  flags: "-t, --harness <list>",
4955
4955
  description: "Restrict removal to specific harnesses"
4956
4956
  },
4957
+ {
4958
+ flags: "--all",
4959
+ description: "Remove all imported Skills & Agents from project"
4960
+ },
4957
4961
  {
4958
4962
  flags: "--force",
4959
4963
  description: "Remove even if SHA-256 drift is detected"
4960
4964
  }
4961
4965
  ],
4962
4966
  examples: [
4967
+ {
4968
+ command: "npx skillops-cli remove",
4969
+ description: "Interactive selector of installed Skills & Agents"
4970
+ },
4963
4971
  {
4964
4972
  command: "npx skillops-cli uninstall mcp-architect",
4965
4973
  description: "Remove runtime files & update lockfile"
@@ -5349,24 +5357,20 @@ function renderGlobalHelp(version, colorEnabled) {
5349
5357
  desc: "Import Skills or Agents (interactive if no name given)"
5350
5358
  },
5351
5359
  {
5352
- cmd: "search <query>",
5353
- desc: "Search Skills & Agents by keyword, stack, or domain"
5360
+ cmd: "remove [name...]",
5361
+ desc: "Remove Skills or Agents (interactive if no name given)"
5354
5362
  },
5355
5363
  {
5356
5364
  cmd: "list [query]",
5357
5365
  desc: "List or filter available Skills & Agents in catalog"
5358
5366
  },
5359
5367
  {
5360
- cmd: "info <name>",
5361
- desc: "View details and how to use a Skill or Agent"
5362
- },
5363
- {
5364
- cmd: "remove <name>",
5365
- desc: "Remove an imported Skill or Agent from your project"
5368
+ cmd: "search <query>",
5369
+ desc: "Search Skills & Agents by keyword, stack, or domain"
5366
5370
  },
5367
5371
  {
5368
- cmd: "init",
5369
- desc: "Auto-detect stack & IDE and import recommended bundle"
5372
+ cmd: "info <name>",
5373
+ desc: "View details and how to use a Skill or Agent"
5370
5374
  }
5371
5375
  ];
5372
5376
  const cmdRows = coreCmds.map(
@@ -5378,12 +5382,12 @@ function renderGlobalHelp(version, colorEnabled) {
5378
5382
  renderSectionHeader("Commands", c),
5379
5383
  cmdRows,
5380
5384
  renderSectionHeader("Options", c),
5381
- `${bar} ${padVisible(style.cyan("-t, --target <ide>", c), 22, "left")}AI IDE (cursor, claude, copilot, windsurf, auto)`,
5385
+ `${bar} ${padVisible(style.cyan("-t, --target <ide>", c), 22, "left")}AI IDE (cursor, claude, copilot, antigravity...)`,
5382
5386
  `${bar} ${padVisible(style.cyan("-p, --project <dir>", c), 22, "left")}Project folder (default: current directory)`,
5383
- `${bar} ${padVisible(style.cyan("--all", c), 22, "left")}Import all 10 Skills & 9 Agents`,
5387
+ `${bar} ${padVisible(style.cyan("--all", c), 22, "left")}Import or remove all Skills & Agents`,
5384
5388
  renderSectionHeader("Examples", c),
5385
- `${bar} ${style.dim("$", c)} ${style.green("skillops add", c)} ${style.dim("\u2014 Interactive selector", c)}`,
5386
- `${bar} ${style.dim("$", c)} ${style.green("skillops add bug-reproducer-tdd red-team-breaker", c)} ${style.dim("\u2014 Direct import", c)}`,
5389
+ `${bar} ${style.dim("$", c)} ${style.green("skillops add", c)} ${style.dim("\u2014 Interactive import", c)}`,
5390
+ `${bar} ${style.dim("$", c)} ${style.green("skillops remove", c)} ${style.dim("\u2014 Interactive removal", c)}`,
5387
5391
  `${bar} ${style.dim("$", c)} ${style.green("skillops list", c)} ${style.dim("\u2014 Browse catalog", c)}`,
5388
5392
  bar,
5389
5393
  `${style.dim("\u2570\u2500", c)} ${style.dim("Run", c)} ${style.cyan("skillops <cmd> -h", c)} ${style.dim("for command options.", c)}`
@@ -5426,6 +5430,9 @@ function optionMatchesQuery(opt, rawQuery) {
5426
5430
  if (!q) {
5427
5431
  return true;
5428
5432
  }
5433
+ if (opt.isSelectAll) {
5434
+ return false;
5435
+ }
5429
5436
  return opt.label.toLowerCase().includes(q) || opt.value.toLowerCase().includes(q) || (opt.hint ?? "").toLowerCase().includes(q);
5430
5437
  }
5431
5438
  function getFilteredTabOptions(state, tab = state.activeTab) {
@@ -5433,16 +5440,47 @@ function getFilteredTabOptions(state, tab = state.activeTab) {
5433
5440
  (opt) => optionMatchesTab(opt, tab) && optionMatchesQuery(opt, state.searchQuery)
5434
5441
  );
5435
5442
  }
5436
- function createMultiSelectState(options) {
5443
+ function createMultiSelectState(options, stateOptions = {}) {
5444
+ let finalOptions = options;
5445
+ if (stateOptions.showSelectAll && !options.some((opt) => opt.isSelectAll === true)) {
5446
+ const skillItems = options.filter((opt) => optionMatchesTab(opt, "skill"));
5447
+ const agentItems = options.filter((opt) => optionMatchesTab(opt, "agent"));
5448
+ const withAll = [];
5449
+ if (skillItems.length > 0) {
5450
+ withAll.push({
5451
+ value: "__all_skills__",
5452
+ label: "[ALL] Select all Skills",
5453
+ kind: "skill",
5454
+ tab: "skill",
5455
+ isSelectAll: true,
5456
+ hint: `Select or deselect all ${skillItems.length} Skills in this tab`
5457
+ });
5458
+ withAll.push(...skillItems);
5459
+ }
5460
+ if (agentItems.length > 0) {
5461
+ withAll.push({
5462
+ value: "__all_agents__",
5463
+ label: "[ALL] Select all Agents",
5464
+ kind: "agent",
5465
+ tab: "agent",
5466
+ isSelectAll: true,
5467
+ hint: `Select or deselect all ${agentItems.length} Agents in this tab`
5468
+ });
5469
+ withAll.push(...agentItems);
5470
+ }
5471
+ if (withAll.length > 0) {
5472
+ finalOptions = withAll;
5473
+ }
5474
+ }
5437
5475
  const initial = /* @__PURE__ */ new Set();
5438
- for (const opt of options) {
5439
- if (opt.initialSelected) {
5476
+ for (const opt of finalOptions) {
5477
+ if (opt.initialSelected && !opt.isSelectAll) {
5440
5478
  initial.add(opt.value);
5441
5479
  }
5442
5480
  }
5443
- const hasSkills = options.some((opt) => optionMatchesTab(opt, "skill"));
5481
+ const hasSkills = finalOptions.some((opt) => optionMatchesTab(opt, "skill"));
5444
5482
  return {
5445
- options,
5483
+ options: finalOptions,
5446
5484
  activeTab: hasSkills ? "skill" : "agent",
5447
5485
  cursor: 0,
5448
5486
  selected: initial,
@@ -5532,6 +5570,29 @@ function applyMultiSelectKey(state, key, options = {}) {
5532
5570
  if (!currentOpt) {
5533
5571
  return state;
5534
5572
  }
5573
+ if (currentOpt.isSelectAll) {
5574
+ const realVisible = visible.filter((opt) => !opt.isSelectAll);
5575
+ if (realVisible.length === 0) {
5576
+ return state;
5577
+ }
5578
+ const allTabSelected = realVisible.every(
5579
+ (opt) => state.selected.has(opt.value)
5580
+ );
5581
+ const nextSelected2 = new Set(state.selected);
5582
+ if (allTabSelected) {
5583
+ for (const opt of realVisible) {
5584
+ nextSelected2.delete(opt.value);
5585
+ }
5586
+ } else {
5587
+ for (const opt of realVisible) {
5588
+ nextSelected2.add(opt.value);
5589
+ }
5590
+ }
5591
+ return {
5592
+ ...state,
5593
+ selected: nextSelected2
5594
+ };
5595
+ }
5535
5596
  const nextSelected = new Set(state.selected);
5536
5597
  if (nextSelected.has(currentOpt.value)) {
5537
5598
  nextSelected.delete(currentOpt.value);
@@ -5595,19 +5656,20 @@ function applyMultiSelectKey(state, key, options = {}) {
5595
5656
  };
5596
5657
  }
5597
5658
  if (key === "a" || key === "A") {
5598
- if (visible.length === 0) {
5659
+ const realVisible = visible.filter((opt) => !opt.isSelectAll);
5660
+ if (realVisible.length === 0) {
5599
5661
  return state;
5600
5662
  }
5601
- const allTabSelected = visible.every(
5663
+ const allTabSelected = realVisible.every(
5602
5664
  (opt) => state.selected.has(opt.value)
5603
5665
  );
5604
5666
  const nextSelected = new Set(state.selected);
5605
5667
  if (allTabSelected) {
5606
- for (const opt of visible) {
5668
+ for (const opt of realVisible) {
5607
5669
  nextSelected.delete(opt.value);
5608
5670
  }
5609
5671
  } else {
5610
- for (const opt of visible) {
5672
+ for (const opt of realVisible) {
5611
5673
  nextSelected.add(opt.value);
5612
5674
  }
5613
5675
  }
@@ -5622,20 +5684,24 @@ function renderMultiSelectFrame(title, state, options = {}) {
5622
5684
  const c = options.colorEnabled;
5623
5685
  const bar = style.dim("\u2502", c);
5624
5686
  const maxVisible = options.maxVisible ?? 10;
5625
- const totalSkills = state.options.filter(
5687
+ const realOptions = state.options.filter((o) => !o.isSelectAll);
5688
+ const totalSkills = realOptions.filter(
5626
5689
  (o) => optionMatchesTab(o, "skill")
5627
5690
  ).length;
5628
- const totalAgents = state.options.filter(
5691
+ const totalAgents = realOptions.filter(
5629
5692
  (o) => optionMatchesTab(o, "agent")
5630
5693
  ).length;
5631
5694
  const filteredSkills = getFilteredTabOptions(state, "skill");
5632
5695
  const filteredAgents = getFilteredTabOptions(state, "agent");
5696
+ const realFilteredSkills = filteredSkills.filter((o) => !o.isSelectAll);
5697
+ const realFilteredAgents = filteredAgents.filter((o) => !o.isSelectAll);
5633
5698
  const visible = state.activeTab === "skill" ? filteredSkills : filteredAgents;
5699
+ const realVisible = state.activeTab === "skill" ? realFilteredSkills : realFilteredAgents;
5634
5700
  const total = visible.length;
5635
5701
  const skillTabTitle = options.tabLabels?.skill ?? "Skills";
5636
5702
  const agentTabTitle = options.tabLabels?.agent ?? "Agents";
5637
5703
  const submitVerb = options.submitActionLabel ?? "import";
5638
- const isSinglePage = totalAgents === 0;
5704
+ const isSinglePage = totalAgents === 0 || totalSkills === 0;
5639
5705
  const header = `${style.bold(style.cyan(`\u25C6 ${title}`, c), c)}`;
5640
5706
  const tabHint = isSinglePage ? "" : " \xB7 \u2190/\u2192 tab";
5641
5707
  const instructions = state.searchMode ? `${bar} ${style.dim(`\u2191/\u2193 move${tabHint} \xB7 Space select \xB7 Esc clear \xB7 Enter ${submitVerb}`, c)}` : `${bar} ${style.dim(`\u2191/\u2193 move${tabHint} \xB7 Space select \xB7 / search \xB7 a all \xB7 Enter ${submitVerb}`, c)}`;
@@ -5645,8 +5711,8 @@ function renderMultiSelectFrame(title, state, options = {}) {
5645
5711
  }
5646
5712
  lines.push(instructions, bar);
5647
5713
  const hasQuery = state.searchQuery.length > 0;
5648
- const skillCountStr = hasQuery ? `${filteredSkills.length}/${totalSkills}` : `${totalSkills}`;
5649
- const agentCountStr = hasQuery ? `${filteredAgents.length}/${totalAgents}` : `${totalAgents}`;
5714
+ const skillCountStr = hasQuery ? `${realFilteredSkills.length}/${totalSkills}` : `${totalSkills}`;
5715
+ const agentCountStr = hasQuery ? `${realFilteredAgents.length}/${totalAgents}` : `${totalAgents}`;
5650
5716
  const skillTabLabel = state.activeTab === "skill" ? style.bold(
5651
5717
  style.cyan(`[ \u25C6 ${skillTabTitle} (${skillCountStr}) ]`, c),
5652
5718
  c
@@ -5657,8 +5723,9 @@ function renderMultiSelectFrame(title, state, options = {}) {
5657
5723
  ) : style.dim(`\u25CB ${agentTabTitle} (${agentCountStr})`, c);
5658
5724
  const rawQuerySlice = state.searchQuery.length > 14 ? `\u2026${state.searchQuery.slice(-13)}` : state.searchQuery;
5659
5725
  const searchSuffix = state.searchMode || hasQuery ? ` ${style.yellow(`/ Filter: ${rawQuerySlice}${state.searchMode ? "\u2588" : ""}`, c)}` : ` ${style.dim("(/ search)", c)}`;
5726
+ const singleTabLabel = totalSkills > 0 ? skillTabLabel : agentTabLabel;
5660
5727
  lines.push(
5661
- isSinglePage ? `${style.dim("\u251C\u2500", c)} ${skillTabLabel}${searchSuffix}` : `${style.dim("\u251C\u2500", c)} ${skillTabLabel} ${agentTabLabel}${searchSuffix}`,
5728
+ isSinglePage ? `${style.dim("\u251C\u2500", c)} ${singleTabLabel}${searchSuffix}` : `${style.dim("\u251C\u2500", c)} ${skillTabLabel} ${agentTabLabel}${searchSuffix}`,
5662
5729
  bar
5663
5730
  );
5664
5731
  if (total === 0) {
@@ -5684,9 +5751,11 @@ function renderMultiSelectFrame(title, state, options = {}) {
5684
5751
  const opt = visible[i];
5685
5752
  if (!opt) continue;
5686
5753
  const isCurrent = i === state.cursor;
5687
- const isChecked = state.selected.has(opt.value);
5754
+ const allRealSelected = realVisible.length > 0 && realVisible.every((o) => state.selected.has(o.value));
5755
+ const someRealSelected = realVisible.some((o) => state.selected.has(o.value)) && !allRealSelected;
5756
+ const isChecked = opt.isSelectAll ? allRealSelected : state.selected.has(opt.value);
5688
5757
  const pointer = isCurrent ? style.cyan("\u276F", c) : " ";
5689
- const checkbox = isChecked ? style.green("\u25C9", c) : style.dim("\u25EF", c);
5758
+ const checkbox = isChecked ? style.green("\u25C9", c) : opt.isSelectAll && someRealSelected ? style.cyan("\u25D0", c) : style.dim("\u25EF", c);
5690
5759
  const labelRaw = opt.label.length > maxLabelCols ? `${opt.label.slice(0, maxLabelCols - 1)}\u2026` : opt.label;
5691
5760
  const labelPadded = padVisible(labelRaw, maxLabelCols + 1, "left");
5692
5761
  const styledLabel = isCurrent ? style.bold(labelPadded, c) : isChecked ? style.green(labelPadded, c) : labelPadded;
@@ -5704,10 +5773,10 @@ function renderMultiSelectFrame(title, state, options = {}) {
5704
5773
  }
5705
5774
  }
5706
5775
  lines.push(bar);
5707
- const selectedSkillsCount = state.options.filter(
5776
+ const selectedSkillsCount = realOptions.filter(
5708
5777
  (o) => optionMatchesTab(o, "skill") && state.selected.has(o.value)
5709
5778
  ).length;
5710
- const selectedAgentsCount = state.options.filter(
5779
+ const selectedAgentsCount = realOptions.filter(
5711
5780
  (o) => optionMatchesTab(o, "agent") && state.selected.has(o.value)
5712
5781
  ).length;
5713
5782
  const currentOptLabel = visible[state.cursor]?.label ?? "";
@@ -5724,7 +5793,9 @@ async function runMultiSelectPrompt(title, options, renderOpts = {}) {
5724
5793
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
5725
5794
  return null;
5726
5795
  }
5727
- let state = createMultiSelectState(options);
5796
+ let state = createMultiSelectState(options, {
5797
+ showSelectAll: renderOpts.showSelectAll
5798
+ });
5728
5799
  let renderedLineCount = 0;
5729
5800
  const render = () => {
5730
5801
  const frame = renderMultiSelectFrame(title, state, renderOpts);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillops-cli",
3
- "version": "1.3.5",
3
+ "version": "1.3.7",
4
4
  "description": "SkillOps — The Zero-API Framework & Benchmark-Verified Library for High-End AI Coding Skills & Context-Isolated Subagents with Universal Top-28 AI Agent Transpilation & Token Economy Auditing.",
5
5
  "type": "module",
6
6
  "license": "MIT",