skillio 0.1.12 → 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/README.md +41 -10
- package/dist/cli.js +317 -34
- package/dist/shared/chunk-2gt0ysd1.js +316 -0
- package/dist/shared/chunk-ajnqh9j9.js +86 -0
- package/package.json +3 -1
- package/dist/shared/chunk-0qvp6v8g.js +0 -138
- package/dist/shared/chunk-j1p4zpqy.js +0 -252
package/README.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/skillio)
|
|
4
4
|
[](https://github.com/ihororlovskyi/skillio/actions/workflows/ci.yml)
|
|
5
|
+
[](https://github.com/ihororlovskyi/skillio/actions/workflows/codeql.yml)
|
|
6
|
+
[](https://securityscorecards.dev/viewer/?uri=github.com/ihororlovskyi/skillio)
|
|
7
|
+
[](https://codecov.io/gh/ihororlovskyi/skillio)
|
|
8
|
+
[](https://github.com/ihororlovskyi/skillio/blob/main/LICENSE)
|
|
9
|
+
[](https://www.npmjs.com/package/skillio)
|
|
5
10
|
|
|
6
11
|
Audit and manage AI agent skills for Claude Code and OpenAI Codex.
|
|
7
12
|
|
|
@@ -76,10 +81,12 @@ skl cost # ambient ballast cost (frontmatter token
|
|
|
76
81
|
skl cst # alias for cost
|
|
77
82
|
skl usage # consumption: usage count × frontmatter tokens
|
|
78
83
|
skl usg # alias for usage
|
|
79
|
-
skl rm brainstorming #
|
|
84
|
+
skl rm brainstorming # delete on-disk dir; lock kept (Y/n prompt)
|
|
80
85
|
skl rm brainstorming writing-plans # remove multiple
|
|
86
|
+
skl rm --all # remove all skills in scope
|
|
81
87
|
skl rm --yes brainstorming # skip confirmation
|
|
82
88
|
skl rm --dry-run brainstorming # preview only
|
|
89
|
+
skl rm --force-lock brainstorming # also remove the lock entry
|
|
83
90
|
|
|
84
91
|
# scope flags
|
|
85
92
|
skl -g # force global scope on any subcommand
|
|
@@ -114,7 +121,7 @@ skl usage -a claude -a codex # equivalent: repeated --agent flag
|
|
|
114
121
|
| `-h, --help` | — | Show help and exit |
|
|
115
122
|
| `-v, --version` | — | Show version and exit |
|
|
116
123
|
| `-g, --global` | `false` | Use global scope (ignore current directory) |
|
|
117
|
-
| `-p, --period` | `all` | Period for `usage`: `
|
|
124
|
+
| `-p, --period` | `all` | Period for `usage`: `60s`, `30m`, `12h`, `7d`, `2w`, `6mo`, `all` (note: `1m` = 1 minute, `1mo` = 30 days) |
|
|
118
125
|
| `-a, --agent` | both | Agent for `usage`: `claude-code` (alias `claude`), `codex` — pass both space-separated (`-a claude-code codex`) or repeat the flag |
|
|
119
126
|
|
|
120
127
|
### `skillio usage` / `us`
|
|
@@ -129,9 +136,9 @@ skillio usage --agent codex --mode activations
|
|
|
129
136
|
| Flag | Default | Description |
|
|
130
137
|
|------|---------|-------------|
|
|
131
138
|
| `-a, --agent` | both | `claude-code`/`claude`, `codex` |
|
|
132
|
-
| `-p, --period` | `all` | `
|
|
139
|
+
| `-p, --period` | `all` | `60s`, `30m`, `24h`, `7d`, `2w`, `6mo`, `all` |
|
|
133
140
|
| `--since` | — | `yyyy-mm-dd`, overrides `--period` |
|
|
134
|
-
| `--mode` | `
|
|
141
|
+
| `--mode` | `merged` (claude) / `activations` (codex) | `merged` \| `attributed` \| `activations` \| `mentions` |
|
|
135
142
|
| `--format` | `text` | `text` \| `json` |
|
|
136
143
|
| `-g, --global` | `false` | Force global scope (ignore current directory) |
|
|
137
144
|
| `--root` | — | Override agent sessions directory; implies global |
|
|
@@ -139,9 +146,10 @@ skillio usage --agent codex --mode activations
|
|
|
139
146
|
|
|
140
147
|
### Modes
|
|
141
148
|
|
|
142
|
-
- **`
|
|
143
|
-
- **`
|
|
144
|
-
- **`
|
|
149
|
+
- **`merged`** — per-session union of `attributed` and `activations` (`max` per skill). Default for Claude.
|
|
150
|
+
- **`attributed`** — entries with an `attributionSkill` field set by Claude Code.
|
|
151
|
+
- **`activations`** — explicit `Skill` tool invocations (Claude) or read-like `exec_command_end` events / `<skill>` XML (Codex). Default for Codex.
|
|
152
|
+
- **`mentions`** — skill paths (`foo/SKILL.md`) or `superpowers:name` strings found anywhere. Broadest signal; can include matches from prompts, specs, or documentation.
|
|
145
153
|
|
|
146
154
|
### `skillio list` / `ls`
|
|
147
155
|
|
|
@@ -160,13 +168,36 @@ skillio cost --global # same, against ~/.agents/.skill-lock.json
|
|
|
160
168
|
### `skillio remove` / `rm`
|
|
161
169
|
|
|
162
170
|
```sh
|
|
163
|
-
skillio remove <skill-name>
|
|
171
|
+
skillio remove <skill-name> # delete on-disk dir; lock kept
|
|
164
172
|
skillio remove <skill-one> <skill-two>
|
|
173
|
+
skillio remove --all # remove all skills in scope
|
|
174
|
+
skillio remove --force-lock <skill-name> # also remove the lock entry
|
|
175
|
+
skillio remove --lock-only <skill-name> # only the lock entry; keep on disk
|
|
165
176
|
skillio remove --global <skill-name>
|
|
166
|
-
skillio remove --dry-run <skill-name>
|
|
167
|
-
skillio remove --yes <skill-name>
|
|
177
|
+
skillio remove --dry-run <skill-name> # preview only
|
|
178
|
+
skillio remove --yes <skill-name> # skip confirmation prompt
|
|
168
179
|
```
|
|
169
180
|
|
|
181
|
+
### Shell completion
|
|
182
|
+
|
|
183
|
+
`skl completion <shell>` prints a completion script. Sourced once in your
|
|
184
|
+
rc-file, it tab-completes subcommands and dynamic skill names for `skl rm`.
|
|
185
|
+
|
|
186
|
+
```sh
|
|
187
|
+
# bash (one-time setup)
|
|
188
|
+
skl completion bash >> ~/.bashrc
|
|
189
|
+
|
|
190
|
+
# zsh
|
|
191
|
+
skl completion zsh >> ~/.zshrc
|
|
192
|
+
|
|
193
|
+
# fish
|
|
194
|
+
skl completion fish | source # one-off in current shell
|
|
195
|
+
skl completion fish > ~/.config/fish/completions/skl.fish
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
`skl list --names` prints one skill name per line (no headers, no colors) and
|
|
199
|
+
is what the completion script calls under the hood.
|
|
200
|
+
|
|
170
201
|
## Requirements
|
|
171
202
|
|
|
172
203
|
- Node.js ≥ 20
|
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-
|
|
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
|
|
|
@@ -650,7 +812,12 @@ function bySource(records, roots, lockLabel) {
|
|
|
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);
|
|
@@ -663,6 +830,18 @@ var listCommand = defineCommand({
|
|
|
663
830
|
};
|
|
664
831
|
const lockLabel = args.global ? ".agents/.skill-lock.json" : "skills-lock.json";
|
|
665
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
|
+
}
|
|
666
845
|
console.log(args.global ? "Global" : "Local");
|
|
667
846
|
const claudeSet = new Set(rows.claude.names.map((n) => n.name));
|
|
668
847
|
const agentsSet = new Set(rows.agents.names.map((n) => n.name));
|
|
@@ -855,17 +1034,28 @@ function fileCount(dir) {
|
|
|
855
1034
|
}
|
|
856
1035
|
return n;
|
|
857
1036
|
}
|
|
858
|
-
function printPlan(plan, modifyLock) {
|
|
1037
|
+
function printPlan(plan, modifyLock, lockOnly) {
|
|
859
1038
|
const { target } = plan;
|
|
860
1039
|
console.log(`Will remove ${q(target.name)}:`);
|
|
861
1040
|
if (target.inLock) {
|
|
862
|
-
if (modifyLock)
|
|
1041
|
+
if (lockOnly || modifyLock)
|
|
863
1042
|
console.log(" - skills-lock.json");
|
|
864
1043
|
else
|
|
865
1044
|
console.log(" - skills-lock.json (kept; use --force-lock to remove lock entry)");
|
|
866
1045
|
} else {
|
|
867
1046
|
console.log(" - skills-lock.json (not in lock)");
|
|
868
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
|
+
}
|
|
869
1059
|
if (target.claudeDir)
|
|
870
1060
|
console.log(` - .claude/skills/${target.name}/ (${plan.claudeFileCount} files)`);
|
|
871
1061
|
else
|
|
@@ -888,10 +1078,26 @@ var removeCommand = defineCommand({
|
|
|
888
1078
|
type: "boolean",
|
|
889
1079
|
default: false,
|
|
890
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"
|
|
891
1086
|
}
|
|
892
1087
|
},
|
|
893
1088
|
async run({ args }) {
|
|
894
|
-
const {
|
|
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
|
+
}
|
|
895
1101
|
const subcmdIdx = process.argv.findIndex((a) => a === "remove" || a === "rm");
|
|
896
1102
|
const names = process.argv.slice(subcmdIdx + 1).filter((a) => !a.startsWith("-"));
|
|
897
1103
|
if (all && names.length > 0) {
|
|
@@ -920,14 +1126,29 @@ var removeCommand = defineCommand({
|
|
|
920
1126
|
agentsFileCount: t.agentsDir ? fileCount(t.agentsDir) : undefined
|
|
921
1127
|
}));
|
|
922
1128
|
for (const p of plans) {
|
|
923
|
-
printPlan(p, modifyLock);
|
|
1129
|
+
printPlan(p, modifyLock, lockOnly);
|
|
924
1130
|
console.log("");
|
|
925
1131
|
}
|
|
926
1132
|
if (dryRun)
|
|
927
1133
|
return;
|
|
928
|
-
if (
|
|
929
|
-
const
|
|
930
|
-
const
|
|
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?");
|
|
931
1152
|
if (!ok) {
|
|
932
1153
|
console.log("Aborted");
|
|
933
1154
|
process.exit(1);
|
|
@@ -936,7 +1157,7 @@ var removeCommand = defineCommand({
|
|
|
936
1157
|
const allowedRoots = [isGlobal ? homedir2() : dirname2(resolve3(lockPath)), homedir2()];
|
|
937
1158
|
for (const { target } of plans) {
|
|
938
1159
|
if (target.inLock) {
|
|
939
|
-
if (modifyLock) {
|
|
1160
|
+
if (lockOnly || modifyLock) {
|
|
940
1161
|
const r = removeSkillFromLock(lockPath, target.name);
|
|
941
1162
|
if (r.removed)
|
|
942
1163
|
console.log(`Removed ${q(target.name)} from skills-lock.json`);
|
|
@@ -946,6 +1167,17 @@ var removeCommand = defineCommand({
|
|
|
946
1167
|
} else {
|
|
947
1168
|
console.log(`Skipped skills-lock.json (not in lock)`);
|
|
948
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
|
+
}
|
|
949
1181
|
if (target.claudeDir) {
|
|
950
1182
|
const r = rmSkillDir(target.claudeDir, { allowedRoots });
|
|
951
1183
|
console.log(`Removed ${q(target.name)} from .claude/skills (${r.fileCount} files)`);
|
|
@@ -1146,7 +1378,7 @@ function extractClaudeMentions(entry) {
|
|
|
1146
1378
|
}
|
|
1147
1379
|
for (const m of node.matchAll(/\bsuperpowers:([a-z0-9-]+)\b/g)) {
|
|
1148
1380
|
if (m[1] !== undefined)
|
|
1149
|
-
seen.add(
|
|
1381
|
+
seen.add(m[1]);
|
|
1150
1382
|
}
|
|
1151
1383
|
});
|
|
1152
1384
|
return [...seen];
|
|
@@ -1217,7 +1449,7 @@ function* findJsonlFiles(dir, since) {
|
|
|
1217
1449
|
}
|
|
1218
1450
|
function isRecentEntry(entry, since) {
|
|
1219
1451
|
if (typeof entry !== "object" || entry === null)
|
|
1220
|
-
return
|
|
1452
|
+
return false;
|
|
1221
1453
|
const e = entry;
|
|
1222
1454
|
if (typeof e.timestamp === "string") {
|
|
1223
1455
|
const d = new Date(e.timestamp);
|
|
@@ -1225,7 +1457,7 @@ function isRecentEntry(entry, since) {
|
|
|
1225
1457
|
}
|
|
1226
1458
|
if (typeof e.ts === "number")
|
|
1227
1459
|
return new Date(e.ts * 1000) >= since;
|
|
1228
|
-
return
|
|
1460
|
+
return false;
|
|
1229
1461
|
}
|
|
1230
1462
|
|
|
1231
1463
|
// src/readers/claude.ts
|
|
@@ -1240,6 +1472,7 @@ function readClaudeUsage(options) {
|
|
|
1240
1472
|
let prevSkill = null;
|
|
1241
1473
|
const sessionAttr = new Map;
|
|
1242
1474
|
const sessionAct = new Map;
|
|
1475
|
+
const sessionMen = new Map;
|
|
1243
1476
|
for (const line of readFileSync2(file, "utf8").split(`
|
|
1244
1477
|
`)) {
|
|
1245
1478
|
if (!line.trim())
|
|
@@ -1271,7 +1504,7 @@ function readClaudeUsage(options) {
|
|
|
1271
1504
|
}
|
|
1272
1505
|
if (options.mode === "mentions") {
|
|
1273
1506
|
for (const skill of extractClaudeMentions(entry)) {
|
|
1274
|
-
|
|
1507
|
+
sessionMen.set(skill, (sessionMen.get(skill) ?? 0) + 1);
|
|
1275
1508
|
}
|
|
1276
1509
|
}
|
|
1277
1510
|
}
|
|
@@ -1281,6 +1514,9 @@ function readClaudeUsage(options) {
|
|
|
1281
1514
|
} else if (options.mode === "activations") {
|
|
1282
1515
|
for (const [k, v] of sessionAct)
|
|
1283
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);
|
|
1284
1520
|
} else if (options.mode === "merged") {
|
|
1285
1521
|
const keys = new Set([...sessionAttr.keys(), ...sessionAct.keys()]);
|
|
1286
1522
|
for (const k of keys) {
|
|
@@ -1418,19 +1654,21 @@ var SECOND_MS = 1000;
|
|
|
1418
1654
|
var MINUTE_MS = 60 * SECOND_MS;
|
|
1419
1655
|
var HOUR_MS = 60 * MINUTE_MS;
|
|
1420
1656
|
var DAY_MS = 24 * HOUR_MS;
|
|
1657
|
+
var MONTH_MS = 30 * DAY_MS;
|
|
1421
1658
|
var UNITS_MS = {
|
|
1422
1659
|
s: SECOND_MS,
|
|
1423
1660
|
m: MINUTE_MS,
|
|
1424
1661
|
h: HOUR_MS,
|
|
1425
1662
|
d: DAY_MS,
|
|
1426
|
-
w: 7 * DAY_MS
|
|
1663
|
+
w: 7 * DAY_MS,
|
|
1664
|
+
mo: MONTH_MS
|
|
1427
1665
|
};
|
|
1428
1666
|
function parsePeriod(period) {
|
|
1429
1667
|
if (period === "all")
|
|
1430
1668
|
return Number.POSITIVE_INFINITY;
|
|
1431
|
-
const match = period.match(/^(\d+)([smhdw])$/);
|
|
1669
|
+
const match = period.match(/^(\d+)(mo|[smhdw])$/);
|
|
1432
1670
|
if (!match) {
|
|
1433
|
-
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.`);
|
|
1434
1672
|
}
|
|
1435
1673
|
const unit = UNITS_MS[match[2] ?? ""] ?? 0;
|
|
1436
1674
|
return Number(match[1]) * unit;
|
|
@@ -1441,7 +1679,8 @@ function pad(n, width) {
|
|
|
1441
1679
|
return String(n).padStart(width);
|
|
1442
1680
|
}
|
|
1443
1681
|
function formatUsageRow(row) {
|
|
1444
|
-
|
|
1682
|
+
const suffix = row.installed === false ? ` ${red("(missing)")}` : "";
|
|
1683
|
+
return `${pad(row.count, row.countWidth)} ${cyan(row.name)}${suffix}`;
|
|
1445
1684
|
}
|
|
1446
1685
|
function parseAgents(agent) {
|
|
1447
1686
|
if (!agent)
|
|
@@ -1465,7 +1704,7 @@ var usageArgs = {
|
|
|
1465
1704
|
type: "string",
|
|
1466
1705
|
alias: "p",
|
|
1467
1706
|
default: "all",
|
|
1468
|
-
description: "60s, 30m, 24h, 30d, 2w, all"
|
|
1707
|
+
description: "60s, 30m, 24h, 30d, 2w, 6mo, all"
|
|
1469
1708
|
},
|
|
1470
1709
|
since: { type: "string", description: "yyyy-mm-dd, overrides --period" },
|
|
1471
1710
|
mode: {
|
|
@@ -1534,15 +1773,13 @@ async function runUsage(args) {
|
|
|
1534
1773
|
name,
|
|
1535
1774
|
count: counts.get(name) ?? 0,
|
|
1536
1775
|
tokens: rec?.frontmatterTokens,
|
|
1537
|
-
|
|
1776
|
+
installed: rec !== undefined && rec.status !== "missing"
|
|
1538
1777
|
};
|
|
1539
1778
|
});
|
|
1540
1779
|
const rows = allRows.filter((r) => r.count > 0);
|
|
1541
1780
|
rows.sort((a, b) => {
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
if (aOk !== bOk)
|
|
1545
|
-
return aOk ? -1 : 1;
|
|
1781
|
+
if (a.installed !== b.installed)
|
|
1782
|
+
return a.installed ? -1 : 1;
|
|
1546
1783
|
if (b.count !== a.count)
|
|
1547
1784
|
return b.count - a.count;
|
|
1548
1785
|
return a.name.localeCompare(b.name);
|
|
@@ -1558,7 +1795,8 @@ async function runUsage(args) {
|
|
|
1558
1795
|
skill: r.name,
|
|
1559
1796
|
count: r.count,
|
|
1560
1797
|
tokensPerSkill: r.tokens ?? null,
|
|
1561
|
-
consumption: (r.tokens ?? 0) * r.count
|
|
1798
|
+
consumption: (r.tokens ?? 0) * r.count,
|
|
1799
|
+
installed: r.installed
|
|
1562
1800
|
}))
|
|
1563
1801
|
}));
|
|
1564
1802
|
console.log(JSON.stringify(output.length === 1 ? output[0] : output, null, 2));
|
|
@@ -1577,7 +1815,7 @@ async function runUsage(args) {
|
|
|
1577
1815
|
continue;
|
|
1578
1816
|
const countWidth = Math.max(...rows.map((r) => String(r.count).length));
|
|
1579
1817
|
for (const r of rows) {
|
|
1580
|
-
console.log(formatUsageRow({ count: r.count, name: r.name, countWidth }));
|
|
1818
|
+
console.log(formatUsageRow({ count: r.count, name: r.name, countWidth, installed: r.installed }));
|
|
1581
1819
|
distinct.add(r.name);
|
|
1582
1820
|
}
|
|
1583
1821
|
grandActivations += activations;
|
|
@@ -1728,7 +1966,8 @@ var SUBCOMMAND_NAMES = new Set([
|
|
|
1728
1966
|
"cst",
|
|
1729
1967
|
"usage",
|
|
1730
1968
|
"us",
|
|
1731
|
-
"usg"
|
|
1969
|
+
"usg",
|
|
1970
|
+
"completion"
|
|
1732
1971
|
]);
|
|
1733
1972
|
function reorderRootFlagsToSubcommand(argv) {
|
|
1734
1973
|
const tail = argv.slice(2);
|
|
@@ -1754,15 +1993,16 @@ function printRootHelp() {
|
|
|
1754
1993
|
" -h, --help Show this help and exit",
|
|
1755
1994
|
" -v, --version Show version and exit",
|
|
1756
1995
|
" -g, --global Use global scope (default: false)",
|
|
1757
|
-
" -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)",
|
|
1758
1997
|
" -a, --agent Agent for `usage`: claude-code, codex (default: both)",
|
|
1759
1998
|
"",
|
|
1760
1999
|
"COMMANDS",
|
|
1761
2000
|
"",
|
|
1762
|
-
" list, ls List skills per source
|
|
1763
|
-
" remove, rm
|
|
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",
|
|
1764
2003
|
" cost, co, cst Show ambient ballast cost (per-skill frontmatter tokens) sorted desc",
|
|
1765
|
-
" 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)"
|
|
1766
2006
|
];
|
|
1767
2007
|
console.log(lines.join(`
|
|
1768
2008
|
`));
|
|
@@ -1774,6 +2014,44 @@ function isRootHelp(argv) {
|
|
|
1774
2014
|
return false;
|
|
1775
2015
|
return args.includes("--help") || args.includes("-h");
|
|
1776
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
|
+
}
|
|
1777
2055
|
function firstPositional(argv) {
|
|
1778
2056
|
for (let i = 2;i < argv.length; i++) {
|
|
1779
2057
|
const tok = argv[i];
|
|
@@ -1819,7 +2097,7 @@ var main = defineCommand({
|
|
|
1819
2097
|
return;
|
|
1820
2098
|
const interactive = process.stdout.isTTY && process.stdin.isTTY;
|
|
1821
2099
|
if (interactive) {
|
|
1822
|
-
const { runPicker } = await import("./shared/chunk-
|
|
2100
|
+
const { runPicker } = await import("./shared/chunk-ajnqh9j9.js");
|
|
1823
2101
|
const status = await runPicker({
|
|
1824
2102
|
global: args.global ?? false
|
|
1825
2103
|
});
|
|
@@ -1841,7 +2119,8 @@ var main = defineCommand({
|
|
|
1841
2119
|
cst: costCommand,
|
|
1842
2120
|
usage: usageCommand,
|
|
1843
2121
|
us: usageCommand,
|
|
1844
|
-
usg: usageCommand
|
|
2122
|
+
usg: usageCommand,
|
|
2123
|
+
completion: completionCommand
|
|
1845
2124
|
}
|
|
1846
2125
|
});
|
|
1847
2126
|
(async () => {
|
|
@@ -1849,6 +2128,10 @@ var main = defineCommand({
|
|
|
1849
2128
|
printRootHelp();
|
|
1850
2129
|
return;
|
|
1851
2130
|
}
|
|
2131
|
+
if (isRemoveHelp(process.argv)) {
|
|
2132
|
+
printRemoveHelp();
|
|
2133
|
+
return;
|
|
2134
|
+
}
|
|
1852
2135
|
if (isRootVersion(process.argv)) {
|
|
1853
2136
|
console.log(version);
|
|
1854
2137
|
return;
|