@booklib/skills 1.7.0 → 1.9.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/PLAN.md +100 -0
- package/README.md +102 -108
- package/bin/skills.js +202 -13
- package/hooks/hooks.json +12 -0
- package/hooks/suggest.js +153 -0
- package/package.json +1 -1
package/PLAN.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Implementation Plan
|
|
2
|
+
|
|
3
|
+
Current version: **1.8.0**
|
|
4
|
+
Next release: **1.9.0**
|
|
5
|
+
|
|
6
|
+
## Remaining features (priority order)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
### 1. `skills agents` list command
|
|
11
|
+
**File:** `bin/skills.js`
|
|
12
|
+
**What:** New `agents` command that lists all agents from `agents/` with name + description (parsed from frontmatter). Mirrors `skills list`.
|
|
13
|
+
**Usage:**
|
|
14
|
+
```bash
|
|
15
|
+
skills agents
|
|
16
|
+
skills agents --info booklib-reviewer
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
### 2. Cursor support (`--target`)
|
|
22
|
+
**File:** `bin/skills.js`
|
|
23
|
+
**What:** `--target` flag on `add` that writes skills to `.cursor/rules/` (Cursor's rule system). Skills install as rule files; agents not applicable to Cursor (no native agent system).
|
|
24
|
+
**Usage:**
|
|
25
|
+
```bash
|
|
26
|
+
skills add --profile=ts --target cursor # writes to .cursor/rules/
|
|
27
|
+
skills add --profile=ts --target all # writes to both .claude/ and .cursor/rules/
|
|
28
|
+
skills add effective-python --target cursor
|
|
29
|
+
```
|
|
30
|
+
**Cursor paths:**
|
|
31
|
+
- Skills → `.cursor/rules/<skill-name>.md` (copy of SKILL.md)
|
|
32
|
+
- Commands → not applicable
|
|
33
|
+
- Agents → not applicable
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
### 3. `hooks/` — UserPromptSubmit skill suggestion
|
|
38
|
+
**Files:** `hooks/hooks.json`, `hooks/suggest.js`
|
|
39
|
+
**What:** A single `UserPromptSubmit` hook. Reads the user's prompt, detects language + "review/check/improve/refactor" intent, outputs a one-line skill suggestion. Only fires when both intent AND a language signal are present — not on every message.
|
|
40
|
+
|
|
41
|
+
**Hook config:**
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"UserPromptSubmit": [{
|
|
45
|
+
"matcher": ".*",
|
|
46
|
+
"hooks": [{ "type": "command", "command": "node ~/.claude/skills/booklib-suggest.js" }]
|
|
47
|
+
}]
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**suggest.js logic:**
|
|
52
|
+
- Read prompt from stdin (JSON event from Claude Code)
|
|
53
|
+
- Check for review intent: `review|check|improve|refactor|fix|audit`
|
|
54
|
+
- Check for language signals: `.py`, `.ts`, `.tsx`, `.java`, `.kt`, `.rs`, etc.
|
|
55
|
+
- Output one-line suggestion or nothing
|
|
56
|
+
|
|
57
|
+
**Install path:** `hooks/suggest.js` in repo → installed to `.claude/` root as `booklib-suggest.js` via `skills add --all` or `skills add --hooks`.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
### 4. README overhaul
|
|
62
|
+
**File:** `README.md`
|
|
63
|
+
**What:** Rewrite to document the full three-tier architecture. Current README only describes skills. Needs: profiles section, agents section, commands section, architecture diagram.
|
|
64
|
+
|
|
65
|
+
**New structure:**
|
|
66
|
+
```
|
|
67
|
+
1. Tagline + install
|
|
68
|
+
2. Three-tier architecture diagram (skills → commands → agents → profiles)
|
|
69
|
+
3. Quick start (pick a profile)
|
|
70
|
+
4. Skills list (existing)
|
|
71
|
+
5. Agents (new section)
|
|
72
|
+
6. Profiles (new section)
|
|
73
|
+
7. Commands (new section — brief, link to commands/)
|
|
74
|
+
8. Quality / evals (existing, improved)
|
|
75
|
+
9. Contributing
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Parallel implementation tracks
|
|
81
|
+
|
|
82
|
+
| Track | Files touched | Depends on |
|
|
83
|
+
|-------|--------------|------------|
|
|
84
|
+
| A | `bin/skills.js` | — |
|
|
85
|
+
| B | `hooks/hooks.json`, `hooks/suggest.js` | — |
|
|
86
|
+
| C | `README.md` | — |
|
|
87
|
+
|
|
88
|
+
Tracks B and C are independent and can be implemented simultaneously.
|
|
89
|
+
Track A (`bin/skills.js`) has no file conflicts with B or C.
|
|
90
|
+
All three can be implemented in parallel.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Release checklist
|
|
95
|
+
|
|
96
|
+
- [ ] Track A: `skills agents` command + Cursor support in bin/skills.js
|
|
97
|
+
- [ ] Track B: hooks/hooks.json + hooks/suggest.js
|
|
98
|
+
- [ ] Track C: README overhaul
|
|
99
|
+
- [ ] Bump version to 1.9.0
|
|
100
|
+
- [ ] Commit + tag v1.9.0 + push
|
package/README.md
CHANGED
|
@@ -12,100 +12,100 @@
|
|
|
12
12
|
|
|
13
13
|
Book-grounded AI agent skills — each skill packages expert practices from a canonical programming book into reusable instructions that Claude and other AI agents can apply to code generation, code review, and design decisions.
|
|
14
14
|
|
|
15
|
-
```bash
|
|
16
|
-
npx skills add booklib-ai/skills
|
|
17
|
-
```
|
|
18
|
-
|
|
19
15
|

