skill-guide 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +156 -248
- package/SKILL.md +4 -1
- package/package.json +1 -1
- package/skill-guide.js +268 -65
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,25 @@ All notable changes to skill-guide will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.4.0] - 2026-06-01
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Platform-scoped filtering: all modes (dashboard, doctor, find, review, recommend, share) default to the current agent's skills.
|
|
12
|
+
- Auto-detection via env vars (`CLAUDE_CODE`, `CODEX_AGENT`) or install path.
|
|
13
|
+
- `--platform <claude|codex>` flag to force a specific platform view.
|
|
14
|
+
- `--all` flag to see the full cross-platform inventory.
|
|
15
|
+
- `applyPlatformFilter()` helper for consistent platform filtering across all CLI modes.
|
|
16
|
+
- 8 new tests covering platform detection, filtering, and env var auto-detection.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- Review mode (`--review`) no longer flags cross-agent duplicates as issues — each agent needs its own skill copies.
|
|
20
|
+
- Cover slide title auto-adapts to show "Your Claude Code Skills" or "Your Codex Skills" based on filtered results.
|
|
21
|
+
- `--find` commands in Next Steps slide simplified (removed redundant `--find` flag).
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- `\n` literal rendering in Copy Review Prompt button (replaced inline string with window variable pattern).
|
|
25
|
+
- `--platform` value no longer misinterpreted as a positional `--find` argument.
|
|
26
|
+
|
|
8
27
|
## [0.3.1] - 2026-06-01
|
|
9
28
|
|
|
10
29
|
### Added
|
package/README.md
CHANGED
|
@@ -1,322 +1,230 @@
|
|
|
1
|
-
<
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
<img src="
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<picture>
|
|
4
|
+
<img src=".github/assets/banner.svg" alt="skill-guide — know what your AI agent can do before you trust it" width="800" />
|
|
5
|
+
</picture>
|
|
6
|
+
|
|
7
|
+
<br />
|
|
8
|
+
|
|
9
|
+
[](https://www.npmjs.com/package/skill-guide)
|
|
10
|
+
[](https://www.npmjs.com/package/skill-guide)
|
|
11
|
+
[](https://github.com/gtskevin/skill-guide/actions/workflows/test.yml)
|
|
12
|
+
[](LICENSE)
|
|
13
|
+
|
|
14
|
+
**Inspect, find, and review your installed Agent Skills across Codex and Claude Code.**
|
|
15
|
+
|
|
16
|
+
[Live Demo](https://gtskevin.github.io/skill-guide/) · [Quick Start](#quick-start) · [npm](https://www.npmjs.com/package/skill-guide) · [FAQ](#faq)
|
|
17
|
+
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
> [!NOTE]
|
|
23
|
+
> You installed a useful Skill from someone else. But what will it actually do? When should it activate? Do you already have another Skill for the same job? Is its description heavy enough to deserve a closer look?
|
|
17
24
|
>
|
|
18
|
-
>
|
|
19
|
-
|
|
20
|
-
<p align="center">
|
|
21
|
-
<a href="https://gtskevin.github.io/skill-guide/"><strong>Live Demo</strong></a>
|
|
22
|
-
·
|
|
23
|
-
<a href="#quick-start">Try it now</a>
|
|
24
|
-
·
|
|
25
|
-
<a href="#install-methods">Install</a>
|
|
26
|
-
·
|
|
27
|
-
<a href="#how-it-works">How it works</a>
|
|
28
|
-
</p>
|
|
25
|
+
> `skill-guide` gives you a local map before you place uncertain trust in downloaded Agent Skills.
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
npx skill-guide # ← that's it. Dashboard opens in your browser.
|
|
32
|
-
```
|
|
27
|
+
## Highlights
|
|
33
28
|
|
|
34
|
-
|
|
29
|
+
| | Capability | Why it matters |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| 🔍 | **Inventory your Skills** | See what is installed across Codex, Claude Code, cc-switch, and plugin directories. |
|
|
32
|
+
| 🎯 | **Find a Skill for a task** | Search names, descriptions, and declared triggers before installing another tool. |
|
|
33
|
+
| 📖 | **Inspect how a Skill is designed to work** | Review its source, declared tools, use cases, limitations, and document structure. |
|
|
34
|
+
| 🛡️ | **Review before you trust** | Surface local metadata signals such as sparse descriptions, duplicate sources, and estimated description tokens. |
|
|
35
|
+
| 🎯 | **Platform-scoped views** | Default to your current agent's Skills; use `--platform` or `--all` to switch scope. |
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
## Quick Start
|
|
37
38
|
|
|
38
|
-
|
|
39
|
+
> ⏱️ **Get started in 30 seconds**
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
No installation is required for the CLI:
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
| **Find** | `npx skill-guide --find <name\|query>` | Search by keyword or deep dive into a specific skill |
|
|
46
|
-
| **Doctor** | `npx skill-guide --doctor` | Environment diagnostics (broken files, duplicates, paths) |
|
|
43
|
+
```bash
|
|
44
|
+
npx skill-guide
|
|
45
|
+
```
|
|
47
46
|
|
|
48
|
-
|
|
49
|
-
- `--all` — Show skills from all platforms (default: current platform only)
|
|
50
|
-
- `--full` — Expand dashboard to include all skill details
|
|
51
|
-
- `--recommend` — Show recommendations from online directories
|
|
52
|
-
- `--share` — Generate shareable portfolio page
|
|
53
|
-
- `--no-open` — Do not open HTML in browser
|
|
47
|
+
The dashboard opens in your browser. The terminal also prints a local summary:
|
|
54
48
|
|
|
55
|
-
|
|
49
|
+
```text
|
|
50
|
+
skill-guide · 338 skills · Local profile: Collector
|
|
56
51
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
52
|
+
Health: 68/100
|
|
53
|
+
338 skills · 9/9 categories · ~20.0K description tokens
|
|
54
|
+
Sources: 59 user-directory · 280 plugin-directory
|
|
55
|
+
16 skills have sparse metadata — review descriptions and triggers
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
> [!IMPORTANT]
|
|
59
|
+
> Token numbers are rough estimates for Skill descriptions, not measured runtime cost. Health output is a local review prompt, not a verdict on quality or safety.
|
|
64
60
|
|
|
65
|
-
##
|
|
61
|
+
## Demo
|
|
66
62
|
|
|
67
|
-
|
|
63
|
+
<div align="center">
|
|
64
|
+
<img src="demo.gif" alt="skill-guide scans installed Agent Skills and opens an HTML dashboard" width="760" />
|
|
65
|
+
</div>
|
|
68
66
|
|
|
69
67
|
<table>
|
|
70
68
|
<tr>
|
|
71
|
-
<td><img src="demo-cover.png" alt="
|
|
72
|
-
<td><img src="demo-categories.png" alt="
|
|
69
|
+
<td><img src="demo-cover.png" alt="skill-guide cover slide with local Skill sources" width="400" /></td>
|
|
70
|
+
<td><img src="demo-categories.png" alt="skill-guide category map grouped by Skill type" width="400" /></td>
|
|
73
71
|
</tr>
|
|
74
72
|
<tr>
|
|
75
|
-
<td align="center"><em>
|
|
76
|
-
<td align="center"><em>Category map
|
|
73
|
+
<td align="center"><em>Local inventory and sources</em></td>
|
|
74
|
+
<td align="center"><em>Category map</em></td>
|
|
77
75
|
</tr>
|
|
78
76
|
<tr>
|
|
79
|
-
<td><img src="demo-highlights.png" alt="
|
|
80
|
-
<td><img src="demo-reference.png" alt="
|
|
77
|
+
<td><img src="demo-highlights.png" alt="skill-guide review highlights for installed Skills" width="400" /></td>
|
|
78
|
+
<td><img src="demo-reference.png" alt="skill-guide searchable reference table" width="400" /></td>
|
|
81
79
|
</tr>
|
|
82
80
|
<tr>
|
|
83
|
-
<td align="center"><em>
|
|
84
|
-
<td align="center"><em>Quick reference
|
|
81
|
+
<td align="center"><em>Review highlights</em></td>
|
|
82
|
+
<td align="center"><em>Quick reference</em></td>
|
|
85
83
|
</tr>
|
|
86
84
|
</table>
|
|
87
85
|
|
|
88
|
-
|
|
86
|
+
## What It Can Tell You
|
|
89
87
|
|
|
90
|
-
|
|
91
|
-
npx skill-guide
|
|
92
|
-
```
|
|
88
|
+
`skill-guide` is a local inventory, discovery, and **pre-use review** tool. It deliberately separates evidence from inference.
|
|
93
89
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
📍 Local profile: based only on the current scan
|
|
103
|
-
|
|
104
|
-
🛡️ Security Review [medium]
|
|
105
|
-
Review skills with security flags manually
|
|
106
|
-
📦 Budget Overage [high]
|
|
107
|
-
Review longer descriptions when the local reference budget is exceeded
|
|
108
|
-
|
|
109
|
-
Token Efficiency ████████░░ 80/100
|
|
110
|
-
Organization ██████████ 100/100
|
|
111
|
-
Security ████░░░░░░ 40/100
|
|
112
|
-
Freshness ██████████ 100/100
|
|
113
|
-
Budget Control ██░░░░░░░░ 21/100
|
|
114
|
-
```
|
|
90
|
+
| Question | What `skill-guide` does today | Boundary |
|
|
91
|
+
|---|---|---|
|
|
92
|
+
| What Skills do I have? | Scans local user, system, cc-switch, and plugin directories. Defaults to the current agent's Skills. | Reports what is visible on the current machine. Use `--all` for cross-platform inventory. |
|
|
93
|
+
| Do I already have a Skill for this task? | Searches names, descriptions, and declared triggers with `--find`. | Returns metadata matches, not a semantic guarantee. |
|
|
94
|
+
| How is this Skill designed to work? | Shows source, declared tools, use cases, limitations, and document sections. | Explains documented intent; it does not execute an audit of every command. |
|
|
95
|
+
| Could Skill descriptions be heavy? | Estimates description tokens and highlights longer descriptions. | Does not measure runtime tokens, API cost, or context injection behavior. |
|
|
96
|
+
| Was a Skill actually invoked? | Not yet. | Runtime invocation tracking requires logging integrations. |
|
|
97
|
+
| Is the output good or cost-effective? | Not yet. | Result quality and actual cost require runtime evidence and evaluation. |
|
|
115
98
|
|
|
116
|
-
|
|
99
|
+
## Commands
|
|
117
100
|
|
|
118
|
-
|
|
101
|
+
| Goal | Command |
|
|
102
|
+
|---|---|
|
|
103
|
+
| Open your local dashboard | `npx skill-guide` |
|
|
104
|
+
| Find a Skill for a task | `npx skill-guide --find security` |
|
|
105
|
+
| Inspect one Skill | `npx skill-guide --find test-driven-development` |
|
|
106
|
+
| Diagnose local setup | `npx skill-guide --doctor` |
|
|
107
|
+
| Review directory mentions and same-category candidates | `npx skill-guide --recommend` |
|
|
108
|
+
| Generate a shareable local profile | `npx skill-guide --share` |
|
|
109
|
+
| Return structured scanner data | `npx skill-guide --format json` |
|
|
110
|
+
| Focus on one agent's Skills | `npx skill-guide --platform claude` |
|
|
111
|
+
| Include every detected platform | `npx skill-guide --all` |
|
|
119
112
|
|
|
120
|
-
|
|
121
|
-
```bash
|
|
122
|
-
npx skill-guide --open
|
|
123
|
-
```
|
|
113
|
+
### Example Prompts
|
|
124
114
|
|
|
125
|
-
|
|
126
|
-
```bash
|
|
127
|
-
npx skills add gtskevin/skill-guide
|
|
128
|
-
```
|
|
115
|
+
When `skill-guide` is installed as an Agent Skill, try:
|
|
129
116
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
npx skill-guide --find tdd # Deep-dive one skill
|
|
135
|
-
npx skill-guide --full --open # Generate a full manual
|
|
136
|
-
npx skill-guide --doctor # Diagnose your setup
|
|
117
|
+
```text
|
|
118
|
+
What Skills do I already have for code review?
|
|
119
|
+
Show me how the test-driven-development Skill is designed to work.
|
|
120
|
+
帮我看看我有哪些 Codex Skills,并找出描述信息较少、值得人工复核的项目。
|
|
137
121
|
```
|
|
138
122
|
|
|
139
|
-
## Install
|
|
123
|
+
## Install as an Agent Skill
|
|
124
|
+
|
|
125
|
+
For Claude Code:
|
|
140
126
|
|
|
141
127
|
```bash
|
|
142
|
-
# Claude Code: npx skills (recommended)
|
|
143
128
|
npx skills add gtskevin/skill-guide
|
|
129
|
+
```
|
|
144
130
|
|
|
145
|
-
|
|
146
|
-
git clone https://github.com/gtskevin/skill-guide.git
|
|
147
|
-
ln -s $(pwd)/skill-guide ~/.claude/skills/skill-guide
|
|
131
|
+
For Codex:
|
|
148
132
|
|
|
149
|
-
|
|
150
|
-
mkdir -p ~/.claude/skills/skill-guide
|
|
151
|
-
curl -sL https://github.com/gtskevin/skill-guide/archive/refs/heads/main.tar.gz | tar xz --strip-components=1 -C ~/.claude/skills/skill-guide
|
|
152
|
-
|
|
153
|
-
# Codex: manual symlink
|
|
133
|
+
```bash
|
|
154
134
|
git clone https://github.com/gtskevin/skill-guide.git
|
|
155
135
|
mkdir -p "${CODEX_HOME:-$HOME/.codex}/skills"
|
|
156
136
|
ln -s "$(pwd)/skill-guide" "${CODEX_HOME:-$HOME/.codex}/skills/skill-guide"
|
|
157
137
|
```
|
|
158
138
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
### Run as a CLI
|
|
162
|
-
```bash
|
|
163
|
-
npx skill-guide --open
|
|
164
|
-
npx skill-guide --search security --open
|
|
165
|
-
npx skill-guide --skill test-driven-development --open
|
|
166
|
-
npx skill-guide --share --open # Share your skill stack
|
|
167
|
-
npx skill-guide --recommend --open # Review directory mentions and same-category candidates
|
|
168
|
-
npx skill-guide # Health dashboard with personality & radar chart
|
|
169
|
-
npx skill-guide --wrapped --open # Compatibility alias for the local profile
|
|
170
|
-
npx skill-guide --format json
|
|
171
|
-
npx skill-guide --doctor
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
### Discover all your skills
|
|
175
|
-
```
|
|
176
|
-
/skill-guide
|
|
177
|
-
```
|
|
178
|
-
Or say: "What skills do I have?" / "帮我看看我有哪些技能"
|
|
179
|
-
|
|
180
|
-
In Codex, you can also say: "What Codex skills do I have?"
|
|
181
|
-
|
|
182
|
-
### Deep-dive one skill
|
|
183
|
-
```
|
|
184
|
-
/skill-guide investigate
|
|
185
|
-
```
|
|
186
|
-
Or say: "Tell me about the TDD skill" / "介绍一下 investigate 技能"
|
|
187
|
-
|
|
188
|
-
### Find the right skill
|
|
189
|
-
```
|
|
190
|
-
Which skill should I use for code review?
|
|
191
|
-
```
|
|
192
|
-
Or: "帮我推荐一个做测试的技能"
|
|
193
|
-
|
|
194
|
-
### Generate a full manual
|
|
195
|
-
```
|
|
196
|
-
/skill-guide all
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
### Share your skill stack
|
|
200
|
-
```bash
|
|
201
|
-
npx skill-guide --share --open # Generate portfolio page
|
|
202
|
-
npx skill-guide --share --user @gtskevin --open # With personalized tag
|
|
203
|
-
```
|
|
139
|
+
<details>
|
|
140
|
+
<summary>More installation options</summary>
|
|
204
141
|
|
|
205
|
-
### Get recommendations
|
|
206
142
|
```bash
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
```
|
|
143
|
+
# Claude Code: manual symlink
|
|
144
|
+
git clone https://github.com/gtskevin/skill-guide.git
|
|
145
|
+
ln -s "$(pwd)/skill-guide" ~/.claude/skills/skill-guide
|
|
211
146
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
npx skill-guide --health --lang zh --open # Chinese UI
|
|
147
|
+
# Claude Code: direct download
|
|
148
|
+
mkdir -p ~/.claude/skills/skill-guide
|
|
149
|
+
curl -sL https://github.com/gtskevin/skill-guide/archive/refs/heads/main.tar.gz \
|
|
150
|
+
| tar xz --strip-components=1 -C ~/.claude/skills/skill-guide
|
|
217
151
|
```
|
|
218
152
|
|
|
219
|
-
|
|
220
|
-
- **Health Score** — 0-100 rating of your skill library's health
|
|
221
|
-
- **Personality Analysis** — Are you a Collector, Minimalist, Security Expert, or Specialist?
|
|
222
|
-
- **Five-Dimension Radar Chart** — Token Efficiency, Organization, Security, Freshness, Budget Control
|
|
223
|
-
- **Review Prompts** — Local candidates based on scanned skill metadata
|
|
224
|
-
- **Token Estimate** — Approximate description cost before a conversation starts
|
|
225
|
-
- **One-Click Share** — Copy report to clipboard for sharing
|
|
153
|
+
</details>
|
|
226
154
|
|
|
227
|
-
|
|
155
|
+
## Platform Support
|
|
228
156
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
157
|
+
| Platform | Status | Scanned paths |
|
|
158
|
+
|---|---|---|
|
|
159
|
+
| Claude Code | Supported | `~/.claude/skills`, `~/.claude/plugins/marketplaces` |
|
|
160
|
+
| Codex | Supported | `~/.codex/skills`, `$CODEX_HOME/skills`, Codex plugin cache |
|
|
161
|
+
| OpenAI system Skills | Supported | `$CODEX_HOME/skills/.system` |
|
|
162
|
+
| cc-switch | Supported | `~/.cc-switch/skills` |
|
|
163
|
+
| Agent Skills | Compatible | Standard `SKILL.md` folders |
|
|
234
164
|
|
|
235
|
-
## How
|
|
165
|
+
## How It Works
|
|
236
166
|
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
├── demo.html # Demo presentation (this is what you see above)
|
|
245
|
-
└── LICENSE # MIT
|
|
167
|
+
```mermaid
|
|
168
|
+
flowchart LR
|
|
169
|
+
A["Local Skill directories"] --> B["scan-skills.js"]
|
|
170
|
+
B --> C["Parse metadata and document sections"]
|
|
171
|
+
C --> D["skill-guide.js"]
|
|
172
|
+
D --> E["Browser-ready HTML dashboard"]
|
|
173
|
+
C --> F["Search, doctor, and review prompts"]
|
|
246
174
|
```
|
|
247
175
|
|
|
248
|
-
|
|
249
|
-
2. `skill-guide.js` turns scanner JSON into deterministic HTML slides with scroll-snap navigation, keyboard controls, and animations
|
|
250
|
-
3. `SKILL.md` lets Claude Code and Codex invoke the same CLI from natural language
|
|
251
|
-
4. Output opens in your browser — zero config, zero dependencies
|
|
176
|
+
The scanner uses Node.js built-ins only:
|
|
252
177
|
|
|
253
|
-
|
|
178
|
+
1. Scan local Skill directories and plugin caches.
|
|
179
|
+
2. Parse frontmatter, descriptions, triggers, declared tools, and selected document sections.
|
|
180
|
+
3. Estimate description tokens and surface deterministic local review prompts.
|
|
181
|
+
4. Render a standalone HTML dashboard that opens in your browser.
|
|
254
182
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
| `--list` | Discovery | Name + description + category |
|
|
258
|
-
| `--skill <name>` | Deep-dive | Full metadata + sections + key paragraphs |
|
|
259
|
-
| `--search <query>` | Recommendations | Matching skills with full data |
|
|
260
|
-
| `--full` | Complete manual | All skills with full data |
|
|
261
|
-
| `--refresh` | Force re-scan | Ignores 5-min cache |
|
|
183
|
+
> [!WARNING]
|
|
184
|
+
> `skill-guide` does not silently delete, install, or modify your Skills. Review prompts require human judgment.
|
|
262
185
|
|
|
263
|
-
|
|
186
|
+
## FAQ
|
|
264
187
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
| `--open` | Open the generated HTML in your browser |
|
|
268
|
-
| `--output <file>` | Save HTML to a specific path |
|
|
269
|
-
| `--format html,json` | Choose HTML slides or raw scanner JSON |
|
|
270
|
-
| `--search <query>` | Generate recommendations for a task |
|
|
271
|
-
| `--skill <name>` | Generate a deep-dive for one skill |
|
|
272
|
-
| `--full` | Generate a complete manual |
|
|
273
|
-
| `--share` | Generate a shareable portfolio HTML |
|
|
274
|
-
| `--user <name>` | Add personalized tag to share page |
|
|
275
|
-
| `--recommend` | Show directory mentions and same-category review candidates |
|
|
276
|
-
| `--health` | Generate health dashboard with local profile, radar chart, and review prompts |
|
|
277
|
-
| `--lang <code>` | UI language (`en` or built-in `zh`) |
|
|
278
|
-
| `--doctor` | Check paths, sources, and scan counts |
|
|
188
|
+
<details>
|
|
189
|
+
<summary>Does skill-guide send my local Skills to a server?</summary>
|
|
279
190
|
|
|
280
|
-
|
|
191
|
+
No. Core scanning and dashboard generation run locally with Node.js built-ins. The optional `--recommend` command reads public online directories to show directory mentions.
|
|
192
|
+
</details>
|
|
281
193
|
|
|
282
|
-
|
|
194
|
+
<details>
|
|
195
|
+
<summary>Can it tell whether Claude Code or Codex actually invoked a Skill?</summary>
|
|
283
196
|
|
|
284
|
-
|
|
197
|
+
Not yet. The current release scans local metadata. Reliable invocation tracking requires runtime logs or platform integrations.
|
|
198
|
+
</details>
|
|
285
199
|
|
|
286
|
-
|
|
200
|
+
<details>
|
|
201
|
+
<summary>Does the token estimate equal my real API cost?</summary>
|
|
287
202
|
|
|
288
|
-
|
|
203
|
+
No. It is a rough estimate based on description text. Runtime token usage depends on the platform, model, loaded context, and execution path.
|
|
204
|
+
</details>
|
|
289
205
|
|
|
290
|
-
|
|
206
|
+
<details>
|
|
207
|
+
<summary>Does a health warning mean I should delete a Skill?</summary>
|
|
291
208
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
- **Other languages** — agent summary in the user's language; dashboard labels remain English
|
|
209
|
+
No. Warnings are review candidates. Read the Skill, check its source, and confirm your actual needs before changing anything.
|
|
210
|
+
</details>
|
|
295
211
|
|
|
296
|
-
|
|
212
|
+
<details>
|
|
213
|
+
<summary>Which languages are supported?</summary>
|
|
297
214
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
- **Zero dependencies** — pure Node.js with `fs`, `path`, `os`. No `npm install` needed
|
|
301
|
-
- **Beautiful output** — scroll-snap slides with keyboard nav, animations, and responsive design
|
|
302
|
-
- **Bilingual dashboard** — English by default, with built-in Chinese labels via `--lang zh`
|
|
303
|
-
- **Smart caching** — 5-minute TTL so repeated queries are instant
|
|
304
|
-
- **5 seconds to "wow"** — `npx skill-guide --open` is all you need
|
|
215
|
+
Dashboard labels are built in for English and Chinese. Agents can summarize the result in other languages without rewriting the generated HTML.
|
|
216
|
+
</details>
|
|
305
217
|
|
|
306
218
|
## Contributing
|
|
307
219
|
|
|
308
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md) for
|
|
309
|
-
|
|
310
|
-
## Roadmap
|
|
220
|
+
Issues and pull requests are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for the zero-dependency constraint and test commands.
|
|
311
221
|
|
|
312
|
-
|
|
313
|
-
- [x] `--health` — health dashboard with local profile, radar chart, and review prompts
|
|
314
|
-
- [ ] Gemini CLI skill scanning (`~/.gemini/skills`)
|
|
315
|
-
- [ ] `--diff` — show recently added/removed skills since last scan
|
|
316
|
-
- [ ] `--export markdown` — output a Markdown table for pasting into issues and docs
|
|
222
|
+
## License
|
|
317
223
|
|
|
318
|
-
|
|
224
|
+
[MIT](LICENSE)
|
|
319
225
|
|
|
320
|
-
|
|
226
|
+
---
|
|
321
227
|
|
|
322
|
-
|
|
228
|
+
<div align="center">
|
|
229
|
+
<sub>Built with care by <a href="https://github.com/gtskevin">@gtskevin</a> for developers who want to understand Agent Skills before trusting them.</sub>
|
|
230
|
+
</div>
|
package/SKILL.md
CHANGED
|
@@ -29,12 +29,15 @@ Use the bundled zero-dependency CLI to scan local skill metadata and generate a
|
|
|
29
29
|
| Generate a full manual | `node <skill-dir>/skill-guide.js --full` |
|
|
30
30
|
| Return structured data | `node <skill-dir>/skill-guide.js --format json` |
|
|
31
31
|
|
|
32
|
-
Use `--refresh` when the user expects newly installed or removed skills to appear. Use `--all` only when the user wants a cross-platform inventory.
|
|
32
|
+
Use `--refresh` when the user expects newly installed or removed skills to appear. Use `--all` only when the user wants a cross-platform inventory. Add `--platform claude` or `--platform codex` to force a specific platform view regardless of auto-detection.
|
|
33
33
|
|
|
34
34
|
## Guardrails
|
|
35
35
|
|
|
36
36
|
- The scanner reads local metadata; it does not measure actual usage frequency.
|
|
37
37
|
- Same-category suggestions and directory mentions require human review before installing, editing, or deleting anything.
|
|
38
38
|
- Never delete skills, install packages, or publish results based only on a generated score.
|
|
39
|
+
- The default dashboard includes a "Review Candidates" slide with evidence-based findings and a Copy Prompt button. The CLI only prepares evidence and questions — semantic judgment comes from the agent. Always wait for agent assessment before acting on any finding.
|
|
40
|
+
- The `--review --format json` flag outputs a structured brief for programmatic agent consumption (no HTML).
|
|
39
41
|
- Built-in dashboard labels support English and Chinese. For other languages, summarize the result in the user's language without rewriting generated HTML unless explicitly requested.
|
|
40
42
|
- If the CLI is unavailable, report the missing file instead of silently replacing its behavior.
|
|
43
|
+
- All modes default to the current platform (auto-detected from env vars or install path). Cross-agent duplicates are normal — each agent needs its own skill copies. Use `--all` to see the full inventory across all agents.
|
package/package.json
CHANGED
package/skill-guide.js
CHANGED
|
@@ -27,6 +27,7 @@ function usage() {
|
|
|
27
27
|
' skill-guide # Dashboard: personality, radar, insights (opens HTML)',
|
|
28
28
|
' skill-guide --find <name|query> # Deep dive or search (opens HTML)',
|
|
29
29
|
' skill-guide --doctor # Quick environment diagnostic',
|
|
30
|
+
' skill-guide --review --format json # Structured review brief for agents',
|
|
30
31
|
'',
|
|
31
32
|
'Options:',
|
|
32
33
|
' --output <file> Write to file instead of default',
|
|
@@ -34,6 +35,7 @@ function usage() {
|
|
|
34
35
|
' --lang en|zh UI language',
|
|
35
36
|
' --refresh Force re-scan (ignore cache)',
|
|
36
37
|
' --all Show skills from all platforms (default: current platform)',
|
|
38
|
+
' --platform <name> Force platform: claude | codex',
|
|
37
39
|
' --no-open Do not open HTML in browser',
|
|
38
40
|
'',
|
|
39
41
|
'Examples:',
|
|
@@ -41,6 +43,7 @@ function usage() {
|
|
|
41
43
|
' npx skill-guide --find investigate # Deep dive into a skill',
|
|
42
44
|
' npx skill-guide --find security # Search for security skills',
|
|
43
45
|
' npx skill-guide --doctor # Check for issues',
|
|
46
|
+
' npx skill-guide --review --format json # Review brief for agents',
|
|
44
47
|
].join('\n');
|
|
45
48
|
}
|
|
46
49
|
|
|
@@ -103,6 +106,16 @@ const LABELS = {
|
|
|
103
106
|
gapHint: '{action}',
|
|
104
107
|
scatteredSkills: 'Scattered skills, no idea what you have?',
|
|
105
108
|
manySkillsPain: '{count}+ skills but no idea what you have?',
|
|
109
|
+
reviewBrief: 'Skill Review Brief',
|
|
110
|
+
reviewItems: 'review items',
|
|
111
|
+
reviewCopyPrompt: 'Copy Review Prompt',
|
|
112
|
+
reviewPasteToAgent: 'Paste to your agent for semantic review',
|
|
113
|
+
reviewNoItems: 'No review items found. Your skill stack looks clean!',
|
|
114
|
+
reviewSecurity: 'Security Flags',
|
|
115
|
+
reviewDuplicates: 'Duplicate Candidates',
|
|
116
|
+
reviewOverlap: 'Category Overlap',
|
|
117
|
+
reviewMalformed: 'Malformed Skills',
|
|
118
|
+
reviewBudget: 'Budget Overflow',
|
|
106
119
|
},
|
|
107
120
|
zh: {
|
|
108
121
|
yourAgentSkills: '你的 Agent Skills 技能库',
|
|
@@ -159,6 +172,16 @@ const LABELS = {
|
|
|
159
172
|
gapHint: '{action}',
|
|
160
173
|
scatteredSkills: '技能散落,不知道自己有什么?',
|
|
161
174
|
manySkillsPain: '{count}+ 个技能但不知道自己有什么?',
|
|
175
|
+
reviewBrief: '技能审查简报',
|
|
176
|
+
reviewItems: '个审查项',
|
|
177
|
+
reviewCopyPrompt: '复制审查提示词',
|
|
178
|
+
reviewPasteToAgent: '粘贴给 Agent 进行语义审查',
|
|
179
|
+
reviewNoItems: '没有发现需要审查的项目,技能栈看起来很干净!',
|
|
180
|
+
reviewSecurity: '安全标记',
|
|
181
|
+
reviewDuplicates: '重复候选',
|
|
182
|
+
reviewOverlap: '类别重叠',
|
|
183
|
+
reviewMalformed: '配置不完整',
|
|
184
|
+
reviewBudget: '预算溢出',
|
|
162
185
|
},
|
|
163
186
|
};
|
|
164
187
|
|
|
@@ -386,6 +409,7 @@ function te(text) {
|
|
|
386
409
|
function parseMode() {
|
|
387
410
|
if (hasFlag('--help') || hasFlag('-h')) return { mode: 'help' };
|
|
388
411
|
if (hasFlag('--doctor')) return { mode: 'doctor' };
|
|
412
|
+
if (hasFlag('--review')) return { mode: 'review' };
|
|
389
413
|
if (hasFlag('--recommend')) return { mode: 'recommend' };
|
|
390
414
|
if (hasFlag('--share')) return { mode: 'share' };
|
|
391
415
|
|
|
@@ -394,7 +418,7 @@ function parseMode() {
|
|
|
394
418
|
if (find) return { mode: 'find', value: find };
|
|
395
419
|
|
|
396
420
|
// Positional arg: treat as --find
|
|
397
|
-
const valueFlags = new Set(['--output', '--find', '--search', '--skill', '--format', '--lang', '--user']);
|
|
421
|
+
const valueFlags = new Set(['--output', '--find', '--search', '--skill', '--format', '--lang', '--user', '--platform']);
|
|
398
422
|
const positional = args.find((arg, index) => !arg.startsWith('-') && !valueFlags.has(args[index - 1]));
|
|
399
423
|
if (positional) return { mode: 'find', value: positional };
|
|
400
424
|
|
|
@@ -407,6 +431,8 @@ function scannerArgsFor(mode) {
|
|
|
407
431
|
|
|
408
432
|
if (mode.mode === 'list' || mode.mode === 'doctor') {
|
|
409
433
|
scannerArgs.push('--list');
|
|
434
|
+
} else if (mode.mode === 'review') {
|
|
435
|
+
scannerArgs.push('--full');
|
|
410
436
|
} else if (mode.mode === 'find') {
|
|
411
437
|
// Try as skill first, fall back to search
|
|
412
438
|
scannerArgs.push('--skill', mode.value);
|
|
@@ -701,35 +727,57 @@ function renderInsightDashboardSlide(skills) {
|
|
|
701
727
|
function renderCleanupSlide(skills) {
|
|
702
728
|
const isZh = lang() === 'zh';
|
|
703
729
|
|
|
730
|
+
// Build review brief for evidence-based candidates
|
|
731
|
+
const data = { skills };
|
|
732
|
+
const details = doctorDetails(data);
|
|
733
|
+
const brief = buildReviewBrief(data, details);
|
|
734
|
+
const copyPrompt = buildCopyPrompt(brief);
|
|
735
|
+
const promptId = `rp-${crypto.randomUUID().slice(0, 8)}`;
|
|
736
|
+
|
|
704
737
|
// Source breakdown
|
|
705
738
|
const userSkills = skills.filter(s => (s.sources || []).some(src => ['claude-user', 'codex-user', 'cc-switch'].includes(src)));
|
|
706
739
|
const pluginSkills = skills.filter(s => (s.sources || []).some(src => ['claude-plugin', 'codex-plugin'].includes(src)));
|
|
707
740
|
const systemSkills = skills.filter(s => (s.sources || []).includes('openai-system'));
|
|
708
741
|
|
|
709
|
-
// Duplicates (same name in user + plugin)
|
|
710
|
-
const nameMap = {};
|
|
711
|
-
for (const s of skills) {
|
|
712
|
-
if (!nameMap[s.name]) nameMap[s.name] = new Set();
|
|
713
|
-
for (const src of (s.sources || [])) nameMap[s.name].add(src);
|
|
714
|
-
}
|
|
715
|
-
const duplicates = Object.entries(nameMap)
|
|
716
|
-
.filter(([, srcs]) =>
|
|
717
|
-
[...srcs].some(s => ['claude-user','codex-user','cc-switch'].includes(s)) &&
|
|
718
|
-
[...srcs].some(s => ['claude-plugin','codex-plugin'].includes(s))
|
|
719
|
-
)
|
|
720
|
-
.map(([name]) => name);
|
|
721
|
-
|
|
722
|
-
// Under-configured
|
|
723
|
-
const wrapped = computeWrappedStats(skills, computeHealthStats(skills));
|
|
724
|
-
const dormant = wrapped.untappedCount || 0;
|
|
725
|
-
const dormantPct = skills.length > 0 ? Math.round((dormant / skills.length) * 100) : 0;
|
|
726
|
-
|
|
727
742
|
const userPct = skills.length > 0 ? Math.round((userSkills.length / skills.length) * 100) : 0;
|
|
728
743
|
const pluginPct = skills.length > 0 ? Math.round((pluginSkills.length / skills.length) * 100) : 0;
|
|
729
744
|
|
|
745
|
+
const hasReviewItems = brief.totalReviewItems > 0;
|
|
746
|
+
const severityColor = { high: '#ef4444', medium: '#f59e0b', low: '#6b7280', info: '#818cf8' };
|
|
747
|
+
const typeIcon = { security: '🔴', duplicate: '🟡', malformed: '⚪', overlap: '🟣', budget: '🔵' };
|
|
748
|
+
const typeLabel = {
|
|
749
|
+
security: isZh ? '安全标记' : 'Security flags',
|
|
750
|
+
duplicate: isZh ? '重复候选' : 'Duplicate candidates',
|
|
751
|
+
malformed: isZh ? '配置不完整' : 'Malformed',
|
|
752
|
+
overlap: isZh ? '类别重叠' : 'Category overlap',
|
|
753
|
+
budget: isZh ? '预算溢出' : 'Budget overflow',
|
|
754
|
+
};
|
|
755
|
+
|
|
756
|
+
// Build review cards by type
|
|
757
|
+
const typeOrder = ['security', 'duplicate', 'malformed', 'overlap', 'budget'];
|
|
758
|
+
let reviewCards = '';
|
|
759
|
+
for (const type of typeOrder) {
|
|
760
|
+
const typeItems = brief.items.filter(i => i.type === type);
|
|
761
|
+
if (typeItems.length === 0) continue;
|
|
762
|
+
const itemsHtml = typeItems.slice(0, 3).map(item => `
|
|
763
|
+
<div style="padding:8px 0;border-bottom:1px solid var(--border)">
|
|
764
|
+
<div style="display:flex;align-items:center;gap:8px;margin-bottom:4px">
|
|
765
|
+
<span style="font-size:11px;font-weight:700;color:${severityColor[item.severity]};background:${severityColor[item.severity]}22;padding:2px 6px;border-radius:4px">${item.severity.toUpperCase()}</span>
|
|
766
|
+
<span style="font-size:13px;font-weight:600">${item.skills.map(s => escapeHtml(s)).join(', ')}</span>
|
|
767
|
+
</div>
|
|
768
|
+
<p style="font-size:12px;color:var(--muted);margin:0">${escapeHtml(truncate(item.evidence, 80))}</p>
|
|
769
|
+
</div>`).join('');
|
|
770
|
+
const moreCount = typeItems.length - 3;
|
|
771
|
+
reviewCards += `<div style="background:var(--card);border:1px solid var(--border);border-radius:var(--r);padding:12px 16px;max-width:580px;margin:0 auto 10px;text-align:left">
|
|
772
|
+
<p style="margin:0 0 6px;font-weight:600;font-size:13px">${typeIcon[type]} ${typeLabel[type]} (${typeItems.length})</p>
|
|
773
|
+
${itemsHtml}
|
|
774
|
+
${moreCount > 0 ? `<p style="font-size:11px;color:var(--muted);margin:4px 0 0">+ ${moreCount} more</p>` : ''}
|
|
775
|
+
</div>`;
|
|
776
|
+
}
|
|
777
|
+
|
|
730
778
|
return `<section class="slide">
|
|
731
779
|
<div class="rv center">
|
|
732
|
-
<div class="kicker" data-i18n="label">${isZh ? '
|
|
780
|
+
<div class="kicker" data-i18n="label">${isZh ? '复核候选' : 'REVIEW CANDIDATES'}</div>
|
|
733
781
|
<h2>${isZh ? '你的技能从哪来的?' : 'Where did your skills come from?'}</h2>
|
|
734
782
|
<div class="stats" style="margin:20px 0">
|
|
735
783
|
<div class="stat" style="border-color:var(--accent)"><b>${userSkills.length}</b><span>${isZh ? '用户目录来源' : 'user-directory sources'}</span></div>
|
|
@@ -741,36 +789,21 @@ function renderCleanupSlide(skills) {
|
|
|
741
789
|
<div style="background:var(--ab);height:100%;width:${pluginPct}%" title="${isZh ? '插件目录来源' : 'Plugin-directory sources'}"></div>
|
|
742
790
|
</div>
|
|
743
791
|
|
|
744
|
-
${
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
<code style="flex:1;padding:4px 8px;background:var(--bg);border-radius:4px;font-size:12px;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${escapeHtml(d.dir)}</code>
|
|
760
|
-
<code style="padding:4px 8px;background:var(--bg);border-radius:4px;font-size:12px;color:var(--accent2);cursor:pointer;white-space:nowrap" onclick="copyText('Please review whether the skill at ${d.dir} duplicates a plugin copy. Do not delete anything until I confirm.')">📋 copy</code>
|
|
761
|
-
</div>`).join('')}
|
|
762
|
-
<p style="font-size:12px;color:var(--muted);margin:8px 0 0;font-style:italic">${isZh
|
|
763
|
-
? '💡 复制后粘贴给 Agent,让它先复核来源;确认前不要删除'
|
|
764
|
-
: '💡 Paste to your agent for source review; do not delete anything until you confirm'}</p>
|
|
765
|
-
</div>`;
|
|
766
|
-
})() : ''}
|
|
767
|
-
|
|
768
|
-
${dormant > 0 ? `<div style="background:var(--card);border:1px solid var(--border);border-radius:var(--r);padding:16px;max-width:580px;margin:0 auto;text-align:left">
|
|
769
|
-
<p style="margin:0 0 8px;color:var(--muted);font-weight:600;font-size:14px">📋 ${isZh ? `${dormant} 个配置不完整(${dormantPct}%)` : `${dormant} under-configured (${dormantPct}%)`}</p>
|
|
770
|
-
<p style="font-size:13px;color:var(--muted);margin:0">${isZh
|
|
771
|
-
? '这些技能缺少描述或触发词,Agent 可能更难发现它们。请先复核内容和使用情况。'
|
|
772
|
-
: 'These lack descriptions or triggers, so agents may have more difficulty discovering them. Review content and usage first.'}</p>
|
|
773
|
-
</div>` : ''}
|
|
792
|
+
${hasReviewItems ? `
|
|
793
|
+
<div style="max-width:580px;margin:0 auto">
|
|
794
|
+
<p style="font-size:13px;color:var(--muted);margin:0 0 12px">${isZh
|
|
795
|
+
? `${brief.totalReviewItems} 个候选需要复核 — 点击下方按钮生成审查提示词,粘贴给你的 Agent 进行语义判断`
|
|
796
|
+
: `${brief.totalReviewItems} candidates to review — click below to generate a review prompt, paste it to your agent for semantic judgment`}</p>
|
|
797
|
+
${reviewCards}
|
|
798
|
+
<div style="text-align:center;margin-top:12px">
|
|
799
|
+
<button onclick="copyText(window['${promptId}'])" style="background:var(--accent);color:#0F172A;border:none;border-radius:var(--r);padding:10px 20px;font-size:14px;font-weight:600;cursor:pointer">${isZh ? '📋 复制审查提示词' : '📋 Copy Review Prompt'}</button>
|
|
800
|
+
<p style="font-size:11px;color:var(--muted);margin:8px 0 0;font-style:italic">${isZh
|
|
801
|
+
? 'Agent 会对每项回复 CONFIRM / DISMISS / SUGGEST,确认前不要删除任何东西'
|
|
802
|
+
: 'Agent responds CONFIRM / DISMISS / SUGGEST per item; do not delete anything until you confirm'}</p>
|
|
803
|
+
</div>
|
|
804
|
+
<script>window['${promptId}']=${JSON.stringify(copyPrompt)};</script>
|
|
805
|
+
</div>
|
|
806
|
+
` : `<p style="font-size:14px;color:var(--accent)">${isZh ? '没有发现需要复核的候选,技能栈看起来很干净!' : 'No review candidates found. Your skill stack looks clean!'}</p>`}
|
|
774
807
|
</div>
|
|
775
808
|
</section>`;
|
|
776
809
|
}
|
|
@@ -780,8 +813,8 @@ function renderNextStepsSlide(skills) {
|
|
|
780
813
|
const sample = (skills || []).filter(s => (s.description || '').length > 100).slice(0, 3);
|
|
781
814
|
const sampleName = sample.length > 0 ? sample[0].name : 'investigate';
|
|
782
815
|
const sampleName2 = sample.length > 1 ? sample[1].name : 'security-audit';
|
|
783
|
-
const searchCmd = `npx skill-guide
|
|
784
|
-
const skillCmd = `npx skill-guide
|
|
816
|
+
const searchCmd = `npx skill-guide security`;
|
|
817
|
+
const skillCmd = `npx skill-guide ${sampleName}`;
|
|
785
818
|
const fullCmd = `npx skill-guide --full`;
|
|
786
819
|
const recommendCmd = `npx skill-guide --recommend`;
|
|
787
820
|
const shareCmd = `npx skill-guide --share`;
|
|
@@ -915,11 +948,17 @@ function shouldAutoOpen() {
|
|
|
915
948
|
return true;
|
|
916
949
|
}
|
|
917
950
|
|
|
951
|
+
const PLATFORM_LABELS = { claude: 'Claude Code', codex: 'Codex', all: 'All Platforms' };
|
|
952
|
+
|
|
918
953
|
function detectPlatform() {
|
|
919
|
-
|
|
954
|
+
if (hasFlag('--all')) return 'all';
|
|
955
|
+
const explicit = getArgValue('--platform');
|
|
956
|
+
if (explicit && PLATFORM_LABELS[explicit]) return explicit;
|
|
920
957
|
if (process.env.CODEX_AGENT) return 'codex';
|
|
921
958
|
if (process.env.CLAUDE_CODE) return 'claude';
|
|
922
|
-
|
|
959
|
+
const dir = __dirname;
|
|
960
|
+
if (dir.includes(path.join('.claude', 'skills')) || dir.includes(path.join('.claude', 'plugins'))) return 'claude';
|
|
961
|
+
if (dir.includes(path.join('.codex', 'skills')) || dir.includes(path.join('.codex', 'plugins'))) return 'codex';
|
|
923
962
|
return 'all';
|
|
924
963
|
}
|
|
925
964
|
|
|
@@ -936,6 +975,35 @@ function filterSkillsByPlatform(skills, platform) {
|
|
|
936
975
|
return skills;
|
|
937
976
|
}
|
|
938
977
|
|
|
978
|
+
function applyPlatformFilter(data) {
|
|
979
|
+
const platform = detectPlatform();
|
|
980
|
+
if (platform === 'all') {
|
|
981
|
+
data._platform = 'all';
|
|
982
|
+
return data;
|
|
983
|
+
}
|
|
984
|
+
data._platform = platform;
|
|
985
|
+
if (data.skills) {
|
|
986
|
+
data.skills = filterSkillsByPlatform(data.skills, platform);
|
|
987
|
+
data.totalCount = data.skills.length;
|
|
988
|
+
}
|
|
989
|
+
if (data.sources) {
|
|
990
|
+
const keep = platform === 'claude'
|
|
991
|
+
? ['claude-user', 'claude-plugin', 'cc-switch']
|
|
992
|
+
: ['codex-user', 'codex-plugin', 'openai-system', 'cc-switch'];
|
|
993
|
+
const filtered = {};
|
|
994
|
+
for (const [k, v] of Object.entries(data.sources)) {
|
|
995
|
+
if (keep.includes(k)) filtered[k] = v;
|
|
996
|
+
}
|
|
997
|
+
data.sources = filtered;
|
|
998
|
+
}
|
|
999
|
+
return data;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
function platformLabel() {
|
|
1003
|
+
const p = detectPlatform();
|
|
1004
|
+
return PLATFORM_LABELS[p] || p;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
939
1007
|
function skillRoots() {
|
|
940
1008
|
const home = os.homedir();
|
|
941
1009
|
const codexHome = process.env.CODEX_HOME || path.join(home, '.codex');
|
|
@@ -1621,6 +1689,133 @@ function computeHealthStats(skills) {
|
|
|
1621
1689
|
};
|
|
1622
1690
|
}
|
|
1623
1691
|
|
|
1692
|
+
// ---------------------------------------------------------------------------
|
|
1693
|
+
// --review: LLM review brief
|
|
1694
|
+
// ---------------------------------------------------------------------------
|
|
1695
|
+
function buildReviewBrief(data, details) {
|
|
1696
|
+
const skills = data.skills || [];
|
|
1697
|
+
const health = computeHealthStats(skills);
|
|
1698
|
+
const items = [];
|
|
1699
|
+
let counter = 0;
|
|
1700
|
+
function nextId(prefix) { return `${prefix}-${++counter}`; }
|
|
1701
|
+
|
|
1702
|
+
// 1. Security flags
|
|
1703
|
+
for (const flag of health.securityFlags) {
|
|
1704
|
+
const skill = skills.find(s => s.name === flag.name);
|
|
1705
|
+
items.push({
|
|
1706
|
+
id: nextId('sec'),
|
|
1707
|
+
type: 'security',
|
|
1708
|
+
severity: 'high',
|
|
1709
|
+
skills: [flag.name],
|
|
1710
|
+
evidence: `Patterns: ${flag.flags.join(', ')}`,
|
|
1711
|
+
context: `Description: ${truncate(skill?.description || '', 200)}`,
|
|
1712
|
+
question: `Does "${flag.name}" actually pose a security risk, or are these patterns used safely in context?`,
|
|
1713
|
+
actionHint: 'Review the full skill content. If the patterns are in example code or are safe, dismiss. If genuinely risky, flag for revision.',
|
|
1714
|
+
});
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
// 2. Duplicate candidates
|
|
1718
|
+
for (const group of health.duplicateGroups) {
|
|
1719
|
+
const descriptions = group.names.map(n => {
|
|
1720
|
+
const s = skills.find(sk => sk.name === n);
|
|
1721
|
+
return `${n}: ${truncate(s?.description || '(no description)', 100)}`;
|
|
1722
|
+
});
|
|
1723
|
+
items.push({
|
|
1724
|
+
id: nextId('dup'),
|
|
1725
|
+
type: 'duplicate',
|
|
1726
|
+
severity: 'medium',
|
|
1727
|
+
skills: group.names,
|
|
1728
|
+
evidence: `Normalized name: "${group.normalized}"`,
|
|
1729
|
+
context: descriptions.join('\n'),
|
|
1730
|
+
question: `Are "${group.names.join('" and "')}" truly duplicate skills, or do they serve different purposes despite similar names?`,
|
|
1731
|
+
actionHint: 'If they differ in scope or purpose, consider renaming to clarify. If truly redundant, keep the one with better documentation.',
|
|
1732
|
+
});
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
// 3. Malformed skills
|
|
1736
|
+
for (const file of (details.malformed || []).slice(0, 10)) {
|
|
1737
|
+
const basename = path.basename(path.dirname(file));
|
|
1738
|
+
items.push({
|
|
1739
|
+
id: nextId('mal'),
|
|
1740
|
+
type: 'malformed',
|
|
1741
|
+
severity: 'low',
|
|
1742
|
+
skills: [basename],
|
|
1743
|
+
evidence: `Missing frontmatter in: ${file.replace(os.homedir(), '~')}`,
|
|
1744
|
+
context: '',
|
|
1745
|
+
question: `What name, description, and triggers should be added to the skill at "${basename}"?`,
|
|
1746
|
+
actionHint: 'Read the file, understand its purpose, and suggest appropriate frontmatter fields.',
|
|
1747
|
+
});
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
// 4. Category overlap (3+ skills in same category)
|
|
1751
|
+
const groups = groupBy(skills, 'category');
|
|
1752
|
+
for (const [cat, catSkills] of Object.entries(groups)) {
|
|
1753
|
+
if (catSkills.length >= 3) {
|
|
1754
|
+
const names = catSkills.slice(0, 5).map(s => s.name);
|
|
1755
|
+
const descs = catSkills.slice(0, 5).map(s =>
|
|
1756
|
+
` - ${s.name}: ${truncate(s.description || '(no description)', 80)}`
|
|
1757
|
+
);
|
|
1758
|
+
items.push({
|
|
1759
|
+
id: nextId('ovr'),
|
|
1760
|
+
type: 'overlap',
|
|
1761
|
+
severity: 'info',
|
|
1762
|
+
skills: names,
|
|
1763
|
+
evidence: `${catSkills.length} skills in "${cat}" category`,
|
|
1764
|
+
context: descs.join('\n'),
|
|
1765
|
+
question: `Are these ${catSkills.length} "${cat}" skills overlapping (doing the same thing) or complementary (covering different aspects)?`,
|
|
1766
|
+
actionHint: 'If overlapping, suggest which to consolidate. If complementary, clarify how they differ.',
|
|
1767
|
+
});
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
// 5. Budget overflow
|
|
1772
|
+
if (health.budgetUsedPercent > 100) {
|
|
1773
|
+
const topConsumers = [...skills]
|
|
1774
|
+
.sort((a, b) => (b.description?.length || 0) - (a.description?.length || 0))
|
|
1775
|
+
.slice(0, 5);
|
|
1776
|
+
items.push({
|
|
1777
|
+
id: nextId('bud'),
|
|
1778
|
+
type: 'budget',
|
|
1779
|
+
severity: 'medium',
|
|
1780
|
+
skills: topConsumers.map(s => s.name),
|
|
1781
|
+
evidence: `Description budget used: ${health.budgetUsedPercent}% (~${health.totalTokenEstimate} tokens)`,
|
|
1782
|
+
context: topConsumers.map(s => ` - ${s.name}: ${(s.description || '').length} chars`).join('\n'),
|
|
1783
|
+
question: 'Which skill descriptions should be shortened to fit within the token budget?',
|
|
1784
|
+
actionHint: 'Prioritize trimming the longest descriptions. Preserve key information about triggers and use cases.',
|
|
1785
|
+
});
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
return {
|
|
1789
|
+
generatedAt: new Date().toISOString(),
|
|
1790
|
+
totalSkills: skills.length,
|
|
1791
|
+
totalReviewItems: items.length,
|
|
1792
|
+
summary: {
|
|
1793
|
+
security: items.filter(i => i.type === 'security').length,
|
|
1794
|
+
duplicate: items.filter(i => i.type === 'duplicate').length,
|
|
1795
|
+
malformed: items.filter(i => i.type === 'malformed').length,
|
|
1796
|
+
overlap: items.filter(i => i.type === 'overlap').length,
|
|
1797
|
+
budget: items.filter(i => i.type === 'budget').length,
|
|
1798
|
+
},
|
|
1799
|
+
items,
|
|
1800
|
+
};
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
function buildCopyPrompt(brief) {
|
|
1804
|
+
const lines = [
|
|
1805
|
+
`Review the following ${brief.totalReviewItems} findings from skill-guide and provide your assessment.`,
|
|
1806
|
+
'',
|
|
1807
|
+
];
|
|
1808
|
+
for (const item of brief.items) {
|
|
1809
|
+
lines.push(`## [${item.severity.toUpperCase()}] ${item.type}: ${item.skills.join(', ')}`);
|
|
1810
|
+
lines.push(`Evidence: ${item.evidence}`);
|
|
1811
|
+
if (item.context) lines.push(`Context:\n${item.context}`);
|
|
1812
|
+
lines.push(`Question: ${item.question}`);
|
|
1813
|
+
lines.push('');
|
|
1814
|
+
}
|
|
1815
|
+
lines.push('For each item, respond with: CONFIRM (agree with the finding), DISMISS (false positive with reason), or SUGGEST (alternative action).');
|
|
1816
|
+
return lines.join('\n');
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1624
1819
|
function renderHealthTerminal(data) {
|
|
1625
1820
|
const skills = data.skills || [];
|
|
1626
1821
|
const health = computeHealthStats(skills);
|
|
@@ -3082,6 +3277,7 @@ function main() {
|
|
|
3082
3277
|
// --doctor: terminal-only diagnostic
|
|
3083
3278
|
if (mode.mode === 'doctor') {
|
|
3084
3279
|
const data = runScanner(mode);
|
|
3280
|
+
applyPlatformFilter(data);
|
|
3085
3281
|
process.stdout.write(`${printDoctor(data)}\n`);
|
|
3086
3282
|
return;
|
|
3087
3283
|
}
|
|
@@ -3105,6 +3301,7 @@ function main() {
|
|
|
3105
3301
|
process.exit(result.status || 1);
|
|
3106
3302
|
}
|
|
3107
3303
|
const data = JSON.parse(result.stdout);
|
|
3304
|
+
applyPlatformFilter(data);
|
|
3108
3305
|
if (format === 'json') {
|
|
3109
3306
|
process.stdout.write(JSON.stringify(data, null, 2) + '\n');
|
|
3110
3307
|
return;
|
|
@@ -3118,6 +3315,7 @@ function main() {
|
|
|
3118
3315
|
} else {
|
|
3119
3316
|
// Skill found — deep dive
|
|
3120
3317
|
mode.mode = 'skill';
|
|
3318
|
+
applyPlatformFilter(skillData);
|
|
3121
3319
|
if (format === 'json') {
|
|
3122
3320
|
process.stdout.write(JSON.stringify(skillData, null, 2) + '\n');
|
|
3123
3321
|
return;
|
|
@@ -3131,9 +3329,21 @@ function main() {
|
|
|
3131
3329
|
return;
|
|
3132
3330
|
}
|
|
3133
3331
|
|
|
3332
|
+
// --review mode (JSON-only for agent programmatic consumption)
|
|
3333
|
+
if (mode.mode === 'review') {
|
|
3334
|
+
const data = runScanner(mode);
|
|
3335
|
+
applyPlatformFilter(data);
|
|
3336
|
+
const details = doctorDetails(data);
|
|
3337
|
+
const brief = buildReviewBrief(data, details);
|
|
3338
|
+
brief.copyPrompt = buildCopyPrompt(brief);
|
|
3339
|
+
process.stdout.write(JSON.stringify(brief, null, 2) + '\n');
|
|
3340
|
+
return;
|
|
3341
|
+
}
|
|
3342
|
+
|
|
3134
3343
|
// --recommend mode (online registry data)
|
|
3135
3344
|
if (mode.mode === 'recommend') {
|
|
3136
3345
|
const data = runScanner(mode);
|
|
3346
|
+
applyPlatformFilter(data);
|
|
3137
3347
|
const installed = data.skills;
|
|
3138
3348
|
const refresh = hasFlag('--refresh');
|
|
3139
3349
|
const onlineEntries = registryModule.fetchRegistry({ refresh });
|
|
@@ -3157,6 +3367,7 @@ function main() {
|
|
|
3157
3367
|
// --share mode (portfolio with --user flag)
|
|
3158
3368
|
if (mode.mode === 'share') {
|
|
3159
3369
|
const data = runScanner(mode);
|
|
3370
|
+
applyPlatformFilter(data);
|
|
3160
3371
|
const user = getArgValue('--user');
|
|
3161
3372
|
if (format === 'json') {
|
|
3162
3373
|
process.stdout.write(JSON.stringify({ skills: data.skills, totalCount: data.totalCount }, null, 2) + '\n');
|
|
@@ -3175,13 +3386,7 @@ function main() {
|
|
|
3175
3386
|
|
|
3176
3387
|
// Default mode (list): overview dashboard
|
|
3177
3388
|
const data = runScanner(mode);
|
|
3178
|
-
|
|
3179
|
-
// Filter by platform unless --all
|
|
3180
|
-
const platform = hasFlag('--all') ? 'all' : detectPlatform();
|
|
3181
|
-
if (platform !== 'all' && data.skills) {
|
|
3182
|
-
data.skills = filterSkillsByPlatform(data.skills, platform);
|
|
3183
|
-
data.totalCount = data.skills.length;
|
|
3184
|
-
}
|
|
3389
|
+
applyPlatformFilter(data);
|
|
3185
3390
|
|
|
3186
3391
|
if (format === 'json') {
|
|
3187
3392
|
const serialized = JSON.stringify(data, null, 2);
|
|
@@ -3202,10 +3407,8 @@ function main() {
|
|
|
3202
3407
|
process.exit(1);
|
|
3203
3408
|
}
|
|
3204
3409
|
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
const platformLabel = platform === 'codex' ? 'Codex' : 'Claude Code';
|
|
3208
|
-
process.stdout.write(` Showing skills for: ${platformLabel} (use --all to see all)\n\n`);
|
|
3410
|
+
if (data._platform && data._platform !== 'all') {
|
|
3411
|
+
process.stdout.write(` Showing: ${platformLabel()} (use --all to see all)\n\n`);
|
|
3209
3412
|
}
|
|
3210
3413
|
process.stdout.write(renderDefaultTerminal(data.skills || []));
|
|
3211
3414
|
|