skillio 0.1.11 → 0.1.13

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
@@ -6,12 +6,13 @@ import {
6
6
  discoverSkills,
7
7
  getLockPath,
8
8
  green,
9
+ promptText,
9
10
  readLock,
10
11
  red,
11
12
  removeSkillFromLock,
12
13
  setColorEnabled,
13
14
  yellow
14
- } from "./shared/chunk-0qvp6v8g.js";
15
+ } from "./shared/chunk-2gt0ysd1.js";
15
16
 
16
17
  // src/cli.ts
17
18
  import { createRequire } from "node:module";
@@ -558,6 +559,167 @@ function _getBuiltinFlags(long, short, userNames, userAliases) {
558
559
  return [`--${long}`, `-${short}`];
559
560
  }
560
561
 
562
+ // src/commands/completion.ts
563
+ var BASH = `# skillio bash completion
564
+ # Install: source <(skl completion bash)
565
+ _skillio_completions() {
566
+ local cur prev words cword
567
+ COMPREPLY=()
568
+ cur="\${COMP_WORDS[COMP_CWORD]}"
569
+ prev="\${COMP_WORDS[COMP_CWORD-1]}"
570
+
571
+ local cmds="list ls remove rm cost co cst usage us usg completion"
572
+ if [ "\${COMP_CWORD}" -eq 1 ]; then
573
+ COMPREPLY=( $(compgen -W "\${cmds} -h --help -v --version" -- "\${cur}") )
574
+ return 0
575
+ fi
576
+
577
+ local sub="\${COMP_WORDS[1]}"
578
+ case "\${sub}" in
579
+ rm|remove)
580
+ if [[ "\${cur}" == -* ]]; then
581
+ COMPREPLY=( $(compgen -W "-g --global --all --dry-run -y --yes --force-lock --lock-only -h --help" -- "\${cur}") )
582
+ else
583
+ local names
584
+ local scope=""
585
+ for w in "\${COMP_WORDS[@]}"; do
586
+ if [ "\${w}" = "-g" ] || [ "\${w}" = "--global" ]; then scope="-g"; fi
587
+ done
588
+ names="$(skl list --names \${scope} 2>/dev/null)"
589
+ COMPREPLY=( $(compgen -W "\${names}" -- "\${cur}") )
590
+ fi
591
+ return 0
592
+ ;;
593
+ completion)
594
+ COMPREPLY=( $(compgen -W "bash zsh fish" -- "\${cur}") )
595
+ return 0
596
+ ;;
597
+ esac
598
+ }
599
+ complete -F _skillio_completions skl
600
+ complete -F _skillio_completions skillio
601
+ `;
602
+ var ZSH = `# skillio zsh completion
603
+ # Install: source <(skl completion zsh)
604
+ _skillio() {
605
+ local -a cmds
606
+ cmds=(
607
+ 'list:List skills per source'
608
+ 'ls:Alias for list'
609
+ 'remove:Delete on-disk skill dirs'
610
+ 'rm:Alias for remove'
611
+ 'cost:Show ambient ballast cost'
612
+ 'co:Alias for cost'
613
+ 'cst:Alias for cost'
614
+ 'usage:Show skill usage'
615
+ 'us:Alias for usage'
616
+ 'usg:Alias for usage'
617
+ 'completion:Print shell completion script'
618
+ )
619
+ if (( CURRENT == 2 )); then
620
+ _describe 'command' cmds
621
+ return
622
+ fi
623
+ local sub=\${words[2]}
624
+ case $sub in
625
+ rm|remove)
626
+ if [[ \${words[CURRENT]} == -* ]]; then
627
+ _values 'flag' \\
628
+ '-g[global scope]' '--global[global scope]' \\
629
+ '--all[remove every skill in scope]' \\
630
+ '--dry-run[print plan without deleting]' \\
631
+ '-y[skip confirmation]' '--yes[skip confirmation]' \\
632
+ '--force-lock[also remove lock entry]' \\
633
+ '--lock-only[remove only lock entry, keep disk]'
634
+ else
635
+ local scope=""
636
+ for w in \${words[@]}; do
637
+ if [[ $w == "-g" || $w == "--global" ]]; then scope="-g"; fi
638
+ done
639
+ local -a names
640
+ names=(\${(f)"$(skl list --names $scope 2>/dev/null)"})
641
+ compadd -- $names
642
+ fi
643
+ ;;
644
+ completion)
645
+ _values 'shell' bash zsh fish
646
+ ;;
647
+ esac
648
+ }
649
+ compdef _skillio skl skillio
650
+ `;
651
+ var FISH = `# skillio fish completion
652
+ # Install: skl completion fish | source
653
+ function __skillio_skill_names
654
+ set -l scope ""
655
+ for w in (commandline -opc)
656
+ if test "$w" = "-g" -o "$w" = "--global"
657
+ set scope "-g"
658
+ end
659
+ end
660
+ skl list --names $scope 2>/dev/null
661
+ end
662
+
663
+ function __skillio_needs_command
664
+ set -l cmd (commandline -opc)
665
+ test (count $cmd) -le 1
666
+ end
667
+
668
+ function __skillio_using_subcommand
669
+ set -l cmd (commandline -opc)
670
+ if test (count $cmd) -lt 2; return 1; end
671
+ test "$cmd[2]" = "$argv[1]"
672
+ end
673
+
674
+ complete -c skl -n __skillio_needs_command -a 'list ls remove rm cost co cst usage us usg completion'
675
+ complete -c skillio -n __skillio_needs_command -a 'list ls remove rm cost co cst usage us usg completion'
676
+
677
+ for sub in rm remove
678
+ complete -c skl -n "__skillio_using_subcommand $sub" -f -a '(__skillio_skill_names)'
679
+ complete -c skillio -n "__skillio_using_subcommand $sub" -f -a '(__skillio_skill_names)'
680
+ complete -c skl -n "__skillio_using_subcommand $sub" -s g -l global -d 'Use global scope'
681
+ complete -c skl -n "__skillio_using_subcommand $sub" -l all -d 'Remove every skill in scope'
682
+ complete -c skl -n "__skillio_using_subcommand $sub" -l dry-run -d 'Print plan without deleting'
683
+ complete -c skl -n "__skillio_using_subcommand $sub" -s y -l yes -d 'Skip confirmation prompt'
684
+ complete -c skl -n "__skillio_using_subcommand $sub" -l force-lock -d 'Also remove lock entry'
685
+ complete -c skl -n "__skillio_using_subcommand $sub" -l lock-only -d 'Remove only lock entry, keep disk'
686
+ end
687
+
688
+ for sub in completion
689
+ complete -c skl -n "__skillio_using_subcommand $sub" -f -a 'bash zsh fish'
690
+ complete -c skillio -n "__skillio_using_subcommand $sub" -f -a 'bash zsh fish'
691
+ end
692
+ `;
693
+ var completionCommand = defineCommand({
694
+ meta: {
695
+ description: "Print shell completion script (bash, zsh, fish)"
696
+ },
697
+ args: {
698
+ shell: {
699
+ type: "positional",
700
+ required: true,
701
+ description: "Target shell: bash, zsh, or fish"
702
+ }
703
+ },
704
+ run({ args }) {
705
+ const shell = String(args.shell ?? "");
706
+ switch (shell) {
707
+ case "bash":
708
+ process.stdout.write(BASH);
709
+ return;
710
+ case "zsh":
711
+ process.stdout.write(ZSH);
712
+ return;
713
+ case "fish":
714
+ process.stdout.write(FISH);
715
+ return;
716
+ default:
717
+ console.error(`unknown shell: ${shell || "(none)"} — supported: bash, zsh, fish`);
718
+ process.exit(1);
719
+ }
720
+ }
721
+ });
722
+
561
723
  // src/commands/cost.ts