|
|
20
16
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
## Structure
|
|
17
|
+
## Architecture
|
|
24
18
|
|
|
25
|
-
|
|
26
|
-
booklib-ai/skills (repo root)
|
|
27
|
-
├── skills/
|
|
28
|
-
│ ├── clean-code-reviewer/
|
|
29
|
-
│ │ ├── SKILL.md # Required
|
|
30
|
-
│ │ ├── examples/
|
|
31
|
-
│ │ ├── references/
|
|
32
|
-
│ │ ├── scripts/
|
|
33
|
-
│ │ └── evals/
|
|
34
|
-
│ └── [skill-name]/ # One folder per book
|
|
35
|
-
│ └── ...
|
|
36
|
-
├── README.md
|
|
37
|
-
├── LICENSE
|
|
38
|
-
└── package.json
|
|
39
|
-
```
|
|
19
|
+
The library is organized into three tiers that work together:
|
|
40
20
|
|
|
41
|
-
|
|
21
|
+
| Tier | Count | What it does |
|
|
22
|
+
|------|-------|--------------|
|
|
23
|
+
| **Skills** | 22 | Passive context loaded from `.claude/skills/` — triggered automatically when the AI detects a matching file or task |
|
|
24
|
+
| **Commands** | 22 | Explicit slash commands — `/effective-python`, `/design-patterns`, etc. — one per skill |
|
|
25
|
+
| **Agents** | 8 | Autonomous reviewers that combine multiple skills and run end-to-end reviews |
|
|
42
26
|
|
|
43
|
-
|
|
27
|
+
**Profiles** bundle all three tiers by language or domain so you install everything you need in one command.
|
|
44
28
|
|
|
45
|
-
|
|
46
|
-
skill-name/
|
|
47
|
-
├── SKILL.md # Required — YAML frontmatter + markdown instructions
|
|
48
|
-
├── scripts/ # Optional — deterministic code for repeated tasks
|
|
49
|
-
├── references/ # Optional — docs loaded into context as needed
|
|
50
|
-
├── assets/ # Optional — templates, fonts, images
|
|
51
|
-
└── evals/ # Optional — test cases for skill evaluation
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### SKILL.md Structure
|
|
55
|
-
|
|
56
|
-
```markdown
|
|
57
|
-
---
|
|
58
|
-
name: skill-name
|
|
59
|
-
description: When to trigger this skill and what it does
|
|
60
|
-
---
|
|
61
|
-
|
|
62
|
-
# Skill Title
|
|
63
|
-
|
|
64
|
-
Instructions for the AI agent...
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## Installation
|
|
68
|
-
|
|
69
|
-
### via skills CLI (recommended)
|
|
29
|
+
## Quick Start
|
|
70
30
|
|
|
71
31
|
```bash
|
|
72
|
-
#
|
|
73
|
-
npx skills add
|
|
32
|
+
# Pick your language or domain
|
|
33
|
+
npx @booklib/skills add --profile=python # Python skills + commands + python-reviewer agent
|
|
34
|
+
npx @booklib/skills add --profile=ts # TypeScript skills + commands + ts-reviewer agent
|
|
35
|
+
npx @booklib/skills add --profile=rust # Rust skills + commands + rust-reviewer agent
|
|
36
|
+
npx @booklib/skills add --profile=jvm # Java/Kotlin skills + commands + jvm-reviewer agent
|
|
37
|
+
npx @booklib/skills add --profile=architecture # DDD/microservices/system design
|
|
38
|
+
npx @booklib/skills add --profile=data # Data pipelines + DDIA
|
|
39
|
+
npx @booklib/skills add --profile=ui # Refactoring UI + animations + data viz
|
|
40
|
+
npx @booklib/skills add --profile=lean # Lean Startup practices
|
|
41
|
+
|
|
42
|
+
# Or install everything
|
|
43
|
+
npx @booklib/skills add --all
|
|
44
|
+
```
|
|
74
45
|
|
|
75
|
-
|
|
76
|
-
npx skills add booklib-ai/skills --skill effective-kotlin
|
|
46
|
+
Skills are installed to `.claude/skills/` in your project, or `~/.claude/skills/` with `--global`.
|
|
77
47
|
|
|
78
|
-
|
|
79
|
-
npx skills add booklib-ai/skills --list
|
|
80
|
-
```
|
|
48
|
+
## Agents
|
|
81
49
|
|
|
82
|
-
|
|
50
|
+
Eight autonomous reviewers that run end-to-end reviews combining the most relevant skills for each domain:
|
|
83
51
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
52
|
+
| Agent | Skills used | Use when |
|
|
53
|
+
|-------|-------------|----------|
|
|
54
|
+
| `booklib-reviewer` | skill-router (auto-selects) | Unsure which skill applies — routes automatically |
|
|
55
|
+
| `python-reviewer` | effective-python · asyncio · web-scraping | Reviewing any Python code |
|
|
56
|
+
| `jvm-reviewer` | effective-java · effective-kotlin · kotlin-in-action · spring-boot | Java or Kotlin code reviews |
|
|
57
|
+
| `rust-reviewer` | programming-with-rust · rust-in-action | Rust ownership, safety, and systems code |
|
|
58
|
+
| `ts-reviewer` | effective-typescript · clean-code-reviewer | TypeScript and TSX reviews |
|
|
59
|
+
| `architecture-reviewer` | domain-driven-design · microservices-patterns · system-design · data-intensive | System design, domain models, service boundaries |
|
|
60
|
+
| `data-reviewer` | data-intensive-patterns · data-pipelines | Schemas, ETL pipelines, stream processing |
|
|
61
|
+
| `ui-reviewer` | refactoring-ui · storytelling-with-data · animation-at-work | UI components, dashboards, data visualizations |
|
|
87
62
|
|
|
88
|
-
|
|
89
|
-
npx @booklib/skills add effective-kotlin
|
|
63
|
+
Invoke an agent in Claude Code with `@booklib-reviewer` or the specific agent name.
|
|
90
64
|
|
|
91
|
-
|
|
92
|
-
npx @booklib/skills list
|
|
93
|
-
```
|
|
65
|
+
## Profiles
|
|
94
66
|
|
|
95
|
-
|
|
67
|
+
| Profile | Skills + agents included |
|
|
68
|
+
|---------|--------------------------|
|
|
69
|
+
| `python` | effective-python · using-asyncio-python · web-scraping-python · python-reviewer |
|
|
70
|
+
| `ts` | effective-typescript · clean-code-reviewer · ts-reviewer |
|
|
71
|
+
| `jvm` | effective-java · effective-kotlin · kotlin-in-action · spring-boot-in-action · jvm-reviewer |
|
|
72
|
+
| `rust` | programming-with-rust · rust-in-action · rust-reviewer |
|
|
73
|
+
| `architecture` | domain-driven-design · microservices-patterns · system-design-interview · data-intensive-patterns · architecture-reviewer |
|
|
74
|
+
| `data` | data-intensive-patterns · data-pipelines · data-reviewer |
|
|
75
|
+
| `ui` | refactoring-ui · storytelling-with-data · animation-at-work · ui-reviewer |
|
|
76
|
+
| `lean` | lean-startup · design-patterns · clean-code-reviewer |
|
|
77
|
+
| `core` | clean-code-reviewer · design-patterns · skill-router · booklib-reviewer |
|
|
96
78
|
|
|
97
|
-
|
|
79
|
+
## Skills
|
|
98
80
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
81
|
+
| Skill | Book |
|
|
82
|
+
|-------|------|
|
|
83
|
+
| [animation-at-work](./skills/animation-at-work/) | *Animation at Work* — Rachel Nabors |
|
|
84
|
+
| [clean-code-reviewer](./skills/clean-code-reviewer/) | *Clean Code* — Robert C. Martin |
|
|
85
|
+
| [data-intensive-patterns](./skills/data-intensive-patterns/) | *Designing Data-Intensive Applications* — Martin Kleppmann |
|
|
86
|
+
| [data-pipelines](./skills/data-pipelines/) | *Data Pipelines Pocket Reference* — James Densmore |
|
|
87
|
+
| [design-patterns](./skills/design-patterns/) | *Head First Design Patterns* |
|
|
88
|
+
| [domain-driven-design](./skills/domain-driven-design/) | *Domain-Driven Design* — Eric Evans |
|
|
89
|
+
| [effective-java](./skills/effective-java/) | *Effective Java* (3rd ed) — Joshua Bloch |
|
|
90
|
+
| [effective-kotlin](./skills/effective-kotlin/) | *Effective Kotlin* (2nd ed) — Marcin Moskała |
|
|
91
|
+
| [effective-python](./skills/effective-python/) | *Effective Python* (2nd ed) — Brett Slatkin |
|
|
92
|
+
| [effective-typescript](./skills/effective-typescript/) | *Effective TypeScript* — Dan Vanderkam |
|
|
93
|
+
| [kotlin-in-action](./skills/kotlin-in-action/) | *Kotlin in Action* (2nd ed) |
|
|
94
|
+
| [lean-startup](./skills/lean-startup/) | *The Lean Startup* — Eric Ries |
|
|
95
|
+
| [microservices-patterns](./skills/microservices-patterns/) | *Microservices Patterns* — Chris Richardson |
|
|
96
|
+
| [programming-with-rust](./skills/programming-with-rust/) | *Programming with Rust* — Donis Marshall |
|
|
97
|
+
| [refactoring-ui](./skills/refactoring-ui/) | *Refactoring UI* — Adam Wathan & Steve Schoger |
|
|
98
|
+
| [rust-in-action](./skills/rust-in-action/) | *Rust in Action* — Tim McNamara |
|
|
99
|
+
| [skill-router](./skills/skill-router/) | Meta-skill — routes to the right skill automatically |
|
|
100
|
+
| [spring-boot-in-action](./skills/spring-boot-in-action/) | *Spring Boot in Action* — Craig Walls |
|
|
101
|
+
| [storytelling-with-data](./skills/storytelling-with-data/) | *Storytelling with Data* — Cole Nussbaumer Knaflic |
|
|
102
|
+
| [system-design-interview](./skills/system-design-interview/) | *System Design Interview* — Alex Xu |
|
|
103
|
+
| [using-asyncio-python](./skills/using-asyncio-python/) | *Using Asyncio in Python* — Caleb Hattingh |
|
|
104
|
+
| [web-scraping-python](./skills/web-scraping-python/) | *Web Scraping with Python* — Ryan Mitchell |
|
|
103
105
|
|
|
104
106
|
## Automatic Skill Routing
|
|
105
107
|
|
|
106
|
-
You don't need to know which skill to apply — the **[skill-router](./skills/skill-router/)** meta-skill does it for you.
|
|
107
|
-
|
|
108
|
-
When an AI agent receives a task, it can invoke `skill-router` first to identify the 1–2 most relevant skills based on the file, language, domain, and work type. The router then returns a ranked recommendation with rationale, so the right expertise is applied automatically.
|
|
108
|
+
You don't need to know which skill to apply — the **[skill-router](./skills/skill-router/)** meta-skill does it for you. When invoked, it reads the file, language, domain, and task type, then returns a ranked recommendation with rationale.
|
|
109
109
|
|
|
110
110
|
```
|
|
111
111
|
User: "Review my order processing service"
|
|
@@ -113,39 +113,11 @@ User: "Review my order processing service"
|
|
|
113
113
|
→ skill-router selects:
|
|
114
114
|
Primary: domain-driven-design — domain model design (Aggregates, Value Objects)
|
|
115
115
|
Secondary: microservices-patterns — service boundaries and inter-service communication
|
|
116
|
-
Skip: clean-code-reviewer — premature at design stage; apply later on implementation code
|
|
117
116
|
```
|
|
118
117
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
**See it in action:** The [`benchmark/`](./benchmark/) folder contains a head-to-head comparison — same buggy Node.js file reviewed by the native PR toolkit vs. `skill-router` routing to `clean-code-reviewer` + `design-patterns`. The skill-router pipeline finds ~47% more unique issues and adds a full refactor roadmap with pattern sequence.
|
|
118
|
+
The `booklib-reviewer` agent wraps this logic end-to-end — invoke it and it handles selection and review automatically.
|
|
122
119
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
| Skill | Description |
|
|
126
|
-
|-------|-------------|
|
|
127
|
-
| 🎬 [animation-at-work](./skills/animation-at-work/) | Apply web animation principles from Rachel Nabors' *Animation at Work* — human perception of motion, 12 principles of animation, and performance |
|
|
128
|
-
| 🧹 [clean-code-reviewer](./skills/clean-code-reviewer/) | Reviews code against Robert C. Martin's *Clean Code* principles with heuristic codes (C1–C5, G1–G36, N1–N7, T1–T9) |
|
|
129
|
-
| 🗄️ [data-intensive-patterns](./skills/data-intensive-patterns/) | Patterns for reliable, scalable, and maintainable systems from Martin Kleppmann's *Designing Data-Intensive Applications* — storage engines, replication, partitioning, and transactions |
|
|
130
|
-
| 🔀 [data-pipelines](./skills/data-pipelines/) | Data pipeline practices from James Densmore's *Data Pipelines Pocket Reference* — ingestion, streaming, transformation, and orchestration |
|
|
131
|
-
| 🏗️ [design-patterns](./skills/design-patterns/) | Apply and review GoF design patterns from *Head First Design Patterns* — creational, structural, and behavioral patterns |
|
|
132
|
-
| 🧩 [domain-driven-design](./skills/domain-driven-design/) | Design and review software using patterns from Eric Evans' *Domain-Driven Design* — tactical and strategic patterns, and Ubiquitous Language |
|
|
133
|
-
| ☕ [effective-java](./skills/effective-java/) | Java best practices from Joshua Bloch's *Effective Java* (3rd Edition) — object creation, generics, enums, lambdas, and concurrency |
|
|
134
|
-
| 🛡️ [effective-kotlin](./skills/effective-kotlin/) | Best practices from Marcin Moskała's *Effective Kotlin* (2nd Ed) — safety, readability, reusability, and abstraction |
|
|
135
|
-
| 🐍 [effective-python](./skills/effective-python/) | Python best practices from Brett Slatkin's *Effective Python* (2nd Edition) — Pythonic thinking, functions, classes, concurrency, and testing |
|
|
136
|
-
| 🔷 [effective-typescript](./skills/effective-typescript/) | TypeScript best practices from Dan Vanderkam's *Effective TypeScript* — type system, type design, avoiding any, type declarations, and migration |
|
|
137
|
-
| 🦀 [programming-with-rust](./skills/programming-with-rust/) | Rust practices from Donis Marshall's *Programming with Rust* — ownership, borrowing, lifetimes, error handling, traits, and fearless concurrency |
|
|
138
|
-
| ⚙️ [rust-in-action](./skills/rust-in-action/) | Systems programming from Tim McNamara's *Rust in Action* — smart pointers, endianness, memory, file formats, TCP networking, concurrency, and OS fundamentals |
|
|
139
|
-
| 🌱 [spring-boot-in-action](./skills/spring-boot-in-action/) | Spring Boot best practices from Craig Walls' *Spring Boot in Action* — auto-configuration, starters, externalized config, profiles, testing with MockMvc, Actuator, and deployment |
|
|
140
|
-
| ⚡ [kotlin-in-action](./skills/kotlin-in-action/) | Practices from *Kotlin in Action* (2nd Ed) — functions, classes, lambdas, nullability, and coroutines |
|
|
141
|
-
| 🚀 [lean-startup](./skills/lean-startup/) | Practices from Eric Ries' *The Lean Startup* — MVP testing, validated learning, Build-Measure-Learn loop, and pivots |
|
|
142
|
-
| 🔧 [microservices-patterns](./skills/microservices-patterns/) | Expert guidance on microservices patterns from Chris Richardson's *Microservices Patterns* — decomposition, sagas, API gateways, event sourcing, CQRS, and service mesh |
|
|
143
|
-
| 🎨 [refactoring-ui](./skills/refactoring-ui/) | UI design principles from *Refactoring UI* by Adam Wathan & Steve Schoger — visual hierarchy, layout, typography, and color |
|
|
144
|
-
| 🗺️ [skill-router](./skills/skill-router/) | **Meta-skill.** Automatically selects the 1–2 most relevant skills for a given file, PR, or task — routes by language, domain, and work type with conflict resolution. Use this when the right skill isn't obvious, or let the AI invoke it automatically before applying any skill |
|
|
145
|
-
| 📊 [storytelling-with-data](./skills/storytelling-with-data/) | Data visualization and storytelling from Cole Nussbaumer Knaflic's *Storytelling with Data* — effective visuals, decluttering, and narrative structure |
|
|
146
|
-
| 🏛️ [system-design-interview](./skills/system-design-interview/) | System design principles from Alex Xu's *System Design Interview* — scaling, estimation, and real-world system designs |
|
|
147
|
-
| 🔄 [using-asyncio-python](./skills/using-asyncio-python/) | Asyncio practices from Caleb Hattingh's *Using Asyncio in Python* — coroutines, event loop, tasks, and signal handling |
|
|
148
|
-
| 🕷️ [web-scraping-python](./skills/web-scraping-python/) | Web scraping practices from Ryan Mitchell's *Web Scraping with Python* — BeautifulSoup, Scrapy, and data storage |
|
|
120
|
+
**Benchmark:** The [`benchmark/`](./benchmark/) folder contains a head-to-head comparison of a native PR review vs. `skill-router` routing to `clean-code-reviewer` + `design-patterns`. The skill-router pipeline finds ~47% more unique issues and produces a full refactor roadmap.
|
|
149
121
|
|
|
150
122
|
## Quality
|
|
151
123
|
|
|
@@ -157,7 +129,7 @@ Skills are evaluated against 6–15 test cases each, run both **with** and **wit
|
|
|
157
129
|
| Skill | Pass Rate | Baseline | Delta | Evals | Last Run |
|
|
158
130
|
|-------|-----------|----------|-------|-------|----------|
|
|
159
131
|
| animation-at-work | — | — | — | — | — |
|
|
160
|
-
| clean-code-reviewer |
|
|
132
|
+
| clean-code-reviewer | 74% ⚠ | 55% | +19pp | 15 | 2026-03-28 |
|
|
161
133
|
| data-intensive-patterns | — | — | — | — | — |
|
|
162
134
|
| data-pipelines | — | — | — | — | — |
|
|
163
135
|
| design-patterns | — | — | — | — | — |
|
|
@@ -182,9 +154,31 @@ Skills are evaluated against 6–15 test cases each, run both **with** and **wit
|
|
|
182
154
|
|
|
183
155
|
Results are stored in each skill's `evals/results.json` and updated by running `npx @booklib/skills eval <name>`.
|
|
184
156
|
|
|
185
|
-
##
|
|
157
|
+
## Structure
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
booklib-ai/skills/
|
|
161
|
+
├── skills/ 22 book-grounded skills (SKILL.md + examples + evals)
|
|
162
|
+
├── commands/ 22 slash commands, one per skill
|
|
163
|
+
├── agents/ 8 autonomous reviewer agents
|
|
164
|
+
├── hooks/ Claude Code hooks (skill suggestion on UserPromptSubmit)
|
|
165
|
+
└── bin/skills.js CLI
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Each skill folder follows the [Agent Skills standard](https://agentskills.io):
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
skill-name/
|
|
172
|
+
├── SKILL.md # Required — YAML frontmatter + instructions
|
|
173
|
+
├── examples/ # before.md and after.md
|
|
174
|
+
├── references/ # Deep reference material loaded on demand
|
|
175
|
+
├── scripts/ # Deterministic helper scripts
|
|
176
|
+
└── evals/ # Test cases for skill evaluation
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Contributing
|
|
186
180
|
|
|
187
|
-
If you've read a book that belongs here, you can add it
|
|
181
|
+
If you've read a book that belongs here, you can add it:
|
|
188
182
|
|
|
189
183
|
```bash
|
|
190
184
|
# 1. Copy an existing skill as a template
|
|
@@ -196,7 +190,7 @@ cp -r skills/clean-code-reviewer skills/your-book-name
|
|
|
196
190
|
npx @booklib/skills check your-book-name
|
|
197
191
|
```
|
|
198
192
|
|
|
199
|
-
The `check` command runs all evals and reports what passes and fails
|
|
193
|
+
The `check` command runs all evals and reports what passes and fails. See [CONTRIBUTING.md](./CONTRIBUTING.md) for the full guide.
|
|
200
194
|
|
|
201
195
|
**Books with open issues** (tagged `good first issue`): [The Pragmatic Programmer](https://github.com/booklib-ai/skills/issues/2) · [Clean Architecture](https://github.com/booklib-ai/skills/issues/3) · [A Philosophy of Software Design](https://github.com/booklib-ai/skills/issues/4) · [Accelerate](https://github.com/booklib-ai/skills/issues/8) · [and more →](https://github.com/booklib-ai/skills/issues?q=is%3Aopen+label%3A%22good+first+issue%22)
|
|
202
196
|
|
package/bin/skills.js
CHANGED
|
@@ -13,6 +13,55 @@ const skillsRoot = path.join(__dirname, '..', 'skills');
|
|
|
13
13
|
const commandsRoot = path.join(__dirname, '..', 'commands');
|
|
14
14
|
const agentsRoot = path.join(__dirname, '..', 'agents');
|
|
15
15
|
|
|
16
|
+
// ─── Installation profiles ────────────────────────────────────────────────────
|
|
17
|
+
const PROFILES = {
|
|
18
|
+
core: {
|
|
19
|
+
description: 'Routing + general code quality — a good starting point for any project',
|
|
20
|
+
skills: ['skill-router', 'clean-code-reviewer'],
|
|
21
|
+
agents: ['booklib-reviewer'],
|
|
22
|
+
},
|
|
23
|
+
python: {
|
|
24
|
+
description: 'Python best practices, async patterns, and web scraping',
|
|
25
|
+
skills: ['effective-python', 'using-asyncio-python', 'web-scraping-python'],
|
|
26
|
+
agents: ['python-reviewer'],
|
|
27
|
+
},
|
|
28
|
+
jvm: {
|
|
29
|
+
description: 'Java, Kotlin, and Spring Boot best practices',
|
|
30
|
+
skills: ['effective-java', 'effective-kotlin', 'kotlin-in-action', 'spring-boot-in-action'],
|
|
31
|
+
agents: ['jvm-reviewer'],
|
|
32
|
+
},
|
|
33
|
+
rust: {
|
|
34
|
+
description: 'Rust ownership, systems programming, and idiomatic patterns',
|
|
35
|
+
skills: ['programming-with-rust', 'rust-in-action'],
|
|
36
|
+
agents: ['rust-reviewer'],
|
|
37
|
+
},
|
|
38
|
+
ts: {
|
|
39
|
+
description: 'TypeScript type system and clean code for JS/TS projects',
|
|
40
|
+
skills: ['effective-typescript', 'clean-code-reviewer'],
|
|
41
|
+
agents: ['ts-reviewer'],
|
|
42
|
+
},
|
|
43
|
+
architecture: {
|
|
44
|
+
description: 'DDD, microservices, system design, and data-intensive patterns',
|
|
45
|
+
skills: ['domain-driven-design', 'microservices-patterns', 'system-design-interview', 'data-intensive-patterns'],
|
|
46
|
+
agents: ['architecture-reviewer'],
|
|
47
|
+
},
|
|
48
|
+
data: {
|
|
49
|
+
description: 'Data pipelines, ETL, and storage system patterns',
|
|
50
|
+
skills: ['data-intensive-patterns', 'data-pipelines'],
|
|
51
|
+
agents: ['data-reviewer'],
|
|
52
|
+
},
|
|
53
|
+
ui: {
|
|
54
|
+
description: 'UI design, data visualization, and web animations',
|
|
55
|
+
skills: ['refactoring-ui', 'storytelling-with-data', 'animation-at-work'],
|
|
56
|
+
agents: ['ui-reviewer'],
|
|
57
|
+
},
|
|
58
|
+
lean: {
|
|
59
|
+
description: 'Lean Startup methodology for product and feature decisions',
|
|
60
|
+
skills: ['lean-startup'],
|
|
61
|
+
agents: [],
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
16
65
|
// ─── ANSI helpers ─────────────────────────────────────────────────────────────
|
|
17
66
|
const c = {
|
|
18
67
|
bold: s => `\x1b[1m${s}\x1b[0m`,
|
|
@@ -118,6 +167,30 @@ function getAvailableAgents() {
|
|
|
118
167
|
.sort();
|
|
119
168
|
}
|
|
120
169
|
|
|
170
|
+
function parseAgentFrontmatter(agentName) {
|
|
171
|
+
const agentMdPath = path.join(agentsRoot, `${agentName}.md`);
|
|
172
|
+
try {
|
|
173
|
+
const content = fs.readFileSync(agentMdPath, 'utf8');
|
|
174
|
+
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
175
|
+
if (!fmMatch) return { name: agentName, description: '', model: '' };
|
|
176
|
+
const fm = fmMatch[1];
|
|
177
|
+
|
|
178
|
+
const blockMatch = fm.match(/^description:\s*>\s*\n((?:[ \t]+.+\n?)+)/m);
|
|
179
|
+
const quotedMatch = fm.match(/^description:\s*["'](.+?)["']\s*$/m);
|
|
180
|
+
const plainMatch = fm.match(/^description:\s*(?!>)(.+)$/m);
|
|
181
|
+
const modelMatch = fm.match(/^model:\s*(\S+)/m);
|
|
182
|
+
|
|
183
|
+
let description = '';
|
|
184
|
+
if (blockMatch) description = blockMatch[1].split('\n').map(l => l.trim()).filter(Boolean).join(' ');
|
|
185
|
+
else if (quotedMatch) description = quotedMatch[1];
|
|
186
|
+
else if (plainMatch) description = plainMatch[1].trim();
|
|
187
|
+
|
|
188
|
+
return { name: agentName, description, model: modelMatch?.[1] ?? '' };
|
|
189
|
+
} catch {
|
|
190
|
+
return { name: agentName, description: '', model: '' };
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
121
194
|
function copyAgent(agentName) {
|
|
122
195
|
const src = path.join(agentsRoot, `${agentName}.md`);
|
|
123
196
|
if (!fs.existsSync(src)) return;
|
|
@@ -127,6 +200,36 @@ function copyAgent(agentName) {
|
|
|
127
200
|
console.log(c.green('✓') + ` @${agentName} agent → ${c.dim(dest)}`);
|
|
128
201
|
}
|
|
129
202
|
|
|
203
|
+
// ─── Cursor support ───────────────────────────────────────────────────────────
|
|
204
|
+
function getCursorRulesDir() {
|
|
205
|
+
return isGlobal
|
|
206
|
+
? path.join(os.homedir(), '.cursor', 'rules')
|
|
207
|
+
: path.join(process.cwd(), '.cursor', 'rules');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function copyHooks() {
|
|
211
|
+
const hooksDir = path.join(__dirname, '..', 'hooks');
|
|
212
|
+
if (!fs.existsSync(hooksDir)) return;
|
|
213
|
+
// Copy suggest.js to the .claude/ root as booklib-suggest.js
|
|
214
|
+
const suggestSrc = path.join(hooksDir, 'suggest.js');
|
|
215
|
+
if (fs.existsSync(suggestSrc)) {
|
|
216
|
+
const claudeDir = isGlobal ? path.join(os.homedir(), '.claude') : path.join(process.cwd(), '.claude');
|
|
217
|
+
fs.mkdirSync(claudeDir, { recursive: true });
|
|
218
|
+
const dest = path.join(claudeDir, 'booklib-suggest.js');
|
|
219
|
+
fs.copyFileSync(suggestSrc, dest);
|
|
220
|
+
console.log(c.green('✓') + ` booklib-suggest.js hook → ${c.dim(dest)}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function copySkillToCursor(skillName) {
|
|
225
|
+
const src = path.join(skillsRoot, skillName, 'SKILL.md');
|
|
226
|
+
if (!fs.existsSync(src)) return;
|
|
227
|
+
const dest = path.join(getCursorRulesDir(), `${skillName}.md`);
|
|
228
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
229
|
+
fs.copyFileSync(src, dest);
|
|
230
|
+
console.log(c.green('✓') + ` ${c.bold(skillName)} → ${c.dim(dest)}`);
|
|
231
|
+
}
|
|
232
|
+
|
|
130
233
|
// ─── CHECK command ────────────────────────────────────────────────────────────
|
|
131
234
|
function checkSkill(skillName) {
|
|
132
235
|
const skillDir = path.join(skillsRoot, skillName);
|
|
@@ -726,10 +829,36 @@ async function main() {
|
|
|
726
829
|
const noCommands = args.includes('--no-commands');
|
|
727
830
|
const noAgents = args.includes('--no-agents');
|
|
728
831
|
const agentArg = args.find(a => a.startsWith('--agent='))?.split('=')[1];
|
|
832
|
+
const profileArg = args.find(a => a.startsWith('--profile='))?.split('=')[1];
|
|
833
|
+
const targetArg = (args.find(a => a.startsWith('--target='))?.split('=')[1] ?? 'claude').toLowerCase();
|
|
834
|
+
const toClaude = targetArg === 'claude' || targetArg === 'all';
|
|
835
|
+
const toCursor = targetArg === 'cursor' || targetArg === 'all';
|
|
729
836
|
const skillName = args.find(a => !a.startsWith('--') && a !== 'add');
|
|
730
837
|
|
|
731
|
-
|
|
732
|
-
|
|
838
|
+
const installSkills = (list) => {
|
|
839
|
+
if (toClaude) list.forEach(s => copySkill(s, targetDir));
|
|
840
|
+
if (toCursor) list.forEach(s => copySkillToCursor(s));
|
|
841
|
+
if (toClaude && !noCommands) list.forEach(s => copyCommand(s));
|
|
842
|
+
};
|
|
843
|
+
const installAgents = (list) => {
|
|
844
|
+
if (toClaude && !noAgents) list.forEach(a => copyAgent(a));
|
|
845
|
+
// agents not applicable to Cursor
|
|
846
|
+
};
|
|
847
|
+
|
|
848
|
+
if (profileArg) {
|
|
849
|
+
const profile = PROFILES[profileArg];
|
|
850
|
+
if (!profile) {
|
|
851
|
+
console.error(c.red(`✗ Profile "${profileArg}" not found.`) + ' Run ' + c.cyan('skills profiles') + ' to see available profiles.');
|
|
852
|
+
process.exit(1);
|
|
853
|
+
}
|
|
854
|
+
installSkills(profile.skills);
|
|
855
|
+
installAgents(profile.agents);
|
|
856
|
+
const targets = [toClaude && '.claude', toCursor && '.cursor/rules'].filter(Boolean).join(' + ');
|
|
857
|
+
const agentStr = (!noAgents && toClaude && profile.agents.length)
|
|
858
|
+
? `, ${profile.agents.length} agent${profile.agents.length > 1 ? 's' : ''}`
|
|
859
|
+
: '';
|
|
860
|
+
console.log(c.dim(`\nInstalled profile "${profileArg}": ${profile.skills.length} skills${agentStr} → ${targets}`));
|
|
861
|
+
} else if (agentArg) {
|
|
733
862
|
const agents = getAvailableAgents();
|
|
734
863
|
if (!agents.includes(agentArg)) {
|
|
735
864
|
console.error(c.red(`✗ Agent "${agentArg}" not found.`) + ' Available: ' + c.dim(agents.join(', ')));
|
|
@@ -739,14 +868,14 @@ async function main() {
|
|
|
739
868
|
console.log(c.dim(`\nInstalled to ${agentsTargetDir}`));
|
|
740
869
|
} else if (addAll) {
|
|
741
870
|
const skills = getAvailableSkills();
|
|
742
|
-
skills
|
|
743
|
-
|
|
744
|
-
if (
|
|
745
|
-
const agentCount = noAgents
|
|
746
|
-
|
|
871
|
+
installSkills(skills);
|
|
872
|
+
installAgents(getAvailableAgents());
|
|
873
|
+
if (toClaude) copyHooks();
|
|
874
|
+
const agentCount = (!noAgents && toClaude) ? getAvailableAgents().length : 0;
|
|
875
|
+
const targets = [toClaude && '.claude', toCursor && '.cursor/rules'].filter(Boolean).join(' + ');
|
|
876
|
+
console.log(c.dim(`\nInstalled ${skills.length} skills, ${agentCount} agents → ${targets}`));
|
|
747
877
|
} else if (skillName) {
|
|
748
|
-
|
|
749
|
-
if (!noCommands) copyCommand(skillName);
|
|
878
|
+
installSkills([skillName]);
|
|
750
879
|
console.log(c.dim(`\nInstalled to ${targetDir}`));
|
|
751
880
|
} else {
|
|
752
881
|
console.error(c.red('Usage: skills add <skill-name> | skills add --all | skills add --agent=<name>'));
|
|
@@ -853,20 +982,80 @@ async function main() {
|
|
|
853
982
|
break;
|
|
854
983
|
}
|
|
855
984
|
|
|
985
|
+
case 'agents': {
|
|
986
|
+
const infoArg = args.find(a => a.startsWith('--info='))?.split('=')[1]
|
|
987
|
+
|| args.find(a => !a.startsWith('--') && a !== 'agents');
|
|
988
|
+
const available = getAvailableAgents();
|
|
989
|
+
|
|
990
|
+
if (infoArg) {
|
|
991
|
+
if (!available.includes(infoArg)) {
|
|
992
|
+
console.error(c.red(`✗ Agent "${infoArg}" not found.`) + ' Run ' + c.cyan('skills agents') + ' to see available agents.');
|
|
993
|
+
process.exit(1);
|
|
994
|
+
}
|
|
995
|
+
const { description, model } = parseAgentFrontmatter(infoArg);
|
|
996
|
+
console.log('');
|
|
997
|
+
console.log(c.bold(` ${infoArg}`) + c.dim(model ? ` [${model}]` : ''));
|
|
998
|
+
console.log(' ' + c.line(55));
|
|
999
|
+
console.log(' ' + description);
|
|
1000
|
+
console.log('');
|
|
1001
|
+
console.log(c.dim(` Install: skills add --agent=${infoArg}`));
|
|
1002
|
+
console.log('');
|
|
1003
|
+
} else {
|
|
1004
|
+
const nameW = Math.max(...available.map(n => n.length)) + 2;
|
|
1005
|
+
console.log('');
|
|
1006
|
+
console.log(c.bold(` @booklib/skills — agents`) + c.dim(` (${available.length})`));
|
|
1007
|
+
console.log(' ' + c.line(60));
|
|
1008
|
+
for (const name of available) {
|
|
1009
|
+
const { description, model } = parseAgentFrontmatter(name);
|
|
1010
|
+
const modelTag = model ? c.dim(` [${model}]`) : '';
|
|
1011
|
+
console.log(` ${c.cyan(name.padEnd(nameW))}${modelTag}`);
|
|
1012
|
+
if (description) console.log(` ${' '.repeat(nameW)}${firstSentence(description, 72)}`);
|
|
1013
|
+
}
|
|
1014
|
+
console.log('');
|
|
1015
|
+
console.log(c.dim(` skills add --agent=<name> install one agent`));
|
|
1016
|
+
console.log(c.dim(` skills agents --info=<name> full description`));
|
|
1017
|
+
console.log('');
|
|
1018
|
+
}
|
|
1019
|
+
break;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
case 'profiles': {
|
|
1023
|
+
const nameW = Math.max(...Object.keys(PROFILES).map(k => k.length)) + 2;
|
|
1024
|
+
console.log('');
|
|
1025
|
+
console.log(c.bold(' Installation profiles'));
|
|
1026
|
+
console.log(' ' + c.line(60));
|
|
1027
|
+
for (const [name, profile] of Object.entries(PROFILES)) {
|
|
1028
|
+
const skillCount = `${profile.skills.length} skill${profile.skills.length !== 1 ? 's' : ''}`;
|
|
1029
|
+
const agentPart = profile.agents.length ? ` + ${profile.agents.length} agent` : '';
|
|
1030
|
+
console.log(` ${c.cyan(name.padEnd(nameW))}${c.dim(skillCount + agentPart)}`);
|
|
1031
|
+
console.log(` ${' '.repeat(nameW)}${profile.description}`);
|
|
1032
|
+
console.log('');
|
|
1033
|
+
}
|
|
1034
|
+
console.log(c.dim(` Install: ${c.cyan('skills add --profile=<name>')}`));
|
|
1035
|
+
console.log('');
|
|
1036
|
+
break;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
856
1039
|
default:
|
|
857
1040
|
console.log(`
|
|
858
1041
|
${c.bold(' @booklib/skills')} — book knowledge distilled into AI agent skills
|
|
859
1042
|
|
|
860
1043
|
${c.bold(' Usage:')}
|
|
861
1044
|
${c.cyan('skills list')} list all available skills
|
|
1045
|
+
${c.cyan('skills agents')} list all available agents
|
|
1046
|
+
${c.cyan('skills agents')} ${c.dim('--info=<name>')} full description of an agent
|
|
1047
|
+
${c.cyan('skills profiles')} list available profiles
|
|
862
1048
|
${c.cyan('skills info')} ${c.dim('<name>')} full description of a skill
|
|
863
1049
|
${c.cyan('skills demo')} ${c.dim('<name>')} before/after example
|
|
864
|
-
${c.cyan('skills add')} ${c.dim('
|
|
865
|
-
${c.cyan('skills add
|
|
1050
|
+
${c.cyan('skills add')} ${c.dim('--profile=<name>')} install a profile (skills + commands + agent)
|
|
1051
|
+
${c.cyan('skills add')} ${c.dim('<name>')} install a single skill + /command
|
|
1052
|
+
${c.cyan('skills add --all')} install everything (skills + commands + agents)
|
|
866
1053
|
${c.cyan('skills add')} ${c.dim('<name> --global')} install globally (~/.claude/)
|
|
867
|
-
${c.cyan('skills add')} ${c.dim('<name> --no-commands')} install skill only, skip command
|
|
868
1054
|
${c.cyan('skills add')} ${c.dim('--agent=<name>')} install a single agent to .claude/agents/
|
|
869
|
-
${c.cyan('skills add
|
|
1055
|
+
${c.cyan('skills add')} ${c.dim('--target=cursor')} install to .cursor/rules/ (Cursor IDE)
|
|
1056
|
+
${c.cyan('skills add')} ${c.dim('--target=all')} install to both .claude/ and .cursor/
|
|
1057
|
+
${c.cyan('skills add')} ${c.dim('--no-commands')} skip /command installation
|
|
1058
|
+
${c.cyan('skills add')} ${c.dim('--no-agents')} skip agent installation
|
|
870
1059
|
${c.cyan('skills check')} ${c.dim('<name>')} quality check (Bronze/Silver/Gold/Platinum)
|
|
871
1060
|
${c.cyan('skills check --all')} quality summary for all skills
|
|
872
1061
|
${c.cyan('skills update-readme')} refresh README quality table from results.json files
|
package/hooks/hooks.json
ADDED
package/hooks/suggest.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* booklib-suggest.js
|
|
4
|
+
* Claude Code UserPromptSubmit hook — suggests a relevant @booklib/skills skill
|
|
5
|
+
* when the prompt contains a review intent AND a language/domain signal.
|
|
6
|
+
*
|
|
7
|
+
* Install: copy (or symlink) this file to ~/.claude/booklib-suggest.js
|
|
8
|
+
* Hook config (hooks.json):
|
|
9
|
+
* { "UserPromptSubmit": [{ "hooks": [{ "type": "command", "command": "node \"$HOME/.claude/booklib-suggest.js\"" }] }] }
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
"use strict";
|
|
13
|
+
|
|
14
|
+
process.exitCode = 0;
|
|
15
|
+
|
|
16
|
+
const REVIEW_KEYWORDS = [
|
|
17
|
+
"review", "check", "improve", "refactor", "fix", "audit",
|
|
18
|
+
"analyse", "analyze", "critique", "lint",
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const LANGUAGE_SIGNALS = {
|
|
22
|
+
python_async: [".py", "asyncio", "async def", "await "],
|
|
23
|
+
python_scraping: [".py", "python", "beautifulsoup", "scrapy", "requests.get", "web scraping"],
|
|
24
|
+
python: [".py", "python", "def ", "async def", "import ", "asyncio", "beautifulsoup", "scrapy"],
|
|
25
|
+
typescript: [".ts", ".tsx", ".js", "typescript", "interface ", "type ", "const ", "function "],
|
|
26
|
+
java: [".java", "java", "class ", "@override", "public static"],
|
|
27
|
+
kotlin: [".kt", "kotlin", "fun ", "val ", "var ", "data class"],
|
|
28
|
+
rust: [".rs", "rust", "fn ", "impl ", "struct ", "enum ", "let mut"],
|
|
29
|
+
ui_animation: [".css", ".scss", "animation", "transition", "@keyframes", "styled", "tailwind"],
|
|
30
|
+
ui: [".css", ".scss", "animation", "transition", "@keyframes", "styled", "tailwind"],
|
|
31
|
+
data: ["pipeline", "etl", "dataframe", "schema", "migration", "replication"],
|
|
32
|
+
architecture: ["microservice", "aggregate", "bounded context", "saga", "event sourcing"],
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const SKILL_MAP = {
|
|
36
|
+
python_async: { skill: "/using-asyncio-python", reason: "Async patterns and asyncio best practices in Python" },
|
|
37
|
+
python_scraping: { skill: "/web-scraping-python", reason: "Web scraping techniques and patterns with Python" },
|
|
38
|
+
python: { skill: "/effective-python", reason: "Pythonic idioms and best practices" },
|
|
39
|
+
typescript: { skill: "/effective-typescript", reason: "TypeScript type safety and idiomatic patterns" },
|
|
40
|
+
java: { skill: "/effective-java", reason: "Effective Java patterns and API design" },
|
|
41
|
+
kotlin: { skill: "/effective-kotlin", reason: "Idiomatic Kotlin and best practices" },
|
|
42
|
+
rust: { skill: "/programming-with-rust", reason: "Rust ownership, safety, and idiomatic patterns" },
|
|
43
|
+
ui_animation: { skill: "/animation-at-work", reason: "Web animation principles and best practices" },
|
|
44
|
+
ui: { skill: "/refactoring-ui", reason: "UI design principles and visual hierarchy" },
|
|
45
|
+
data: { skill: "/data-pipelines", reason: "Data pipeline design and ETL best practices" },
|
|
46
|
+
architecture: { skill: "/skill-router", reason: "Routes to the right skill for architecture concerns" },
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
function extractPrompt(raw) {
|
|
50
|
+
if (!raw || raw.trim() === "") return "";
|
|
51
|
+
try {
|
|
52
|
+
const parsed = JSON.parse(raw);
|
|
53
|
+
// Claude Code may send { prompt: "..." } or { message: "..." }
|
|
54
|
+
if (parsed && typeof parsed === "object") {
|
|
55
|
+
return String(parsed.prompt || parsed.message || parsed.text || "");
|
|
56
|
+
}
|
|
57
|
+
} catch (_) {
|
|
58
|
+
// Not JSON — treat as raw prompt text
|
|
59
|
+
}
|
|
60
|
+
return raw;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function hasReviewIntent(text) {
|
|
64
|
+
const lower = text.toLowerCase();
|
|
65
|
+
return REVIEW_KEYWORDS.some((kw) => lower.includes(kw));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Returns the most specific language key that matches, or null.
|
|
70
|
+
* Order matters: more specific keys (python_async, python_scraping) are checked first.
|
|
71
|
+
*/
|
|
72
|
+
function detectLanguage(text) {
|
|
73
|
+
const lower = text.toLowerCase();
|
|
74
|
+
|
|
75
|
+
const orderedKeys = [
|
|
76
|
+
"python_async",
|
|
77
|
+
"python_scraping",
|
|
78
|
+
"python",
|
|
79
|
+
"typescript",
|
|
80
|
+
"java",
|
|
81
|
+
"kotlin",
|
|
82
|
+
"rust",
|
|
83
|
+
"ui_animation",
|
|
84
|
+
"ui",
|
|
85
|
+
"data",
|
|
86
|
+
"architecture",
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
for (const key of orderedKeys) {
|
|
90
|
+
const signals = LANGUAGE_SIGNALS[key];
|
|
91
|
+
// For compound keys, require at least 2 signals to avoid false positives
|
|
92
|
+
// (e.g. python_async needs both a python marker AND an async marker)
|
|
93
|
+
if (key === "python_async") {
|
|
94
|
+
const hasPython = [".py", "python", "def ", "import "].some((s) => lower.includes(s));
|
|
95
|
+
const hasAsync = ["asyncio", "async def", "await "].some((s) => lower.includes(s));
|
|
96
|
+
if (hasPython && hasAsync) return key;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (key === "python_scraping") {
|
|
100
|
+
const hasPython = [".py", "python", "def ", "import "].some((s) => lower.includes(s));
|
|
101
|
+
const hasScraping = ["beautifulsoup", "scrapy", "web scraping", "requests.get"].some((s) => lower.includes(s));
|
|
102
|
+
if (hasPython && hasScraping) return key;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (signals.some((s) => lower.includes(s.toLowerCase()))) {
|
|
106
|
+
return key;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function main() {
|
|
114
|
+
let raw = "";
|
|
115
|
+
try {
|
|
116
|
+
// Read all of stdin synchronously
|
|
117
|
+
const fd = require("fs").openSync("/dev/stdin", "r");
|
|
118
|
+
const chunks = [];
|
|
119
|
+
const buf = Buffer.alloc(4096);
|
|
120
|
+
let bytesRead;
|
|
121
|
+
while ((bytesRead = require("fs").readSync(fd, buf, 0, buf.length, null)) > 0) {
|
|
122
|
+
chunks.push(buf.slice(0, bytesRead));
|
|
123
|
+
}
|
|
124
|
+
require("fs").closeSync(fd);
|
|
125
|
+
raw = Buffer.concat(chunks).toString("utf8");
|
|
126
|
+
} catch (_) {
|
|
127
|
+
// stdin unavailable or empty — exit silently
|
|
128
|
+
process.exit(0);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const prompt = extractPrompt(raw);
|
|
132
|
+
if (!prompt) process.exit(0);
|
|
133
|
+
|
|
134
|
+
if (!hasReviewIntent(prompt)) process.exit(0);
|
|
135
|
+
|
|
136
|
+
const langKey = detectLanguage(prompt);
|
|
137
|
+
|
|
138
|
+
if (!langKey) {
|
|
139
|
+
// Review intent but no specific language — suggest clean-code-reviewer
|
|
140
|
+
process.stdout.write(
|
|
141
|
+
"💡 booklib: try /clean-code-reviewer — General clean code review principles\n"
|
|
142
|
+
);
|
|
143
|
+
process.exit(0);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const match = SKILL_MAP[langKey];
|
|
147
|
+
if (!match) process.exit(0);
|
|
148
|
+
|
|
149
|
+
process.stdout.write(`💡 booklib: try ${match.skill} — ${match.reason}\n`);
|
|
150
|
+
process.exit(0);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
main();
|