@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 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
  ![Demo](demo.gif)
20
16
 
21
- Each skill is a self-contained folder with a `SKILL.md` file containing instructions and metadata that AI agents use to perform specialized tasks.
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
- ## Skill Format
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
- Each skill follows the [Agent Skills standard](https://agentskills.io):
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
- # Install all skills globally
73
- npx skills add booklib-ai/skills --all -g
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
- # Install a specific skill
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
- # List available skills without installing
79
- npx skills add booklib-ai/skills --list
80
- ```
48
+ ## Agents
81
49
 
82
- ### via npm
50
+ Eight autonomous reviewers that run end-to-end reviews combining the most relevant skills for each domain:
83
51
 
84
- ```bash
85
- # Install all skills globally
86
- npx @booklib/skills add --all --global
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
- # Install a single skill
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
- # List available skills
92
- npx @booklib/skills list
93
- ```
65
+ ## Profiles
94
66
 
95
- Skills are installed to `.claude/skills/` in your project (or `~/.claude/skills/` with `--global`).
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
- ### Manual
79
+ ## Skills
98
80
 
99
- ```bash
100
- git clone https://github.com/booklib-ai/skills.git
101
- cp -r booklib-ai-skills/skills/effective-kotlin /path/to/project/.claude/skills/
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
- This means skills compose: `skill-router` acts as an orchestrator that picks the right specialist skills for the context, without requiring the user to know the library upfront.
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
- ## Skills
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
- ## Contributing a skill
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. The bar is lower than you think:
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 — you get a quality signal before anyone else sees the PR. See [CONTRIBUTING.md](./CONTRIBUTING.md) for the full guide.
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
- if (agentArg) {
732
- // explicit: skills add --agent=booklib-reviewer
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.forEach(s => copySkill(s, targetDir));
743
- if (!noCommands) skills.forEach(s => copyCommand(s));
744
- if (!noAgents) getAvailableAgents().forEach(a => copyAgent(a));
745
- const agentCount = noAgents ? 0 : getAvailableAgents().length;
746
- console.log(c.dim(`\nInstalled ${skills.length} skills, ${agentCount} agents to .claude/`));
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
- copySkill(skillName, targetDir);
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('<name>')} install skill + /command to .claude/
865
- ${c.cyan('skills add --all')} install all skills + commands + agents
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 --all --no-agents')} install skills + commands, skip agents
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
@@ -0,0 +1,12 @@
1
+ {
2
+ "UserPromptSubmit": [
3
+ {
4
+ "hooks": [
5
+ {
6
+ "type": "command",
7
+ "command": "node \"$HOME/.claude/booklib-suggest.js\""
8
+ }
9
+ ]
10
+ }
11
+ ]
12
+ }
@@ -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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@booklib/skills",
3
- "version": "1.7.0",
3
+ "version": "1.9.0",
4
4
  "description": "Book knowledge distilled into structured AI skills for Claude Code and other AI assistants",
5
5
  "bin": {
6
6
  "skills": "bin/skills.js"