562
724
  function classify(total) {
563
725
  if (total < 1000)
@@ -608,7 +770,7 @@ var costCommand = defineCommand({
608
770
  console.log(`${cyan(r.name)}${namePad} ${tokenCell}${tokenPad}${suffix}`);
609
771
  }
610
772
  console.log("");
611
- console.log(`Total: ~${total} tok across ${rows.length} skills ${paint(message)}`);
773
+ console.log(`Total: ~${total} tok across ${rows.length} skills ${paint(message)} · method: chars/4, yaml-frontmatter`);
612
774
  }
613
775
  });
614
776
 
@@ -634,7 +796,7 @@ function paintDisk(n) {
634
796
  return green(n.name);
635
797
  return cyan(n.name);
636
798
  }
637
- function bySource(records, roots) {
799
+ function bySource(records, roots, lockLabel) {
638
800
  const claudeRecords = records.filter((r) => r.sources.includes(".claude"));
639
801
  const agentsRecords = records.filter((r) => r.sources.includes(".agents"));
640
802
  const lockRecords = records.filter((r) => r.sources.includes("lock"));
@@ -644,13 +806,18 @@ function bySource(records, roots) {
644
806
  return {
645
807
  agents: { label: ".agents/skills", names: agentsNames, totalCount: agentsNames.length },
646
808
  claude: { label: ".claude/skills", names: claudeNames, totalCount: claudeNames.length },
647
- lock: { label: "skills-lock.json", names: lockNames, totalCount: lockNames.length }
809
+ lock: { label: lockLabel, names: lockNames, totalCount: lockNames.length }
648
810
  };
649
811
  }
650
812
  var listCommand = defineCommand({
651
813
  meta: { description: "List skills per source with install-type coloring and lock orphan filter" },
652
814
  args: {
653
- global: { type: "boolean", alias: "g", default: false, description: "Use global scope" }
815
+ global: { type: "boolean", alias: "g", default: false, description: "Use global scope" },
816
+ names: {
817
+ type: "boolean",
818
+ default: false,
819
+ description: "Print one skill name per line (no header, no colors) — for completion scripts"
820
+ }
654
821
  },
655
822
  run({ args }) {
656
823
  const lockPath = getLockPath(args.global);
@@ -661,7 +828,21 @@ var listCommand = defineCommand({
661
828
  claude: rootFor(args.global, lockPath, ".claude"),
662
829
  agents: rootFor(args.global, lockPath, ".agents")
663
830
  };
664
- const rows = bySource(records, roots);
831
+ const lockLabel = args.global ? ".agents/.skill-lock.json" : "skills-lock.json";
832
+ const rows = bySource(records, roots, lockLabel);
833
+ if (args.names) {
834
+ const all = new Set;
835
+ for (const n of rows.agents.names)
836
+ all.add(n.name);
837
+ for (const n of rows.claude.names)
838
+ all.add(n.name);
839
+ for (const n of rows.lock.names)
840
+ all.add(n.name);
841
+ for (const name of [...all].sort())
842
+ console.log(name);
843
+ return;
844
+ }
845
+ console.log(args.global ? "Global" : "Local");
665
846
  const claudeSet = new Set(rows.claude.names.map((n) => n.name));
666
847
  const agentsSet = new Set(rows.agents.names.map((n) => n.name));
667
848
  const orphans = rows.lock.names.filter((n) => !claudeSet.has(n.name) && !agentsSet.has(n.name));
@@ -676,7 +857,13 @@ var listCommand = defineCommand({
676
857
  },
677
858
  {
678
859
  row: rows.lock,
679
- render: () => orphans.length === 0 ? green("All skills onboard!") : orphans.map((n) => red(n.name)).join(" ")
860
+ render: () => {
861
+ if (rows.lock.totalCount === 0)
862
+ return "";
863
+ if (orphans.length === 0)
864
+ return green("All skills onboard!");
865
+ return orphans.map((n) => red(n.name)).join(" ");
866
+ }
680
867
  }
681
868
  ];
682
869
  const labelWidth = Math.max(...sourceRows.map((r) => r.row.label.length));
@@ -847,17 +1034,28 @@ function fileCount(dir) {
847
1034
  }
848
1035
  return n;
849
1036
  }
850
- function printPlan(plan, modifyLock) {
1037
+ function printPlan(plan, modifyLock, lockOnly) {
851
1038
  const { target } = plan;
852
1039
  console.log(`Will remove ${q(target.name)}:`);
853
1040
  if (target.inLock) {
854
- if (modifyLock)
1041
+ if (lockOnly || modifyLock)
855
1042
  console.log(" - skills-lock.json");
856
1043
  else
857
1044
  console.log(" - skills-lock.json (kept; use --force-lock to remove lock entry)");
858
1045
  } else {
859
1046
  console.log(" - skills-lock.json (not in lock)");
860
1047
  }
1048
+ if (lockOnly) {
1049
+ if (target.claudeDir)
1050
+ console.log(` - .claude/skills/${target.name}/ (kept; --lock-only)`);
1051
+ else
1052
+ console.log(" - .claude/skills/ (not found)");
1053
+ if (target.agentsDir)
1054
+ console.log(` - .agents/skills/${target.name}/ (kept; --lock-only)`);
1055
+ else
1056
+ console.log(" - .agents/skills/ (not found)");
1057
+ return;
1058
+ }
861
1059
  if (target.claudeDir)
862
1060
  console.log(` - .claude/skills/${target.name}/ (${plan.claudeFileCount} files)`);
863
1061
  else
@@ -880,10 +1078,26 @@ var removeCommand = defineCommand({
880
1078
  type: "boolean",
881
1079
  default: false,
882
1080
  description: "Also remove entry from skills-lock.json (default is to keep lock untouched)"
1081
+ },
1082
+ "lock-only": {
1083
+ type: "boolean",
1084
+ default: false,
1085
+ description: "Remove only the skills-lock.json entry; keep on-disk directories"
883
1086
  }
884
1087
  },
885
1088
  async run({ args }) {
886
- const { global: isGlobal, "dry-run": dryRun, yes, all, "force-lock": modifyLock } = args;
1089
+ const {
1090
+ global: isGlobal,
1091
+ "dry-run": dryRun,
1092
+ yes,
1093
+ all,
1094
+ "force-lock": modifyLock,
1095
+ "lock-only": lockOnly
1096
+ } = args;
1097
+ if (lockOnly && modifyLock) {
1098
+ console.error("--lock-only is mutually exclusive with --force-lock");
1099
+ process.exit(1);
1100
+ }
887
1101
  const subcmdIdx = process.argv.findIndex((a) => a === "remove" || a === "rm");
888
1102
  const names = process.argv.slice(subcmdIdx + 1).filter((a) => !a.startsWith("-"));
889
1103
  if (all && names.length > 0) {
@@ -912,14 +1126,29 @@ var removeCommand = defineCommand({
912
1126
  agentsFileCount: t.agentsDir ? fileCount(t.agentsDir) : undefined
913
1127
  }));
914
1128
  for (const p of plans) {
915
- printPlan(p, modifyLock);
1129
+ printPlan(p, modifyLock, lockOnly);
916
1130
  console.log("");
917
1131
  }
918
1132
  if (dryRun)
919
1133
  return;
920
- if (!yes) {
921
- const promptText = all ? `Remove ALL ${plans.length} skills?` : "Proceed?";
922
- const ok = await confirm(promptText);
1134
+ if (all) {
1135
+ const subject = lockOnly ? `ALL ${plans.length} lock entries (disk preserved)` : `ALL ${plans.length} skills`;
1136
+ const interactive = process.stdin.isTTY && process.stdout.isTTY;
1137
+ if (interactive) {
1138
+ const phrase = await promptText(`This will remove ${subject}. Type "all" to confirm:`);
1139
+ if (phrase !== "all") {
1140
+ console.log("Aborted");
1141
+ process.exit(1);
1142
+ }
1143
+ } else if (!yes) {
1144
+ const ok = await confirm(`Remove ${subject}?`);
1145
+ if (!ok) {
1146
+ console.log("Aborted");
1147
+ process.exit(1);
1148
+ }
1149
+ }
1150
+ } else if (!yes) {
1151
+ const ok = await confirm("Proceed?");
923
1152
  if (!ok) {
924
1153
  console.log("Aborted");
925
1154
  process.exit(1);
@@ -928,7 +1157,7 @@ var removeCommand = defineCommand({
928
1157
  const allowedRoots = [isGlobal ? homedir2() : dirname2(resolve3(lockPath)), homedir2()];
929
1158
  for (const { target } of plans) {
930
1159
  if (target.inLock) {
931
- if (modifyLock) {
1160
+ if (lockOnly || modifyLock) {
932
1161
  const r = removeSkillFromLock(lockPath, target.name);
933
1162
  if (r.removed)
934
1163
  console.log(`Removed ${q(target.name)} from skills-lock.json`);
@@ -938,6 +1167,17 @@ var removeCommand = defineCommand({
938
1167
  } else {
939
1168
  console.log(`Skipped skills-lock.json (not in lock)`);
940
1169
  }
1170
+ if (lockOnly) {
1171
+ if (target.claudeDir)
1172
+ console.log(`Kept .claude/skills/${target.name}/ (--lock-only)`);
1173
+ else
1174
+ console.log("Skipped .claude/skills (not found)");
1175
+ if (target.agentsDir)
1176
+ console.log(`Kept .agents/skills/${target.name}/ (--lock-only)`);
1177
+ else
1178
+ console.log("Skipped .agents/skills (not found)");
1179
+ continue;
1180
+ }
941
1181
  if (target.claudeDir) {
942
1182
  const r = rmSkillDir(target.claudeDir, { allowedRoots });
943
1183
  console.log(`Removed ${q(target.name)} from .claude/skills (${r.fileCount} files)`);
@@ -1138,7 +1378,7 @@ function extractClaudeMentions(entry) {
1138
1378
  }
1139
1379
  for (const m of node.matchAll(/\bsuperpowers:([a-z0-9-]+)\b/g)) {
1140
1380
  if (m[1] !== undefined)
1141
- seen.add(`superpowers:${m[1]}`);
1381
+ seen.add(m[1]);
1142
1382
  }
1143
1383
  });
1144
1384
  return [...seen];
@@ -1209,7 +1449,7 @@ function* findJsonlFiles(dir, since) {
1209
1449
  }
1210
1450
  function isRecentEntry(entry, since) {
1211
1451
  if (typeof entry !== "object" || entry === null)
1212
- return true;
1452
+ return false;
1213
1453
  const e = entry;
1214
1454
  if (typeof e.timestamp === "string") {
1215
1455
  const d = new Date(e.timestamp);
@@ -1217,7 +1457,7 @@ function isRecentEntry(entry, since) {
1217
1457
  }
1218
1458
  if (typeof e.ts === "number")
1219
1459
  return new Date(e.ts * 1000) >= since;
1220
- return true;
1460
+ return false;
1221
1461
  }
1222
1462
 
1223
1463
  // src/readers/claude.ts
@@ -1232,6 +1472,7 @@ function readClaudeUsage(options) {
1232
1472
  let prevSkill = null;
1233
1473
  const sessionAttr = new Map;
1234
1474
  const sessionAct = new Map;
1475
+ const sessionMen = new Map;
1235
1476
  for (const line of readFileSync2(file, "utf8").split(`
1236
1477
  `)) {
1237
1478
  if (!line.trim())
@@ -1263,7 +1504,7 @@ function readClaudeUsage(options) {
1263
1504
  }
1264
1505
  if (options.mode === "mentions") {
1265
1506
  for (const skill of extractClaudeMentions(entry)) {
1266
- counts.set(skill, (counts.get(skill) ?? 0) + 1);
1507
+ sessionMen.set(skill, (sessionMen.get(skill) ?? 0) + 1);
1267
1508
  }
1268
1509
  }
1269
1510
  }
@@ -1273,6 +1514,9 @@ function readClaudeUsage(options) {
1273
1514
  } else if (options.mode === "activations") {
1274
1515
  for (const [k, v] of sessionAct)
1275
1516
  counts.set(k, (counts.get(k) ?? 0) + v);
1517
+ } else if (options.mode === "mentions") {
1518
+ for (const [k, v] of sessionMen)
1519
+ counts.set(k, (counts.get(k) ?? 0) + v);
1276
1520
  } else if (options.mode === "merged") {
1277
1521
  const keys = new Set([...sessionAttr.keys(), ...sessionAct.keys()]);
1278
1522
  for (const k of keys) {
@@ -1410,19 +1654,21 @@ var SECOND_MS = 1000;
1410
1654
  var MINUTE_MS = 60 * SECOND_MS;
1411
1655
  var HOUR_MS = 60 * MINUTE_MS;
1412
1656
  var DAY_MS = 24 * HOUR_MS;
1657
+ var MONTH_MS = 30 * DAY_MS;
1413
1658
  var UNITS_MS = {
1414
1659
  s: SECOND_MS,
1415
1660
  m: MINUTE_MS,
1416
1661
  h: HOUR_MS,
1417
1662
  d: DAY_MS,
1418
- w: 7 * DAY_MS
1663
+ w: 7 * DAY_MS,
1664
+ mo: MONTH_MS
1419
1665
  };
1420
1666
  function parsePeriod(period) {
1421
1667
  if (period === "all")
1422
1668
  return Number.POSITIVE_INFINITY;
1423
- const match = period.match(/^(\d+)([smhdw])$/);
1669
+ const match = period.match(/^(\d+)(mo|[smhdw])$/);
1424
1670
  if (!match) {
1425
- throw new Error(`Invalid period: "${period}". Use values like 60s, 30m, 24h, 30d, 2w, all.`);
1671
+ throw new Error(`Invalid period: "${period}". Use values like 60s, 30m, 24h, 30d, 2w, 6mo, all.`);
1426
1672
  }
1427
1673
  const unit = UNITS_MS[match[2] ?? ""] ?? 0;
1428
1674
  return Number(match[1]) * unit;
@@ -1433,7 +1679,8 @@ function pad(n, width) {
1433
1679
  return String(n).padStart(width);
1434
1680
  }
1435
1681
  function formatUsageRow(row) {
1436
- return `${pad(row.count, row.countWidth)} ${cyan(row.name)}`;
1682
+ const suffix = row.installed === false ? ` ${red("(missing)")}` : "";
1683
+ return `${pad(row.count, row.countWidth)} ${cyan(row.name)}${suffix}`;
1437
1684
  }
1438
1685
  function parseAgents(agent) {
1439
1686
  if (!agent)
@@ -1457,7 +1704,7 @@ var usageArgs = {
1457
1704
  type: "string",
1458
1705
  alias: "p",
1459
1706
  default: "all",
1460
- description: "60s, 30m, 24h, 30d, 2w, all"
1707
+ description: "60s, 30m, 24h, 30d, 2w, 6mo, all"
1461
1708
  },
1462
1709
  since: { type: "string", description: "yyyy-mm-dd, overrides --period" },
1463
1710
  mode: {
@@ -1526,15 +1773,13 @@ async function runUsage(args) {
1526
1773
  name,
1527
1774
  count: counts.get(name) ?? 0,
1528
1775
  tokens: rec?.frontmatterTokens,
1529
- status: rec?.status ?? "ok"
1776
+ installed: rec !== undefined && rec.status !== "missing"
1530
1777
  };
1531
1778
  });
1532
1779
  const rows = allRows.filter((r) => r.count > 0);
1533
1780
  rows.sort((a, b) => {
1534
- const aOk = a.status === "ok";
1535
- const bOk = b.status === "ok";
1536
- if (aOk !== bOk)
1537
- return aOk ? -1 : 1;
1781
+ if (a.installed !== b.installed)
1782
+ return a.installed ? -1 : 1;
1538
1783
  if (b.count !== a.count)
1539
1784
  return b.count - a.count;
1540
1785
  return a.name.localeCompare(b.name);
@@ -1550,7 +1795,8 @@ async function runUsage(args) {
1550
1795
  skill: r.name,
1551
1796
  count: r.count,
1552
1797
  tokensPerSkill: r.tokens ?? null,
1553
- consumption: (r.tokens ?? 0) * r.count
1798
+ consumption: (r.tokens ?? 0) * r.count,
1799
+ installed: r.installed
1554
1800
  }))
1555
1801
  }));
1556
1802
  console.log(JSON.stringify(output.length === 1 ? output[0] : output, null, 2));
@@ -1569,7 +1815,7 @@ async function runUsage(args) {
1569
1815
  continue;
1570
1816
  const countWidth = Math.max(...rows.map((r) => String(r.count).length));
1571
1817
  for (const r of rows) {
1572
- console.log(formatUsageRow({ count: r.count, name: r.name, countWidth }));
1818
+ console.log(formatUsageRow({ count: r.count, name: r.name, countWidth, installed: r.installed }));
1573
1819
  distinct.add(r.name);
1574
1820
  }
1575
1821
  grandActivations += activations;
@@ -1720,7 +1966,8 @@ var SUBCOMMAND_NAMES = new Set([
1720
1966
  "cst",
1721
1967
  "usage",
1722
1968
  "us",
1723
- "usg"
1969
+ "usg",
1970
+ "completion"
1724
1971
  ]);
1725
1972
  function reorderRootFlagsToSubcommand(argv) {
1726
1973
  const tail = argv.slice(2);
@@ -1746,15 +1993,16 @@ function printRootHelp() {
1746
1993
  " -h, --help Show this help and exit",
1747
1994
  " -v, --version Show version and exit",
1748
1995
  " -g, --global Use global scope (default: false)",
1749
- " -p, --period Period for `usage`: 60s, 30m, 24h, 30d, 2w, all (default: all)",
1996
+ " -p, --period Period for `usage`: 60s, 30m, 24h, 30d, 2w, 6mo, all (default: all)",
1750
1997
  " -a, --agent Agent for `usage`: claude-code, codex (default: both)",
1751
1998
  "",
1752
1999
  "COMMANDS",
1753
2000
  "",
1754
- " list, ls List skills per source with totals and lock-vs-disk diff",
1755
- " remove, rm Remove skills from lock and delete their on-disk dirs",
2001
+ " list, ls List skills per source: install type, lock orphans, disk/lock diff",
2002
+ " remove, rm Delete on-disk skill dirs; lock kept unless --force-lock",
1756
2003
  " cost, co, cst Show ambient ballast cost (per-skill frontmatter tokens) sorted desc",
1757
- " usage, us, usg Show skill usage × cost (consumption) with missed rows"
2004
+ " usage, us, usg Show skill usage × cost (consumption) with missed rows",
2005
+ " completion Print shell completion script (bash, zsh, fish)"
1758
2006
  ];
1759
2007
  console.log(lines.join(`
1760
2008
  `));
@@ -1766,6 +2014,44 @@ function isRootHelp(argv) {
1766
2014
  return false;
1767
2015
  return args.includes("--help") || args.includes("-h");
1768
2016
  }
2017
+ function isRemoveHelp(argv) {
2018
+ const args = argv.slice(2);
2019
+ const first = args[0];
2020
+ if (first !== "remove" && first !== "rm")
2021
+ return false;
2022
+ return args.includes("--help") || args.includes("-h");
2023
+ }
2024
+ function printRemoveHelp() {
2025
+ const lines = [
2026
+ "Remove skills from on-disk dirs (lock preserved unless --force-lock).",
2027
+ "",
2028
+ "USAGE skillio remove [SKILL...] [OPTIONS]",
2029
+ " skillio rm [SKILL...] [OPTIONS]",
2030
+ "",
2031
+ "ARGUMENTS",
2032
+ "",
2033
+ " SKILL... One or more skill names. Use --all to target every skill in scope.",
2034
+ "",
2035
+ "OPTIONS",
2036
+ "",
2037
+ " -g, --global Use global scope (default: false)",
2038
+ " --all Remove every skill in scope (mutually exclusive with SKILL)",
2039
+ " --dry-run Print plan without deleting",
2040
+ " -y, --yes Skip confirmation prompt (non-TTY only for --all)",
2041
+ " --force-lock Also remove entry from skills-lock.json (default: lock preserved)",
2042
+ " --lock-only Remove only the lock entry; keep on-disk directories",
2043
+ "",
2044
+ "EXAMPLES",
2045
+ "",
2046
+ " skillio rm brainstorming",
2047
+ " skillio rm brainstorming writing-plans --yes",
2048
+ " skillio rm --all --dry-run",
2049
+ " skillio rm --force-lock obsolete-skill",
2050
+ " skillio rm --lock-only stale-entry"
2051
+ ];
2052
+ console.log(lines.join(`
2053
+ `));
2054
+ }
1769
2055
  function firstPositional(argv) {
1770
2056
  for (let i = 2;i < argv.length; i++) {
1771
2057
  const tok = argv[i];
@@ -1811,7 +2097,7 @@ var main = defineCommand({
1811
2097
  return;
1812
2098
  const interactive = process.stdout.isTTY && process.stdin.isTTY;
1813
2099
  if (interactive) {
1814
- const { runPicker } = await import("./shared/chunk-j9jc4e4c.js");
2100
+ const { runPicker } = await import("./shared/chunk-ajnqh9j9.js");
1815
2101
  const status = await runPicker({
1816
2102
  global: args.global ?? false
1817
2103
  });
@@ -1833,7 +2119,8 @@ var main = defineCommand({
1833
2119
  cst: costCommand,
1834
2120
  usage: usageCommand,
1835
2121
  us: usageCommand,
1836
- usg: usageCommand
2122
+ usg: usageCommand,
2123
+ completion: completionCommand
1837
2124
  }
1838
2125
  });
1839
2126
  (async () => {
@@ -1841,6 +2128,10 @@ var main = defineCommand({
1841
2128
  printRootHelp();
1842
2129
  return;
1843
2130
  }
2131
+ if (isRemoveHelp(process.argv)) {
2132
+ printRemoveHelp();
2133
+ return;
2134
+ }
1844
2135
  if (isRootVersion(process.argv)) {
1845
2136
  console.log(version);
1846
2137
  return;